diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
index a1c0bde5..092793af 100644
--- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
+++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
@@ -90,6 +90,7 @@
+
diff --git a/FFXIVClassic Map Server/actors/chara/ai/HateContainer.cs b/FFXIVClassic Map Server/actors/chara/ai/HateContainer.cs
new file mode 100644
index 00000000..c4a4c014
--- /dev/null
+++ b/FFXIVClassic Map Server/actors/chara/ai/HateContainer.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using FFXIVClassic_Map_Server.Actors;
+
+namespace FFXIVClassic_Map_Server.actors.chara.ai
+{
+ // todo: actually implement enmity properly
+ class HateEntry
+ {
+ public Character actor;
+ public uint cumulativeEnmity;
+ public uint volatileEnmity;
+ public bool isActive;
+
+ public HateEntry(Character actor, uint cumulativeEnmity = 0, uint volatileEnmity = 0, bool isActive = false)
+ {
+ this.actor = actor;
+ this.cumulativeEnmity = cumulativeEnmity;
+ this.volatileEnmity = volatileEnmity;
+ this.isActive = isActive;
+ }
+ }
+ class HateContainer
+ {
+ private Dictionary hateList;
+ private Character owner;
+
+ public HateContainer(Character owner)
+ {
+ this.owner = owner;
+ this.hateList = new Dictionary();
+ }
+
+ public void AddBaseHate(Character target)
+ {
+ if (!HasHateForTarget(target))
+ hateList.Add(target, new HateEntry(target, 0, 0, true));
+ else
+ Program.Log.Error($"{target.actorName} is already on [{owner.actorId}]{owner.actorName}'s hate list!");
+ }
+
+ public void ClearHate(Character target = null)
+ {
+ if (target != null)
+ {
+ hateList.Remove(target);
+ }
+ else
+ {
+ hateList.Clear();
+ }
+ }
+
+ private void UpdateHate(HateEntry entry)
+ {
+
+ }
+
+ public Dictionary GetHateList()
+ {
+ // todo: return unmodifiable collection?
+ return hateList;
+ }
+
+ public bool HasHateForTarget(Character target)
+ {
+ return hateList.ContainsKey(target);
+ }
+
+ public Character GetMostHatedTarget()
+ {
+ uint enmity = 0;
+ Character target = null;
+
+ foreach(var entry in hateList.Values)
+ {
+ if (entry.cumulativeEnmity > enmity && entry.isActive)
+ {
+ enmity = entry.cumulativeEnmity;
+ target = entry.actor;
+ }
+ }
+ return target;
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/actors/chara/npc/Mob.cs b/FFXIVClassic Map Server/actors/chara/npc/Mob.cs
index 44642f9a..d0616af7 100644
--- a/FFXIVClassic Map Server/actors/chara/npc/Mob.cs
+++ b/FFXIVClassic Map Server/actors/chara/npc/Mob.cs
@@ -15,12 +15,14 @@ namespace FFXIVClassic_Map_Server.Actors
{
class Mob : Npc
{
+ public HateContainer hateContainer;
public Mob(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot,
ushort actorState, uint animationId, string customDisplayName)
: base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName)
{
this.aiContainer = new AIContainer(this, new MobController(this), new PathFind(this), new TargetFind(this));
this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
+ this.hateContainer = new HateContainer(this);
}
}
}