From 5ea082ab4e82409aebc889b496f43e52b003d4f7 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 19 Jun 2026 21:20:32 -0400 Subject: feat: science combat/triggers overhauled. decks now use autotrigger attacks. --- internal/game/cmd_attack.go | 85 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 16 deletions(-) (limited to 'internal/game/cmd_attack.go') diff --git a/internal/game/cmd_attack.go b/internal/game/cmd_attack.go index 222e3c3..f9cfba2 100644 --- a/internal/game/cmd_attack.go +++ b/internal/game/cmd_attack.go @@ -129,12 +129,9 @@ func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobIn g.cancelBackgroundAction(p) combat.EnterCombat(p.Name, mob.InstanceID) + p.AttackTimer = 0 autotriggerActive := p.AutotriggerMod != "" - playerSpeed := g.playerWeaponSpeed(p) - if autotriggerActive { - playerSpeed = 5.0 - } if !autotriggerActive { attBonus, strBonus, defBonus := combat.AttackStyleBonus(string(p.AttackStyle)) @@ -173,7 +170,7 @@ func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobIn } } - g.Ticks.Subscribe(engine.ToTicks(playerSpeed), func() bool { + g.Ticks.Subscribe(1, func() bool { cs := combat.GetCombat(p.Name) if cs == nil || !cs.Active { return false @@ -183,21 +180,46 @@ func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobIn g.endCombat(sess, p, currentMob) return false } - if p.AutotriggerMod != "" { - mod := GetMod(p.AutotriggerMod) - if mod != nil { - if !g.scienceAttack(sess, p, currentMob, mod) { - p.AutotriggerMod = "" - sess.WriteLine("You've run out of junk. Switching to melee.") - g.playerAttack(sess, p, currentMob) + + p.AttackTimer++ + if float64(p.AttackTimer) >= g.playerWeaponSpeed(p) { + p.AttackTimer = 0 + + if g.hasDeckEquipped(p) { + if p.AutotriggerMod != "" { + mod := GetMod(p.AutotriggerMod) + if mod != nil && g.hasJunkCost(p, mod) { + g.scienceAttack(sess, p, currentMob, mod) + } else if mod != nil { + sess.WriteLine(g.colorize(sess, "error", + fmt.Sprintf("\nYou're out of junk for %s!! You flail with your fists in desperation!", mod.Name))) + g.playerAttackUnarmed(sess, p, currentMob) + } else { + p.AutotriggerMod = "" + sess.WriteLine(g.colorize(sess, "error", + "\nNo autotrigger set! Set a module with the 'autotrigger' command.")) + g.playerAttackUnarmed(sess, p, currentMob) + } + } else { + sess.WriteLine(g.colorize(sess, "error", + "\nNo autotrigger set! Set a module with the 'autotrigger' command.")) + g.playerAttackUnarmed(sess, p, currentMob) } } else { - p.AutotriggerMod = "" - g.playerAttack(sess, p, currentMob) + if p.QueuedTrigger != "" { + mod := GetMod(p.QueuedTrigger) + p.QueuedTrigger = "" + if mod != nil && g.hasJunkCost(p, mod) { + g.scienceAttack(sess, p, currentMob, mod) + } else { + g.playerAttack(sess, p, currentMob) + } + } else { + g.playerAttack(sess, p, currentMob) + } } - } else { - g.playerAttack(sess, p, currentMob) } + if currentMob.HP <= 0 { g.endCombat(sess, p, currentMob) return false @@ -284,6 +306,37 @@ func (g *Game) calculatePlayerAttackRoll(p *player.Player, mob *world.MobInstanc return } +func (g *Game) playerAttackUnarmed(sess *net.Session, p *player.Player, mob *world.MobInstance) { + if g.processConsumeQueue(p, sess) { + p.ActionState = &ActionState{Type: ActionEating, TargetName: "food"} + return + } + + attackType, _, attRoll, defRoll, maxHit := g.calculateUnarmedAttackRoll(p, mob) + + if combat.HitCheck(attRoll, defRoll) { + g.applyPlayerHit(sess, p, mob, attackType, object.WeaponMelee, maxHit) + } else { + g.applyPlayerMiss(sess, p, mob, object.WeaponMelee) + } +} + +func (g *Game) calculateUnarmedAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType object.WeaponType, attRoll int, defRoll int, maxHit int) { + attackType = "crush" + weaponType = object.WeaponMelee + + attBonus, strBonus, _ := combat.AttackStyleBonus(string(p.AttackStyle)) + effectiveAttack := p.Level(player.Attack) + g.techLevelBonus(p, "attack") + g.buffLevelBonus(p, "attack") + attRoll = combat.AttackRoll(effectiveAttack, attBonus, 0) + + mobDefBonus := combat.SelectDefenseBonus(attackType, + mob.StabDefense, mob.SlashDefense, mob.CrushDefense, + mob.ScienceDefense, mob.RangedDefense) + defRoll = combat.DefenseRoll(mob.Defense, 0, mobDefBonus) + maxHit = combat.MaxHit(p.Level(player.Strength)+g.techLevelBonus(p, "strength")+g.buffLevelBonus(p, "strength"), strBonus, 0) + return +} + func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.MobInstance, attackType string, weaponType object.WeaponType, maxHit int) { dmg := combat.RollDamage(maxHit) -- cgit v1.2.3