fix: Fixes memory leak in virtual mounts and codegens specials (#1476)

This commit is contained in:
Kamron Batman 2023-08-25 22:42:23 -07:00 committed by GitHub
parent 701deb53c1
commit fc3aa13b8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 2349 additions and 2505 deletions

View file

@ -1,4 +1,5 @@
using System; using System;
using ModernUO.Serialization;
using Server.Factions.AI; using Server.Factions.AI;
using Server.Items; using Server.Items;
using Server.Mobiles; using Server.Mobiles;
@ -485,45 +486,43 @@ namespace Server.Factions
} }
} }
public class VirtualMountItem : Item, IMountItem [SerializationGenerator(0, false)]
public partial class VirtualMountItem : Item, IMountItem
{ {
private readonly VirtualMount m_Mount; private VirtualMount _mount;
[SerializableField(0)]
private Mobile _rider;
public VirtualMountItem(Mobile mob) : base(0x3EA0) public VirtualMountItem(Mobile mob) : base(0x3EA0)
{ {
Layer = Layer.Mount; Layer = Layer.Mount;
Rider = mob; Rider = mob;
m_Mount = new VirtualMount(this); _mount = new VirtualMount(this);
} }
public VirtualMountItem(Serial serial) : base(serial) => m_Mount = new VirtualMount(this); public IMount Mount => _mount;
public Mobile Rider { get; private set; } [AfterDeserialization]
private void AfterDeserialization()
public IMount Mount => m_Mount;
public override void Serialize(IGenericWriter writer)
{ {
base.Serialize(writer); if (_rider?.Deleted != false)
writer.Write(0); // version
writer.Write(Rider);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
Rider = reader.ReadEntity<Mobile>();
if (Rider == null)
{ {
Delete(); Delete();
} }
else
{
_mount = new VirtualMount(this);
}
}
public override DeathMoveResult OnParentDeath(Mobile parent)
{
_mount = null;
Delete();
return DeathMoveResult.RemainEquipped;
} }
} }
} }

View file

