public void Start() { foreach (var spawngroup in _spawnGroups) { foreach (var spawnmap in spawngroup.Maps) { if (Game.World.WorldData.TryGetValueByIndex(spawnmap.Name, out Map map)) { spawnmap.Id = map.Id; spawnmap.LastSpawn = DateTime.MinValue; } else { spawnmap.Disabled = true; GameLog.SpawnError("Specified map {map} not found", spawnmap.Name); } } } while (true) { if (World.ControlMessageQueue.IsCompleted) { break; } foreach (var spawnGroup in _spawnGroups) { if (!spawnGroup.Disabled) { Spawn(spawnGroup); } } Thread.Sleep(5000); } }
public void Spawn(Xml.SpawnGroup spawnGroup) { foreach (var map in spawnGroup.Maps) { if (map.Disabled) { continue; } try { var spawnMap = Game.World.WorldData.Get <Map>(map.Id); GameLog.SpawnDebug("Spawn: calculating {0}", spawnMap.Name); var monsterList = spawnMap.Objects.OfType <Monster>().ToList(); var monsterCount = monsterList.Count; // If there is no limit specified, we want a reasonable limit, which we consider to be 1/10th of total // number of map tiles var spawnLimit = map.Limit == 0 ? (spawnMap.X * spawnMap.Y) / 10 : map.Limit; if (monsterCount > spawnLimit) { if (spawnMap.SpawnDebug) { GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, mob count is {monsterCount}, limit is {spawnLimit}"); } continue; } var since = DateTime.Now - map.LastSpawn; if (since.TotalSeconds < map.Interval) { if (spawnMap.SpawnDebug) { GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, last spawn was {since.TotalSeconds} ago, interval {map.Interval}"); } continue; } map.LastSpawn = DateTime.Now; var thisSpawn = _random.Next(map.MinSpawn, map.MaxSpawn + 1); GameLog.SpawnInfo($"Spawn: {map.Name}: spawning {thisSpawn} mobs "); for (var i = 0; i < thisSpawn; i++) { var spawn = spawnGroup.Spawns.PickRandom(true); if (spawn == null) { GameLog.SpawnError("Spawngroup empty, skipping"); break; } var creature = _creatures.FirstOrDefault(x => x.Name == spawn.Base); if (creature is default(Xml.Creature)) { GameLog.SpawnError($"Base monster {spawn.Base} not found"); break; } var newSpawnLoot = LootBox.CalculateLoot(spawn); if (spawnMap.SpawnDebug) { GameLog.SpawnInfo("Spawn {name}, map {map}: {Xp} xp, {Gold} gold, items {Items}", spawn.Base, map.Name, newSpawnLoot.Xp, newSpawnLoot.Gold, string.Join(',', newSpawnLoot.Items)); } var baseMob = new Monster(creature, spawn, map.Id, newSpawnLoot); var mob = (Monster)baseMob.Clone(); var xcoord = 0; var ycoord = 0; if (map.Coordinates.Count > 0) { // TODO: optimize / improve foreach (var coord in map.Coordinates) { if (spawnMap.EntityTree.GetObjects(new System.Drawing.Rectangle(coord.X, coord.Y, 1, 1)).Where(e => e is Creature).Count() == 0) { xcoord = coord.X; ycoord = coord.Y; break; } } } else { do { xcoord = _random.Next(0, spawnMap.X); ycoord = _random.Next(0, spawnMap.Y); } while (spawnMap.IsWall[xcoord, ycoord]); } mob.X = (byte)xcoord; mob.Y = (byte)ycoord; if (spawnMap.SpawnDebug) { GameLog.SpawnInfo($"Spawn: spawning {mob.Name} on {spawnMap.Name}"); } SpawnMonster(mob, spawnMap); } } catch (Exception e) { Game.ReportException(e); GameLog.SpawnError(e, "Spawngroup {Filename}: disabled map {Name} due to error", spawnGroup.Filename, map.Name); map.Disabled = true; continue; } } }
/// <summary> /// Given a list of items in a loot table, return the items awarded. /// </summary> /// <param name="list">LootTableItemList containing items</param> /// <returns>List of items</returns> public static List <string> CalculateItems(Xml.LootTableItemList list) { // Ordinarily, return one item from the list. var rolls = CalculateSuccessfulRolls(list.Rolls, list.Chance); var loot = new List <Xml.LootItem>(); var itemList = new List <ItemObject>(); // First, process any "always" items, which always drop when the container fires foreach (var item in list.Item.Where(i => i.Always)) { GameLog.SpawnInfo("Processing loot: added always item {item}", item.Value); loot.Add(item); } var totalRolls = 0; // Process the rest of the rolls now do { // Get a random item from the list var item = list.Item.Where(i => !i.Always).PickRandom(); // As soon as we get an item from our table, we've "rolled"; we'll add another roll below if needed rolls--; // Check uniqueness. If something has already dropped, don't drop it again, and reroll if (item.Unique && loot.Contains(item)) { rolls++; GameLog.SpawnInfo("Processing loot: added duplicate unique item {item}. Rerolling", item.Value); continue; } // Check max quantity. If it is exceeded, reroll if (item.Max > 0 && loot.Where(i => i.Value == item.Value).Count() >= item.Max) { rolls++; GameLog.SpawnInfo("Processing loot: added over max quantity for {item}. Rerolling", item.Value); continue; } // If quantity and uniqueness are good, add the item loot.Add(item); GameLog.SpawnInfo("Processing loot: added {item}", item.Value); totalRolls++; // As a check against something incredibly stupid in XML, we only allow a maximum of // 100 rolls if (totalRolls > 100) { GameLog.SpawnInfo("Processing loot: maximum number of rolls exceeded..?"); throw new LootRecursionError("Maximum number of rolls (100) exceeded!"); } }while (rolls > 0); // Now we have the canonical droplist, which needs resolving into Items foreach (var lootitem in loot) { // Does the base item exist? var xmlItemList = Game.World.WorldData.FindItem(lootitem.Value); // Don't handle the edge case of multiple genders .... yet if (xmlItemList.Count != 0) { var xmlItem = xmlItemList.First(); // Handle variants. // If multiple variants are specified, we pick one at random if (lootitem.Variants.Count() > 0) { var lootedVariant = lootitem.Variants.PickRandom(); if (xmlItem.Variants.TryGetValue(lootedVariant, out List <Xml.Item> variantItems)) { itemList.Add(Game.World.CreateItem(variantItems.PickRandom().Id)); } else { GameLog.SpawnError("Spawn loot calculation: variant group {name} not found", lootedVariant); } } else { itemList.Add(Game.World.CreateItem(xmlItem.Id)); } } else { GameLog.SpawnError("Spawn loot calculation: item {name} not found!", lootitem.Value); } } // We store loot as strings inside mobs to avoid having tens or hundreds of thousands of ItemObjects or // Items lying around - they're made into real objects at the time of mob death if (itemList.Count > 0) { return(itemList.Where(x => x != null).Select(y => y.Name).ToList()); } return(new List <String>()); }
public void Spawn(Xml.SpawnGroup spawnGroup) { foreach (var map in spawnGroup.Maps) { if (map.Disabled) { continue; } try { var spawnMap = Game.World.WorldData.Get <Map>(map.Id); GameLog.SpawnDebug("Spawn: calculating {0}", spawnMap.Name); var monsterList = spawnMap.Objects.OfType <Monster>().ToList(); var monsterCount = monsterList.Count; // If there is no limit specified, we want a reasonable limit, which we consider to be 1/10th of total // number of map tiles var spawnLimit = map.Limit == 0 ? (spawnMap.X * spawnMap.Y) / 10 : map.Limit; if (monsterCount > spawnLimit) { if (spawnMap.SpawnDebug) { GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, mob count is {monsterCount}, limit is {spawnLimit}"); } continue; } var since = DateTime.Now - map.LastSpawn; if (since.TotalSeconds < map.Interval) { if (spawnMap.SpawnDebug) { GameLog.SpawnInfo($"Spawn: {map.Name}: not spawning, last spawn was {since.TotalSeconds} ago, interval {map.Interval}"); } continue; } map.LastSpawn = DateTime.Now; var thisSpawn = _random.Next(map.MinSpawn, map.MaxSpawn); GameLog.SpawnInfo($"Spawn: {map.Name}: spawning {thisSpawn} mobs "); for (var i = 0; i < thisSpawn; i++) { var idx = _random.Next(0, spawnGroup.Spawns.Count - 1); var spawn = spawnGroup.Spawns[idx]; var creature = _creatures.Single(x => x.Name == spawn.Base); var newSpawnLoot = LootBox.CalculateLoot(spawn); if (spawnMap.SpawnDebug) { GameLog.SpawnInfo("Spawn {name}, map {map}: {Xp} xp, {Gold} gold, items {Items}", spawn.Base, map.Name, newSpawnLoot.Xp, newSpawnLoot.Gold, string.Join(',', newSpawnLoot.Items)); } var baseMob = new Monster(creature, spawn, map.Id, newSpawnLoot); var mob = (Monster)baseMob.Clone(); var xcoord = 0; var ycoord = 0; do { xcoord = _random.Next(0, spawnMap.X - 1); ycoord = _random.Next(0, spawnMap.Y - 1); } while (spawnMap.IsWall[xcoord, ycoord]); mob.X = (byte)xcoord; mob.Y = (byte)ycoord; if (spawnMap.SpawnDebug) { GameLog.SpawnInfo($"Spawn: spawning {mob.Name} on {spawnMap.Name}"); } SpawnMonster(mob, spawnMap); } } catch (Exception e) { GameLog.SpawnError(e, "Spawngroup {Filename}: disabled map {Name} due to error", spawnGroup.Filename, map.Name); map.Disabled = true; continue; } } }