aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_attack.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/cmd_attack.go')
-rw-r--r--internal/game/cmd_attack.go143
1 files changed, 107 insertions, 36 deletions
diff --git a/internal/game/cmd_attack.go b/internal/game/cmd_attack.go
index 4c6941a..4d2d19f 100644
--- a/internal/game/cmd_attack.go
+++ b/internal/game/cmd_attack.go
@@ -46,6 +46,15 @@ func (g *Game) doAttack(sess *net.Session, input string) {
return
}
+ if itemID, ok := p.Equipment[object.SlotMainHand]; ok {
+ if def, err := g.ItemStore.Load(itemID); err == nil && def.WeaponType == object.WeaponRanged {
+ if _, hasAmmo := p.Equipment[object.SlotAmmo]; !hasAmmo || p.AmmoQty <= 0 {
+ sess.WriteLine("You don't have any ammo equipped.")
+ return
+ }
+ }
+ }
+
g.startCombat(sess, p, mob)
}
@@ -177,23 +186,51 @@ func (g *Game) playerAttack(sess *net.Session, p *player.Player, mob *world.MobI
return
}
- attBonus, strBonus, _ := combat.AttackStyleBonus(string(p.AttackStyle))
-
- equipAtt := 0
- equipStr := 0
+ attackType := "crush"
+ var weaponType object.WeaponType
if itemID, ok := p.Equipment[object.SlotMainHand]; ok {
- def, err := g.ItemStore.Load(itemID)
- if err == nil {
- equipAtt = def.Stats.AttackBonus
- equipStr = def.Stats.StrengthBonus
+ if def, err := g.ItemStore.Load(itemID); err == nil {
+ if def.AttackType != "" {
+ attackType = def.AttackType
+ } else if def.WeaponType == object.WeaponRanged {
+ attackType = "ranged"
+ } else if def.WeaponType == object.WeaponScience {
+ attackType = "science"
+ }
+ weaponType = def.WeaponType
}
}
- attRoll := combat.AttackRoll(p.Level(player.Attack), attBonus, equipAtt)
- defRoll := combat.DefenseRoll(mob.Defense, 0, 0)
+ totals := g.playerEquipBonuses(p)
+ isRanged := weaponType == object.WeaponRanged
+
+ var attRoll, defRoll, maxHit int
+
+ if isRanged {
+ rangedBonus, _ := combat.RangedStyleBonus(string(p.AttackStyle))
+ equipAttack := totals.RangedAttack
+ attRoll = combat.AttackRoll(p.Level(player.Ranged), rangedBonus, equipAttack)
+
+ 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.Ranged), rangedBonus, totals.RangedStrength)
+ } else {
+ attBonus, strBonus, _ := combat.AttackStyleBonus(string(p.AttackStyle))
+ equipAttack := combat.SelectAttackBonus(attackType,
+ totals.StabAttack, totals.SlashAttack, totals.CrushAttack,
+ totals.ScienceAttack, totals.RangedAttack)
+ attRoll = combat.AttackRoll(p.Level(player.Attack), attBonus, equipAttack)
+
+ 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), strBonus, totals.StrengthBonus)
+ }
if combat.HitCheck(attRoll, defRoll) {
- maxHit := combat.MaxHit(p.Level(player.Strength), strBonus, equipStr)
dmg := combat.RollDamage(maxHit)
mob.HP -= dmg
@@ -203,7 +240,16 @@ func (g *Game) playerAttack(sess *net.Session, p *player.Player, mob *world.MobI
if mob.HP < mob.MaxHP && mob.HP > 0 {
mob.StartRegen()
}
- gains, leveled := g.awardCombatXP(p, dmg)
+ gains, leveled := g.awardCombatXP(p, dmg, isRanged)
+
+ if isRanged {
+ p.AmmoQty--
+ if p.AmmoQty <= 0 {
+ delete(p.Equipment, object.SlotAmmo)
+ p.AmmoQty = 0
+ }
+ g.AccountStore.SaveCharacter(p)
+ }
for _, skill := range leveled {
sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d %s! ***", p.Level(skill), skill)))
@@ -230,27 +276,45 @@ func (g *Game) playerAttack(sess *net.Session, p *player.Player, mob *world.MobI
line += g.colorize(sess, "xp", " ("+strings.Join(parts, ", ")+")")
}
sess.WriteLine(line)
+
+ if isRanged && p.AmmoQty <= 0 {
+ sess.WriteLine("You've run out of ammo!")
+ combat.LeaveCombat(p.Name)
+ }
} else {
sess.WriteLine(g.colorize(sess, "miss", fmt.Sprintf(" You miss %s.", mobDisplayName(mob, true))))
+
+ if isRanged {
+ p.AmmoQty--
+ 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) mobAttack(sess *net.Session, p *player.Player, mob *world.MobInstance) {
- _, _, defBonus := combat.AttackStyleBonus(string(p.AttackStyle))
+ _, _, defStyleBonus := combat.AttackStyleBonus(string(p.AttackStyle))
- equipDef := 0
- for _, itemID := range p.Equipment {
- def, err := g.ItemStore.Load(itemID)
- if err == nil {
- equipDef += def.Stats.DefenseBonus
- }
+ mobAttackType := mob.AttackType
+ if mobAttackType == "" {
+ mobAttackType = "crush"
}
- attRoll := combat.AttackRoll(mob.Attack, 0, 0)
- defRoll := combat.DefenseRoll(p.Level(player.Defense), defBonus, equipDef)
+ attRoll := combat.AttackRoll(mob.Attack, 0, mob.AttackBonus)
+
+ totals := g.playerEquipBonuses(p)
+ equipDef := combat.SelectDefenseBonus(mobAttackType,
+ totals.StabDefense, totals.SlashDefense, totals.CrushDefense,
+ totals.ScienceDefense, totals.RangedDefense)
+ defRoll := combat.DefenseRoll(p.Level(player.Defense), defStyleBonus, equipDef)
if combat.HitCheck(attRoll, defRoll) {
- maxHit := combat.MaxHit(mob.Strength, 0, 0)
+ maxHit := combat.MaxHit(mob.Strength, 0, mob.StrengthBonus)
dmg := combat.RollDamage(maxHit)
p.HP -= dmg
@@ -375,25 +439,32 @@ func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInst
}
}
-func (g *Game) awardCombatXP(p *player.Player, dmg int) ([]xpGain, []player.SkillName) {
+func (g *Game) awardCombatXP(p *player.Player, dmg int, isRanged bool) ([]xpGain, []player.SkillName) {
baseXP := dmg * 4
var gains []xpGain
var leveledUp []player.SkillName
- switch p.AttackStyle {
- case player.Accurate:
- gains = []xpGain{{string(player.Attack), baseXP * 3 / 4}, {string(player.Hitpoints), baseXP / 4}}
- case player.Aggressive:
- gains = []xpGain{{string(player.Strength), baseXP * 3 / 4}, {string(player.Hitpoints), baseXP / 4}}
- case player.Defensive:
- gains = []xpGain{{string(player.Defense), baseXP * 3 / 4}, {string(player.Hitpoints), baseXP / 4}}
- case player.Balanced:
- quarter := baseXP / 4
+ if isRanged {
gains = []xpGain{
- {string(player.Attack), quarter},
- {string(player.Strength), quarter},
- {string(player.Defense), quarter},
- {string(player.Hitpoints), quarter},
+ {string(player.Ranged), baseXP * 3 / 4},
+ {string(player.Hitpoints), baseXP / 4},
+ }
+ } else {
+ switch p.AttackStyle {
+ case player.Accurate:
+ gains = []xpGain{{string(player.Attack), baseXP * 3 / 4}, {string(player.Hitpoints), baseXP / 4}}
+ case player.Aggressive:
+ gains = []xpGain{{string(player.Strength), baseXP * 3 / 4}, {string(player.Hitpoints), baseXP / 4}}
+ case player.Defensive:
+ gains = []xpGain{{string(player.Defense), baseXP * 3 / 4}, {string(player.Hitpoints), baseXP / 4}}
+ case player.Balanced:
+ quarter := baseXP / 4
+ gains = []xpGain{
+ {string(player.Attack), quarter},
+ {string(player.Strength), quarter},
+ {string(player.Defense), quarter},
+ {string(player.Hitpoints), quarter},
+ }
}
}