@ -0,0 +1,11 @@
{
"version": 0,
"type": "Server.Factions.VirtualMountItem",
"properties": [
{
"name": "Rider",
"type": "Server.Mobile",
"rule": "SerializableInterfaceMigrationRule"
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Barracoon"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.BaseChampion"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.BaseShieldGuard"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.CapturedHordeMinion"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.ChaosGuard"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.DarkGuardian"
}

View file

@ -0,0 +1,28 @@
{
"version": 1,
"type": "Server.Mobiles.Harrower",
"properties": [
{
"name": "TrueForm",
"type": "bool",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "GateItem",
"type": "Server.Item",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "Tentacles",
"type": "System.Collections.Generic.List\u003CServer.Mobiles.HarrowerTentacles\u003E",
"rule": "ListMigrationRule",
"ruleArguments": [
"Server.Mobiles.HarrowerTentacles",
"SerializableInterfaceMigrationRule"
]
}
]
}

View file

@ -0,0 +1,11 @@
{
"version": 0,
"type": "Server.Mobiles.HarrowerTentacles",
"properties": [
{
"name": "Harrower",
"type": "Server.Mobile",
"rule": "SerializableInterfaceMigrationRule"
}
]
}

View file

@ -0,0 +1,19 @@
{
"version": 0,
"type": "Server.Mobiles.LordOaks",
"properties": [
{
"name": "Queen",
"type": "Server.Mobiles.BaseCreature",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "SpawnedQueen",
"type": "bool",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Mephitis"
}

View file

@ -0,0 +1,11 @@
{
"version": 0,
"type": "Server.Mobiles.Neira.VirtualMountItem",
"properties": [
{
"name": "Rider",
"type": "Server.Mobile",
"rule": "SerializableInterfaceMigrationRule"
}
]
}

View file

@ -0,0 +1,4 @@
{
"version": 1,
"type": "Server.Mobiles.Neira"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.OrderGuard"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Rikktor"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Semidar"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Serado"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.ServantOfSemidar"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Silvani"
}

View file

@ -1,251 +1,234 @@
using System; using System;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Items; using Server.Items;
using Server.Spells.Fifth; using Server.Spells.Fifth;
using Server.Spells.Seventh; using Server.Spells.Seventh;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Barracoon : BaseChampion
{ {
public class Barracoon : BaseChampion [Constructible]
public Barracoon() : base(AIType.AI_Melee)
{ {
[Constructible] Title = "the piper";
public Barracoon() : base(AIType.AI_Melee) Body = 0x190;
Hue = 0x83EC;
SetStr(305, 425);
SetDex(72, 150);
SetInt(505, 750);
SetHits(4200);
SetStam(102, 300);
SetDamage(25, 35);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 60, 70);
SetResistance(ResistanceType.Fire, 50, 60);
SetResistance(ResistanceType.Cold, 50, 60);
SetResistance(ResistanceType.Poison, 40, 50);
SetResistance(ResistanceType.Energy, 40, 50);
SetSkill(SkillName.MagicResist, 100.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 97.6, 100.0);
Fame = 22500;
Karma = -22500;
VirtualArmor = 70;
AddItem(new FancyShirt(Utility.RandomGreenHue()));
AddItem(new LongPants(Utility.RandomYellowHue()));
AddItem(new JesterHat(Utility.RandomPinkHue()));
AddItem(new Cloak(Utility.RandomPinkHue()));
AddItem(new Sandals());
HairItemID = 0x203B; // Short Hair
HairHue = 0x94;
}
public override ChampionSkullType SkullType => ChampionSkullType.Greed;
public override Type[] UniqueList => new[] { typeof(FangOfRactus) };
public override Type[] SharedList => new[]
{
typeof(EmbroideredOakLeafCloak),
typeof(DjinnisRing),
typeof(DetectiveBoots),
typeof(GuantletsOfAnger)
};
public override Type[] DecorativeList => new[] { typeof(SwampTile), typeof(MonsterStatuette) };
public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Slime };
public override string DefaultName => "Barracoon";
public override bool AlwaysMurderer => true;
public override bool AutoDispel => true;
public override double AutoDispelChance => 1.0;
public override bool BardImmune => !Core.SE;
public override bool Unprovokable => Core.SE;
public override bool Uncalmable => Core.SE;
public override Poison PoisonImmune => Poison.Deadly;
public override bool ShowFameTitle => false;
public override bool ClickTitle => false;
public override void GenerateLoot()
{
AddLoot(LootPack.UltraRich, 3);
}
public void Polymorph(Mobile m)
{
if (!m.CanBeginAction<PolymorphSpell>() || !m.CanBeginAction<IncognitoSpell>() || m.IsBodyMod)
{ {
Title = "the piper"; return;
Body = 0x190;
Hue = 0x83EC;
SetStr(305, 425);
SetDex(72, 150);
SetInt(505, 750);
SetHits(4200);
SetStam(102, 300);
SetDamage(25, 35);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 60, 70);
SetResistance(ResistanceType.Fire, 50, 60);
SetResistance(ResistanceType.Cold, 50, 60);
SetResistance(ResistanceType.Poison, 40, 50);
SetResistance(ResistanceType.Energy, 40, 50);
SetSkill(SkillName.MagicResist, 100.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 97.6, 100.0);
Fame = 22500;
Karma = -22500;
VirtualArmor = 70;
AddItem(new FancyShirt(Utility.RandomGreenHue()));
AddItem(new LongPants(Utility.RandomYellowHue()));
AddItem(new JesterHat(Utility.RandomPinkHue()));
AddItem(new Cloak(Utility.RandomPinkHue()));
AddItem(new Sandals());
HairItemID = 0x203B; // Short Hair
HairHue = 0x94;
} }
public Barracoon(Serial serial) : base(serial) var mount = m.Mount;
if (mount != null)
{ {
mount.Rider = null;
} }
public override ChampionSkullType SkullType => ChampionSkullType.Greed; if (m.Mounted)
public override Type[] UniqueList => new[] { typeof(FangOfRactus) };
public override Type[] SharedList => new[]
{ {
typeof(EmbroideredOakLeafCloak), return;
typeof(DjinnisRing),
typeof(DetectiveBoots),
typeof(GuantletsOfAnger)
};
public override Type[] DecorativeList => new[] { typeof(SwampTile), typeof(MonsterStatuette) };
public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Slime };
public override string DefaultName => "Barracoon";
public override bool AlwaysMurderer => true;
public override bool AutoDispel => true;
public override double AutoDispelChance => 1.0;
public override bool BardImmune => !Core.SE;
public override bool Unprovokable => Core.SE;
public override bool Uncalmable => Core.SE;
public override Poison PoisonImmune => Poison.Deadly;
public override bool ShowFameTitle => false;
public override bool ClickTitle => false;
public override void GenerateLoot()
{
AddLoot(LootPack.UltraRich, 3);
} }
public void Polymorph(Mobile m) if (m.BeginAction<PolymorphSpell>())
{ {
if (!m.CanBeginAction<PolymorphSpell>() || !m.CanBeginAction<IncognitoSpell>() || m.IsBodyMod) var disarm = m.FindItemOnLayer(Layer.OneHanded);
if (disarm?.Movable == true)
{ {
return; m.AddToBackpack(disarm);
} }
var mount = m.Mount; disarm = m.FindItemOnLayer(Layer.TwoHanded);
if (mount != null) if (disarm?.Movable == true)
{ {
mount.Rider = null; m.AddToBackpack(disarm);
} }
if (m.Mounted) m.BodyMod = 42;
{ m.HueMod = 0;
return;
}
if (m.BeginAction<PolymorphSpell>()) new ExpirePolymorphTimer(m).Start();
{ }
var disarm = m.FindItemOnLayer(Layer.OneHanded); }
if (disarm?.Movable == true) public void SpawnRatmen(Mobile target)
{
var map = Map;
if (map == null)
{
return;
}
var eable = GetMobilesInRange<BaseCreature>(10);
var rats = 0;
foreach (var m in eable)
{
if (m is Ratman or RatmanArcher or RatmanMage)
{
rats++;
if (rats >= 16)
{ {
m.AddToBackpack(disarm); eable.Free();
} return;
disarm = m.FindItemOnLayer(Layer.TwoHanded);
if (disarm?.Movable == true)
{
m.AddToBackpack(disarm);
}
m.BodyMod = 42;
m.HueMod = 0;
new ExpirePolymorphTimer(m).Start();
}
}
public void SpawnRatmen(Mobile target)
{
var map = Map;
if (map == null)
{
return;
}
var eable = GetMobilesInRange<BaseCreature>(10);
var rats = 0;
foreach (var m in eable)
{
if (m is Ratman or RatmanArcher or RatmanMage)
{
rats++;
if (rats >= 16)
{
eable.Free();
return;
}
} }
} }
eable.Free();
PlaySound(0x3D);
rats = Utility.RandomMinMax(3, 6);
for (var i = 0; i < rats; ++i)
{
var rat = Utility.Random(5) switch
{
2 => (BaseCreature)new RatmanArcher(),
3 => new RatmanArcher(),
4 => new RatmanMage(),
_ => new Ratman()
};
rat.Team = Team;
rat.MoveToWorld(map.GetRandomNearbyLocation(Location), map);
rat.Combatant = target;
}
} }
public void DoSpecialAbility(Mobile target) eable.Free();
PlaySound(0x3D);
rats = Utility.RandomMinMax(3, 6);
for (var i = 0; i < rats; ++i)
{ {
if (target?.Deleted != false) // sanity var rat = Utility.Random(5) switch
{ {
return; 2 => (BaseCreature)new RatmanArcher(),
} 3 => new RatmanArcher(),
4 => new RatmanMage(),
_ => new Ratman()
};
if (Utility.RandomDouble() < 0.6) // 60% chance to polymorph attacker into a ratman rat.Team = Team;
{ rat.MoveToWorld(map.GetRandomNearbyLocation(Location), map);
Polymorph(target); rat.Combatant = target;
} }
}
if (Utility.RandomDouble() < 0.2) // 20% chance to spawn more ratmen public void DoSpecialAbility(Mobile target)
{ {
SpawnRatmen(target); if (target?.Deleted != false) // sanity
} {
return;
if (Hits < 500 && !IsBodyMod) // Baracoon is low on life, polymorph into a ratman
{
Polymorph(this);
}
} }
public override void OnGotMeleeAttack(Mobile attacker, int damage) if (Utility.RandomDouble() < 0.6) // 60% chance to polymorph attacker into a ratman
{ {
base.OnGotMeleeAttack(attacker, damage); Polymorph(target);
DoSpecialAbility(attacker);
} }
public override void OnGaveMeleeAttack(Mobile defender, int damage) if (Utility.RandomDouble() < 0.2) // 20% chance to spawn more ratmen
{ {
base.OnGaveMeleeAttack(defender, damage); SpawnRatmen(target);
DoSpecialAbility(defender);
} }
public override void Serialize(IGenericWriter writer) if (Hits < 500 && !IsBodyMod) // Baracoon is low on life, polymorph into a ratman
{ {
base.Serialize(writer); Polymorph(this);
}
}
writer.Write(0); // version public override void OnGotMeleeAttack(Mobile attacker, int damage)
{
base.OnGotMeleeAttack(attacker, damage);
DoSpecialAbility(attacker);
}
public override void OnGaveMeleeAttack(Mobile defender, int damage)
{
base.OnGaveMeleeAttack(defender, damage);
DoSpecialAbility(defender);
}
private class ExpirePolymorphTimer : Timer
{
private readonly Mobile m_Owner;
public ExpirePolymorphTimer(Mobile owner) : base(TimeSpan.FromMinutes(3.0))
{
m_Owner = owner;
} }
public override void Deserialize(IGenericReader reader) protected override void OnTick()
{ {
base.Deserialize(reader); if (!m_Owner.CanBeginAction<PolymorphSpell>())
var version = reader.ReadInt();
}
private class ExpirePolymorphTimer : Timer
{
private readonly Mobile m_Owner;
public ExpirePolymorphTimer(Mobile owner) : base(TimeSpan.FromMinutes(3.0))
{ {
m_Owner = owner; m_Owner.BodyMod = 0;
} m_Owner.HueMod = -1;
m_Owner.EndAction<PolymorphSpell>();
protected override void OnTick()
{
if (!m_Owner.CanBeginAction<PolymorphSpell>())
{
m_Owner.BodyMod = 0;
m_Owner.HueMod = -1;
m_Owner.EndAction<PolymorphSpell>();
}
} }
} }
} }

View file

@ -1,97 +1,245 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Engines.Virtues; using Server.Engines.Virtues;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public abstract partial class BaseChampion : BaseCreature
{ {
public abstract class BaseChampion : BaseCreature public BaseChampion(AIType aiType, FightMode mode = FightMode.Closest) : base(aiType, mode, 18)
{ {
public BaseChampion(AIType aiType, FightMode mode = FightMode.Closest) : base(aiType, mode, 18) }
public override bool CanMoveOverObstacles => true;
public override bool CanDestroyObstacles => true;
public abstract ChampionSkullType SkullType { get; }
public abstract Type[] UniqueList { get; }
public abstract Type[] SharedList { get; }
public abstract Type[] DecorativeList { get; }
public abstract MonsterStatuetteType[] StatueTypes { get; }
public virtual bool NoGoodies => false;
public Item GetArtifact() =>
Utility.RandomDouble() switch
{ {
< 0.05 => CreateArtifact(UniqueList),
< 0.15 => CreateArtifact(SharedList),
< 0.30 => CreateArtifact(DecorativeList),
_ => null
};
public Item CreateArtifact(Type[] list)
{
if (list.Length == 0)
{
return null;
} }
public BaseChampion(Serial serial) : base(serial) var type = list.RandomElement();
var artifact = Loot.Construct(type);
if (StatueTypes.Length > 0 && artifact is MonsterStatuette statuette)
{ {
statuette.Type = StatueTypes.RandomElement();
statuette.LootType = LootType.Regular;
} }
public override bool CanMoveOverObstacles => true; return artifact;
public override bool CanDestroyObstacles => true; }
public abstract ChampionSkullType SkullType { get; } private static PowerScroll CreateRandomPowerScroll()
{
public abstract Type[] UniqueList { get; } var level = Utility.RandomDouble() switch
public abstract Type[] SharedList { get; }
public abstract Type[] DecorativeList { get; }
public abstract MonsterStatuetteType[] StatueTypes { get; }
public virtual bool NoGoodies => false;
public override void Serialize(IGenericWriter writer)
{ {
base.Serialize(writer); < 0.05 => 20,
< 0.4 => 15,
_ => 10
};
writer.Write(0); // version return PowerScroll.CreateRandomNoCraft(level, level);
}
public void GivePowerScrolls()
{
if (Map != Map.Felucca)
{
return;
} }
public override void Deserialize(IGenericReader reader) var toGive = new List<Mobile>();
var rights = GetLootingRights(DamageEntries, HitsMax);
for (var i = rights.Count - 1; i >= 0; --i)
{ {
base.Deserialize(reader); var ds = rights[i];
var version = reader.ReadInt(); if (ds.m_HasRight)
}
public Item GetArtifact() =>
Utility.RandomDouble() switch
{ {
< 0.05 => CreateArtifact(UniqueList), toGive.Add(ds.m_Mobile);
< 0.15 => CreateArtifact(SharedList), }
< 0.30 => CreateArtifact(DecorativeList), }
_ => null
};
public Item CreateArtifact(Type[] list) if (toGive.Count == 0)
{ {
if (list.Length == 0) return;
}
for (var i = 0; i < toGive.Count; i++)
{
var m = toGive[i];
if (m is not PlayerMobile pm)
{ {
return null; continue;
} }
var type = list.RandomElement(); var gainedPath = false;
var artifact = Loot.Construct(type); const int pointsToGain = 800;
if (StatueTypes.Length > 0 && artifact is MonsterStatuette statuette) if (VirtueSystem.Award(pm, VirtueName.Valor, pointsToGain, ref gainedPath))
{ {
statuette.Type = StatueTypes.RandomElement(); if (gainedPath)
statuette.LootType = LootType.Regular; {
} m.SendLocalizedMessage(1054032); // You have gained a path in Valor!
}
else
{
m.SendLocalizedMessage(1054030); // You have gained in Valor!
}
return artifact; // No delay on Valor gains
}
} }
private static PowerScroll CreateRandomPowerScroll() // Randomize
{ toGive.Shuffle();
var level = Utility.RandomDouble() switch
{
< 0.05 => 20,
< 0.4 => 15,
_ => 10
};
return PowerScroll.CreateRandomNoCraft(level, level); for (var i = 0; i < 6; ++i)
{
var m = toGive[i % toGive.Count];
var ps = CreateRandomPowerScroll();
GivePowerScrollTo(m, ps);
}
}
public static void GivePowerScrollTo(Mobile m, PowerScroll ps)
{
if (ps == null || m == null) // sanity
{
return;
} }
public void GivePowerScrolls() m.SendLocalizedMessage(1049524); // You have received a scroll of power!
if (!Core.SE || m.Alive)
{ {
if (Map != Map.Felucca) m.AddToBackpack(ps);
}
else
{
if (m.Corpse?.Deleted == false)
{ {
return; m.Corpse.DropItem(ps);
}
else
{
m.AddToBackpack(ps);
}
}
if (m is not PlayerMobile pm)
{
return;
}
var prot = JusticeVirtue.GetProtector(pm);
if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal ||
!JusticeVirtue.CheckMapRegion(pm, prot))
{
return;
}
var chance = VirtueSystem.GetLevel(prot, VirtueName.Justice) switch
{
VirtueLevel.Seeker => 60,
VirtueLevel.Follower => 80,
VirtueLevel.Knight => 100,
_ => 0
};
if (chance > 0 && chance > Utility.Random(100))
{
var powerScroll = new PowerScroll(ps.Skill, ps.Value);
prot.SendLocalizedMessage(1049368); // You have been rewarded for your dedication to Justice!
if (!Core.SE || prot.Alive)
{
prot.AddToBackpack(powerScroll);
}
else if (prot.Corpse?.Deleted == false)
{
prot.Corpse.DropItem(powerScroll);
}
else
{
prot.AddToBackpack(powerScroll);
}
}
}
public override bool OnBeforeDeath()
{
if (!NoKillAwards)
{
GivePowerScrolls();
if (NoGoodies)
{
return base.OnBeforeDeath();
} }
var toGive = new List<Mobile>(); var map = Map;
if (map != null)
{
for (var x = -12; x <= 12; ++x)
{
for (var y = -12; y <= 12; ++y)
{
var dist = Math.Sqrt(x * x + y * y);
if (dist <= 12)
{
new GoodiesTimer(map, X + x, Y + y).Start();
}
}
}
}
}
return base.OnBeforeDeath();
}
public override void OnDeath(Container c)
{
if (Map == Map.Felucca)
{
// TODO: Confirm SE change or AoS one too?
var rights = GetLootingRights(DamageEntries, HitsMax); var rights = GetLootingRights(DamageEntries, HitsMax);
var toGive = new List<Mobile>();
for (var i = rights.Count - 1; i >= 0; --i) for (var i = rights.Count - 1; i >= 0; --i)
{ {
@ -103,266 +251,101 @@ namespace Server.Mobiles
} }
} }
if (toGive.Count == 0) if (toGive.Count > 0)
{ {
return; toGive.RandomElement().AddToBackpack(new ChampionSkull(SkullType));
}
for (var i = 0; i < toGive.Count; i++)
{
var m = toGive[i];
if (m is not PlayerMobile pm)
{
continue;
}
var gainedPath = false;
const int pointsToGain = 800;
if (VirtueSystem.Award(pm, VirtueName.Valor, pointsToGain, ref gainedPath))
{
if (gainedPath)
{
m.SendLocalizedMessage(1054032); // You have gained a path in Valor!
}
else
{
m.SendLocalizedMessage(1054030); // You have gained in Valor!
}
// No delay on Valor gains
}
}
// Randomize
toGive.Shuffle();
for (var i = 0; i < 6; ++i)
{
var m = toGive[i % toGive.Count];
var ps = CreateRandomPowerScroll();
GivePowerScrollTo(m, ps);
}
}
public static void GivePowerScrollTo(Mobile m, PowerScroll ps)
{
if (ps == null || m == null) // sanity
{
return;
}
m.SendLocalizedMessage(1049524); // You have received a scroll of power!
if (!Core.SE || m.Alive)
{
m.AddToBackpack(ps);
} }
else else
{ {
if (m.Corpse?.Deleted == false) c.DropItem(new ChampionSkull(SkullType));
}
}
base.OnDeath(c);
}
private class GoodiesTimer : Timer
{
private readonly Map m_Map;
private readonly int m_X;
private readonly int m_Y;
public GoodiesTimer(Map map, int x, int y) : base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0))
{
m_Map = map;
m_X = x;
m_Y = y;
}
protected override void OnTick()
{
var z = m_Map.GetAverageZ(m_X, m_Y);
var canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false);
for (var i = -3; !canFit && i <= 3; ++i)
{
canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false);
if (canFit)
{ {
m.Corpse.DropItem(ps); z += i;
}
else
{
m.AddToBackpack(ps);
} }
} }
if (m is not PlayerMobile pm) if (!canFit)
{ {
return; return;
} }
var prot = JusticeVirtue.GetProtector(pm); var g = new Gold(500, 1000);
if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map);
!JusticeVirtue.CheckMapRegion(pm, prot))
if (Utility.RandomDouble() < 0.05)
{ {
return; return;
} }
var chance = VirtueSystem.GetLevel(prot, VirtueName.Justice) switch switch (Utility.Random(3))
{ {
VirtueLevel.Seeker => 60, case 0: // Fire column
VirtueLevel.Follower => 80,
VirtueLevel.Knight => 100,
_ => 0
};
if (chance > 0 && chance > Utility.Random(100))
{
var powerScroll = new PowerScroll(ps.Skill, ps.Value);
prot.SendLocalizedMessage(1049368); // You have been rewarded for your dedication to Justice!
if (!Core.SE || prot.Alive)
{
prot.AddToBackpack(powerScroll);
}
else if (prot.Corpse?.Deleted == false)
{
prot.Corpse.DropItem(powerScroll);
}
else
{
prot.AddToBackpack(powerScroll);
}
}
}
public override bool OnBeforeDeath()
{
if (!NoKillAwards)
{
GivePowerScrolls();
if (NoGoodies)
{
return base.OnBeforeDeath();
}
var map = Map;
if (map != null)
{
for (var x = -12; x <= 12; ++x)
{ {
for (var y = -12; y <= 12; ++y) Effects.SendLocationParticles(
{ EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration),
var dist = Math.Sqrt(x * x + y * y); 0x3709,
10,
30,
5052
);
Effects.PlaySound(g, 0x208);
if (dist <= 12) break;
{
new GoodiesTimer(map, X + x, Y + y).Start();
}
}
} }
} case 1: // Explosion
}
return base.OnBeforeDeath();
}
public override void OnDeath(Container c)
{
if (Map == Map.Felucca)
{
// TODO: Confirm SE change or AoS one too?
var rights = GetLootingRights(DamageEntries, HitsMax);
var toGive = new List<Mobile>();
for (var i = rights.Count - 1; i >= 0; --i)
{
var ds = rights[i];
if (ds.m_HasRight)
{ {
toGive.Add(ds.m_Mobile); Effects.SendLocationParticles(
EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration),
0x36BD,
20,
10,
5044
);
Effects.PlaySound(g, 0x307);
break;
} }
} case 2: // Ball of fire
if (toGive.Count > 0)
{
toGive.RandomElement().AddToBackpack(new ChampionSkull(SkullType));
}
else
{
c.DropItem(new ChampionSkull(SkullType));
}
}
base.OnDeath(c);
}
private class GoodiesTimer : Timer
{
private readonly Map m_Map;
private readonly int m_X;
private readonly int m_Y;
public GoodiesTimer(Map map, int x, int y) : base(TimeSpan.FromSeconds(Utility.RandomDouble() * 10.0))
{
m_Map = map;
m_X = x;
m_Y = y;
}
protected override void OnTick()
{
var z = m_Map.GetAverageZ(m_X, m_Y);
var canFit = m_Map.CanFit(m_X, m_Y, z, 6, false, false);
for (var i = -3; !canFit && i <= 3; ++i)
{
canFit = m_Map.CanFit(m_X, m_Y, z + i, 6, false, false);
if (canFit)
{ {
z += i; Effects.SendLocationParticles(
EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration),
0x36FE,
10,
10,
5052
);
break;
} }
}
if (!canFit)
{
return;
}
var g = new Gold(500, 1000);
g.MoveToWorld(new Point3D(m_X, m_Y, z), m_Map);
if (Utility.RandomDouble() < 0.05)
{
return;
}
switch (Utility.Random(3))
{
case 0: // Fire column
{
Effects.SendLocationParticles(
EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration),
0x3709,
10,
30,
5052
);
Effects.PlaySound(g, 0x208);
break;
}
case 1: // Explosion
{
Effects.SendLocationParticles(
EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration),
0x36BD,
20,
10,
5044
);
Effects.PlaySound(g, 0x307);
break;
}
case 2: // Ball of fire
{
Effects.SendLocationParticles(
EffectItem.Create(g.Location, g.Map, EffectItem.DefaultDuration),
0x36FE,
10,
10,
5052
);
break;
}
}
} }
} }
} }

