diff --git a/game/engine/interface/ActorProject.lua b/game/engine/interface/ActorProject.lua index 2b0f48a901f8cee743e58d9c56dc2452c3439d94..49997ad03146bf78c671ba288c1a9fca1480f673 100644 --- a/game/engine/interface/ActorProject.lua +++ b/game/engine/interface/ActorProject.lua @@ -102,7 +102,7 @@ function _M:project(t, x, y, damtype, dam, particles) if particles then game.level.map:particleEmitter(px, py, 1, particles.type) end - if damtype(px, py) then stop=true break end + if damtype(px, py, tg, self) then stop=true break end end if stop then break end end @@ -217,7 +217,7 @@ function _M:projectDoAct(typ, tg, damtype, dam, particles, px, py, tmp) if particles and type(particles) == "table" then game.level.map:particleEmitter(px, py, 1, particles.type) end - if damtype(px, py) then return true end + if damtype(px, py, tg, self) then return true end return false else -- Call the projected method of the target grid if possible diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index b3fa3ae119d828afb7440b024334932492200877..52e8e6c64f5f38b8d49c420c3a26fb2cfd34915d 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -32,6 +32,7 @@ require "engine.interface.ActorQuest" require "engine.interface.BloodyDeath" require "engine.interface.ActorFOV" require "mod.class.interface.Combat" +require "mod.class.interface.Archery" local Faction = require "engine.Faction" local Map = require "engine.Map" local DamageType = require "engine.DamageType" @@ -50,7 +51,8 @@ module(..., package.seeall, class.inherit( engine.interface.ActorQuest, engine.interface.BloodyDeath, engine.interface.ActorFOV, - mod.class.interface.Combat + mod.class.interface.Combat, + mod.class.interface.Archery )) -- Dont save the can_see_cache diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index d337f057a698eeb46a7db9c739bebbd5ba7a9200..436d248ff24e73d2d8f061feca76884df900e25f 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -469,7 +469,7 @@ function _M:setupCommands() self.player.esp.all = 1 self.player.esp.range = 50 self.player.inc_damage.all = 100000 - self:changeLevel(2, "caverns-osse") + self:changeLevel(2, "eruan") -- self:changeLevel(1, "wilderness-arda-fareast") -- game.memory_levels["wilderness-arda-fareast-1"] = game.level -- self.player:grantQuest("orc-pride") diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index 5d440d28a9c5a9425cca8a80ba907b4458acf404..6bbb80a6ccf852869f1514259d4f50ac317d11dc 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -143,112 +143,6 @@ function _M:attackTarget(target, damtype, mult, noenergy) return hit end -function _M:archeryShoot(damtype, mult, on_hit, tg, params) - local weapon, ammo = self:hasArcheryWeapon() - local sound, sound_miss = nil, nil - if not weapon then - game.logPlayer(self, "You must wield a bow or a sling (%s)!", ammo) - return nil - end - params = params or {} - - print("[SHOOT WITH]", weapon.name, ammo.name) - local realweapon = weapon - weapon = weapon.combat - - local ret = {} - - local tg = tg or {type="bolt"} - if not tg.range then tg.range=weapon.range or 10 end - tg.display = tg.display or {display='/'} - tg.speed = tg.speed or 20 - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:projectile(tg, x, y, function(tx, ty) - for i = 1, params.multishots or 1 do - local target = game.level.map(tx, ty, game.level.map.ACTOR) - if not target then return end - - local ammo = ammo - if not params.one_shot then - ammo = self:removeObject(self:getInven("QUIVER"), 1) - if not ammo then return end - end - if params.limit_shots then if params.limit_shots <= 0 then return end end - - ret.firsttarget = ret.firsttarget or target - ammo = ammo.combat - - damtype = damtype or ammo.damtype or DamageType.PHYSICAL - mult = mult or 1 - - if params.limit_shots then params.limit_shots = params.limit_shots - 1 end - - -- Does the blow connect? yes .. complex :/ - local atk, def = self:combatAttack(weapon), target:combatDefenseRanged() - local dam, apr, armor = self:combatDamage(ammo), self:combatAPR(ammo), target:combatArmor() - print("[ATTACK] to ", target.name, " :: ", dam, apr, armor, "::", mult) - if not self:canSee(target) then atk = atk / 3 end - - -- If hit is over 0 it connects, if it is 0 we still have 50% chance - local hitted = false - if self:checkHit(atk, def) then - print("[ATTACK] raw dam", dam, "versus", armor, "with APR", apr) - local dam = math.max(0, dam - math.max(0, armor - apr)) - local damrange = self:combatDamageRange(ammo) - dam = rng.range(dam, dam * damrange) - print("[ATTACK] after range", dam) - local crit - dam, crit = self:physicalCrit(dam, ammo, target) - print("[ATTACK] after crit", dam) - dam = dam * mult - print("[ATTACK] after mult", dam) - if crit then game.logSeen(self, "%s performs a critical strike!", self.name:capitalize()) end - DamageType:get(damtype).projector(self, target.x, target.y, damtype, math.max(0, dam)) - game.level.map:particleEmitter(target.x, target.y, 1, "archery") - hitted = true - - if on_hit then on_hit(target, target.x, target.y) end - else - local srcname = game.level.map.seens(self.x, self.y) and self.name:capitalize() or "Something" - game.logSeen(target, "%s misses %s.", srcname, target.name) - end - - -- Ranged project - if hitted and not target.dead then for typ, dam in pairs(self.ranged_project) do - if dam > 0 then - DamageType:get(typ).projector(self, target.x, target.y, typ, dam) - end - end end - - -- Regen on being hit - if hitted and not target.dead and target:attr("stamina_regen_on_hit") then target:incStamina(target.stamina_regen_on_hit) end - if hitted and not target.dead and target:attr("mana_regen_on_hit") then target:incMana(target.mana_regen_on_hit) end - - ret.speed = self:combatSpeed(weapon) - ret.hitted = hitted - end - end) - - if ret.hitted and not sound then sound = weapon.sound - elseif not ret.hitted and not sound_miss then sound_miss = weapon.sound_miss end - - print("[SHOOT] speed", ret.speed or 1, "=>", game.energy_to_act * (ret.speed or 1)) - self:useEnergy(game.energy_to_act * (ret.speed or 1)) - - -- If we used only one arrow, use it - if params.one_shot then self:removeObject(self:getInven("QUIVER"), 1) end - - if sound then game:playSoundNear(ret.firsttarget or self, sound) - elseif sound_miss then game:playSoundNear(ret.firsttarget or self, sound_miss) end - - if ammo:getNumber() < 10 or ammo:getNumber() == 50 or ammo:getNumber() == 40 or ammo:getNumber() == 25 then - game.logPlayer(self, "You only have %d %s left!", ammo:getNumber(), ammo.name) - end - - return ret.hitted -end - --- Computes a logarithmic chance to hit, opposing chance to hit to chance to miss -- This will be used for melee attacks, physical and spell resistance function _M:checkHit(atk, def, min, max, factor) @@ -634,21 +528,6 @@ function _M:combatMentalResist() return self.combat_mentalresist + (self:getCun() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.25 end ---- Check if the actor has a bow or sling and corresponding ammo -function _M:hasArcheryWeapon() - if not self:getInven("MAINHAND") then return nil, "no shooter" end - if not self:getInven("QUIVER") then return nil, "no ammo" end - local weapon = self:getInven("MAINHAND")[1] - local ammo = self:getInven("QUIVER")[1] - if not weapon or not weapon.archery then - return nil, "no shooter" - end - if not ammo or not ammo.archery_ammo or weapon.archery ~= ammo.archery_ammo then - return nil, "bad or no ammo" - end - return weapon, ammo -end - --- Check if the actor has a gem bomb in quiver function _M:hasAlchemistWeapon() if not self:getInven("QUIVER") then return nil, "no ammo" end diff --git a/game/modules/tome/data/talents/spells/fire-alchemy.lua b/game/modules/tome/data/talents/spells/fire-alchemy.lua index 2db3bc60c6c9c692946b1549001fb7b638064840..fd453ba65d6e8256a78afbdbe706f3c5ecceeb67 100644 --- a/game/modules/tome/data/talents/spells/fire-alchemy.lua +++ b/game/modules/tome/data/talents/spells/fire-alchemy.lua @@ -130,7 +130,8 @@ newTalent{ sustain_mana = 250, points = 5, range = 1, - proj_speed = 2, + proj_speed = 1.3, + range = 12, do_fire = function(self, t) if self:getMana() <= 0 then local old = self.energy.value @@ -174,7 +175,7 @@ newTalent{ end, deactivate = function(self, t, p) game.logSeen(self, "#FF8000#The raging fire around %s calms down and disappears.", self.name) - self:removeTemporaryValue("onhit", p.onhit) + self:removeTemporaryValue("on_melee_hit", p.onhit) self:removeTemporaryValue("resists", p.res) self:removeTemporaryValue("mana_regen", p.drain) return true diff --git a/game/modules/tome/data/talents/techniques/archery.lua b/game/modules/tome/data/talents/techniques/archery.lua index 69f5d7b22a3c82d476369bf29dc3921a3e0158a2..4d5abbe8190e9c832fad84cd383b2136cec27aeb 100644 --- a/game/modules/tome/data/talents/techniques/archery.lua +++ b/game/modules/tome/data/talents/techniques/archery.lua @@ -27,9 +27,10 @@ newTalent{ range = 20, message = "@Source@ shoots!", action = function(self, t) - local energy = self.energy.value - self:archeryShoot() - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets() + if not targets then return end + self:archeryShoot(targets, t) + return true end, info = function(self, t) return ([[Shoot your bow or sling!]]) @@ -47,10 +48,10 @@ newTalent{ require = techs_dex_req1, range = 20, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1.1, 2.2)) - print(energy , self.energy.value, energy ~= self.energy.value) - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets() + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 1.1, 2.2)}) + return true end, info = function(self, t) return ([[A steady shot, doing %d%% damage.]]):format(self:combatTalentWeaponDamage(t, 1.1, 2.2) * 100) @@ -144,11 +145,10 @@ newTalent{ require = techs_dex_req4, range = 20, action = function(self, t) - local energy = self.energy.value - self.combat_physcrit = self.combat_physcrit + 1000 - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1.2, 2)) - self.combat_physcrit = self.combat_physcrit - 1000 - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets() + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 1.2, 2), crit_chance=1000}) + return true end, info = function(self, t) return ([[You concentrate on your aim to produce a guaranted critical hit (with a base damage of %d%%).]]):format(self:combatTalentWeaponDamage(t, 1.2, 2) * 100) @@ -205,16 +205,18 @@ newTalent{ stamina = 15, require = techs_dex_req2, range = 20, + archery_onhit = function(self, t, target, x, y) + if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then + target:setEffect(target.EFF_SLOW, 7, {power=util.bound((self:combatAttack() * 0.15 * self:getTalentLevel(t)) / 100, 0.1, 0.4)}) + else + game.logSeen(target, "%s resists!", target.name:capitalize()) + end + end, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), function(target, x, y) - if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then - target:setEffect(target.EFF_SLOW, 7, {power=util.bound((self:combatAttack() * 0.15 * self:getTalentLevel(t)) / 100, 0.1, 0.4)}) - else - game.logSeen(target, "%s resists!", target.name:capitalize()) - end - end) - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets() + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 1, 1.5)}) + return true end, info = function(self, t) return ([[You fire a crippling shot, doing %d%% damage and reducing your target's speed by %0.2f for 7 turns.]]):format(self:combatTalentWeaponDamage(t, 1, 1.5) * 100, util.bound((5 + 5 * self:getTalentLevel(t)) / 100, 0.1, 0.4)) @@ -231,16 +233,18 @@ newTalent{ stamina = 15, require = techs_dex_req3, range = 20, + archery_onhit = function(self, t, target, x, y) + if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then + target:setEffect(target.EFF_PINNED, 2 + self:getTalentLevelRaw(t), {}) + else + game.logSeen(target, "%s resists!", target.name:capitalize()) + end + end, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.4), function(target, x, y) - if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then - target:setEffect(target.EFF_PINNED, 2 + self:getTalentLevelRaw(t), {}) - else - game.logSeen(target, "%s resists!", target.name:capitalize()) - end - end) - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets() + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 1, 1.4)}) + return true end, info = function(self, t) return ([[You fire a pinning shot, doing %d%% damage and pinning your target to the ground for %d turns.]]):format(self:combatTalentWeaponDamage(t, 1, 1.4) * 100, 2 + self:getTalentLevelRaw(t)) @@ -257,16 +261,19 @@ newTalent{ stamina = 15, require = techs_dex_req4, range = 20, + archery_onhit = function(self, t, target, x, y) + if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then + target:setEffect(target.EFF_STUNNED, 2 + self:getTalentLevelRaw(t), {}) + else + game.logSeen(target, "%s resists!", target.name:capitalize()) + end + end, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 0.5, 1.5), function(target, x, y) - if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) then - target:setEffect(target.EFF_STUNNED, 2 + self:getTalentLevelRaw(t), {}) - else - game.logSeen(target, "%s resists!", target.name:capitalize()) - end - end, {type="ball", radius=1 + self:getTalentLevel(t) / 3}) - return energy ~= self.energy.value + local tg = {type="ball", radius=1 + self:getTalentLevel(t) / 3} + local targets = self:archeryAcquireTargets(tg, {one_shot=true}) + if not targets then return end + self:archeryShoot(targets, t, tg, {mult=self:combatTalentWeaponDamage(t, 0.5, 1.5)}) + return true end, info = function(self, t) return ([[You fire multiple shots at the area, doing %d%% damage and stunning your targets for %d turns.]]):format(self:combatTalentWeaponDamage(t, 0.5, 1.5) * 100, 2 + self:getTalentLevelRaw(t)) diff --git a/game/modules/tome/data/talents/techniques/bow.lua b/game/modules/tome/data/talents/techniques/bow.lua index a952e7c8b82f9cc3c1ae87d59ac4ac530b5f5f29..edfa4f4685ee8c2ddc1c984f0397bd54a68c811c 100644 --- a/game/modules/tome/data/talents/techniques/bow.lua +++ b/game/modules/tome/data/talents/techniques/bow.lua @@ -38,11 +38,10 @@ newTalent{ require = techs_dex_req2, range = 20, action = function(self, t) - local energy = self.energy.value - self.combat_apr = self.combat_apr + 1000 - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), nil, {type="beam"}, {one_shot=true}) - self.combat_apr = self.combat_apr - 1000 - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets({type="beam"}, {one_shot=true}) + if not targets then return end + self:archeryShoot(targets, t, {type="beam"}, {mult=self:combatTalentWeaponDamage(t, 1, 1.5), apr=1000}) + return true end, info = function(self, t) return ([[You fire an arrow that cuts right through anything, piercing multiple targets if possible with nigh infinite armor penetration, doing %d%% damage.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.5)) @@ -59,9 +58,11 @@ newTalent{ require = techs_dex_req3, range = 20, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1.2, 1.7), nil, {type="ball", radius=1}, {limit_shots=2}) - return energy ~= self.energy.value + local tg = {type="ball", radius=1} + local targets = self:archeryAcquireTargets(tg, {limit_shots=2}) + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 1.2, 1.7)}) + return true end, info = function(self, t) return ([[You fire two arrows at your target, hitting it and a nearby foe if possible, doing %d%% damage.]]):format(100 * self:combatTalentWeaponDamage(t, 1.2, 1.7)) @@ -78,9 +79,11 @@ newTalent{ require = techs_dex_req4, range = 20, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 0.6, 1.3), nil, {type="ball", radius=2 + self:getTalentLevel(t)/3, firendlyfire=false}) - return energy ~= self.energy.value + local tg = {type="ball", radius=2 + self:getTalentLevel(t)/3, friendlyfire=false} + local targets = self:archeryAcquireTargets(tg) + if not targets then return end + self:archeryShoot(targets, t, {type="bolt",friendlyfire=false}, {mult=self:combatTalentWeaponDamage(t, 0.6, 1.3)}) + return true end, info = function(self, t) return ([[You fire multiple arrows at the area, doing %d%% damage.]]):format(100 * self:combatTalentWeaponDamage(t, 0.6, 1.3)) diff --git a/game/modules/tome/data/talents/techniques/sling.lua b/game/modules/tome/data/talents/techniques/sling.lua index 4226823a6846f53ece3bcd7c7174e9e67a8e8344..b5e536ccd69c527bba0135692e7cc5049d488562 100644 --- a/game/modules/tome/data/talents/techniques/sling.lua +++ b/game/modules/tome/data/talents/techniques/sling.lua @@ -37,16 +37,18 @@ newTalent{ stamina = 15, require = techs_dex_req2, range = 20, + archery_onhit = function(self, t, target, x, y) + if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) and target:canBe("blind") then + target:setEffect(target.EFF_BLINDED, 2 + self:getTalentLevelRaw(t), {}) + else + game.logSeen(target, "%s resists!", target.name:capitalize()) + end + end, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), function(target, x, y) - if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 10) and target:canBe("blind") then - target:setEffect(target.EFF_BLINDED, 2 + self:getTalentLevelRaw(t), {}) - else - game.logSeen(target, "%s resists!", target.name:capitalize()) - end - end) - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets() + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 1, 1.5)}) + return true end, info = function(self, t) return ([[You fire a shot to your target's eyes, blinding it for %d turns and doing %d%% damage.]]):format(2 + self:getTalentLevelRaw(t), 100 * self:combatTalentWeaponDamage(t, 1, 1.5)) @@ -62,17 +64,19 @@ newTalent{ stamina = 15, require = techs_dex_req3, range = 20, + archery_onhit = function(self, t, target, x, y) + if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 15) and target:canBe("knockback") then + target:knockback(self.x, self.y, 4) + game.logSeen(target, "%s is knocked back!", target.name:capitalize()) + else + game.logSeen(target, "%s resists the wave!", target.name:capitalize()) + end + end, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 1, 1.5), function(target, x, y) - if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 15) and target:canBe("knockback") then - target:knockback(self.x, self.y, 4) - game.logSeen(target, "%s is knocked back!", target.name:capitalize()) - else - game.logSeen(target, "%s resists the wave!", target.name:capitalize()) - end - end) - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets() + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 1, 1.5)}) + return true end, info = function(self, t) return ([[You fire a mighty shot at your target doing %d%% damage and knocking it back.]]):format(100 * self:combatTalentWeaponDamage(t, 1, 1.5)) @@ -89,9 +93,10 @@ newTalent{ require = techs_dex_req4, range = 20, action = function(self, t) - local energy = self.energy.value - self:archeryShoot(nil, self:combatTalentWeaponDamage(t, 0.3, 0.7), nil, nil, {multishots=2+self:getTalentLevelRaw(t)/2}) - return energy ~= self.energy.value + local targets = self:archeryAcquireTargets(nil, {multishots=2+self:getTalentLevelRaw(t)/2}) + if not targets then return end + self:archeryShoot(targets, t, nil, {mult=self:combatTalentWeaponDamage(t, 0.3, 0.7)}) + return true end, info = function(self, t) return ([[You fire %d shots at your target, doing %d%% damage with each shot.]]):format(2+self:getTalentLevelRaw(t)/2, 100 * self:combatTalentWeaponDamage(t, 0.3, 0.7))