Skip to content

Commit

Permalink
HS-1280: wrap up
Browse files Browse the repository at this point in the history
  • Loading branch information
baughj committed Apr 21, 2024
1 parent 459975f commit 348b9e7
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 35 deletions.
1 change: 1 addition & 0 deletions hybrasyl/Objects/Creature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public Creature LastHitter
}

public Creature LastTarget { get; set; }
public Creature CurrentTarget { get; set; }

public bool AbsoluteImmortal { get; set; }
public bool PhysicalImmortal { get; set; }
Expand Down
45 changes: 26 additions & 19 deletions hybrasyl/Objects/Monster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using System.Collections.Generic;
using System.Linq;
using Hybrasyl.ChatCommands;
using Serilog;

namespace Hybrasyl.Objects;

Expand Down Expand Up @@ -287,7 +288,7 @@ public bool Active
_active = value;
}
}

public bool HasAssailSkills { get; set; }

public uint LootableXp
Expand Down Expand Up @@ -351,6 +352,7 @@ public override void OnDeath()
DeathProcessed = true;
_actionQueue.Clear();

// TODO: implement death for charmed monsters
if (!(LastHitter is User hitter))
{
Map.Remove(this);
Expand Down Expand Up @@ -928,38 +930,44 @@ public void ProcessActions()
}
break;
case MobAction.Attack:
if (nextCastable is null)
{
Attack();
return;
}

targets = ThreatInfo.GetTargets(nextCastable.CurrentPriority);
if (targets.Count == 0)

if (targets == null && Target == null)
{
GameLog.SpawnDebug(
$"{Name}: ({Map.Name}@{X},{Y}): no targets returned from priority {nextCastable.CurrentPriority}");
$"{Name}: ({Map.Name}@{X},{Y}): no targets returned from priority {nextCastable.CurrentPriority} and no current target");
return;
}

if (targets.Count != 0 && nextCastable.Slot.Castable.IsAssail)
// If the monster is charmed and it doesn't already have a target, the target is a user, or the target is dead (or in the process of getting dead)
// get a new target. If it is not charmed, our castable determines targeting priority and we take the first target.
if ((Condition.Charmed && (Target is null or User || Target.Stats.Hp <=0)) || !Condition.Charmed)
Target = targets.First();

if (nextCastable == null || nextCastable.Slot.Castable.IsAssail)
{
var target = targets.First();
if (Distance(target) > 1)
if (Condition.Disarmed)
{
// Disarmed means we can't use assails, so we mark them as having been used so the next rotation entry can hit
nextCastable.Slot.LastCast = DateTime.Now;
nextCastable.Slot.UseCount++;
return;
}

if (Distance(Target) > 1)
{
_actionQueue.Enqueue(MobAction.Move);
}
else
Cast(nextCastable.Slot, Target);

if (!CheckFacing(Direction, targets.First()))
Turn(Relation(targets.First().X, targets.First().Y));
if (!CheckFacing(Direction, Target))
Turn(Relation(Target.X, Target.Y));
return;
}

foreach (var target in targets)
{
Cast(nextCastable.Slot, target);
LastTarget = target;
}
Cast(nextCastable.Slot, Target);

return;
case MobAction.Move when !Condition.MovementAllowed:
Expand Down Expand Up @@ -994,7 +1002,6 @@ public void ProcessActions()
return;
}

LastTarget = target;
if (Condition.MovementAllowed)
{
if (CurrentPath == null || !AStarPathClear())
Expand Down
49 changes: 33 additions & 16 deletions hybrasyl/Objects/ThreatInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Antlr4.Runtime;
using Antlr4.Runtime.Atn;

namespace Hybrasyl.Objects;

Expand Down Expand Up @@ -69,14 +71,35 @@ public List<Creature> GetTargets(CreatureTargetPriority priority)
var ret = new List<Creature>();
if (OwnerObject == null) return ret;
ThreatEntry entry;
var monstersInViewport = OwnerObject.Map.EntityTree.GetObjects(OwnerObject.GetViewport()).OfType<Monster>().ToList();
if (OwnerObject.Condition.Charmed)
{
// Generate target list: for every creature in threat table, last target of that creature; if nothing found, use monsters in viewport
ret.AddRange(ThreatTableByCreature.Values.Where(x => x.TargetObject is User { LastTarget: not null })
.Select(y => y.TargetObject).ToList());
if (ret.Count == 0)
ret.AddRange(OwnerObject.Map.EntityTree.GetObjects(OwnerObject.GetViewport()).OfType<Monster>().ToList());
return ret;
switch (OwnerObject.LastTarget)
{
// If our immediate target is grouped, add every monster they've collectively targeted to our target list.
case User { Group: not null } u1:
ret.AddRange(u1.Group.Members.Where(x=> x.LastTarget != null).Select(y => y.LastTarget));
break;
// If we are already targeting a monster, continue to target it
case Monster:
ret.Add(OwnerObject.LastTarget);
break;
// Add everything targeting the last player to use a spell on this monster
default:
{
if (LastCaster is User u2)
{
ret.AddRange(monstersInViewport.Where(x => x.ThreatInfo.ContainsThreat(LastCaster)));
}
break;
}
}
// If we still have no targets, or our (singular) target is dead, add every monster in the viewport.
if (ret.Count == 0 || (ret.Count == 1 && ret.First().Stats.Hp <= 0))
ret.AddRange(OwnerObject.Map.EntityTree.GetObjects(OwnerObject.GetViewport()).OfType<Monster>());
// Order by distance, take closest first, make sure to not target ourselves
return ret.OrderBy(x => x.Distance(OwnerObject)).Where(x => x.Guid != Owner).ToList();

}

switch (priority)
Expand All @@ -88,28 +111,22 @@ public List<Creature> GetTargets(CreatureTargetPriority priority)
ret.Add(Game.World.WorldState.GetWorldObject<Creature>(ThreatTableByThreat.First().Value));
break;
case CreatureTargetPriority.Attacker:
entry = ThreatTableByThreat.Keys.OrderByDescending(keySelector: x => x.SecondsSinceLastMelee)
.FirstOrDefault();
entry = ThreatTableByThreat.Keys.MaxBy(keySelector: x => x.SecondsSinceLastMelee);
if (entry != null)
ret.Add(entry.TargetObject);
break;
case CreatureTargetPriority.AttackingCaster:
entry = ThreatTableByThreat.Keys.Where(predicate: x => x.IsCaster)
.OrderByDescending(keySelector: x => x.SecondsSinceLastNonHealCast)
.FirstOrDefault();
entry = ThreatTableByThreat.Keys.Where(predicate: x => x.IsCaster).MaxBy(keySelector: x => x.SecondsSinceLastNonHealCast);
if (entry != null)
ret.Add(entry.TargetObject);
break;
case CreatureTargetPriority.AttackingGroup:
entry = ThreatTableByThreat.Keys.OrderByDescending(keySelector: x => x.SecondsSinceLastMelee)
.FirstOrDefault();
entry = ThreatTableByThreat.Keys.MaxBy(keySelector: x => x.SecondsSinceLastMelee);
if (entry != null)
ret.Add(entry.TargetObject);
break;
case CreatureTargetPriority.AttackingHealer:
entry = ThreatTableByThreat.Keys.Where(predicate: x => x.IsHealer)
.OrderByDescending(keySelector: x => x.SecondsSinceLastHeal)
.FirstOrDefault();
entry = ThreatTableByThreat.Keys.Where(predicate: x => x.IsHealer).MaxBy(keySelector: x => x.SecondsSinceLastHeal);
if (entry != null)
ret.Add(entry.TargetObject);
break;
Expand Down

0 comments on commit 348b9e7

Please sign in to comment.