View file

@ -1,166 +1,163 @@
using ModernUO.Serialization;
using Server.Guilds; using Server.Guilds;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public abstract partial class BaseShieldGuard : BaseCreature
{ {
public abstract class BaseShieldGuard : BaseCreature public BaseShieldGuard() : base(AIType.AI_Melee, FightMode.Aggressor, 14)
{ {
public BaseShieldGuard() : base(AIType.AI_Melee, FightMode.Aggressor, 14) InitStats(1000, 1000, 1000);
Title = "the guard";
SetSpeed(0.5, 2.0);
SpeechHue = Utility.RandomDyedHue();
Hue = Race.Human.RandomSkinHue();
if (Female = Utility.RandomBool())
{ {
InitStats(1000, 1000, 1000); Body = 0x191;
Title = "the guard"; Name = NameList.RandomName("female");
SetSpeed(0.5, 2.0); AddItem(new FemalePlateChest());
SpeechHue = Utility.RandomDyedHue(); AddItem(new PlateArms());
Hue = Race.Human.RandomSkinHue(); AddItem(new PlateLegs());
if (Female = Utility.RandomBool()) switch (Utility.Random(2))
{ {
Body = 0x191; case 0:
Name = NameList.RandomName("female"); {
AddItem(new FemalePlateChest());
AddItem(new PlateArms());
AddItem(new PlateLegs());
switch (Utility.Random(2))
{
case 0:
AddItem(new Doublet(Utility.RandomNondyedHue())); AddItem(new Doublet(Utility.RandomNondyedHue()));
break; break;
case 1: }
case 1:
{
AddItem(new BodySash(Utility.RandomNondyedHue())); AddItem(new BodySash(Utility.RandomNondyedHue()));
break; break;
} }
}
switch (Utility.Random(2)) switch (Utility.Random(2))
{ {
case 0: case 0:
{
AddItem(new Skirt(Utility.RandomNondyedHue())); AddItem(new Skirt(Utility.RandomNondyedHue()));
break; break;
case 1: }
case 1:
{
AddItem(new Kilt(Utility.RandomNondyedHue())); AddItem(new Kilt(Utility.RandomNondyedHue()));
break; break;
} }
}
}
else
{
Body = 0x190;
Name = NameList.RandomName("male");
AddItem(new PlateChest());
AddItem(new PlateArms());
AddItem(new PlateLegs());
switch (Utility.Random(3))
{
case 0:
{
AddItem(new Doublet(Utility.RandomNondyedHue()));
break;
}
case 1:
{
AddItem(new Tunic(Utility.RandomNondyedHue()));
break;
}
case 2:
{
AddItem(new BodySash(Utility.RandomNondyedHue()));
break;
}
}
}
Utility.AssignRandomHair(this);
if (Utility.RandomBool())
{
Utility.AssignRandomFacialHair(this, HairHue);
}
var weapon = new VikingSword();
weapon.Movable = false;
AddItem(weapon);
var shield = Shield;
shield.Movable = false;
AddItem(shield);
PackGold(250, 500);
Skills.Anatomy.Base = 120.0;
Skills.Tactics.Base = 120.0;
Skills.Swords.Base = 120.0;
Skills.MagicResist.Base = 120.0;
Skills.DetectHidden.Base = 100.0;
}
public abstract int Keyword { get; }
public abstract BaseShield Shield { get; }
public abstract int SignupNumber { get; }
public abstract GuildType Type { get; }
public override bool HandlesOnSpeech(Mobile from)
{
if (from.InRange(Location, 2))
{
return true;
}
return base.HandlesOnSpeech(from);
}
public override void OnSpeech(SpeechEventArgs e)
{
if (!e.Handled && e.HasKeyword(Keyword) && e.Mobile.InRange(Location, 2))
{
e.Handled = true;
var from = e.Mobile;
if (from.Guild is not Guild g || g.Type != Type)
{
Say(SignupNumber);
} }
else else
{ {
Body = 0x190; var pack = from.Backpack;
Name = NameList.RandomName("male"); var shield = Shield;
var twoHanded = from.FindItemOnLayer(Layer.TwoHanded);
AddItem(new PlateChest()); if (pack?.FindItemByType(shield.GetType()) != null ||
AddItem(new PlateArms()); twoHanded != null && shield.GetType().IsInstanceOfType(twoHanded))
AddItem(new PlateLegs());
switch (Utility.Random(3))
{ {
case 0: Say(1007110); // Why dost thou ask about virtue guards when thou art one?
AddItem(new Doublet(Utility.RandomNondyedHue())); shield.Delete();
break;
case 1:
AddItem(new Tunic(Utility.RandomNondyedHue()));
break;
case 2:
AddItem(new BodySash(Utility.RandomNondyedHue()));
break;
} }
} else if (from.PlaceInBackpack(shield))
Utility.AssignRandomHair(this);
if (Utility.RandomBool())
{
Utility.AssignRandomFacialHair(this, HairHue);
}
var weapon = new VikingSword();
weapon.Movable = false;
AddItem(weapon);
var shield = Shield;
shield.Movable = false;
AddItem(shield);
PackGold(250, 500);
Skills.Anatomy.Base = 120.0;
Skills.Tactics.Base = 120.0;
Skills.Swords.Base = 120.0;
Skills.MagicResist.Base = 120.0;
Skills.DetectHidden.Base = 100.0;
}
public BaseShieldGuard(Serial serial) : base(serial)
{
}
public abstract int Keyword { get; }
public abstract BaseShield Shield { get; }
public abstract int SignupNumber { get; }
public abstract GuildType Type { get; }
public override bool HandlesOnSpeech(Mobile from)
{
if (from.InRange(Location, 2))
{
return true;
}
return base.HandlesOnSpeech(from);
}
public override void OnSpeech(SpeechEventArgs e)
{
if (!e.Handled && e.HasKeyword(Keyword) && e.Mobile.InRange(Location, 2))
{
e.Handled = true;
var from = e.Mobile;
if (from.Guild is not Guild g || g.Type != Type)
{ {
Say(SignupNumber); Say(Utility.Random(1007101, 5));
Say(1007139); // I see you are in need of our shield, Here you go.
from.AddToBackpack(shield);
} }
else else
{ {
var pack = from.Backpack; from.SendLocalizedMessage(502868); // Your backpack is too full.
var shield = Shield; shield.Delete();
var twoHanded = from.FindItemOnLayer(Layer.TwoHanded);
if (pack?.FindItemByType(shield.GetType()) != null ||
twoHanded != null && shield.GetType().IsInstanceOfType(twoHanded))
{
Say(1007110); // Why dost thou ask about virtue guards when thou art one?
shield.Delete();
}
else if (from.PlaceInBackpack(shield))
{
Say(Utility.Random(1007101, 5));
Say(1007139); // I see you are in need of our shield, Here you go.
from.AddToBackpack(shield);
}
else
{
from.SendLocalizedMessage(502868); // Your backpack is too full.
shield.Delete();
}
} }
} }
base.OnSpeech(e);
} }
public override void Serialize(IGenericWriter writer) base.OnSpeech(e);
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }
} }

View file

@ -1,30 +1,14 @@
namespace Server.Mobiles using ModernUO.Serialization;
namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class CapturedHordeMinion : HordeMinion
{ {
public class CapturedHordeMinion : HordeMinion [Constructible]
{ public CapturedHordeMinion() => FightMode = FightMode.None;
[Constructible]
public CapturedHordeMinion() => FightMode = FightMode.None;
public CapturedHordeMinion(Serial serial) : base(serial) public override bool InitialInnocent => true;
{
}
public override bool InitialInnocent => true; public override bool CanBeDamaged() => false;
public override bool CanBeDamaged() => false;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
}
} }

View file

@ -1,38 +1,21 @@
using ModernUO.Serialization;
using Server.Guilds; using Server.Guilds;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class ChaosGuard : BaseShieldGuard
{ {
public class ChaosGuard : BaseShieldGuard [Constructible]
public ChaosGuard()
{ {
[Constructible]
public ChaosGuard()
{
}
public ChaosGuard(Serial serial) : base(serial)
{
}
public override int Keyword => 0x22; // *chaos shield*
public override BaseShield Shield => new ChaosShield();
public override int SignupNumber => 1007140; // Sign up with a guild of chaos if thou art interested.
public override GuildType Type => GuildType.Chaos;
public override bool BardImmune => true;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }
public override int Keyword => 0x22; // *chaos shield*
public override BaseShield Shield => new ChaosShield();
public override int SignupNumber => 1007140; // Sign up with a guild of chaos if thou art interested.
public override GuildType Type => GuildType.Chaos;
public override bool BardImmune => true;
} }

View file

@ -1,79 +1,62 @@
using ModernUO.Serialization;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class DarkGuardian : BaseCreature
{ {
public class DarkGuardian : BaseCreature [Constructible]
public DarkGuardian() : base(AIType.AI_Mage)
{ {
[Constructible] Body = 78;
public DarkGuardian() : base(AIType.AI_Mage) BaseSoundID = 0x3E9;
{
Body = 78;
BaseSoundID = 0x3E9;
SetStr(125, 150); SetStr(125, 150);
SetDex(100, 120); SetDex(100, 120);
SetInt(200, 235); SetInt(200, 235);
SetHits(150, 180); SetHits(150, 180);
SetDamage(43, 48); SetDamage(43, 48);
SetDamageType(ResistanceType.Physical, 10); SetDamageType(ResistanceType.Physical, 10);
SetDamageType(ResistanceType.Cold, 40); SetDamageType(ResistanceType.Cold, 40);
SetDamageType(ResistanceType.Energy, 50); SetDamageType(ResistanceType.Energy, 50);
SetResistance(ResistanceType.Physical, 40, 50); SetResistance(ResistanceType.Physical, 40, 50);
SetResistance(ResistanceType.Fire, 20, 45); SetResistance(ResistanceType.Fire, 20, 45);
SetResistance(ResistanceType.Cold, 50, 60); SetResistance(ResistanceType.Cold, 50, 60);
SetResistance(ResistanceType.Poison, 20, 45); SetResistance(ResistanceType.Poison, 20, 45);
SetResistance(ResistanceType.Energy, 30, 40); SetResistance(ResistanceType.Energy, 30, 40);
SetSkill(SkillName.EvalInt, 40.1, 50); SetSkill(SkillName.EvalInt, 40.1, 50);
SetSkill(SkillName.Magery, 50.1, 60.0); SetSkill(SkillName.Magery, 50.1, 60.0);
SetSkill(SkillName.Meditation, 85.1, 95.0); SetSkill(SkillName.Meditation, 85.1, 95.0);
SetSkill(SkillName.MagicResist, 50.1, 70.0); SetSkill(SkillName.MagicResist, 50.1, 70.0);
SetSkill(SkillName.Tactics, 50.1, 70.0); SetSkill(SkillName.Tactics, 50.1, 70.0);
Fame = 5000; Fame = 5000;
Karma = -5000; Karma = -5000;
VirtualArmor = 50; VirtualArmor = 50;
PackNecroReg(15, 25); PackNecroReg(15, 25);
PackItem(new DaemonBone(30)); PackItem(new DaemonBone(30));
} }
public DarkGuardian(Serial serial) : base(serial) public override string CorpseName => "a dark guardians' corpse";
{ public override string DefaultName => "a dark guardian";
}
public override string CorpseName => "a dark guardians' corpse"; public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead;
public override string DefaultName => "a dark guardian";
public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead; public override int TreasureMapLevel => 2;
public override bool BleedImmune => true;
public override Poison PoisonImmune => Poison.Lethal;
public override bool Unprovokable => true;
public override int TreasureMapLevel => 2; public override void GenerateLoot()
public override bool BleedImmune => true; {
public override Poison PoisonImmune => Poison.Lethal; AddLoot(LootPack.Rich);
public override bool Unprovokable => true; AddLoot(LootPack.MedScrolls, 2);
public override void GenerateLoot()
{
AddLoot(LootPack.Rich);
AddLoot(LootPack.MedScrolls, 2);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,185 +1,161 @@
using System; using System;
using ModernUO.Serialization;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class HarrowerTentacles : BaseCreature
{ {
public class HarrowerTentacles : BaseCreature private DrainTimer _timer;
[SerializableField(0)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private Mobile _harrower;
[Constructible]
public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee)
{ {
private DrainTimer m_Timer; _harrower = harrower;
Body = 129;
[Constructible] SetStr(901, 1000);
public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee) SetDex(126, 140);
SetInt(1001, 1200);
SetHits(541, 600);
SetDamage(13, 20);
SetDamageType(ResistanceType.Physical, 20);
SetDamageType(ResistanceType.Fire, 20);
SetDamageType(ResistanceType.Cold, 20);
SetDamageType(ResistanceType.Poison, 20);
SetDamageType(ResistanceType.Energy, 20);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 35, 45);
SetResistance(ResistanceType.Cold, 35, 45);
SetResistance(ResistanceType.Poison, 35, 45);
SetResistance(ResistanceType.Energy, 35, 45);
SetSkill(SkillName.Meditation, 100.0);
SetSkill(SkillName.MagicResist, 120.1, 140.0);
SetSkill(SkillName.Swords, 90.1, 100.0);
SetSkill(SkillName.Tactics, 90.1, 100.0);
SetSkill(SkillName.Wrestling, 90.1, 100.0);
Fame = 15000;
Karma = -15000;
VirtualArmor = 60;
_timer = new DrainTimer(this);
_timer.Start();
PackReg(50);
PackNecroReg(15, 75);
}
public override string CorpseName => "a tentacles corpse";
public override string DefaultName => "tentacles of the harrower";
public override bool AutoDispel => true;
public override bool Unprovokable => true;
public override Poison PoisonImmune => Poison.Lethal;
public override bool DisallowAllMoves => true;
public override void CheckReflect(Mobile caster, ref bool reflect)
{
reflect = true;
}
public override int GetIdleSound() => 0x101;
public override int GetAngerSound() => 0x5E;
public override int GetDeathSound() => 0x1C2;
public override int GetAttackSound() => -1;
public override int GetHurtSound() => 0x289;
public override void GenerateLoot()
{
AddLoot(LootPack.FilthyRich, 2);
AddLoot(LootPack.MedScrolls, 3);
AddLoot(LootPack.HighScrolls, 2);
}
[AfterDeserialization]
private void AfterDeserialization()
{
_timer = new DrainTimer(this);
_timer.Start();
}
public override void OnAfterDelete()
{
_timer?.Stop();
_timer = null;
base.OnAfterDelete();
}
private class DrainTimer : Timer
{
private readonly HarrowerTentacles m_Owner;
public DrainTimer(HarrowerTentacles owner) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0))
{ {
Harrower = harrower; m_Owner = owner;
Body = 129;
SetStr(901, 1000);
SetDex(126, 140);
SetInt(1001, 1200);
SetHits(541, 600);
SetDamage(13, 20);
SetDamageType(ResistanceType.Physical, 20);
SetDamageType(ResistanceType.Fire, 20);
SetDamageType(ResistanceType.Cold, 20);
SetDamageType(ResistanceType.Poison, 20);
SetDamageType(ResistanceType.Energy, 20);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 35, 45);
SetResistance(ResistanceType.Cold, 35, 45);
SetResistance(ResistanceType.Poison, 35, 45);
SetResistance(ResistanceType.Energy, 35, 45);
SetSkill(SkillName.Meditation, 100.0);
SetSkill(SkillName.MagicResist, 120.1, 140.0);
SetSkill(SkillName.Swords, 90.1, 100.0);
SetSkill(SkillName.Tactics, 90.1, 100.0);
SetSkill(SkillName.Wrestling, 90.1, 100.0);
Fame = 15000;
Karma = -15000;
VirtualArmor = 60;
m_Timer = new DrainTimer(this);
m_Timer.Start();
PackReg(50);
PackNecroReg(15, 75);
} }
public HarrowerTentacles(Serial serial) : base(serial) protected override void OnTick()
{ {
} if (m_Owner.Deleted)
public override string CorpseName => "a tentacles corpse";
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Harrower { get; set; }
public override string DefaultName => "tentacles of the harrower";
public override bool AutoDispel => true;
public override bool Unprovokable => true;
public override Poison PoisonImmune => Poison.Lethal;
public override bool DisallowAllMoves => true;
public override void CheckReflect(Mobile caster, ref bool reflect)
{
reflect = true;
}
public override int GetIdleSound() => 0x101;
public override int GetAngerSound() => 0x5E;
public override int GetDeathSound() => 0x1C2;
public override int GetAttackSound() => -1;
public override int GetHurtSound() => 0x289;
public override void GenerateLoot()
{
AddLoot(LootPack.FilthyRich, 2);
AddLoot(LootPack.MedScrolls, 3);
AddLoot(LootPack.HighScrolls, 2);
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(Harrower);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
switch (version)
{ {
case 0: Stop();
{ return;
Harrower = reader.ReadEntity<Mobile>();
m_Timer = new DrainTimer(this);
m_Timer.Start();
break;
}
}
}
public override void OnAfterDelete()
{
m_Timer?.Stop();
m_Timer = null;
base.OnAfterDelete();
}
private class DrainTimer : Timer
{
private readonly HarrowerTentacles m_Owner;
public DrainTimer(HarrowerTentacles owner) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0))
{
m_Owner = owner;
} }
protected override void OnTick() var eable = m_Owner.GetMobilesInRange<Mobile>(9);
foreach (var m in eable)
{ {
if (m_Owner.Deleted) if (m == m_Owner || !m_Owner.CanBeHarmful(m))
{ {
Stop(); continue;
return;
} }
var eable = m_Owner.GetMobilesInRange<Mobile>(9); if (m.Player && !m.Alive)
foreach (var m in eable)
{ {
if (m == m_Owner || !m_Owner.CanBeHarmful(m)) continue;
{
continue;
}
if (m.Player && !m.Alive)
{
continue;
}
if (m is BaseCreature bc && !(bc.Controlled || bc.IsAnimatedDead || bc.Summoned || bc.Team != m_Owner.Team))
{
continue;
}
m_Owner.DoHarmful(m);
m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist);
m.PlaySound(0x1F1);
var drain = Utility.RandomMinMax(14, 30);
m_Owner.Hits += drain;
if (m_Owner.Harrower != null)
{
m_Owner.Harrower.Hits += drain;
}
m.Damage(drain, m_Owner);
} }
eable.Free(); if (m is BaseCreature bc && !(bc.Controlled || bc.IsAnimatedDead || bc.Summoned || bc.Team != m_Owner.Team))
{
continue;
}
m_Owner.DoHarmful(m);
m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist);
m.PlaySound(0x1F1);
var drain = Utility.RandomMinMax(14, 30);
m_Owner.Hits += drain;
if (m_Owner.Harrower != null)
{
m_Owner.Harrower.Hits += drain;
}
m.Damage(drain, m_Owner);
} }
eable.Free();
} }
} }
} }

