/** * Creates a new MZCondition that requires this MZCondition to be satisfied and * requires another {@link MZSymbol} to be obtained as well. * * @param sym the Added symbol the player must have for the new MZCondition * to be satisfied * @return the new MZCondition */ public MZCondition And(MZSymbol sym) { MZCondition result = new MZCondition(this); result.Add(sym); return(result); }
/** * 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); } }
private void Add(MZCondition cond) { if (switchState == SwitchState.Either) { switchState = cond.switchState; } keyLevel = Math.Max(keyLevel, cond.keyLevel); }
/** * Fill the dungeon's space with rooms and doors (some locked). * Keys are not inserted at this point. * * @param levels the keyLevel -> room-set mapping to update * @ if it fails * @see KeyLevelRoomMapping */ protected void PlaceRooms(KeyLevelRoomMapping levels, int roomsPerLock) { // keyLevel: the number of keys required to Get to the new room int keyLevel = 0; MZSymbol latestKey = null; // condition that must hold true for the player to reach the new room // (the set of keys they must have). MZCondition cond = new MZCondition(); // Loop to place rooms and link them while (dungeon.RoomCount() < constraints.GetMaxRooms()) { bool doLock = false; // Decide whether we need to place a new lock // (Don't place the last lock, since that's reserved for the boss) if (ShouldAddNewLock(keyLevel, levels.GetRooms(keyLevel).Count, roomsPerLock)) { latestKey = new MZSymbol(keyLevel++); cond = cond.And(latestKey); doLock = true; } // Find an existing room with a free edge: MZRoom parentRoom = null; if (!doLock && UnityEngine.Random.Range(0, 10) > 0) { parentRoom = ChooseRoomWithFreeEdge(levels.GetRooms(keyLevel), keyLevel); } if (parentRoom == null) { parentRoom = ChooseRoomWithFreeEdge(new List <MZRoom>(dungeon.GetRooms()), keyLevel); doLock = true; } if (parentRoom == null) { throw new OutOfRoomsException(); } // Decide which direction to put the new room in relative to the // parent int nextId = ChooseFreeEdge(parentRoom, keyLevel); List <Vector2Int> coords = constraints.GetCoords(nextId); MZRoom room = new MZRoom(nextId, coords, parentRoom, null, cond); // Add the room to the dungeon dungeon.Add(room); parentRoom.AddChild(room); dungeon.Link(parentRoom, room, doLock ? latestKey : null); levels.AddRoom(keyLevel, room); } }
/** * Creates a new MZCondition that requires this MZCondition and another * MZCondition to both be satisfied. * * @param other the other MZCondition that must be satisfied. * @return the new MZCondition */ public MZCondition And(MZCondition other) { if (other == null) { return(this); } MZCondition result = new MZCondition(this); result.Add(other); return(result); }
public override bool Equals(object other) { if (other.GetType() == typeof(MZCondition)) { MZCondition o = (MZCondition)other; return(keyLevel == o.keyLevel && switchState == o.switchState); } else { return(base.Equals(other)); } }
/** * Creates a MZRoom at the given coordinates, with the given parent, * containing a specific item, and having a certain pre-{@link MZCondition}. * <p> * The parent of a room is the parent node of this MZRoom in the initial * tree of the dungeon during * {@link generators.MZDungeonGenerator#Generate()}, and * before * {@link generators.MZDungeonGenerator#Graphify()}. * * @param coords the coordinates of the new room * @param parent the parent room or null if it is the root / entry room * @param item the symbol to place in the room or null if no item * @param precond the precondition of the room * @see MZCondition */ public MZRoom(int id, List <Vector2Int> coords, MZRoom parent, MZSymbol item, MZCondition precond) { this.id = id; this.coords = coords; this.item = item; this.edges = new List <MZEdge>(); this.precond = precond; this.intensity = 0.0; this.parent = parent; this.children = new List <MZRoom>(3); // all edges initially null int x = 0, y = 0; foreach (Vector2Int xy in coords) { x += xy.x; y += xy.y; } center = new Vector2Int(x / coords.Count, y / coords.Count); }
/** * Gets the single {@link MZSymbol} needed to make this MZCondition and another * MZCondition identical. * <p> * If {@link #and}ed to both MZConditions, the MZConditions would then imply * each other. * * @param other the other MZCondition * @return the MZSymbol needed to make the MZConditions identical, or null if * there is no single MZSymbol that would make them identical or if * they are already identical. */ public MZSymbol SingleSymbolDifference(MZCondition other) { // If the difference between this and other can be made up by obtaining // a single new symbol, this returns the symbol. If multiple or no // symbols are required, returns null. if (this.Equals(other)) { return(null); } if (switchState == other.switchState) { return(new MZSymbol(Math.Max(keyLevel, other.keyLevel) - 1)); } else { if (keyLevel != other.keyLevel) { return(null); } // Multiple symbols needed ^^^ if (switchState != SwitchState.Either && other.switchState != SwitchState.Either) { return(null); } SwitchState nonEither = switchState != SwitchState.Either ? switchState : other.switchState; return(new MZSymbol(nonEither == SwitchState.On ? (int)MZSymbol.MZSymbolValue.SwitchOn : (int)MZSymbol.MZSymbolValue.SwitchOff)); } }
/** * Makes some {@link MZEdge}s within the dungeon require the dungeon's switch * to be in a particular state, and places the switch in a room in the * dungeon. * * @ if it fails */ protected void PlaceSwitches() { // Possible TODO: have multiple switches on separate circuits // At the moment, we only have one switch per dungeon. if (constraints.GetMaxSwitches() <= 0) { return; } List <MZRoom> solution = GetSolutionPath(); for (int attempt = 0; attempt < 10; ++attempt) { List <MZRoom> rooms = new List <MZRoom>(dungeon.GetRooms()); Shuffle(rooms); Shuffle(solution); // Pick a base room from the solution path so that the player // will have to encounter a switch-lock to solve the dungeon. MZRoom baseRoom = null; foreach (MZRoom room in solution) { if (room.GetChildren().Count > 1 && room.GetParent() != null) { baseRoom = room; break; } } if (baseRoom == null) { throw new RetryException(); } MZCondition baseRoomCond = baseRoom.GetPrecond(); RemoveDescendantsFromList(rooms, baseRoom); MZSymbol switchSym = new MZSymbol((int)MZSymbol.MZSymbolValue.Switch); MZRoom switchRoom = null; foreach (MZRoom room in rooms) { if (room.GetItem() == null && baseRoomCond.Implies(room.GetPrecond()) && constraints.RoomCanFitItem(room.id, switchSym)) { switchRoom = room; break; } } if (switchRoom == null) { continue; } if (SwitchLockChildRooms(baseRoom, MZCondition.SwitchState.Either)) { switchRoom.SetItem(switchSym); return; } } throw new RetryException(); }
/** * 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); } } }
/** * Creates a MZCondition from another MZCondition (copy it). * * @param other the other MZCondition */ public MZCondition(MZCondition other) { keyLevel = other.keyLevel; switchState = other.switchState; }
/** * Determines whether another MZCondition is necessarily true if this one is. * * @param other the other MZCondition * @return whether the other MZCondition is implied by this one */ public bool Implies(MZCondition other) { return(keyLevel >= other.keyLevel && (switchState == other.switchState || other.switchState == SwitchState.Either)); }
public MZRoom(int id, Vector2Int coords, MZRoom parent, MZSymbol item, MZCondition precond) : this(id, new List <Vector2Int> { coords }, parent, item, precond) { }
/** * @param precond the precondition to set this MZRoom's to * @see MZCondition */ public void SetPrecond(MZCondition precond) { this.precond = precond; }