From 8fe9e3c3fef0049df82281a62afe35dda516dfe2 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 18 Sep 2026 17:48:26 -0400 Subject: fix: close combat ghost, admin traversal, login throttle, journal double-pay, and quality/economy gaps --- internal/game/combat_attack.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'internal/game/combat_attack.go') diff --git a/internal/game/combat_attack.go b/internal/game/combat_attack.go index 80994e1..1e53510 100644 --- a/internal/game/combat_attack.go +++ b/internal/game/combat_attack.go @@ -194,6 +194,10 @@ func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobIn } g.SubscribeTicks(1, func() bool { + if !g.sessionRegistered(sess, p.Name) { + g.Combat.Leave(p.Name) + return false + } cs := g.Combat.Get(p.Name) if cs == nil || !cs.Active { return false @@ -256,6 +260,10 @@ func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobIn if !isTask { g.SubscribeTicks(engine.ToTicks(mob.Speed), func() bool { + if !g.sessionRegistered(sess, p.Name) { + g.Combat.Leave(p.Name) + return false + } cs := g.Combat.Get(p.Name) if cs == nil || !cs.Active { return false @@ -354,6 +362,15 @@ func (g *Game) playerAttackUnarmed(sess *net.Session, p *player.Player, mob *wor return } + // Unarmed is a melee crush attack, so it must leave cover exactly like + // every other melee attack: otherwise a safespotted player can punch a + // melee-only mob that cannot retaliate. + if g.isSafespotted(p.Name) { + if ss, ok := g.safespot.Get(p.Name); ok { + g.forceLeaveSafespot(sess, p, &ss, "You leave your cover to close in for melee!") + } + } + _, _, attRoll, defRoll, maxHit := g.calculateUnarmedAttackRoll(p, mob) if combat.HitCheck(attRoll, defRoll) { -- cgit v1.2.3