View file

@ -1,13 +1,18 @@
using System; using System;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Items; using Server.Items;
namespace Server.Mobiles; namespace Server.Mobiles;
public class LordOaks : BaseChampion [SerializationGenerator(0, false)]
public partial class LordOaks : BaseChampion
{ {
private BaseCreature m_Queen; [SerializableField(0)]
private bool m_SpawnedQueen; private BaseCreature _queen;
[SerializableField(1)]
private bool _spawnedQueen;
[Constructible] [Constructible]
public LordOaks() : base(AIType.AI_Mage, FightMode.Evil) public LordOaks() : base(AIType.AI_Mage, FightMode.Evil)
@ -45,10 +50,6 @@ public class LordOaks : BaseChampion
VirtualArmor = 100; VirtualArmor = 100;
} }
public LordOaks(Serial serial) : base(serial)
{
}
public override ChampionSkullType SkullType => ChampionSkullType.Enlightenment; public override ChampionSkullType SkullType => ChampionSkullType.Enlightenment;
public override Type[] UniqueList => new[] { typeof(OrcChieftainHelm) }; public override Type[] UniqueList => new[] { typeof(OrcChieftainHelm) };
@ -109,20 +110,20 @@ public class LordOaks : BaseChampion
return false; return false;
} }
if (!m_SpawnedQueen) if (!_spawnedQueen)
{ {
Say(1042153); // Come forth my queen! Say(1042153); // Come forth my queen!
m_Queen = new Silvani { Team = Team }; _queen = new Silvani { Team = Team };
m_Queen.MoveToWorld(Location, Map); _queen.MoveToWorld(Location, Map);
m_SpawnedQueen = true; _spawnedQueen = true;
return true; return true;
} }
if (m_Queen?.Deleted != false) if (_queen?.Deleted != false)
{ {
m_Queen = null; _queen = null;
return false; return false;
} }
@ -154,32 +155,4 @@ public class LordOaks : BaseChampion
attacker.Stam -= Utility.Random(20, 10); attacker.Stam -= Utility.Random(20, 10);
attacker.Mana -= Utility.Random(20, 10); attacker.Mana -= Utility.Random(20, 10);
} }
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(m_Queen);
writer.Write(m_SpawnedQueen);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Queen = reader.ReadEntity<BaseCreature>();
m_SpawnedQueen = reader.ReadBool();
break;
}
}
}
} }

View file

