private IEnumerator moveBetweenNodes(Entity entity, MarioClearPipeInteraction interaction, Vector2 from, Vector2 to, float?travelSpeed = null, bool?lerpPipeOffset = null) { interaction.Distance = (from - to).Length(); interaction.DirectionVector = GetPipeExitDirectionVector(to, from); interaction.Direction = GetPipeExitDirection(to, from); interaction.From = from; interaction.To = to; interaction.TravelSpeed = travelSpeed != null ? travelSpeed.Value : interaction.TravelSpeed; interaction.LerpPipeOffset = lerpPipeOffset != null ? lerpPipeOffset.Value : interaction.LerpPipeOffset; while (entity != null && interaction.Moved <= interaction.Distance && interaction.Distance != 0f && !interaction.ExitEarly) { interaction?.OnPipeUpdate(entity, interaction); float lerpValue = interaction.Moved / interaction.Distance; entity.Position = Vector2.Lerp(from, to, lerpValue) + (interaction.LerpPipeOffset ? Vector2.Lerp(Vector2.Zero, interaction.PipeRenderOffset, lerpValue) : interaction.PipeRenderOffset); interaction.Moved += interaction.TravelSpeed * Engine.DeltaTime; yield return(null); } interaction.Moved -= interaction.Distance; }
private void ejectFromPipe(Entity entity, MarioClearPipeInteraction interaction) { // Fix float positions, causes weird collision bugs for entities entity.Position = new Vector2((int)Math.Round(entity.Position.X), (int)Math.Round(entity.Position.Y)); interaction.OnPipeExit?.Invoke(entity, interaction); interaction.CurrentClearPipe = null; CurrentlyTransportedEntities.Remove(entity); }
public static bool CanTransportEntity(Entity entity, Direction direction) { if (entity != null && !CurrentlyTransportedEntities.Contains(entity)) { MarioClearPipeInteraction interaction = GetClearPipeInteraction(entity); return(interaction?.CanEnterPipe?.Invoke(entity, direction) == true); } return(false); }
// Visually update exiting steps // Correct for exit overshooting private IEnumerator exitPipeMovement(Entity entity, MarioClearPipeInteraction interaction) { Vector2 previousPosition = entity.Position; Vector2 currentPosition = entity.Position; bool colliding = entity?.CollideFirst <MarioClearPipeSolid>() != null; while (entity != null && entity.Scene != null && colliding) { entity.Position += interaction.DirectionVector * TransportSpeed * Engine.DeltaTime; previousPosition = currentPosition; currentPosition = entity.Position; colliding = entity.CollideFirst <MarioClearPipeSolid>() != null; if (colliding) { yield return(null); } } // Correct for overshooting the exit, attempt to place entity as close as possible to the pipe Vector2 lowerValue = previousPosition; Vector2 upperValue = currentPosition; Vector2 lastUnblocked = entity.Position; while (entity != null && entity.Scene != null && (lowerValue - upperValue).LengthSquared() > 0.5f) { entity.Position = Vector2.Lerp(lowerValue, upperValue, 0.5f); if (entity.CollideFirst <MarioClearPipeSolid>() != null) { lowerValue = entity.Position; } else { upperValue = entity.Position; lastUnblocked = entity.Position; } } entity.Position = lastUnblocked; }
// 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); } } }