feat: extend game with Game:awardKill and Game:applyWeaponRiders

This commit is contained in:
dualfroz committed 2026-09-06 15:12:41 +02:00
1 parent 928f1e6a9e
commit 9d17fcb7d8
1 file changed
+166 -8
+166 -8
View File
@@ -695,8 +695,53 @@ function Game:takeMonsterTurn(monster)
self:onActorDeath(monster)
return
end
if not Status.skipsTurn(monster) then
Ai.act(self, monster)
Conductivity.tick(self.level, monster)
local hazardLine = Hazards.afflict(self.level, monster)
if hazardLine then self.log:add(hazardLine) end
if not monster:isAlive() then
self:onActorDeath(monster)
return
end
-- Mirrors Game:hungerTorchTick's own Disease.tick(self.player) call - a
-- disease is plain generic entity state (see disease.lua), and since
-- Infighting.strike can now infect a rival monster or the player's own
-- companion/hireling/thrall (not just the player), every non-player actor
-- needs its own infection to actually progress on its turn too, or an
-- infected ally would sit permanently incubating with no way to ever
-- erupt, escalate, or be noticed.
local diseaseLine = Disease.tick(monster)
if diseaseLine then self.log:add(diseaseLine) end
if not monster:isAlive() then
self:onActorDeath(monster)
return
end
local regenLine = Abilities.regenTick(monster)
if regenLine then self.log:add(regenLine) end
local enrageLine = Bosses.tryEnrage(monster)
if enrageLine then self.log:add(enrageLine) end
local phaseLine, phaseConsumedTurn = Phases.tick(self, monster)
if phaseLine then self.log:add(phaseLine) end
if not phaseConsumedTurn and not Status.skipsTurn(monster) then
if monster.kind == "companion" then
Companions.act(self, monster)
elseif monster.kind == "hireling" then
Mercenaries.act(self, monster)
elseif monster.kind == "thrall" then
Reanimation.act(self, monster)
elseif Infighting.tryAct(self, monster) then
-- A rivalry or a discorded mind has nothing to do with how this
-- monster currently feels about the player: it fights regardless of
-- wariness or - unlike Stealth.tick's own gating - whether it has
-- otherwise noticed the player at all this turn.
elseif Factions.isWary(self.player, monster) then
-- A wary tribe member holds its ground: neither provoked nor hated
-- into open hostility yet, it leaves the player be this turn.
elseif Stealth.tick(self, monster) then
Abilities.tickCooldown(monster)
if not Abilities.tryAct(self, monster) then
Ai.act(self, monster)
end
end
end
Scheduler.spend(monster)
self:cleanupDead()
@@ -713,15 +758,128 @@ end
-- Player actions.
function Game:playerAttack(target)
local outcome = Combat.attack(self.player, target)
self.log:add(string.format("you hit %s for %d", target.name, outcome.damage))
local weapon = self.player.weapon
if weapon and weapon.affix and weapon.affix.onHit and target:isAlive() then
local rider = weapon.affix.onHit
-- Common tail of any player-caused kill: log it, grant the xp it was worth,
-- and sweep it off the level. Shared by melee and offensive spells so xp
-- always tracks a felled monster regardless of what felled it.
function Game:awardKill(target)
self.log:add(target.name .. " dies")
local burst = Abilities.onDeath(self, target)
if burst then self.log:add(burst) end
local eliteBurst = Elites.onDeath(self, target)
if eliteBurst then self.log:add(eliteBurst) end
self.stats.kills = self.stats.kills + 1
if target.isBoss then self.stats.bossKills = self.stats.bossKills + 1 end
Factions.registerKill(self.player, target)
local xp = target.isBoss and Bosses.xpFor(target) or target.mimic and Mimics.xpFor()
or target.grave and Graves.xpFor() or Monsters.xpFor(target)
xp = xp + Elites.xpBonus(target, xp)
if target.nemesis then xp = xp + Nemesis.xpBonus(target.nemesisGrudge or 1) end
self.log:addAll(Progression.grantXp(self.player, xp))
self.player.essence = self.player.essence + math.floor(Crafting.essenceFor(xp) * self:essenceMultiplier())
Quests.registerKill(self.player, target.species)
Compendium.noteKill(self.compendium, target.species)
Corpses.spawn(self.level, target)
self:resolveQuest()
if target.isBoss then
local loot = Bosses.lootFor(target)
if loot then
self.level.floorItems[#self.level.floorItems + 1] = { x = target.x, y = target.y, item = loot }
self.log:add(string.format("%s drops %s", target.name, loot.name))
end
end
if target.grave then
local gold, essence = Graves.lootFor(target)
if gold > 0 then
self.player.gold = self.player.gold + gold
self.log:add(string.format("the shade scatters %d gold as it fades", gold))
end
if essence > 0 then
self.player.essence = self.player.essence + essence
end
end
if target.nemesis then
local bounty = Nemesis.bountyGold(target.nemesisGrudge or 1)
self.player.gold = self.player.gold + bounty
self.log:add(string.format("the hunt is over - you claim %d gold from %s", bounty, target.name))
self.nemesis = nil
end
self:cleanupDead()
end
-- Applies a weapon's on-hit rider - rolled affix or socketed gem, or both -
-- to a still-living target, logging each status it lands.
function Game:applyWeaponRiders(weapon, target)
if not (weapon and target:isAlive()) then return end
local riders = {}
if weapon.affix and weapon.affix.onHit then riders[#riders + 1] = weapon.affix.onHit end
if weapon.socket and weapon.socket.onHit then riders[#riders + 1] = weapon.socket.onHit end
for _, rider in ipairs(riders) do
Status.apply(target, rider.status, rider.duration, rider.potency)
self.log:add(string.format("%s is %s", target.name, rider.status))
end
end
-- Marks a monster as no longer wary (see factions.lua): once the player has
-- aimed any offensive action at it, a tribal target that used to hold back
-- fights (or flees) like any other monster from then on, hit or miss. Any
-- offensive action also wakes a dormant target outright (see stealth.lua) -
-- an arrow or a spell that misses still gives away exactly where you are.
function Game:provoke(target)
if target and target.kind == "monster" then
target.provoked = true
Stealth.alert(target, self.player.x, self.player.y)
end
end
-- Floods a burst of noise.lua sound out from (x, y) and gives every other
-- still-dormant monster the flood actually reaches a chance to stir - a
-- clash of steel three rooms over does not single out whichever target
-- Game:provoke already woke outright, it startles whatever else is close
-- enough to hear it too. A monster startled this way only ever starts
-- searching the noise's origin (see Stealth.hear), never snaps straight to
-- hunting the way a direct hit does.
function Game:emitNoise(x, y, loudness)
local field = Noise.propagate(self.level.map, x, y, loudness)
for _, entity in ipairs(self.level.entities) do
if entity.kind == "monster" and Stealth.isUnaware(entity) then
local intensity = Noise.intensityAt(field, self.level.map, entity.x, entity.y)
if intensity > 0 and self.rng:chance(intensity, loudness) then
Stealth.hear(entity, x, y)
self.log:add(string.format("%s stirs at the noise", entity.name))
end
end
end
end
function Game:playerAttack(target)
-- Read before provoke() wakes the target below, so the bonus reflects
-- whatever it actually was the instant the blow landed.
local sneak = Stealth.sneakMultiplier(target)
self:provoke(target)
self:emitNoise(self.player.x, self.player.y, Noise.MELEE)
local multiplier = Skills.meleeDamageMultiplier(self.player, target) * sneak
local outcome = Combat.attack(self.player, target, multiplier)
Momentum.onMeleeHit(self.player)
if sneak > 1 then
self.log:add(string.format("you catch %s completely unaware", target.name))
end
self.log:add(string.format("you hit %s for %d", target.name, outcome.damage))
local broke = Durability.wearWeapon(self.player)
if broke then self.log:add(broke) end
local lifesteal = Artifacts.lifestealAmount(self.player, outcome.damage)
if lifesteal > 0 then
self.player.hp = math.min(self.player.maxHp, self.player.hp + lifesteal)
self.log:add(string.format("your %s drinks %d hp from %s", self.player.weapon.name, lifesteal, target.name))
end
self:applyWeaponRiders(self.player.weapon, target)
if Skills.consumePoisonBlade(self.player) and target:isAlive() then
Status.apply(target, Status.POISON, 5, 2)
self.log:add(string.format("%s is envenomed", target.name))
end
local masteredKey, masteredBonus = Proficiency.onMeleeHit(self.player)
if masteredKey then
self.log:add(string.format("you grow more skilled with your %s (+%d attack)", masteredKey, masteredBonus))
end
if outcome.killed then
self.log:add(target.name .. " dies")
self:cleanupDead()