@ -1,92 +1,75 @@
using System; using System;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Mephitis : BaseChampion
{ {
public class Mephitis : BaseChampion [Constructible]
public Mephitis() : base(AIType.AI_Melee)
{ {
[Constructible] Body = 173;
public Mephitis() : base(AIType.AI_Melee) BaseSoundID = 0x183;
{
Body = 173;
BaseSoundID = 0x183;
SetStr(505, 1000); SetStr(505, 1000);
SetDex(102, 300); SetDex(102, 300);
SetInt(402, 600); SetInt(402, 600);
SetHits(3000); SetHits(3000);
SetStam(105, 600); SetStam(105, 600);
SetDamage(21, 33); SetDamage(21, 33);
SetDamageType(ResistanceType.Physical, 50); SetDamageType(ResistanceType.Physical, 50);
SetDamageType(ResistanceType.Poison, 50); SetDamageType(ResistanceType.Poison, 50);
SetResistance(ResistanceType.Physical, 75, 80); SetResistance(ResistanceType.Physical, 75, 80);
SetResistance(ResistanceType.Fire, 60, 70); SetResistance(ResistanceType.Fire, 60, 70);
SetResistance(ResistanceType.Cold, 60, 70); SetResistance(ResistanceType.Cold, 60, 70);
SetResistance(ResistanceType.Poison, 100); SetResistance(ResistanceType.Poison, 100);
SetResistance(ResistanceType.Energy, 60, 70); SetResistance(ResistanceType.Energy, 60, 70);
SetSkill(SkillName.MagicResist, 70.7, 140.0); SetSkill(SkillName.MagicResist, 70.7, 140.0);
SetSkill(SkillName.Tactics, 97.6, 100.0); SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 97.6, 100.0); SetSkill(SkillName.Wrestling, 97.6, 100.0);
Fame = 22500; Fame = 22500;
Karma = -22500; Karma = -22500;
VirtualArmor = 80; VirtualArmor = 80;
} }
public Mephitis(Serial serial) : base(serial) public override ChampionSkullType SkullType => ChampionSkullType.Venom;
{
}
public override ChampionSkullType SkullType => ChampionSkullType.Venom; public override Type[] UniqueList => new[] { typeof(Calm) };
public override Type[] UniqueList => new[] { typeof(Calm) }; public override Type[] SharedList => new[]
{
typeof(OblivionsNeedle), typeof(ANecromancerShroud), typeof(EmbroideredOakLeafCloak),
typeof(TheMostKnowledgePerson)
};
public override Type[] SharedList => new[] public override Type[] DecorativeList => new[] { typeof(Web), typeof(MonsterStatuette) };
{
typeof(OblivionsNeedle), typeof(ANecromancerShroud), typeof(EmbroideredOakLeafCloak),
typeof(TheMostKnowledgePerson)
};
public override Type[] DecorativeList => new[] { typeof(Web), typeof(MonsterStatuette) }; public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Spider };
public override MonsterStatuetteType[] StatueTypes => new[] { MonsterStatuetteType.Spider }; public override string DefaultName => "Mephitis";
public override string DefaultName => "Mephitis"; public override Poison PoisonImmune => Poison.Lethal;
public override Poison HitPoison => Poison.Lethal;
public override Poison PoisonImmune => Poison.Lethal; public override void GenerateLoot()
public override Poison HitPoison => Poison.Lethal; {
AddLoot(LootPack.UltraRich, 4);
}
public override void GenerateLoot() public override void OnGotMeleeAttack(Mobile attacker, int damage)
{ {
AddLoot(LootPack.UltraRich, 4); base.OnGotMeleeAttack(attacker, damage);
}
public override void OnGotMeleeAttack(Mobile attacker, int damage) // TODO: Web ability
{
base.OnGotMeleeAttack(attacker, damage);
// TODO: Web ability
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }
} }

View file

@ -1,303 +1,283 @@
using System; using System;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(1, false)]
public partial class Neira : BaseChampion
{ {
public class Neira : BaseChampion private const double SpeedBoostScalar = 1.2;
private bool _speedBoost;
[Constructible]
public Neira() : base(AIType.AI_Mage)
{ {
private const double SpeedBoostScalar = 1.2; Title = "the necromancer";
Body = 401;
Hue = 0x83EC;
private bool m_SpeedBoost; SetStr(305, 425);
SetDex(72, 150);
SetInt(505, 750);
[Constructible] SetHits(4800);
public Neira() : base(AIType.AI_Mage) SetStam(102, 300);
SetDamage(25, 35);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 25, 30);
SetResistance(ResistanceType.Fire, 35, 45);
SetResistance(ResistanceType.Cold, 50, 60);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 20, 30);
SetSkill(SkillName.EvalInt, 120.0);
SetSkill(SkillName.Magery, 120.0);
SetSkill(SkillName.Meditation, 120.0);
SetSkill(SkillName.MagicResist, 150.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 97.6, 100.0);
Fame = 22500;
Karma = -22500;
VirtualArmor = 30;
Female = true;
Item shroud = new HoodedShroudOfShadows();
shroud.Movable = false;
AddItem(shroud);
var weapon = new Scimitar
{ {
Title = "the necromancer"; Skill = SkillName.Wrestling,
Body = 401; Hue = 38,
Hue = 0x83EC; Movable = false
SetStr(305, 425);
SetDex(72, 150);
SetInt(505, 750);
SetHits(4800);
SetStam(102, 300);
SetDamage(25, 35);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 25, 30);
SetResistance(ResistanceType.Fire, 35, 45);
SetResistance(ResistanceType.Cold, 50, 60);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 20, 30);
SetSkill(SkillName.EvalInt, 120.0);
SetSkill(SkillName.Magery, 120.0);
SetSkill(SkillName.Meditation, 120.0);
SetSkill(SkillName.MagicResist, 150.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 97.6, 100.0);
Fame = 22500;
Karma = -22500;
VirtualArmor = 30;
Female = true;
Item shroud = new HoodedShroudOfShadows();
shroud.Movable = false;
AddItem(shroud);
var weapon = new Scimitar();
weapon.Skill = SkillName.Wrestling;
weapon.Hue = 38;
weapon.Movable = false;
AddItem(weapon);
// new SkeletalMount().Rider = this;
AddItem(new VirtualMountItem(this));
}
public Neira(Serial serial) : base(serial)
{
}
public override ChampionSkullType SkullType => ChampionSkullType.Death;
public override Type[] UniqueList => new[] { typeof(ShroudOfDeciet) };
public override Type[] SharedList => new[]
{
typeof(ANecromancerShroud),
typeof(CaptainJohnsHat)
}; };
public override Type[] DecorativeList => new[] { typeof(WallBlood), typeof(TatteredAncientMummyWrapping) }; AddItem(weapon);
public override MonsterStatuetteType[] StatueTypes => Array.Empty<MonsterStatuetteType>(); // new SkeletalMount().Rider = this;
AddItem(new VirtualMountItem(this));
}
public override string DefaultName => "Neira"; public override ChampionSkullType SkullType => ChampionSkullType.Death;
public override bool AlwaysMurderer => true; public override Type[] UniqueList => new[] { typeof(ShroudOfDeciet) };
public override bool BardImmune => !Core.SE;
public override bool Unprovokable => Core.SE;
public override bool Uncalmable => Core.SE;
public override Poison PoisonImmune => Poison.Deadly;
public override bool ShowFameTitle => false; public override Type[] SharedList => new[]
public override bool ClickTitle => false; {
typeof(ANecromancerShroud),
public override void GenerateLoot() typeof(CaptainJohnsHat)
};
public override Type[] DecorativeList => new[] { typeof(WallBlood), typeof(TatteredAncientMummyWrapping) };
public override MonsterStatuetteType[] StatueTypes => Array.Empty<MonsterStatuetteType>();
public override string DefaultName => "Neira";
public override bool AlwaysMurderer => true;
public override bool BardImmune => !Core.SE;
public override bool Unprovokable => Core.SE;
public override bool Uncalmable => Core.SE;
public override Poison PoisonImmune => Poison.Deadly;
public override bool ShowFameTitle => false;
public override bool ClickTitle => false;
public override void GenerateLoot()
{
AddLoot(LootPack.UltraRich, 3);
AddLoot(LootPack.Meager);
}
public override bool OnBeforeDeath()
{
var mount = Mount;
if (mount != null)
{ {
AddLoot(LootPack.UltraRich, 3); mount.Rider = null;
AddLoot(LootPack.Meager);
} }
public override bool OnBeforeDeath() (mount as Item)?.Delete();
return base.OnBeforeDeath();
}
public override void OnDamage(int amount, Mobile from, bool willKill)
{
CheckSpeedBoost();
base.OnDamage(amount, from, willKill);
}
private void CheckSpeedBoost()
{
if (Hits < HitsMax / 4)
{ {
var mount = Mount; if (!_speedBoost)
if (mount != null)
{ {
mount.Rider = null; ActiveSpeed /= SpeedBoostScalar;
} PassiveSpeed /= SpeedBoostScalar;
_speedBoost = true;
(mount as Mobile)?.Delete();
return base.OnBeforeDeath();
}
public override void OnDamage(int amount, Mobile from, bool willKill)
{
CheckSpeedBoost();
base.OnDamage(amount, from, willKill);
}
private void CheckSpeedBoost()
{
if (Hits < HitsMax / 4)
{
if (!m_SpeedBoost)
{
ActiveSpeed /= SpeedBoostScalar;
PassiveSpeed /= SpeedBoostScalar;
m_SpeedBoost = true;
}
}
else if (m_SpeedBoost)
{
ActiveSpeed *= SpeedBoostScalar;
PassiveSpeed *= SpeedBoostScalar;
m_SpeedBoost = false;
} }
} }
else if (_speedBoost)
public override void OnGaveMeleeAttack(Mobile defender, int damage)
{ {
base.OnGaveMeleeAttack(defender, damage); ActiveSpeed *= SpeedBoostScalar;
PassiveSpeed *= SpeedBoostScalar;
_speedBoost = false;
}
}
if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone public override void OnGaveMeleeAttack(Mobile defender, int damage)
{ {
AddUnholyBone(defender, 0.25); base.OnGaveMeleeAttack(defender, damage);
}
CheckSpeedBoost(); if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone
{
AddUnholyBone(defender, 0.25);
} }
public override void OnGotMeleeAttack(Mobile attacker, int damage) CheckSpeedBoost();
{ }
base.OnGotMeleeAttack(attacker, damage);
if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone public override void OnGotMeleeAttack(Mobile attacker, int damage)
{ {
AddUnholyBone(attacker, 0.25); base.OnGotMeleeAttack(attacker, damage);
}
if (Utility.RandomDouble() < 0.1) // 10% chance to drop or throw an unholy bone
{
AddUnholyBone(attacker, 0.25);
}
}
public override void AlterDamageScalarFrom(Mobile caster, ref double scalar)
{
base.AlterDamageScalarFrom(caster, ref scalar);
if (Utility.RandomDouble() < 0.1) // 10% chance to throw an unholy bone
{
AddUnholyBone(caster, 1.0);
}
}
public void AddUnholyBone(Mobile target, double chanceToThrow)
{
if (Map == null)
{
return;
} }
public override void AlterDamageScalarFrom(Mobile caster, ref double scalar) if (chanceToThrow >= Utility.RandomDouble())
{ {
base.AlterDamageScalarFrom(caster, ref scalar); Direction = GetDirectionTo(target);
MovingEffect(target, 0xF7E, 10, 1, true, false, 0x496, 0);
new DelayTimer(this, target).Start();
}
else
{
new UnholyBone().MoveToWorld(Location, Map);
}
}
if (Utility.RandomDouble() < 0.1) // 10% chance to throw an unholy bone private void Deserialize(IGenericReader reader, int version)
{ {
AddUnholyBone(caster, 1.0); // We don't need to serialize this
} reader.ReadBool(); // _speedBoost
}
[AfterDeserialization]
private void AfterDeserialization()
{
CheckSpeedBoost();
}
private class VirtualMount : IMount
{
private readonly VirtualMountItem _item;
public VirtualMount(VirtualMountItem item) => _item = item;
Mobile IMount.Rider
{
get => _item.Rider;
set { }
} }
public void AddUnholyBone(Mobile target, double chanceToThrow) public virtual void OnRiderDamaged(int amount, Mobile from, bool willKill)
{ {
if (Map == null) }
{ }
return;
}
if (chanceToThrow >= Utility.RandomDouble()) [SerializationGenerator(0)]
private partial class VirtualMountItem : Item, IMountItem
{
private VirtualMount _mount;
[SerializableField(0, setter: "private")]
private Mobile _rider;
public VirtualMountItem(Mobile mob) : base(0x3EBB)
{
Layer = Layer.Mount;
Movable = false;
_rider = mob;
_mount = new VirtualMount(this);
}
public IMount Mount => _mount;
[AfterDeserialization(false)]
private void AfterDeserialization()
{
if (_rider?.Deleted != false)
{ {
Direction = GetDirectionTo(target); Delete();
MovingEffect(target, 0xF7E, 10, 1, true, false, 0x496, 0);
new DelayTimer(this, target).Start();
} }
else else
{ {
new UnholyBone().MoveToWorld(Location, Map); _mount = new VirtualMount(this);
} }
} }
public override void Serialize(IGenericWriter writer) public override DeathMoveResult OnParentDeath(Mobile parent)
{ {
base.Serialize(writer); _mount = null;
Delete();
writer.Write(1); // version return DeathMoveResult.RemainEquipped;
writer.Write(m_SpeedBoost); }
}
private class DelayTimer : Timer
{
private readonly Mobile m_Mobile;
private readonly Mobile m_Target;
public DelayTimer(Mobile m, Mobile target) : base(TimeSpan.FromSeconds(1.0))
{
m_Mobile = m;
m_Target = target;
} }
public override void Deserialize(IGenericReader reader) protected override void OnTick()
{ {
base.Deserialize(reader); if (m_Mobile.CanBeHarmful(m_Target))
var version = reader.ReadInt();
switch (version)
{ {
case 1: m_Mobile.DoHarmful(m_Target);
{ AOS.Damage(m_Target, m_Mobile, Utility.RandomMinMax(10, 20), 100, 0, 0, 0, 0);
m_SpeedBoost = reader.ReadBool(); new UnholyBone().MoveToWorld(m_Target.Location, m_Target.Map);
break;
}
}
}
private class VirtualMount : IMount
{
private readonly VirtualMountItem m_Item;
public VirtualMount(VirtualMountItem item) => m_Item = item;
Mobile IMount.Rider
{
get => m_Item.Rider;
set { }
}
public virtual void OnRiderDamaged(int amount, Mobile from, bool willKill)
{
}
}
private class VirtualMountItem : Item, IMountItem
{
private readonly VirtualMount m_Mount;
public VirtualMountItem(Mobile mob)
: base(0x3EBB)
{
Layer = Layer.Mount;
Movable = false;
Rider = mob;
m_Mount = new VirtualMount(this);
}
public VirtualMountItem(Serial serial)
: base(serial) =>
m_Mount = new VirtualMount(this);
public Mobile Rider { get; private set; }
public IMount Mount => m_Mount;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(Rider);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
Rider = reader.ReadEntity<Mobile>();
if (Rider == null)
{
Delete();
}
}
}
private class DelayTimer : Timer
{
private readonly Mobile m_Mobile;
private readonly Mobile m_Target;
public DelayTimer(Mobile m, Mobile target) : base(TimeSpan.FromSeconds(1.0))
{
m_Mobile = m;
m_Target = target;
}
protected override void OnTick()
{
if (m_Mobile.CanBeHarmful(m_Target))
{
m_Mobile.DoHarmful(m_Target);
AOS.Damage(m_Target, m_Mobile, Utility.RandomMinMax(10, 20), 100, 0, 0, 0, 0);
new UnholyBone().MoveToWorld(m_Target.Location, m_Target.Map);
}
} }
} }
} }

