fix: Fixes memory leak in virtual mounts and codegens specials (#1476)
This commit is contained in:
parent
701deb53c1
commit
fc3aa13b8e
38 changed files with 2349 additions and 2505 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
Projects/UOContent/Migrations/Server.Factions.VirtualMountItem.v0.json
generated
Normal file
11
Projects/UOContent/Migrations/Server.Factions.VirtualMountItem.v0.json
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Factions.VirtualMountItem",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "Rider",
|
||||||
|
"type": "Server.Mobile",
|
||||||
|
"rule": "SerializableInterfaceMigrationRule"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.Barracoon.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.Barracoon.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.Barracoon"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.BaseChampion.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.BaseChampion.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.BaseChampion"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.BaseShieldGuard.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.BaseShieldGuard.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.BaseShieldGuard"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.CapturedHordeMinion.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.CapturedHordeMinion.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.CapturedHordeMinion"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.ChaosGuard.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.ChaosGuard.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.ChaosGuard"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.DarkGuardian.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.DarkGuardian.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.DarkGuardian"
|
||||||
|
}
|
||||||
28
Projects/UOContent/Migrations/Server.Mobiles.Harrower.v1.json
generated
Normal file
28
Projects/UOContent/Migrations/Server.Mobiles.Harrower.v1.json
generated
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json
generated
Normal file
11
Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.HarrowerTentacles",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "Harrower",
|
||||||
|
"type": "Server.Mobile",
|
||||||
|
"rule": "SerializableInterfaceMigrationRule"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
19
Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json
generated
Normal file
19
Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json
generated
Normal 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": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.Mephitis.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.Mephitis.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.Mephitis"
|
||||||
|
}
|
||||||
11
Projects/UOContent/Migrations/Server.Mobiles.Neira.VirtualMountItem.v0.json
generated
Normal file
11
Projects/UOContent/Migrations/Server.Mobiles.Neira.VirtualMountItem.v0.json
generated
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.Neira.VirtualMountItem",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "Rider",
|
||||||
|
"type": "Server.Mobile",
|
||||||
|
"rule": "SerializableInterfaceMigrationRule"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.Neira.v1.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.Neira.v1.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"type": "Server.Mobiles.Neira"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.OrderGuard.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.OrderGuard.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.OrderGuard"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.Rikktor.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.Rikktor.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.Rikktor"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.Semidar.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.Semidar.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.Semidar"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.Serado.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.Serado.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.Serado"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.ServantOfSemidar.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.ServantOfSemidar.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.ServantOfSemidar"
|
||||||
|
}
|
||||||
4
Projects/UOContent/Migrations/Server.Mobiles.Silvani.v0.json
generated
Normal file
4
Projects/UOContent/Migrations/Server.Mobiles.Silvani.v0.json
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.Mobiles.Silvani"
|
||||||
|
}
|
||||||
|
|
@ -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>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue