Skip to content

Commit fe76a0d

Browse files
Caeldethclaude
andcommitted
fix: recursive HP/MP calculation for creatures at spawn
Creatures were allocating all stat points in a single batch at their spawn level, causing MpHpIncrease() to evaluate the HP growth formula with final-level stats every time instead of compounding level-by-level. Now iterates from level 1 to target level, allocating 2 stat points per level and evaluating MonsterHpGainPerLevel with the correct accumulated stats at each step. Extracted pattern-based allocation into AllocatePatternPoints() for clarity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0e029e6 commit fe76a0d

1 file changed

Lines changed: 67 additions & 53 deletions

File tree

hybrasyl/Objects/Monster.cs

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -377,16 +377,16 @@ public override void OnDeath()
377377
User hitter = null;
378378
switch (LastHitter)
379379
{
380-
case Monster monster:
381-
{
382-
var hitterStatuses = monster.Condition.Charmed
383-
? monster.CurrentStatuses.Values
384-
: CurrentStatuses.Values;
385-
var statuses = hitterStatuses.Cast<CreatureStatus>().Where(predicate: x =>
386-
x.ConditionChanges != null && (x.ConditionChanges.Set & CreatureCondition.Charm) != 0)
387-
.ToList();
388-
if (statuses.Count != 0) hitter = statuses.First().Source as User;
389-
break;
380+
case Monster monster:
381+
{
382+
var hitterStatuses = monster.Condition.Charmed
383+
? monster.CurrentStatuses.Values
384+
: CurrentStatuses.Values;
385+
var statuses = hitterStatuses.Cast<CreatureStatus>().Where(predicate: x =>
386+
x.ConditionChanges != null && (x.ConditionChanges.Set & CreatureCondition.Charm) != 0)
387+
.ToList();
388+
if (statuses.Count != 0) hitter = statuses.First().Source as User;
389+
break;
390390
}
391391
case User:
392392
hitter = LastHitter as User;
@@ -463,7 +463,7 @@ public override void OnDeath()
463463
Game.ReportException(e);
464464
}
465465
else
466-
hitter?.SendCombatLogMessage(new NoLootEvent
466+
hitter?.SendCombatLogMessage(new NoLootEvent
467467
{ Reason = "You or your group must hit a monster to collect loot" });
468468

469469
Game.World.RemoveStatusCheck(this);
@@ -598,43 +598,57 @@ private void MpHpIncrease()
598598

599599
public void AllocateStats()
600600
{
601-
var totalPoints = Stats.Level * 2;
602-
if (BehaviorSet is null || string.IsNullOrEmpty(BehaviorSet.StatAlloc))
601+
var targetLevel = Stats.Level;
602+
603+
for (byte level = 1; level <= targetLevel; level++)
603604
{
604-
RandomlyAllocateStatPoints(totalPoints);
605+
Stats.Level = level;
606+
607+
if (BehaviorSet is null || string.IsNullOrEmpty(BehaviorSet.StatAlloc))
608+
{
609+
RandomlyAllocateStatPoints(2);
610+
}
611+
else
612+
{
613+
AllocatePatternPoints(2);
614+
}
605615
}
606-
else
616+
}
617+
618+
private void AllocatePatternPoints(int points)
619+
{
620+
var allocPattern = BehaviorSet.StatAlloc.Trim().ToLower().Split(" ");
621+
var patternIndex = 0;
622+
623+
for (var i = 0; i < points; i++)
607624
{
608-
var allocPattern = BehaviorSet.StatAlloc.Trim().ToLower().Split(" ");
609-
while (totalPoints > 0)
610-
foreach (var alloc in allocPattern)
611-
{
612-
switch (alloc)
613-
{
614-
case "str":
615-
Stats.BaseStr += 1;
616-
break;
617-
case "int":
618-
Stats.BaseInt += 1;
619-
break;
620-
case "wis":
621-
Stats.BaseWis += 1;
622-
break;
623-
case "con":
624-
Stats.BaseCon += 1;
625-
break;
626-
case "dex":
627-
Stats.BaseDex += 1;
628-
break;
629-
default:
630-
RandomlyAllocateStatPoints(1);
631-
break;
632-
}
625+
var alloc = allocPattern[patternIndex % allocPattern.Length];
626+
patternIndex++;
633627

634-
totalPoints--;
635-
if (totalPoints % 2 == 0)
636-
MpHpIncrease();
637-
}
628+
switch (alloc)
629+
{
630+
case "str":
631+
Stats.BaseStr += 1;
632+
break;
633+
case "int":
634+
Stats.BaseInt += 1;
635+
break;
636+
case "wis":
637+
Stats.BaseWis += 1;
638+
break;
639+
case "con":
640+
Stats.BaseCon += 1;
641+
break;
642+
case "dex":
643+
Stats.BaseDex += 1;
644+
break;
645+
default:
646+
RandomlyAllocateStatPoints(1);
647+
break;
648+
}
649+
650+
if ((i + 1) % 2 == 0)
651+
MpHpIncrease();
638652
}
639653
}
640654

@@ -940,10 +954,10 @@ public void DetermineNextAction()
940954
_actionQueue.Enqueue(MobAction.Move);
941955
return;
942956
}
943-
else
957+
else
944958
{
945-
_actionQueue.Enqueue(MobAction.Move);
946-
return;
959+
_actionQueue.Enqueue(MobAction.Move);
960+
return;
947961
}
948962
}
949963
else
@@ -1026,8 +1040,8 @@ public void ProcessActions()
10261040
return;
10271041
case MobAction.Move when !Condition.MovementAllowed:
10281042
return;
1029-
case MobAction.Move when Condition.Blinded:
1030-
1043+
case MobAction.Move when Condition.Blinded:
1044+
10311045
var rand = Random.Shared.NextDouble();
10321046
dir = (Direction)Random.Shared.Next(0, 4);
10331047
if (rand > 0.33)
@@ -1048,7 +1062,7 @@ public void ProcessActions()
10481062
Walk(Direction);
10491063
}
10501064
break;
1051-
case MobAction.Move when ShouldWander:
1065+
case MobAction.Move when ShouldWander:
10521066
{
10531067
// Make sure mob is on a map, and the right map at that (we don't support mobs wandering between maps yet)
10541068
if (SpawnPoint == null || SpawnPoint.MapId != Map.Id)
@@ -1064,15 +1078,15 @@ public void ProcessActions()
10641078
if (Destination == null || Destination != SpawnPoint || (SpawnPoint.X == X && SpawnPoint.Y == Y))
10651079
Destination = RandomDestination;
10661080

1067-
CurrentPath = AStarPathFind(Destination.X, Destination.Y, X, Y);
1068-
1081+
CurrentPath = AStarPathFind(Destination.X, Destination.Y, X, Y);
1082+
10691083
if (Walk(AStarGetDirection()))
10701084
CurrentPath = CurrentPath.Parent;
10711085
else
10721086
// Couldn't move, attempt to recalculate path
10731087
CurrentPath = AStarPathFind(Destination.X,
10741088
Destination.Y, X, Y);
1075-
break;
1089+
break;
10761090
}
10771091
case MobAction.Move:
10781092
{

0 commit comments

Comments
 (0)