View file

@ -1,38 +1,21 @@
using ModernUO.Serialization;
using Server.Guilds; using Server.Guilds;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class OrderGuard : BaseShieldGuard
{ {
public class OrderGuard : BaseShieldGuard [Constructible]
public OrderGuard()
{ {
[Constructible]
public OrderGuard()
{
}
public OrderGuard(Serial serial) : base(serial)
{
}
public override int Keyword => 0x21; // *order shield*
public override BaseShield Shield => new OrderShield();
public override int SignupNumber => 1007141; // Sign up with a guild of order if thou art interested.
public override GuildType Type => GuildType.Order;
public override bool BardImmune => true;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }
public override int Keyword => 0x21; // *order shield*
public override BaseShield Shield => new OrderShield();
public override int SignupNumber => 1007141; // Sign up with a guild of order if thou art interested.
public override GuildType Type => GuildType.Order;
public override bool BardImmune => true;
} }

View file

@ -2,255 +2,249 @@ using System;
using Server.Items; using Server.Items;
using Server.Utilities; using Server.Utilities;
namespace Server.Mobiles namespace Server.Mobiles;
public static class Paragon
{ {
public static class Paragon public const double ChestChance = 0.10; // Chance that a paragon will carry a paragon chest
public const double ChocolateIngredientChance = 0.20; // Chance that a paragon will drop a chocolatiering ingredient
public static Map[] Maps =
{ {
public const double ChestChance = 0.10; // Chance that a paragon will carry a paragon chest Map.Ilshenar
public const double ChocolateIngredientChance = 0.20; // Chance that a paragon will drop a chocolatiering ingredient };
public static Map[] Maps = private static readonly TimeSpan FastRegenRate = TimeSpan.FromSeconds(0.5);
private static readonly TimeSpan CPUSaverRate = TimeSpan.FromSeconds(2);
public static Type[] Artifacts =
{
typeof(GoldBricks), typeof(PhillipsWoodenSteed),
typeof(AlchemistsBauble), typeof(ArcticDeathDealer),
typeof(BlazeOfDeath), typeof(BowOfTheJukaKing),
typeof(BurglarsBandana), typeof(CavortingClub),
typeof(EnchantedTitanLegBone), typeof(GwennosHarp),
typeof(IolosLute), typeof(LunaLance),
typeof(NightsKiss), typeof(NoxRangersHeavyCrossbow),
typeof(OrcishVisage), typeof(PolarBearMask),
typeof(ShieldOfInvulnerability), typeof(StaffOfPower),
typeof(VioletCourage), typeof(HeartOfTheLion),
typeof(WrathOfTheDryad), typeof(PixieSwatter),
typeof(GlovesOfThePugilist)
};
public static int Hue = 0x501; // Paragon hue
// Buffs
public const double HitsBuff = 5.0;
public const double StrBuff = 1.05;
public const double IntBuff = 1.20;
public const double DexBuff = 1.20;
public const double SkillsBuff = 1.20;
public const double SpeedBuff = 1.20;
public const double FameBuff = 1.40;
public const double KarmaBuff = 1.40;
public const int DamageBuff = 5;
public static void Convert(BaseCreature bc)
{
if (bc.IsParagon)
{ {
Map.Ilshenar return;
};
private static readonly TimeSpan FastRegenRate = TimeSpan.FromSeconds(0.5);
private static readonly TimeSpan CPUSaverRate = TimeSpan.FromSeconds(2);
public static Type[] Artifacts =
{
typeof(GoldBricks), typeof(PhillipsWoodenSteed),
typeof(AlchemistsBauble), typeof(ArcticDeathDealer),
typeof(BlazeOfDeath), typeof(BowOfTheJukaKing),
typeof(BurglarsBandana), typeof(CavortingClub),
typeof(EnchantedTitanLegBone), typeof(GwennosHarp),
typeof(IolosLute), typeof(LunaLance),
typeof(NightsKiss), typeof(NoxRangersHeavyCrossbow),
typeof(OrcishVisage), typeof(PolarBearMask),
typeof(ShieldOfInvulnerability), typeof(StaffOfPower),
typeof(VioletCourage), typeof(HeartOfTheLion),
typeof(WrathOfTheDryad), typeof(PixieSwatter),
typeof(GlovesOfThePugilist)
};
public static int Hue = 0x501; // Paragon hue
// Buffs
public const double HitsBuff = 5.0;
public const double StrBuff = 1.05;
public const double IntBuff = 1.20;
public const double DexBuff = 1.20;
public const double SkillsBuff = 1.20;
public const double SpeedBuff = 1.20;
public const double FameBuff = 1.40;
public const double KarmaBuff = 1.40;
public const int DamageBuff = 5;
public static void Convert(BaseCreature bc)
{
if (bc.IsParagon)
{
return;
}
bc.Hue = Hue;
if (bc.HitsMaxSeed >= 0)
{
bc.HitsMaxSeed = (int)(bc.HitsMaxSeed * HitsBuff);
}
bc.RawStr = (int)(bc.RawStr * StrBuff);
bc.RawInt = (int)(bc.RawInt * IntBuff);
bc.RawDex = (int)(bc.RawDex * DexBuff);
bc.Hits = bc.HitsMax;
bc.Mana = bc.ManaMax;
bc.Stam = bc.StamMax;
for (var i = 0; i < bc.Skills.Length; i++)
{
var skill = bc.Skills[i];
if (skill.Base > 0.0)
{
skill.Base *= SkillsBuff;
}
}
bc.PassiveSpeed /= SpeedBuff;
bc.ActiveSpeed /= SpeedBuff;
bc.CurrentSpeed = bc.PassiveSpeed;
bc.DamageMin += DamageBuff;
bc.DamageMax += DamageBuff;
if (bc.Fame > 0)
{
bc.Fame = (int)(bc.Fame * FameBuff);
}
if (bc.Fame > 32000)
{
bc.Fame = 32000;
}
// TODO: Mana regeneration rate = Sqrt( buffedFame ) / 4
if (bc.Karma != 0)
{
bc.Karma = (int)(bc.Karma * KarmaBuff);
if (bc.Karma.Abs() > 32000)
{
bc.Karma = 32000 * Math.Sign(bc.Karma);
}
}
new ParagonStamRegen(bc).Start();
} }
public static void UnConvert(BaseCreature bc) bc.Hue = Hue;
if (bc.HitsMaxSeed >= 0)
{ {
if (!bc.IsParagon) bc.HitsMaxSeed = (int)(bc.HitsMaxSeed * HitsBuff);
}
bc.RawStr = (int)(bc.RawStr * StrBuff);
bc.RawInt = (int)(bc.RawInt * IntBuff);
bc.RawDex = (int)(bc.RawDex * DexBuff);
bc.Hits = bc.HitsMax;
bc.Mana = bc.ManaMax;
bc.Stam = bc.StamMax;
for (var i = 0; i < bc.Skills.Length; i++)
{
var skill = bc.Skills[i];
if (skill.Base > 0.0)
{ {
return; skill.Base *= SkillsBuff;
}
bc.Hue = 0;
if (bc.HitsMaxSeed >= 0)
{
bc.HitsMaxSeed = (int)(bc.HitsMaxSeed / HitsBuff);
}
bc.RawStr = (int)(bc.RawStr / StrBuff);
bc.RawInt = (int)(bc.RawInt / IntBuff);
bc.RawDex = (int)(bc.RawDex / DexBuff);
bc.Hits = bc.HitsMax;
bc.Mana = bc.ManaMax;
bc.Stam = bc.StamMax;
for (var i = 0; i < bc.Skills.Length; i++)
{
var skill = bc.Skills[i];
if (skill.Base > 0.0)
{
skill.Base /= SkillsBuff;
}
}
bc.PassiveSpeed *= SpeedBuff;
bc.ActiveSpeed *= SpeedBuff;
bc.CurrentSpeed = bc.PassiveSpeed;
bc.DamageMin -= DamageBuff;
bc.DamageMax -= DamageBuff;
if (bc.Fame > 0)
{
bc.Fame = (int)(bc.Fame / FameBuff);
}
if (bc.Karma != 0)
{
bc.Karma = (int)(bc.Karma / KarmaBuff);
} }
} }
public static bool CheckConvert(BaseCreature bc) => CheckConvert(bc, bc.Location, bc.Map); bc.PassiveSpeed /= SpeedBuff;
bc.ActiveSpeed /= SpeedBuff;
bc.CurrentSpeed = bc.PassiveSpeed;
public static bool CheckConvert(BaseCreature bc, Point3D location, Map m) bc.DamageMin += DamageBuff;
bc.DamageMax += DamageBuff;
if (bc.Fame > 0)
{ {
if (!Core.AOS) bc.Fame = (int)(bc.Fame * FameBuff);
{
return false;
}
if (Array.IndexOf(Maps, m) == -1)
{
return false;
}
if (bc is BaseChampion or Harrower or BaseVendor or BaseEscortable or Clone || bc.IsParagon)
{
return false;
}
var fame = bc.Fame;
if (fame > 32000)
{
fame = 32000;
}
var chance = 1 / Math.Round(20.0 - fame / 3200.0);
return chance > Utility.RandomDouble();
} }
public static bool CheckArtifactChance(Mobile m, BaseCreature bc) if (bc.Fame > 32000)
{ {
if (!Core.AOS) bc.Fame = 32000;
{
return false;
}
double fame = bc.Fame;
if (fame > 32000)
{
fame = 32000;
}
var chance =
1 / (Math.Max(10, 100 * (0.83 - Math.Round(Math.Log(Math.Round(fame / 6000, 3) + 0.001, 10), 3))) *
(100 - Math.Sqrt(m.Luck)) / 100.0);
return chance > Utility.RandomDouble();
} }
public static void GiveArtifactTo(Mobile m) // TODO: Mana regeneration rate = Sqrt( buffedFame ) / 4
{
var item = Artifacts.RandomElement().CreateInstance<Item>();
if (m.AddToBackpack(item)) if (bc.Karma != 0)
{
bc.Karma = (int)(bc.Karma * KarmaBuff);
if (bc.Karma.Abs() > 32000)
{ {
m.SendMessage("As a reward for slaying the mighty paragon, an artifact has been placed in your backpack."); bc.Karma = 32000 * Math.Sign(bc.Karma);
}
}
new ParagonStamRegen(bc).Start();
}
public static void UnConvert(BaseCreature bc)
{
if (!bc.IsParagon)
{
return;
}
bc.Hue = 0;
if (bc.HitsMaxSeed >= 0)
{
bc.HitsMaxSeed = (int)(bc.HitsMaxSeed / HitsBuff);
}
bc.RawStr = (int)(bc.RawStr / StrBuff);
bc.RawInt = (int)(bc.RawInt / IntBuff);
bc.RawDex = (int)(bc.RawDex / DexBuff);
bc.Hits = bc.HitsMax;
bc.Mana = bc.ManaMax;
bc.Stam = bc.StamMax;
for (var i = 0; i < bc.Skills.Length; i++)
{
var skill = bc.Skills[i];
if (skill.Base > 0.0)
{
skill.Base /= SkillsBuff;
}
}
bc.PassiveSpeed *= SpeedBuff;
bc.ActiveSpeed *= SpeedBuff;
bc.CurrentSpeed = bc.PassiveSpeed;
bc.DamageMin -= DamageBuff;
bc.DamageMax -= DamageBuff;
if (bc.Fame > 0)
{
bc.Fame = (int)(bc.Fame / FameBuff);
}
if (bc.Karma != 0)
{
bc.Karma = (int)(bc.Karma / KarmaBuff);
}
}
public static bool CheckConvert(BaseCreature bc) => CheckConvert(bc, bc.Location, bc.Map);
public static bool CheckConvert(BaseCreature bc, Point3D location, Map m)
{
if (!Core.AOS)
{
return false;
}
if (Array.IndexOf(Maps, m) == -1)
{
return false;
}
if (bc is BaseChampion or Harrower or BaseVendor or BaseEscortable or Clone || bc.IsParagon)
{
return false;
}
var fame = bc.Fame;
if (fame > 32000)
{
fame = 32000;
}
var chance = 1 / Math.Round(20.0 - fame / 3200.0);
return chance > Utility.RandomDouble();
}
public static bool CheckArtifactChance(Mobile m, BaseCreature bc)
{
if (!Core.AOS)
{
return false;
}
double fame = bc.Fame;
if (fame > 32000)
{
fame = 32000;
}
var chance =
1 / (Math.Max(10, 100 * (0.83 - Math.Round(Math.Log(Math.Round(fame / 6000, 3) + 0.001, 10), 3))) *
(100 - Math.Sqrt(m.Luck)) / 100.0);
return chance > Utility.RandomDouble();
}
public static void GiveArtifactTo(Mobile m)
{
var item = Artifacts.RandomElement().CreateInstance<Item>();
if (m.AddToBackpack(item))
{
m.SendMessage("As a reward for slaying the mighty paragon, an artifact has been placed in your backpack.");
}
else
{
m.SendMessage(
"As your backpack is full, your reward for destroying the legendary paragon has been placed at your feet."
);
}
}
private class ParagonStamRegen : Timer
{
private readonly BaseCreature m_Owner;
public ParagonStamRegen(Mobile m) : base(FastRegenRate, FastRegenRate) => m_Owner = m as BaseCreature;
protected override void OnTick()
{
if (!m_Owner.Deleted && m_Owner.IsParagon && m_Owner.Map != Map.Internal)
{
m_Owner.Stam++;
Delay = Interval = m_Owner.Stam < m_Owner.StamMax * .75 ? FastRegenRate : CPUSaverRate;
} }
else else
{ {
m.SendMessage( Stop();
"As your backpack is full, your reward for destroying the legendary paragon has been placed at your feet."
);
}
}
private class ParagonStamRegen : Timer
{
private readonly BaseCreature m_Owner;
public ParagonStamRegen(Mobile m)
: base(FastRegenRate, FastRegenRate)
{
m_Owner = m as BaseCreature;
}
protected override void OnTick()
{
if (!m_Owner.Deleted && m_Owner.IsParagon && m_Owner.Map != Map.Internal)
{
m_Owner.Stam++;
Delay = Interval = m_Owner.Stam < m_Owner.StamMax * .75 ? FastRegenRate : CPUSaverRate;
}
else
{
Stop();
}
} }
} }
} }

View file

@ -1,165 +1,148 @@
using System; using System;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Rikktor : BaseChampion
{ {
public class Rikktor : BaseChampion [Constructible]
public Rikktor() : base(AIType.AI_Melee)
{ {
[Constructible] Body = 172;
public Rikktor() : base(AIType.AI_Melee)
SetStr(701, 900);
SetDex(201, 350);
SetInt(51, 100);
SetHits(3000);
SetStam(203, 650);
SetDamage(28, 55);
SetDamageType(ResistanceType.Physical, 25);
SetDamageType(ResistanceType.Fire, 50);
SetDamageType(ResistanceType.Energy, 25);
SetResistance(ResistanceType.Physical, 80, 90);
SetResistance(ResistanceType.Fire, 80, 90);
SetResistance(ResistanceType.Cold, 30, 40);
SetResistance(ResistanceType.Poison, 80, 90);
SetResistance(ResistanceType.Energy, 80, 90);
SetSkill(SkillName.Anatomy, 100.0);
SetSkill(SkillName.MagicResist, 140.2, 160.0);
SetSkill(SkillName.Tactics, 100.0);
Fame = 22500;
Karma = -22500;
VirtualArmor = 130;
}
public override ChampionSkullType SkullType => ChampionSkullType.Power;
public override Type[] UniqueList => new[] { typeof(CrownOfTalKeesh) };
public override Type[] SharedList => new[]
{
typeof(TheMostKnowledgePerson),
typeof(BraveKnightOfTheBritannia),
typeof(LieutenantOfTheBritannianRoyalGuard)
};
public override Type[] DecorativeList => new[]
{
typeof(LavaTile),
typeof(MonsterStatuette),
typeof(MonsterStatuette)
};
public override MonsterStatuetteType[] StatueTypes => new[]
{
MonsterStatuetteType.OphidianArchMage,
MonsterStatuetteType.OphidianWarrior
};
public override string DefaultName => "Rikktor";
public override Poison PoisonImmune => Poison.Lethal;
public override ScaleType ScaleType => ScaleType.All;
public override int Scales => 20;
public override void GenerateLoot()
{
AddLoot(LootPack.UltraRich, 4);
}
public override void OnGaveMeleeAttack(Mobile defender, int damage)
{
base.OnGaveMeleeAttack(defender, damage);
if (Utility.RandomDouble() < 0.2)
{ {
Body = 172; Earthquake();
SetStr(701, 900);
SetDex(201, 350);
SetInt(51, 100);
SetHits(3000);
SetStam(203, 650);
SetDamage(28, 55);
SetDamageType(ResistanceType.Physical, 25);
SetDamageType(ResistanceType.Fire, 50);
SetDamageType(ResistanceType.Energy, 25);
SetResistance(ResistanceType.Physical, 80, 90);
SetResistance(ResistanceType.Fire, 80, 90);
SetResistance(ResistanceType.Cold, 30, 40);
SetResistance(ResistanceType.Poison, 80, 90);
SetResistance(ResistanceType.Energy, 80, 90);
SetSkill(SkillName.Anatomy, 100.0);
SetSkill(SkillName.MagicResist, 140.2, 160.0);
SetSkill(SkillName.Tactics, 100.0);
Fame = 22500;
Karma = -22500;
VirtualArmor = 130;
}
public Rikktor(Serial serial) : base(serial)
{
}
public override ChampionSkullType SkullType => ChampionSkullType.Power;
public override Type[] UniqueList => new[] { typeof(CrownOfTalKeesh) };
public override Type[] SharedList => new[]
{
typeof(TheMostKnowledgePerson),
typeof(BraveKnightOfTheBritannia),
typeof(LieutenantOfTheBritannianRoyalGuard)
};
public override Type[] DecorativeList => new[]
{
typeof(LavaTile),
typeof(MonsterStatuette),
typeof(MonsterStatuette)
};
public override MonsterStatuetteType[] StatueTypes => new[]
{
MonsterStatuetteType.OphidianArchMage,
MonsterStatuetteType.OphidianWarrior
};
public override string DefaultName => "Rikktor";
public override Poison PoisonImmune => Poison.Lethal;
public override ScaleType ScaleType => ScaleType.All;
public override int Scales => 20;
public override void GenerateLoot()
{
AddLoot(LootPack.UltraRich, 4);
}
public override void OnGaveMeleeAttack(Mobile defender, int damage)
{
base.OnGaveMeleeAttack(defender, damage);
if (Utility.RandomDouble() < 0.2)
{
Earthquake();
}
}
public void Earthquake()
{
var map = Map;
if (map == null)
{
return;
}
PlaySound(0x2F3);
var eable = GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
continue;
}
if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team))
{
continue;
}
var damage = m.Hits * 0.6;
if (damage < 10.0)
{
damage = 10.0;
}
else if (damage > 75.0)
{
damage = 75.0;
}
DoHarmful(m);
AOS.Damage(m, this, (int)damage, 100, 0, 0, 0, 0);
if (m.Alive && m.Body.IsHuman && !m.Mounted)
{
m.Animate(20, 7, 1, true, false, 0); // take hit
}
}
eable.Free();
}
public override int GetAngerSound() => Utility.Random(0x2CE, 2);
public override int GetIdleSound() => 0x2D2;
public override int GetAttackSound() => Utility.Random(0x2C7, 5);
public override int GetHurtSound() => 0x2D1;
public override int GetDeathSound() => 0x2CC;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
} }
} }
public void Earthquake()
{
var map = Map;
if (map == null)
{
return;
}
PlaySound(0x2F3);
var eable = GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
continue;
}
if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team))
{
continue;
}
var damage = m.Hits * 0.6;
if (damage < 10.0)
{
damage = 10.0;
}
else if (damage > 75.0)
{
damage = 75.0;
}
DoHarmful(m);
AOS.Damage(m, this, (int)damage, 100, 0, 0, 0, 0);
if (m.Alive && m.Body.IsHuman && !m.Mounted)
{
m.Animate(20, 7, 1, true, false, 0); // take hit
}
}
eable.Free();
}
public override int GetAngerSound() => Utility.Random(0x2CE, 2);
public override int GetIdleSound() => 0x2D2;
public override int GetAttackSound() => Utility.Random(0x2C7, 5);
public override int GetHurtSound() => 0x2D1;
public override int GetDeathSound() => 0x2CC;
} }

