/// <summary> /// Immediately attempts to perform a creature movement to a tile on the map. /// </summary> /// <param name="context">A reference to the operation context.</param> /// <param name="creature">The creature being moved.</param> /// <param name="toLocation">The tile to which the movement is being performed.</param> /// <param name="isTeleport">Optional. A value indicating whether the movement is considered a teleportation. Defaults to false.</param> /// <param name="requestorCreature">Optional. The creature that this movement is being performed in behalf of, if any.</param> /// <returns>True if the movement was successfully performed, false otherwise.</returns> /// <remarks>Changes game state, should only be performed after all pertinent validations happen.</remarks> private bool PerformCreatureMovement(IOperationContext context, ICreature creature, Location toLocation, bool isTeleport = false, ICreature requestorCreature = null) { if (creature == null || !(creature.ParentContainer is ITile fromTile) || !context.Map.HasTileAt(toLocation, out ITile toTile)) { return(false); } var moveDirection = fromTile.Location.DirectionTo(toLocation, true); // Try to figure out the position in the stack of the creature. var fromTileStackPos = fromTile.GetIndexOfThing(creature); if (fromTileStackPos == byte.MaxValue) { // couldn't find this creature in this tile... return(false); } // Do the actual move first. if (!fromTile.RemoveCreature(creature)) { return(false); } if (!toTile.AddCreature(creature)) { // attempt to rollback state. if (!fromTile.RemoveCreature(creature)) { // Leaves us in a really bad spot. throw new ApplicationException("Unable to rollback state after filing to move creature. Game state is altered and inconsistent now."); } } var toStackPosition = toTile.GetIndexOfThing(creature); if (toStackPosition == byte.MaxValue) { context.Logger.LogWarning("Unexpected destination stack position during creature movement, suggests that the creature is not found in the destination tile after the move."); } // Then deal with the consequences of the move. ((Creature)creature).Direction = moveDirection; ((Creature)creature).LastMovementCostModifier = (fromTile.Location - toLocation).Z != 0 ? 2 : moveDirection.IsDiagonal() ? 3 : 1; this.ExhaustionInfo[ExhaustionFlag.Movement] = creature.CalculateStepDuration(fromTile); this.SendNotification( context, new CreatureMovedNotification( context.Map.FindPlayersThatCanSee(fromTile.Location, toLocation), context.Map, creature, fromTile.Location, fromTileStackPos, toTile.Location, toStackPosition, isTeleport)); return(true); }