fix: Converts ethics system to generic entity persistence (#1824)

### Summary

Converts ethics system to entity persistence and removes the persistence item.


### TODO

1. The enable/disable toggle doesn't propagate to all code that does Ethics checks (notoriety, pvp, etc).
2. We need commands to remove them from an ethic.
This commit is contained in:
Kamron Batman 2024-06-16 10:26:09 -07:00 committed by GitHub
parent f87c8f73ab
commit 20596e52fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 1327 additions and 1338 deletions

View file

@ -1,288 +1,293 @@
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Ethics.Evil;
using Server.Ethics.Hero;
using Server.Items;
using Server.Mobiles;
namespace Server.Ethics
namespace Server.Ethics;
[SerializationGenerator(1)]
public abstract partial class Ethic : EthicsEntity
{
public abstract class Ethic
public static Ethic Hero { get; private set; }
public static Ethic Evil { get; private set; }
public static readonly Ethic[] Ethics =
{
public static readonly Ethic Hero = new HeroEthic();
public static readonly Ethic Evil = new EvilEthic();
Hero,
Evil
};
public static readonly Ethic[] Ethics =
public static bool RegisterEthic(Ethic ethic)
{
if (ethic is HeroEthic)
{
Hero,
Evil
};
protected EthicDefinition m_Definition;
protected PlayerCollection m_Players;
public Ethic() => m_Players = new PlayerCollection();
public static bool Enabled { get; private set; }
public EthicDefinition Definition => m_Definition;
public PlayerCollection Players => m_Players;
public static Ethic Find(Item item)
{
if ((item.SavedFlags & 0x100) != 0)
{
if (item.Hue == Hero.Definition.PrimaryHue)
{
return Hero;
}
item.SavedFlags &= ~0x100;
}
if ((item.SavedFlags & 0x200) != 0)
{
if (item.Hue == Evil.Definition.PrimaryHue)
{
return Evil;
}
item.SavedFlags &= ~0x200;
}
return null;
}
public static bool CheckTrade(Mobile from, Mobile to, Mobile newOwner, Item item)
{
var itemEthic = Find(item);
if (itemEthic == null || Find(newOwner) == itemEthic)
if (Hero == null)
{
Hero = ethic;
return true;
}
if (itemEthic == Hero)
{
(from == newOwner ? to : from).SendMessage("Only heros may receive this item.");
}
else if (itemEthic == Evil)
{
(from == newOwner ? to : from).SendMessage("Only the evil may receive this item.");
}
return false;
}
public static bool CheckEquip(Mobile from, Item item)
if (ethic is EvilEthic)
{
var itemEthic = Find(item);
if (itemEthic == null || Find(from) == itemEthic)
if (Evil == null)
{
Evil = ethic;
return true;
}
if (itemEthic == Hero)
{
from.SendMessage("Only heros may wear this item.");
}
else if (itemEthic == Evil)
{
from.SendMessage("Only the evil may wear this item.");
}
return false;
}
public static bool IsImbued(Item item) => IsImbued(item, false);
return false;
}
public static bool IsImbued(Item item, bool recurse)
public Ethic() => Players = [];
public static bool Enabled { get; private set; }
public virtual EthicDefinition Definition { get; }
public List<Player> Players { get; protected set; }
public static Ethic Find(Item item)
{
if ((item.SavedFlags & 0x100) != 0)
{
if (Find(item) != null)
if (item.Hue == Hero.Definition.PrimaryHue)
{
return true;
return Hero;
}
if (recurse)
item.SavedFlags &= ~0x100;
}
if ((item.SavedFlags & 0x200) != 0)
{
if (item.Hue == Evil.Definition.PrimaryHue)
{
foreach (var child in item.Items)
return Evil;
}
item.SavedFlags &= ~0x200;
}
return null;
}
public static bool CheckTrade(Mobile from, Mobile to, Mobile newOwner, Item item)
{
var itemEthic = Find(item);
if (itemEthic == null || Find(newOwner) == itemEthic)
{
return true;
}
if (itemEthic == Hero)
{
(from == newOwner ? to : from).SendMessage("Only heroes may receive this item.");
}
else if (itemEthic == Evil)
{
(from == newOwner ? to : from).SendMessage("Only the evil may receive this item.");
}
return false;
}
public static bool CheckEquip(Mobile from, Item item)
{
var itemEthic = Find(item);
if (itemEthic == null || Find(from) == itemEthic)
{
return true;
}
if (itemEthic == Hero)
{
from.SendMessage("Only heroes may wear this item.");
}
else if (itemEthic == Evil)
{
from.SendMessage("Only the evil may wear this item.");
}
return false;
}
public static bool IsImbued(Item item) => IsImbued(item, false);
public static bool IsImbued(Item item, bool recurse)
{
if (Find(item) != null)
{
return true;
}
if (recurse)
{
foreach (var child in item.Items)
{
if (IsImbued(child, true))
{
if (IsImbued(child, true))
return true;
}
}
}
return false;
}
public static void Configure()
{
Enabled = ServerConfiguration.GetOrUpdateSetting("ethics.enable", false);
}
public static void Initialize()
{
if (Enabled)
{
EventSink.Speech += EventSink_Speech;
}
}
public static void EventSink_Speech(SpeechEventArgs e)
{
if (e.Blocked || e.Handled)
{
return;
}
var pl = Player.Find(e.Mobile);
if (pl == null)
{
for (var i = 0; i < Ethics.Length; ++i)
{
var ethic = Ethics[i];
if (!ethic.IsEligible(e.Mobile))
{
continue;
}
if (!ethic.Definition.JoinPhrase.String.InsensitiveEquals(e.Speech))
{
continue;
}
var found = false;
foreach (var item in e.Mobile.GetItemsInRange(2))
{
if (item is AnkhNorth or AnkhWest)
{
return true;
found = true;
break;
}
}
}
return false;
}
if (!found)
{
continue;
}
public static void Configure()
{
Enabled = ServerConfiguration.GetOrUpdateSetting("ethics.enable", false);
}
pl = new Player(ethic, e.Mobile);
public static void Initialize()
{
if (Enabled)
{
EventSink.Speech += EventSink_Speech;
pl.Attach();
e.Mobile.FixedEffect(0x373A, 10, 30);
e.Mobile.PlaySound(0x209);
e.Handled = true;
break;
}
}
public static void EventSink_Speech(SpeechEventArgs e)
else
{
if (e.Blocked || e.Handled)
if (e.Mobile is PlayerMobile mobile && mobile.DuelContext != null)
{
return;
}
var pl = Player.Find(e.Mobile);
var ethic = pl.Ethic;
if (pl == null)
for (var i = 0; i < ethic.Definition.Powers.Length; ++i)
{
for (var i = 0; i < Ethics.Length; ++i)
var power = ethic.Definition.Powers[i];
if (!power.Definition.Phrase.String.InsensitiveEquals(e.Speech))
{
var ethic = Ethics[i];
if (!ethic.IsEligible(e.Mobile))
{
continue;
}
if (!ethic.Definition.JoinPhrase.String.InsensitiveEquals(e.Speech))
{
continue;
}
var found = false;
foreach (var item in e.Mobile.GetItemsInRange(2))
{
if (item is AnkhNorth or AnkhWest)
{
found = true;
break;
}
}
if (!found)
{
continue;
}
pl = new Player(ethic, e.Mobile);
pl.Attach();
e.Mobile.FixedEffect(0x373A, 10, 30);
e.Mobile.PlaySound(0x209);
e.Handled = true;
break;
continue;
}
if (!power.CheckInvoke(pl))
{
continue;
}
power.BeginInvoke(pl);
e.Handled = true;
break;
}
else
}
}
public static Ethic Find(Mobile mob) => Find(mob, false, false);
public static Ethic Find(Mobile mob, bool inherit) => Find(mob, inherit, false);
public static Ethic Find(Mobile mob, bool inherit, bool allegiance)
{
var pl = Player.Find(mob);
if (pl != null)
{
return pl.Ethic;
}
if (inherit && mob is BaseCreature bc)
{
if (bc.Controlled)
{
if (e.Mobile is PlayerMobile mobile && mobile.DuelContext != null)
{
return;
}
return Find(bc.ControlMaster, false);
}
var ethic = pl.Ethic;
if (bc.Summoned)
{
return Find(bc.SummonMaster, false);
}
for (var i = 0; i < ethic.Definition.Powers.Length; ++i)
{
var power = ethic.Definition.Powers[i];
if (!power.Definition.Phrase.String.InsensitiveEquals(e.Speech))
{
continue;
}
if (!power.CheckInvoke(pl))
{
continue;
}
power.BeginInvoke(pl);
e.Handled = true;
break;
}
if (allegiance)
{
return bc.EthicAllegiance;
}
}
public static Ethic Find(Mobile mob) => Find(mob, false, false);
return null;
}
public static Ethic Find(Mobile mob, bool inherit) => Find(mob, inherit, false);
public abstract bool IsEligible(Mobile mob);
public static Ethic Find(Mobile mob, bool inherit, bool allegiance)
private void Deserialize(IGenericReader reader, int version)
{
var playerCount = reader.ReadEncodedInt();
for (var i = 0; i < playerCount; ++i)
{
var pl = Player.Find(mob);
var pl = new Player(this, null);
pl.Deserialize(reader);
if (pl != null)
if (pl.Mobile != null)
{
return pl.Ethic;
}
if (inherit && mob is BaseCreature bc)
{
if (bc.Controlled)
{
return Find(bc.ControlMaster, false);
}
if (bc.Summoned)
{
return Find(bc.SummonMaster, false);
}
if (allegiance)
{
return bc.EthicAllegiance;
}
}
return null;
}
public abstract bool IsEligible(Mobile mob);
public virtual void Deserialize(IGenericReader reader)
{
var version = reader.ReadEncodedInt();
switch (version)
{
case 0:
{
var playerCount = reader.ReadEncodedInt();
for (var i = 0; i < playerCount; ++i)
{
var pl = new Player(this, reader);
if (pl.Mobile != null)
{
Timer.StartTimer(pl.CheckAttach);
}
}
break;
}
}
}
public virtual void Serialize(IGenericWriter writer)
{
writer.WriteEncodedInt(0); // version
writer.WriteEncodedInt(m_Players.Count);
for (var i = 0; i < m_Players.Count; ++i)
{
m_Players[i].Serialize(writer);
Timer.StartTimer(pl.CheckAttach);
}
}
}

View file

@ -0,0 +1,26 @@
using System;
using ModernUO.Serialization;
namespace Server.Ethics;
[SerializationGenerator(0)]
public partial class EthicsEntity : ISerializable
{
public EthicsEntity()
{
Serial = EthicsSystem.NewProfile;
EthicsSystem.Add(this);
}
public DateTime Created { get; set; } = Core.Now;
public Serial Serial { get; }
public bool Deleted { get; private set; }
public void Delete()
{
Deleted = true;
EthicsSystem.Remove(this);
}
}

View file

@ -0,0 +1,59 @@
using System;
using Server.Ethics.Evil;
using Server.Ethics.Hero;
namespace Server.Ethics;
public class EthicsSystem : GenericEntityPersistence<EthicsEntity>
{
private static EthicsSystem _persistence;
public static void Configure()
{
_persistence = new EthicsSystem();
}
public EthicsSystem() : base("Ethics", 3, 0x1, 0x7FFFFFFF)
{
}
public static Serial NewProfile => _persistence.NewEntity;
public static void Add(EthicsEntity entity) => _persistence.AddEntity(entity);
public static void Remove(EthicsEntity entity) => _persistence.RemoveEntity(entity);
public static T Find<T>(Serial serial) where T : EthicsEntity => _persistence.FindEntity<T>(serial);
}
[ManualDirtyChecking]
[TypeAlias("Server.Ethics.EthicsPersistance")]
[Obsolete("Deprecated in favor of the static system. Only used for legacy deserialization")]
public class EthicsPersistence : Item
{
public EthicsPersistence()
{
Delete();
}
public EthicsPersistence(Serial serial) : base(serial)
{
}
public override void Serialize(IGenericWriter writer)
{
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var heroEthic = new HeroEthic();
heroEthic.Deserialize(reader);
var evilEthic = new EvilEthic();
evilEthic.Deserialize(reader);
Timer.DelayCall(Delete);
}
}

View file

@ -1,65 +0,0 @@
namespace Server.Ethics
{
public class EthicsPersistance : Item
{
[Constructible]
public EthicsPersistance()
: base(1)
{
Movable = false;
if (Instance?.Deleted != false)
{
Instance = this;
}
else
{
base.Delete();
}
}
public EthicsPersistance(Serial serial)
: base(serial) =>
Instance = this;
public static EthicsPersistance Instance { get; private set; }
public override string DefaultName => "Ethics Persistance - Internal";
public override void Serialize(IGenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
for (var i = 0; i < Ethic.Ethics.Length; ++i)
{
Ethic.Ethics[i].Serialize(writer);
}
}
public override void Deserialize(IGenericReader reader)
{
base.Deserialize(reader);
var version = reader.ReadInt();
switch (version)
{
case 0:
{
for (var i = 0; i < Ethic.Ethics.Length; ++i)
{
Ethic.Ethics[i].Deserialize(reader);
}
break;
}
}
}
public override void Delete()
{
}
}
}

View file

@ -1,169 +1,141 @@
using System;
using System.Collections.ObjectModel;
using ModernUO.Serialization;
using Server.Mobiles;
namespace Server.Ethics
namespace Server.Ethics;
[PropertyObject]
[SerializationGenerator(1)]
public partial class Player : EthicsEntity
{
public class PlayerCollection : Collection<Player>
[SerializableField(0, setter: "private")]
private Mobile _mobile;
[SerializableField(1)]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
private int _power;
[SerializableField(2)]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
private int _history;
[SerializableField(3)]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
private Mobile _steed;
[SerializableField(4)]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
private Mobile _familiar;
[DeltaDateTime]
[SerializableField(5, setter: "private")]
private DateTime _shield;
[SerializableField(6, setter: "private")]
private Ethic _ethic;
public Player(Ethic ethic, Mobile mobile)
{
_ethic = ethic;
_mobile = mobile;
_power = 5;
_history = 5;
}
[PropertyObject]
public class Player
private void Deserialize(IGenericReader reader, int version)
{
private DateTime m_Shield;
// Don't call the base Deserialize
public Player(Ethic ethic, Mobile mobile)
_mobile = reader.ReadEntity<Mobile>();
_power = reader.ReadEncodedInt();
_history = reader.ReadEncodedInt();
_steed = reader.ReadEntity<Mobile>();
_familiar = reader.ReadEntity<Mobile>();
_shield = reader.ReadDeltaTime();
}
[CommandProperty(AccessLevel.GameMaster)]
public bool IsShielded
{
get
{
Ethic = ethic;
Mobile = mobile;
Power = 5;
History = 5;
}
public Player(Ethic ethic, IGenericReader reader)
{
Ethic = ethic;
var version = reader.ReadEncodedInt();
switch (version)
if (_shield == DateTime.MinValue)
{
case 0:
{
Mobile = reader.ReadEntity<Mobile>();
Power = reader.ReadEncodedInt();
History = reader.ReadEncodedInt();
Steed = reader.ReadEntity<Mobile>();
Familiar = reader.ReadEntity<Mobile>();
m_Shield = reader.ReadDeltaTime();
break;
}
}
}
public Ethic Ethic { get; }
public Mobile Mobile { get; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public int Power { get; set; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public int History { get; set; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public Mobile Steed { get; set; }
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
public Mobile Familiar { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool IsShielded
{
get
{
if (m_Shield == DateTime.MinValue)
{
return false;
}
if (Core.Now < m_Shield + TimeSpan.FromHours(1.0))
{
return true;
}
FinishShield();
return false;
}
if (Core.Now < _shield + TimeSpan.FromHours(1.0))
{
return true;
}
FinishShield();
return false;
}
}
public static Player Find(Mobile mob) => Find(mob, false);
public static Player Find(Mobile mob) => Find(mob, false);
public static Player Find(Mobile mob, bool inherit)
public static Player Find(Mobile mob, bool inherit)
{
var pm = mob as PlayerMobile;
if (pm == null)
{
var pm = mob as PlayerMobile;
if (inherit && mob is BaseCreature bc)
{
pm = bc.GetMaster() as PlayerMobile;
}
if (pm == null)
{
if (inherit && mob is BaseCreature bc)
{
if (bc.Controlled)
{
pm = bc.ControlMaster as PlayerMobile;
}
else if (bc.Summoned)
{
pm = bc.SummonMaster as PlayerMobile;
}
}
if (pm == null)
{
return null;
}
}
var pl = pm.EthicPlayer;
if (pl?.Ethic.IsEligible(pl.Mobile) == false)
{
pm.EthicPlayer = pl = null;
}
return pl;
}
public void BeginShield() => m_Shield = Core.Now;
public void FinishShield() => m_Shield = DateTime.MinValue;
public void CheckAttach()
{
if (Ethic.IsEligible(Mobile))
{
Attach();
return null;
}
}
public void Attach()
{
if (Mobile is PlayerMobile mobile)
{
mobile.EthicPlayer = this;
}
var pl = pm.EthicPlayer;
Ethic.Players.Add(this);
if (pl?.Ethic.IsEligible(pl.Mobile) == false)
{
pm.EthicPlayer = pl = null;
}
public void Detach()
return pl;
}
public void BeginShield() => _shield = Core.Now;
public void FinishShield() => _shield = DateTime.MinValue;
public void CheckAttach()
{
if (Ethic.IsEligible(Mobile))
{
if (Mobile is PlayerMobile mobile)
{
mobile.EthicPlayer = null;
}
Ethic.Players.Remove(this);
}
public void Serialize(IGenericWriter writer)
{
writer.WriteEncodedInt(0); // version
writer.Write(Mobile);
writer.WriteEncodedInt(Power);
writer.WriteEncodedInt(History);
writer.Write(Steed);
writer.Write(Familiar);
writer.WriteDeltaTime(m_Shield);
Attach();
}
}
public void Attach()
{
if (Mobile is PlayerMobile mobile)
{
mobile.EthicPlayer = this;
}
Ethic.Players.Add(this);
}
public void Detach()
{
if (Mobile is PlayerMobile mobile)
{
mobile.EthicPlayer = null;
}
Ethic.Players.Remove(this);
}
}

View file

@ -1,37 +1,31 @@
namespace Server.Ethics
namespace Server.Ethics;
public abstract class Power
{
public abstract class Power
public PowerDefinition Definition { get; protected set; }
public virtual bool CheckInvoke(Player from)
{
protected PowerDefinition m_Definition;
public PowerDefinition Definition => m_Definition;
public virtual bool CheckInvoke(Player from)
if (!from.Mobile.CheckAlive())
{
if (!from.Mobile.CheckAlive())
{
return false;
}
if (from.Power < m_Definition.Power)
{
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You lack the power to invoke this ability."
);
return false;
}
return true;
return false;
}
public abstract void BeginInvoke(Player from);
public virtual void FinishInvoke(Player from)
if (from.Power < Definition.Power)
{
from.Power -= m_Definition.Power;
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You lack the power to invoke this ability."
);
return false;
}
return true;
}
public abstract void BeginInvoke(Player from);
public virtual void FinishInvoke(Player from) => from.Power -= Definition.Power;
}

View file

@ -1,30 +1,29 @@
namespace Server.Ethics
namespace Server.Ethics;
public class EthicDefinition
{
public class EthicDefinition
public EthicDefinition(
int primaryHue, TextDefinition title, TextDefinition adjunct, TextDefinition joinPhrase,
Power[] powers
)
{
public EthicDefinition(
int primaryHue, TextDefinition title, TextDefinition adjunct, TextDefinition joinPhrase,
Power[] powers
)
{
PrimaryHue = primaryHue;
PrimaryHue = primaryHue;
Title = title;
Adjunct = adjunct;
Title = title;
Adjunct = adjunct;
JoinPhrase = joinPhrase;
JoinPhrase = joinPhrase;
Powers = powers;
}
public int PrimaryHue { get; }
public TextDefinition Title { get; }
public TextDefinition Adjunct { get; }
public TextDefinition JoinPhrase { get; }
public Power[] Powers { get; }
Powers = powers;
}
public int PrimaryHue { get; }
public TextDefinition Title { get; }
public TextDefinition Adjunct { get; }
public TextDefinition JoinPhrase { get; }
public Power[] Powers { get; }
}

View file

@ -1,22 +1,21 @@
namespace Server.Ethics
namespace Server.Ethics;
public class PowerDefinition
{
public class PowerDefinition
public PowerDefinition(int power, TextDefinition name, TextDefinition phrase, TextDefinition description)
{
public PowerDefinition(int power, TextDefinition name, TextDefinition phrase, TextDefinition description)
{
Power = power;
Power = power;
Name = name;
Phrase = phrase;
Description = description;
}
public int Power { get; }
public TextDefinition Name { get; }
public TextDefinition Phrase { get; }
public TextDefinition Description { get; }
Name = name;
Phrase = phrase;
Description = description;
}
}
public int Power { get; }
public TextDefinition Name { get; }
public TextDefinition Phrase { get; }
public TextDefinition Description { get; }
}

View file

@ -1,35 +1,44 @@
using ModernUO.Serialization;
using Server.Factions;
namespace Server.Ethics.Evil
namespace Server.Ethics.Evil;
[SerializationGenerator(0, false)]
public sealed partial class EvilEthic : Ethic
{
public sealed class EvilEthic : Ethic
public override EthicDefinition Definition => new(
0x455,
"Evil",
"(Evil)",
"I am evil incarnate",
[
new UnholySense(),
new UnholyItem(),
new SummonFamiliar(),
new VileBlade(),
new Blight(),
new UnholyShield(),
new UnholySteed(),
new UnholyWord()
]
);
public EvilEthic()
{
public EvilEthic()
if (!RegisterEthic(this))
{
m_Definition = new EthicDefinition(
0x455,
"Evil",
"(Evil)",
"I am evil incarnate",
new Power[]
{
new UnholySense(),
new UnholyItem(),
new SummonFamiliar(),
new VileBlade(),
new Blight(),
new UnholyShield(),
new UnholySteed(),
new UnholyWord()
}
);
Delete();
}
}
public override bool IsEligible(Mobile mob)
public override bool IsEligible(Mobile mob) => Faction.Find(mob) is Minax or Shadowlords;
[AfterDeserialization]
private void AfterDeserialization()
{
if (!RegisterEthic(this))
{
var fac = Faction.Find(mob);
return fac is Minax or Shadowlords;
Delete();
}
}
}

View file

@ -1,92 +1,75 @@
using ModernUO.Serialization;
using Server.Ethics;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class UnholySteed : BaseMount
{
public class UnholySteed : BaseMount
public override string DefaultName => "a dark steed";
[Constructible]
public UnholySteed() : base(0x74, 0x3EA7, AIType.AI_Melee, FightMode.Aggressor)
{
public override string DefaultName => "a dark steed";
SetStr(496, 525);
SetDex(86, 105);
SetInt(86, 125);
[Constructible]
public UnholySteed() : base(0x74, 0x3EA7, AIType.AI_Melee, FightMode.Aggressor)
SetHits(298, 315);
SetDamage(16, 22);
SetDamageType(ResistanceType.Physical, 40);
SetDamageType(ResistanceType.Fire, 40);
SetDamageType(ResistanceType.Energy, 20);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 30, 40);
SetResistance(ResistanceType.Cold, 30, 40);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 20, 30);
SetSkill(SkillName.MagicResist, 25.1, 30.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 80.5, 92.5);
Fame = 14000;
Karma = -14000;
VirtualArmor = 60;
Tamable = false;
ControlSlots = 1;
}
public override int StepsMax => 6400;
public override string CorpseName => "an unholy corpse";
public override bool IsDispellable => false;
public override bool IsBondable => false;
public override FoodType FavoriteFood => FoodType.FruitsAndVeggies | FoodType.GrainsAndHay;
private static MonsterAbility[] _abilities = { MonsterAbilities.FireBreath };
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
public override string ApplyNameSuffix(string suffix)
{
if (suffix.Length == 0)
{
SetStr(496, 525);
SetDex(86, 105);
SetInt(86, 125);
SetHits(298, 315);
SetDamage(16, 22);
SetDamageType(ResistanceType.Physical, 40);
SetDamageType(ResistanceType.Fire, 40);
SetDamageType(ResistanceType.Energy, 20);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 30, 40);
SetResistance(ResistanceType.Cold, 30, 40);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 20, 30);
SetSkill(SkillName.MagicResist, 25.1, 30.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 80.5, 92.5);
Fame = 14000;
Karma = -14000;
VirtualArmor = 60;
Tamable = false;
ControlSlots = 1;
return base.ApplyNameSuffix(Ethic.Evil.Definition.Adjunct.String);
}
public UnholySteed(Serial serial) : base(serial)
return base.ApplyNameSuffix($"{suffix} {Ethic.Evil.Definition.Adjunct.String}");
}
public override void OnDoubleClick(Mobile from)
{
if (Ethic.Find(from) != Ethic.Evil)
{
from.SendMessage("You may not ride this steed.");
}
public override int StepsMax => 6400;
public override string CorpseName => "an unholy corpse";
public override bool IsDispellable => false;
public override bool IsBondable => false;
public override FoodType FavoriteFood => FoodType.FruitsAndVeggies | FoodType.GrainsAndHay;
private static MonsterAbility[] _abilities = { MonsterAbilities.FireBreath };
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
public override string ApplyNameSuffix(string suffix)
else
{
if (suffix.Length == 0)
{
return base.ApplyNameSuffix(Ethic.Evil.Definition.Adjunct.String);
}
return base.ApplyNameSuffix($"{suffix} {Ethic.Evil.Definition.Adjunct.String}");
}
public override void OnDoubleClick(Mobile from)
{
if (Ethic.Find(from) != Ethic.Evil)
{
from.SendMessage("You may not ride this steed.");
}
else
{
base.OnDoubleClick(from);
}
}
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();
base.OnDoubleClick(from);
}
}
}

