void Update()
    {
        UpdateHP();
        if (isWaiting)
        {
            if (Input.GetMouseButtonDown(0))
            {
                isWaiting = false;
            }
            return;
        }

        if (gameState == GameState.SurvivorVictory || gameState == GameState.KillerVictory)
        {
            GameOuput.Output(endMessage);
        }

        if (gameState == GameState.GenerateMoves)
        {
            // Generate every actor's move
            moves.Clear();
            for (int i = 0; i < actors.Count; i++)
            {
                Actor actor = actors[i];
                if (actor.hp <= 0)
                {
                    continue;
                }
                ActorController controller = actorControllers[i];
                GameMove        move       = controller.GenerateMove(gameData.Clone());
                move.actionAuthorName = actor.name;
                moves.Add(move);
            }

            gameState = GameState.ProcessingMoves;
            moveIndex = 0;
        }

        if (gameState == GameState.ProcessingMoves)
        {
            if (moveIndex >= moves.Count)
            {
                gameState = GameState.GenerateMoves;
                return;
            }

            ProcessOneMove();
        }
    }
    static bool ProcessAttack(GameMove gameMove, List <Actor> actors, Actor attacker)
    {
        Actor attackTarget = actors.Find(actor => actor.name == gameMove.actionTargetName);

        if (attackTarget == null)
        {
            var otherActorsQuery = from actor in actors
                                   where actor.currentLocation == gameMove.nextLocation &&
                                   actor.hp > 0 &&
                                   actor != attacker
                                   select actor;

            var result = otherActorsQuery.ToList();
            if (result.Count > 0)
            {
                attackTarget = result[MyRandom.Next(0, result.Count)];
            }
        }
        if (attackTarget != null)
        {
            bool   isAttackGood = false;
            string attackText   = "";
            if (gameMove.gameAction == GameAction.NormalAttack)
            {
                if (attacker.items.Contains(Item.Gun))
                {
                    attackText      += ($"BANG! {attacker.name} shoots {attackTarget.name} with a gun!");
                    attackTarget.hp -= 100;
                    isAttackGood     = true;
                }
                else
                {
                    attackText      += ($"{attacker.name} punches {attackTarget.name} in the face!");
                    attackTarget.hp -= 25;
                    isAttackGood     = true;
                }
            }
            else if (gameMove.gameAction == GameAction.StabAttack && attacker.isKiller)
            {
                attackText      += ($"{attackTarget.name} is stabbed by the killer in the shadows.");
                attackTarget.hp -= 50;
                isAttackGood     = true;
            }

            if (attackTarget.hp <= 0)
            {
                attackText += ($"\n{attackTarget.name} has died...");
                attacker.items.AddRange(attackTarget.items);
                attackTarget.items.Clear();
            }

            if (isAttackGood)
            {
                GameOuput.Output(attackText);
            }
            return(isAttackGood);
        }
        else
        {
            return(false);
        }
    }
    bool ProcessMove(GameMove gameMove, List <Actor> actors, GameData gameData)
    {
        Actor currentActor = actors.Find(a => a.name == gameMove.actionAuthorName);

        currentActor.currentLocation = gameMove.nextLocation;
        int index = actors.IndexOf(currentActor);

        playerTokens[index].TargetPosition = GetLocation(gameMove.nextLocation).playerLocations[index].position;

        switch (gameMove.gameAction)
        {
        default:
        case GameAction.Nothing:
            break;

        case GameAction.NormalAttack:
        case GameAction.StabAttack:
            return(ProcessAttack(gameMove, actors, currentActor));

        case GameAction.RepairGenerator:
            if (currentActor.currentLocation == Location.Basement && gameData.generatorHp > 0)
            {
                gameData.generatorHp -= objectiveRepairRate;
                GameOuput.Output($"{currentActor.name} repairs the generator in the basement.");
                if (gameData.generatorHp <= 0)
                {
                    basement.ClearObjective();
                }
                return(true);
            }
            break;

        case GameAction.RepairGate:
            if (currentActor.currentLocation == Location.Exit && gameData.gateHp > 0)
            {
                gameData.gateHp -= objectiveRepairRate;
                GameOuput.Output($"{currentActor.name} repairs the exit gate.");
                if (gameData.gateHp <= 0)
                {
                    exitGate.ClearObjective();
                }
                return(true);
            }
            break;

        case GameAction.UnlockSafe:
            if (currentActor.currentLocation == Location.Armory && !gameData.isSafeUnlocked)
            {
                gameData.isSafeUnlocked = true;
                GameOuput.Output($"{currentActor.name} unlocks the safe containing the gun.");
                armory.ChangeObjective();
                return(true);
            }
            break;

        case GameAction.TakeGun:
            if (currentActor.currentLocation == Location.Armory && !gameData.isGunTaken && gameData.isSafeUnlocked)
            {
                gameData.isGunTaken = true;
                currentActor.items.Add(Item.Gun);
                GameOuput.Output($"{currentActor.name} takes the gun in the safe.");
                armory.ClearObjective();
                return(true);
            }
            break;

        case GameAction.TakeMedkit:
            if (currentActor.currentLocation == Location.Bathroom && !gameData.isMedkitTaken)
            {
                gameData.isMedkitTaken = true;
                currentActor.items.Add(Item.Medkit);
                GameOuput.Output($"{currentActor.name} takes the medkit in the bathroom.");
                bathroom.ClearObjective();
                return(true);
            }
            break;

        case GameAction.UseMedkit:
            if (currentActor.items.Contains(Item.Medkit))
            {
                Actor healTarget = actors.Find(actor => actor.name == gameMove.actionTargetName);
                if (healTarget == null)
                {
                    var anyActorQuery = from actor in actors
                                        where actor.currentLocation == currentActor.currentLocation
                                        select actor;
                    var anyActorList = anyActorQuery.ToList();
                    healTarget = anyActorList[MyRandom.Next(0, anyActorList.Count)];
                }

                if (healTarget.currentLocation == currentActor.currentLocation)
                {
                    currentActor.items.Remove(Item.Medkit);
                    healTarget.hp = 100;
                    GameOuput.Output($"{currentActor.name} heals {healTarget.name} with the medkit!");
                    return(true);
                }
            }
            break;
        }
        return(false);
    }
 void Awake()
 {
     GameOuput.SetOutput(this);
 }