/// <summary> /// Creates new monster group. /// </summary> /// <param name="name"></param> /// <param name="puzzle"></param> /// <param name="place"></param> /// <param name="spawnPosition"></param> public MonsterGroup(string name, Puzzle puzzle, PuzzlePlace place, Placement spawnPosition = Placement.Random) { _monsters = new List<NPC>(); this.Name = name; this.Puzzle = puzzle; this.Place = place; _spawnPosition = spawnPosition; }
/// <summary> /// Adds prop to puzzle in place. /// </summary> /// <param name="place"></param> /// <param name="prop"></param> /// <param name="positionType"></param> public void AddProp(PuzzlePlace place, DungeonProp prop, Placement positionType) { if (this.Region == null) { throw new PuzzleException("AddProp outside of OnPuzzleCreate."); } var pos = place.GetPosition(positionType); prop.RegionId = this.Region.Id; prop.Info.X = pos[0]; prop.Info.Y = pos[1]; prop.UpdateShapes(); prop.Info.Direction = MabiMath.DegreeToRadian(pos[2]); prop.Behavior += PuzzleEvent; this.Region.AddProp(prop); this.Props[prop.Name] = prop; }
/// <summary> /// Locks place and creates and returns a key for it. /// </summary> /// <param name="place"></param> /// <param name="keyName"></param> /// <returns></returns> public Item LockPlace(PuzzlePlace place, string keyName) { if (!place.IsLock) { throw new PuzzleException("Tried to lock a place that isn't a Lock"); } var doorName = place.GetLockDoor().Name; Item key; if (place.IsBossLock) { key = Item.CreateKey(70030, 0xFF0000, doorName); // Boss Room Key } else { key = Item.CreateKey(70029, place.LockColor, doorName); // Dungeon Room Key } place.LockPlace(key); this.Keys[keyName] = key; return(key); }
/// <summary> /// Adds key for lock place to a random monster of this group as a drop. /// </summary> /// <param name="lockPlace"></param> public void AddKeyForLock(PuzzlePlace lockPlace) { var place = lockPlace as PuzzlePlace; if (!place.IsLock) { Log.Warning("PuzzleChest.AddKeyForLock: This place isn't a Lock. ({0})", this.Puzzle.Name); return; } if (this.Count == 0) { Log.Warning("MonsterGroup.AddKeyForLock: No monsters in group."); return; } if (place.Key == null) { Log.Warning("MonsterGroup.AddKeyForLock: Place's key is null."); return; } this.AddDrop(place.Key); }
/// <summary> /// This place will precede locked place and contain some means to unlock it. /// </summary> /// <param name="lockPlace"></param> public void DeclareUnlock(PuzzlePlace lockPlace) { var place = lockPlace as PuzzlePlace; if (place == null || place.PlaceIndex == -1) throw new PuzzleException("We can't declare unlock"); this.PlaceIndex = _section.GetUnlock(place); _room = _section.Places[PlaceIndex].Room; this.IsUnlock = true; this.UpdatePosition(); }
/// <summary> /// Finds appropriate place to use as unlock place for given locked place. /// </summary> /// <param name="lockedPlace"></param> /// <returns>Index of unlock place in Places list</returns> public int GetUnlock(PuzzlePlace lockedPlace) { var lockedPlaceIndex = lockedPlace.PlaceIndex; // Get index of room behind lockedPlace door. lockedPlaceIndex = this.Places[lockedPlaceIndex].Room.Neighbor[lockedPlace.DoorDirection].RoomIndex; if (lockedPlaceIndex == 0 || lockedPlaceIndex == -1) // should be last room in section. lockedPlaceIndex = this.Places.Count; List<int> possiblePlacesOnPath = new List<int>(); List<int> possiblePlacesNotOnPath = new List<int>(); var deadEnd = -1; var deadEndDepth = 0; RoomTrait room; // places before lockedPlaceIndex are always behind it. for (var i = 0; i < lockedPlaceIndex; ++i) { room = this.Places[i].Room; if (!this.Places[i].IsUsed && !room.isLocked && room.RoomType != RoomType.Start) { // Check is this place have locked doors. // Locked places tend to not reserve themselves, so they could share place with some puzzle, like another locked place. // But they couldn't be shared with unlock places, because they have to control all doors. var haveLockedDoors = false; for (var j = 0; j < 4; ++j) if (room.DoorType[j] == (int)DungeonBlockType.DoorWithLock && room.Links[j] == LinkType.To) { haveLockedDoors = true; break; } if (!haveLockedDoors) { var emptyNeighborCount = 0; for (var dir = 0; dir < 4; ++dir) if (room.IsLinked(dir) && !room.Neighbor[dir].isReserved) emptyNeighborCount++; if (emptyNeighborCount < 2) { if (deadEndDepth < this.Places[i].Depth) deadEnd = i; } if (room.isOnPath) possiblePlacesOnPath.Add(i); else possiblePlacesNotOnPath.Add(i); } } } // Chance to not use deepest corner for unlock place. if (_rng.GetUInt32(100) < 40) deadEnd = -1; if (possiblePlacesOnPath.Count == 0 && possiblePlacesNotOnPath.Count == 0) { // Convert locked place room back to alley if there are no more locked doors. room = this.Places[lockedPlace.PlaceIndex].Room; room.SetDoorType(lockedPlace.DoorDirection, (int)DungeonBlockType.Alley); room.SetPuzzleDoor(null, lockedPlace.DoorDirection); var isLockedRoom = room.DoorType.Any(x => (x == (int)DungeonBlockType.DoorWithLock || x == (int)DungeonBlockType.BossDoor)); if (!isLockedRoom) { room.isLocked = false; room.RoomType = RoomType.Alley; } // Return locked place door to list on available doors. var lockedDoorCandidate = new LockedDoorCandidateNode(room, lockedPlace.DoorDirection, room.RoomIndex); _lockedDoorCandidates.AddFirst(lockedDoorCandidate); throw new PuzzleException("Out of unlock places."); } var possiblePlaces = (possiblePlacesNotOnPath.Count > 0 ? possiblePlacesNotOnPath : possiblePlacesOnPath); var placeIndex = deadEnd != -1 ? deadEnd : possiblePlaces[(int)_rng.GetUInt32((uint)possiblePlaces.Count)]; // Walk down from current place to our path and add new possible doors to this._lockedDoorCandidates room = this.Places[placeIndex].Room; while (room.RoomType != RoomType.Start) { if (room.isOnPath) break; var dir = room.GetIncomingDirection(); room.isOnPath = true; room = room.Neighbor[dir]; // skip reserved doors //if (room.ReservedDoor[Direction.GetOppositeDirection(dir)]) continue; // skip reserved places if (room.isReserved) continue; var lockedDoorCandidate = new LockedDoorCandidateNode(room, Direction.GetOppositeDirection(dir), room.RoomIndex); _lockedDoorCandidates.AddFirst(lockedDoorCandidate); } return placeIndex; }
/// <summary> /// Adds key for lock place to a random monster of this group as a drop. /// </summary> /// <param name="lockPlace"></param> public void AddKeyForLock(PuzzlePlace lockPlace) { var place = lockPlace as PuzzlePlace; if (!place.IsLock) { Log.Warning("PuzzleChest.AddKeyForLock: This place isn't a Lock. ({0})", this.Puzzle.Name); return; } if (this.Count == 0) { Log.Warning("MonsterGroup.AddKeyForLock: No monsters in group."); return; } this.AddDrop(place.Key); }
/// <summary> /// Creates a new place for the puzzle to use. /// </summary> /// <param name="name"></param> /// <returns></returns> public PuzzlePlace NewPlace(string name) { _places[name] = new PuzzlePlace(_section, this, name); return(_places[name]); }
/// <summary> /// Spawns mob in place. /// </summary> /// <param name="place"></param> /// <param name="name"></param> /// <param name="group"></param> /// <param name="spawnPosition"></param> public void AllocateAndSpawnMob(PuzzlePlace place, string name, DungeonMonsterGroupData group, Placement spawnPosition) { var mob = new MonsterGroup(name, this, place, spawnPosition); _monsterGroups.Add(name, mob); mob.Allocate(group); mob.Spawn(); }
/// <summary> /// Adds prop to puzzle in place. /// </summary> /// <param name="place"></param> /// <param name="prop"></param> /// <param name="positionType"></param> public void AddProp(PuzzlePlace place, DungeonProp prop, Placement positionType) { if (this.Region == null) throw new PuzzleException("AddProp outside of OnPuzzleCreate."); var pos = place.GetPosition(positionType); prop.RegionId = this.Region.Id; prop.Info.X = pos[0]; prop.Info.Y = pos[1]; prop.UpdateShapes(); prop.Info.Direction = MabiMath.DegreeToRadian(pos[2]); prop.Behavior += PuzzleEvent; this.Region.AddProp(prop); this.Props[prop.Name] = prop; }
/// <summary> /// Locks place without a key. /// </summary> /// <param name="place"></param> /// <returns></returns> public void LockPlace(PuzzlePlace place) { if (!place.IsLock) throw new PuzzleException("Tried to lock a place that isn't a Lock"); place.LockPlace(); }
/// <summary> /// Locks place and creates and returns a key for it. /// </summary> /// <param name="place"></param> /// <param name="keyName"></param> /// <returns></returns> public Item LockPlace(PuzzlePlace place, string keyName) { if (!place.IsLock) throw new PuzzleException("Tried to lock a place that isn't a Lock"); var doorName = place.GetLockDoor().Name; Item key; if (place.IsBossLock) key = Item.CreateKey(70030, 0xFF0000, doorName); // Boss Room Key else key = Item.CreateKey(70029, place.LockColor, doorName); // Dungeon Room Key place.LockPlace(key); this.Keys[keyName] = key; return key; }
/// <summary> /// Creates a new place for the puzzle to use. /// </summary> /// <param name="name"></param> /// <returns></returns> public PuzzlePlace NewPlace(string name) { _places[name] = new PuzzlePlace(_section, this, name); return _places[name]; }