View file

@ -2,82 +2,81 @@ using System;
using Server.Spells;
using Server.Targeting;
namespace Server.Ethics.Evil
{
public sealed class Blight : Power
{
public Blight() =>
m_Definition = new PowerDefinition(
15,
"Blight",
"Velgo Ontawl",
""
);
namespace Server.Ethics.Evil;
public override void BeginInvoke(Player from)
public sealed class Blight : Power
{
public Blight() =>
Definition = new PowerDefinition(
15,
"Blight",
"Velgo Ontawl",
""
);
public override void BeginInvoke(Player from)
{
from.Mobile.BeginTarget(12, true, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Where do you wish to blight?");
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
{
if (obj is not IPoint3D p)
{
from.Mobile.BeginTarget(12, true, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Where do you wish to blight?");
return;
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
if (!CheckInvoke(from))
{
if (obj is not IPoint3D p)
return;
}
var powerFunctioned = false;
SpellHelper.GetSurfaceTop(ref p);
foreach (var mob in from.Mobile.GetMobilesInRange(6))
{
if (mob == from.Mobile || !SpellHelper.ValidIndirectTarget(from.Mobile, mob))
{
return;
continue;
}
if (!CheckInvoke(from))
if (mob.GetStatMod("Holy Curse") != null)
{
return;
continue;
}
var powerFunctioned = false;
SpellHelper.GetSurfaceTop(ref p);
foreach (var mob in from.Mobile.GetMobilesInRange(6))
if (!from.Mobile.CanBeHarmful(mob, false))
{
if (mob == from.Mobile || !SpellHelper.ValidIndirectTarget(from.Mobile, mob))
{
continue;
}
if (mob.GetStatMod("Holy Curse") != null)
{
continue;
}
if (!from.Mobile.CanBeHarmful(mob, false))
{
continue;
}
from.Mobile.DoHarmful(mob, true);
mob.AddStatMod(new StatMod(StatType.All, "Holy Curse", -10, TimeSpan.FromMinutes(30.0)));
mob.FixedParticles(0x374A, 10, 15, 5028, EffectLayer.Waist);
mob.PlaySound(0x1FB);
powerFunctioned = true;
continue;
}
if (powerFunctioned)
{
SpellHelper.Turn(from.Mobile, p);
from.Mobile.DoHarmful(mob, true);
Effects.PlaySound(new Point3D(p), from.Mobile.Map, 0x1FB);
mob.AddStatMod(new StatMod(StatType.All, "Holy Curse", -10, TimeSpan.FromMinutes(30.0)));
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You curse the area.");
mob.FixedParticles(0x374A, 10, 15, 5028, EffectLayer.Waist);
mob.PlaySound(0x1FB);
FinishInvoke(from);
}
else
{
from.Mobile.FixedEffect(0x3735, 6, 30);
from.Mobile.PlaySound(0x5C);
}
powerFunctioned = true;
}
if (powerFunctioned)
{
SpellHelper.Turn(from.Mobile, p);
Effects.PlaySound(new Point3D(p), from.Mobile.Map, 0x1FB);
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You curse the area.");
FinishInvoke(from);
}
else
{
from.Mobile.FixedEffect(0x3735, 6, 30);
from.Mobile.PlaySound(0x5C);
}
}
}
}

View file

@ -1,45 +1,44 @@
using System;
using Server.Mobiles;
namespace Server.Ethics.Evil
namespace Server.Ethics.Evil;
public sealed class SummonFamiliar : Power
{
public sealed class SummonFamiliar : Power
public SummonFamiliar() =>
Definition = new PowerDefinition(
5,
"Summon Familiar",
"Trubechs Vingir",
""
);
public override void BeginInvoke(Player from)
{
public SummonFamiliar() =>
m_Definition = new PowerDefinition(
5,
"Summon Familiar",
"Trubechs Vingir",
""
);
public override void BeginInvoke(Player from)
if (from.Familiar?.Deleted == true)
{
if (from.Familiar?.Deleted == true)
{
from.Familiar = null;
}
from.Familiar = null;
}
if (from.Familiar != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have an unholy familiar.");
return;
}
if (from.Familiar != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have an unholy familiar.");
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
var familiar = new UnholyFamiliar();
var familiar = new UnholyFamiliar();
if (BaseCreature.Summon(familiar, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Familiar = familiar;
if (BaseCreature.Summon(familiar, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Familiar = familiar;
FinishInvoke(from);
}
FinishInvoke(from);
}
}
}
}

View file

@ -1,71 +1,70 @@
using Server.Items;
using Server.Targeting;
namespace Server.Ethics.Evil
{
public sealed class UnholyItem : Power
{
public UnholyItem() =>
m_Definition = new PowerDefinition(
5,
"Unholy Item",
"Vidda K'balc",
""
);
namespace Server.Ethics.Evil;
public override void BeginInvoke(Player from)
public sealed class UnholyItem : Power
{
public UnholyItem() =>
Definition = new PowerDefinition(
5,
"Unholy Item",
"Vidda K'balc",
""
);
public override void BeginInvoke(Player from)
{
from.Mobile.BeginTarget(12, false, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Which item do you wish to imbue?");
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
{
if (obj is not Item item)
{
from.Mobile.BeginTarget(12, false, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Which item do you wish to imbue?");
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
return;
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
if (item.Parent != from.Mobile)
{
if (obj is not Item item)
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You may only imbue items you are wearing."
);
return;
}
if ((item.SavedFlags & 0x300) != 0)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "That has already been imbued.");
return;
}
var canImbue = item is Spellbook or BaseClothing or BaseArmor or BaseWeapon &&
item.Name == null;
if (canImbue)
{
if (!CheckInvoke(from))
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
return;
}
if (item.Parent != from.Mobile)
{
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You may only imbue items you are wearing."
);
return;
}
item.Hue = Ethic.Evil.Definition.PrimaryHue;
item.SavedFlags |= 0x200;
if ((item.SavedFlags & 0x300) != 0)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "That has already been imbued.");
return;
}
from.Mobile.FixedEffect(0x375A, 10, 20);
from.Mobile.PlaySound(0x209);
var canImbue = item is Spellbook or BaseClothing or BaseArmor or BaseWeapon &&
item.Name == null;
if (canImbue)
{
if (!CheckInvoke(from))
{
return;
}
item.Hue = Ethic.Evil.Definition.PrimaryHue;
item.SavedFlags |= 0x200;
from.Mobile.FixedEffect(0x375A, 10, 20);
from.Mobile.PlaySound(0x209);
FinishInvoke(from);
}
else
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
}
FinishInvoke(from);
}
else
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
}
}
}
}

View file

@ -1,97 +1,76 @@
using System;
using Server.Text;
namespace Server.Ethics.Evil
namespace Server.Ethics.Evil;
public sealed class UnholySense : Power
{
public sealed class UnholySense : Power
public UnholySense() =>
Definition = new PowerDefinition(
0,
"Unholy Sense",
"Drewrok Velgo",
""
);
public override void BeginInvoke(Player from)
{
public UnholySense() =>
m_Definition = new PowerDefinition(
0,
"Unholy Sense",
"Drewrok Velgo",
""
);
var opposition = Ethic.Hero;
public override void BeginInvoke(Player from)
var enemyCount = 0;
var maxRange = 18 + from.Power;
Player primary = null;
foreach (var pl in opposition.Players)
{
var opposition = Ethic.Hero;
var mob = pl.Mobile;
var enemyCount = 0;
var maxRange = 18 + from.Power;
Player primary = null;
foreach (var pl in opposition.Players)
if (mob == null || mob.Map != from.Mobile.Map || !mob.Alive)
{
var mob = pl.Mobile;
if (mob == null || mob.Map != from.Mobile.Map || !mob.Alive)
{
continue;
}
if (!mob.InRange(from.Mobile, Math.Max(18, maxRange - pl.Power)))
{
continue;
}
if (primary == null || pl.Power > primary.Power)
{
primary = pl;
}
++enemyCount;
continue;
}
using var sb = ValueStringBuilder.Create();
sb.Append("You sense ");
sb.Append(enemyCount == 0 ? "no" : enemyCount.ToString());
sb.Append(enemyCount == 1 ? " enemy" : " enemies");
if (primary != null)
if (!mob.InRange(from.Mobile, Math.Max(18, maxRange - pl.Power)))
{
sb.Append(", and a strong presence");
switch (from.Mobile.GetDirectionTo(primary.Mobile))
{
case Direction.West:
sb.Append(" to the west.");
break;
case Direction.East:
sb.Append(" to the east.");
break;
case Direction.North:
sb.Append(" to the north.");
break;
case Direction.South:
sb.Append(" to the south.");
break;
case Direction.Up:
sb.Append(" to the north-west.");
break;
case Direction.Down:
sb.Append(" to the south-east.");
break;
case Direction.Left:
sb.Append(" to the south-west.");
break;
case Direction.Right:
sb.Append(" to the north-east.");
break;
}
}
else
{
sb.Append('.');
continue;
}
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x59, false, sb.ToString());
if (primary == null || pl.Power > primary.Power)
{
primary = pl;
}
FinishInvoke(from);
++enemyCount;
}
using var sb = ValueStringBuilder.Create();
sb.Append($"You sense {(enemyCount == 0 ? "no" : enemyCount.ToString())} {(enemyCount == 1 ? "enemy" : "enemies")}");
if (primary != null)
{
var direction = from.Mobile.GetDirectionTo(primary.Mobile) switch
{
Direction.East => "east",
Direction.North => "north",
Direction.South => "south",
Direction.Up => "north-west",
Direction.Down => "south-east",
Direction.Left => "south-west",
Direction.Right => "north-east",
_ => "west"
};
sb.Append($", and a strong presence to the {direction}.");
}
else
{
sb.Append('.');
}
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x59, false, sb.ToString());
FinishInvoke(from);
}
}

View file

@ -1,38 +1,37 @@
namespace Server.Ethics.Evil
namespace Server.Ethics.Evil;
public sealed class UnholyShield : Power
{
public sealed class UnholyShield : Power
public UnholyShield() =>
Definition = new PowerDefinition(
20,
"Unholy Shield",
"Velgo K'blac",
""
);
public override void BeginInvoke(Player from)
{
public UnholyShield() =>
m_Definition = new PowerDefinition(
20,
"Unholy Shield",
"Velgo K'blac",
""
);
public override void BeginInvoke(Player from)
if (from.IsShielded)
{
if (from.IsShielded)
{
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You are already under the protection of an unholy shield."
);
return;
}
from.BeginShield();
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You are now under the protection of an unholy shield."
"You are already under the protection of an unholy shield."
);
FinishInvoke(from);
return;
}
from.BeginShield();
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You are now under the protection of an unholy shield."
);
FinishInvoke(from);
}
}
}

