// TODO - Cleanup and make more generic private IEnumerator pipeMovement(Entity entity, bool fromStart, bool canBounceBack = true, Vector2?forcedStartPosition = null) { MarioClearPipeInteraction interaction = GetClearPipeInteraction(entity); int startIndex = fromStart ? 0 : nodes.Length - 1; int lastIndex = fromStart ? nodes.Length - 1 : 0; int direction = fromStart ? 1 : -1; Direction transportStartDirection = fromStart ? startDirection : endDirection; Direction transportEndDirection = fromStart ? endDirection : startDirection; if (forcedStartPosition != null || CanTransportEntity(entity, transportStartDirection)) { CurrentlyTransportedEntities.Add(entity); interaction.CurrentClearPipe = this; interaction.ExitEarly = false; interaction?.OnPipeEnter?.Invoke(entity, interaction); // Check if we are entering the pipe or bouncing back from a blocked exit if (forcedStartPosition != null) { entity.Position = forcedStartPosition.Value; } else { // Gracefully attempt to move to the first node yield return(moveBetweenNodes(entity, interaction, entity.Position, nodes[startIndex], TransportSpeed * transportSpeedEnterMultiplier, true)); } // Follow the nodes for (int i = startIndex; i != lastIndex && !interaction.ExitEarly; i += direction) { yield return(moveBetweenNodes(entity, interaction, nodes[i], nodes[i + direction], TransportSpeed, false)); } if (interaction.ExitEarly) { ejectFromPipe(entity, interaction); yield break; } // Check if we can exit the pipe if (CanExitPipe(entity, interaction.DirectionVector, TransportSpeed)) { yield return(exitPipeMovement(entity, interaction)); } // Send back if it gets stuck in a solid if (entity != null && entity.Scene != null && entity.CollideCheck <Solid>()) { if (canBounceBack) { entity.Position = nodes[lastIndex] + interaction.PipeRenderOffset; yield return(pipeMovement(entity, !fromStart, false, entity.Position)); } else { ejectFromPipe(entity, interaction); } } else { ejectFromPipe(entity, interaction); } } }