Exemple #1
0
    public void Spawn(Xml.SpawnGroup spawnGroup)
    {
        if (!Game.World.WorldData.TryGetValue(spawnGroup.MapId, out Map spawnmap))
        {
            GameLog.SpawnWarning($"Map id {spawnGroup.MapId}: not found");
            return;
        }

        //    if (spawnGroup.Spawns.Count == 0)
        //GameLog.SpawnWarning($"Spawngroup {spawnGroup.Name}: no spawns?");

        foreach (var spawn in spawnGroup.Spawns)
        {
            if (spawnmap.SpawnDebug)
            {
                GameLog.SpawnInfo($"Spawngroup {spawnGroup.Name}: {spawn.Name} processing");
            }
            var monsters = spawnmap.Monsters;

            // If the map is disabled, or we don't have a spec for our spawning, or the individual spawn
            // previously had errors and was disabled - continue on
            if (spawnmap.SpawningDisabled || spawn.Disabled)
            {
                GameLog.SpawnWarning($"Spawngroup {spawnGroup.Name}, map {spawnmap.Name}: spawn disabled or map spawning disabled");
                continue;
            }
            if (spawn.Spec is null)
            {
                GameLog.SpawnWarning($"Spawngroup {spawnGroup.Name}, map {spawnmap.Name}: no spec defined for spawning");
                continue;
            }

            var formeval = new FormulaEvaluation()
            {
                Map        = spawnmap, XmlSpawn = spawn,
                SpawnGroup = spawnGroup
            };

            // Set some reasonable defaults.
            //
            // If there is no maximum specified, we consider an appropriate maximum
            // to be 1/10th of total number of map tiles for any given mob (maximum of 30) spawned
            // at a default interval of every 30 seconds, with (maxcount/5) spawned
            // per tick.

            int maxcount       = Math.Min(20, spawnmap.X * spawnmap.Y / 30);
            int interval       = 30;
            int maxPerInterval = maxcount / 5;
            int baseLevel      = 0;

            try
            {
                if (!string.IsNullOrEmpty(spawn.Spec.MaxCount))
                {
                    maxcount = (int)FormulaParser.Eval(spawn.Spec.MaxCount, formeval);
                }
                if (!string.IsNullOrEmpty(spawn.Spec.Interval))
                {
                    interval = (int)FormulaParser.Eval(spawn.Spec.Interval, formeval);
                }
                if (!string.IsNullOrEmpty(spawn.Spec.MaxPerInterval))
                {
                    maxPerInterval = (int)FormulaParser.Eval(spawn.Spec.MaxPerInterval, formeval);
                }

                // If the spawn itself has a level defined, evaluate and use it; otherwise,
                // the spawn group (imported, or in the map itself) should define a base level
                if (string.IsNullOrEmpty(spawn.Base?.Level))
                {
                    baseLevel = (int)FormulaParser.Eval(spawnGroup.BaseLevel, formeval);
                }
                else
                {
                    baseLevel = (int)FormulaParser.Eval(spawn.Base.Level, formeval);
                }
            }
            catch (Exception e)
            {
                spawn.Disabled     = true;
                spawn.ErrorMessage = $"Spawn disabled due to formula evaluation exception: {e}";
                GameLog.SpawnError("Spawn {spawn} on map {map} disabled due to exception: {ex}", spawn.Name, spawnmap.Name, e);
                continue;
            }

            var currentCount = monsters.Where(x => x.Name == spawn.Name).ToList().Count();

            if (currentCount >= maxcount)
            {
                if (spawnmap.SpawnDebug)
                {
                    GameLog.SpawnInfo($"Spawn: {spawnmap.Name}: not spawning {spawn.Name} - mob count is {currentCount}, maximum is {maxcount}");
                }
                continue;
            }

            var since = (DateTime.Now - spawn.LastSpawn).TotalSeconds;

            if (since < interval)
            {
                if (spawnmap.SpawnDebug)
                {
                    GameLog.SpawnInfo($"Spawn: {spawnmap.Name}: not spawning {spawn.Name} - last spawn was {since} ago, interval {interval}");
                }
                continue;
            }

            // Now spawn stuff

            for (var x = 0; x <= Math.Min(maxcount - currentCount, maxPerInterval); x++)
            {
                if (Game.World.WorldData.TryGetValue(spawn.Name, out Xml.Creature creature))
                {
                    var newSpawnLoot = LootBox.CalculateLoot(spawn.Loot);
                    newSpawnLoot += LootBox.CalculateLoot(creature.Loot);
                    newSpawnLoot += LootBox.CalculateLoot(spawnGroup.Loot);

                    var baseMob = new Monster(creature, spawn.Flags, (byte)baseLevel,
                                              newSpawnLoot);

                    if (baseMob.LootableXP == 0)
                    {
                        // If no XP defined, prepopulate based on defaults.
                        // TODO: another place a hardcoded formula should be elsewhere
                        // This is most simply expressed as "amount between mob level and last level times .7%"
                        baseMob.LootableXP = Convert.ToUInt32((Math.Pow(baseMob.Stats.Level, 3) * 250) -
                                                              (Math.Pow(baseMob.Stats.Level - 1, 3) * 250) * 0.007);
                    }
                    // Is this a strong or weak mob?
                    if (spawn.Base.StrongChance > 0 || spawn.Base.WeakChance > 0)
                    {
                        // TODO: potentially refactor with xml control. This defaults to 3-15%
                        // modifications randomly
                        var modifier = Math.Min(.03, Random.Shared.NextDouble() * .15);
                        var mobtype  = Random.Shared.NextDouble() * 100;

                        if (mobtype <= spawn.Base.StrongChance + spawn.Base.WeakChance)
                        {
                            if (spawn.Base.StrongChance >= spawn.Base.WeakChance)
                            {
                                if (mobtype <= spawn.Base.WeakChance)
                                {
                                    baseMob.ApplyModifier(modifier * -1);
                                    if (spawnmap.SpawnDebug)
                                    {
                                        GameLog.SpawnInfo($"Mob is weak: modifier {modifier}");
                                    }
                                }
                                else
                                {
                                    baseMob.ApplyModifier(modifier);
                                    if (spawnmap.SpawnDebug)
                                    {
                                        GameLog.SpawnInfo($"Mob is strong: modifier {modifier}");
                                    }
                                }
                            }
                            else
                            {
                                if (mobtype <= spawn.Base.StrongChance)
                                {
                                    baseMob.ApplyModifier(modifier);
                                    if (spawnmap.SpawnDebug)
                                    {
                                        GameLog.SpawnInfo($"Mob is strong: modifier {modifier}");
                                    }
                                }
                                else
                                {
                                    baseMob.ApplyModifier(modifier * -1);
                                    if (spawnmap.SpawnDebug)
                                    {
                                        GameLog.SpawnInfo($"Mob is weak: modifier {modifier}");
                                    }
                                }
                            }
                        }
                    }

                    var mob    = (Monster)baseMob.Clone();
                    var xcoord = 0;
                    var ycoord = 0;

                    if (spawn.Coordinates.Any())
                    {
                        foreach (var coord in spawn.Coordinates)
                        {
                            if (spawnmap.EntityTree.GetObjects(new Rectangle(coord.X, coord.Y, 1, 1))
                                .Any(e => e is Creature))
                            {
                                continue;
                            }
                            xcoord = coord.X;
                            ycoord = coord.Y;
                        }
                    }
                    else
                    {
                        do
                        {
                            xcoord = Random.Shared.Next(0, spawnmap.X);
                            ycoord = Random.Shared.Next(0, spawnmap.Y);
                        } while (spawnmap.IsWall[xcoord, ycoord]);
                    }
                    baseMob.X = (byte)xcoord;
                    baseMob.Y = (byte)ycoord;

                    if (spawn.Damage != null)
                    {
                        ushort minDmg = 0;
                        ushort maxDmg = 0;

                        try
                        {
                            minDmg = (ushort)FormulaParser.Eval(spawn.Damage.MinDmg, formeval);
                            maxDmg = (ushort)FormulaParser.Eval(spawn.Damage.MaxDmg, formeval);

                            if (minDmg > 0)
                            {
                                // They need some kind of weapon
                                if (Game.World.WorldData.TryGetValueByIndex("monsterblade", out Xml.Item template))
                                {
                                    var newTemplate = template.Clone();
                                    template.Properties.Damage.Small.Min    = minDmg;
                                    template.Properties.Damage.Small.Max    = maxDmg;
                                    template.Properties.Damage.Large.Min    = minDmg;
                                    template.Properties.Damage.Large.Max    = maxDmg;
                                    template.Properties.Physical.Durability = uint.MaxValue;
                                    baseMob.Stats.OffensiveElementOverride  = spawn.Damage.Element switch
                                    {
                                        ElementType.RandomFour => (ElementType)Random.Shared.Next(1, 5),  // earth/fire/wind/water
                                        ElementType.RandomEight => (ElementType)Random.Shared.Next(1, 9), // Above plus light/dark/metal/wood
                                        ElementType.Random => (ElementType)Random.Shared.Next(1, 10),     // Above plus undead
                                        _ => spawn.Damage.Element
                                    };

                                    var item = new ItemObject(newTemplate);
                                    baseMob.Equipment.Insert((byte)ItemSlots.Weapon, item);
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            spawn.Disabled     = true;
                            spawn.ErrorMessage = $"Spawn disabled due to formula evaluation exception: {e}";
                            GameLog.SpawnError("Spawn {spawn} on map {map} disabled due to exception: {ex}", spawn.Name, spawnmap.Name, e);
                            continue;
                        }
                    }
                    if (spawn.Defense != null)
                    {
                        sbyte Ac = 0;
                        sbyte Mr = 0;

                        try
                        {
                            Ac = (sbyte)FormulaParser.Eval(spawn.Defense.Ac, formeval);
                            Mr = (sbyte)FormulaParser.Eval(spawn.Defense.Mr, formeval);
                        }
                        catch (Exception e)
                        {
                            spawn.Disabled     = true;
                            spawn.ErrorMessage = $"Spawn disabled due to formula evaluation exception: {e}";
                            GameLog.SpawnError("Spawn {spawn} on map {map} disabled due to exception: {ex}", spawn.Name, spawnmap.Name, e);
                            continue;
                        }
                        baseMob.Stats.BonusAc = Ac;
                        baseMob.Stats.BonusMr = Mr;
                        baseMob.Stats.DefensiveElementOverride = spawn.Defense.Element switch
                        {
                            ElementType.RandomFour => (ElementType)Random.Shared.Next(1, 5),  // earth/fire/wind/water
                            ElementType.RandomEight => (ElementType)Random.Shared.Next(1, 9), // Above plus light/dark/metal/wood
                            ElementType.Random => (ElementType)Random.Shared.Next(1, 10),     // Above plus undead
                            _ => spawn.Damage.Element
                        };
                    }
                    foreach (var cookie in spawn.SetCookies)
                    {
                        baseMob.SetCookie(cookie.Name, cookie.Value);
                    }
                    SpawnMonster(baseMob, spawnmap);
                }
                else
                {
                    GameLog.SpawnWarning("Map {map}: Spawn {spawn} not found", spawnmap.Name, spawn.Name);
                }
            }
            spawn.LastSpawn = DateTime.Now;
        }
    }
Exemple #2
0
        public override void Attack(Direction direction, Castable castObject = null, Creature target = null)
        {
            if (target != null)
            {
                var damage = castObject.Effects.Damage;

                if (damage.Formula == null) //will need to be expanded. also will need to account for damage scripts
                {
                    var simple = damage.Simple;
                    var damageType = EnumUtil.ParseEnum<Enums.DamageType>(damage.Type.ToString(), Enums.DamageType.Magical);
                    Random rand = new Random();
                    var dmg = rand.Next(Convert.ToInt32(simple.Min), Convert.ToInt32(simple.Max)); //these need to be set to integers as attributes. note to fix.
                    target.Damage(dmg, OffensiveElement, damageType, this);
                }
                else
                {
                    var formula = damage.Formula;
                    var damageType = EnumUtil.ParseEnum<Enums.DamageType>(damage.Type.ToString(), Enums.DamageType.Magical);
                    FormulaParser parser = new FormulaParser(this, castObject, target);
                    var dmg = parser.Eval(formula);
                    target.Damage(dmg, OffensiveElement, damageType, this);
                }
            }
        }