View file

@ -1,45 +1,44 @@
using System;
using Server.Mobiles;
namespace Server.Ethics.Evil
namespace Server.Ethics.Evil;
public sealed class UnholySteed : Power
{
public sealed class UnholySteed : Power
public UnholySteed() =>
Definition = new PowerDefinition(
30,
"Unholy Steed",
"Trubechs Yeliab",
""
);
public override void BeginInvoke(Player from)
{
public UnholySteed() =>
m_Definition = new PowerDefinition(
30,
"Unholy Steed",
"Trubechs Yeliab",
""
);
public override void BeginInvoke(Player from)
if (from.Steed?.Deleted == true)
{
if (from.Steed?.Deleted == true)
{
from.Steed = null;
}
from.Steed = null;
}
if (from.Steed != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have an unholy steed.");
return;
}
if (from.Steed != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have an unholy steed.");
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
var steed = new Mobiles.UnholySteed();
var steed = new Mobiles.UnholySteed();
if (BaseCreature.Summon(steed, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Steed = steed;
if (BaseCreature.Summon(steed, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Steed = steed;
FinishInvoke(from);
}
FinishInvoke(from);
}
}
}
}

View file

@ -1,17 +1,16 @@
namespace Server.Ethics.Evil
{
public sealed class UnholyWord : Power
{
public UnholyWord() =>
m_Definition = new PowerDefinition(
100,
"Unholy Word",
"Velgo Oostrac",
""
);
namespace Server.Ethics.Evil;
public override void BeginInvoke(Player from)
{
}
public sealed class UnholyWord : Power
{
public UnholyWord() =>
Definition = new PowerDefinition(
100,
"Unholy Word",
"Velgo Oostrac",
""
);
public override void BeginInvoke(Player from)
{
}
}
}

View file

@ -1,17 +1,16 @@
namespace Server.Ethics.Evil
{
public sealed class VileBlade : Power
{
public VileBlade() =>
m_Definition = new PowerDefinition(
10,
"Vile Blade",
"Velgo Reyam",
""
);
namespace Server.Ethics.Evil;
public override void BeginInvoke(Player from)
{
}
public sealed class VileBlade : Power
{
public VileBlade() =>
Definition = new PowerDefinition(
10,
"Vile Blade",
"Velgo Reyam",
""
);
public override void BeginInvoke(Player from)
{
}
}
}

View file

@ -1,40 +1,44 @@
using ModernUO.Serialization;
using Server.Factions;
namespace Server.Ethics.Hero
namespace Server.Ethics.Hero;
[SerializationGenerator(0, false)]
public sealed partial class HeroEthic : Ethic
{
public sealed class HeroEthic : Ethic
public override EthicDefinition Definition => new(
0x482,
"Hero",
"(Hero)",
"I will defend the virtues",
[
new HolySense(),
new HolyItem(),
new SummonFamiliar(),
new HolyBlade(),
new Bless(),
new HolyShield(),
new HolySteed(),
new HolyWord()
]
);
public HeroEthic()
{
public HeroEthic()
if (!RegisterEthic(this))
{
m_Definition = new EthicDefinition(
0x482,
"Hero",
"(Hero)",
"I will defend the virtues",
new Power[]
{
new HolySense(),
new HolyItem(),
new SummonFamiliar(),
new HolyBlade(),
new Bless(),
new HolyShield(),
new HolySteed(),
new HolyWord()
}
);
Delete();
}
}
public override bool IsEligible(Mobile mob)
public override bool IsEligible(Mobile mob) => mob.Kills < 5 && Faction.Find(mob) is TrueBritannians or CouncilOfMages;
[AfterDeserialization]
private void AfterDeserialization()
{
if (!RegisterEthic(this))
{
if (mob.Kills >= 5)
{
return false;
}
var fac = Faction.Find(mob);
return fac is TrueBritannians or CouncilOfMages;
Delete();
}
}
}

View file

@ -1,91 +1,75 @@
using ModernUO.Serialization;
using Server.Ethics;
namespace Server.Mobiles
namespace Server.Mobiles;
[SerializationGenerator(0, false)]
public partial class HolySteed : BaseMount
{
public class HolySteed : BaseMount
public override string DefaultName => "a silver steed";
[Constructible]
public HolySteed() : base(0x75, 0x3EA8, AIType.AI_Melee, FightMode.Aggressor)
{
public override string DefaultName => "a silver steed";
[Constructible]
public HolySteed() : base(0x75, 0x3EA8, AIType.AI_Melee, FightMode.Aggressor)
SetStr(496, 525);
SetDex(86, 105);
SetInt(86, 125);
SetHits(298, 315);
SetDamage(16, 22);
SetDamageType(ResistanceType.Physical, 40);
SetDamageType(ResistanceType.Fire, 40);
SetDamageType(ResistanceType.Energy, 20);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 30, 40);
SetResistance(ResistanceType.Cold, 30, 40);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 20, 30);
SetSkill(SkillName.MagicResist, 25.1, 30.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 80.5, 92.5);
Fame = 14000;
Karma = 14000;
VirtualArmor = 60;
Tamable = false;
ControlSlots = 1;
}
public override int StepsMax => 6400;
public override string CorpseName => "a holy corpse";
public override bool IsDispellable => false;
public override bool IsBondable => false;
public override FoodType FavoriteFood => FoodType.FruitsAndVeggies | FoodType.GrainsAndHay;
private static MonsterAbility[] _abilities = [MonsterAbilities.FireBreath];
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
public override string ApplyNameSuffix(string suffix)
{
if (suffix.Length == 0)
{
SetStr(496, 525);
SetDex(86, 105);
SetInt(86, 125);
SetHits(298, 315);
SetDamage(16, 22);
SetDamageType(ResistanceType.Physical, 40);
SetDamageType(ResistanceType.Fire, 40);
SetDamageType(ResistanceType.Energy, 20);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 30, 40);
SetResistance(ResistanceType.Cold, 30, 40);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 20, 30);
SetSkill(SkillName.MagicResist, 25.1, 30.0);
SetSkill(SkillName.Tactics, 97.6, 100.0);
SetSkill(SkillName.Wrestling, 80.5, 92.5);
Fame = 14000;
Karma = 14000;
VirtualArmor = 60;
Tamable = false;
ControlSlots = 1;
return base.ApplyNameSuffix(Ethic.Hero.Definition.Adjunct.String);
}
public HolySteed(Serial serial) : base(serial)
return base.ApplyNameSuffix($"{suffix} {Ethic.Hero.Definition.Adjunct.String}");
}
public override void OnDoubleClick(Mobile from)
{
if (Ethic.Find(from) != Ethic.Hero)
{
from.SendMessage("You may not ride this steed.");
}
public override int StepsMax => 6400;
public override string CorpseName => "a holy corpse";
public override bool IsDispellable => false;
public override bool IsBondable => false;
public override FoodType FavoriteFood => FoodType.FruitsAndVeggies | FoodType.GrainsAndHay;
private static MonsterAbility[] _abilities = { MonsterAbilities.FireBreath };
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
public override string ApplyNameSuffix(string suffix)
else
{
if (suffix.Length == 0)
{
return base.ApplyNameSuffix(Ethic.Hero.Definition.Adjunct.String);
}
return base.ApplyNameSuffix($"{suffix} {Ethic.Hero.Definition.Adjunct.String}");
}
public override void OnDoubleClick(Mobile from)
{
if (Ethic.Find(from) != Ethic.Hero)
{
from.SendMessage("You may not ride this steed.");
}
else
{
base.OnDoubleClick(from);
}
}
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();
base.OnDoubleClick(from);
}
}
}

