From abd612c15799f604e671e83dc7c410ed2b44185f Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Thu, 25 Jun 2026 15:40:48 -0400 Subject: slop refactor --- internal/game/cmd_attack.go | 730 -------------------------------------------- 1 file changed, 730 deletions(-) (limited to 'internal/game/cmd_attack.go') diff --git a/internal/game/cmd_attack.go b/internal/game/cmd_attack.go index abaefbb..5635fd0 100644 --- a/internal/game/cmd_attack.go +++ b/internal/game/cmd_attack.go @@ -2,740 +2,11 @@ package game import ( "fmt" - "math/rand" - "sort" - "strconv" "strings" - "thehouseoficarus/internal/action" - "thehouseoficarus/internal/color" - "thehouseoficarus/internal/combat" - "thehouseoficarus/internal/engine" "thehouseoficarus/internal/net" - "thehouseoficarus/internal/object" - "thehouseoficarus/internal/player" - "thehouseoficarus/internal/world" ) -func (g *Game) doAttack(sess *net.Session, input string) { - p := sess.Player - - if combat.GetCombat(p.Name) != nil { - sess.WriteLine("You are already in combat!") - return - } - - if p.Action != nil { - g.cancelAction(p) - } - - mob := g.findMob(sess, input, p.RoomID) - if mob == nil { - return - } - - if mob.HP <= 0 { - if mob.IsTask() { - sess.WriteLine(fmt.Sprintf("%s is already complete.", mobDisplayName(mob, true))) - } else { - sess.WriteLine("That is already dead.") - } - return - } - - if mob.Protected { - sess.WriteLine(fmt.Sprintf("You can't attack %s!", mobDisplayName(mob, true))) - return - } - - if mob.AssassinLevel > 0 && p.Level(player.Assassin) < mob.AssassinLevel { - sess.WriteLine(fmt.Sprintf("You need Assassin level %d to attack %s.", mob.AssassinLevel, mobDisplayName(mob, true))) - return - } - - if combat.IsMobInCombat(mob.InstanceID) { - sess.WriteLine(fmt.Sprintf("%s is already engaged in combat!", mobDisplayName(mob, false))) - 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) -} - -func (g *Game) findMob(sess *net.Session, input string, roomID int) *world.MobInstance { - lower := strings.ToLower(input) - mobs := g.MobStore.MobsInRoom(roomID) - - idx := -1 - name := lower - if dotPos := strings.Index(lower, "."); dotPos > 0 { - if n, err := strconv.Atoi(lower[:dotPos]); err == nil && n > 0 { - idx = n - name = lower[dotPos+1:] - } - } - - var exact []*world.MobInstance - var prefix []*world.MobInstance - for _, m := range mobs { - q := m.MatchQuality(name) - if q == world.MatchExact { - exact = append(exact, m) - } else if q == world.MatchPrefix { - prefix = append(prefix, m) - } - } - - candidates := exact - if len(candidates) == 0 { - candidates = prefix - } - - if len(candidates) == 0 { - sess.WriteLine(fmt.Sprintf("There's no '%s' here.", input)) - return nil - } - - sort.Slice(candidates, func(i, j int) bool { - return candidates[i].InstanceID < candidates[j].InstanceID - }) - - if idx > 0 { - if idx-1 < len(candidates) { - return candidates[idx-1] - } - return nil - } - - if len(candidates) == 1 { - return candidates[0] - } - - seen := make(map[string]bool) - for _, m := range candidates { - seen[mobDisplayName(m, false)] = true - } - if len(seen) > 1 { - sess.WriteLine("Which one?") - return nil - } - return candidates[0] -} - -func (g *Game) startCombat(sess *net.Session, p *player.Player, mob *world.MobInstance) { - g.cancelRest(p.Name) - g.cancelBgAction(p) - - combat.EnterCombat(p.Name, mob.InstanceID) - p.AttackTimer = 0 - - isTask := mob.IsTask() - autotriggerActive := p.AutotriggerMod != "" - - if !autotriggerActive { - attBonus, strBonus, defBonus := combat.AttackStyleBonus(string(p.AttackStyle)) - var styleParts []string - if attBonus > 0 { - styleParts = append(styleParts, fmt.Sprintf("+%d atk", attBonus)) - } - if strBonus > 0 { - styleParts = append(styleParts, fmt.Sprintf("+%d str", strBonus)) - } - if defBonus > 0 { - styleParts = append(styleParts, fmt.Sprintf("+%d def", defBonus)) - } - styleStr := "" - if len(styleParts) > 0 { - styleStr = " Style: " + string(p.AttackStyle) + " (" + strings.Join(styleParts, ", ") + ")" - } - if isTask { - sess.WriteLine(fmt.Sprintf("You begin to %s %s.%s", taskWorkVerb(mob), g.colorize(sess, "mob", mobDisplayName(mob, true)), styleStr)) - } else { - sess.WriteLine(fmt.Sprintf("You attack %s!%s", g.colorize(sess, "mob", mobDisplayName(mob, true)), styleStr)) - } - } else { - mod := GetMod(p.AutotriggerMod) - modName := p.AutotriggerMod - if mod != nil { - modName = mod.Name - } - if isTask { - sess.WriteLine(fmt.Sprintf("You begin to %s %s using %s!", - taskWorkVerb(mob), - g.colorize(sess, "mob", mobDisplayName(mob, true)), - g.colorize(sess, "science_mod", modName))) - } else { - sess.WriteLine(fmt.Sprintf("You attack %s with %s!", - g.colorize(sess, "mob", mobDisplayName(mob, true)), - g.colorize(sess, "science_mod", modName))) - } - } - if isTask { - g.broadcastAction(sess, "\n%s begins working on %s.", p.Name, mobDisplayName(mob, true)) - } else { - g.broadcastAction(sess, "\n%s attacks %s!", p.Name, mobDisplayName(mob, true)) - } - - g.Ticks.Subscribe(1, func() bool { - cs := combat.GetCombat(p.Name) - if cs == nil || !cs.Active { - return false - } - currentMob := g.MobStore.GetInstance(cs.MobID) - if currentMob == nil || currentMob.HP <= 0 { - g.endCombat(sess, p, currentMob) - return false - } - - 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 { - 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) - } - } - } - - if currentMob.HP <= 0 { - g.endCombat(sess, p, currentMob) - return false - } - return true - }) - - if !isTask { - g.Ticks.Subscribe(engine.ToTicks(mob.Speed), func() bool { - cs := combat.GetCombat(p.Name) - if cs == nil || !cs.Active { - return false - } - currentMob := g.MobStore.GetInstance(cs.MobID) - if currentMob == nil || currentMob.HP <= 0 { - return false - } - g.mobAttack(sess, p, currentMob) - if p.HP <= 0 { - g.endCombat(sess, p, currentMob) - return false - } - return true - }) - } -} - -func (g *Game) playerAttack(sess *net.Session, p *player.Player, mob *world.MobInstance) { - if g.processConsumeQueue(p, sess) { - return - } - - attackType, weaponType, attRoll, defRoll, maxHit := g.calculatePlayerAttackRoll(p, mob) - - if g.isSafespotted(p.Name) && attackType != "ranged" && attackType != "science" { - if ss, ok := g.safespot.Get(p.Name); 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 { - g.applyPlayerMiss(sess, p, mob, weaponType) - } -} - -func (g *Game) calculatePlayerAttackRoll(p *player.Player, mob *world.MobInstance) (attackType string, weaponType object.WeaponType, attRoll int, defRoll int, maxHit int) { - attackType = "crush" - if itemID, ok := p.Equipment[object.SlotMainHand]; ok { - 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 - } - } - - totals := g.playerEquipBonuses(p) - isRanged := weaponType == object.WeaponRanged - - if isRanged { - 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) - - 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) - } 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) - - 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) - } - return -} - -func (g *Game) playerAttackUnarmed(sess *net.Session, p *player.Player, mob *world.MobInstance) { - if g.processConsumeQueue(p, sess) { - 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)) - effectiveAccuracy := p.Level(player.Accuracy) + g.techLevelBonus(p, "accuracy") + g.buffLevelBonus(p, "accuracy") - attRoll = combat.EffectiveRoll(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) - 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) - - mob.HP -= dmg - if mob.HP < 0 { - mob.HP = 0 - } - if mob.FinishingBlow != "" && mob.HP <= 0 { - mob.HP = 1 - } - if mob.HP < mob.MaxHP && mob.HP > 0 { - mob.StartRegen() - } - - 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))) - } - - isRanged := weaponType == object.WeaponRanged - gains := g.awardCombatXP(sess, p, dmg, isRanged) - - if isRanged { - g.consumeAmmo(sess, p) - } - - if mob.IsTask() { - 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 - } - 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) - } - 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) - } - - if isRanged && p.AmmoQty <= 0 { - sess.WriteLine("You've run out of ammo!") - combat.LeaveCombat(p.Name) - } -} - -func (g *Game) applyPlayerMiss(sess *net.Session, p *player.Player, mob *world.MobInstance, weaponType object.WeaponType) { - sess.WriteLine(g.colorize(sess, "miss", fmt.Sprintf("You miss %s.", mobDisplayName(mob, true)))) - - if weaponType == object.WeaponRanged { - g.consumeAmmo(sess, p) - if p.AmmoQty <= 0 { - 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) { - g.applyMobHit(sess, p, mob) - } else { - g.applyMobMiss(sess, mob) - } -} - -func (g *Game) calculateMobAttack(p *player.Player, mob *world.MobInstance) (attRoll int, defRoll int) { - _, _, defStyleBonus := combat.AttackStyleBonus(string(p.AttackStyle)) - - mobAttackType := mob.AttackType - if mobAttackType == "" { - mobAttackType = "crush" - } - - attRoll = combat.EffectiveRoll(mob.Attack, 0, mob.AttackBonus) - - totals := g.playerEquipBonuses(p) - equipDef := combat.SelectBonus(mobAttackType, - totals.StabDefense, totals.SlashDefense, totals.CrushDefense, - totals.ScienceDefense, totals.RangedDefense) - defRoll = combat.EffectiveRoll(p.Level(player.Defense)+g.techLevelBonus(p, "defense")+g.buffLevelBonus(p, "defense"), defStyleBonus, equipDef) - return -} - -func (g *Game) applyMobHit(sess *net.Session, p *player.Player, mob *world.MobInstance) { - maxHit := combat.MaxHit(mob.Strength, 0, mob.StrengthBonus) - dmg := combat.RollDamage(maxHit) - dmg = g.applyTechProtection(p, mob, dmg) - - if mob.DamageWithout != "" { - hasProtection := false - for _, itemID := range p.Equipment { - if itemID == mob.DamageWithout { - hasProtection = true - break - } - } - if !hasProtection { - dmg = dmg * 3 / 2 - if dmg < 1 { - dmg = 1 - } - cs := combat.GetCombat(p.Name) - if cs != nil && !cs.DamageWarningShown { - cs.DamageWarningShown = true - fbDef, _ := g.ItemStore.Load(mob.DamageWithout) - fbName := mob.DamageWithout - if fbDef != nil { - fbName = fbDef.Name - } - attacker := mob.Name - if !mob.Unique { - attacker = "The " + mob.Name - } - sess.WriteLine(g.colorize(sess, "warning", - fmt.Sprintf(" %s's attack is extra effective! Equip %s for protection.", - attacker, fbName))) - } - } - } - - p.HP -= dmg - if p.HP < 0 { - p.HP = 0 - } - p.StartRegen() - g.AccountStore.SaveCharacter(p) - - attacker := mob.Name - if !mob.Unique { - attacker = "The " + mob.Name - } - mobName := mobDisplayName(mob, true) - w := len(fmt.Sprintf("%s hits you for %d damage.", attacker, 999)) - if w2 := len(fmt.Sprintf("You hit %s for %d damage.", mobName, 999)); w2 > w { - w = w2 - } - prefix := fmt.Sprintf("%s hits you for %s damage.", g.colorize(sess, "mob", attacker), g.colorize(sess, "damage_taken", fmt.Sprint(dmg))) - visLen := color.VisibleLen(prefix) - if visLen < w { - prefix += strings.Repeat(" ", w-visLen+1) - } - hpSuffix := fmt.Sprintf("%s [%2d/%d]", g.hpBar(sess, p.HP, p.MaxHP()), p.HP, p.MaxHP()) - sess.WriteLine(prefix + hpSuffix) - - if ss, hasSS := g.safespot.Get(p.Name); hasSS && ss.HideCountdown > 0 { - g.safespot.Delete(p.Name) - sess.WriteLine(g.colorize(sess, "error", "You're hit while repositioning! You failed to hide.")) - } - - if p.MoveTicks > 0 { - p.ClearMoveState() - sess.WriteLine("Can't escape!") - } -} - -func (g *Game) applyMobMiss(sess *net.Session, mob *world.MobInstance) { - attacker := mob.Name - if !mob.Unique { - attacker = "The " + mob.Name - } - sess.WriteLine(g.colorize(sess, "miss", fmt.Sprintf("%s misses you.", attacker))) -} - -func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInstance) { - combat.LeaveCombat(p.Name) - - if p.HP <= 0 { - g.killPlayer(sess, p, mob) - return - } - - if mob != nil && mob.HP <= 0 { - isTask := mob.IsTask() - p.Stats.RecordMobKill(mob.DefID) - - if isTask { - complete := mob.CompleteMessage - if complete == "" { - complete = fmt.Sprintf("You finish your work on %s!", mobDisplayName(mob, true)) - } - sess.WriteLine(g.colorize(sess, "victory", "\n"+complete)) - } else { - sess.WriteLine(g.colorize(sess, "victory", fmt.Sprintf("\nYou have defeated %s!", mobDisplayName(mob, true)))) - g.onAssassinKill(sess, p, mob) - } - - if g.Hub != nil { - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess && other.Player != nil { - if isTask { - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s finishes working on %s.", p.Name, mobDisplayName(mob, false)))) - } else { - op := other.Player - mobLvl := mobCombatLevel(mob) - levelStr := g.levelColorize(other, op.CombatLevel(), mobLvl, fmt.Sprintf("(level %d)", mobLvl)) - other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s has slain %s %s!", p.Name, mobDisplayName(mob, false), levelStr))) - } - } - } - } - - dropLabel := func() string { - if isTask { - return "You receive:" - } - dropper := mob.Name - if !mob.Unique { - dropper = "The " + mob.Name - } - return dropper + " drops:" - }() - - if mob.Drops.Remains != "" { - g.World.AddReservedItem(p.RoomID, mob.Drops.Remains, 1, p.Name) - def, _ := g.ItemStore.Load(mob.Drops.Remains) - name := mob.Drops.Remains - if def != nil { - name = def.Name - } - coloredName := g.itemColorize(sess, def, name) - sess.WriteLine(fmt.Sprintf("%s %s", g.colorize(sess, "drop_message", dropLabel), coloredName)) - } - - if len(mob.Drops.Loot) > 0 { - entry := action.ResolveDrop(g.DataDir, mob.Drops.Loot) - if entry != nil && entry.ItemID != "" { - qty := entry.Quantity - if qty <= 0 { - qty = 1 - } - g.World.AddReservedItem(p.RoomID, entry.ItemID, qty, p.Name) - def, _ := g.ItemStore.Load(entry.ItemID) - name := entry.ItemID - if def != nil { - name = def.Name - } - coloredName := g.itemColorize(sess, def, name) - if qty > 1 { - sess.WriteLine(fmt.Sprintf("%s %d x %s", g.colorize(sess, "drop_message", dropLabel), qty, coloredName)) - } else { - sess.WriteLine(fmt.Sprintf("%s %s", g.colorize(sess, "drop_message", dropLabel), coloredName)) - } - } - } - - respawnTicks := engine.ToTicks(mob.RespawnTicks) - if respawnTicks <= 0 { - respawnTicks = engine.ToTicks(30) - } - instanceID := mob.InstanceID - g.Ticks.Subscribe(respawnTicks, func() bool { - g.respawnMob(instanceID) - return false - }) - g.writePrompt(sess) - } -} - -// killPlayer handles a player death from any source (combat or a room hazard). -// mob may be nil (e.g. a hazard kill); it is only used for Dead Man's Switch -// retribution. -func (g *Game) killPlayer(sess *net.Session, p *player.Player, mob *world.MobInstance) { - combat.LeaveCombat(p.Name) - - if p.HasActiveTech("retribution") { - retDef := GetTechDef("retribution") - if retDef != nil && mob != nil && mob.HP > 0 { - retDmg := int(float64(p.MaxHP()) * retDef.Effects.RetributionPct) - if retDmg > 0 { - mob.HP -= retDmg - if mob.HP < 0 { - mob.HP = 0 - } - sess.WriteLine(fmt.Sprintf("\nDead Man's Switch activates! %s takes %d damage!", - mobDisplayName(mob, true), retDmg)) - } - } - } - p.DeactivateAllTechs() - p.Stats.RecordDeath() - sess.WriteLine(g.colorize(sess, "death", "\nOh dear, you are dead!")) - p.ClearMoveState() - p.HazardTimer = 0 - if ss, ok := g.safespot.Get(p.Name); ok { - g.forceLeaveSafespot(sess, p, &ss, "") - } - g.dropItemsOnDeath(p) - p.HP = p.MaxHP() - p.RoomID = 1001 - g.AccountStore.SaveCharacter(p) - if g.Hub != nil { - g.Hub.EnterRoom(sess, p.RoomID) - } - g.doLook(sess) - g.writePrompt(sess) -} - -func (g *Game) awardCombatXP(sess *net.Session, p *player.Player, dmg int, isRanged bool) []xpGain { - baseXP := dmg * 4 - var gains []xpGain - - if isRanged { - gains = []xpGain{ - {string(player.Ranged), baseXP * 3 / 4}, - {string(player.Hitpoints), baseXP / 4}, - } - } else { - switch p.AttackStyle { - case player.Accurate: - gains = []xpGain{{string(player.Accuracy), 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.Accuracy), quarter}, - {string(player.Strength), quarter}, - {string(player.Defense), quarter}, - {string(player.Hitpoints), quarter}, - } - } - } - - for _, gain := range gains { - g.awardSkillXP(sess, p, player.SkillName(gain.Skill), gain.XP) - } - g.AccountStore.SaveCharacter(p) - return gains -} - -func (g *Game) stopCombat(playerName string) { - if cs := combat.GetCombat(playerName); cs != nil { - combat.LeaveCombat(playerName) - } -} - func (g *Game) executeAttack(sess *net.Session, args []string, rawInput string) { if len(args) == 0 { p := sess.Player @@ -786,4 +57,3 @@ func (g *Game) respawnMob(instanceID string) { } } } - -- cgit v1.2.3