From 241c603c539c211b45e4064109c567769594e884 Mon Sep 17 00:00:00 2001 From: MrShadow Date: Fri, 14 Nov 2025 20:23:07 -0300 Subject: [PATCH] Adds 'whereis' chatcommand --- .../Commands/ChatCommands.Handlers.cs | 103 ++++++++++++++++++ .../World/Spawning/MonsterSpawner.cs | 5 + system/conf/commands.conf | 3 + 3 files changed, 111 insertions(+) diff --git a/src/ZoneServer/Commands/ChatCommands.Handlers.cs b/src/ZoneServer/Commands/ChatCommands.Handlers.cs index de7a5e38c..fbdd41a93 100644 --- a/src/ZoneServer/Commands/ChatCommands.Handlers.cs +++ b/src/ZoneServer/Commands/ChatCommands.Handlers.cs @@ -21,6 +21,8 @@ using Yggdrasil.Network.Communication; using Yggdrasil.Util; using Yggdrasil.Util.Commands; +using Melia.Zone.World.Maps; +using Melia.Zone.World.Spawning; namespace Melia.Zone.Commands { @@ -72,6 +74,7 @@ public ChatCommands() this.Add("iteminfo", "", "Displays information about an item.", this.HandleItemInfo); this.Add("monsterinfo", "", "Displays information about a monster.", this.HandleMonsterInfo); this.Add("whodrops", "", "Finds monsters that drop a given item", this.HandleWhoDrops); + this.Add("whereis", "", "Finds the maps where a monster spawns.", this.HandleWhereIs); this.Add("go", "", "Warps to certain pre-defined destinations.", this.HandleGo); this.Add("goto", "", "Warps to another character.", this.HandleGoTo); this.Add("recall", "", "Warps another character back.", this.HandleRecall); @@ -1127,6 +1130,106 @@ private CommandResult HandleWhoDrops(Character sender, Character target, string return CommandResult.Okay; } + + /// + /// Finds the maps where a monster respawns + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleWhereIs(Character sender, Character target, string message, string commandName, Arguments args) + { + if (args.Count == 0) + return CommandResult.InvalidArgument; + + var search = string.Join(" ", args.GetAll()).ToLower(); + + // Find the monster data that matches the search term + var monsters = ZoneServer.Instance.Data.MonsterDb.FindAllPreferExact(search); + if (monsters.Count == 0) + { + sender.ServerMessage(Localization.Get("No monsters found for '{0}'."), search); + return CommandResult.Okay; + } + + // Get all spawners in the world + var spawners = ZoneServer.Instance.World.GetSpawners() + .OfType(); + + var results = new List<(MonsterData Monster, Map Map, MonsterSpawner Spawner)>(); + + foreach (var monster in monsters) + { + // Find all spawners that spawn this monster + foreach (var spawner in spawners) + { + if (spawner is MonsterSpawner monsterSpawner) + { + // Skip if this spawner doesn't spawn our monster + if (!ZoneServer.Instance.Data.MonsterDb.TryFind(monsterSpawner.MonsterData.Id, out var spawnerMonster) || + spawnerMonster.Id != monster.Id) + continue; + + // Get the map from the spawn areas + if (!ZoneServer.Instance.World.TryGetSpawnAreas(spawner.SpawnPointsIdent, out var spawnAreas)) + continue; + + foreach (var area in spawnAreas.GetAll()) + { + if (!ZoneServer.Instance.World.TryGetMap(area.Map.Id, out var map)) + continue; + + results.Add((monster, map, monsterSpawner)); + } + } + } + } + + if (results.Count == 0) + { + sender.ServerMessage(Localization.Get("No spawn locations found for '{0}'."), search); + return CommandResult.Okay; + } + + // Order results by map name + results = results.OrderBy(x => x.Map.ClassName).ToList(); + + // Display results + foreach (var result in results) + { + var spawner = result.Spawner; + var respawnInfo = ""; + + // Add respawn time info if it exists + if (spawner.MinRespawnDelay != TimeSpan.Zero || spawner.MaxRespawnDelay != TimeSpan.Zero) + { + if (spawner.MinRespawnDelay == spawner.MaxRespawnDelay) + respawnInfo = $" [Delay: {spawner.MinRespawnDelay.TotalSeconds:0}s]"; + else + respawnInfo = $" [Delay: {spawner.MinRespawnDelay.TotalSeconds:0}s~{spawner.MaxRespawnDelay.TotalSeconds:0}s]"; + } + + var response = string.Format( + "{0} ({1}) - {2} ({3}) - Quantity: {4}~{5}{6}", + result.Monster.Name, + result.Monster.ClassName, + result.Map?.Data?.Name ?? "Unknown", + result.Map.ClassName, + spawner.MinAmount, + spawner.MaxAmount, + respawnInfo + ); + + sender.ServerMessage(response); + } + + return CommandResult.Okay; + } + + /// /// Warps target to a pre-defined location. /// diff --git a/src/ZoneServer/World/Spawning/MonsterSpawner.cs b/src/ZoneServer/World/Spawning/MonsterSpawner.cs index 83c09c24e..1ed62a8fb 100644 --- a/src/ZoneServer/World/Spawning/MonsterSpawner.cs +++ b/src/ZoneServer/World/Spawning/MonsterSpawner.cs @@ -70,6 +70,11 @@ public class MonsterSpawner : IUpdateable /// public int FlexAmount { get; private set; } + /// + /// Returns the monster data currently being used for this spawner. + /// + public MonsterData MonsterData { get { return _monsterData; } } + /// /// Returns the amount of monsters currently spawned. /// diff --git a/system/conf/commands.conf b/system/conf/commands.conf index 8bcb8605b..ebe8568e6 100644 --- a/system/conf/commands.conf +++ b/system/conf/commands.conf @@ -113,6 +113,9 @@ monsterinfo : 50,-1 // Searches for monsters that drop a given item whodrops : 50,-1 +// Searches for spawns of monsters in any maps +whereis: 50,-1 + // Warps player to pre-defined destinations go : 50,50