diff --git a/game/engine/resolvers.lua b/game/engine/resolvers.lua index 0123317f26e51acf07b77c437c9d95ae26e2db80..c4fef90a3e85b09d774cf5017d339388a880b151 100644 --- a/game/engine/resolvers.lua +++ b/game/engine/resolvers.lua @@ -36,6 +36,14 @@ function resolvers.calc.rngavg(t) return rng.avg(t[1], t[2]) end +--- Random table element +function resolvers.rngtable(t) + return {__resolver="rngtable", t} +end +function resolvers.calc.rngtable(t) + return rng.table(t[1]) +end + --- Random bonus based on level resolvers.current_level = 1 resolvers.mbonus_max_level = 50 diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 4a0a7604fbf5d293d91e74d443c3b84cd8f371fd..be098e8d592daadc7688c4bc99521c5386a051b6 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -194,6 +194,9 @@ function _M:act() -- Still enough energy to act ? if self.energy.value < game.energy_to_act then return false end + -- Still not dead ? + if self.dead then return false end + return true end diff --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua index 9305598c68b2909555e3f4fe028c0dff478c7b28..6b7535642bb7eb3029ff29a30383420ad390f1df 100644 --- a/game/modules/tome/class/Object.lua +++ b/game/modules/tome/class/Object.lua @@ -100,6 +100,9 @@ function _M:descAttribute(attr) elseif attr == "COMBAT" then local c = self.combat return c.dam.."-"..(c.dam*(c.damrange or 1.1)).." power, "..(c.apr or 0).." apr" + elseif attr == "COMBAT_DAMTYPE" then + local c = self.combat + return c.dam.."-"..(c.dam*(c.damrange or 1.1)).." power, "..(c.apr or 0).." apr, "..DamageType:get(c.damtype).name.." damage" elseif attr == "ARMOR" then return (self.wielder and self.wielder.combat_def or 0).." def, "..(self.wielder and self.wielder.combat_armor or 0).." armor" elseif attr == "ATTACK" then @@ -161,13 +164,13 @@ function _M:getTextualDesc() -- Stop here if unided if not self:isIdentified() then return table.concat(desc, "\n") end - if self.combat then local dm = {} for stat, i in pairs(self.combat.dammod or {}) do dm[#dm+1] = ("+%d%% %s"):format(i * 100, Stats.stats_def[stat].name) end desc[#desc+1] = ("%d Power [Range %0.2f] (%s), %d Attack, %d Armor Penetration, Crit %d%%"):format(self.combat.dam or 0, self.combat.damrange or 1.1, table.concat(dm, ','), self.combat.atk or 0, self.combat.apr or 0, self.combat.physcrit or 0) + desc[#desc+1] = "Damage type: "..DamageType:get(self.combat.damtype or DamageType.PHYSICAL).name if self.combat.range then desc[#desc+1] = "Firing range: "..self.combat.range end desc[#desc+1] = "" end @@ -275,7 +278,7 @@ function _M:getTextualDesc() local use_desc = self:getUseDesc() if use_desc then desc[#desc+1] = use_desc end - return table.concat(desc, "\n ") + return desc end --- Gets the full desc of the object @@ -293,7 +296,7 @@ function _M:getDesc() desc[#desc+1] = reqs end - local textdesc = self:getTextualDesc() + local textdesc = table.concat(self:getTextualDesc(), "\n") return table.concat(desc, "\n").."\n"..textdesc end diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index 834bb3761349615630462b85339ec8370cce6a47..4e91813761c98dc32dacbb5141795b5912abf53b 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -364,8 +364,9 @@ local weapon_talents = { axe = Talents.T_AXE_MASTERY, mace = Talents.T_MACE_MASTERY, knife = Talents.T_KNIFE_MASTERY, - bow = Talents.T_BOW_MASTERY, + bow = Talents.T_BOW_MASTERY, sling = Talents.T_SLING_MASTERY, + staff = Talents.T_STAFF_MASTERY, } --- Checks weapon training @@ -595,6 +596,16 @@ function _M:hasAlchemistWeapon() return ammo end +--- Check if the actor has a two handed weapon +function _M:hasTwoStaffWeapon() + if not self:getInven("MAINHAND") then return end + local weapon = self:getInven("MAINHAND")[1] + if not weapon or weapon.subtype ~= "staff" then + return nil + end + return weapon +end + --- Check if the actor has a two handed weapon function _M:hasTwoHandedWeapon() if not self:getInven("MAINHAND") then return end diff --git a/game/modules/tome/data/general/objects/staves.lua b/game/modules/tome/data/general/objects/staves.lua index 9cef77c0ac66e39e5e6b1b9f1114a0a0a52553bd..4c8c51ccfbc7d596f5ce990e7f23ad7ae69e6d51 100644 --- a/game/modules/tome/data/general/objects/staves.lua +++ b/game/modules/tome/data/general/objects/staves.lua @@ -22,10 +22,16 @@ newEntity{ slot = "MAINHAND", slot_forbid = "OFFHAND", type = "weapon", subtype="staff", - add_name = " (#COMBAT#)", + add_name = " (#COMBAT_DAMTYPE#)", display = "\\", color=colors.LIGHT_RED, encumber = 5, rarity = 4, + combat = { + talented = "staff", + damrange = 1.2, + sound = "actions/melee", sound_miss = "actions/melee_miss", + damtype = resolvers.rngtable{DamageType.FIRE, DamageType.COLD, DamageType.ACID, DamageType.LIGHTNING, DamageType.LIGHT, DamageType.DARKNESS, DamageType.NATURE, DamageType.BLIGHT}, + }, desc = [[Staves designed for wielders of magic, by the greats of the art.]], egos = "/data/general/objects/egos/staves.lua", egos_chance = { prefix=resolvers.mbonus(40, 5), suffix=resolvers.mbonus(40, 5) }, } diff --git a/game/modules/tome/data/talents/spells/enhancement.lua b/game/modules/tome/data/talents/spells/enhancement.lua index eff5d11134a8572cb889b557a1d81f6f82be7ea4..f39cd94173d1eba25df73fff9812232897036d0d 100644 --- a/game/modules/tome/data/talents/spells/enhancement.lua +++ b/game/modules/tome/data/talents/spells/enhancement.lua @@ -106,7 +106,7 @@ newTalent{ }, activate = function(self, t) game:playSoundNear(self, "talents/spell_generic") - local power = math.floor(self:combatTalentSpellDamage(t, 2, 18)) + local power = math.min(math.floor(self:combatTalentSpellDamage(t, 2, 10)), 11) return { stats = self:addTemporaryValue("inc_stats", { [self.STAT_STR] = power, @@ -124,6 +124,6 @@ newTalent{ end, info = function(self, t) return ([[You concentrate on your inner self, increasing your stats each by %d.]]): - format(self:combatTalentSpellDamage(t, 2, 18)) + format(math.min(math.floor(self:combatTalentSpellDamage(t, 2, 10)), 11)) end, } diff --git a/game/modules/tome/data/talents/spells/golemancy.lua b/game/modules/tome/data/talents/spells/golemancy.lua index b6f1c4609aaa1348e8fd00c7fc17ecb53339570b..2eb67336dd00547cae0f7e55261b7a578bee47d5 100644 --- a/game/modules/tome/data/talents/spells/golemancy.lua +++ b/game/modules/tome/data/talents/spells/golemancy.lua @@ -39,6 +39,8 @@ local function makeGolem() open_door = true, blind_immune = 1, fear_immune = 1, + poison_immune = 1, + disease_immune = 1, see_invisible = 2, no_breath = 1, } @@ -81,7 +83,7 @@ newTalent{ -- Find space local x, y = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) if not x then - game.logPlayer(self, "Not enough space to summon!") + game.logPlayer(self, "Not enough space to refit!") return end game.zone:addEntity(game.level, self.alchemy_golem, "actor", x, y) @@ -247,10 +249,24 @@ newTalent{ mana = 10, cooldown = 20, action = function(self, t) + if not game.level:hasEntity(self.alchemy_golem) then + game.logPlayer(self, "Your golem is currently inactive.") + return + end + + -- Find space + local x, y = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) + if not x then + game.logPlayer(self, "Not enough space to invoke!") + return + end + + self.alchemy_golem:setEffect(self.alchemy_golem.EFF_MIGHTY_BLOWS, 5, {power=self:combatTalentSpellDamage(t, 15, 50)}) + self.alchemy_golem:move(x, y, true) game:playSoundNear(self, "talents/arcane") return true end, info = function(self, t) - return ([[Imbue an alchemist gem with an explosive charge of mana and throw it.]]):format() + return ([[You invoke your golem to your side, granting it a temporary melee power increase of %d for 5 turns.]]):format(self:combatTalentSpellDamage(t, 15, 50)) end, } diff --git a/game/modules/tome/data/talents/spells/spells.lua b/game/modules/tome/data/talents/spells/spells.lua index 17ff36aca722eb2e7b05e146d06cc039eb9f7554..ff763ccf6cf6c4373ced4c84bdd0b14e50eff246 100644 --- a/game/modules/tome/data/talents/spells/spells.lua +++ b/game/modules/tome/data/talents/spells/spells.lua @@ -40,6 +40,7 @@ newTalentType{ type="spell/golemancy", name = "golemancy", description = "Learn newTalentType{ type="spell/advanced-golemancy", name = "advanced-golemancy", description = "Advanced golem operations." } newTalentType{ type="spell/gemology", name = "gemology", generic = true, description = "Manipulate gems, imbue their powers into other objects." } newTalentType{ type="spell/herbalism", name = "herbalism", generic = true, description = "Herbs lore." } +newTalentType{ type="spell/staff-combat", name = "staff combat", generic = true, description = "Harness the power of magical staves." } -- Generic requires for spells based on talent level spells_req1 = { @@ -79,3 +80,4 @@ load("/data/talents/spells/enhancement.lua") load("/data/talents/spells/alchemy.lua") load("/data/talents/spells/infusion.lua") load("/data/talents/spells/golemancy.lua") +load("/data/talents/spells/staff-combat.lua") diff --git a/game/modules/tome/data/talents/spells/staff-combat.lua b/game/modules/tome/data/talents/spells/staff-combat.lua new file mode 100644 index 0000000000000000000000000000000000000000..d0a9d1e230ee06d33c2271ef11613d4b301ab4a6 --- /dev/null +++ b/game/modules/tome/data/talents/spells/staff-combat.lua @@ -0,0 +1,122 @@ +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 Nicolas Casalini +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +-- Nicolas Casalini "DarkGod" +-- darkgod@te4.org + +newTalent{ + name = "Channel Staff", + type = {"spell/staff-combat", 1}, + require = spells_req1, + points = 5, + mana = 5, + tactical = { + ATTACK = 10, + }, + range = 10, + reflectable = true, + action = function(self, t) + local weapon = self:hasTwoStaffWeapon() + if not weapon then + game.logPlayer(self, "You need a staff to use this spell.") + return + end + + local tg = {type="bolt", range=self:getTalentRange(t), talent=t} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + local dam = self:combatDamage(weapon) * self:combatTalentWeaponDamage(t, 0.4, 1.1) + self:project(tg, x, y, weapon.combat.damtype or DamageType.ARCANE, self:spellCrit(dam), {type="manathrust"}) + game:playSoundNear(self, "talents/arcane") + return true + end, + info = function(self, t) + return ([[Channel raw mana through your staff, projecting a bolt your staff damage type doing %d%% staff damage. + This attack always hits and ignores target armour.]]): + format(self:combatTalentWeaponDamage(t, 0.4, 1.1) * 100) + end, +} + +newTalent{ + name = "Staff Mastery", + type = {"spell/staff-combat", 2}, + mode = "passive", + require = spells_req2, + points = 5, + info = function(self, t) + return ([[Increases damage done with staves by %d%%.]]):format(100 * (math.sqrt(self:getTalentLevel(t) / 10))) + end, +} + +newTalent{ + name = "Defensive Posture", + type = {"spell/staff-combat", 3}, + require = spells_req3, + mode = "sustained", + points = 5, + sustain_mana = 80, + cooldown = 30, + tactical = { + DEFEND = 20, + }, + activate = function(self, t) + local power = self:combatTalentSpellDamage(t, 10, 20) + game:playSoundNear(self, "talents/arcane") + return { + power = self:addTemporaryValue("combat_def", power), + } + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("combat_def", p.power) + return true + end, + info = function(self, t) + return ([[Adopt a defensive posture, reducing your staff attack power by %d and increasing your defense by %d. + The mana restored will increase with the Magic stat]]):format(self:combatTalentSpellDamage(t, 20, 230)) + end, +} + +newTalent{ + name = "Disruption Shield", + type = {"spell/staff-combat",4}, + require = spells_req4, no_sustain_autoreset = true, + points = 5, + mode = "sustained", + sustain_mana = 150, + tactical = { + DEFEND = 10, + }, + activate = function(self, t) + local power = math.max(0.8, 3 - (self:combatSpellpower(1) * self:getTalentLevel(t)) / 280) + self.disruption_shield_absorb = 0 + game:playSoundNear(self, "talents/arcane") + return { + shield = self:addTemporaryValue("disruption_shield", power), + particle = self:addParticles(Particles.new("disruption_shield", 1)), + } + end, + deactivate = function(self, t, p) + self:removeParticles(p.particle) + self:removeTemporaryValue("disruption_shield", p.shield) + self.disruption_shield_absorb = nil + return true + end, + info = function(self, t) + return ([[Uses mana instead of life to take damage. Uses %0.2f mana per damage point taken. + If your mana is brought too low by the shield, it will de-activate and the chain reaction will release a deadly arcane explosion of the amount of damage absorbed. + The damage to mana ratio increases with the Magic stat]]):format(math.max(0.8, 3 - (self:combatSpellpower(1) * self:getTalentLevel(t)) / 280)) + end, +} diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua index 25a712026254eeebfac8145a5d524a1717f00bae..741260449bca9a0b686b75072df07c84846c5e02 100644 --- a/game/modules/tome/data/timed_effects.lua +++ b/game/modules/tome/data/timed_effects.lua @@ -652,6 +652,22 @@ newEffect{ end, } +newEffect{ + name = "MIGHTY_BLOWS", + desc = "Migth Blows", + type = "physical", + status = "beneficial", + parameters = { power=10 }, + on_gain = function(self, err) return "#Target# looks menacing." end, + on_lose = function(self, err) return "#Target# looks less menacing." end, + activate = function(self, eff) + eff.tmpid = self:addTemporaryValue("combat_dam", eff.power) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("combat_dam", eff.tmpid) + end, +} + newEffect{ name = "ROTTING_DISEASE", desc = "Rotting Disease", diff --git a/game/modules/tome/data/zones/mount-doom/npcs.lua b/game/modules/tome/data/zones/mount-doom/npcs.lua index 1a66b03837848af8d3da89b0e2d5ce98d4a996f9..ac54e8fd32d9f7de2965a5049469547757f81ade 100644 --- a/game/modules/tome/data/zones/mount-doom/npcs.lua +++ b/game/modules/tome/data/zones/mount-doom/npcs.lua @@ -265,7 +265,7 @@ newEntity{ base = "BASE_NPC_FAEROS", define_as = "FYRK", This one looks even nastier and looks toward you with what seems to be disdain. Flames swirly all around him.]], level_range = {35, nil}, exp_worth = 2, rank = 5, - max_life = resolvers.rngavg(300,400), life_rating = 20, fixed_rating = true, + max_life = resolvers.rngavg(800,900), life_rating = 20, fixed_rating = true, combat_armor = 0, combat_def = 20, on_melee_hit = { [DamageType.FIRE] = resolvers.mbonus(30, 10), }, move_others=true, diff --git a/game/modules/tome/dialogs/CharacterSheet.lua b/game/modules/tome/dialogs/CharacterSheet.lua index 3368e24629990302142a7342143d5cc0cd69c940..36e05b8087bd312b80f22708131f55a3cf5fc88d 100644 --- a/game/modules/tome/dialogs/CharacterSheet.lua +++ b/game/modules/tome/dialogs/CharacterSheet.lua @@ -388,7 +388,7 @@ function _M:dump() if not self.filter or self.filter(o) then local char = string.char(string.byte('a') + index) nl(("%s) %s"):format(char, o:getName{force_id=true})) - nl((" %s"):format(o:getTextualDesc())) + nl((" %s"):format(table.concat(o:getTextualDesc(), "\n "))) index = index + 1 end end @@ -402,7 +402,7 @@ function _M:dump() if not self.filter or self.filter(o) then local char = string.char(string.byte('a') + item - 1) nl(("%s) %s"):format(char, o:getName{force_id=true})) - nl((" %s"):format(o:getTextualDesc())) + nl((" %s"):format(table.concat(o:getTextualDesc(), "\n "))) end end diff --git a/ideas/crafting.ods b/ideas/crafting.ods index 33659f50e225d4236e7a70c07f18d7171d639d60..25d04e752174cd90e1c0756ec4c1615e940813bb 100644 Binary files a/ideas/crafting.ods and b/ideas/crafting.ods differ