View file

@ -2,82 +2,81 @@ using System;
using Server.Spells;
using Server.Targeting;
namespace Server.Ethics.Hero
{
public sealed class Bless : Power
{
public Bless() =>
m_Definition = new PowerDefinition(
15,
"Bless",
"Erstok Ontawl",
""
);
namespace Server.Ethics.Hero;
public override void BeginInvoke(Player from)
public sealed class Bless : Power
{
public Bless() =>
Definition = new PowerDefinition(
15,
"Bless",
"Erstok Ontawl",
""
);
public override void BeginInvoke(Player from)
{
from.Mobile.BeginTarget(12, true, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Where do you wish to bless?");
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
{
if (obj is not IPoint3D p)
{
from.Mobile.BeginTarget(12, true, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Where do you wish to bless?");
return;
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
if (!CheckInvoke(from))
{
if (obj is not IPoint3D p)
return;
}
var powerFunctioned = false;
SpellHelper.GetSurfaceTop(ref p);
foreach (var mob in from.Mobile.GetMobilesInRange(6))
{
if (mob != from.Mobile && SpellHelper.ValidIndirectTarget(from.Mobile, mob))
{
return;
continue;
}
if (!CheckInvoke(from))
if (mob.GetStatMod("Holy Bless") != null)
{
return;
continue;
}
var powerFunctioned = false;
SpellHelper.GetSurfaceTop(ref p);
foreach (var mob in from.Mobile.GetMobilesInRange(6))
if (!from.Mobile.CanBeBeneficial(mob, false))
{
if (mob != from.Mobile && SpellHelper.ValidIndirectTarget(from.Mobile, mob))
{
continue;
}
if (mob.GetStatMod("Holy Bless") != null)
{
continue;
}
if (!from.Mobile.CanBeBeneficial(mob, false))
{
continue;
}
from.Mobile.DoBeneficial(mob);
mob.AddStatMod(new StatMod(StatType.All, "Holy Bless", 10, TimeSpan.FromMinutes(30.0)));
mob.FixedParticles(0x373A, 10, 15, 5018, EffectLayer.Waist);
mob.PlaySound(0x1EA);
powerFunctioned = true;
continue;
}
if (powerFunctioned)
{
SpellHelper.Turn(from.Mobile, p);
from.Mobile.DoBeneficial(mob);
Effects.PlaySound(new Point3D(p), from.Mobile.Map, 0x299);
mob.AddStatMod(new StatMod(StatType.All, "Holy Bless", 10, TimeSpan.FromMinutes(30.0)));
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You consecrate the area.");
mob.FixedParticles(0x373A, 10, 15, 5018, EffectLayer.Waist);
mob.PlaySound(0x1EA);
FinishInvoke(from);
}
else
{
from.Mobile.FixedEffect(0x3735, 6, 30);
from.Mobile.PlaySound(0x5C);
}
powerFunctioned = true;
}
if (powerFunctioned)
{
SpellHelper.Turn(from.Mobile, p);
Effects.PlaySound(new Point3D(p), from.Mobile.Map, 0x299);
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You consecrate the area.");
FinishInvoke(from);
}
else
{
from.Mobile.FixedEffect(0x3735, 6, 30);
from.Mobile.PlaySound(0x5C);
}
}
}

View file

@ -1,17 +1,16 @@
namespace Server.Ethics.Hero
{
public sealed class HolyBlade : Power
{
public HolyBlade() =>
m_Definition = new PowerDefinition(
10,
"Holy Blade",
"Erstok Reyam",
""
);
namespace Server.Ethics.Hero;
public override void BeginInvoke(Player from)
{
}
public sealed class HolyBlade : Power
{
public HolyBlade() =>
Definition = new PowerDefinition(
10,
"Holy Blade",
"Erstok Reyam",
""
);
public override void BeginInvoke(Player from)
{
}
}
}

View file

@ -1,71 +1,70 @@
using Server.Items;
using Server.Targeting;
namespace Server.Ethics.Hero
{
public sealed class HolyItem : Power
{
public HolyItem() =>
m_Definition = new PowerDefinition(
5,
"Holy Item",
"Vidda K'balc",
""
);
namespace Server.Ethics.Hero;
public override void BeginInvoke(Player from)
public sealed class HolyItem : Power
{
public HolyItem() =>
Definition = new PowerDefinition(
5,
"Holy Item",
"Vidda K'balc",
""
);
public override void BeginInvoke(Player from)
{
from.Mobile.BeginTarget(12, false, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Which item do you wish to imbue?");
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
{
if (obj is not Item item)
{
from.Mobile.BeginTarget(12, false, TargetFlags.None, Power_OnTarget, from);
from.Mobile.SendMessage("Which item do you wish to imbue?");
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
return;
}
private void Power_OnTarget(Mobile fromMobile, object obj, Player from)
if (item.Parent != from.Mobile)
{
if (obj is not Item item)
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You may only imbue items you are wearing."
);
return;
}
if ((item.SavedFlags & 0x300) != 0)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "That has already beem imbued.");
return;
}
var canImbue = item is Spellbook or BaseClothing or BaseArmor or BaseWeapon &&
item.Name == null;
if (canImbue)
{
if (!CheckInvoke(from))
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
return;
}
if (item.Parent != from.Mobile)
{
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You may only imbue items you are wearing."
);
return;
}
item.Hue = Ethic.Hero.Definition.PrimaryHue;
item.SavedFlags |= 0x100;
if ((item.SavedFlags & 0x300) != 0)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "That has already beem imbued.");
return;
}
from.Mobile.FixedEffect(0x375A, 10, 20);
from.Mobile.PlaySound(0x209);
var canImbue = item is Spellbook or BaseClothing or BaseArmor or BaseWeapon &&
item.Name == null;
if (canImbue)
{
if (!CheckInvoke(from))
{
return;
}
item.Hue = Ethic.Hero.Definition.PrimaryHue;
item.SavedFlags |= 0x100;
from.Mobile.FixedEffect(0x375A, 10, 20);
from.Mobile.PlaySound(0x209);
FinishInvoke(from);
}
else
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
}
FinishInvoke(from);
}
else
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You may not imbue that.");
}
}
}
}

View file

@ -1,97 +1,76 @@
using System;
using Server.Text;
namespace Server.Ethics.Hero
namespace Server.Ethics.Hero;
public sealed class HolySense : Power
{
public sealed class HolySense : Power
public HolySense() =>
Definition = new PowerDefinition(
0,
"Holy Sense",
"Drewrok Erstok",
""
);
public override void BeginInvoke(Player from)
{
public HolySense() =>
m_Definition = new PowerDefinition(
0,
"Holy Sense",
"Drewrok Erstok",
""
);
var opposition = Ethic.Evil;
public override void BeginInvoke(Player from)
var enemyCount = 0;
var maxRange = 18 + from.Power;
Player primary = null;
foreach (var pl in opposition.Players)
{
var opposition = Ethic.Evil;
var mob = pl.Mobile;
var enemyCount = 0;
var maxRange = 18 + from.Power;
Player primary = null;
foreach (var pl in opposition.Players)
if (mob == null || mob.Map != from.Mobile.Map || !mob.Alive)
{
var mob = pl.Mobile;
if (mob == null || mob.Map != from.Mobile.Map || !mob.Alive)
{
continue;
}
if (!mob.InRange(from.Mobile, Math.Max(18, maxRange - pl.Power)))
{
continue;
}
if (primary == null || pl.Power > primary.Power)
{
primary = pl;
}
++enemyCount;
continue;
}
using var sb = ValueStringBuilder.Create();
sb.Append("You sense ");
sb.Append(enemyCount == 0 ? "no" : enemyCount.ToString());
sb.Append(enemyCount == 1 ? " enemy" : " enemies");
if (primary != null)
if (!mob.InRange(from.Mobile, Math.Max(18, maxRange - pl.Power)))
{
sb.Append(", and a strong presence");
switch (from.Mobile.GetDirectionTo(primary.Mobile))
{
case Direction.West:
sb.Append(" to the west.");
break;
case Direction.East:
sb.Append(" to the east.");
break;
case Direction.North:
sb.Append(" to the north.");
break;
case Direction.South:
sb.Append(" to the south.");
break;
case Direction.Up:
sb.Append(" to the north-west.");
break;
case Direction.Down:
sb.Append(" to the south-east.");
break;
case Direction.Left:
sb.Append(" to the south-west.");
break;
case Direction.Right:
sb.Append(" to the north-east.");
break;
}
}
else
{
sb.Append('.');
continue;
}
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x59, false, sb.ToString());
if (primary == null || pl.Power > primary.Power)
{
primary = pl;
}
FinishInvoke(from);
++enemyCount;
}
using var sb = ValueStringBuilder.Create();
sb.Append($"You sense {(enemyCount == 0 ? "no" : enemyCount.ToString())} {(enemyCount == 1 ? "enemy" : "enemies")}");
if (primary != null)
{
var direction = from.Mobile.GetDirectionTo(primary.Mobile) switch
{
Direction.East => "east",
Direction.North => "north",
Direction.South => "south",
Direction.Up => "north-west",
Direction.Down => "south-east",
Direction.Left => "south-west",
Direction.Right => "north-east",
_ => "west"
};
sb.Append($", and a strong presence to the {direction}.");
}
else
{
sb.Append('.');
}
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x59, false, sb.ToString());
FinishInvoke(from);
}
}

