diff options
| author | historia <[not public]> | 2026-07-01 22:30:31 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-01 22:30:31 -0400 |
| commit | c14c2e403c75a913e1a6d962fb6fe64f013adae5 (patch) | |
| tree | 75070293fe2535b594ac72e501cbf08a3f1146e0 /internal/game/combat_attack.go | |
| parent | 8eec7b75ff9c8c368b252373db45fb891732d2ca (diff) | |
| download | thehouseoficarus-c14c2e403c75a913e1a6d962fb6fe64f013adae5.tar.gz | |
fix: combat formulas and mob attack types (mobs switch attack styles if safespotting)
Diffstat (limited to 'internal/game/combat_attack.go')
| -rw-r--r-- | internal/game/combat_attack.go | 79 |
1 files changed, 37 insertions, 42 deletions
diff --git a/internal/game/combat_attack.go b/internal/game/combat_attack.go index ddc3b8f..6c314ff 100644 --- a/internal/game/combat_attack.go +++ b/internal/game/combat_attack.go @@ -8,7 +8,6 @@ import ( "strings" "thehouseoficarus/internal/behavior" - "thehouseoficarus/internal/color" "thehouseoficarus/internal/combat" "thehouseoficarus/internal/engine" "thehouseoficarus/internal/item" @@ -213,13 +212,13 @@ func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobIn 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("You're out of junk for %s!! You flail with your fists in desperation!", mod.Name))) + sess.WriteLine(g.colorize(sess, "error", + fmt.Sprintf("You'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", - "No autotrigger set! Set a module with the 'autotrigger' command.")) + sess.WriteLine(g.colorize(sess, "error", + "No autotrigger set! Set a module with the 'autotrigger' command.")) g.playerAttackUnarmed(sess, p, currentMob) } } else { @@ -289,20 +288,29 @@ func (g *Game) playerAttack(sess *net.Session, p *player.Player, mob *world.MobI } } -func (g *Game) calculatePlayerAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType item.EquipmentType, attRoll int, defRoll int, maxHit int) { - attackType = "crush" +// resolvePlayerAttackType determines the player's attack type and main-hand +// weapon type (and the weapon def, if any) from their equipped weapon, +// defaulting to crush when unarmed or when the weapon specifies no type. +func (g *Game) resolvePlayerAttackType(p *player.Player) (attackType string, weaponType item.EquipmentType, def *item.ItemDef) { + attackType = combat.DefaultAttackType if itemID, ok := p.Equipment[item.SlotMainHand]; ok { - if def, err := g.ItemStore.Load(itemID); err == nil { - if def.AttackType() != "" { - attackType = def.AttackType() - } else if def.EquipmentType() == item.EquipRangedWeapon { - attackType = "ranged" - } else if def.EquipmentType() == item.EquipScienceWeapon { - attackType = "science" + if d, err := g.ItemStore.Load(itemID); err == nil { + def = d + weaponType = d.EquipmentType() + if at := d.AttackType(); at != "" { + attackType = at + } else if weaponType == item.EquipRangedWeapon { + attackType = combat.AttackRanged + } else if weaponType == item.EquipScienceWeapon { + attackType = combat.AttackScience } - weaponType = def.EquipmentType() } } + return +} + +func (g *Game) calculatePlayerAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType item.EquipmentType, attRoll int, defRoll int, maxHit int) { + attackType, weaponType, _ = g.resolvePlayerAttackType(p) totals := g.playerEquipBonuses(p) isRanged := weaponType == item.EquipRangedWeapon @@ -311,26 +319,26 @@ func (g *Game) calculatePlayerAttackRoll(p *player.Player, mob *world.MobInstanc rangedBonus, _ := combat.RangedStyleBonus(string(p.AttackStyle)) equipAttack := totals.RangedAttack effectiveRanged := p.Level(player.Ranged) + g.techLevelBonus(p, "ranged") + g.buffLevelBonus(p, "ranged") - attRoll = combat.EffectiveRoll(effectiveRanged, rangedBonus, equipAttack) + attRoll = combat.AttackRoll(combat.PlayerEffective(effectiveRanged, rangedBonus), equipAttack) mobDefBonus := combat.SelectBonus(attackType, mob.StabDefense, mob.SlashDefense, mob.CrushDefense, mob.ScienceDefense, mob.RangedDefense) - defRoll = combat.EffectiveRoll(mob.Defense, 0, mobDefBonus) - maxHit = combat.MaxHit(effectiveRanged, rangedBonus, totals.RangedStrength) + defRoll = combat.AttackRoll(combat.NPCEffective(mob.Defense), mobDefBonus) + maxHit = combat.MaxHit(combat.PlayerEffective(effectiveRanged, rangedBonus), totals.RangedStrength) } else { attBonus, strBonus, _ := combat.AttackStyleBonus(string(p.AttackStyle)) equipAttack := combat.SelectBonus(attackType, totals.StabAttack, totals.SlashAttack, totals.CrushAttack, totals.ScienceAttack, totals.RangedAttack) effectiveAccuracy := p.Level(player.Accuracy) + g.techLevelBonus(p, "accuracy") + g.buffLevelBonus(p, "accuracy") - attRoll = combat.EffectiveRoll(effectiveAccuracy, attBonus, equipAttack) + attRoll = combat.AttackRoll(combat.PlayerEffective(effectiveAccuracy, attBonus), equipAttack) mobDefBonus := combat.SelectBonus(attackType, mob.StabDefense, mob.SlashDefense, mob.CrushDefense, mob.ScienceDefense, mob.RangedDefense) - defRoll = combat.EffectiveRoll(mob.Defense, 0, mobDefBonus) - maxHit = combat.MaxHit(p.Level(player.Strength)+g.techLevelBonus(p, "strength")+g.buffLevelBonus(p, "strength"), strBonus, totals.StrengthBonus) + defRoll = combat.AttackRoll(combat.NPCEffective(mob.Defense), mobDefBonus) + maxHit = combat.MaxHit(combat.PlayerEffective(p.Level(player.Strength)+g.techLevelBonus(p, "strength")+g.buffLevelBonus(p, "strength"), strBonus), totals.StrengthBonus) } return } @@ -355,13 +363,13 @@ func (g *Game) calculateUnarmedAttackRoll(p *player.Player, mob *world.MobInstan attBonus, strBonus, _ := combat.AttackStyleBonus(string(p.AttackStyle)) effectiveAccuracy := p.Level(player.Accuracy) + g.techLevelBonus(p, "accuracy") + g.buffLevelBonus(p, "accuracy") - attRoll = combat.EffectiveRoll(effectiveAccuracy, attBonus, 0) + attRoll = combat.AttackRoll(combat.PlayerEffective(effectiveAccuracy, attBonus), 0) mobDefBonus := combat.SelectBonus(attackType, mob.StabDefense, mob.SlashDefense, mob.CrushDefense, mob.ScienceDefense, mob.RangedDefense) - defRoll = combat.EffectiveRoll(mob.Defense, 0, mobDefBonus) - maxHit = combat.MaxHit(p.Level(player.Strength)+g.techLevelBonus(p, "strength")+g.buffLevelBonus(p, "strength"), strBonus, 0) + defRoll = combat.AttackRoll(combat.NPCEffective(mob.Defense), mobDefBonus) + maxHit = combat.MaxHit(combat.PlayerEffective(p.Level(player.Strength)+g.techLevelBonus(p, "strength")+g.buffLevelBonus(p, "strength"), strBonus), 0) return } @@ -380,14 +388,9 @@ func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.Mo } if mob.FinishingBlow != "" && mob.HP == 1 { - fbDef, _ := g.ItemStore.Load(mob.FinishingBlow) - fbName := mob.FinishingBlow - if fbDef != nil { - fbName = fbDef.Name - } sess.WriteLine(g.colorize(sess, "warning", fmt.Sprintf("%s resists death! Use %s on it to finish it off.", - mobDisplayName(mob, false), fbName))) + mobDisplayName(mob, false), g.itemDisplayName(mob.FinishingBlow)))) } isRanged := weaponType == item.EquipRangedWeapon @@ -401,19 +404,11 @@ func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.Mo g.writeTaskProgress(sess, p, mob, dmg, gains) } else { mobName := mobDisplayName(mob, true) - attacker := mob.Name - if !mob.Unique { - attacker = "The " + mob.Name - } - w := len(fmt.Sprintf("You hit %s for %d damage.", mobName, 999)) - if w2 := len(fmt.Sprintf("%s hits you for %d damage.", attacker, 999)); w2 > w { - w = w2 - } + attacker := mobDisplayNameCap(mob, true) prefix := fmt.Sprintf("You hit %s for %s damage.", g.colorize(sess, "mob", mobName), g.colorize(sess, "damage_dealt", fmt.Sprint(dmg))) - visLen := color.VisibleLen(prefix) - if visLen < w { - prefix += strings.Repeat(" ", w-visLen+1) - } + prefix = padCombatPrefix(prefix, + fmt.Sprintf("You hit %s for %d damage.", mobName, 999), + fmt.Sprintf("%s hits you for %d damage.", attacker, 999)) hpSuffix := fmt.Sprintf("%s [%2d/%d]", g.hpBar(sess, mob.HP, mob.MaxHP), mob.HP, mob.MaxHP) line := prefix + hpSuffix + g.formatXpDrop(sess, p, gains) sess.WriteLine(line) |
