1- using FMOD . Studio ;
2- using System . Linq ;
1+ using Celeste . Mod . Helpers ;
2+ using FMOD . Studio ;
33
44namespace Celeste . Mod . CommunalHelper . Entities ;
55
@@ -55,18 +55,22 @@ public void ConstraintTo(Vector2 to, float distance, bool cancelAcceleration)
5555 private readonly MTexture texture ;
5656 private readonly MTexture segment ;
5757
58+ private readonly bool updateOffscreen ;
59+ private readonly Vector2 [ ] oldNodePositions ;
60+
5861 public Chain ( EntityData data , Vector2 offset )
5962 : this (
6063 data . Bool ( "outline" , true ) ,
6164 ( int ) ( ( Vector2 . Distance ( data . Position + offset , data . NodesOffset ( offset ) [ 0 ] ) / 8 ) + 1 + data . Int ( "extraJoints" ) ) ,
6265 8 ,
6366 ( ) => data . Position + offset ,
6467 ( ) => data . Nodes [ 0 ] + offset ,
65- GFX . Game . GetOrDefault ( data . Attr ( "texture" , DEFAULT_CHAIN_PATH ) , DefaultChain )
68+ GFX . Game . GetOrDefault ( data . Attr ( "texture" , DEFAULT_CHAIN_PATH ) , DefaultChain ) ,
69+ data . Bool ( "updateOffscreen" , true )
6670 )
6771 { }
6872
69- public Chain ( bool outline , int nodeCount , float distanceConstraint , Func < Vector2 > attachedStartGetter , Func < Vector2 > attachedEndGetter , MTexture texture )
73+ public Chain ( bool outline , int nodeCount , float distanceConstraint , Func < Vector2 > attachedStartGetter , Func < Vector2 > attachedEndGetter , MTexture texture , bool updateOffscreen )
7074 : base ( attachedStartGetter ( ) )
7175 {
7276 this . texture = texture ;
@@ -76,6 +80,7 @@ public Chain(bool outline, int nodeCount, float distanceConstraint, Func<Vector2
7680 this . attachedStartGetter = attachedStartGetter ;
7781 this . attachedEndGetter = attachedEndGetter ;
7882 this . distanceConstraint = distanceConstraint ;
83+ this . updateOffscreen = updateOffscreen ;
7984
8085 this . outline = outline ;
8186
@@ -89,6 +94,7 @@ public Chain(bool outline, int nodeCount, float distanceConstraint, Func<Vector2
8994
9095 UpdateChain ( ) ;
9196 sfx = Audio . Play ( CustomSFX . game_chain_move ) ;
97+ oldNodePositions = new Vector2 [ nodes . Length ] ;
9298 }
9399
94100 private void AttachedEndsToSolids ( Scene scene )
@@ -181,9 +187,13 @@ public override void Update()
181187 {
182188 base . Update ( ) ;
183189
184- Vector2 [ ] oldPositions = nodes . Select ( node => node . Position ) . ToArray ( ) ;
190+ if ( ! updateOffscreen && ! IsVisible ( ) )
191+ return ;
192+
193+ for ( int i = 0 ; i < nodes . Length ; i ++ )
194+ oldNodePositions [ i ] = nodes [ i ] . Position ;
185195 UpdateChain ( ) ;
186- UpdateSfx ( oldPositions ) ;
196+ UpdateSfx ( oldNodePositions ) ;
187197
188198 if ( Vector2 . Distance ( nodes [ 0 ] . Position , nodes [ ^ 1 ] . Position ) > ( nodes . Length + 1 ) * distanceConstraint )
189199 BreakInHalf ( ) ;
@@ -195,11 +205,11 @@ private void BreakInHalf()
195205 Vector2 middleNode = nodes [ nodes . Length / 2 ] . Position ;
196206
197207 Chain a , b ;
198- Scene . Add ( a = new Chain ( outline , nodes . Length / 2 , 8 , ( ) => middleNode , attachedStartGetter , texture ) ) ;
208+ Scene . Add ( a = new Chain ( outline , nodes . Length / 2 , 8 , ( ) => middleNode , attachedStartGetter , texture , updateOffscreen ) ) ;
199209 a . AttachedEndsToSolids ( Scene ) ;
200210 a . ShakeImpulse ( ) ;
201211
202- Scene . Add ( b = new Chain ( outline , nodes . Length / 2 , 8 , ( ) => middleNode , attachedEndGetter , texture ) ) ;
212+ Scene . Add ( b = new Chain ( outline , nodes . Length / 2 , 8 , ( ) => middleNode , attachedEndGetter , texture , updateOffscreen ) ) ;
203213 b . AttachedEndsToSolids ( Scene ) ;
204214 b . ShakeImpulse ( ) ;
205215
@@ -262,9 +272,28 @@ private void ShakeImpulse()
262272 }
263273 }
264274
275+ private bool IsVisible ( )
276+ {
277+ // Construct a rectangle containing the first, middle and last node, which acts as the bounding box of this chain.
278+ // Code adapted from CullHelper.IsCurveVisible
279+ Vector2 begin = nodes [ 0 ] . Position ;
280+ Vector2 control = nodes [ nodes . Length / 2 ] . Position ;
281+ Vector2 end = nodes [ ^ 1 ] . Position ;
282+
283+ float x = Math . Min ( begin . X , Math . Min ( control . X , end . X ) ) ;
284+ float right = Math . Max ( begin . X , Math . Max ( control . X , end . X ) ) ;
285+ float y = Math . Min ( begin . Y , Math . Min ( control . Y , end . Y ) ) ;
286+ float bottom = Math . Max ( begin . Y , Math . Max ( control . Y , end . Y ) ) ;
287+
288+ return CullHelper . IsRectangleVisible ( x , y , right - x , bottom - y , lenience : segment . Width + 2 ) ;
289+ }
290+
265291 public override void Render ( )
266292 {
267293 base . Render ( ) ;
294+
295+ if ( ! IsVisible ( ) )
296+ return ;
268297
269298 if ( outline )
270299 {
0 commit comments