View file

@ -1,38 +1,37 @@
namespace Server.Ethics.Hero
namespace Server.Ethics.Hero;
public sealed class HolyShield : Power
{
public sealed class HolyShield : Power
public HolyShield() =>
Definition = new PowerDefinition(
20,
"Holy Shield",
"Erstok K'blac",
""
);
public override void BeginInvoke(Player from)
{
public HolyShield() =>
m_Definition = new PowerDefinition(
20,
"Holy Shield",
"Erstok K'blac",
""
);
public override void BeginInvoke(Player from)
if (from.IsShielded)
{
if (from.IsShielded)
{
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You are already under the protection of a holy shield."
);
return;
}
from.BeginShield();
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You are now under the protection of a holy shield."
"You are already under the protection of a holy shield."
);
FinishInvoke(from);
return;
}
from.BeginShield();
from.Mobile.LocalOverheadMessage(
MessageType.Regular,
0x3B2,
false,
"You are now under the protection of a holy shield."
);
FinishInvoke(from);
}
}
}

View file

@ -1,45 +1,44 @@
using System;
using Server.Mobiles;
namespace Server.Ethics.Hero
namespace Server.Ethics.Hero;
public sealed class HolySteed : Power
{
public sealed class HolySteed : Power
public HolySteed() =>
Definition = new PowerDefinition(
30,
"Holy Steed",
"Trubechs Yeliab",
""
);
public override void BeginInvoke(Player from)
{
public HolySteed() =>
m_Definition = new PowerDefinition(
30,
"Holy Steed",
"Trubechs Yeliab",
""
);
public override void BeginInvoke(Player from)
if (from.Steed?.Deleted == true)
{
if (from.Steed?.Deleted == true)
{
from.Steed = null;
}
from.Steed = null;
}
if (from.Steed != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have a holy steed.");
return;
}
if (from.Steed != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have a holy steed.");
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
var steed = new Mobiles.HolySteed();
var steed = new Mobiles.HolySteed();
if (BaseCreature.Summon(steed, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Steed = steed;
if (BaseCreature.Summon(steed, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Steed = steed;
FinishInvoke(from);
}
FinishInvoke(from);
}
}
}
}

View file

@ -1,17 +1,16 @@
namespace Server.Ethics.Hero
{
public sealed class HolyWord : Power
{
public HolyWord() =>
m_Definition = new PowerDefinition(
100,
"Holy Word",
"Erstok Oostrac",
""
);
namespace Server.Ethics.Hero;
public override void BeginInvoke(Player from)
{
}
public sealed class HolyWord : Power
{
public HolyWord() =>
Definition = new PowerDefinition(
100,
"Holy Word",
"Erstok Oostrac",
""
);
public override void BeginInvoke(Player from)
{
}
}
}

View file

@ -1,45 +1,44 @@
using System;
using Server.Mobiles;
namespace Server.Ethics.Hero
namespace Server.Ethics.Hero;
public sealed class SummonFamiliar : Power
{
public sealed class SummonFamiliar : Power
public SummonFamiliar() =>
Definition = new PowerDefinition(
5,
"Summon Familiar",
"Trubechs Vingir",
""
);
public override void BeginInvoke(Player from)
{
public SummonFamiliar() =>
m_Definition = new PowerDefinition(
5,
"Summon Familiar",
"Trubechs Vingir",
""
);
public override void BeginInvoke(Player from)
if (from.Familiar?.Deleted == true)
{
if (from.Familiar?.Deleted == true)
{
from.Familiar = null;
}
from.Familiar = null;
}
if (from.Familiar != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have a holy familiar.");
return;
}
if (from.Familiar != null)
{
from.Mobile.LocalOverheadMessage(MessageType.Regular, 0x3B2, false, "You already have a holy familiar.");
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
if (from.Mobile.Followers + 1 > from.Mobile.FollowersMax)
{
from.Mobile.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
return;
}
var familiar = new HolyFamiliar();
var familiar = new HolyFamiliar();
if (BaseCreature.Summon(familiar, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Familiar = familiar;
if (BaseCreature.Summon(familiar, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours(1.0)))
{
from.Familiar = familiar;
FinishInvoke(from);
}
FinishInvoke(from);
}
}
}
}

View file

@ -120,5 +120,7 @@ public class FactionPersistence : Item
new TownState(reader);
}
}
Timer.DelayCall(Delete);
}
}

View file

@ -0,0 +1,4 @@
{
"version": 1,
"type": "Server.Ethics.Ethic"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Ethics.EthicsEntity"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Ethics.Evil.EvilEthic"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Ethics.Hero.HeroEthic"
}

View file

@ -0,0 +1,50 @@
{
"version": 1,
"type": "Server.Ethics.Player",
"properties": [
{
"name": "Mobile",
"type": "Server.Mobile",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "Power",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "History",
"type": "int",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
""
]
},
{
"name": "Steed",
"type": "Server.Mobile",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "Familiar",
"type": "Server.Mobile",
"rule": "SerializableInterfaceMigrationRule"
},
{
"name": "Shield",
"type": "System.DateTime",
"rule": "PrimitiveTypeMigrationRule",
"ruleArguments": [
"DeltaTime"
]
},
{
"name": "Ethic",
"type": "Server.Ethics.Ethic",
"rule": "SerializableInterfaceMigrationRule"
}
]
}

View file

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

View file

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