feat: extend game with Game:awardKill and Game:applyWeaponRiders
This commit is contained in:
1 parent
928f1e6a9e
commit
9d17fcb7d8
1 file changed
+166
-8
+166
-8
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user