View file

@ -1,10 +1,12 @@
using System; using System;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Items; using Server.Items;
namespace Server.Mobiles; namespace Server.Mobiles;
public class Semidar : BaseChampion [SerializationGenerator(0, false)]
public partial class Semidar : BaseChampion
{ {
[Constructible] [Constructible]
public Semidar() : base(AIType.AI_Mage) public Semidar() : base(AIType.AI_Mage)
@ -43,10 +45,6 @@ public class Semidar : BaseChampion
VirtualArmor = 20; VirtualArmor = 20;
} }
public Semidar(Serial serial) : base(serial)
{
}
public override ChampionSkullType SkullType => ChampionSkullType.Pain; public override ChampionSkullType SkullType => ChampionSkullType.Pain;
public override Type[] UniqueList => new[] { typeof(GladiatorsCollar) }; public override Type[] UniqueList => new[] { typeof(GladiatorsCollar) };
@ -88,18 +86,6 @@ public class Semidar : BaseChampion
private static MonsterAbility[] _abilities = { new SemidarDrainLife() }; private static MonsterAbility[] _abilities = { new SemidarDrainLife() };
public override MonsterAbility[] GetMonsterAbilities() => _abilities; public override MonsterAbility[] GetMonsterAbilities() => _abilities;
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
private class SemidarDrainLife : DrainLifeAreaAttack private class SemidarDrainLife : DrainLifeAreaAttack
{ {
public override double ChanceToTrigger => 0.25; public override double ChanceToTrigger => 0.25;

View file

@ -1,194 +1,177 @@
using System; using System;
using ModernUO.Serialization;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Engines.Plants; using Server.Engines.Plants;
using Server.Items; using Server.Items;
namespace Server.Mobiles namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class Serado : BaseChampion
{ {
public class Serado : BaseChampion [Constructible]
public Serado() : base(AIType.AI_Melee)
{ {
[Constructible] Title = "the awakened";
public Serado() : base(AIType.AI_Melee)
Body = 249;
Hue = 0x96C;
SetStr(1000);
SetDex(150);
SetInt(300);
SetHits(9000);
SetMana(300);
SetDamage(29, 35);
SetDamageType(ResistanceType.Physical, 70);
SetDamageType(ResistanceType.Poison, 20);
SetDamageType(ResistanceType.Energy, 10);
SetResistance(ResistanceType.Physical, 30);
SetResistance(ResistanceType.Fire, 60);
SetResistance(ResistanceType.Cold, 60);
SetResistance(ResistanceType.Poison, 90);
SetResistance(ResistanceType.Energy, 50);
SetSkill(SkillName.MagicResist, 120.0);
SetSkill(SkillName.Tactics, 120.0);
SetSkill(SkillName.Wrestling, 70.0);
SetSkill(SkillName.Poisoning, 150.0);
Fame = 22500;
Karma = -22500;
PackItem(Seed.RandomBonsaiSeed());
}
public override ChampionSkullType SkullType => ChampionSkullType.Power;
public override Type[] UniqueList => new[] { typeof(Pacify) };
public override Type[] SharedList => new[]
{
typeof(BraveKnightOfTheBritannia),
typeof(DetectiveBoots),
typeof(EmbroideredOakLeafCloak),
typeof(LieutenantOfTheBritannianRoyalGuard)
};
public override Type[] DecorativeList => new[] { typeof(Futon), typeof(SwampTile) };
public override MonsterStatuetteType[] StatueTypes => Array.Empty<MonsterStatuetteType>();
public override string DefaultName => "Serado";
public override int TreasureMapLevel => 5;
public override Poison HitPoison => Poison.Lethal;
public override Poison PoisonImmune => Poison.Lethal;
public override double HitPoisonChance => 0.8;
public override int Feathers => 30;
public override bool ShowFameTitle => false;
public override bool ClickTitle => false;
public override WeaponAbility GetWeaponAbility() => WeaponAbility.DoubleStrike;
public override void GenerateLoot()
{
AddLoot(LootPack.UltraRich, 4);
AddLoot(LootPack.FilthyRich);
AddLoot(LootPack.Gems, 6);
}
// TODO: Hit Lightning Area
public override void OnDamagedBySpell(Mobile attacker, int damage)
{
base.OnDamagedBySpell(attacker, damage);
ScaleResistances();
DoCounter(attacker);
}
public override void OnGotMeleeAttack(Mobile attacker, int damage)
{
base.OnGotMeleeAttack(attacker, damage);
ScaleResistances();
DoCounter(attacker);
}
private void ScaleResistances()
{
var hitsLost = (HitsMax - Hits) / (double)HitsMax;
SetResistance(ResistanceType.Physical, 30 + (int)(hitsLost * (95 - 30)));
SetResistance(ResistanceType.Fire, 60 + (int)(hitsLost * (95 - 60)));
SetResistance(ResistanceType.Cold, 60 + (int)(hitsLost * (95 - 60)));
SetResistance(ResistanceType.Poison, 90 + (int)(hitsLost * (95 - 90)));
SetResistance(ResistanceType.Energy, 50 + (int)(hitsLost * (95 - 50)));
}
private void DoCounter(Mobile attacker)
{
if (Map == null)
{ {
Title = "the awakened"; return;
Body = 249;
Hue = 0x96C;
SetStr(1000);
SetDex(150);
SetInt(300);
SetHits(9000);
SetMana(300);
SetDamage(29, 35);
SetDamageType(ResistanceType.Physical, 70);
SetDamageType(ResistanceType.Poison, 20);
SetDamageType(ResistanceType.Energy, 10);
SetResistance(ResistanceType.Physical, 30);
SetResistance(ResistanceType.Fire, 60);
SetResistance(ResistanceType.Cold, 60);
SetResistance(ResistanceType.Poison, 90);
SetResistance(ResistanceType.Energy, 50);
SetSkill(SkillName.MagicResist, 120.0);
SetSkill(SkillName.Tactics, 120.0);
SetSkill(SkillName.Wrestling, 70.0);
SetSkill(SkillName.Poisoning, 150.0);
Fame = 22500;
Karma = -22500;
PackItem(Seed.RandomBonsaiSeed());
} }
public Serado(Serial serial) : base(serial) if (!(Utility.RandomDouble() < 0.2))
{ {
return;
} }
public override ChampionSkullType SkullType => ChampionSkullType.Power; Mobile target = null;
public override Type[] UniqueList => new[] { typeof(Pacify) }; if (attacker is BaseCreature bcAttacker)
public override Type[] SharedList => new[]
{ {
typeof(BraveKnightOfTheBritannia), if (bcAttacker.BardProvoked)
typeof(DetectiveBoots),
typeof(EmbroideredOakLeafCloak),
typeof(LieutenantOfTheBritannianRoyalGuard)
};
public override Type[] DecorativeList => new[] { typeof(Futon), typeof(SwampTile) };
public override MonsterStatuetteType[] StatueTypes => Array.Empty<MonsterStatuetteType>();
public override string DefaultName => "Serado";
public override int TreasureMapLevel => 5;
public override Poison HitPoison => Poison.Lethal;
public override Poison PoisonImmune => Poison.Lethal;
public override double HitPoisonChance => 0.8;
public override int Feathers => 30;
public override bool ShowFameTitle => false;
public override bool ClickTitle => false;
public override WeaponAbility GetWeaponAbility() => WeaponAbility.DoubleStrike;
public override void GenerateLoot()
{
AddLoot(LootPack.UltraRich, 4);
AddLoot(LootPack.FilthyRich);
AddLoot(LootPack.Gems, 6);
}
// TODO: Hit Lightning Area
public override void OnDamagedBySpell(Mobile attacker, int damage)
{
base.OnDamagedBySpell(attacker, damage);
ScaleResistances();
DoCounter(attacker);
}
public override void OnGotMeleeAttack(Mobile attacker, int damage)
{
base.OnGotMeleeAttack(attacker, damage);
ScaleResistances();
DoCounter(attacker);
}
private void ScaleResistances()
{
var hitsLost = (HitsMax - Hits) / (double)HitsMax;
SetResistance(ResistanceType.Physical, 30 + (int)(hitsLost * (95 - 30)));
SetResistance(ResistanceType.Fire, 60 + (int)(hitsLost * (95 - 60)));
SetResistance(ResistanceType.Cold, 60 + (int)(hitsLost * (95 - 60)));
SetResistance(ResistanceType.Poison, 90 + (int)(hitsLost * (95 - 90)));
SetResistance(ResistanceType.Energy, 50 + (int)(hitsLost * (95 - 50)));
}
private void DoCounter(Mobile attacker)
{
if (Map == null)
{ {
return; return;
} }
if (!(Utility.RandomDouble() < 0.2)) target = bcAttacker.GetMaster();
{
return;
}
Mobile target = null;
if (attacker is BaseCreature bcAttacker)
{
if (bcAttacker.BardProvoked)
{
return;
}
target = bcAttacker.GetMaster();
}
/* Counterattack with Hit Poison Area
* 20-25 damage, unresistable
* Lethal poison, 100% of the time
* Particle effect: Type: "2" From: "0x4061A107" To: "0x0" ItemId: "0x36BD" ItemIdName: "explosion" FromLocation: "(296 615, 17)" ToLocation: "(296 615, 17)" Speed: "1" Duration: "10" FixedDirection: "True" Explode: "False" Hue: "0xA6" RenderMode: "0x0" Effect: "0x1F78" ExplodeEffect: "0x1" ExplodeSound: "0x0" Serial: "0x4061A107" Layer: "255" Unknown: "0x0"
* Doesn't work on provoked monsters
*/
if (target?.InRange(this, 25) != true)
{
target = attacker;
}
Animate(10, 4, 1, true, false, 0);
var eable = target.GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
continue;
}
if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team))
{
continue;
}
DoHarmful(m);
AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0);
m.FixedParticles(0x36BD, 1, 10, 0x1F78, 0xA6, 0, (EffectLayer)255);
m.ApplyPoison(this, Poison.Lethal);
}
} }
public override void Serialize(IGenericWriter writer) /* Counterattack with Hit Poison Area
{ * 20-25 damage, unresistable
base.Serialize(writer); * Lethal poison, 100% of the time
* Particle effect: Type: "2" From: "0x4061A107" To: "0x0" ItemId: "0x36BD" ItemIdName: "explosion" FromLocation: "(296 615, 17)" ToLocation: "(296 615, 17)" Speed: "1" Duration: "10" FixedDirection: "True" Explode: "False" Hue: "0xA6" RenderMode: "0x0" Effect: "0x1F78" ExplodeEffect: "0x1" ExplodeSound: "0x0" Serial: "0x4061A107" Layer: "255" Unknown: "0x0"
* Doesn't work on provoked monsters
*/
writer.Write(0); // version if (target?.InRange(this, 25) != true)
{
target = attacker;
} }
public override void Deserialize(IGenericReader reader) Animate(10, 4, 1, true, false, 0);
{
base.Deserialize(reader);
var version = reader.ReadInt(); var eable = target.GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
continue;
}
if (m is not BaseCreature bc || !(bc.Controlled || bc.Summoned || bc.Team != Team))
{
continue;
}
DoHarmful(m);
AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0);
m.FixedParticles(0x36BD, 1, 10, 0x1F78, 0xA6, 0, (EffectLayer)255);
m.ApplyPoison(this, Poison.Lethal);
} }
} }
} }

