internal override async Task OnFollowUpTransactionAsync(IFollowUpArgs e, Point position)
        {
            // Teleport using a new transaction
            // (because if teleportation fails, we still want every other move
            // that happened before to apply)

            if (Entity != Entity.None && AcceptedEntities.Includes(Entity.GetType()) &&
                !(e.Initiator.Object is TeleporterTile))
            {
                // Now the initiator is the teleporter itself, so
                // if the target is a teleporter as well the entity
                // won't end up in an endless loop of teleportations.
                e.Initiator = new MoveInitiator(this, position);

                var isSuccessful = await e.MoveAsync(position, TargetPosition);
                
                if (isSuccessful)
                    e.Emit(new TeleporterTileTeleportEvent(position, TargetPosition, Entity));
            }
        }
        internal override async Task OnFollowUpTransactionAsync(IFollowUpArgs e, Point position)
        {
            if (e.Initiator.Object is PistonTile)
                return;

            var trigger = (await e.GetAsync(TriggerPosition)).Tile as ITrigger;
            var isTriggerOn = trigger?.IsOn ?? false;

            var initiator = new MoveInitiator(this, position);

            if (!IsExtended && isTriggerOn)
            {
                // Extend piston
                if (await e.MoveAsync(position, position + Direction))
                    e.Emit(new PistonExtendRetractEvent(true));
            }
            else if (IsExtended && !isTriggerOn)
            {
                // Retract piston
                if (await e.MoveAsync(position + Direction, position))
                    e.Emit(new PistonExtendRetractEvent(false));
            }

            // Otherwise do nothing
        }