diff options
Diffstat (limited to 'internal/game/cmd_attack.go')
| -rw-r--r-- | internal/game/cmd_attack.go | 88 |
1 files changed, 65 insertions, 23 deletions
diff --git a/internal/game/cmd_attack.go b/internal/game/cmd_attack.go index 703b9ac..ce396e8 100644 --- a/internal/game/cmd_attack.go +++ b/internal/game/cmd_attack.go @@ -2,6 +2,7 @@ package game import ( "fmt" + "math/rand" "sort" "strconv" "strings" @@ -248,6 +249,15 @@ func (g *Game) playerAttack(sess *net.Session, p *player.Player, mob *world.MobI attackType, weaponType, attRoll, defRoll, maxHit := g.calculatePlayerAttackRoll(p, mob) + if g.isSafespotted(p.Name) && attackType != "ranged" && attackType != "science" { + g.safespotMu.Lock() + ss, ok := g.safespotStates[p.Name] + g.safespotMu.Unlock() + if ok { + g.forceLeaveSafespot(sess, p, ss, "You leave your cover to close in for melee!") + } + } + if combat.HitCheck(attRoll, defRoll) { g.applyPlayerHit(sess, p, mob, attackType, weaponType, maxHit) } else { @@ -277,25 +287,25 @@ 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.AttackRoll(effectiveRanged, rangedBonus, equipAttack) + attRoll = combat.EffectiveRoll(effectiveRanged, rangedBonus, equipAttack) - mobDefBonus := combat.SelectDefenseBonus(attackType, + mobDefBonus := combat.SelectBonus(attackType, mob.StabDefense, mob.SlashDefense, mob.CrushDefense, mob.ScienceDefense, mob.RangedDefense) - defRoll = combat.DefenseRoll(mob.Defense, 0, mobDefBonus) + defRoll = combat.EffectiveRoll(mob.Defense, 0, mobDefBonus) maxHit = combat.MaxHit(effectiveRanged, rangedBonus, totals.RangedStrength) } else { attBonus, strBonus, _ := combat.AttackStyleBonus(string(p.AttackStyle)) - equipAttack := combat.SelectAttackBonus(attackType, + equipAttack := combat.SelectBonus(attackType, totals.StabAttack, totals.SlashAttack, totals.CrushAttack, totals.ScienceAttack, totals.RangedAttack) effectiveAttack := p.Level(player.Attack) + g.techLevelBonus(p, "attack") + g.buffLevelBonus(p, "attack") - attRoll = combat.AttackRoll(effectiveAttack, attBonus, equipAttack) + attRoll = combat.EffectiveRoll(effectiveAttack, attBonus, equipAttack) - mobDefBonus := combat.SelectDefenseBonus(attackType, + mobDefBonus := combat.SelectBonus(attackType, mob.StabDefense, mob.SlashDefense, mob.CrushDefense, mob.ScienceDefense, mob.RangedDefense) - defRoll = combat.DefenseRoll(mob.Defense, 0, mobDefBonus) + 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) } return @@ -322,12 +332,12 @@ func (g *Game) calculateUnarmedAttackRoll(p *player.Player, mob *world.MobInstan 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) + attRoll = combat.EffectiveRoll(effectiveAttack, attBonus, 0) - mobDefBonus := combat.SelectDefenseBonus(attackType, + mobDefBonus := combat.SelectBonus(attackType, mob.StabDefense, mob.SlashDefense, mob.CrushDefense, mob.ScienceDefense, mob.RangedDefense) - defRoll = combat.DefenseRoll(mob.Defense, 0, mobDefBonus) + defRoll = combat.EffectiveRoll(mob.Defense, 0, mobDefBonus) maxHit = combat.MaxHit(p.Level(player.Strength)+g.techLevelBonus(p, "strength")+g.buffLevelBonus(p, "strength"), strBonus, 0) return } @@ -361,12 +371,7 @@ func (g *Game) applyPlayerHit(sess *net.Session, p *player.Player, mob *world.Mo gains := g.awardCombatXP(sess, p, dmg, isRanged) if isRanged { - p.AmmoQty-- - if p.AmmoQty <= 0 { - delete(p.Equipment, object.SlotAmmo) - p.AmmoQty = 0 - } - g.AccountStore.SaveCharacter(p) + g.consumeAmmo(sess, p) } mobName := mobDisplayName(mob, true) @@ -404,18 +409,39 @@ func (g *Game) applyPlayerMiss(sess *net.Session, p *player.Player, mob *world.M sess.WriteLine(g.colorize(sess, "miss", fmt.Sprintf("You miss %s.", mobDisplayName(mob, true)))) if weaponType == object.WeaponRanged { - p.AmmoQty-- + g.consumeAmmo(sess, p) if p.AmmoQty <= 0 { - delete(p.Equipment, object.SlotAmmo) - p.AmmoQty = 0 - g.AccountStore.SaveCharacter(p) sess.WriteLine("You've run out of ammo!") combat.LeaveCombat(p.Name) } } } +func (g *Game) consumeAmmo(sess *net.Session, p *player.Player) { + ammoID := p.Equipment[object.SlotAmmo] + + p.AmmoQty-- + if p.AmmoQty <= 0 { + delete(p.Equipment, object.SlotAmmo) + p.AmmoQty = 0 + } + + if ammoID != "" { + if def, err := g.ItemStore.Load(ammoID); err == nil && def.Recoverable { + if rand.Float64() < 0.80 { + g.World.AddGroundItem(p.RoomID, def.ID, 1) + } + } + } + + g.AccountStore.SaveCharacter(p) +} + func (g *Game) mobAttack(sess *net.Session, p *player.Player, mob *world.MobInstance) { + if g.isSafespotted(p.Name) && g.safespotBlocksMob(p, mob) { + return + } + attRoll, defRoll := g.calculateMobAttack(p, mob) if combat.HitCheck(attRoll, defRoll) { @@ -433,13 +459,13 @@ func (g *Game) calculateMobAttack(p *player.Player, mob *world.MobInstance) (att mobAttackType = "crush" } - attRoll = combat.AttackRoll(mob.Attack, 0, mob.AttackBonus) + attRoll = combat.EffectiveRoll(mob.Attack, 0, mob.AttackBonus) totals := g.playerEquipBonuses(p) - equipDef := combat.SelectDefenseBonus(mobAttackType, + equipDef := combat.SelectBonus(mobAttackType, totals.StabDefense, totals.SlashDefense, totals.CrushDefense, totals.ScienceDefense, totals.RangedDefense) - defRoll = combat.DefenseRoll(p.Level(player.Defense)+g.techLevelBonus(p, "defense")+g.buffLevelBonus(p, "defense"), defStyleBonus, equipDef) + defRoll = combat.EffectiveRoll(p.Level(player.Defense)+g.techLevelBonus(p, "defense")+g.buffLevelBonus(p, "defense"), defStyleBonus, equipDef) return } @@ -504,6 +530,15 @@ func (g *Game) applyMobHit(sess *net.Session, p *player.Player, mob *world.MobIn hpSuffix := fmt.Sprintf("%s [%2d/%d]", g.hpBar(sess, p.HP, p.MaxHP()), p.HP, p.MaxHP()) sess.WriteLine(prefix + hpSuffix) + g.safespotMu.Lock() + ss, hasSS := g.safespotStates[p.Name] + if hasSS && ss.HideCountdown > 0 { + delete(g.safespotStates, p.Name) + p.ActionState = &ActionState{Type: ActionCombating, TargetName: mob.Name} + sess.WriteLine(g.colorize(sess, "error", "You're hit while repositioning! You failed to hide.")) + } + g.safespotMu.Unlock() + if p.MoveTicks > 0 { p.ClearMoveState() p.ActionState = &ActionState{Type: ActionCombating, TargetName: mob.Name} @@ -542,6 +577,13 @@ func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInst p.Stats.RecordDeath() sess.WriteLine(g.colorize(sess, "death", "\nOh dear, you are dead!")) p.ClearMoveState() + g.safespotMu.Lock() + if ss, ok := g.safespotStates[p.Name]; ok { + g.safespotMu.Unlock() + g.forceLeaveSafespot(sess, p, ss, "") + } else { + g.safespotMu.Unlock() + } g.dropItemsOnDeath(p) p.HP = p.MaxHP() p.RoomID = 1 |