View file

@ -1,41 +1,25 @@
namespace Server.Mobiles using ModernUO.Serialization;
namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class ServantOfSemidar : BaseCreature
{ {
public class ServantOfSemidar : BaseCreature [Constructible]
public ServantOfSemidar() : base(AIType.AI_Melee, FightMode.None) => Body = 0x26;
public override string DefaultName => "a Servant of Semidar";
public override bool DisallowAllMoves => true;
public override bool InitialInnocent => true;
public override bool CanBeDamaged() => false;
public override void AddNameProperties(IPropertyList list)
{ {
[Constructible] base.AddNameProperties(list);
public ServantOfSemidar() : base(AIType.AI_Melee, FightMode.None) => Body = 0x26;
public ServantOfSemidar(Serial serial) : base(serial) list.Add(1005494); // enslaved
{
}
public override string DefaultName => "a Servant of Semidar";
public override bool DisallowAllMoves => true;
public override bool InitialInnocent => true;
public override bool CanBeDamaged() => false;
public override void AddNameProperties(IPropertyList list)
{
base.AddNameProperties(list);
list.Add(1005494); // enslaved
}
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.WriteEncodedInt(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadEncodedInt();
}
} }
} }

View file

@ -1,6 +1,9 @@
using ModernUO.Serialization;
namespace Server.Mobiles; namespace Server.Mobiles;
public class Silvani : BaseCreature [SerializationGenerator(0, false)]
public partial class Silvani : BaseCreature
{ {
[Constructible] [Constructible]
public Silvani() : base(AIType.AI_Mage, FightMode.Evil, 18) public Silvani() : base(AIType.AI_Mage, FightMode.Evil, 18)
@ -38,10 +41,6 @@ public class Silvani : BaseCreature
VirtualArmor = 50; VirtualArmor = 50;
} }
public Silvani(Serial serial) : base(serial)
{
}
public override string DefaultName => "Silvani"; public override string DefaultName => "Silvani";
public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead; public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead;
@ -67,18 +66,4 @@ public class Silvani : BaseCreature
defender.Stam -= Utility.Random(20, 10); defender.Stam -= Utility.Random(20, 10);
defender.Mana -= Utility.Random(20, 10); defender.Mana -= Utility.Random(20, 10);
} }
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
}
} }