/**
  * Removes the given {@link MZRoom} and all its descendants from the given
  * list.
  *
  * @param rooms the list of MZRooms to remove nodes from
  * @param room  the MZRoom whose descendants to remove from the list
  */
 protected void RemoveDescendantsFromList(List <MZRoom> rooms, MZRoom room)
 {
     rooms.Remove(room);
     foreach (MZRoom child in room.GetChildren())
     {
         RemoveDescendantsFromList(rooms, child);
     }
 }
 /**
  * Adds extra conditions to the given {@link MZRoom}'s preconditions and all
  * of its descendants.
  *
  * @param room  the MZRoom to Add extra preconditions to
  * @param cond  the extra preconditions to Add
  */
 protected void AddPrecond(MZRoom room, MZCondition cond)
 {
     room.SetPrecond(room.GetPrecond().And(cond));
     foreach (MZRoom child in room.GetChildren())
     {
         AddPrecond(child, cond);
     }
 }
    /**
     * Recursively applies the given intensity to the given {@link MZRoom}, and
     * higher intensities to each of its descendants that are within the same
     * keyLevel.
     * <p>
     * Intensities set by this method may (will) be outside of the normal range
     * from 0.0 to 1.0. See {@link #NormalizeIntensity} to correct this.
     *
     * @param room      the room to set the intensity of
     * @param intensity the value to set intensity to (some randomn variance is
     *                  Added)
     * @see MZRoom
     */
    protected double ApplyIntensity(MZRoom room, double intensity)
    {
        intensity *= 1.0 - intensityGrowthJitter / 2.0 +
                     intensityGrowthJitter * UnityEngine.Random.value;

        room.SetIntensity(intensity);

        double maxIntensity = intensity;

        foreach (MZRoom child in room.GetChildren())
        {
            if (room.GetPrecond().Implies(child.GetPrecond()))
            {
                maxIntensity = Math.Max(maxIntensity, ApplyIntensity(child,
                                                                     intensity + 1.0));
            }
        }

        return(maxIntensity);
    }
    /**
     * Randomly locks descendant rooms of the given {@link MZRoom} with
     * {@link MZEdge}s that require the switch to be in the given state.
     * <p>
     * If the given state is Either, the required states will be random.
     *
     * @param room          the room whose child to lock
     * @param givenState    the state to require the switch to be in for the
     *                      child rooms to be accessible
     * @return              true if any locks were Added, false if none were
     *                      Added (which can happen due to the way the random
     *                      decisions are made)
     * @see MZCondition.SwitchState
     */
    protected bool SwitchLockChildRooms(MZRoom room,
                                        MZCondition.SwitchState givenState)
    {
        bool anyLocks = false;

        MZCondition.SwitchState state = givenState != MZCondition.SwitchState.Either
                ? givenState
                : (UnityEngine.Random.Range(0, 2) == 0
                    ? MZCondition.SwitchState.On
                    : MZCondition.SwitchState.Off);

        foreach (MZEdge edge in room.GetEdges())
        {
            int    neighborId = edge.GetTargetRoomId();
            MZRoom nextRoom   = dungeon.Get(neighborId);
            if (room.GetChildren().Contains(nextRoom))
            {
                if (room.GetEdge(neighborId).GetSymbol() == null &&
                    UnityEngine.Random.Range(0, 4) != 0)
                {
                    dungeon.Link(room, nextRoom, state.ToSymbol());
                    AddPrecond(nextRoom, new MZCondition(state.ToSymbol()));
                    anyLocks = true;
                }
                else
                {
                    anyLocks |= SwitchLockChildRooms(nextRoom, state);
                }

                if (givenState == MZCondition.SwitchState.Either)
                {
                    state = state.Invert();
                }
            }
        }
        return(anyLocks);
    }
    /**
     * Places the BOSS and GOAL rooms within the dungeon, in existing rooms.
     * These rooms are moved into the next keyLevel.
     *
     * @param levels    the keyLevel -> room-set mapping to update
     * @ if it fails
     * @see KeyLevelRoomMapping
     */
    protected void PlaceBossGoalRooms(KeyLevelRoomMapping levels)
    {
        List <MZRoom> possibleGoalRooms = new List <MZRoom>(dungeon.RoomCount());

        MZSymbol goalSym = new MZSymbol((int)MZSymbol.MZSymbolValue.Goal),
                 bossSym = new MZSymbol((int)MZSymbol.MZSymbolValue.Boss);

        foreach (MZRoom room in dungeon.GetRooms())
        {
            if (room.GetChildren().Count > 0 || room.GetItem() != null)
            {
                continue;
            }
            MZRoom parent = room.GetParent();
            if (parent == null)
            {
                continue;
            }
            if (IsGenerateGoal() && (parent.GetChildren().Count != 1 ||
                                     !parent.GetPrecond().Implies(room.GetPrecond())))
            {
                continue;
            }
            if (IsGenerateGoal())
            {
                if (!constraints.RoomCanFitItem(room.id, goalSym) ||
                    !constraints.RoomCanFitItem(parent.id, bossSym))
                {
                    continue;
                }
            }
            else
            {
                if (!constraints.RoomCanFitItem(room.id, bossSym))
                {
                    continue;
                }
            }
            possibleGoalRooms.Add(room);
        }

        if (possibleGoalRooms.Count == 0)
        {
            throw new RetryException();
        }

        MZRoom goalRoom = possibleGoalRooms[UnityEngine.Random.Range(0,
                                                                     possibleGoalRooms.Count)],
               bossRoom = goalRoom.GetParent();

        if (!IsGenerateGoal())
        {
            bossRoom = goalRoom;
            goalRoom = null;
        }

        if (goalRoom != null)
        {
            goalRoom.SetItem(goalSym);
        }
        bossRoom.SetItem(bossSym);

        int oldKeyLevel = bossRoom.GetPrecond().GetKeyLevel(),
            newKeyLevel = Math.Min(levels.KeyCount(), constraints.GetMaxKeys());

        if (oldKeyLevel != newKeyLevel)
        {
            List <MZRoom> oklRooms = levels.GetRooms(oldKeyLevel);
            if (goalRoom != null)
            {
                oklRooms.Remove(goalRoom);
            }
            oklRooms.Remove(bossRoom);

            if (goalRoom != null)
            {
                levels.AddRoom(newKeyLevel, goalRoom);
            }
            levels.AddRoom(newKeyLevel, bossRoom);

            MZSymbol    bossKey = new MZSymbol(newKeyLevel - 1);
            MZCondition precond = bossRoom.GetPrecond().And(bossKey);
            bossRoom.SetPrecond(precond);
            if (goalRoom != null)
            {
                goalRoom.SetPrecond(precond);
            }

            if (newKeyLevel == 0)
            {
                dungeon.Link(bossRoom.GetParent(), bossRoom);
            }
            else
            {
                dungeon.Link(bossRoom.GetParent(), bossRoom, bossKey);
            }
            if (goalRoom != null)
            {
                dungeon.Link(bossRoom, goalRoom);
            }
        }
    }