diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..cc56d0dbc9df8b9a504909be266122141f833256 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +cef3spawn +build/*.make +libte4-web.so +Makefile +bin +divx2pass.log +game/addons/** +game/dlcs/** +obj/** +out.avi +out.ogv +te4_log_web.txt +t-engine diff --git a/game/modules/tome/data/font/INSULA__.ttf b/game/engines/default/data/font/INSULA__.ttf similarity index 100% rename from game/modules/tome/data/font/INSULA__.ttf rename to game/engines/default/data/font/INSULA__.ttf diff --git a/game/modules/tome/data/font/SVBasicManual-Bold.ttf b/game/engines/default/data/font/SVBasicManual-Bold.ttf similarity index 100% rename from game/modules/tome/data/font/SVBasicManual-Bold.ttf rename to game/engines/default/data/font/SVBasicManual-Bold.ttf diff --git a/game/modules/tome/data/font/SVBasicManual.ttf b/game/engines/default/data/font/SVBasicManual.ttf similarity index 100% rename from game/modules/tome/data/font/SVBasicManual.ttf rename to game/engines/default/data/font/SVBasicManual.ttf diff --git a/game/engines/default/data/font/Salsa-Mono.ttf b/game/engines/default/data/font/Salsa-Mono.ttf new file mode 100644 index 0000000000000000000000000000000000000000..43fc00ffa9430992d01b6fe3119806621fd474e1 Binary files /dev/null and b/game/engines/default/data/font/Salsa-Mono.ttf differ diff --git a/game/engines/default/data/font/Salsa-Regular.ttf b/game/engines/default/data/font/Salsa-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4da138786fbffadd9a3f64f4b1f6aef6545c6db2 Binary files /dev/null and b/game/engines/default/data/font/Salsa-Regular.ttf differ diff --git a/game/modules/tome/data/font/USENETA.ttf b/game/engines/default/data/font/USENETA.ttf similarity index 100% rename from game/modules/tome/data/font/USENETA.ttf rename to game/engines/default/data/font/USENETA.ttf diff --git a/game/modules/tome/data/font/USENET_.ttf b/game/engines/default/data/font/USENET_.ttf similarity index 100% rename from game/modules/tome/data/font/USENET_.ttf rename to game/engines/default/data/font/USENET_.ttf diff --git a/game/engines/default/data/font/packages/default.lua b/game/engines/default/data/font/packages/default.lua new file mode 100644 index 0000000000000000000000000000000000000000..afae7c4de113d3503e8537498e4b6c0d0687068a --- /dev/null +++ b/game/engines/default/data/font/packages/default.lua @@ -0,0 +1,54 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +newPackage{ id = "basic", name = "Basic", weight = 1, + small = {font="/data/font/Vera.ttf", normal=12, small=10, big=14}, + default = {font="/data/font/Vera.ttf", normal=12, small=10, big=14}, + bold = {font="/data/font/VeraBd.ttf", normal=12, small=10, big=14}, + mono = {font="/data/font/VeraMono.ttf", normal=12, small=10, big=14}, + flyer = {font="/data/font/INSULA__.ttf", normal=14, small=12, big=16}, + bignews = {font="/data/font/VeraMono.ttf", normal=30}, + resources_normal = {font="/data/font/Vera.ttf", bold=true, normal=12}, + resources_small = {font="/data/font/Vera.ttf", bold=true, normal=10}, +} + +newPackage{ id = "web", name = "Web", weight = 10, + small = {font="/data/font/DroidSans.ttf", normal=12, small=10, big=14}, + default = {font="/data/font/DroidSans.ttf", normal=16, small=12, big=18}, + bold = {font="/data/font/DroidSans.ttf", bold=true, normal=16, small=12, big=18}, + zone = {font="/data/font/DroidSans.ttf", bold=true, normal=16, small=12, big=18}, + mono = {font="/data/font/DroidSansMono.ttf", normal=16, small=12, big=18}, + mono_small = {font="/data/font/DroidSansMono.ttf", normal=14, small=10, big=16}, + flyer = {font="/data/font/INSULA__.ttf", normal=14, small=12, big=16}, + bignews = {font="/data/font/DroidSansMono.ttf", normal=30}, + resources_normal = {font="/data/font/DroidSans.ttf", bold=true, normal=12}, + resources_small = {font="/data/font/DroidSans.ttf", bold=true, normal=10}, +} + +newPackage{ id = "fantasy", name = "Fantasy", weight = 100, + small = {font="/data/font/Salsa-Regular.ttf", normal=12, small=10, big=14}, + default = {font="/data/font/Salsa-Regular.ttf", normal=16, small=12, big=18}, + bold = {font="/data/font/Salsa-Regular.ttf", normal=16, small=12, big=18}, + mono = {font="/data/font/Salsa-Mono.ttf", normal=16, small=12, big=18}, + mono_small = {font="/data/font/Salsa-Mono.ttf", normal=14, small=10, big=16}, + flyer = {font="/data/font/Salsa-Regular.ttf", normal=14, small=12, big=16}, + bignews = {font="/data/font/Salsa-Regular.ttf", normal=30}, + resources_normal = {font="/data/font/Salsa-Regular.ttf", normal=14}, + resources_small = {font="/data/font/Salsa-Regular.ttf", normal=12}, +} diff --git a/game/engines/default/engine/Actor.lua b/game/engines/default/engine/Actor.lua index 9faf00221017af8d5f2fb3319830b9057faea3d7..bc7a13be33c3a8dcf1d3869fe7f557e12b7c8735 100644 --- a/game/engines/default/engine/Actor.lua +++ b/game/engines/default/engine/Actor.lua @@ -115,8 +115,8 @@ function _M:defineDisplayCallback() e:checkDisplay() if e.ps:isAlive() then if game.level and game.level.map then e:shift(game.level.map, self._mo) end - e.ps:toScreen(x + w / 2, y + dy + h / 2, true, w / (game.level and game.level.map.tile_w or w)) - else self:removeParticles(e) + e.ps:toScreen(x + w / 2 + (e.dx or 0) * w, y + h / 2 + (e.dy or 0) * h, true, w / game.level.map.tile_w) + elseif weak[1] then weak[1]:removeParticles(e) end end end @@ -198,7 +198,7 @@ function _M:move(x, y, force) if not force and map:checkAllEntities(x, y, "block_move", self, true) then return true end if self.x and self.y then - map:remove(self.x, self.y, Map.ACTOR) + map:remove(self.x, self.y, Map.ACTOR, self) else -- print("[MOVE] actor moved without a starting position", self.name, x, y) end @@ -397,7 +397,8 @@ end function _M:deleteFromMap(map) if self.x and self.y and map then - map:remove(self.x, self.y, engine.Map.ACTOR) + map:remove(self.x, self.y, engine.Map.ACTOR, self) + -- self.x, self.y = nil, nil self:closeParticles() end end @@ -515,3 +516,8 @@ end function _M:getEntityKind() return "actor" end + +function _M:he_she() return string.he_she(self) end +function _M:his_her() return string.his_her(self) end +function _M:him_her() return string.him_her(self) end +function _M:his_her_self() return string.his_her_self(self) end diff --git a/game/engines/default/engine/Entity.lua b/game/engines/default/engine/Entity.lua index ec036fc7fdc8892d3b93d116466d03aebb3a5a10..bfad71f40dff3b007fb97a026602a0d4fa38a352 100644 --- a/game/engines/default/engine/Entity.lua +++ b/game/engines/default/engine/Entity.lua @@ -43,7 +43,7 @@ end local function copy_recurs(dst, src, deep) for k, e in pairs(src) do - if type(e) == "table" and e.__CLASSNAME then + if type(e) == "table" and e.__ATOMIC then dst[k] = e elseif dst[k] == nil then if deep then @@ -52,7 +52,7 @@ local function copy_recurs(dst, src, deep) else dst[k] = e end - elseif type(dst[k]) == "table" and type(e) == "table" and not e.__CLASSNAME then + elseif type(dst[k]) == "table" and type(e) == "table" and not e.__ATOMIC then copy_recurs(dst[k], e, deep) end end @@ -64,6 +64,7 @@ end -- @usage Entity.new{display='#', color_r=255, color_g=255, color_b=255} function _M:init(t, no_default) t = t or {} + self.uid = next_uid __uids[self.uid] = self next_uid = next_uid + 1 @@ -71,7 +72,7 @@ function _M:init(t, no_default) for k, e in pairs(t) do if k ~= "__CLASSNAME" and k ~= "uid" then local ee = e - if type(e) == "table" and not e.__CLASSNAME then ee = table.clone(e, true) end + if type(e) == "table" and not e.__ATOMIC then ee = table.clone(e, true) end self[k] = ee end end @@ -120,6 +121,21 @@ function _M:init(t, no_default) self:addParticles(Particles.new(pd.name, pd.rad or 1, pd.args)) end end + + if config.settings.cheat then + local ok, err = table.check( + self, + function(t, where, v, tv) + if tv ~= "function" then return true end + local n, v = debug.getupvalue(v, 1) + if not n then return true end + return nil, ("%s has upvalue %s"):format(tostring(where), tostring(n)) + end, + function(value) return not value._allow_upvalues end) + if not ok then + error("Entity definition has a closure: "..err) + end + end end --- If we are cloned we need a new uid @@ -248,7 +264,7 @@ function _M:defineDisplayCallback() e:checkDisplay() if e.ps:isAlive() then if game.level and game.level.map then e:shift(game.level.map, self._mo) end - e.ps:toScreen(x + w / 2, y + h / 2, true, w / (game.level and game.level.map.tile_w or w)) + e.ps:toScreen(x + w / 2 + (e.dx or 0) * w, y + h / 2 + (e.dy or 0) * h, true, w / game.level.map.tile_w) else self:removeParticles(e) end end @@ -571,7 +587,7 @@ function _M:resolve(t, last, on_entity, key_chain) for k, e in pairs(t) do if type(e) == "table" and e.__resolver and (not e.__resolve_last or last) then list[k] = e - elseif type(e) == "table" and not e.__CLASSNAME then + elseif type(e) == "table" and not e.__ATOMIC then list[k] = e end end @@ -580,7 +596,7 @@ function _M:resolve(t, last, on_entity, key_chain) for k, e in pairs(list) do if type(e) == "table" and e.__resolver and (not e.__resolve_last or last) then t[k] = resolvers.calc[e.__resolver](e, on_entity or self, self, t, k, key_chain) - elseif type(e) == "table" and not e.__CLASSNAME then + elseif type(e) == "table" and not e.__ATOMIC then local key_chain = table.clone(key_chain) key_chain[#key_chain+1] = k self:resolve(e, last, on_entity, key_chain) @@ -879,6 +895,26 @@ end function _M:onTemporaryValueChange(prop, v, base) end +--- Gets the change in an attribute/function based on changing another. +-- Measures the difference in result_attr from adding temporary values from and to to changed_attr. +-- @param changed_attr the attribute being changed +-- @param from the temp value added to changed_attr which we are starting from +-- @param to the temp value added to changed_attr which we are ending on +-- @param result_attr the result we are measuring the difference in +-- @param ... arguments to pass to result_attr if it is a function +-- @return the difference, the from result, the to result +function _M:getAttrChange(changed_attr, from, to, result_attr, ...) + local temp = self:addTemporaryValue(changed_attr, from) + local from_result = util.getval(self[result_attr], self, ...) + self:removeTemporaryValue(changed_attr, temp) + + temp = self:addTemporaryValue(changed_attr, to) + local to_result = util.getval(self[result_attr], self, ...) + self:removeTemporaryValue(changed_attr, temp) + + return to_result - from_result, from_result, to_result +end + --- Increases/decreases an attribute -- The attributes are just actor properties, but this ensures they are numbers and not booleans -- thus making them compatible with temporary values system @@ -966,18 +1002,6 @@ function _M:loadList(file, no_default, res, mod, loaded) local e = newenv.class.new(t, no_default) if type(mod) == "function" then mod(e) end - if config.settings.cheat then - local ok, err = table.check(e, function(t, where, v, tv) - if tv ~= "function" then return true end - local n, v = debug.getupvalue(v, 1) - if not n then return true end - return nil, ("Entity closure checker: %s has upvalue %s"):format(tostring(where), tostring(n)) - end) - if not ok then - error("Entity definition has a closure: "..err) - end - end - res[#res+1] = e if t.define_as then res[t.define_as] = e end end, @@ -1013,4 +1037,3 @@ end function _M:checkClassification(type_str) return false end - diff --git a/game/engines/default/engine/FontPackage.lua b/game/engines/default/engine/FontPackage.lua new file mode 100644 index 0000000000000000000000000000000000000000..252fbf49e4a7e2eb5b01fd983f3eb8ebb843e886 --- /dev/null +++ b/game/engines/default/engine/FontPackage.lua @@ -0,0 +1,84 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 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 + +require "engine.class" + +--- Configure sets of fonts +module(..., package.seeall, class.make) + +local packages = {} + +--- Loads lore +function _M:loadDefinition(file, env) + local f, err = loadfile(file) + if not f and err then error(err) end + setfenv(f, setmetatable(env or { + newPackage = function(t) self.new(t) end, + load = function(f) self:loadDefinition(f, getfenv(2)) end + }, {__index=_G})) + f() +end + +local cur_size = "normal" +function _M:setDefaultSize(size) + cur_size = size +end + +local cur_id = "default" +function _M:setDefaultId(id) + if not packages[id] then id = "basic" end + cur_id = id +end + +function _M:resolveFont(name, orname) + local font = packages[cur_id] + local size = cur_size + if not font[name] then name = orname end + if not font[name] then name = "default" end + if not font[name][size] then size = "normal" end + return font[name], size +end + +function _M:getFont(name, orname) + local font, size = self:resolveFont(name, orname) + return font.font, font[size] +end + +function _M:get(name, force) + local font, size = self:resolveFont(name, orname) + local f = core.display.newFont(font.font, font[size], font.bold or force) + if font.bold then f:setStyle("bold") end + return f +end + +function _M:list() + local list = {} + for _, f in pairs(packages) do list[#list+1] = f end + table.sort(list, function(a, b) return a.weight > b.weight end) + return list +end + +function _M:init(t) + assert(t.id, "no font package id") + assert(t.default, "no font package default") + + for k, e in pairs(t) do self[k] = e end + + packages[t.id] = self +end diff --git a/game/engines/default/engine/Game.lua b/game/engines/default/engine/Game.lua index f46d7d018430a2f19fab2c88ede82c5f03f18db2..b3b3dd1336189355c2cf3a343969017aa9f44d29 100644 --- a/game/engines/default/engine/Game.lua +++ b/game/engines/default/engine/Game.lua @@ -165,11 +165,7 @@ function _M:display(nb_keyframes) end -- Check profile thread events - local evt = profile:popEvent() - while evt do - self:handleProfileEvent(evt) - evt = profile:popEvent() - end + self:handleEvents() -- Check timers if self._timers_cb and nb_keyframes > 0 then @@ -202,6 +198,16 @@ function _M:idling(focus) -- print("Game got focus/unfocus", focus) end + +--- Handle pending events +function _M:handleEvents() + local evt = profile:popEvent() + while evt do + self:handleProfileEvent(evt) + evt = profile:popEvent() + end +end + --- Receives a profile event -- Usualy this just transfers it to the PlayerProfile class but you can overload it to handle special stuff function _M:handleProfileEvent(evt) @@ -381,6 +387,11 @@ function _M:unregisterDialog(d) if last.on_recover_focus then last:on_recover_focus() end end +--- Do we have a specific dialog +function _M:hasDialog(d) + return self.dialogs[d] and true or false +end + --- Do we have a dialog running function _M:hasDialogUp(nb) nb = nb or 0 diff --git a/game/engines/default/engine/Map.lua b/game/engines/default/engine/Map.lua index 8351c19dc3bc235835b1dda3b9c452289d007104..ef9a5c1ba9fc4ddbaef689ab9dcf48cb1a367878 100644 --- a/game/engines/default/engine/Map.lua +++ b/game/engines/default/engine/Map.lua @@ -545,9 +545,11 @@ end -- @param x position -- @param y position -- @param pos what kind of entity to set(Map.TERRAIN, Map.OBJECT, Map.ACTOR) -function _M:remove(x, y, pos) +-- @param only only remove if the value was equal to that entity +function _M:remove(x, y, pos, only) if self.map[x + y * self.w] then local e = self.map[x + y * self.w][pos] + if only and only ~= e then return end self.map[x + y * self.w][pos]= nil self:updateMap(x, y) self.changed = true @@ -1040,13 +1042,13 @@ function _M:addEffect(src, x, y, duration, damtype, dam, radius, dir, angle, ove local e = { src=src, x=x, y=y, duration=duration, damtype=damtype, dam=dam, radius=radius, dir=dir, angle=angle, - overlay=overlay and overlay.__CLASSNAME and overlay, + overlay=overlay and overlay.__ATOMIC and overlay, grids = grids, update_fct=update_fct, selffire=selffire, friendlyfire=friendlyfire, } local overlay_particle = nil - if overlay and not overlay.__CLASSNAME then + if overlay and not overlay.__ATOMIC then overlay_particle = overlay elseif overlay then if overlay.overlay_particle then overlay_particle = overlay.overlay_particle end @@ -1343,16 +1345,16 @@ function _M:displayParticles(z, nb_keyframes) end end --- Returns the compass direction from a vector +-- Returns the compass direction from a vector -- dx, dy = x change (+ is east), y change (+ is south) function _M:compassDirection(dx, dy) local dir = "" - if dx == 0 and dy == 0 then + if dx == 0 and dy == 0 then return nil else local dydx, dxdy = dy/math.abs(dx), dx/math.abs(dy) if dydx <= -0.5 then dir = "north" elseif dydx >= 0.5 then dir="south" end - if dxdy < -0.5 then dir = dir.."west" + if dxdy < -0.5 then dir = dir.."west" elseif dxdy > 0.5 then dir = dir.."east" end end return dir diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua index 5c19bf02e69963e098e01909404f8b31b1cfecbc..5e5f3f4a5c430e3ab39c30964ee36deb126809d3 100644 --- a/game/engines/default/engine/Module.lua +++ b/game/engines/default/engine/Module.lua @@ -21,6 +21,7 @@ require "engine.class" local Savefile = require "engine.Savefile" local UIBase = require "engine.ui.Base" +local FontPackage = require "engine.FontPackage" require "engine.PlayerProfile" --- Handles dialog windows @@ -55,7 +56,6 @@ function _M:listModules(incompatible, moddir_filter) end table.sort(ms, function(a, b) - print(a.short_name,b.short_name) if a.short_name == "tome" then return 1 elseif b.short_name == "tome" then return nil else return a.name < b.name @@ -286,10 +286,91 @@ function _M:listVaultSavesForCurrent() return lss end +--- List all available background alterations +function _M:listBackgrounds(mod) + local defs = {} + local load = function(dir, teaa) + local add_def = loadfile(dir.."/boot-screen/init.lua") + if add_def then + local add = {} + setfenv(add_def, add) + add_def() + table.print(add) + + add.for_modules = table.reverse(add.for_modules) + if add.for_modules[mod.short_name] then + if add.add_backgrounds then + for i, d in ipairs(add.add_backgrounds) do + local nd = {chance=d.chance, name=dir.."/boot-screen/"..d.name} + if d.logo then nd.logo = dir.."/boot-screen/"..d.logo end + if teaa then + nd.mount = function() fs.mount(fs.getRealPath(teaa), "/testload", false) end + nd.umount = function() fs.umount(fs.getRealPath(teaa)) end + end + defs[#defs+1] = nd + end + end + if add.replace_backgrounds then + defs = {} + for i, d in ipairs(add.replace_backgrounds) do + local nd = {chance=d.chance, name=dir.."/boot-screen/"..d.name} + if d.logo then nd.logo = dir.."/boot-screen/"..d.logo end + if teaa then + nd.mount = function() fs.mount(fs.getRealPath(teaa), "/testload", false) end + nd.umount = function() fs.umount(fs.getRealPath(teaa)) end + end + defs[#defs+1] = nd + end + end + end + end + end + + local parse = function(basedir) + for i, short_name in ipairs(fs.list(basedir)) do if short_name:find("^.+%-.+") or short_name:find(".teaac$") then + local dir = basedir..short_name + -- print("Checking background", short_name, ":: (as dir)", fs.exists(dir.."/init.lua"), ":: (as teaa)", short_name:find(".teaa$"), "") + if fs.exists(dir.."/boot-screen/init.lua") then + load(dir, nil) + elseif short_name:find(".teaa$") or short_name:find(".teaac$") then + fs.mount(fs.getRealPath(dir), "/testload", false) + local mod + if fs.exists("/testload/boot-screen/init.lua") then + load("/testload", dir) + end + fs.umount(fs.getRealPath(dir)) + end + end end + end + + -- Add the default one + local backname = util.getval(mod.background_name) or "tome" + defs[#defs+1] = {name="/data/gfx/background/"..backname..".png", logo="/data/gfx/background/"..backname.."-logo.png", chance=100} + + -- Look for more + parse("/addons/") + parse("/dlcs/") + + -- os.exit() + + local def = nil + while not def or not rng.percent(def.chance or 100) do + def = rng.table(defs) + end + + if def.mount then def.mount() end + local bkgs = core.display.loadImage(def.name) or core.display.loadImage("/data/gfx/background/tome.png") + local logo = nil + if def.logo then logo = {(core.display.loadImage(def.logo) or core.display.loadImage("/data/gfx/background/tome-logo.png")):glTexture()} end + if def.umount then def.umount() end + + return bkgs, logo +end + --- List all available addons function _M:listAddons(mod, ignore_compat) local adds = {} - local load = function(dir, teaa) + local load = function(dir, teaa, teaac) local add_def = loadfile(dir.."/init.lua") if add_def then local add = {} @@ -299,6 +380,7 @@ function _M:listAddons(mod, ignore_compat) if (ignore_compat or engine.version_nearly_same(mod.version, add.version)) and add.for_module == mod.short_name then add.dir = dir add.teaa = teaa + add.teaac = teaac add.natural_compatible = engine.version_nearly_same(mod.version, add.version) add.version_txt = ("%d.%d.%d"):format(add.version[1], add.version[2], add.version[3]) if add.dlc and not profile:isDonator(add.dlc) then add.dlc = "no" end @@ -309,7 +391,7 @@ function _M:listAddons(mod, ignore_compat) end local parse = function(basedir) - for i, short_name in ipairs(fs.list(basedir)) do if short_name:find("^"..mod.short_name.."%-") then + for i, short_name in ipairs(fs.list(basedir)) do if short_name:find("^"..mod.short_name.."%-") or short_name:find(".teaac$") then local dir = basedir..short_name print("Checking addon", short_name, ":: (as dir)", fs.exists(dir.."/init.lua"), ":: (as teaa)", short_name:find(".teaa$"), "") if fs.exists(dir.."/init.lua") then @@ -321,6 +403,16 @@ function _M:listAddons(mod, ignore_compat) load("/testload", dir) end fs.umount(fs.getRealPath(dir)) + elseif short_name:find(".teaac$") then + fs.mount(fs.getRealPath(dir), "/testload", false) + for sdir in fs.iterate("/testload", function(p) return p:find("%-") end) do + print(" * Addon collection subaddon", sdir) + local mod + if fs.exists("/testload/"..sdir.."/init.lua") then + load("/testload/"..sdir, dir, sdir) + end + end + fs.umount(fs.getRealPath(dir)) end end end end @@ -454,13 +546,15 @@ You may try to force loading if you are sure the savefile does not use that addo if add.data then print(" * with data") - if add.teaa then fs.mount("subdir:/data/|"..fs.getRealPath(add.teaa), "/data-"..add.short_name, true) + if add.teaac then fs.mount("subdir:/"..add.teaac.."/data/|"..fs.getRealPath(add.teaa), "/data-"..add.short_name, true) + elseif add.teaa then fs.mount("subdir:/data/|"..fs.getRealPath(add.teaa), "/data-"..add.short_name, true) else fs.mount(base.."/data", "/data-"..add.short_name, true) end end if add.superload then print(" * with superload") - if add.teaa then fs.mount("subdir:/superload/|"..fs.getRealPath(add.teaa), "/mod/addons/"..add.short_name.."/superload", true) + if add.teaac then fs.mount("subdir:/"..add.teaac.."/superload/|"..fs.getRealPath(add.teaa), "/mod/addons/"..add.short_name.."/superload", true) + elseif add.teaa then fs.mount("subdir:/superload/|"..fs.getRealPath(add.teaa), "/mod/addons/"..add.short_name.."/superload", true) else fs.mount(base.."/superload", "/mod/addons/"..add.short_name.."/superload", true) end @@ -468,12 +562,14 @@ You may try to force loading if you are sure the savefile does not use that addo end if add.overload then print(" * with overload") - if add.teaa then fs.mount("subdir:/overload/|"..fs.getRealPath(add.teaa), "/", false) + if add.teaac then fs.mount("subdir:/"..add.teaac.."/overload/|"..fs.getRealPath(add.teaa), "/", false) + elseif add.teaa then fs.mount("subdir:/overload/|"..fs.getRealPath(add.teaa), "/", false) else fs.mount(base.."/overload", "/", false) end end if add.hooks then - if add.teaa then fs.mount("subdir:/hooks/|"..fs.getRealPath(add.teaa), "/hooks/"..add.short_name, true) + if add.teaac then fs.mount("subdir:/"..add.teaac.."/hooks/|"..fs.getRealPath(add.teaa), "/hooks/"..add.short_name, true) + elseif add.teaa then fs.mount("subdir:/hooks/|"..fs.getRealPath(add.teaa), "/hooks/"..add.short_name, true) else fs.mount(base.."/hooks", "/hooks/"..add.short_name, true) end @@ -511,6 +607,7 @@ end -- Grab some fun facts! function _M:selectFunFact(ffdata) local l = {} + local ok = false print("Computing fun facts") print(pcall(function() @@ -524,9 +621,10 @@ function _M:selectFunFact(ffdata) if ffdata.total_deaths then l[#l+1] = ("The character's vault has registered a total of #RED#%d#WHITE# character's deaths"):format(ffdata.total_deaths) end if ffdata.wins_this_version then l[#l+1] = ("The character's vault has registered a total of #LIGHT_BLUE#%d#WHITE# winners for the current version"):format(ffdata.wins_this_version) end if ffdata.latest_donator then l[#l+1] = ("The latest donator is #LIGHT_GREEN#%s#WHITE#. Many thanks to all donators, you are keeping this game alive!"):format(ffdata.latest_donator) end - + ok = true end)) - table.print(l) + if not ok then return false end + -- table.print(l) return #l > 0 and rng.table(l) or false end @@ -538,14 +636,13 @@ function _M:loadScreen(mod) local has_max = mod.loading_wait_ticks if has_max then core.wait.addMaxTicks(has_max) end local i, max, dir = has_max or 20, has_max or 20, -1 - local backname = util.getval(mod.background_name) or "tome" + local bkgs, logo = self:listBackgrounds(mod) - local bkgs = core.display.loadImage("/data/gfx/background/"..backname..".png") or core.display.loadImage("/data/gfx/background/tome.png") local sw, sh = core.display.size() local bw, bh = bkgs:getSize() + local obw, obh = bkgs:getSize() local bkg = {bkgs:glTexture()} - local logo = {(core.display.loadImage("/data/gfx/background/"..backname.."-logo.png") or core.display.loadImage("/data/gfx/background/tome-logo.png")):glTexture()} local pubimg, publisher = nil, nil if mod.publisher_logo then pubimg, publisher = core.display.loadImage("/data/gfx/background/"..mod.publisher_logo..".png"), nil @@ -557,8 +654,8 @@ function _M:loadScreen(mod) local middle = {core.display.loadImage("/data/gfx/metal-ui/waiter/middle.png"):glTexture()} local bar = {core.display.loadImage("/data/gfx/metal-ui/waiter/bar.png"):glTexture()} - local font = core.display.newFont("/data/font/DroidSans.ttf", 12) - local bfont = core.display.newFont("/data/font/DroidSans.ttf", 16) + local font = FontPackage:get("small") + local bfont = FontPackage:get("default") local dw, dh = math.floor(sw / 2), left[7] local dx, dy = math.floor((sw - dw) / 2), sh - dh @@ -635,19 +732,24 @@ function _M:loadScreen(mod) return function() -- Background local x, y = 0, 0 + bw, bh = sw, sh if bw > bh then - bh = sw * bh / bw - bw = sw + bh = bw * obh / obw y = (sh - bh) / 2 + if bh < sh then + bh = sh + bw = bh * obw / obh + x = (sw - bw) / 2 + y = 0 + end else - bw = sh * bw / bh - bh = sh + bw = bh * obw / obh x = (sw - bw) / 2 end - bkg[1]:toScreenFull(x, y, bw, bh, bw * bkg[4], bh * bkg[5]) + bkg[1]:toScreenFull(x, y, bw, bh, bw * bkg[2] / obw, bh * bkg[3] / obh) -- Logo - logo[1]:toScreenFull(0, 0, logo[6], logo[7], logo[2], logo[3]) + if logo then logo[1]:toScreenFull(0, 0, logo[6], logo[7], logo[2], logo[3]) end -- Publisher Logo if publisher then publisher[1]:toScreenFull(sw - publisher[6], 0, publisher[6], publisher[7], publisher[2], publisher[3]) end @@ -727,6 +829,12 @@ function _M:instanciate(mod, name, new_game, no_reboot, extra_module_info) fs.mount(engine.homepath, "/") mod.load("setup") + -- Load font packages + FontPackage:loadDefinition("/data/font/packages/default.lua") + if mod.font_packages_definitions then FontPackage:loadDefinition(mod.font_packages_definitions) end + FontPackage:setDefaultId(util.getval(mod.font_package_id)) + FontPackage:setDefaultSize(util.getval(mod.font_package_size)) + -- Check the savefile if possible, to add to the progress bar size local savesize = 0 local save = Savefile.new("") diff --git a/game/engines/default/engine/Object.lua b/game/engines/default/engine/Object.lua index 4dff64cf1ff13ed860e0f90a8919d18a3b83a6b9..512885746426fe109e83b31c947ed7da6ab1f2a3 100644 --- a/game/engines/default/engine/Object.lua +++ b/game/engines/default/engine/Object.lua @@ -129,35 +129,53 @@ function _M:canStack(o) return false end ---- Adds object to the stack --- @return true if stacking worked, false if not -function _M:stack(o, force) - if not force and not self:canStack(o) then return false end +--- Adds an object to the stack +-- @param o = object to stack onto self +-- @param force boolean to stack unstackable objects +-- @param num = maximum number of stacked objects to move +-- @return true if stacking worked or false if not, and boolean if all of the stack was moved +function _M:stack(o, force, num) + local last = true + num = num or math.huge + if (not force and not self:canStack(o)) or num < 1 then return false end self.stacked = self.stacked or {} - self.stacked[#self.stacked+1] = o - - -- Merge stacks - if o.stacked then - for i = 1, #o.stacked do - self.stacked[#self.stacked+1] = o.stacked[i] - end - o.stacked = nil + for i = 1, math.min(o:getNumber(), num) do + self.stacked[#self.stacked+1], last = o:unstack() end - return true + return true, last end ---- Removes an object of the stack --- @return object, true if the last, or object, false if more -function _M:unstack() +--- Removes one or more objects from a stack of objects +-- @param num = maximum number to remove +-- @return self or new object, true if last item was removed (self is not deleted) or false if more on the stack +function _M:unstack(num) if not self:stackable() or not self.stacked or #self.stacked == 0 then return self, true end - local o = table.remove(self.stacked) + num = math.min(num or 1, #self.stacked + 1) + if num < 1 then return self.stacked[1], false end -- next item to remove + local o + local last, uo = false + repeat + num = num - 1 + uo = table.remove(self.stacked) + if not o then + o = uo; o.stacked = {} + else + if uo then + o.stacked[#o.stacked+1] = uo + else + o.stacked[#o.stacked+1] = self; last = true; break + end + end + until num <= 0 or last if #self.stacked == 0 then self.stacked = nil end - return o, false + if #o.stacked == 0 then o.stacked = nil end + return o, last end --- Applies a function to all items of the stack +-- stops after fct(so, i) returns true function _M:forAllStack(fct) - fct(self) + if fct(self) then return end if not self.stacked then return end for i, so in ipairs(self.stacked) do if fct(so, i) then break end diff --git a/game/engines/default/engine/Particles.lua b/game/engines/default/engine/Particles.lua index 133fcd1976c4b2f4d364effacb3104197f89d552..e555eddf32cc35f91386c6707ac0e5deb3bd459b 100644 --- a/game/engines/default/engine/Particles.lua +++ b/game/engines/default/engine/Particles.lua @@ -132,7 +132,7 @@ end function _M:shift(map, mo) if not self.can_shift then return end - local adx, ady = mo:getMoveAnimRaw(map._map) + local adx, ady = mo:getWorldPos() if self._adx then self.ps:shift(self._adx - adx, self._ady - ady) end diff --git a/game/engines/default/engine/PlayerProfile.lua b/game/engines/default/engine/PlayerProfile.lua index 67e8c3b78394265ccda89d8cb3ffbe42a6a3f112..6b6846a3765c19b58b719261d36e7125c608c77c 100644 --- a/game/engines/default/engine/PlayerProfile.lua +++ b/game/engines/default/engine/PlayerProfile.lua @@ -88,6 +88,7 @@ function _M:init() ["^donations$"] = { invalid = { read={offline=true}, write="offline" }, valid = { read={offline=true}, write="offline" } }, } self.auth = false + self.connected = nil end function _M:start() @@ -457,6 +458,7 @@ function _M:waitFirstAuth(timeout) if self.auth_tried and self.auth_tried >= 1 then return end if not self.waiting_auth then return end print("[PROFILE] waiting for first auth") + if self.connected == false then print("[PROFILE] waiting cancelled, connected = false") return end -- Set to false when we got a disconnect event, at boot it is nil local first = true timeout = timeout or 120 while self.waiting_auth and timeout > 0 do @@ -466,8 +468,10 @@ function _M:waitFirstAuth(timeout) end local evt = self:popEvent() while evt do - if type(game) == "table" then game:handleProfileEvent(evt) - else self:handleEvent(evt) end + local e + if type(game) == "table" then e = game:handleProfileEvent(evt) + else e = self:handleEvent(evt) end + if e and e.e == "Disconnected" then print("[PROFILE] waiting cancelled, got disconnect event") timeout = 0 break end if not self.waiting_auth then break end evt = self:popEvent() end @@ -534,10 +538,12 @@ end function _M:eventConnected(e) if game and type(game) == "table" and game.log then game.log("#YELLOW#Connection to online server established.") end + self.connected = true end function _M:eventDisconnected(e) - if game and type(game) == "table" and game.log then game.log("#YELLOW#Connection to online server lost, trying to reconnect.") end + if game and type(game) == "table" and game.log and self.connected then game.log("#YELLOW#Connection to online server lost, trying to reconnect.") end + self.connected = false end function _M:eventFunFacts(e) @@ -690,6 +696,21 @@ function _M:checkAddonHash(module, addon, md5) return true end +function _M:checkAddonUpdates(list) + if not self.auth then return nil, "no online profile active" end + if #list == 0 then return nil, "nothing to update" end + core.profile.pushOrder(table.serialize{o="CheckAddonUpdates", list=list}) + + local ok = false + self:waitEvent("CheckAddonUpdates", function(e) ok = e.ok end, 10000) + + if not ok then return nil, "bad game addon version" end + ok = ok:unserialize() + print("[ONLINE PROFILE] addon update list returned") + table.print(ok) + return ok +end + function _M:checkBatchHash(list) if not self.auth then return nil, "no online profile active" end if config.settings.cheat then return nil, "cheat mode active" end diff --git a/game/engines/default/engine/Projectile.lua b/game/engines/default/engine/Projectile.lua index 10d8d32b194b52ff1f48198e7a6dae1843e04e43..bb4acc084d6c3ed9de1f6f256f613d281dbd5d7f 100644 --- a/game/engines/default/engine/Projectile.lua +++ b/game/engines/default/engine/Projectile.lua @@ -250,8 +250,15 @@ function _M:on_move(x, y, target) self.src.__project_source = self -- intermediate projector source if self.project and self.project.def.typ.line then self.src:projectDoAct(self.project.def.typ, self.project.def.tg, self.project.def.damtype, self.project.def.dam, self.project.def.particles, self.x, self.y, self.tmp_proj) end if self.project and self.project.def.typ.stop_block then - self.src:projectDoStop(self.project.def.typ, self.project.def.tg, self.project.def.damtype, self.project.def.dam, self.project.def.particles, self.x, self.y, self.tmp_proj, self.x, self.y, self) + elseif self.homing then + if (self.x == self.homing.target.x and self.y == self.homing.target.y) then + game.level:removeEntity(self, true) + self.dead = true + self.homing.on_hit(self, self.src, self.homing.target) + else + self.homing.on_move(self, self.src) + end end self.src.__project_source = nil end diff --git a/game/engines/default/engine/Savefile.lua b/game/engines/default/engine/Savefile.lua index 6fc52ca69fce1a2b53cc5467f5b7c743d4b89663..fb440bbeb3d5478874d2682a49c39ab37e67f744 100644 --- a/game/engines/default/engine/Savefile.lua +++ b/game/engines/default/engine/Savefile.lua @@ -367,7 +367,7 @@ function _M:saveEntity(e, no_dialog) end local function resolveSelf(o, base, allow_object) - if o.__CLASSNAME and not allow_object then return end + if o.__ATOMIC and not allow_object then return end local change = {} for k, e in pairs(o) do diff --git a/game/engines/default/engine/Store.lua b/game/engines/default/engine/Store.lua index 2c1e196afc379109e1366d0167aafa0061dd26f6..c877c3119d964022eb6b9d02a83a996ddc9737a2 100644 --- a/game/engines/default/engine/Store.lua +++ b/game/engines/default/engine/Store.lua @@ -152,6 +152,7 @@ function _M:transfer(src, dest, item, nb) dest:sortInven(dest_inven) end +--- assumes buyer has room function _M:doBuy(who, o, item, nb, store_dialog) nb = math.min(nb, o:getNumber()) nb = self:tryBuy(who, o, item, nb) diff --git a/game/engines/default/engine/Target.lua b/game/engines/default/engine/Target.lua index 61d6cb2e5c5df97f3f9780fde1c50e38671f0a18..78bd6aab8eba32d481803548781580dc2ae8bae6 100644 --- a/game/engines/default/engine/Target.lua +++ b/game/engines/default/engine/Target.lua @@ -387,6 +387,10 @@ end -- Cone: hits everything in a cone in the direction<br/> -- @param t.radius The radius of the ball/cone AoE -- @param t.cone_angle The angle for the cone AoE (default 55°) +-- @param t.grid_exclude = {[x1][y1]=true,...[x2][y2]=true...} Grids to exclude - for making holes in the AOE +-- @param t.act_exclude = {[uid] = true,...} exclude grids containing actor(s) with the matching uid(s) +-- @param t.selffire = boolean or % chance to project against grids with self +-- @param t.friendlyfire = boolean or % chance to project against grids with friendly Actors (based on Actor:reactionToward(target)>0) -- @param t.no_restrict Boolean that removes all restrictions in the t.type defined block functions. -- @param t.stop_block Boolean that stops the target on the first tile that has an entity that blocks move. -- @param t.range The range the target can be from the origin. diff --git a/game/engines/default/engine/UserChat.lua b/game/engines/default/engine/UserChat.lua index ca4f269d889152f22b34c4476a1a6f2408ad58f4..b14a58d589e45298d94277159d83fc603ffcb169 100644 --- a/game/engines/default/engine/UserChat.lua +++ b/game/engines/default/engine/UserChat.lua @@ -444,7 +444,8 @@ function _M:talkBox(on_end, only_friends) if not profile.auth then return end local Talkbox = require "engine.dialogs.Talkbox" local d = Talkbox.new(self, on_end, only_friends) - if not d.nobody then game:registerDialog(d) end + if not d.nobody then game:registerDialog(d) + else game.log("#CRIMSON#You are not subscribed to any channel, you can change that in the game options.#LAST#") end self:updateChanList() end diff --git a/game/engines/default/engine/Zone.lua b/game/engines/default/engine/Zone.lua index 2021094b10813dc84e3806eaace6e192d33d7800..6222bfa7c29517328807f5a9aa6b3f6ce5c095e2 100644 --- a/game/engines/default/engine/Zone.lua +++ b/game/engines/default/engine/Zone.lua @@ -488,6 +488,7 @@ function _M:finishEntity(level, type, e, ego_filter) print("applying addon", ego.name, "to ", e.name, "::", newname, "///", e.unided_name, ego.unided_name) ego.unided_name = nil ego.__CLASSNAME = nil + ego.__ATOMIC = nil -- The ego requested instant resolving before merge ? if ego.instant_resolve then ego:resolve(nil, nil, e) end ego.instant_resolve = nil @@ -576,6 +577,7 @@ function _M:finishEntity(level, type, e, ego_filter) print("applying ego", ego.name, "to ", e.name, "::", newname, "///", e.unided_name, ego.unided_name) ego.unided_name = nil ego.__CLASSNAME = nil + ego.__ATOMIC = nil -- The ego requested instant resolving before merge ? if ego.instant_resolve then ego:resolve(nil, nil, e) end ego.instant_resolve = nil @@ -605,7 +607,7 @@ function _M:finishEntity(level, type, e, ego_filter) end e:resolve(nil, true) - + e:check("finish", e, self, level) return e end @@ -654,6 +656,7 @@ end --- If we are loaded we need a new uid function _M:loaded() __zone_store[self] = true + self._tmp_data = {} if type(self.reload_lists) ~= "boolean" or self.reload_lists then self:loadBaseLists() @@ -702,7 +705,7 @@ end local recurs = function(t) local nt = {} - for k, e in pairs(nt) do if k ~= "__CLASSNAME" then nt[k] = e end end + for k, e in pairs(nt) do if k ~= "__CLASSNAME" and k ~= "__ATOMIC" then nt[k] = e end end return nt end function _M:getLevelData(lev) @@ -713,6 +716,7 @@ function _M:getLevelData(lev) if res.alter_level_data then res.alter_level_data(self, lev, res) end -- Make sure it is not considered a class res.__CLASSNAME = nil + res.__ATOMIC = nil return res end diff --git a/game/engines/default/engine/class.lua b/game/engines/default/engine/class.lua index d2a8a622abe9c663b0a0b6c2cbe4d0a8dd0b84df..2acf78ca8c355f341b3f6e26592c26275c596a8e 100644 --- a/game/engines/default/engine/class.lua +++ b/game/engines/default/engine/class.lua @@ -35,6 +35,7 @@ function make(c) c.new = function(...) local obj = {} obj.__CLASSNAME = c._NAME + obj.__ATOMIC = true setmetatable(obj, {__index=c}) if obj.init then obj:init(...) end return obj @@ -93,6 +94,7 @@ function inherit(...) c.new = function(...) local obj = {} obj.__CLASSNAME = c._NAME + obj.__ATOMIC = true setmetatable(obj, {__index=c}) if obj.init then obj:init(...) end return obj @@ -140,8 +142,8 @@ local function clonerecurs(d) local n = {} for k, e in pairs(d) do local nk, ne = k, e - if type(k) == "table" and not k.__CLASSNAME then nk = clonerecurs(k) end - if type(e) == "table" and not e.__CLASSNAME then ne = clonerecurs(e) end + if type(k) == "table" and not k.__ATOMIC then nk = clonerecurs(k) end + if type(e) == "table" and not e.__ATOMIC then ne = clonerecurs(e) end n[nk] = ne end return n @@ -169,7 +171,7 @@ function _M:clone(t) end local function clonerecursfull(clonetable, d, noclonecall, use_saveinstead) - if use_saveinstead and d.__CLASSNAME and d.__SAVEINSTEAD then + if use_saveinstead and d.__ATOMIC and d.__SAVEINSTEAD then d = d.__SAVEINSTEAD if clonetable[d] then return d, 1 end end @@ -194,8 +196,8 @@ local function clonerecursfull(clonetable, d, noclonecall, use_saveinstead) k, e = next(d, k) end setmetatable(n, getmetatable(d)) - if not noclonecall and n.cloned and n.__CLASSNAME then n:cloned(d) end - if n.__CLASSNAME then nb = nb + 1 end + if not noclonecall and n.cloned and n.__ATOMIC then n:cloned(d) end + if n.__ATOMIC then nb = nb + 1 end return n, nb end @@ -302,7 +304,7 @@ function _M:save(filter, allow) local mt = getmetatable(self) setmetatable(self, {}) local savefile = engine.Savefile.current_save - + local s = core.serial.new( -- Zip to write to savefile.current_save_zip, diff --git a/game/engines/default/engine/dialogs/Downloader.lua b/game/engines/default/engine/dialogs/Downloader.lua new file mode 100644 index 0000000000000000000000000000000000000000..3ae6fd9549395c7a6a194e2184eca6cabbdfd810 --- /dev/null +++ b/game/engines/default/engine/dialogs/Downloader.lua @@ -0,0 +1,170 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 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 + +require "engine.class" +local Base = require "engine.ui.Base" +local Focusable = require "engine.ui.Focusable" + +--- A web browser +module(..., package.seeall, class.inherit(Base, Focusable)) + +function _M:init(t) + self.w = 50 + self.h = 50 + self.title = t.title + self.co = assert(t.co, "no downloader coroutine") + self.url = assert(t.url, "no downloader url") + self.dest = assert(t.dest, "no downloader dest") + + if fs.exists(self.dest) then + self.dest = fs.getRealPath(self.dest) + else + local _, _, dir, name = self.dest:find("(.+)/([^/]+)$") + if dir then + self.dest = fs.getRealPath(dir)..fs.getPathSeparator()..name + end + end + + self.allow_downloads = t.allow_downloads or {} + self.allow_login = t.allow_login + self.custom_calls = t.custom_calls or {} + if self.allow_login == nil then self.allow_login = true end + + if self.allow_login and self.url:find("^http://te4%.org/") and profile.auth then + local param = "_te4ah="..profile.auth.hash.."&_te4ad="..profile.auth.drupid + + local first = self.url:find("?", 1, 1) + if first then self.url = self.url.."&"..param + else self.url = self.url.."?"..param end + end + + if self.url:find("^http://te4%.org/") then + local param = "_te4" + + local first = self.url:find("?", 1, 1) + if first then self.url = self.url.."&"..param + else self.url = self.url.."?"..param end + end + + print("Creating Downloader with url", self.url) + + Base.init(self, t) +end + +function _M:generate() + self.mouse:reset() + self.key:reset() + + local handlers = { + on_crash = function() + print("WebView crashed, closing C view") + self.view = nil + end, + } + if self.allow_downloads then self:onDownload(handlers) end + self.view = core.webview.new(self.w, self.h, handlers) + if not self.view:usable() then + self.unusable = true + return + end + + self.custom_calls._nextDownloadName = function(name) + if name then self._next_download_name = {name=name, time=os.time()} + else self._next_download_name = nil + end + end + + for name, fct in pairs(self.custom_calls) do + handlers[name] = fct + self.view:setMethod(name) + end + self.view:loadURL(self.url) + self.loading = 0 + self.loading_rotation = 0 + self.scroll_inertia = 0 +end + +function _M:makeDownloadbox(downid, file) + local Dialog = require "engine.ui.Dialog" + local Waitbar = require "engine.ui.Waitbar" + local Button = require "engine.ui.Button" + + local d = Dialog.new(self.title or "Download: "..file, 600, 100) + local b = Button.new{text="Cancel", fct=function() self.view:downloadAction(downid, false) game:unregisterDialog(d) end} + local w = Waitbar.new{size=600, text=file} + d:loadUI{ + {left=0, top=0, ui=w}, + {right=0, bottom=0, ui=b}, + } + d:setupUI(true, true) + function d:updateFill(...) w:updateFill(...) end + return d +end + +function _M:finish() + if not self.never_clean then + self.downloader = nil + self.view = nil + end +end + +function _M:onDownload(handlers) + local Dialog = require "engine.ui.Dialog" + + handlers.on_download_request = function(downid, url, file, mime) + if mime == "application/t-engine-addon" and self.allow_downloads.addons and url:find("^http://te4%.org/") then + local name = file + if self._next_download_name and os.time() - self._next_download_name.time <= 3 then name = self._next_download_name.name self._next_download_name = nil end + print("Accepting addon download to:", self.dest) + self.download_dialog = self:makeDownloadbox(downid, file) + self.download_dialog.install_kind = "Addon" + game:registerDialog(self.download_dialog) + self.view:downloadAction(downid, self.dest) + return + elseif mime == "application/t-engine-module" and self.allow_downloads.modules and url:find("^http://te4%.org/") then + local name = file + if self._next_download_name and os.time() - self._next_download_name.time <= 3 then name = self._next_download_name.name self._next_download_name = nil end + print("Accepting module download to:", self.dest) + self.download_dialog = self:makeDownloadbox(downid, file) + self.download_dialog.install_kind = "Game Module" + game:registerDialog(self.download_dialog) + self.view:downloadAction(downid, self.dest) + return + end + self.view:downloadAction(downid, false) + end + + handlers.on_download_update = function(downid, cur_size, total_size, percent, speed) + if not self.download_dialog then return end + self.download_dialog:updateFill(cur_size, total_size, ("%d%% - %d KB/s"):format(cur_size * 100 / total_size, speed / 1024)) + end + + handlers.on_download_finish = function(downid) + if not self.download_dialog then return end + game:unregisterDialog(self.download_dialog) + local kind = self.download_dialog.install_kind + self.download_dialog = nil + self:finish() + coroutine.resume(self.co, true, kind) + end +end + +function _M:start() + return coroutine.yield() +end \ No newline at end of file diff --git a/game/engines/default/engine/dialogs/ShowText.lua b/game/engines/default/engine/dialogs/ShowText.lua index 901b6966643542b9ac96d2ac19ca40a4bfc8ecc0..6849bc27c65a53b78e7cb469870a2a38b23a7de7 100644 --- a/game/engines/default/engine/dialogs/ShowText.lua +++ b/game/engines/default/engine/dialogs/ShowText.lua @@ -51,7 +51,8 @@ end function _M:generateList(file, replace) local f, err = loadfile("/data/texts/"..file..".lua") if not f and err then error(err) end - setfenv(f, setmetatable({}, {__index=_G})) + local env = setmetatable({}, {__index=_G}) + setfenv(f, env) local str = f() str = str:gsub("@([^@]+)@", function(what) @@ -60,5 +61,10 @@ function _M:generateList(file, replace) end) self.text = str + + if env.title then + self.title = env.title + end + return true end diff --git a/game/engines/default/engine/generator/map/Building.lua b/game/engines/default/engine/generator/map/Building.lua index e8deb284a5839cbc9e8fe28bd2a1fec0cc5b179e..b41db9319c36fa7c17c813ca0ba8ac00a324590c 100644 --- a/game/engines/default/engine/generator/map/Building.lua +++ b/game/engines/default/engine/generator/map/Building.lua @@ -32,6 +32,8 @@ function _M:init(zone, map, level, data) self.max_block_h = data.max_block_h or 20 self.max_building_w = data.max_building_w or 7 self.max_building_h = data.max_building_h or 7 + self.margin_w = data.margin_w or 0 + self.margin_h = data.margin_h or 0 RoomsLoader.init(self, data) end @@ -161,7 +163,7 @@ function _M:generate(lev, old_lev) self.walls = {} self.rooms = {} - local bsp = BSP.new(self.map.w, self.map.h, self.max_block_w, self.max_block_h) + local bsp = BSP.new(self.map.w - self.margin_w, self.map.h - self.margin_h, self.max_block_w, self.max_block_h) bsp:partition() print("Building gen made ", #bsp.leafs, "blocks BSP leafs") diff --git a/game/engines/default/engine/generator/map/Filled.lua b/game/engines/default/engine/generator/map/Filled.lua new file mode 100644 index 0000000000000000000000000000000000000000..26cf9082fddc4158aa9368eec25bb6686cdf4917 --- /dev/null +++ b/game/engines/default/engine/generator/map/Filled.lua @@ -0,0 +1,111 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 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 + +require "engine.class" +local Map = require "engine.Map" +require "engine.Generator" +module(..., package.seeall, class.inherit(engine.Generator)) + +function _M:init(zone, map, level, data) + engine.Generator.init(self, zone, map, level) + self.data = data + self.grid_list = zone.grid_list + self.wall = self:resolve("wall") or self:resolve("#") +end + +--- Create the stairs inside the level +function _M:makeStairsInside(lev, old_lev, spots) + -- Put down stairs + local dx, dy + if lev < self.zone.max_level or self.data.force_last_stair then + while true do + dx, dy = rng.range(1, self.map.w - 1), rng.range(1, self.map.h - 1) + if not self.map:checkEntity(dx, dy, Map.TERRAIN, "block_move") and not self.map.room_map[dx][dy].special then + self.map(dx, dy, Map.TERRAIN, self:resolve("down")) + self.map.room_map[dx][dy].special = "exit" + break + end + end + end + + -- Put up stairs + local ux, uy + while true do + ux, uy = rng.range(1, self.map.w - 1), rng.range(1, self.map.h - 1) + if not self.map:checkEntity(ux, uy, Map.TERRAIN, "block_move") and not self.map.room_map[ux][uy].special then + self.map(ux, uy, Map.TERRAIN, self:resolve("up")) + self.map.room_map[ux][uy].special = "exit" + break + end + end + + return ux, uy, dx, dy, spots +end + +--- Create the stairs on the sides +function _M:makeStairsSides(lev, old_lev, sides, spots) + -- Put down stairs + local dx, dy + if lev < self.zone.max_level or self.data.force_last_stair then + while true do + if sides[2] == 4 then dx, dy = 0, rng.range(1, self.map.h - 1) + elseif sides[2] == 6 then dx, dy = self.map.w - 1, rng.range(1, self.map.h - 1) + elseif sides[2] == 8 then dx, dy = rng.range(1, self.map.w - 1), 0 + elseif sides[2] == 2 then dx, dy = rng.range(1, self.map.w - 1), self.map.h - 1 + end + + if not self.map.room_map[dx][dy].special then + self.map(dx, dy, Map.TERRAIN, self:resolve("down")) + self.map.room_map[dx][dy].special = "exit" + break + end + end + end + + -- Put up stairs + local ux, uy + while true do + if sides[1] == 4 then ux, uy = 0, rng.range(1, self.map.h - 1) + elseif sides[1] == 6 then ux, uy = self.map.w - 1, rng.range(1, self.map.h - 1) + elseif sides[1] == 8 then ux, uy = rng.range(1, self.map.w - 1), 0 + elseif sides[1] == 2 then ux, uy = rng.range(1, self.map.w - 1), self.map.h - 1 + end + + if not self.map.room_map[ux][uy].special then + self.map(ux, uy, Map.TERRAIN, self:resolve("up")) + self.map.room_map[ux][uy].special = "exit" + break + end + end + + return ux, uy, dx, dy, spots +end + +function _M:generate(lev, old_lev) + for i = 0, self.map.w - 1 do for j = 0, self.map.h - 1 do + self.map(i, j, Map.TERRAIN, self.wall) + end end + + local spots = {} + if self.data.edge_entrances then + return self:makeStairsSides(lev, old_lev, self.data.edge_entrances, rooms, spots) + else + return self:makeStairsInside(lev, old_lev, spots) + end +end diff --git a/game/engines/default/engine/generator/map/Static.lua b/game/engines/default/engine/generator/map/Static.lua index 6711761cb73119c80bc11b115e9b6ad0e7c70855..57605ccd9fe4d2cfb7fc04508fc70ca08db37302 100644 --- a/game/engines/default/engine/generator/map/Static.lua +++ b/game/engines/default/engine/generator/map/Static.lua @@ -199,7 +199,7 @@ function _M:resolve(typ, c) local res = self.tiles[c][typ] if type(res) == "function" then return self.grid_list[res()] - elseif type(res) == "table" and res.__CLASSNAME then + elseif type(res) == "table" and res.__ATOMIC then return res elseif type(res) == "table" then return self.grid_list[res[rng.range(1, #res)]] diff --git a/game/engines/default/engine/interface/ActorInventory.lua b/game/engines/default/engine/interface/ActorInventory.lua index 93f9b13ecd054e661062ae1f2f121e767e9df9b4..2b4b25da41132142349a86ab5ccc2202c3bb7734 100644 --- a/game/engines/default/engine/interface/ActorInventory.lua +++ b/game/engines/default/engine/interface/ActorInventory.lua @@ -29,8 +29,14 @@ module(..., package.seeall, class.make) _M.inven_def = {} ---- Defines stats +--- Defines an Inventory slot -- Static! +-- @param short_name = name for reference (required) +-- @param name = name used for messages (required) +-- @param is_worn = boolean true if equipment can be worn in this inventory +-- @param desc = description (required) +-- @param show_equip = boolean to show inventory when displaying equipment dialogs +-- @param infos = additional information (including default stack_limit) function _M:defineInventory(short_name, name, is_worn, desc, show_equip, infos) assert(name, "no inventory slot name") assert(short_name, "no inventory slot short_name") @@ -42,6 +48,7 @@ function _M:defineInventory(short_name, name, is_worn, desc, show_equip, infos) is_worn = is_worn, is_shown_equip = show_equip, infos = infos, + stack_limit = infos and infos.stack_limit, }) self.inven_def[#self.inven_def].id = #self.inven_def self.inven_def[short_name] = self.inven_def[#self.inven_def] @@ -50,7 +57,7 @@ function _M:defineInventory(short_name, name, is_worn, desc, show_equip, infos) end -- Auto define the inventory -_M:defineInventory("INVEN", "In inventory", false, "") +_M:defineInventory("INVEN", "In inventory", false, "") --INVEN_INVEN assumed to have no stacking limit --- Initialises inventories with default values if needed function _M:init(t) @@ -58,10 +65,21 @@ function _M:init(t) self:initBody() end +--- generate inventories according to the body definition table +-- @param self.body = {SLOT_ID = max, ...} +-- @param max = number of slots if number or table of properties (max = , stack_limit = , ..) merged into definition function _M:initBody() if self.body then + local def for inven, max in pairs(self.body) do - self.inven[self["INVEN_"..inven]] = {max=max, worn=self.inven_def[self["INVEN_"..inven]].is_worn, id=self["INVEN_"..inven], name=inven} + def = self.inven_def[self["INVEN_"..inven]] + assert(def, "inventory slot undefined") + self.inven[self["INVEN_"..inven]] = {worn=def.is_worn, id=self["INVEN_"..inven], name=inven, stack_limit = def.stack_limit} + if type(max) == "table" then + table.merge(self.inven[self["INVEN_"..inven]], max, true) + else + self.inven[self["INVEN_"..inven]].max = max + end end self.body = nil end @@ -89,18 +107,59 @@ function _M:canAddToInven(id) end end +--- Get stacking limit for an inventory +-- @param id inventory id or table (stack_limit in inventory table takes precedence) +function _M:invenStackLimit(id) + local inven = self:getInven(id) + return inven.stack_limit or self.inven_def[inven.id].stack_limit or math.huge +end + --- Adds an object to an inventory --- @return false if the object could not be added otherwise true and the inventory index where it is now -function _M:addObject(inven_id, o) +-- @param inven_id = inventory id to add to +-- @param o = object to add +-- @param no_unstack = boolean to prevent unstacking the object to be added +-- @return false if the object could not be added or true, inventory index it was moved to, remaining stack if any or false +-- checks o:on_preaddobject(self, inven) (must return true to add to inventory) +function _M:addObject(inven_id, o, no_unstack) local inven = self:getInven(inven_id) - - -- No room ? - if #inven >= inven.max then return false end + local slot + local stack, rs, ok + local stackable, stack_limit = o and o:stackable(), self:invenStackLimit(inven_id) + + -- No room, stackable ? + if #inven >= inven.max then + if stackable and not no_unstack then -- try to find a stack to add to + for i, obj in ipairs(inven) do + if o:canStack(obj) and obj:getNumber() < stack_limit then + slot = i + stack = obj break -- only room left + end + end + if not stack then return false end + else + return false + end + end if o:check("on_preaddobject", self, inven) then return false end - -- Ok add it - table.insert(inven, o) + -- Add the object + if stackable and not no_unstack then -- handle stackable objects + local last = true + rs = true + if stack then -- add to stack already found + ok, last = stack:stack(o, false, stack_limit - stack:getNumber()) + elseif o:getNumber() > stack_limit then -- stack too big - unstack some before adding + stack, last = o:unstack(o:getNumber() - stack_limit) + table.insert(inven, o) + o = stack + else + table.insert(inven, o) + end + if last then rs = false end + else + table.insert(inven, o) + end -- Do whatever is needed when wearing this object if inven.worn then @@ -111,57 +170,83 @@ function _M:addObject(inven_id, o) -- Make sure the object is registered with the game, if need be if not game:hasEntity(o) then game:addEntity(o) end - - return true, #inven + return true, slot or #inven, rs and o end --- Returns the position of an item in the given inventory, or nil -function _M:itemPosition(inven, o) +-- @param inven = inventory or inventory id to search +-- @param o = object to look for +-- @param by_reference set true to match by exact (memory) reference, otherwise matches by o.name +-- @return nil or the inventory slot, stack position if stacked +function _M:itemPosition(inven, o, by_reference) inven = self:getInven(inven) + local found, pos = nil, nil for i, p in ipairs(inven) do - local found = nil - o:forAllStack(function(so) - if p.name == so.name then found = i return true end + p:forAllStack(function(so, j) + if (not by_reference and (so.name == o.name) or (so == o)) then + found = i pos = j return true + end end) - if found then return found end + if found then return found, pos end end return nil end ---- Picks an object from the floor +--- Pick up an object from the floor +-- @param i = object position on map at self.x, self.y +-- @param vocal = boolean to post messages to log +-- @param no_sort = boolen to suppress automatic sorting of inventory +-- puts picked up objects in self.INVEN_INVEN +-- @return the object picked up (or stack added to), num picked up or true if o:on_prepickup(i) returns true (not "skip") or nil +-- checks obj:on_prepickup(self, i) (must return true to pickup) +-- checks obj:on_pickup(self, num) and self:on_pickup_object(obj, num) functions after pickup (includes stacks) function _M:pickupFloor(i, vocal, no_sort) - if not self:getInven(self.INVEN_INVEN) then return end + local inven = self:getInven(self.INVEN_INVEN) + if not inven then return end local o = game.level.map:getObject(self.x, self.y, i) if o then local prepickup = o:check("on_prepickup", self, i) - if not prepickup and self:addObject(self.INVEN_INVEN, o) then - game.level.map:removeObject(self.x, self.y, i) - if not no_sort then self:sortInven(self.INVEN_INVEN) end - - o:check("on_pickup", self) - self:check("on_pickup_object", o) - - local letter = ShowPickupFloor:makeKeyChar(self:itemPosition(self.INVEN_INVEN, o) or 1) - if vocal then game.logSeen(self, "%s picks up (%s.): %s.", self.name:capitalize(), letter, o:getName{do_color=true}) end - return o - elseif not prepickup then - if vocal then game.logSeen(self, "%s has no room for: %s.", self.name:capitalize(), o:getName{do_color=true}) end - return + if not prepickup then + local num = o:getNumber() + local ok, slot, ro = self:addObject(self.INVEN_INVEN, o) + if ok then + local newo = inven[slot] -- get exact object added or stack (resolves duplicates) + game.level.map:removeObject(self.x, self.y, i) + if ro then -- return remaining stack to floor + game.level.map:addObject(self.x, self.y, ro) + num = num - ro:getNumber() + end + if not no_sort then self:sortInven(self.INVEN_INVEN) end + -- Apply checks to whole stack (including already carried) assuming homogeneous stack + -- num added passed to functions to allow checks on part of the stack + newo:check("on_pickup", self, num) + self:check("on_pickup_object", newo, num) + + slot = self:itemPosition(self.INVEN_INVEN, newo, true) or 1 + local letter = ShowPickupFloor:makeKeyChar(slot) + + if vocal then game.logSeen(self, "%s picks up (%s.): %s%s.", self.name:capitalize(), letter, num>1 and ("%d "):format(num) or "", o:getName{do_color=true, no_count = true}) end + return inven[slot], num + else + if vocal then game.logSeen(self, "%s has no room for: %s.", self.name:capitalize(), o:getName{do_color=true}) end + return + end elseif prepickup == "skip" then return else return true end else - if vocal then game.logSeen(self, "There is nothing to pick up there.") end + if vocal then game.logSeen(self, "There is nothing to pick up here.") end end end --- Removes an object from inventory --- @param inven the inventory to drop from --- @param item the item id to drop --- @param no_unstack if the item was a stack takes off the whole stack if true +-- @param inven the inventory to remove from +-- @param item inven slot of the item to remove +-- @param no_unstack = num items to remove into a new stack (set true to remove the original stack unchanged) -- @return the object removed or nil if no item existed and a boolean saying if there is no more objects +-- checks obj:on_preremoveobject(self, inven) (return true to not remove) function _M:removeObject(inven_id, item, no_unstack) local inven = self:getInven(inven_id) @@ -170,8 +255,11 @@ function _M:removeObject(inven_id, item, no_unstack) local o, finish = inven[item], true if o:check("on_preremoveobject", self, inven) then return false, true end - - if not no_unstack then + if no_unstack then + if type(no_unstack) == "number" then + o, finish = o:unstack(no_unstack) + end + else o, finish = o:unstack() end if finish then @@ -221,7 +309,9 @@ end --- Drop an object on the floor -- @param inven the inventory to drop from -- @param item the item id to drop +-- @param all set to remove part (if number) or all (if true) a stack -- @return the object removed or nil if no item existed +-- checks obj:on_drop(self) (return true to not drop) function _M:dropFloor(inven, item, vocal, all) local o = self:getInven(inven)[item] if not o then @@ -240,7 +330,7 @@ function _M:dropFloor(inven, item, vocal, all) if ok and game.level.map.attrs(self.x, self.y, "on_drop") then game.level.map.attrs(self.x, self.y, "on_drop")(self, self.x, self.y, idx, o) end - return true + return o end --- Show combined equipment/inventory dialog @@ -283,6 +373,9 @@ function _M:showPickupFloor(title, filter, action) end --- Can we wear this item? +-- @param o = object to wear +-- @param try_slot = inventory slot to wear (override) +-- checks self:canWearObjectCustom(o, try_slot) (return true to make unwearable) function _M:canWearObject(o, try_slot) local req = rawget(o, "require") @@ -343,6 +436,11 @@ function _M:getObjectOffslot(o) end --- Wear/wield an item +-- @param o = object to be worn +-- @param replace = boolean allow first object in wearable inventory to be removed to make space if needed +-- @vocal = boolean to post messages to game.logSeen(self, ....) +-- returns true or replaced object if succeeded or false if not, remaining stack of o if any +-- checks o:on_canwear(self, inven) (return true to prevent wearing) function _M:wearObject(o, replace, vocal) local inven = o:wornInven() if not inven then @@ -360,27 +458,26 @@ function _M:wearObject(o, replace, vocal) return false end if o:check("on_canwear", self, inven) then return false end - local offslot = self:getObjectOffslot(o) + local offslot, stackable = self:getObjectOffslot(o), o:stackable() + local added, slot, stack = self:addObject(inven, o) - if self:addObject(inven, o) then + if added then if vocal then game.logSeen(self, "%s wears: %s.", self.name:capitalize(), o:getName{do_color=true}) end - return true + return true, stack elseif offslot and self:getInven(offslot) and #(self:getInven(offslot)) < self:getInven(offslot).max and self:canWearObject(o, offslot) then if vocal then game.logSeen(self, "%s wears(offslot): %s.", self.name:capitalize(), o:getName{do_color=true}) end - -- Warning: assume there is now space - self:addObject(self:getInven(offslot), o) - return true - elseif replace then + added, slot, stack = self:addObject(self:getInven(offslot), o) + return added, stack + elseif replace then -- no room but replacement is allowed + if stackable then + end local ro = self:removeObject(inven, 1, true) - - if vocal then game.logSeen(self, "%s wears(replacing): %s.", self.name:capitalize(), o:getName{do_color=true}) end - - -- Can we stack the old and new one ? - if o:stack(ro) then ro = true end - - -- Warning: assume there is now space - self:addObject(inven, o) - return ro + added, slot, stack = self:addObject(inven, o) + if vocal then game.logSeen(self, "%s wears(replacing %s): %s.", self.name:capitalize(), ro:getName{do_color=true}, o:getName{do_color=true}) end + if stack and ro:stack(stack) then -- stack remaining stack with old if possible (ignores stack limits) + stack = nil + end + return ro, stack -- caller handles the replaced object and remaining stack if any else if vocal then game.logSeen(self, "%s can not wear: %s.", self.name:capitalize(), o:getName{do_color=true}) end return false @@ -388,8 +485,11 @@ function _M:wearObject(o, replace, vocal) end --- Takeoff item -function _M:takeoffObject(inven, item) - inven = self:getInven(inven) +-- @param inven_id = inventory id +-- @param item = slot to remove from +-- checks obj:on_cantakeoff(self, inven) (return true to prevent taking off) +function _M:takeoffObject(inven_id, item) + inven = self:getInven(inven_id) if not inven then return false end local o = inven[item] @@ -400,6 +500,9 @@ function _M:takeoffObject(inven, item) end --- Call when an object is worn +-- @param o = object being worn +-- @param inven_id = inventory id +-- checks o:on_wear(self, inven_id) function _M:onWear(o, inven_id) -- Apply wielder properties o.wielded = {} @@ -412,6 +515,9 @@ function _M:onWear(o, inven_id) end --- Call when an object is taken off +-- @param o = object being taken off +-- @param inven_id = inventory id +-- checks o:on_takeoff(self, inven_id) function _M:onTakeoff(o, inven_id) if o.wielded then for k, id in pairs(o.wielded) do @@ -427,19 +533,28 @@ function _M:onTakeoff(o, inven_id) end --- Re-order inventory, sorting and stacking it +-- sort order is type > subtype > name > getNumber() function _M:sortInven(inven) if not inven then inven = self.inven[self.INVEN_INVEN] end inven = self:getInven(inven) if not inven then return end +-- local stacked, last, stacklimit = false, false, inven.stack_limit or math.huge + local stacked, last, stacklimit = false, false, self:invenStackLimit(inven) - -- Stack objects first, from bottom - for i = #inven, 1, -1 do - -- If it is stackable, look for objects before it that it could stack into + -- First, stack objects from top + for i = 1, #inven do + if not inven[i] then break end + -- If it is stackable, look for objects after it that can stack into it if inven[i]:stackable() then - for j = i - 1, 1, -1 do - if inven[j]:stack(inven[i]) then - table.remove(inven, i) - break + for j = #inven, i + 1, -1 do + -- check stack limit + stacked, last = inven[i]:stack(inven[j], false, stacklimit - inven[i]:getNumber()) + if stacked then + if last then + table.remove(inven, j) + else + break + end end end end @@ -451,7 +566,11 @@ function _M:sortInven(inven) local sa, sb = a:getSubtypeOrder(), b:getSubtypeOrder() if ta == tb then if sa == sb then - return a.name < b.name + if a.name == b.name then + return a:getNumber() > b:getNumber() + else + return a.name < b.name + end else return sa < sb end @@ -466,6 +585,7 @@ end -- @param inven the inventory to look into -- @param name the name to look for -- @param getname the parameters to pass to getName(), if nil the default is {no_count=true, force_id=true} +-- @return object, position or nil if not found function _M:findInInventory(inven, name, getname) getname = getname or {no_count=true, force_id=true} for item, o in ipairs(inven) do @@ -476,6 +596,7 @@ end --- Finds an object by name in all the actor's inventories -- @param name the name to look for -- @param getname the parameters to pass to getName(), if nil the default is {no_count=true, force_id=true} +-- @return object, position, inven_id or nil if not found function _M:findInAllInventories(name, getname) for inven_id, inven in pairs(self.inven) do local o, item = self:findInInventory(inven, name, getname) @@ -487,6 +608,7 @@ end -- @param inven the inventory to look into -- @param prop the property to look for -- @param value the value to look for, can be a function +-- @return object, position or nil if not found function _M:findInInventoryBy(inven, prop, value) if type(value) == "function" then for item, o in ipairs(inven) do @@ -502,6 +624,7 @@ end --- Finds an object by property in all the actor's inventories -- @param prop the property to look for -- @param value the value to look for, can be a function +-- @return object, position, inven_id or nil if not found function _M:findInAllInventoriesBy(prop, value) for inven_id, inven in pairs(self.inven) do local o, item = self:findInInventoryBy(inven, prop, value) @@ -509,19 +632,20 @@ function _M:findInAllInventoriesBy(prop, value) end end ---- Finds an object by property in an inventory +--- Finds an object by reference in an inventory -- @param inven the inventory to look into --- @param prop the property to look for --- @param value the value to look for, can be a function +-- @param so the object(reference) to look for +-- @return object, position or nil if not found function _M:findInInventoryByObject(inven, so) for item, o in ipairs(inven) do if o == so then return o, item end end end ---- Finds an object by property in all the actor's inventories --- @param prop the property to look for --- @param value the value to look for, can be a function +--- Finds an object by reference in all the actor's inventories +-- @param inven the inventory to look into +-- @param so the object(reference) to look for +-- @return object, position, inven_id or nil if not found function _M:findInAllInventoriesByObject(so) for inven_id, inven in pairs(self.inven) do local o, item = self:findInInventoryByObject(inven, so) diff --git a/game/engines/default/engine/interface/ActorProject.lua b/game/engines/default/engine/interface/ActorProject.lua index 5c7df29b024f84ea839480f3daf411286c067781..5f45b8376ffd7af7a85bca9af4e72cfbeab4ce24 100644 --- a/game/engines/default/engine/interface/ActorProject.lua +++ b/game/engines/default/engine/interface/ActorProject.lua @@ -34,7 +34,7 @@ end -- @param t a type table describing the attack, passed to engine.Target:getType() for interpretation -- @param x target coords -- @param y target coords --- @param damtype a damage type ID from the DamageType class +-- @param damtype a damage type ID from the DamageType class or a function to be called as damtype(px, py, t, self) on each grid -- @param dam damage to be done -- @param particles particles effect configuration, or nil function _M:project(t, x, y, damtype, dam, particles) @@ -178,6 +178,17 @@ function _M:project(t, x, y, damtype, dam, particles) return end + --Remove any excluded grids + if typ.grid_exclude then + for px, ys in pairs(typ.grid_exclude) do + if grids[px] then + for py, _ in pairs(ys) do + grids[px][py]=nil + end + end + end + end + self:check("on_project_grids", grids) -- Now project on each grid, one type @@ -188,9 +199,9 @@ function _M:project(t, x, y, damtype, dam, particles) for py, _ in pairs(ys) do -- Call the projected method of the target grid if possible if not game.level.map:checkAllEntities(px, py, "projected", self, t, px, py, damtype, dam, particles) then - -- Check self- and friendly-fire, and if the projection "misses" + -- Check self- and friendly-fire, excluded Actors, and if the projection "misses" local act = game.level.map(px, py, engine.Map.ACTOR) - if act and act == self and not ((type(typ.selffire) == "number" and rng.percent(typ.selffire)) or (type(typ.selffire) ~= "number" and typ.selffire)) then + if act and (typ.act_exclude and typ.act_exclude[act.uid]) or act == self and not ((type(typ.selffire) == "number" and rng.percent(typ.selffire)) or (type(typ.selffire) ~= "number" and typ.selffire)) then elseif act and self.reactionToward and (self:reactionToward(act) >= 0) and not ((type(typ.friendlyfire) == "number" and rng.percent(typ.friendlyfire)) or (type(typ.friendlyfire) ~= "number" and typ.friendlyfire)) then -- Otherwise hit else @@ -307,7 +318,7 @@ function _M:projectile(t, x, y, damtype, dam, particles) typ.line_function:set_corner_block(block_corner) - local proj = require(self.projectile_class):makeProject(self, t.display, {x=x, y=y, start_x=typ.start_x, start_y=typ.start_y, damtype=damtype, tg=t, typ=typ, dam=dam, particles=particles}) + local proj = require(self.projectile_class):makeProject(self, t.display, {x=x, y=y, start_x=typ.start_x, start_y=typ.start_y, damtype=damtype, tg=t, typ=typ, dam=dam, particles=particles, _allow_upvalues = true,}) game.zone:addEntity(game.level, proj, "projectile", typ.start_x, typ.start_y) self:check("on_projectile_fired", proj, typ, x, y, damtype, dam, particles) diff --git a/game/engines/default/engine/interface/ActorTalents.lua b/game/engines/default/engine/interface/ActorTalents.lua index b0481d12f3fc4ceedc7f5bf259eb1083029f1ff8..a055f4dda57ba799dc27a371aaa1b2bc9501e2a4 100644 --- a/game/engines/default/engine/interface/ActorTalents.lua +++ b/game/engines/default/engine/interface/ActorTalents.lua @@ -45,6 +45,7 @@ end --- Defines one talent type(group) -- Static! function _M:newTalentType(t) + t.__ATOMIC = true assert(t.name, "no talent type name") assert(t.type, "no talent type type") t.description = t.description or "" @@ -57,6 +58,7 @@ end --- Defines one talent -- Static! function _M:newTalent(t) + t.__ATOMIC = true assert(t.name, "no talent name") assert(t.type, "no or unknown talent type") if type(t.type) == "string" then t.type = {t.type, 1} end @@ -116,7 +118,7 @@ end -- Make the actor use the talent -- @param id talent ID -- @param who talent user --- @param force_level talent level(raw) override +-- @param force_level talent level(raw) override -- @param ignore_cd do not affect or consider cooldown -- @param force_target the target of the talent (override) -- @param silent do not display messages about use @@ -325,7 +327,7 @@ function _M:learnTalent(t_id, force, nb) if not self.talents[t_id] then -- Auto assign to hotkey - if t.mode ~= "passive" and self.hotkey then + if t.mode ~= "passive" and not t.no_auto_hotkey and self.hotkey then local position if self.player then @@ -355,7 +357,7 @@ function _M:learnTalent(t_id, force, nb) for i = 1, (nb or 1) do self.talents[t_id] = (self.talents[t_id] or 0) + 1 - if t.on_learn then + if t.on_learn then local ret = t.on_learn(self, t) if ret then if ret == true then ret = {} end @@ -365,7 +367,7 @@ function _M:learnTalent(t_id, force, nb) end end - if t.passives then + if t.passives then self.talents_learn_vals[t.id] = self.talents_learn_vals[t.id] or {} local p = self.talents_learn_vals[t.id] @@ -411,7 +413,7 @@ function _M:unlearnTalent(t_id, nb) self.talents[t_id] = self.talents[t_id] - 1 if self.talents[t_id] == 0 then self.talents[t_id] = nil end - if t.on_unlearn then + if t.on_unlearn then local p = nil if self.talents_learn_vals[t.id] and self.talents_learn_vals[t.id][(self.talents[t_id] or 0) + 1] then p = self.talents_learn_vals[t.id][(self.talents[t_id] or 0) + 1] @@ -425,7 +427,7 @@ function _M:unlearnTalent(t_id, nb) end end - if t.passives then + if t.passives then self.talents_learn_vals[t.id] = self.talents_learn_vals[t.id] or {} local p = self.talents_learn_vals[t.id] @@ -451,7 +453,7 @@ end function _M:updateTalentPassives(tid) if not self:knowTalent(tid) then return end - local t = self:getTalentFromId(tid) + local t = self:getTalentFromId(tid) if not t.passives then return end self.talents_learn_vals[t.id] = self.talents_learn_vals[t.id] or {} @@ -700,10 +702,12 @@ end --- Starts a talent cooldown -- @param t the talent to cooldown -function _M:startTalentCooldown(t) +-- @param v override the normal cooldown that that, nil to get the normal effect +function _M:startTalentCooldown(t, v) t = self:getTalentFromId(t) - if not t.cooldown then return end local cd = t.cooldown + if v then cd = math.max(v, self.talents_cd[t.id] or 0) end + if not cd then return end if type(cd) == "function" then cd = cd(self, t) end self.talents_cd[t.id] = cd self.changed = true @@ -859,4 +863,36 @@ function _M:talentCallbackOn(on, ...) self:callTalent(tid, on, ...) end end -end \ No newline at end of file +end + +local dialog_returns_list = setmetatable({}, {__mode="v"}) +local dialog_returns = setmetatable({}, {__mode="k"}) + +--- Set the result for a talent dialog +function _M:talentDialogReturn(...) + local d = dialog_returns_list[#dialog_returns_list] + if not d then return end + + dialog_returns[d] = {...} +end + +--- Get the dialog +function _M:talentDialogGet() + return dialog_returns_list[#dialog_returns_list] +end + +--- Show a dialog and wait for it to end in a talent +function _M:talentDialog(d) + if not game:hasDialog(d) then game:registerDialog(d) end + + dialog_returns_list[#dialog_returns_list+1] = d + + local co = coroutine.running() + d.unload = function(self) coroutine.resume(co, dialog_returns[d]) end + local ret = coroutine.yield() + + dialog_returns[d] = nil + table.removeFromList(dialog_returns_list, d) + + return unpack(ret) +end diff --git a/game/engines/default/engine/interface/ActorTemporaryEffects.lua b/game/engines/default/engine/interface/ActorTemporaryEffects.lua index 08da07ba604d5b8c5cc97932942243cbdeb2f168..be6d0936277b27eeb0bae9cc568a496534dc6470 100644 --- a/game/engines/default/engine/interface/ActorTemporaryEffects.lua +++ b/game/engines/default/engine/interface/ActorTemporaryEffects.lua @@ -106,7 +106,7 @@ function _M:setEffect(eff_id, dur, p, silent) end p.dur = dur p.effect_id = eff_id - self:check("on_set_temporary_effect", eff_id, _M.tempeffect_def[eff_id], p) + if self:check("on_set_temporary_effect", eff_id, _M.tempeffect_def[eff_id], p) then return end if p.dur <= 0 then return self:removeEffect(eff_id) end -- If we already have it, we check if it knows how to "merge", or else we remove it and re-add it @@ -182,7 +182,7 @@ function _M:copyEffect(eff_id) param.__tmpvals = nil return param -end +end --- Removes the effect function _M:removeAllEffects() diff --git a/game/engines/default/engine/ui/Dialog.lua b/game/engines/default/engine/ui/Dialog.lua index 585cbdd93e427b33419d27664075a9044cf69e19..d30a84f373bdb2209b15ae35701bc87dd7f60bb9 100644 --- a/game/engines/default/engine/ui/Dialog.lua +++ b/game/engines/default/engine/ui/Dialog.lua @@ -60,8 +60,8 @@ function _M:listPopup(title, text, list, w, h, fct) {left = 3, top = 3 + desc.h + 3, ui=require("engine.ui.Separator").new{dir="vertical", size=w - 12}}, {left = 3, bottom = 3, ui=l}, } - d.key:addBind("EXIT", function() game:unregisterDialog(d) if fct then fct() end end) - d.key:addBind("ACCEPT", function() game:unregisterDialog(d) if list[l.sel].fct then list[l.sel].fct(list[l.sel]) return end if fct then fct(list[l.sel]) end end) + d.key:addBind("EXIT", function() if fct then fct() end game:unregisterDialog(d) end) + d.key:addBind("ACCEPT", function() if list[l.sel].fct then list[l.sel].fct(list[l.sel]) return end if fct then fct(list[l.sel]) end game:unregisterDialog(d) end) d:setFocus(l) d:setupUI(true, true) game:registerDialog(d) diff --git a/game/engines/default/engine/ui/EquipDoll.lua b/game/engines/default/engine/ui/EquipDoll.lua index 0fbca59dd191d54187b1516561ae4089931fed4e..4265dab51c931ffccf1770f1607e11adcfe2159b 100644 --- a/game/engines/default/engine/ui/EquipDoll.lua +++ b/game/engines/default/engine/ui/EquipDoll.lua @@ -33,6 +33,7 @@ function _M:init(t) self.actorWear = t.actorWear self.filter = t.filter self.subobject = t.subobject + self.subobject_restrict_slots = t.subobject_restrict_slots self.focus_ui = nil Base.init(self, t) @@ -123,13 +124,34 @@ function _M:generateEquipDollFrames() if inven then for item, def in ipairs(v) do if item > inven.max then break end - local frame = EquipDollFrame.new{actor=self.actor, inven=inven, name_pos=def.text, item=item, w=doll.w, h=doll.h, iw=doll.iw, ih=doll.ih, ix=doll.ix, iy=doll.iy, bg=doll.itemframe, bg_sel=doll.itemframe_sel, bg_empty=self.actor.inven_def[inven.name].infos and self.actor.inven_def[inven.name].infos.equipdoll_back, drag_enable=self.drag_enable, subobject=self.subobject} + + local frame = EquipDollFrame.new{actor=self.actor, inven=inven, name_pos=def.text, item=item, w=doll.w, h=doll.h, iw=doll.iw, ih=doll.ih, ix=doll.ix, iy=doll.iy, bg=doll.itemframe, bg_sel=doll.itemframe_sel, bg_empty=self.actor.inven_def[inven.name].infos and self.actor.inven_def[inven.name].infos.equipdoll_back, drag_enable=self.drag_enable} frame.doll_select = true frame.actorWear = function(_, ...) if self.actorWear then self.actorWear(frame, ...) end end frame.fct=function(button, event) if frame:getItem() and self.fct then self.fct({inven=inven, item=item, object=frame:getItem()}, button, event) end end frame.filter = self.filter frame.on_focus_change=function(status) local ui = self.focus_ui if self.on_select and ui then self.on_select(ui, ui.ui.inven, ui.ui.item, ui.ui:getItem()) end end uis[#uis+1] = {x=def.x, y=def.y, ui=frame, _weight=def.weight} + + if self.subobject and (not self.subobject_restrict_slots or (self.subobject_restrict_slots[inven.name] and self.subobject_restrict_slots[inven.name] >= item)) then + local frame = EquipDollFrame.new{actor=self.actor, inven=inven, name_pos=def.text, item=item, w=math.ceil(doll.w/2), h=math.ceil(doll.h/2), iw=math.ceil(doll.iw/2), ih=math.ceil(doll.ih/2), ix=math.floor(doll.ix/2), iy=math.floor(doll.iy/2), bg=doll.itemframe, bg_sel=doll.itemframe_sel, bg_empty=self.actor.inven_def[inven.name].infos and self.actor.inven_def[inven.name].infos.equipdoll_back, drag_enable=self.drag_enable, subobject=self.subobject} + frame.doll_select = true + frame.secondary = true + frame.no_name = true + frame.actorWear = function(_, ...) if self.actorWear then self.actorWear(frame, ...) end end + frame.fct=function(button, event) if frame:getItem() and self.fct then self.fct({inven=inven, item=item, object=frame:getItem()}, button, event) end end + frame.filter = self.filter + frame.on_focus_change=function(status) local ui = self.focus_ui if self.on_select and ui then self.on_select(ui, ui.ui.inven, ui.ui.item, ui.ui:getItem()) end end + + local dsx, dsy = doll.w + 3, 0 + if def.subshift == "up" then dsx, dsy = 0, -math.ceil(doll.h/2) - 3 + elseif def.subshift == "bottom" then dsx, dsy = 0, doll.h + 3 + elseif def.subshift == "left" then dsx, dsy = -math.ceil(doll.w/2) - 3, 0 + end + + uis[#uis+1] = {x=def.x + dsx, y=def.y + dsy, ui=frame, _weight=def.weight} + end + max_w = math.max(def.x, max_w) max_h = math.max(def.y, max_h) end diff --git a/game/engines/default/engine/ui/EquipDollFrame.lua b/game/engines/default/engine/ui/EquipDollFrame.lua index 144a8023a3b013968b61b61d3276df8b21e65586..d23de2f9ad4ae85e467bd07e69aa0fec1de6998f 100644 --- a/game/engines/default/engine/ui/EquipDollFrame.lua +++ b/game/engines/default/engine/ui/EquipDollFrame.lua @@ -105,6 +105,7 @@ end function _M:drawItemShortName(o, x, y) if not o then return end + if self.no_name then return end local t = nil if self.last_o == o then @@ -137,16 +138,16 @@ end function _M:display(x, y, nb_keyframes, ox, oy) if self.focused then - self.bg_sel.t:toScreenFull(x, y, self.w, self.h, self.bg_sel.tw, self.bg_sel.th) + self.bg_sel.t:toScreenPrecise(x, y, self.w, self.h, 0, self.bg_sel.w/self.bg_sel.tw, 0, self.bg_sel.h/self.bg_sel.th) else - self.bg.t:toScreenFull(x, y, self.w, self.h, self.bg.tw, self.bg.th) + self.bg.t:toScreenPrecise(x, y, self.w, self.h, 0, self.bg.w/self.bg.tw, 0, self.bg.h/self.bg.th) end local o = self:getItem() if o and o.toScreen then o:toScreen(nil, x + self.f_ix, y + self.f_iy, self.f_iw, self.f_ih) elseif self.bg_empty then - self.bg_empty.t:toScreenFull(x + self.f_ix, y + self.f_iy, self.f_iw, self.f_ih, self.bg_empty.tw, self.bg_empty.th) + self.bg_empty.t:toScreenPrecise(x + self.f_ix, y + self.f_iy, self.f_iw, self.f_ih, 0, self.bg_empty.w/self.bg_empty.tw, 0, self.bg_empty.h/self.bg_empty.th) end self:drawItemShortName(o, x, y) diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua index eb432ea61fbe95db992faa81f3f5d4fc2444b283..830a95f9eacbfe9be0176144b7ecd941d13cd6e5 100644 --- a/game/engines/default/engine/utils.lua +++ b/game/engines/default/engine/utils.lua @@ -70,12 +70,20 @@ function table.max(t) return m end +function table.print_shallow(src, offset, ret) + if type(src) ~= "table" then print("table.print has no table:", src) return end + offset = offset or "" + for k, e in pairs(src) do + print(("%s[%s] = %s"):format(offset, tostring(k), tostring(e))) + end +end + function table.print(src, offset, ret) if type(src) ~= "table" then print("table.print has no table:", src) return end offset = offset or "" for k, e in pairs(src) do -- Deep copy subtables, but not objects! - if type(e) == "table" and not e.__CLASSNAME then + if type(e) == "table" and not e.__ATOMIC then print(("%s[%s] = {"):format(offset, tostring(k))) table.print(e, offset.." ") print(("%s}"):format(offset)) @@ -89,7 +97,7 @@ function table.iprint(src, offset) offset = offset or "" for k, e in ipairs(src) do -- Deep copy subtables, but not objects! - if type(e) == "table" and not e.__CLASSNAME then + if type(e) == "table" and not e.__ATOMIC then print(("%s[%s] = {"):format(offset, tostring(k))) table.print(e, offset.." ") print(("%s}"):format(offset)) @@ -127,7 +135,7 @@ function table.clone(tbl, deep, k_skip) for k, e in pairs(tbl) do if not k_skip[k] then -- Deep copy subtables, but not objects! - if deep and type(e) == "table" and not e.__CLASSNAME then + if deep and type(e) == "table" and not e.__ATOMIC then n[k] = table.clone(e, true, k_skip) else n[k] = e @@ -153,10 +161,10 @@ function table.merge(dst, src, deep, k_skip, k_skip_deep, addnumbers) for k, e in pairs(src) do if not k_skip[k] and not k_skip_deep[k] then -- Recursively merge tables - if deep and dst[k] and type(e) == "table" and type(dst[k]) == "table" and not e.__CLASSNAME then + if deep and dst[k] and type(e) == "table" and type(dst[k]) == "table" and not e.__ATOMIC then table.merge(dst[k], e, deep, nil, k_skip_deep, addnumbers) -- Clone tables if into the destination - elseif deep and not dst[k] and type(e) == "table" and not e.__CLASSNAME then + elseif deep and not dst[k] and type(e) == "table" and not e.__ATOMIC then dst[k] = table.clone(e, deep, nil, k_skip_deep) -- Nil out any NIL_MERGE entries elseif e == table.NIL_MERGE then @@ -180,7 +188,7 @@ function table.mergeAppendArray(dst, src, deep, k_skip, k_skip_deep, addnumbers) k_skip[i] = true local b = src[i] if deep and type(b) == "table" then - if b.__CLASSNAME then + if b.__ATOMIC then b = b:clone() else b = table.clone(b, true) @@ -255,22 +263,24 @@ function table.removeFromList(t, ...) end end -function table.check(t, fct) +function table.check(t, fct, do_recurse, path) + if path and path ~= '' then path = path..'/' else path = '' end + do_recurse = do_recurse or function() return true end for k, e in pairs(t) do local tk, te = type(k), type(e) - if te == "table" and not e.__CLASSNAME then - local ok, err = table.check(e, fct) + if te == "table" and not e.__ATOMIC and do_recurse(e) then + local ok, err = table.check(e, fct, do_recurse, path..tostring(k)) if not ok then return nil, err end else - local ok, err = fct(t, "value["..tostring(k).."]", e, te) + local ok, err = fct(t, path..tostring(k), e, te) if not ok then return nil, err end end - - if tk == "table" and not k.__CLASSNAME then - local ok, err = table.check(k, fct) + + if tk == "table" and not k.__ATOMIC and do_recurse(k) then + local ok, err = table.check(k, fct, do_recurse, path..tostring(k)) if not ok then return nil, err end else - local ok, err = fct(t, "key", k, tk) + local ok, err = fct(t, path.."<key>", k, tk) if not ok then return nil, err end end end @@ -283,9 +293,9 @@ end -- @param deep Boolean that determines if tables will be recursively merged. function table.update(dst, src, deep) for k, e in pairs(src) do - if deep and dst[k] and type(e) == "table" and type(dst[k]) == "table" and not e.__CLASSNAME then + if deep and dst[k] and type(e) == "table" and type(dst[k]) == "table" and not e.__ATOMIC then table.update(dst[k], e, deep) - elseif deep and not dst[k] and type(e) == "table" and not e.__CLASSNAME then + elseif deep and not dst[k] and type(e) == "table" and not e.__ATOMIC then dst[k] = table.clone(e, deep) elseif not dst[k] and type(dst[k]) ~= "boolean" then dst[k] = e @@ -490,7 +500,7 @@ end table.rules.overwrite = function(dvalue, svalue, key, dst) if svalue == table.NIL_MERGE then svalue = nil - elseif type(svalue) == 'table' and not svalue.__CLASSNAME then + elseif type(svalue) == 'table' and not svalue.__ATOMIC then svalue = table.clone(svalue, true) end dst[key] = svalue @@ -499,7 +509,7 @@ end -- Does the recursion. table.rules.recurse = function(dvalue, svalue, key, dst, src, rules, state) if type(svalue) ~= 'table' or - svalue.__CLASSNAME or + svalue.__ATOMIC or (type(dvalue) ~= 'table' and svalue == table.NIL_MERGE) then return end if type(dvalue) ~= 'table' then @@ -516,7 +526,7 @@ end table.rules.append = function(dvalue, svalue, key, dst, src, rules, state) if type(key) ~= 'number' then return end if type(svalue) == 'table' then - if svalue.__CLASSNAME then + if svalue.__ATOMIC then svalue = svalue:clone() else svalue = table.clone(svalue, true) @@ -530,7 +540,7 @@ table.rules.append_top = function(dvalue, svalue, key, dst, src, rules, state) if state.path and #state.path > 0 then return end if type(key) ~= 'number' then return end if type(svalue) == 'table' then - if svalue.__CLASSNAME then + if svalue.__ATOMIC then svalue = svalue:clone() else svalue = table.clone(svalue, true) @@ -586,6 +596,13 @@ function string.a_an(str) else return "a "..str end end +function string.he_she(actor) + if actor.female then return "she" + elseif actor.neuter then return "it" + else return "he" + end +end + function string.his_her(actor) if actor.female then return "her" elseif actor.neuter then return "it" @@ -593,6 +610,13 @@ function string.his_her(actor) end end +function string.him_her(actor) + if actor.female then return "her" + elseif actor.neuter then return "it" + else return "him" + end +end + function string.his_her_self(actor) if actor.female then return "herself" elseif actor.neuter then return "itself" @@ -988,6 +1012,20 @@ function core.display.loadImage(path) return oldloadimage(path) end +function fs.iterate(path, filter) + local list = fs.list(path) + if filter then + for i = #list, 1, -1 do if not filter(list[i]) then + table.remove(list, i) + end end + end + local i = 0 + return function() + i = i + 1 + return list[i] + end +end + local oldfsexists = fs.exists function fs.exists(path) if virtualimages[path] then return true end @@ -1830,8 +1868,8 @@ function util.findAllReferences(t, what) if type(e) == "function" then local fenv = getfenv(e) local data = table.clone(data) - if fenv.__CLASSNAME then - data[#data+1] = "e:fenv["..fenv.__CLASSNAME"]:"..tostring(k) + if fenv.__ATOMIC then + data[#data+1] = "e:fenv["..(fenv.__CLASSNAME or true)"]:"..tostring(k) else data[#data+1] = "e:fenv[--]:"..tostring(k) end @@ -2306,6 +2344,7 @@ function util.removeForceSafeBoot() end -- Alias os.exit to our own exit method for cleanliness +os.crash = os.exit os.exit = core.game.exit_engine -- Ultra weird, this is used by the C serialization code because I'm too dumb to make lua_dump() work on windows ... diff --git a/game/engines/default/engine/version.lua b/game/engines/default/engine/version.lua index e7272a850ca8fcbc8d02355b6fb812dfc7e26a88..9a9fc4a80697aa06c2610a18afa2b75e28469ad1 100644 --- a/game/engines/default/engine/version.lua +++ b/game/engines/default/engine/version.lua @@ -18,7 +18,7 @@ -- darkgod@te4.org -- Engine Version -engine.version = {1,2,3,"te4",17} +engine.version = {1,2,4,"te4",17} engine.require_c_core = engine.version[5] engine.version_id = ("%s-%d_%d.%d.%d"):format(engine.version[4], engine.require_c_core, engine.version[1], engine.version[2], engine.version[3]) diff --git a/game/engines/default/modules/boot/class/Game.lua b/game/engines/default/modules/boot/class/Game.lua index e30648cc8daf9c327c6e1b977685fbde9a01282d..7c451060a406b8499e5e8714af1b11e3584aa74d 100644 --- a/game/engines/default/modules/boot/class/Game.lua +++ b/game/engines/default/modules/boot/class/Game.lua @@ -28,6 +28,7 @@ local Module = require "engine.Module" local Dialog = require "engine.ui.Dialog" local Tooltip = require "engine.Tooltip" local MainMenu = require "mod.dialogs.MainMenu" +local Downloader = require "engine.dialogs.Downloader" local Shader = require "engine.Shader" local Zone = require "engine.Zone" @@ -35,6 +36,7 @@ local Map = require "engine.Map" local Level = require "engine.Level" local LogDisplay = require "engine.LogDisplay" local FlyingText = require "engine.FlyingText" +local FontPackage = require "engine.FontPackage" local NicerTiles = require "mod.class.NicerTiles" local Grid = require "mod.class.Grid" @@ -51,24 +53,31 @@ function _M:init() engine.interface.GameMusic.init(self) engine.interface.GameSound.init(self) engine.GameEnergyBased.init(self, engine.KeyBind.new(), 100, 100) - self.profile_font = core.display.newFont("/data/font/DroidSerif-Italic.ttf", 14) + self.profile_font = FontPackage:get("default") local background_name if not config.settings.censor_boot then background_name = {"tome","tome2","tome3"} else background_name = {"tome3"} end - + + local value = {name=background_name} + local hd = {"Boot:loadBackground", value=value} + if self:triggerHook(hd) then background_name = hd.value.name end + self.background = core.display.loadImage("/data/gfx/background/"..util.getval(background_name)..".png") if self.background then self.background_w, self.background_h = self.background:getSize() self.background, self.background_tw, self.background_th = self.background:glTexture() end + self:handleEvents() + if not profile.connected then core.webview, core.webview_inactive = nil, core.webview end if not core.webview then self.tooltip = Tooltip.new(nil, 14, nil, colors.DARK_GREY, 380) end -- self.refuse_threads = true self.normal_key = self.key - self.stopped = config.settings.boot_menu_background + -- self.stopped = config.settings.boot_menu_background + self.stopped = true if core.display.safeMode() then self.stopped = true end if self.stopped then core.game.setRealtime(0) @@ -86,18 +95,22 @@ function _M:loaded() engine.interface.GameSound.loaded(self) end +function _M:makeWebtooltip() + self.webtooltip = require("engine.ui.WebView").new{width=380, height=500, has_frame=true, never_clean=true, allow_popup=true, + url = ("http://te4.org/tooltip-ingame?steam=%d&vM=%d&vm=%d&vp=%d"):format(core.steam and 1 or 0, engine.version[1], engine.version[2], engine.version[3]) + } + if self.webtooltip.unusable then + self.webtooltip = nil + self.tooltip = Tooltip.new(nil, 14, nil, colors.DARK_GREY, 380) + end +end + function _M:run() self:triggerHook{"Boot:run"} -- Web Tooltip? if core.webview then - self.webtooltip = require("engine.ui.WebView").new{width=380, height=500, has_frame=true, never_clean=true, allow_popup=true, - url = ("http://te4.org/tooltip-ingame?steam=%d&vM=%d&vm=%d&vp=%d"):format(core.steam and 1 or 0, engine.version[1], engine.version[2], engine.version[3]) - } - if self.webtooltip.unusable then - self.webtooltip = nil - self.tooltip = Tooltip.new(nil, 14, nil, colors.DARK_GREY, 380) - end + self:makeWebtooltip() end self.flyers = FlyingText.new() @@ -203,13 +216,15 @@ A usual problem is shaders and thus should be your first target to disable.]], 7 end function _M:grabAddons() + if config.settings.no_auto_update_addons then return end + if core.steam then self.updating_addons = {} - self.logdisplay("#{italic}##ROYAL_BLUe#Retrieving addons to update/download from Steam...#{normal}#") + self.logdisplay("#{italic}##ROYAL_BLUE#Retrieving addons to update/download from Steam...#{normal}#") core.steam.grabSubscribedAddons(function(mode, teaa, title) if mode == "end" then self.updating_addons = nil - self.logdisplay("#{italic}##ROYAL_BLUe#Addons update finished.#{normal}#") + self.logdisplay("#{italic}##ROYAL_BLUE#Addons update finished.#{normal}#") return end @@ -222,6 +237,57 @@ function _M:grabAddons() self.logdisplay("#{italic}#Download of #LIGHT_RED#%s#LAST# failed.#{normal}#", self.updating_addons[teaa] or "???") end end) + else + if not core.webview then return end + self.logdisplay("#{italic}##ROYAL_BLUE#Retrieving addons to update/download from te4.org...#{normal}#") + local mlist = Module:listModules(true) + list = {} + for i = 1, #mlist do + for j, mod in ipairs(mlist[i].versions) do + if j > 1 then break end + if not mod.is_boot then + local adds = Module:listAddons(mod, true) + for k, add in ipairs(adds) do + if add.addon_version and add.teaa then + local a = { + long_name = add.long_name, + name = add.for_module..'-'..add.short_name, + version = add.addon_version, + id_dlc = add.id_dlc and add.id_dlc[1], + file = add.teaa, + } + table.insert(list, a) + list[a.name] = a + end + end + end + end + end + table.print(list) + local update_list = profile:checkAddonUpdates(list) + if update_list then + local co co = coroutine.create(function() + for i, add in ipairs(update_list) do + if core.webview then + local d = Downloader.new{title="Updating addon: #LIGHT_GREEN#"..list[add.name].long_name, co=co, dest=add.file..".tmp", url=add.download_url, allow_downloads={addons=true}} + local ok = d:start() + if ok then + local wdir = fs.getWritePath() + local _, _, dir, name = add.file:find("(.+)/([^/]+)$") + if dir then + fs.setWritePath(fs.getRealPath(dir)) + fs.delete(name) + fs.rename(name..".tmp", name) + fs.setWritePath(wdir) + self.logdisplay("#{italic}#Download of #LIGHT_GREEN#%s#LAST# finished.#{normal}#", list[add.name].long_name) + end + end + end + end + self.logdisplay("#{italic}##ROYAL_BLUE#Addons update finished.#{normal}#") + end) + coroutine.resume(co) + end end end @@ -385,6 +451,12 @@ function _M:display(nb_keyframes) if w > h then h = w * self.background_h / self.background_w y = (self.h - h) / 2 + if h < self.h then + h = self.h + w = h * self.background_w / self.background_h + x = (self.w - w) / 2 + y = 0 + end else w = h * self.background_w / self.background_h x = (self.w - w) / 2 @@ -558,5 +630,17 @@ function _M:handleProfileEvent(evt) d:on_recover_focus() end end + if evt and evt.e == "Connected" then + if core.webview_inactive then + core.webview, core.webview_inactive = core.webview_inactive, nil + self.tooltip = nil + self:makeWebtooltip() + + local d = self.dialogs[#self.dialogs] + if d and d.__CLASSNAME == "mod.dialogs.MainMenu" then + d:on_recover_focus() + end + end + end return evt end diff --git a/game/engines/default/modules/boot/dialogs/Addons.lua b/game/engines/default/modules/boot/dialogs/Addons.lua index d2b2018a580ca83a9dfe2d6f8fc3765337aa64d7..3c14b84a935d4fac46bb637499fd854737849378 100644 --- a/game/engines/default/modules/boot/dialogs/Addons.lua +++ b/game/engines/default/modules/boot/dialogs/Addons.lua @@ -50,6 +50,7 @@ function _M:init() end self.c_compat = Checkbox.new{default=false, width=math.floor(self.iw / 3 - 40), title="Show incompatible", on_change=function() self:switch() end} + self.c_auto_update = Checkbox.new{default=not config.settings.no_auto_update_addons, width=math.floor(self.iw / 3 - 40), title="Auto-update on start", on_change=function() self:switchAuto() end} self:generateList() @@ -82,6 +83,7 @@ function _M:init() {left=0, top=url1.h, ui=self.c_list}, {left=self.c_list.w+sep.w, top=url1.h, ui=self.c_adds}, {left=0, bottom=0, ui=self.c_compat}, + {left=self.c_list.w-self.c_auto_update.w, bottom=0, ui=self.c_auto_update}, {left=self.c_list.w + 5, top=5+url1.h, ui=sep}, } self:setFocus(self.c_list) @@ -150,6 +152,11 @@ function _M:switch() self.c_list:setList(self.list) end +function _M:switchAuto() + config.settings.no_auto_update_addons = not self.c_auto_update.checked + game:saveSettings("no_auto_update_addons", ("no_auto_update_addons = %s"):format(config.settings.no_auto_update_addons and "true" or "false")) +end + function _M:unload() game:grabAddons() end diff --git a/game/engines/default/modules/boot/dialogs/Credits.lua b/game/engines/default/modules/boot/dialogs/Credits.lua index e3c94d659862b7577c56f4c710ffcc773d4b82b2..4d0db80acb85f757e72a52369093db3e5a639c2c 100644 --- a/game/engines/default/modules/boot/dialogs/Credits.lua +++ b/game/engines/default/modules/boot/dialogs/Credits.lua @@ -20,12 +20,13 @@ require "engine.class" local Dialog = require "engine.ui.Dialog" local Shader = require "engine.Shader" +local FontPackage = require "engine.FontPackage" module(..., package.seeall, class.inherit(Dialog)) __show_only = true -local title_font = core.display.newFont("/data/font/DroidSans-Bold.ttf", 32) +local title_font = core.display.newFont(FontPackage:getFont("default"), 32) local aura = { Shader.new("awesomeaura", {flameScale=0.6, time_factor=8000}), Shader.new("awesomeaura", {flameScale=0.6, time_factor=8000}), @@ -42,7 +43,7 @@ local fallback_colors = { local outline = Shader.new("textoutline", {}) local credits = { - {img="/data/gfx/background/tome-logo.png"}, + {img="/data/gfx/background/tome-logo.png", offset_x=30}, {"by"}, {img="/data/gfx/background/netcore-logo.png"}, false, @@ -75,8 +76,8 @@ local credits = { {"Graphic Artists", title=2}, {"Assen 'Rexorcorum' Kanev"}, + {"Matt 'Amagad' Hill"}, {"Raymond 'Shockbolt' Gaustadnes"}, - {"Ross 'Daftigod' Raphael"}, false, false, @@ -101,6 +102,7 @@ local credits = { {"Lore Creation and Writing", title=2}, {"Burb Lulls"}, {"Darren Grey"}, + {"David Mott"}, {"Gwai"}, {"Nicolas 'DarkGod' Casalini"}, {"Ron Billingsley"}, @@ -169,6 +171,7 @@ local credits = { {"Font: SVBasicManual: http://www.dafont.com/fr/johan-winge.d757"}, {"Font FSEX300: http://www.fixedsysexcelsior.com/"}, {"Font: square: http://strlen.com/square"}, + {"Font: Salsa: http://www.google.com/fonts/specimen/Salsa"}, } function _M:init() @@ -181,17 +184,20 @@ function _M:init() EXIT = function() game:unregisterDialog(self) end, } + self:triggerHook{"Boot:credits", credits=credits} + self.list = { self:makeEntry(credits[1]) } self.list[1].y = self.list[1].y - self.list[1].h self.next_credit = 2 end -function _M:makeLogo(img) +function _M:makeLogo(img, offx) local txt = {y=game.h} local i, w, h = core.display.loadImage(img) txt._tex, txt._tex_w, txt._tex_h = i:glTexture() txt.w, txt.h = w, h txt.step_h = h + txt.offset_x = offx txt.offset_y = 0 txt.img = true return txt @@ -200,7 +206,7 @@ end function _M:makeEntry(credit) if not credit then return {none=true, y=game.h, h=32, step_h=32, offset_y=0} end - if credit.img then return self:makeLogo(credit.img) end + if credit.img then return self:makeLogo(credit.img, credit.offset_x) end local txt if credit.title then @@ -215,7 +221,7 @@ function _M:makeEntry(credit) txt.step_h = txt.h txt.offset_y = 0 else - local w, h = title_font:size(credit[1]) + 20, 36 + local w, h = title_font:size(credit[1]) + 20, 42 local s = core.display.newSurface(w, h) s:alpha(0) s:drawStringBlended(title_font, credit[1], 10, 0, 255, 255, 255, false) @@ -238,28 +244,28 @@ function _M:displayCredit(txt, x, y) aura[txt.title].shad:use(true) if aura[txt.title].shad.uniQuadSize then aura[txt.title].shad:uniQuadSize(txt.w/txt._tex_w, txt.h/txt._tex_h) end if aura[txt.title].shad.uniTexSize then aura[txt.title].shad:uniTexSize(txt._tex_w, txt._tex_h) end - txt._texf:toScreenPrecise(x, y, txt.w, txt.h, 0, txt.w/txt._tex_w, 0, txt.h/txt._tex_h) + txt._texf:toScreenPrecise(x + (txt.offset_x or 0), y, txt.w, txt.h, 0, txt.w/txt._tex_w, 0, txt.h/txt._tex_h) aura[txt.title].shad:use(false) outline.shad:use(true) outline.shad:uniOutlineSize(0.7, 0.7) outline.shad:uniTextSize(txt._tex_w, txt._tex_h) - txt._tex:toScreenFull(x, y, txt.w, txt.h, txt._tex_w, txt._tex_h) + txt._tex:toScreenFull(x + (txt.offset_x or 0), y, txt.w, txt.h, txt._tex_w, txt._tex_h) outline.shad:use(false) else outline.shad:use(true) outline.shad:uniOutlineSize(0.7, 0.7) outline.shad:uniTextSize(txt._tex_w, txt._tex_h) - txt._tex:toScreenFull(x, y, txt.w, txt.h, txt._tex_w, txt._tex_h) + txt._tex:toScreenFull(x + (txt.offset_x or 0), y, txt.w, txt.h, txt._tex_w, txt._tex_h) outline.shad:use(false) end else - if not txt.img then txt._tex:toScreenFull(x + 3, y + 3, txt.w, txt.h, txt._tex_w, txt._tex_h, 0, 0, 0, 1) end + if not txt.img then txt._tex:toScreenFull(x + 3 + (txt.offset_x or 0), y + 3, txt.w, txt.h, txt._tex_w, txt._tex_h, 0, 0, 0, 1) end if txt.title and not txt.img then local c = fallback_colors[txt.title] - txt._tex:toScreenFull(x, y, txt.w, txt.h, txt._tex_w, txt._tex_h, c.r/255, c.g/255, c.b/255, 1) + txt._tex:toScreenFull(x + (txt.offset_x or 0), y, txt.w, txt.h, txt._tex_w, txt._tex_h, c.r/255, c.g/255, c.b/255, 1) else - txt._tex:toScreenFull(x, y, txt.w, txt.h, txt._tex_w, txt._tex_h) + txt._tex:toScreenFull(x + (txt.offset_x or 0), y, txt.w, txt.h, txt._tex_w, txt._tex_h) end end end diff --git a/game/engines/default/modules/boot/dialogs/MainMenu.lua b/game/engines/default/modules/boot/dialogs/MainMenu.lua index 873d7f9abb7c9d7777daf20172b6f838bf441828..fb38a7474a1754458ca6620ee7b8d67f4feeabcd 100644 --- a/game/engines/default/modules/boot/dialogs/MainMenu.lua +++ b/game/engines/default/modules/boot/dialogs/MainMenu.lua @@ -26,6 +26,7 @@ local Textzone = require "engine.ui.Textzone" local Textbox = require "engine.ui.Textbox" local Separator = require "engine.ui.Separator" local KeyBind = require "engine.KeyBind" +local FontPackage = require "engine.FontPackage" module(..., package.seeall, class.inherit(Dialog)) @@ -62,9 +63,9 @@ function _M:init() -- if config.settings.cheat then l[#l+1] = {name="webtest", fct=function() util.browserOpenUrl("asset://te4/html/test.html") end} end self.c_background = Button.new{text=game.stopped and "Enable background" or "Disable background", fct=function() self:switchBackground() end} - self.c_version = Textzone.new{font={"/data/font/DroidSansMono.ttf", 10}, auto_width=true, auto_height=true, text=("#{bold}##B9E100#T-Engine4 version: %d.%d.%d"):format(engine.version[1], engine.version[2], engine.version[3])} + self.c_version = Textzone.new{font={FontPackage:getFont("default"), 10}, auto_width=true, auto_height=true, text=("#{bold}##B9E100#T-Engine4 version: %d.%d.%d"):format(engine.version[1], engine.version[2], engine.version[3])} - self.c_list = List.new{width=self.iw, nb_items=#self.list, list=self.list, fct=function(item) end, font={"/data/font/DroidSans-Bold.ttf", 16}} + self.c_list = List.new{width=self.iw, nb_items=#self.list, list=self.list, fct=function(item) end, font={FontPackage:getFont("default")}} self.c_facebook = ButtonImage.new{no_decoration=true, alpha_unfocus=0.5, file="facebook.png", fct=function() util.browserOpenUrl("https://www.facebook.com/tales.of.maj.eyal") end} self.c_twitter = ButtonImage.new{no_decoration=true, alpha_unfocus=0.5, file="twitter.png", fct=function() util.browserOpenUrl("https://twitter.com/darkgodone") end} @@ -72,18 +73,14 @@ function _M:init() self.base_uis = { {left=0, top=0, ui=self.c_list}, - {left=0, bottom=0, absolute=true, ui=self.c_background}, + -- {left=0, bottom=0, absolute=true, ui=self.c_background}, {right=self.c_facebook.w, bottom=0, absolute=true, ui=self.c_version}, {right=0, bottom=self.c_facebook.h+self.c_twitter.h, absolute=true, ui=self.c_forums}, {right=0, bottom=self.c_twitter.h, absolute=true, ui=self.c_facebook}, {right=0, bottom=0, absolute=true, ui=self.c_twitter}, } - if core.webview and game.webtooltip then - self.c_tooltip = game.webtooltip - self.base_uis[#self.base_uis+1] = {left=9, top=9, absolute=true, ui=self.c_tooltip} - - end + self:enableWebtooltip() if game.__mod_info.publisher_logo then local c_pub = ButtonImage.new{no_decoration=true, alpha_unfocus=1, file="background/"..game.__mod_info.publisher_logo..".png", fct=function() @@ -97,6 +94,13 @@ function _M:init() self:updateUI() end +function _M:enableWebtooltip() + if core.webview and game.webtooltip and not self.c_tooltip then + self.c_tooltip = game.webtooltip + self.base_uis[#self.base_uis+1] = {left=9, top=9, absolute=true, ui=self.c_tooltip} + end +end + function _M:updateUI() local uis = table.clone(self.base_uis) diff --git a/game/engines/default/modules/boot/init.lua b/game/engines/default/modules/boot/init.lua index 610d5148bab73d676c66c6cd64f04ab50f9e6048..458796d3e39e482870e433e1b34361473d62377c 100644 --- a/game/engines/default/modules/boot/init.lua +++ b/game/engines/default/modules/boot/init.lua @@ -36,3 +36,6 @@ allow_userchat = {"mainmenu"} -- We can talk to the online community but not joi if not config.settings.censor_boot then background_name = {"tome","tome2","tome3"} else background_name = {"tome3"} end + +font_package_id = function() config.settings.tome = config.settings.tome or {} if not config.settings.tome.fonts then config.settings.tome.fonts = {type="fantasy", size="normal"} end return config.settings.tome.fonts.type end +font_package_size = function() config.settings.tome = config.settings.tome or {} if not config.settings.tome.fonts then config.settings.tome.fonts = {type="fantasy", size="normal"} end return config.settings.tome.fonts.size end diff --git a/game/engines/default/modules/boot/load.lua b/game/engines/default/modules/boot/load.lua index a68c1881a04230aea6c1b218b4e09673c27c9c14..a3bf9dbb9ce5e268560a6a91e4e910acdb4c4720 100644 --- a/game/engines/default/modules/boot/load.lua +++ b/game/engines/default/modules/boot/load.lua @@ -31,11 +31,12 @@ local ActorLevel = require "engine.interface.ActorLevel" local ActorTemporaryEffects = require "engine.interface.ActorTemporaryEffects" local Birther = require "engine.Birther" local UIBase = require "engine.ui.Base" +local FontPackage = require "engine.FontPackage" -UIBase.font = core.display.newFont("/data/font/DroidSans.ttf", 16) -UIBase.font_bold = core.display.newFont("/data/font/DroidSans.ttf", 16) -UIBase.font_mono = core.display.newFont("/data/font/DroidSansMono.ttf", 16) -UIBase.font_bold:setStyle("bold") +-- Dialogs fonts +UIBase.font = FontPackage:get("default") +UIBase.font_bold = FontPackage:get("bold") +UIBase.font_mono = FontPackage:get("mono") UIBase.font_h = UIBase.font:lineSkip() UIBase.font_bold_h = UIBase.font_bold:lineSkip() UIBase.font_mono_w = UIBase.font_mono:size(" ") diff --git a/game/modules/tome/ai/summon.lua b/game/modules/tome/ai/summon.lua index 34b5d9e5942308f8339c8da917488db2b25d442f..b777c9e1fce34cc53cda3899e686115dfbd6b24a 100644 --- a/game/modules/tome/ai/summon.lua +++ b/game/modules/tome/ai/summon.lua @@ -26,6 +26,7 @@ newAI("summoned", function(self) game.logPlayer(self.summoner, "#PINK#Your summoned %s disappears.", self.name) end self:die() + self.dead_by_unsummon = true end end diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 36038fbe67a894fd1e2646a27160d04b09bb6e5d..af392548834a96fbd92aff3a63642d6f836510df 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -91,6 +91,7 @@ _M.temporary_values_conf.force_use_resist = "last" _M.temporary_values_conf.force_use_resist_percent = "last" _M.temporary_values_conf.all_damage_convert = "last" _M.temporary_values_conf.all_damage_convert_percent = "last" +_M.temporary_values_conf.force_melee_damtype = "last" -- AI _M.temporary_values_conf.ai_move = "last" @@ -307,6 +308,79 @@ end function _M:runStop() end function _M:restStop() end +function _M:getSpeed(speed_type) + if type(speed_type) == "number" then return speed_type end + + local speed + + if speed_type == "weapon" or speed_type == "mainhand" or + speed_type == "offhand" or speed_type == "combat" + then + if (speed_type == "weapon" or speed_type == "mainhand") and + self:getInven(self.INVEN_MAINHAND) + then + local o = self:getInven(self.INVEN_MAINHAND)[1] + speed = self:combatSpeed(self:getObjectCombat(o, "mainhand")) + end + + if (speed_type == "weapon" or speed_type == "offhand") and + self:getInven(self.INVEN_OFFHAND) + then + local o = self:getInven(self.INVEN_OFFHAND)[1] + speed = math.max(speed or 0, self:combatSpeed(self:getObjectCombat(o, "offhand"))) + end + + if (speed_type == "combat" or speed_type == "weapon") and not speed then + speed = self:combatSpeed() + end + + elseif speed_type == "archery" then + if self:getInven(self.INVEN_MAINHAND) then + local o = self:getInven(self.INVEN_MAINHAND)[1] + if o and o.archery then + speed = self:combatSpeed(self:getObjectCombat(o, "mainhand")) + end + end + + if self:getInven(self.INVEN_OFFHAND) then + local o = self:getInven(self.INVEN_OFFHAND)[1] + if o and o.archery then + speed = math.max(speed or 0, self:combatSpeed(self:getObjectCombat(o, "offhand"))) + end + end + + if not speed then speed = self:combatSpeed() end + + elseif speed_type == "shield" then + if self:getInven(self.INVEN_OFFHAND) then + local o = self:getInven(self.INVEN_OFFHAND)[1] + if o and o.special_combat then + speed = self:combatSpeed(o.special_combat) + end + end + + if self:getInven(self.INVEN_MAINHAND) then + local o = self:getInven(self.INVEN_MAINHAND)[1] + if o and o.special_combat then + speed = math.max(speed or 0, self:combatSpeed(o.special_combat)) + end + end + + if not speed then speed = self:combatSpeed() end + + elseif speed_type == "spell" then speed = self:combatSpellSpeed() + elseif speed_type == "summon" then speed = self:combatSummonSpeed() + elseif speed_type == "mind" then speed = self:combatMindSpeed() + elseif speed_type == "movement" then speed = self:combatMovementSpeed() + elseif speed_type == "standard" then speed = 1 + end + + local hd = {"Actor:getSpeed", speed_type = speed_type, speed = speed,} + if self:triggerHook(hd) then speed = hd.speed end + + return speed or 1 +end + function _M:useEnergy(val) engine.Actor.useEnergy(self, val) @@ -467,7 +541,7 @@ function _M:actBase() if self:knowTalent(self.T_GESTURE_OF_GUARDING) then self:setEffect(self.EFF_GESTURE_OF_GUARDING,1,{}) end if self:knowTalent(self.T_DUAL_WEAPON_DEFENSE) then self:setEffect(self.EFF_DUAL_WEAPON_DEFENSE,1,{}) end - if self:knowTalent(self.T_COUNTER_ATTACK) then self:setEffect(self.EFF_COUNTER_ATTACKING,1,{}) end + if self:knowTalent(self.T_COUNTER_ATTACK) then self:setEffect(self.EFF_COUNTER_ATTACKING,1,{}) end if self:knowTalent(self.T_DEFENSIVE_THROW) then self:setEffect(self.EFF_DEFENSIVE_GRAPPLING,1,{}) end -- Compute timed effects @@ -551,7 +625,7 @@ function _M:actBase() local t, p = self:getTalentFromId(self.T_DREAMFORGE), self:isTalentActive(self.T_DREAMFORGE) t.doForgeStrike(self, t, p) end - + if self:isTalentActive(self.T_TIME_DILATION) then local t, p = self:getTalentFromId(self.T_TIME_DILATION), self:isTalentActive(self.T_TIME_DILATION) t.doTimeDilation(self, t, p) @@ -572,7 +646,7 @@ function _M:actBase() t.do_chargedaura(self, t) end end - + if self:isTalentActive(self.T_BEYOND_THE_FLESH) then local t = self:getTalentFromId(self.T_BEYOND_THE_FLESH) t.do_tkautoattack(self, t) @@ -581,7 +655,7 @@ function _M:actBase() local t = self:getTalentFromId(self.T_MASTERFUL_TELEKINETIC_ARCHERY) t.do_tkautoshoot(self, t) end - + self:triggerHook{"Actor:actBase:Effects"} self:fireTalentCheck("callbackOnActBase") @@ -632,23 +706,6 @@ function _M:act() end end - -- Conduit talent prevents all auras from cooling down - if self:isTalentActive(self.T_CONDUIT) then - local auras = self:isTalentActive(self.T_CONDUIT) - if auras.k_aura_on then - local t_kinetic_aura = self:getTalentFromId(self.T_KINETIC_AURA) - self.talents_cd[self.T_KINETIC_AURA] = t_kinetic_aura.cooldown(self, t) - end - if auras.t_aura_on then - local t_thermal_aura = self:getTalentFromId(self.T_THERMAL_AURA) - self.talents_cd[self.T_THERMAL_AURA] = t_thermal_aura.cooldown(self, t) - end - if auras.c_aura_on then - local t_charged_aura = self:getTalentFromId(self.T_CHARGED_AURA) - self.talents_cd[self.T_CHARGED_AURA] = t_charged_aura.cooldown(self, t) - end - end - if self:attr("paralyzed") then self.paralyzed_counter = (self.paralyzed_counter or 0) + (self:attr("stun_immune") or 0) * 100 if self.paralyzed_counter < 100 then @@ -876,7 +933,7 @@ function _M:alterMakeMapObject(tiles, mo, z, lastmo) local tex, texx, texy, pos_x, pos_y = tiles:get("", 0, 0, 0, 0, 0, 0, "invis.png", false, false, true) submo:texture(0, tex, false, texx, texy, pos_x, pos_y) submo:chain(mo) - if lastmo == mo then lastmo = submo end + -- if lastmo == mo then lastmo = submo end mo = submo self._mo = mo @@ -1253,7 +1310,7 @@ function _M:defineDisplayCallback() for i = 1, #backps do e = backps[i] e:checkDisplay() - if e.ps:isAlive() then e.ps:toScreen(x + w / 2, y + dy + h / 2, true, w / (game.level and game.level.map.tile_w or w)) + if e.ps:isAlive() then e.ps:toScreen(x + w / 2 + (e.dx or 0) * w, y + dy + h / 2 + (e.dy or 0) * h, true, w / (game.level and game.level.map.tile_w or w)) else self:removeParticles(e) end end @@ -1315,10 +1372,6 @@ function _M:move(x, y, force) if not force and moved and (self.x ~= ox or self.y ~= oy) and not self.did_energy then local eff = self:hasEffect(self.EFF_CURSE_OF_SHROUDS) if eff then eff.moved = true end - - if self:knowTalent(self.T_CELERITY) then - self:callTalent(self.T_CELERITY, "doCelerity") - end if self:attr("move_stamina_instead_of_energy") and self:getStamina() > self:attr("move_stamina_instead_of_energy") then self:incStamina(-self:attr("move_stamina_instead_of_energy")) @@ -1380,12 +1433,16 @@ function _M:move(x, y, force) if moved and self:isTalentActive(self.T_BODY_OF_STONE) and not self:attr("preserve_body_of_stone") then self:forceUseTalent(self.T_BODY_OF_STONE, {ignore_energy=true}) end - - -- Chronomancy auras + -- Celerity + if moved and ox and oy and (ox ~= self.x or oy ~= self.y) and self:knowTalent(self.T_CELERITY) then + self:callTalent(self.T_CELERITY, "doCelerity") + end + -- Break channels if moved then self:breakPsionicChannel() + self:breakSpacetimeTuning() end if not force and moved and ox and oy and (ox ~= self.x or oy ~= self.y) and self:knowTalent(self.T_LIGHT_OF_FOOT) then @@ -1396,6 +1453,7 @@ function _M:move(x, y, force) local blur = 0 if game.level.data.zero_gravity then blur = 2 end if self:attr("lightning_speed") or self:attr("step_up") or self:attr("wild_speed") then blur = 3 end + if self:hasEffect(self.EFF_CELERITY) then local eff = self:hasEffect(self.EFF_CELERITY) blur = eff.charges end self:setMoveAnim(ox, oy, config.settings.tome.smooth_move, blur, 8, config.settings.tome.twitch_move and 0.15 or 0) end @@ -1409,7 +1467,7 @@ function _M:move(x, y, force) end end - self:fireTalentCheck("callbackOnMove", moved, force, ox, oy) + self:fireTalentCheck("callbackOnMove", moved, force, ox, oy, x, y) self:triggerHook{"Actor:move", moved=moved, force=force, ox=ox, oy=oy} @@ -1424,6 +1482,16 @@ function _M:waitTurn() self:reloadQS() end end + + -- Tune paradox up or down + if not self:hasEffect(self.EFF_SPACETIME_TUNING) and self.preferred_paradox and (self:getParadox() ~= self:getMinParadox() or self.preferred_paradox > self:getParadox()) then + local power = 0 + if math.abs(self:getParadox() - self.preferred_paradox) > 1 then + local duration = self:callTalent(self.T_SPACETIME_TUNING, "getDuration") + power = (self.preferred_paradox - self:getParadox())/duration + self:setEffect(self.EFF_SPACETIME_TUNING, duration, {power=power}) + end + end self:useEnergy() end @@ -1511,23 +1579,44 @@ function _M:teleportRandom(x, y, dist, min_dist) if game.level.data.no_teleport_south and y + dist > self.y then y = self.y - dist end + + -- Dimensional Anchor, prevent teleports and deal damage if self:hasEffect(self.EFF_DIMENSIONAL_ANCHOR) then local p = self:hasEffect(self.EFF_DIMENSIONAL_ANCHOR) - DamageType:get(DamageType.MATTER).projector(p.src or self, self.x, self.y, DamageType.MATTER, p.damage) + DamageType:get(DamageType.WARP).projector(p.src or self, self.x, self.y, DamageType.WARP, p.damage) return end + local ox, oy = self.x, self.y local ret = engine.Actor.teleportRandom(self, x, y, dist, min_dist) if self.x ~= ox or self.y ~= oy then + -- Phase Pulse + if self:isTalentActive(self.T_PHASE_PULSE) then + self:callTalent(self.T_PHASE_PULSE, "doPulse", ox, oy) + end + self.x, self.y, ox, oy = ox, oy, self.x, self.y self:dropNoTeleportObjects() if self:attr("defense_on_teleport") or self:attr("resist_all_on_teleport") or self:attr("effect_reduction_on_teleport") then self:setEffect(self.EFF_OUT_OF_PHASE, 5, {defense=self:attr("defense_on_teleport") or 0, resists=self:attr("resist_all_on_teleport") or 0, effect_reduction=self:attr("effect_reduction_on_teleport") or 0}) end - if self:knowTalent(self.T_PHASE_SHIFT) then - self:callTalent(self.T_PHASE_SHIFT, "doPhaseShift") + + -- Dimensional shift, chance to clear effects on teleport + if self:knowTalent(self.T_DIMENSIONAL_SHIFT) then + self:callTalent(self.T_DIMENSIONAL_SHIFT, "doShift") end + + -- Teleportation does not clear Time Dilation + if self:isTalentActive(self.T_TIME_DILATION) then + self:callTalent(self.T_TIME_DILATION, "updateOnTeleport", ox, oy) + end + self.x, self.y, ox, oy = ox, oy, self.x, self.y + else + -- Phase Blast failure + if self:isTalentActive(self.T_PHASE_PULSE) then + self:callTalent(self.T_PHASE_PULSE, "doPulse", ox, oy, true) + end end return ret end @@ -1774,15 +1863,15 @@ function _M:tooltip(x, y, seen_by) end --ts:add(("Stats: %d / %d / %d / %d / %d / %d"):format(self:getStr(), self:getDex(), self:getCon(), self:getMag(), self:getWil(), self:getCun()), true) --if #resists > 0 then ts:add("Resists: ", table.concat(resists, ','), true) end - + local resists = tstring{} ts:add({"color", "ANTIQUE_WHITE"}, "Resists: ") for t, v in pairs(self.resists) do - if t == "all" then + if t == "all" then ts:add({"color", "LIGHT_BLUE"}, tostring(math.floor(v)) .. "%", " ", {"color", "LAST"}, "all, ") elseif type(t) == "string" and math.abs(v) >= 20 then local res = tostring ( math.floor(self:combatGetResist(t)) ) .. "%" - if v > 0 then + if v > 0 then ts:add({"color", "LIGHT_GREEN"}, res, " ", {"color", "LAST"}, DamageType:get(t).name, ", ") else ts:add({"color", "LIGHT_RED"}, res, " ", {"color", "LAST"}, DamageType:get(t).name, ", ") @@ -1805,7 +1894,7 @@ function _M:tooltip(x, y, seen_by) ts:add("#FFD700#M. power#FFFFFF#: ", self:colorStats("combatMindpower"), " ") ts:add("#0080FF#M. save#FFFFFF#: ", self:colorStats("combatMentalResist"), true) ts:add({"color", "WHITE"}) - + if (150 + (self.combat_critical_power or 0) ) > 150 then ts:add("Critical Mult: ", ("%d%%"):format(150 + (self.combat_critical_power or 0) ), true ) end @@ -1850,6 +1939,9 @@ function _M:tooltip(x, y, seen_by) if retal > 0 then ts:add("Melee Retaliation: ", {"color", "RED"}, tostring(math.floor(retal)), {"color", "WHITE"}, true ) end if self.desc then ts:add(self.desc, true) end + if config.settings.cheat and self.descriptor and self.descriptor.classes then + ts:add("Classes:", table.concat(self.descriptor.classes or {}, ","), true) + end if self.faction and Faction.factions[self.faction] then ts:add("Faction: ") ts:merge(factcolor:toTString()) ts:add(("%s (%s, %d)"):format(Faction.factions[self.faction].name, factstate, factlevel), {"color", "WHITE"}, true) end if game.player ~= self then ts:add("Personal reaction: ") ts:merge(pfactcolor:toTString()) ts:add(("%s, %d"):format(pfactstate, pfactlevel), {"color", "WHITE"} ) end @@ -1868,23 +1960,34 @@ function _M:tooltip(x, y, seen_by) local effother = tstring{} local effbeneficial = tstring{} + local desceffect = function(e, p, dur) + local dur = e.decrease > 0 and dur or nil + local charges = nil + if e.charges then charges = e.charges and tostring(e.charges(self, p)) end + + if dur and charges then return ("%s(%d, %s)"):format(e.desc, dur, charges) + elseif dur and not charges then return ("%s(%d)"):format(e.desc, dur) + elseif not dur and charges then return ("%s(%s)"):format(e.desc, charges) + else return e.desc end + end + for eff_id, p in pairs(self.tmp) do local e = self.tempeffect_def[eff_id] local dur = p.dur + 1 if e.status == "detrimental" then if e.type == "physical" then - effphysical:add(true, "- ", {"color", "LIGHT_RED"}, (e.decrease > 0) and ("%s(%d)"):format(e.desc,dur) or e.desc, {"color", "WHITE"} ) + effphysical:add(true, "- ", {"color", "LIGHT_RED"}, desceffect(e, p, dur), {"color", "WHITE"} ) elseif e.type == "magical" then - effmagical:add(true, "- ", {"color", "DARK_ORCHID"}, (e.decrease > 0) and ("%s(%d)"):format(e.desc,dur) or e.desc, {"color", "WHITE"} ) + effmagical:add(true, "- ", {"color", "DARK_ORCHID"}, desceffect(e, p, dur), {"color", "WHITE"} ) elseif e.type == "mental" then - effmental:add(true, "- ", {"color", "YELLOW"}, (e.decrease > 0) and ("%s(%d)"):format(e.desc,dur) or e.desc, {"color", "WHITE"} ) + effmental:add(true, "- ", {"color", "YELLOW"}, desceffect(e, p, dur), {"color", "WHITE"} ) elseif e.type == "other" then - effother:add(true, "- ", {"color", "ORCHID"}, (e.decrease > 0) and ("%s(%d)"):format(e.desc,dur) or e.desc, {"color", "WHITE"} ) + effother:add(true, "- ", {"color", "ORCHID"}, desceffect(e, p, dur), {"color", "WHITE"} ) else - ts:add(true, "- ", {"color", "LIGHT_RED"}, (e.decrease > 0) and ("%s(%d)"):format(e.desc,dur) or e.desc, {"color", "WHITE"} ) - end + ts:add(true, "- ", {"color", "LIGHT_RED"}, desceffect(e, p, dur), {"color", "WHITE"} ) + end else - effbeneficial:add(true, "- ", {"color", "LIGHT_GREEN"}, (e.decrease > 0) and ("%s(%d)"):format(e.desc,dur) or e.desc, {"color", "WHITE"} ) + effbeneficial:add(true, "- ", {"color", "LIGHT_GREEN"}, desceffect(e, p, dur), {"color", "WHITE"} ) end end @@ -2056,6 +2159,21 @@ function _M:onTakeHit(value, src, death_note) return 0 end end + if self:attr("phase_shift_chrono") and not self.turn_procs.phase_shift_chrono and value > self.max_life *0.1 then + self.turn_procs.phase_shift_chrono = true + local nx, ny = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) + if nx then + local ox, oy = self.x, self.y + if not self:teleportRandom(nx, ny, 0) then + game.logSeen(self, "The spell fizzles!") + else + game.level.map:particleEmitter(ox, oy, 1, "temporal_teleport") + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + game:delayedLogDamage(src or {}, self, 0, ("#STEEL_BLUE#(%d shifted)#LAST#"):format(value/2), nil) + value = value/2 + end + end + end if self:attr("retribution") then -- Absorb damage into the retribution @@ -2080,7 +2198,7 @@ function _M:onTakeHit(value, src, death_note) game.level.map:particleEmitter(self.x, self.y, tg.radius, "sunburst", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y}) end end - + --Special Flag (currently for Terrasca) if value > 0 and self:attr("speed_resist") then value = value * (util.bound(self.global_speed * self.movement_speed, 0.3, 1)) @@ -2091,7 +2209,7 @@ function _M:onTakeHit(value, src, death_note) value = value * (100-self:attr("incoming_reduce")) / 100 print("[onTakeHit] After Trained Reactions effect reduction ", value) end - + if self:knowTalent(self.T_SKIRMISHER_TRAINED_REACTIONS) then local t = self:getTalentFromId(self.T_SKIRMISHER_TRAINED_REACTIONS) if self:isTalentActive(t.id) then @@ -2099,7 +2217,7 @@ function _M:onTakeHit(value, src, death_note) print("[onTakeHit] After Trained Reactions life% trigger ", value) end end - + if value > 0 and self:knowTalent(self.T_MITOSIS) and self:isTalentActive(self.T_MITOSIS) then local t = self:getTalentFromId(self.T_MITOSIS) local chance = t.getChance(self, t) @@ -2186,7 +2304,7 @@ function _M:onTakeHit(value, src, death_note) self:removeEffect(self.EFF_PSI_DAMAGE_SHIELD) end end - + if value > 0 and self:attr("shadow_empathy") then -- Absorb damage into a random shadow local shadow = self:callTalent(self.T_SHADOW_EMPATHY, "getRandomShadow") @@ -2254,6 +2372,11 @@ function _M:onTakeHit(value, src, death_note) value = 0 end end + + -- Paradox Shield + if value > 0 and self:isTalentActive(self.T_PRESERVE_PATTERN) then + value = self:callTalent(self.T_PRESERVE_PATTERN, "doPerservePattern", src, value) + end if value <=0 then return 0 end if self.knowTalent and (self:knowTalent(self.T_SEETHE) or self:knowTalent(self.T_GRIM_RESOLVE)) then @@ -2618,11 +2741,10 @@ function _M:onTakeHit(value, src, death_note) if self:attr("reduce_spell_cooldown_on_hit") and value >= self.max_life * self:attr("reduce_spell_cooldown_on_hit") / 100 then local alt = {} for tid, cd in pairs(self.talents_cd) do - if rng.percent(self:attr("reduce_spell_cooldown_on_hit_chance")) then alt[tid] = cd - 1 end + if rng.percent(self:attr("reduce_spell_cooldown_on_hit_chance")) then alt[tid] = true end end for tid, cd in pairs(alt) do - if cd <= 0 then self.talents_cd[tid] = nil - else self.talents_cd[tid] = cd end + self:alterTalentCoolingdown(tid, -1) end end @@ -2811,6 +2933,7 @@ function _M:die(src, death_note) end if self:fireTalentCheck("callbackOnDeath", src, death_note) then return end + if self.summoner and self.summoner.fireTalentCheck and self.summoner:fireTalentCheck("callbackOnSummonDeath", self, src, death_note) then return end mod.class.interface.ActorLife.die(self, src, death_note) @@ -2915,7 +3038,7 @@ function _M:die(src, death_note) if src and src.attr and src:attr("psi_per_kill") then src:incPsi(src:attr("psi_per_kill")) end - + -- Increases blood frenzy if src and src.knowTalent and src:knowTalent(src.T_BLOOD_FRENZY) and src:isTalentActive(src.T_BLOOD_FRENZY) then src.blood_frenzy = src.blood_frenzy + src:callTalent(src.T_BLOOD_FRENZY,"bonuspower") @@ -2934,7 +3057,7 @@ function _M:die(src, death_note) rsrc.changed = true end end - + -- handle hate changes on kill if src and src.knowTalent and src:knowTalent(src.T_HATE_POOL) then local t = src:getTalentFromId(src.T_HATE_POOL) @@ -2994,8 +3117,7 @@ function _M:die(src, death_note) if src and self.reset_rush_on_death and self.reset_rush_on_death == src then game:onTickEnd(function() - src.talents_cd[src.T_RUSH] = nil - src.changed = true + src:alterTalentCoolingdown(src.T_RUSH, -1000) end) end @@ -3004,6 +3126,7 @@ function _M:die(src, death_note) p.src:project({type="ball", radius=4, x=self.x, y=self.y}, self.x, self.y, DamageType.ACID, p.explosion, {type="acid"}) end + -- Chronomancy stuff if self:hasEffect(self.EFF_TEMPORAL_DESTABILIZATION) then local p = self:hasEffect(self.EFF_TEMPORAL_DESTABILIZATION) if self:hasEffect(self.EFF_CONTINUUM_DESTABILIZATION) then @@ -3033,6 +3156,11 @@ function _M:die(src, death_note) end) end + if self:hasEffect(self.EFF_TRIM_THREADS) then + local p = self:hasEffect(self.EFF_TRIM_THREADS) + p.src:incParadox(-p.reduction) + end + if self:hasEffect(self.EFF_GHOUL_ROT) then local p = self:hasEffect(self.EFF_GHOUL_ROT) if p.make_ghoul > 0 then @@ -3145,6 +3273,7 @@ function _M:resetToFull() self.air = self.max_air self.psi = self.max_psi self.hate = self.max_hate + self.paradox = self.preferred_paradox or 300 end -- Level up talents to match actor level @@ -3489,7 +3618,9 @@ function _M:updateModdableTile() end end - add[#add+1] = {image = base..(self.moddable_tile_base or "base_01.png")} + local basebody = self.moddable_tile_base or "base_01.png" + if self.moddable_tile_base_alter then basebody = self:moddable_tile_base_alter(basebody) end + add[#add+1] = {image = base..basebody} if not self:attr("disarmed") then i = self.inven[self.INVEN_MAINHAND]; if i and i[1] and i[1].moddable_tile_back then @@ -3629,7 +3760,7 @@ function _M:quickSwitchWeapons(free_swap, message) else game.logPlayer(self, "You switch your weapons to: %s.", names) end - + self.off_weapon_slots = not self.off_weapon_slots self.changed = true end @@ -3705,6 +3836,7 @@ function _M:onWear(o, inven_id, bypass_set) for k, e in pairs(o.wielder) do o.wielded[k] = self:addTemporaryValue(k, e) end + o.wielder.wielded = true end if o.talent_on_spell then @@ -3762,6 +3894,14 @@ function _M:onWear(o, inven_id, bypass_set) end end + -- Callbacks! + if not o.carrier_callbacks then for event, store in pairs(sustainCallbackCheck) do + if o[event] then + self[store] = self[store] or {} + self[store][o] = "object" + end + end end + self:breakReloading() self:fireTalentCheck("callbackOnWear", o, bypass_set) @@ -3846,12 +3986,24 @@ function _M:onTakeoff(o, inven_id, bypass_set) t.updatePsychometryCount(self, t) end + if o.wielder then + o.wielder.wielded = nil + end + if o.wielder and o.wielder.learn_talent then for tid, level in pairs(o.wielder.learn_talent) do self:unlearnItemTalent(o, tid, level) end end + -- Callbacks + if not o.carrier_callbacks then for event, store in pairs(sustainCallbackCheck) do + if o[event] then + self[store][o] = nil + if not next(self[store]) then self[store] = nil end + end + end end + self:checkMindstar(o) self:breakReloading() @@ -3929,6 +4081,14 @@ function _M:onAddObject(o) end end + -- Callbacks! + if o.carrier_callbacks then for event, store in pairs(sustainCallbackCheck) do + if o[event] then + self[store] = self[store] or {} + self[store][o] = "object" + end + end end + self:checkEncumbrance() -- Achievement checks @@ -3950,6 +4110,14 @@ function _M:onRemoveObject(o) end end + -- Callbacks + if o.carrier_callbacks then for event, store in pairs(sustainCallbackCheck) do + if o[event] then + self[store][o] = nil + if not next(self[store]) then self[store] = nil end + end + end end + self:checkEncumbrance() end @@ -4041,6 +4209,7 @@ function _M:learnTalent(t_id, force, nb, extra) end if t.is_spell then self:attr("has_arcane_knowledge", nb or 1) end + if t.is_antimagic then self:attr("forbid_arcane", nb or 1) end if t.dont_provide_pool then return true end @@ -4223,6 +4392,7 @@ function _M:unlearnTalent(t_id, nb, no_unsustain, extra) self:recomputeRegenResources() if t.is_spell then self:attr("has_arcane_knowledge", -nb) end + if t.is_antimagic then self:attr("forbid_arcane", -nb) end -- If we learn mindslayer things we learn telekinetic grasp & beyond the flesh if t.autolearn_mindslayer then @@ -4259,7 +4429,7 @@ end --- Paradox checks function _M:getModifiedParadox() - local will_modifier = 2 + self:callTalent(self.T_PARADOX_MASTERY,"WilMult") + local will_modifier = (1 + (self:attr("paradox_will_multi") or 0)) * 2 will_modifier = (self:getWil() + (self:attr("paradox_reduce_anomalies") or 0)) * will_modifier local sustain_modifier = self:getMinParadox() local modified_paradox = math.max(0, self:getParadox() - will_modifier + sustain_modifier) @@ -4287,15 +4457,16 @@ function _M:paradoxDoAnomaly(reduction, anomaly_type, chance, target, silent) local anomaly_type = anomaly_type or "random" local forced = false local chance = chance or self:paradoxFailChance() - if chance == "forced" then + if chance == "forced" then forced = true chance = 100 end - + -- See if we create an anomaly - if not game.zone.no_anomalies and not self:attr("no_paradox_fail") and not self.turn_procs.anomalies_checked then - self.turn_procs.anomalies_checked = true -- This is so players can't chain cancel out of targeting to trigger anomalies on purpose, we clear it out in postUse - + if not game.zone.no_anomalies and not self:attr("no_paradox_fail") then + if not forced and self.turn_procs.anomalies_checked then return false end -- This is so players can't chain cancel out of targeting to trigger anomalies on purpose, we clear it out in postUse + if not forced then self.turn_procs.anomalies_checked = true end + -- return true if we roll an anomly if rng.percent(chance) then -- If our Paradox is over 600 do a major anomaly @@ -4303,44 +4474,47 @@ function _M:paradoxDoAnomaly(reduction, anomaly_type, chance, target, silent) anomaly_type = "major" else -- Check for Bias? - if self.anomaly_bias and rng.percent(self.anomaly_bias.chance) then - anomaly_type = self.anomaly_bias.type - end + if self.anomaly_bias and rng.percent(self.anomaly_bias.chance) then anomaly_type = self.anomaly_bias.type end + -- Revert no-major to random + if anomaly_type == "no-major" then anomaly_type = "random" end end - + -- Now pick anomalies filtered by type local ts = {} for id, t in pairs(self.talents_def) do - if anomaly_type ~= "random" and anomaly_type ~= "no-major" then - if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type == anomaly_type then ts[#ts+1] = id end + if anomaly_type ~= "random" then + if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type == anomaly_type and not self:isTalentCoolingDown(t) then ts[#ts+1] = id end else - if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type ~= "major" then ts[#ts+1] = id end + if t.type[1] == "chronomancy/anomalies" and t.anomaly_type and t.anomaly_type ~= "major" and not self:isTalentCoolingDown(t) then ts[#ts+1] = id end end end - - -- Did we find anomalies? + + -- Did we find anomalies? if ts[1] then -- Do we have a target? If not we pass to anomaly targeting - -- The ignore energy calls here allow anomalies to be cast even when it's not the players turn (i.e. Preserve Pattern) + -- The ignore energy calls here allow anomalies to be cast even when it's not the players turn if target then - self:forceUseTalent(rng.table(ts), {ignore_cooldown=true, ignore_energy=true, force_target=target}) + self:attr("anomaly_forced_target", 1) + self:forceUseTalent(rng.table(ts), {ignore_energy=true, force_target=target}) + self:attr("anomaly_forced_target", -1) else - self:forceUseTalent(rng.table(ts), {ignore_cooldown=true, ignore_energy=true}) - end - -- Drop some game messages - if not silent then - if forced then - game.logPlayer(self, "#STEEL_BLUE#You've moved to another time thread.") - else - game.logPlayer(self, "#LIGHT_RED#You lose control and unleash an anomaly!") - end - end - -- Reduce Paradox - if reduction and reduction > 0 then - self:incParadox(-reduction) + self:forceUseTalent(rng.table(ts), {ignore_energy=true}) end end + -- Drop some game messages; these happen so Paradox gets reduced even if an anomaly isn't found + if not silent then + if forced then + game.logPlayer(self, "#STEEL_BLUE#You've moved to another time thread.") + else + game.logPlayer(self, "#LIGHT_RED#You lose control and unleash an anomaly!") + end + end + -- Reduce Paradox + if reduction and reduction > 0 then + self:incParadox(-reduction) + end + return true end end @@ -4361,9 +4535,15 @@ function _M:incParadox(paradox) if self:getModifiedParadox() < 600 and self:getModifiedParadox() + paradox >= 600 then game.logPlayer(self, "#LIGHT_RED#Spacetime fights against your control!") end - if self:getParadox() > 600 and self:getParadox() + paradox <= 600 then + if self:getModifiedParadox() > 600 and self:getModifiedParadox() + paradox <= 600 then game.logPlayer(self, "#LIGHT_BLUE#Spacetime has calmed... somewhat.") end + + -- Cosmic Cycle + if self:isTalentActive(self.T_COSMIC_CYCLE) then + self:callTalent(self.T_COSMIC_CYCLE, "doCosmicCycle") + end + return previous_incParadox(self, paradox) end @@ -4511,7 +4691,7 @@ function _M:preUseTalent(ab, silent, fake) return false end end - + if not self:enoughEnergy() and not fake then return false end if ab.mode == "sustained" then @@ -4603,6 +4783,17 @@ function _M:preUseTalent(ab, silent, fake) if rng.percent(self:attr("spell_failure")) then if not silent then game.logSeen(self, "%s's %s has been disrupted by #ORCHID#anti-magic forces#LAST#!", self.name:capitalize(), ab.name) end self:useEnergy() + self:fireTalentCheck("callbackOnTalentDisturbed", ab) + return false + end + end + + -- Nature can fail + if (ab.is_nature and not self:isTalentActive(ab.id)) and not fake and self:attr("nature_failure") then + if rng.percent(self:attr("nature_failure")) then + if not silent then game.logSeen(self, "%s's %s has been disrupted by #ORCHID#anti-nature forces#LAST#!", self.name:capitalize(), ab.name) end + self:useEnergy() + self:fireTalentCheck("callbackOnTalentDisturbed", ab) return false end end @@ -4698,7 +4889,9 @@ local sustainCallbackCheck = { callbackOnActBase = "talents_on_act_base", callbackOnMove = "talents_on_move", callbackOnRest = "talents_on_rest", + callbackOnRun = "talents_on_run", callbackOnDeath = "talents_on_death", + callbackOnSummonDeath = "talents_on_summon_death", callbackOnKill = "talents_on_kill", callbackOnMeleeAttack = "talents_on_melee_attack", callbackOnMeleeHit = "talents_on_melee_hit", @@ -4713,6 +4906,9 @@ local sustainCallbackCheck = { callbackOnWear = "talents_on_wear", callbackOnTakeoff = "talents_on_takeoff", callbackOnTalentPost = "talents_on_talent_post", + callbackOnTemporaryEffect = "talents_on_tmp", + callbackOnTalentDisturbed = "talents_on_talent_disturbed", + callbackOnBlock = "talents_on_block", } _M.sustainCallbackCheck = sustainCallbackCheck @@ -4724,6 +4920,9 @@ function _M:fireTalentCheck(event, ...) if kind == "effect" then self.__project_source = self.tmp[tid] ret = self:callEffect(tid, event, ...) or ret + elseif kind == "object" then + self.__project_source = self.tmp[tid] + ret = tid:check(event, self, ...) or ret else self.__project_source = self.sustain_talents[tid] ret = self:callTalent(tid, event, ...) or ret @@ -4734,6 +4933,40 @@ function _M:fireTalentCheck(event, ...) return ret end +function _M:getTalentSpeedType(t) + if t.speed then + return util.getval(t.speed, self, t) + elseif t.is_spell then + return "spell" + elseif t.is_summon then + return "summon" + elseif t.type[1]:find("^technique/archery") then + return "archery" + elseif t.type[1]:find("^technique/") then + return "weapon" + elseif t.is_mind then + return "mind" + else + return "standard" + end +end + +function _M:getTalentSpeed(t) + local speed_type = self:getTalentSpeedType(t) + local speed = self:getSpeed(speed_type) + + -- Quicken + local p = self:isTalentActive(self.T_QUICKEN) + if p and p.talent == t.id then + speed = math.max(0.1, speed - self:callTalent(self.T_QUICKEN, "getPower")) + end + + local hd = {"Actor:getTalentSpeed", talent = t, speed_type = speed_type, speed = speed,} + if self:triggerHook(hd) then speed = hd.speed end + + return speed +end + --- Called after a talent is used -- Check if it must use a turn, mana, stamina, ... -- @param ab the talent (not the id, the table) @@ -4764,25 +4997,7 @@ function _M:postUseTalent(ab, ret, silent) end) if not util.getval(ab.no_energy, self, ab) then - if ab.is_spell then - self:useEnergy(game.energy_to_act * self:combatSpellSpeed()) - elseif ab.is_summon then - self:useEnergy(game.energy_to_act * self:combatSummonSpeed()) - elseif ab.type[1]:find("^technique/") then - local combat = self.combat - if self:getInven(self.INVEN_MAINHAND) then - local o = self:getInven(self.INVEN_MAINHAND)[1] - combat = self:getObjectCombat(o, "mainhand") - elseif self:getInven(self.INVEN_OFFHAND) then - local o = self:getInven(self.INVEN_OFFHAND)[1] - combat = self:getObjectCombat(o, "offhand") - end - self:useEnergy(game.energy_to_act * self:combatSpeed(combat)) - elseif ab.is_mind then - self:useEnergy(game.energy_to_act * self:combatMindSpeed()) - else - self:useEnergy() - end + self:useEnergy(self:getTalentSpeed(ab) * game.energy_to_act) -- Free melee blow if ab.is_spell and ab.mode ~= "sustained" and self:knowTalent(self.T_CORRUPTED_STRENGTH) and not self:attr("forbid_corrupted_strength_blow") and not self.turn_procs.corrupted_strength then @@ -4836,6 +5051,16 @@ function _M:postUseTalent(ab, ret, silent) if ab.sustain_feedback then trigger = true; self:incMaxFeedback(-util.getval(ab.sustain_feedback, self, ab)) end + if ab.sustain_slots then + if not self.sustain_slots then self.sustain_slots = {} end + local slots = ab.sustain_slots + if 'string' == type(slots) then slots = {slots} end + for _, slot in pairs(slots) do + local t = self.sustain_slots[slot] + if t and self:isTalentActive(t) then self:forceUseTalent(t, {ignore_energy=true}) end + self.sustain_slots[slot] = ab.id + end + end for event, store in pairs(sustainCallbackCheck) do if ab[event] then self[store] = self[store] or {} @@ -4875,6 +5100,15 @@ function _M:postUseTalent(ab, ret, silent) end if ab.sustain_feedback then self:incMaxFeedback(util.getval(ab.sustain_feedback, self, ab)) + end + if ab.sustain_slots then + local slots = ab.sustain_slots + if 'string' == type(slots) then slots = {slots} end + for _, slot in pairs(slots) do + if self.sustain_slots[slot] == ab.id then + self.sustain_slots[slot] = nil + end + end end for event, store in pairs(sustainCallbackCheck) do if ab[event] then @@ -4943,6 +5177,7 @@ function _M:postUseTalent(ab, ret, silent) if ab.id ~= self.T_GATHER_THE_THREADS and ab.is_spell then self:breakChronoSpells() end if not ab.no_reload_break then self:breakReloading() end self:breakStepUp() + self:breakSpacetimeTuning() --if not (util.getval(ab.no_energy, self, ab) or ab.no_break_channel) and not (ab.mode == "sustained" and self:isTalentActive(ab.id)) then self:breakPsionicChannel(ab.id) end for tid, _ in pairs(self.sustain_talents) do @@ -4952,12 +5187,6 @@ function _M:postUseTalent(ab, ret, silent) end end - if ab.id ~= self.T_REDUX and self:hasEffect(self.EFF_REDUX) and ab.type[1]:find("^chronomancy/") and ab.mode == "activated" and self:getTalentLevel(self.T_REDUX) >= self:getTalentLevel(ab.id) then - self:removeEffect(self.EFF_REDUX) - -- this still consumes energy but works as the talent suggests it does - self:forceUseTalent(ab.id, {ignore_energy=true, ignore_cd = true}) - end - if not ab.innate and self:hasEffect(self.EFF_RAMPAGE) and ab.id ~= self.T_RAMPAGE and ab.id ~= self.T_SLAM then local eff = self:hasEffect(self.EFF_RAMPAGE) value = self.tempeffect_def[self.EFF_RAMPAGE].do_postUseTalent(self, eff, value) @@ -4982,16 +5211,35 @@ function _M:postUseTalent(ab, ret, silent) for i = 1, self:attr("random_talent_cooldown_on_use_nb") do local t = rng.tableRemove(tids) if not t then break end - self.talents_cd[t.id] = self:attr("random_talent_cooldown_on_use_turns") + self:startTalentCooldown(t.id, self:attr("random_talent_cooldown_on_use_turns")) game.logSeen(self, "%s talent '%s%s' is disrupted by the mind parasite.", self.name:capitalize(), (t.display_entity and t.display_entity:getDisplayString() or ""), t.name) end end - + if self.turn_procs.anomalies_checked then self.turn_procs.anomalies_checked = nil end -- clears out anomaly checks return true end +-- Return the talent currently taking up a given sustain slot. +function _M:getSustainSlot(slot) + if not self.sustain_slots then return end + return self.sustain_slots[slot] +end + +-- Get the sustain which a talent will replace. +function _M:getReplacedSustains(talent) + if 'string' == type(talent) then talent = self:getTalentFromId(talent) end + local slots = talent.sustain_slots or {} + if 'string' == type(slots) then slots = {slots} end + local ret = {} + for _, slot in pairs(slots) do + local t = self:getSustainSlot(slot) + if t ~= talent.id then table.insert(ret, t) end + end + return ret +end + --- Force a talent to activate without using energy or such function _M:forceUseTalent(t, def) if def.no_talent_fail then self:attr("no_talent_fail", 1) end @@ -5046,9 +5294,6 @@ function _M:breakStepUp() if self:hasEffect(self.EFF_SKIRMISHER_DIRECTED_SPEED) then self:removeEffect(self.EFF_SKIRMISHER_DIRECTED_SPEED) end - if self:hasEffect(self.EFF_CELERITY) then - self:removeEffect(self.EFF_CELERITY) - end end --- Breaks lightning speed if active @@ -5065,6 +5310,12 @@ function _M:breakChronoSpells() end end +function _M:breakSpacetimeTuning() + if self:hasEffect(self.EFF_SPACETIME_TUNING) then + self:removeEffect(self.EFF_SPACETIME_TUNING) + end +end + --- Break Psionic Channels function _M:breakPsionicChannel(talent) if self:isTalentActive(self.T_MIND_STORM) and talent ~= self.T_MIND_STORM then @@ -5117,7 +5368,7 @@ function _M:getTalentFullDescription(t, addlevel, config, fake_mastery) if t.positive then d:add({"color",0x6f,0xff,0x83}, "Positive energy cost: ", {"color",255, 215, 0}, ""..math.round(util.getval(t.positive, self, t) * (100 + self:combatFatigue()) / 100, 0.1), true) end if t.negative then d:add({"color",0x6f,0xff,0x83}, "Negative energy cost: ", {"color", 127, 127, 127}, ""..math.round(util.getval(t.negative, self, t) * (100 + self:combatFatigue()) / 100, 0.1), true) end if t.hate then d:add({"color",0x6f,0xff,0x83}, "Hate cost: ", {"color", 127, 127, 127}, ""..math.round(util.getval(t.hate, self, t) * (100 + 2 * self:combatFatigue()) / 100, 0.1), true) end - if t.paradox then d:add({"color",0x6f,0xff,0x83}, "Paradox cost: ", {"color", 176, 196, 222}, ""..math.round(util.getval(t.paradox, self, t), 0.1), true) end + if t.paradox then d:add({"color",0x6f,0xff,0x83}, "Paradox cost: ", {"color", 176, 196, 222}, ""..math.round(util.getval(t.paradox, self, t)), true) end if t.psi then d:add({"color",0x6f,0xff,0x83}, "Psi cost: ", {"color",0x7f,0xff,0xd4}, ""..math.round(util.getval(t.psi, self, t) * (100 + 2 * self:combatFatigue()) / 100, 0.1), true) end if t.feedback then d:add({"color",0x6f,0xff,0x83}, "Feedback cost: ", {"color",0xFF, 0xFF, 0x00}, ""..math.round(util.getval(t.feedback, self, t) * (100 + 2 * self:combatFatigue()) / 100, 0.1), true) end if t.fortress_energy then d:add({"color",0x6f,0xff,0x83}, "Fortress Energy cost: ", {"color",0x00,0xff,0xa0}, ""..math.round(t.fortress_energy, 0.1), true) end @@ -5141,29 +5392,52 @@ function _M:getTalentFullDescription(t, addlevel, config, fake_mastery) else d:add({"color",0x6f,0xff,0x83}, "Range: ", {"color",0xFF,0xFF,0xFF}, "melee/personal", true) end if not config.ignore_ressources then - if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, "Cooldown: ", {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end + if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, ("%sCooldown: "):format(t.fixed_cooldown and "Fixed " or ""), {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end end local speed = self:getTalentProjectileSpeed(t) if speed then d:add({"color",0x6f,0xff,0x83}, "Travel Speed: ", {"color",0xFF,0xFF,0xFF}, ""..(speed * 100).."% of base", true) else d:add({"color",0x6f,0xff,0x83}, "Travel Speed: ", {"color",0xFF,0xFF,0xFF}, "instantaneous", true) end if not config.ignore_use_time then - local uspeed = "1 turn" + local uspeed = "Full Turn" local no_energy = util.getval(t.no_energy, self, t) - if no_energy and type(no_energy) == "boolean" and no_energy == true then uspeed = "instant" end + local display_speed = util.getval(t.display_speed, self, t) + if display_speed then + uspeed = display_speed + elseif no_energy and type(no_energy) == "boolean" and no_energy == true then + uspeed = "Instant (#LIGHT_GREEN#0%#LAST# of a turn)" + else + local speed = self:getTalentSpeed(t) + local speed_type = self:getTalentSpeedType(t) + if type(speed_type) == "string" then + speed_type = speed_type:capitalize() + else + speed_type = 'Special' + end + uspeed = ("%s (#LIGHT_GREEN#%d%%#LAST# of a turn)"):format(speed_type, speed * 100) + end d:add({"color",0x6f,0xff,0x83}, "Usage Speed: ", {"color",0xFF,0xFF,0xFF}, uspeed, true) end local is_a = {} if t.is_spell then is_a[#is_a+1] = "a spell" end if t.is_mind then is_a[#is_a+1] = "a mind power" end if t.is_nature then is_a[#is_a+1] = "a nature gift" end + if t.is_antimagic then is_a[#is_a+1] = "an antimagic ability" end if t.is_summon then is_a[#is_a+1] = " a summon power" end if #is_a > 0 then d:add({"color",0x6f,0xff,0x83}, "Is: ", {"color",0xFF,0xFF,0xFF}, table.concatNice(is_a, ", ", " and "), true) end else if not config.ignore_ressources then - if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, "Cooldown: ", {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end + if self:getTalentCooldown(t) then d:add({"color",0x6f,0xff,0x83}, ("%sCooldown: "):format(t.fixed_cooldown and "Fixed " or ""), {"color",0xFF,0xFF,0xFF}, ""..self:getTalentCooldown(t), true) end + end + end + + if t.mode == 'sustained' then + local replaces = self:getReplacedSustains(t) + if #replaces > 0 then + for k, v in pairs(replaces) do replaces[k] = self:getTalentFromId(v).name end + d:add({"color",0x6f,0xff,0x83}, "Will Deactivate: ", {"color",0xFF,0xFF,0xFF}, table.concat(replaces, ', '), true) end end @@ -5211,6 +5485,11 @@ function _M:getTalentCooldown(t) cd = 1 + cd * eff.power end + local p = self:isTalentActive(self.T_MATRIX) + if p and p.talent == t.id then + cd = math.floor(cd * (1 - self:callTalent(self.T_MATRIX, "getPower"))) + end + if t.is_spell then return math.ceil(cd * (1 - (self.spell_cooldown_reduction or 0))) elseif t.is_summon then @@ -5222,12 +5501,38 @@ end --- Starts a talent cooldown; overloaded from the default to handle talent cooldown reduction -- @param t the talent to cooldown -function _M:startTalentCooldown(t) +-- @param v override the normal cooldown that that, nil to get the normal effect +function _M:startTalentCooldown(t, v) t = self:getTalentFromId(t) - if not t.cooldown then return end - self.talents_cd[t.id] = self:getTalentCooldown(t) + if t.cooldown_override then t = self:getTalentFromId(t.cooldown_override) end + if v then + self.talents_cd[t.id] = math.max(v, self.talents_cd[t.id] or 0) + else + if not t.cooldown then return end + self.talents_cd[t.id] = self:getTalentCooldown(t) + + if t.id ~= self.T_REDUX and self:hasEffect(self.EFF_REDUX) then + local eff = self:hasEffect(self.EFF_REDUX) + if t.type[1]:find("^chronomancy/") and self:getTalentCooldown(t) <= eff.max_cd and t.mode == "activated" and not t.fixed_cooldown then + self.talents_cd[t.id] = 1 + end + end + end if self.talents_cd[t.id] <= 0 then self.talents_cd[t.id] = nil end self.changed = true + if t.cooldownStart then t.cooldownStart(self, t) end +end + +--- Alter the remanining cooldown of a talent +-- @param t the talent affect cooldown +-- @param v the value to add/remove to the cooldown +function _M:alterTalentCoolingdown(t, v) + t = self:getTalentFromId(t) + if t.cooldown_override then t = self:getTalentFromId(t.cooldown_override) end + if not self.talents_cd[t.id] then return nil end + self.talents_cd[t.id] = self.talents_cd[t.id] + v + if self.talents_cd[t.id] <= 0 then self.talents_cd[t.id] = nil end + return self.talents_cd[t.id] end --- Setup the talent as autocast @@ -5271,7 +5576,7 @@ function _M:checkSetTalentAuto(tid, v, opt) end end - + -- Classifications for actor resist/damage -- Thanks to grayswandir for this really neat code structure @@ -5282,7 +5587,7 @@ _M.classifications = { natural = function(self) return not self:checkClassification('unnatural') end, summoned = function(self) return (self.summoner ~= nil) end ,} - + --- Check if the actor is a certain type or in an arbitrary set of classifications -- @param string representing the classification to check -- @return whether the actor is in this classification @@ -5386,16 +5691,7 @@ function _M:talentCooldownFilter(t, change, nb, duplicate) local t = talents[i] local removed = false - ---[[ Change the cooldown to the reduced value or mark it as off cooldown - t[2] = t[2] - change - if t[2] <= 0 then - self.talents_cd[ t[1] ] = nil - table.remove(talents, i) - removed = true - else - self.talents_cd[ t[1] ] = t[2] - end - --]] + self:alterTalentCoolingdown(t[1], -change) if not duplicate then if not removed then table.remove(talents, i) end -- only remove if it hasn't already been removed @@ -5635,12 +5931,17 @@ function _M:on_set_temporary_effect(eff_id, e, p) if not p.no_ct_effect and not e.no_ct_effect and e.status == "detrimental" then self:crossTierEffect(eff_id, p.apply_power, p.apply_save or save_for_effects[e.type]) end p.total_dur = p.dur - if e.status == "detrimental" and self:checkHit(save, p.apply_power, 0, 95) and p.dur > 0 then - game.logSeen(self, "#ORANGE#%s shrugs off the effect '%s'!", self.name:capitalize(), e.desc) - p.dur = 0 + if p.dur > 0 and e.status == "detrimental" then + local saved = self:checkHit(save, p.apply_power, 0, 95) + local hd = {"Actor:effectSave", saved = saved, save_type = save_type, eff_id = eff_id, e = e, p = p,} + self:triggerHook(hd) + self:fireTalentCheck("callbackOnEffectSave", hd) + saved, eff_id, e, p = hd.saved, hd.eff_id, hd.e, hd.p + if saved then + game.logSeen(self, "#ORANGE#%s shrugs off the effect '%s'!", self.name:capitalize(), e.desc) + return true + end end - - p.apply_power = nil end if e.status == "detrimental" and self:knowTalent(self.T_RESILIENT_BONES) then @@ -5687,6 +5988,8 @@ function _M:on_set_temporary_effect(eff_id, e, p) self:triggerTalent(self.T_SPINE_OF_THE_WORLD) end + self:fireTalentCheck("callbackOnTemporaryEffect", eff_id, e, p) + if self.player and not self.tmp[eff_id] then p.__set_time = core.game.getTime() end @@ -5893,7 +6196,9 @@ function _M:doDrop(inven, item, on_done, nb) if nb == nil or nb >= self:getInven(inven)[item]:getNumber() then self:dropFloor(inven, item, true, true) else - for i = 1, nb do self:dropFloor(inven, item, true) end + local stack = self:removeObject(inven, item, nb) + game.logSeen(self, "%s drops on the floor: %s.", self.name:capitalize(), stack:getName{do_color=true, do_count=true}) + game.level.map:addObject(self.x, self.y, stack) end self:sortInven(inven) self:useEnergy() @@ -5902,28 +6207,51 @@ function _M:doDrop(inven, item, on_done, nb) if on_done then on_done() end end +--- wear an object from an inventory +-- @param inven = inventory id to take object from +-- @param item = inventory slot to take from +-- @param o = object to wear +-- @param dst = actor holding object to be worn <self> function _M:doWear(inven, item, o, dst) if self.no_inventory_access then return end dst = dst or self dst:removeObject(inven, item, true) - local ro = self:wearObject(o, true, true) + local ro, rs = self:wearObject(o, true, true) -- removed object and remaining stack if any + local added, slot if ro then if not self:attr("quick_wear_takeoff") or self:attr("quick_wear_takeoff_disable") then self:useEnergy() end if self:attr("quick_wear_takeoff") then self:setEffect(self.EFF_SWIFT_HANDS_CD, 1, {}) self.tmp[self.EFF_SWIFT_HANDS_CD].dur = 0 end - if type(ro) == "table" then dst:addObject(inven, ro) end - elseif not ro then - dst:addObject(inven, o) + if type(ro) == "table" then dst:addObject(inven, ro, true) end -- always give full stack back + else -- failed, add object back + dst:addObject(inven, o, true) + end + if type(rs) == "table" then + local rrs + repeat -- handles a case of stacking limits causing part of a stack to be discarded + rrs = rs + added, slot, rs = dst:addObject(inven, rs) + until not added or not rs + if not added then + game.logPlayer(self, "You had to drop %s due to lack of space.", rrs:getName{do_color = true}) + if rrs and not game.zone.wilderness then game.level.map:addObject(self.x, self.y, rrs) end -- extra stack discarded in wilderness + end end dst:sortInven() self:actorCheckSustains() self.changed = true end +--- Take off an item +-- @param inven = inven id +-- @param item = slot to remove from +-- @param o = object to remove +-- @param simple set true to skip equipment takeoff checks and energy use +-- @param dst = actor to receive object (in dst.INVEN_INVEN) function _M:doTakeoff(inven, item, o, simple, dst) - if self.no_inventory_access then return end dst = dst or self + if self.no_inventory_access or not dst:canAddToInven(dst.INVEN_INVEN) then return end if self:takeoffObject(inven, item) then - dst:addObject(self.INVEN_INVEN, o) + dst:addObject(dst.INVEN_INVEN, o, true) --note: moves a whole stack end if not simple then dst:sortInven() @@ -5996,3 +6324,107 @@ end function _M:transmoGetWord() return "transmogrify" end + +function _M:canUseTinker(tinker) + if not tinker.is_tinker then return nil, "not an attachable item" end + if not self.can_tinker then return nil, "can not use attachements" end + if not self.can_tinker[tinker.is_tinker] then return nil, "can not use attachements of this type" end + if tinker.tinker_allow_attach and tinker:tinker_allow_attach() then return nil, tinker:tinker_allow_attach() end + return true +end + +function _M:doTakeoffTinker(base_o, oldo) + if base_o.tinker ~= oldo then return end + + local mustwear = base_o.wielded + if mustwear then self:onTakeoff(base_o, true) end + base_o.tinker = nil + local forbid = oldo:check("on_untinker", base_o, self) + if oldo.tinkered then + for k, id in pairs(oldo.tinkered) do + if type(id) == "table" then base_o:removeTemporaryValue(id[1], id[2]) + else base_o:removeTemporaryValue(k, id) + end + end + end + oldo.tinkered = nil + if mustwear then self:onWear(base_o, true) end + + self:addObject(self.INVEN_INVEN, oldo) + game.logPlayer(self, "You detach %s from your %s.", oldo:getName{do_color=true}, base_o:getName{do_color=true}) + + return true +end + +function _M:doWearTinker(wear_inven, wear_item, wear_o, base_inven, base_item, base_o, can_remove) + if not base_o and base_inven and base_item then base_o = base_inven[base_item] end + if not base_o then + game.logPlayer(self, "You can not use a tinker without the corresponding item.") + return + end + local ok, err = self:canUseTinker(wear_o) + if not ok then + game.logPlayer(self, "This item is not usable: %s.", err) + return + end + if wear_o.on_type and wear_o.on_type ~= rawget(base_o, "type") then + game.logPlayer(self, "This tinker can not be applied to this item.") + return + end + if wear_o.on_slot and wear_o.on_slot ~= base_o.slot then + game.logPlayer(self, "This tinker can not be applied to this item.") + return + end + if base_o.tinker then + if not can_remove then + game.logPlayer(self, "You already have a tinker on this item.") + return + else + self:doTakeoffTinker(base_o, base_o.tinker) + end + end + + local mustwear = base_o.wielded + if mustwear then self:onTakeoff(base_o, true) end + + wear_o.tinkered = {} + local forbid = wear_o:check("on_tinker", base_o, self) + if wear_o.object_tinker then + for k, e in pairs(wear_o.object_tinker) do + wear_o.tinkered[k] = base_o:addTemporaryValue(k, e) + end + end + + if mustwear then self:onWear(base_o, true) end + + if not forbid then + base_o.tinker = wear_o + game.logPlayer(self, "You attach %s to your %s.", wear_o:getName{do_color=true}, base_o:getName{do_color=true}) + + if wear_inven and wear_item then self:removeObject(wear_inven, wear_item) end + return true, base_o + else + game.logPlayer(self, "You fail to attach %s to %s.", wear_o:getName{do_color=true}, base_o:getName{do_color=true}) + end +end + +function _M:findTinkerSpot(tinker) + local possible = {} + self:inventoryApplyAll(function(inven, item, o) + if not inven.worn then return end + if self.tinker_restrict_slots and (not self.tinker_restrict_slots[inven.name] or self.tinker_restrict_slots[inven.name] < item) then return end + if o:canAttachTinker(tinker, true) then + possible[#possible+1] = {inven=inven, item=item, free=o.tinker and 1 or 0} + end + end) + if #possible == 0 then return end + + -- Find one that has no atatchement if possible, if not replace one + table.sort(possible, function(a, b) + if a.free ~= b.free then return a.free < b.free + elseif a.inven.id ~= b.inven.id then return a.inven.id < b.inven.id + else return a.item < b.item + end + end) + return possible[1].inven, possible[1].item, possible[1].free == 0 +end diff --git a/game/modules/tome/class/BigNews.lua b/game/modules/tome/class/BigNews.lua index 83c28cadc23aff3d854a4f459df9deb0e237c310..6c456a6ab031837bb99a75be947f439db85b72ec 100644 --- a/game/modules/tome/class/BigNews.lua +++ b/game/modules/tome/class/BigNews.lua @@ -18,6 +18,7 @@ -- darkgod@te4.org require "engine.class" +local tween = require "tween" local Map = require "engine.Map" local Shader = require "engine.Shader" @@ -30,14 +31,21 @@ function _M:init(font, size) end function _M:say(time, txt, ...) - self:saySimple(time, txt, ...) + return self:easing(time, nil, txt, ...) +end + +function _M:easing(time, easing, txt, ...) + self:easingSimple(time, easing, txt, ...) game.logPlayer(game.player, "%s", txt:toString()) end function _M:saySimple(time, txt, ...) + return self:easingSimple(time, nil, txt, ...) +end + +function _M:easingSimple(time, easing, txt, ...) txt = txt:format(...) - self.time = time or 60 - self.max_time = self.time + self.max_time = time or 60 self.list, self.max_lines, self.max_w = self.font:draw(txt:toString(), math.floor(game.w * 0.8), 255, 255, 255) self.list_x = (- self.max_w) / 2 @@ -48,6 +56,10 @@ function _M:saySimple(time, txt, ...) if game.player.stopRun then game.player:stopRun("important news") end if game.player.stopRest then game.player:stopRest("important news") end + + self.scale = 1 + if self.tweenid then tween.stop(self.tweenid) end + self.tweenid = tween(self.max_time, self, {scale=0}, easing or "inQuint", function() self.list = nil end) end function _M:display(nb_keyframes) @@ -56,8 +68,7 @@ function _M:display(nb_keyframes) local shader = Shader.default.textoutline and Shader.default.textoutline.shad core.display.glTranslate(self.center_x, self.center_y, 0) - local scale = util.bound(1-math.log10(self.time / self.max_time)^2, 0, 1) - core.display.glScale(scale, scale, scale) + core.display.glScale(self.scale, self.scale, self.scale) local x = self.list_x local y = self.list_y @@ -80,12 +91,6 @@ function _M:display(nb_keyframes) y = y + item.h end - self.time = self.time - nb_keyframes - if self.time < 0 then - self.time = nil - self.list = nil - end - core.display.glScale() core.display.glTranslate(-self.center_x, -self.center_y, 0) end diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 5b742bd5d6659de64bf1bf6b773e248b2bfcd41a..c0805fd928373d41de06aabac54f1a1902a88cfa 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -34,6 +34,7 @@ local Astar = require "engine.Astar" local DirectPath = require "engine.DirectPath" local Shader = require "engine.Shader" local HighScores = require "engine.HighScores" +local FontPackage = require "engine.FontPackage" local NicerTiles = require "mod.class.NicerTiles" local GameState = require "mod.class.GameState" @@ -100,14 +101,14 @@ function _M:runReal() self.uiset:activate() - local flysize = ({normal=14, small=12, big=16})[config.settings.tome.fonts.size] + local flyfont, flysize = FontPackage:getFont("flyer") self.tooltip = Tooltip.new(self.uiset.init_font_mono, self.uiset.init_size_mono, {255,255,255}, {30,30,30,230}) self.tooltip2 = Tooltip.new(self.uiset.init_font_mono, self.uiset.init_size_mono, {255,255,255}, {30,30,30,230}) - self.flyers = FlyingText.new("/data/font/INSULA__.ttf", flysize, "/data/font/INSULA__.ttf", flysize + 3) + self.flyers = FlyingText.new(flyfont, flysize, flyfont, flysize + 3) self.flyers:enableShadow(0.6) game:setFlyingText(self.flyers) - self.bignews = BigNews.new("/data/font/DroidSansMono.ttf", 30) + self.bignews = BigNews.new(FontPackage:getFont("bignews")) self.nicer_tiles = NicerTiles.new() @@ -148,14 +149,14 @@ function _M:runReal() end) -- Create the map scroll text overlay - local lfont = core.display.newFont("/data/font/DroidSans.ttf", 30) + local lfont = FontPackage:get("bignews", true) lfont:setStyle("bold") local s = core.display.drawStringBlendedNewSurface(lfont, "<Scroll mode, press keys to scroll, caps lock to exit>", unpack(colors.simple(colors.GOLD))) lfont:setStyle("normal") self.caps_scroll = {s:glTexture()} self.caps_scroll.w, self.caps_scroll.h = s:getSize() - self.zone_font = core.display.newFont("/data/font/DroidSans.ttf", 12) + self.zone_font = FontPackage:get("zone") self.inited = true @@ -268,7 +269,7 @@ function _M:newGame() self.paused = true print("[PLAYER BIRTH] resolved!") local birthend = function() - local d = require("engine.dialogs.ShowText").new("Welcome to ToME", "intro-"..self.player.starting_intro, {name=self.player.name}, nil, nil, function() + local d = require("engine.dialogs.ShowText").new("Welcome to #LIGHT_BLUE#Tales of Maj'Eyal", "intro-"..self.player.starting_intro, {name=self.player.name}, nil, nil, function() self.player:resetToFull() self.player:registerCharacterPlayed() self.player:onBirth(birth) @@ -1167,9 +1168,7 @@ function _M:updateZoneName() end if self.zone_name_s and self.old_zone_name == name then return end - self.zone_font:setStyle("bold") local s = core.display.drawStringBlendedNewSurface(self.zone_font, name, unpack(colors.simple(colors.GOLD))) - self.zone_font:setStyle("normal") self.zone_name_w, self.zone_name_h = s:getSize() self.zone_name_s, self.zone_name_tw, self.zone_name_th = s:glTexture() self.old_zone_name = name @@ -1581,6 +1580,8 @@ function _M:setupCommands() print("===============") end end, [{"_g","ctrl"}] = function() if config.settings.cheat then + self.player:setEffect("EFF_STUNNED", 10, {}) +do return end local o = game.zone:makeEntity(game.level, "object", {subtype="staff", random_object=true}, nil, true) if o then o:identify(true) diff --git a/game/modules/tome/class/GameState.lua b/game/modules/tome/class/GameState.lua index 8c80aa84d6922d8369b1cfa61984a84dba88a521..d57fed6968feee4cd240a3d5c41e95861ddd460b 100644 --- a/game/modules/tome/class/GameState.lua +++ b/game/modules/tome/class/GameState.lua @@ -245,10 +245,160 @@ local randart_name_rules = { }, } ---- Generate randarts for this state +--- Unided name possibilities for randarts +local unided_names = {"glowing","scintillating","rune-covered","unblemished","jewel-encrusted","humming","gleaming","immaculate","flawless","crackling","glistening","plated","twisted","silvered","faceted","faded","sigiled","shadowy","laminated"} + +--- defined power themes, affects equipment generation +_M.power_themes = { + 'physical', 'mental', 'spell', 'defense', 'misc', 'fire', + 'lightning', 'acid', 'mind', 'arcane', 'blight', 'nature', + 'temporal', 'light', 'dark', 'antimagic' +} + +--- defined power sources, used for equipment generation, defined in class descriptors +_M.power_sources = table.map(function(k, v) return k, true end, table.keys_to_values({'technique','technique_ranged','nature','arcane','psionic','antimagic'})) + +--- map attributes to power restrictions for an entity +-- returns an updated list of forbidden power types including attributes +-- used for checking for compatible equipment and npc randboss classes +function _M:attrPowers(e, not_ps) + not_ps = table.clone(not_ps or e.not_power_source or e.forbid_power_source) or {} + if e.attr then + if e:attr("has_arcane_knowledge") then not_ps.antimagic = true end + if e:attr("undead") then not_ps.antimagic = true end + if e:attr("forbid_arcane") then not_ps.arcane = true end +-- if e:attr("forbid_nature") then not_ps.nature = true end + end + return not_ps +end + +--- Checks power_source compatibility between two entities +-- returns true if e2 is compatible with e1, false otherwise +-- by default, only checks .power_source vs. .forbid_power_source between entities +-- @param require_power if true, will also check that e2.power_source (if present) has a match in e1.power_source +-- use updatePowers to resolve conflicts. +function _M:checkPowers(e1, e2, require_power) + if not e1 or not e2 then return true end + local ok = true +--print("Comparing power sources",e1.name, e2.name) + -- check for excluded power sources first + local not_ps = self:attrPowers(e2) + for ps, _ in pairs(e1.power_source or {}) do + if not_ps[ps] then return false end + end + not_ps = self:attrPowers(e1) + for ps, _ in pairs(e2.power_source or {}) do + if not_ps[ps] then return false end + end + -- check for required power_sources + if require_power and e1.power_source and e2.power_source then + ok = false + for yes_ps, _ in pairs(e1.power_source) do + if (e2.power_source and e2.power_source[yes_ps]) then return true end + end + return false + end + return true +end + +--- Adjusts power source parameters and themes to remove conflicts +-- @param forbid_ps = {arcane = true, technique = true, ...} forbidden power sources <none> +-- @param allow_ps = {arcane = true, technique = true, ...} allowed power sources <all allowed> +-- @param randthemes = number of themes to pick randomly from the global pool <0> +-- @param force_themes = themes to always include {"attack", "antimagic", ...} applied last (optional) +-- themes included can add to forbid_ps and allow_ps +-- precedence is: forbid_ps > allow_ps > force_themes +-- returns new forbid_ps, allow_ps, themes (made consistent) +function _M:updatePowers(forbid_ps, allow_ps, randthemes, force_themes) + local spec_powers = allow_ps and next(allow_ps) + local yes_ps = spec_powers and table.clone(allow_ps) or table.clone(self.power_sources) + local not_ps = forbid_ps and table.clone(forbid_ps) or {} + local allthemes, themes = table.clone(self.power_themes), {} + local force_themes = force_themes and table.clone(force_themes) or {} + + for fps, _ in pairs(not_ps) do --enforce forbidden power restrictions + yes_ps[fps] = nil + if fps == "arcane" then + table.removeFromList(allthemes, 'spell', 'arcane', 'blight', 'temporal') + yes_ps.arcane = nil + elseif fps == "antimagic" then + table.removeFromList(allthemes, 'antimagic') + yes_ps.antimagic = nil + elseif fps == "nature" then + table.removeFromList(allthemes, 'nature') + elseif fps == "psionic" then + table.removeFromList(allthemes, 'mental', 'mind') + end + end + if spec_powers then --apply specified power sources + if yes_ps.antimagic then + not_ps.arcane = true + table.removeFromList(allthemes, 'spell', 'arcane', 'blight', 'temporal') + end + if yes_ps.arcane then + not_ps.antimagic = true + table.removeFromList(allthemes, 'antimagic') + end + if yes_ps.nature then + if not table.keys_to_values(allthemes).nature then table.insert(allthemes, 'nature') end + end + end + -- build themes list if needed beginning with those requested + local theme_count = (randthemes or 0) + #force_themes + for n = 1, theme_count do + local v = nil + if #force_themes > 0 then -- always add forced_themes if possible + v = rng.tableRemove(force_themes) + table.removeFromList(allthemes, v) + end + if not v then v = rng.tableRemove(allthemes) end -- pick from remaining themes + themes[#themes+1] = v + -- enforce theme-theme exclusions + if v == 'antimagic' then + table.removeFromList(allthemes, 'spell', 'arcane', 'blight', 'temporal') + yes_ps.antimagic, yes_ps.arcane = true, nil + not_ps.arcane = true + elseif v == 'spell' or v == 'arcane' or v == 'blight' or v == 'temporal' then + table.removeFromList(allthemes, 'antimagic') + yes_ps.antimagic, yes_ps.arcane = nil, true + not_ps.antimagic = true + elseif v == 'nature' then + table.removeFromList(allthemes, 'blight') + yes_ps.nature = true + elseif v == 'mind' or v == 'mental' then + yes_ps.psionic = true + elseif v == 'physical' then + yes_ps.technique, yes_ps.technique_ranged = true, true + end + end + return not_ps, yes_ps, themes +end + +--- Generate randarts for this state with optional parameters: +-- @param data.base = base object to add powers to (base.randart_able must be defined) <random object> +-- @param data.base_filter = filter passed to makeEntity when making base object +-- @param data.lev = character level to generate for (affects point budget, #themes and #powers) <12-50> +-- @param data.power_points_factor = lev based power points multiplier <1> +-- @param data.nb_points_add = #extra budget points to spend on random powers <0> +-- @param data.powers_special = function(p) that must return true on each random power to add (from base.randart_able) +-- @param data.nb_themes = #power themes (power groups) for random powers to use <scales to 5 with lev> +-- @param data.force_themes = additional power theme(s) to use for random powers = {"attack", "arcane", ...} +-- @param data.egos = total #egos to include (forced + random) <3> +-- @param data.greater_egos_bias = #egos that should be greater egos <2/3 * data.egos> +-- @param data.force_egos = list of egos ("egoname1", "egoname2", ...) to add first (overrides restrictions) +-- @param data.ego_special = function(e) on ego table that must return true for allowed egos +-- @param data.forbid_power_source = disallowed power type(s) for egos +-- eg:{arcane = true, psionic = true, technique = true, nature = true, antimagic = true} +-- note some objects always have a power source by default (i.e. wands are always arcane powered) +-- @param data.power_source = allowed power type(s) <all allowed> if specified, only egos matching at least one of the power types will be added. themes (random or forced) can add allowed power_sources +-- @param data.namescheme = parameters to be passed to the NameGenerator <local randart_name_rules table> +-- @param data.add_pool if true, adds the randart to the world artifact pool <nil> +-- @param data.post = function(o) to be applied to the randart after all egos and powers have been added and resolved function _M:generateRandart(data) - -- Setup level + -- Setup basic parameters and override global variables to match data + data = data or {} local lev = data.lev or rng.range(12, 50) + data.forbid_power_source = data.forbid_power_source or {} local oldlev = game.level.level local oldclev = resolvers.current_level game.level.level = lev @@ -258,71 +408,90 @@ function _M:generateRandart(data) local base = data.base or game.zone:makeEntity(game.level, "object", data.base_filter or {ignore_material_restriction=true, no_tome_drops=true, ego_filter={keep_egos=true, ego_chance=-1000}, special=function(e) return (not e.unique and e.randart_able) and (not e.material_level or e.material_level >= 2) and true or false end}, nil, true) - if not base then game.level.level = oldlev resolvers.current_level = oldclev return end + if not base or not base.randart_able then game.level.level = oldlev resolvers.current_level = oldclev return end local o = base:cloneFull() - local powers_list = engine.Object:loadList(o.randart_able, nil, nil, function(e) if e.rarity then e.rarity = math.ceil(e.rarity / 5) end end) +--o.baseobj = base:cloneFull() -- debugging code +--o.gendata = table.clone(data, true) -- debugging code + + -- Load possible random powers + local powers_list = engine.Object:loadList(o.randart_able, nil, nil, + function(e) + if data.powers_special and not data.powers_special(e) then e.rarity = nil end + if e.rarity then + e.rarity = math.ceil(e.rarity / 5) + end + end) + --print(" * loaded powers list:") o.randart_able = nil + ----------------------------------------------------------- + -- Pick Themes + ----------------------------------------------------------- local nb_themes = data.nb_themes if not nb_themes then -- Gradually increase number of themes at higher levels so there are enough powers to spend points on nb_themes = math.max(2,5*lev/(lev+50)) -- Maximum 5 themes possible nb_themes= math.floor(nb_themes) + (rng.percent((nb_themes-math.floor(nb_themes))*100) and 1 or 0) end - local allthemes = { - 'physical', 'mental', 'spell', 'defense', 'misc', 'fire', - 'lightning', 'acid', 'mind', 'arcane', 'blight', 'nature', - 'temporal', 'light', 'dark', 'antimagic' - } - local themes = {} - if data.force_themes then - for i, v in ipairs(data.force_themes) do - table.removeFromList(allthemes, v) - themes[v] = true - if v == 'antimagic' then table.removeFromList(allthemes, 'spell', 'arcane', 'blight', 'temporal') end - if v == 'spell' or v == 'arcane' or v == 'blight' or v == 'temporal' then table.removeFromList(allthemes, 'antimagic') end - end - end - for i = #themes + 1, (nb_themes or 2) do - if #allthemes == 0 then break end - local v = rng.tableRemove(allthemes) - themes[v] = true - if v == 'antimagic' then table.removeFromList(allthemes, 'spell', 'arcane', 'blight', 'temporal') end - if v == 'spell' or v == 'arcane' or v == 'blight' or v == 'temporal' then table.removeFromList(allthemes, 'antimagic') end - end - local themes_fct = function(e) - for theme, _ in pairs(e.theme) do if themes[theme] then return true end end - return false - end + -- update power sources and themes lists based on base object properties + local psource + if o.power_source then + psource = table.clone(o.power_source) + if data.power_source then table.merge(psource, data.power_source) end + -- forbid power sources that conflict with existing power source + data.forbid_power_source, psource = self:updatePowers(data.forbid_power_source, psource) + if data.power_source then data.power_source = psource end + end + -- resolve any power/theme conflicts with input data + local themes + data.forbid_power_source, psource, themes = self:updatePowers(data.forbid_power_source, data.power_source, nb_themes, data.force_themes) + if data.power_source then data.power_source = psource end + + themes = table.map(function(k, v) return k, true end, table.keys_to_values(themes)) ----------------------------------------------------------- -- Determine power ----------------------------------------------------------- - -- Note double diminishing returns when coupled with scaling factor in merger (below) + -- Note double diminishing returns when coupled with scaling factor in merger (below) -- Maintains randomness throughout level range ~50% variability in points - local points = math.ceil(0.1*lev^0.75*(8 + rng.range(1, 7)) * (data.power_points_factor or 1))+(data.nb_points_add or 0) + local points = math.max(0, math.ceil(0.1*lev^0.75*(8 + rng.range(1, 7)) * (data.power_points_factor or 1))+(data.nb_points_add or 0)) local nb_powers = 1 + rng.dice(math.max(1, math.ceil(0.281*lev^0.6)), 2) + (data.nb_powers_add or 0) + local nb_egos = data.egos or 3 + local gr_egos = data.greater_egos_bias or math.floor(nb_egos*2/3) -- 2/3 greater egos by default local powers = {} - print("Randart generation:", "level = ", lev, "points = ",points, "nb_powers = ",nb_powers) + print("Begin randart generation:", "level = ", lev, "egos =", nb_egos,"gr egos =", gr_egos, "rand themes = ", nb_themes, "points = ", points, "nb_powers = ",nb_powers) + if data.force_themes and #data.force_themes > 0 then print(" * forcing themes:",table.concat(data.force_themes,",")) end + print(" * using themes", table.concat(table.keys(themes), ',')) + local force_egos = table.clone(data.force_egos) + if force_egos then print(" * forcing egos:", table.concat(force_egos, ',')) end + if data.forbid_power_source and next(data.forbid_power_source) then print(" * forbid power sources:", table.concat(table.keys(data.forbid_power_source), ',')) end + if data.power_source and next(data.power_source) then print(" * allowed power sources:", table.concat(table.keys(data.power_source), ',')) end o.cost = o.cost + points * 7 + local use_themes = next(themes) and true or false -- Select some powers + local themes_fct = function(e) + if use_themes then + for theme, _ in pairs(e.theme) do if themes[theme] then return true end end + return false + end + return true + end local power_themes = {} - local lst = game.zone:computeRarities("powers", powers_list, game.level, themes_fct) --Note: probabilities diminish as level exceeds 50 (limited to ~1000 by updated game.zone:computeRarities function) - + local lst = game.zone:computeRarities("powers", powers_list, game.level, themes_fct) --Note: probabilities diminish as level exceeds 50 (limited to ~1000 by mod.class.zone:adjustComputeRaritiesLevel(level, type, lev)) + for i = 1, nb_powers do local p = game.zone:pickEntity(lst) if p then for t, _ in pairs(p.theme) do if themes[t] and randart_name_rules[t] then power_themes[t] = (power_themes[t] or 0) + 1 end end - powers[p.name] = p:clone() - powers[#powers+1] = powers[p.name] + powers[#powers+1] = p:clone() end end --- print("Selected powers:") table.print(powers) +-- print("Selected powers:") for i, p in ipairs(powers) do print(" * ",p.name, table.concat(table.keys(p.theme or {}), ",")) end power_themes = table.listify(power_themes) table.sort(power_themes, function(a, b) return a[2] < b[2] end) ----------------------------------------------------------- - -- Make up a name + -- Make up a name based on themes ----------------------------------------------------------- local themename = power_themes[#power_themes] themename = themename and themename[1] or nil @@ -340,62 +509,94 @@ function _M:generateRandart(data) name = ngd:generate().." the "..ngt:generate() end o.define_as = name:upper():gsub("[^A-Z]", "_") - - o.unided_name = rng.table{"glowing","scintillating","rune-covered","unblemished","jewel-encrusted","humming","gleaming","immaculate","flawless","crackling","glistening","plated","twisted","silvered","faceted","faded","sigiled","shadowy","laminated"}.." "..(o.unided_name or o.name) - + o.unided_name = rng.table(unided_names).." "..(o.unided_name or o.name) o.unique = name + o.name = name o.randart = true o.no_unique_lore = true o.rarity = rng.range(200, 290) - print("Creating randart "..name.."("..o.unided_name..") with "..(themename or "nil").." with level "..lev) - print(" * using themes", table.concat(table.keys(themes), ',')) + print("Creating randart "..name.."("..o.unided_name..") with "..(themename or "no themename")) ----------------------------------------------------------- - -- Add ego properties + -- Add ego properties (modified by power_source restrictions) ----------------------------------------------------------- - - local nb_egos = data.egos or 3 - local gr_egos = data.greater_egos_bias or math.floor(nb_egos*2/3) -- 2/3 greater egos by default if o.egos and nb_egos > 0 then local picked_egos = {} local legos = {} local been_greater = 0 - table.insert(legos, game.level:getEntitiesList("object/"..o.egos..":prefix")) - table.insert(legos, game.level:getEntitiesList("object/"..o.egos..":suffix")) - table.insert(legos, game.level:getEntitiesList("object/"..o.egos..":")) + -- merge all egos into one list to correctly calculate rarities + table.append(legos, game.level:getEntitiesList("object/"..o.egos..":prefix") or {}) + table.append(legos, game.level:getEntitiesList("object/"..o.egos..":suffix") or {}) + table.append(legos, game.level:getEntitiesList("object/"..o.egos..":") or {}) +--print(" * loaded ", #legos, "ego definitions") for i = 1, nb_egos or 3 do - local egos = rng.table(legos) local list = {} - local filter = nil - if rng.percent(100*lev/(lev+50)) and been_greater < gr_egos then been_greater = been_greater + 1 filter = function(e) return e.greater_ego end end --RE Phase out (but don't eliminate) lesser egos with level - for z = 1, #egos do list[#list+1] = egos[z].e end - - local ef = self:egoFilter(game.zone, game.level, "object", "randartego", o, {special=filter, forbid_power_source=data.forbid_power_source, power_source=data.power_source}, picked_egos, {}) - filter = ef.special + local gr_ego, ignore_filter = false, false + if rng.percent(100*lev/(lev+50)) and been_greater < gr_egos then -- Phase out (but don't eliminate) lesser egos with level + gr_ego = true + end + if force_egos then -- use forced egos list first + local found = false + repeat + local fego = rng.tableRemove(force_egos) + if not fego then break end + for z, e in ipairs(legos) do + if e.e.name:find(fego, nil, true) then +-- print(" * found forced ego", e.e.name) + list[1] = e.e + found = true + gr_ego, ignore_filter = false, true -- make sure forced ego is not filtered out later + break + end + end + until found or #force_egos <= 0 + if #force_egos == 0 then force_egos = nil end + end + if #list == 0 then -- no forced egos, copy the whole list + for z = 1, #legos do + list[#list+1] = legos[z].e + end + end + + local ef = self:egoFilter(game.zone, game.level, "object", "randartego", o, {special=data.ego_special, forbid_power_source=data.forbid_power_source, power_source=data.power_source}, picked_egos, {}) + + local filter = function(e) -- check ego definition properties + if ignore_filter then return true end + if not ef.special or ef.special(e) then + if gr_ego and not e.greater_ego then return false end + return game.state:checkPowers(ef, e, true) -- check power_source compatibility + end + end local pick_egos = game.zone:computeRarities("object", list, game.level, filter, nil, nil) local ego = game.zone:pickEntity(pick_egos) if ego then table.insert(picked_egos, ego) --- print(" ** selected ego", ego.name) + print(" ** selected ego", ego.name, (ego.greater_ego and "(greater)" or "(normal)"), ego.power_source and table.concat(table.keys(ego.power_source), ",")) ego = ego:clone() + if ego.greater_ego then been_greater = been_greater + 1 end if ego.instant_resolve then ego:resolve(nil, nil, o) end -- Don't allow resolvers.generic here (conflict) ego.instant_resolve = nil ego.uid = nil ego.name = nil ego.unided_name = nil - - -- OMFG this is ugly, there is a very rare combinaison that can result in a crash there, so we .. well, ignore it :/ + -- OMFG this is ugly, there is a very rare combination that can result in a crash there, so we .. well, ignore it :/ -- Sorry. + -- Fixed against overflow local ok, err = pcall(table.mergeAddAppendArray, o, ego, true) if not ok then - print("table.mergeAddAppendArray failed at creating a randart, retrying") + data.fails = (data.fails or 0) + 1 + print("table.mergeAddAppendArray failed at creating a randart, retrying", data.fails) game.level.level = oldlev resolvers.current_level = oldclev - return self:generateRandart(data) + if data.fails < 4 then return self:generateRandart(data) else return end end + else -- no ego found: increase budget for random powers to compensate + local xpoints = gr_ego and 8 or 5 + print((" ** no ego found (+%d points)"):format(xpoints)) + points = points + xpoints end end o.egos = nil o.egos_chance = nil o.force_ego = nil @@ -405,7 +606,7 @@ function _M:generateRandart(data) o:resolve(nil, true) ----------------------------------------------------------- - -- Imbue powers in the randart + -- Imbue random powers into the randart according to themes ----------------------------------------------------------- local function merger(dst, src, scale) --scale: factor to adjust power limits for levels higher than 50 scale = scale or 1 @@ -434,7 +635,7 @@ function _M:generateRandart(data) end end - -- Distribute points + -- Distribute points: half to any powers and half to a shortened list of powers to focus their effects local hpoints = math.ceil(points / 2) local i = 0 local fails = 0 @@ -463,9 +664,9 @@ function _M:generateRandart(data) fails = fails + 1 end end - o:resolve() o:resolve(nil, true) +-- o:resolve() o:resolve(nil, true) - -- Bias toward some powers + -- Bias towards a shortened list of powers local bias_powers = {} local nb_bias = math.max(1,rng.range(math.ceil(#powers/2), 20*lev /(lev+50))) --Limit bias powers to 20 (50/5 * 2) powers for i = 1, nb_bias do bias_powers[#bias_powers+1] = rng.table(powers) end @@ -478,6 +679,7 @@ function _M:generateRandart(data) local p = bias_powers[i] and bias_powers[i] if p and p.points <= hpoints * 2 then local scaleup = math.max(1,(lev/(p.level_range[2] or 50))^0.5) -- Adjust scaleup factor for each power based on lev and level_range max +-- print(" * adding bias power: "..p.name.."("..p.points.." points)") if p.wielder then o.wielder = o.wielder or {} merger(o.wielder, p.wielder, scaleup) @@ -491,64 +693,98 @@ function _M:generateRandart(data) merger(o.special_combat, p.special_combat, scaleup) end if p.copy then merger(o, p.copy, scaleup) end --- print(" * adding bias power: "..p.name.."("..p.points.." points)") hpoints = hpoints - p.points p.points = p.points * 1.5 --increased cost (=diminishing returns) on extra applications of the same power else fails = fails + 1 end end + o:resolve() o:resolve(nil, true) - -- Power source if none + -- Always assign at least one power source based on themes and restrictions if not o.power_source then - local ps = {} + local not_ps = data.forbid_power_source or {} + local ps = data.power_source or {} if themes.physical or themes.defense then ps.technique = true end if themes.mental then ps[rng.percent(50) and 'nature' or 'psionic'] = true end - if themes.spell or themes.arcane or themes.blight or themes.temporal then ps.arcane = true end + if themes.spell or themes.arcane or themes.blight or themes.temporal then + ps.arcane = true not_ps.antimagic = true + end if themes.nature then ps.nature = true end - if themes.antimagic then ps.antimagic = true end - if not next(ps) then ps[rng.table{'technique','nature','arcane','psionic','antimagic'}] = true end + if themes.antimagic then + ps.antimagic = true not_ps.arcane = true + end + if not next(ps) then ps[rng.tableIndex(data.power_source or self.power_sources)] = true end + ps = table.minus_keys(ps, not_ps) + if not next(ps) then ps = {unknown = true} end + print(" * using implied power source(s) ", table.concat(table.keys(ps), ',')) o.power_source = ps end - -- Setup the name - o.name = name - - local theme_map = { - physical = engine.DamageType.PHYSICAL, - --mental = engine.DamageType.MIND, - fire = engine.DamageType.FIRE, - lightning = engine.DamageType.LIGHTNING, - acid = engine.DamageType.ACID, - mind = engine.DamageType.MIND, - arcane = engine.DamageType.ARCANE, - blight = engine.DamageType.BLIGHT, - nature = engine.DamageType.NATURE, - temporal = engine.DamageType.TEMPORAL, - light = engine.DamageType.LIGHT, - dark = engine.DamageType.DARK, - } + -- Assign weapon damage + if o.combat and not (o.subtype == "staff" or o.subtype == "mindstar") then + local theme_map = { + physical = engine.DamageType.PHYSICAL, + --mental = engine.DamageType.MIND, + fire = engine.DamageType.FIRE, + lightning = engine.DamageType.LIGHTNING, + acid = engine.DamageType.ACID, + mind = engine.DamageType.MIND, + arcane = engine.DamageType.ARCANE, + blight = engine.DamageType.BLIGHT, + nature = engine.DamageType.NATURE, + temporal = engine.DamageType.TEMPORAL, + light = engine.DamageType.LIGHT, + dark = engine.DamageType.DARK, + } - local pickDamtype = function(themes_list) - if not rng.percent(18) then return engine.DamageType.PHYSICAL end - for k, v in pairs(themes_list) do - if theme_map[k] then return theme_map[k] end - end - return engine.DamageType.PHYSICAL + local pickDamtype = function(themes_list) + if not rng.percent(18) then return engine.DamageType.PHYSICAL end + for k, v in pairs(themes_list) do + if theme_map[k] then return theme_map[k] end + end + return engine.DamageType.PHYSICAL + end + o.combat.damtype = pickDamtype(themes) end - if o and o.combat and not (o.subtype and o.subtype == "staff") and not (o.subtype and o.subtype == "mindstar") then o.combat.damtype = pickDamtype(themes) end - if data.post then data.post(o) end if data.add_pool then self:addWorldArtifact(o) end - + -- restore global variables game.level.level = oldlev resolvers.current_level = oldclev return o end + +--- Adds randart properties (egos and random powers) to an existing object +-- @param o is the object to be updated (o.egos and o.randart_able should be defined as needed) +-- @param data is the table of randart parameters passed to generateRandart +-- usable powers and set properties are not overwritten if present +function _M:addRandartProperties(o, data) + print(" ** adding randart properties to ", o.name, o.uid) + data.base = o + -- properties to not overwrite + local protect_props = {name = true, uid=true, rarity = true, unided_name = true, define_as = true, unique = o.unique, randart = o.unique, no_unique_lore = true, require=true, egos = true, randart_able = true} + if o.use_power or o.use_talent or o.use_simple then -- allow only one use power + table.merge(protect_props, {use_power = true, use_talent = true, use_simple = true, + use_no_energy=true, use_no_blind = o.use_no_blind, use_no_silence = o.use_no_silence, use_no_wear = o.use_no_wear, + talent_cooldown = true, power = true, max_power=true, power_regen = true, charm_on_use = o.charm_on_use}) + end + if o.set_list then -- preserve set properties Note: mindstar set flags ARE copied + table.merge(protect_props, {set_list = true, on_set_complete = true, on_set_broken = true}) + end + print(" ** addRandartProperties: property merge restrictions: ", table.concat(table.keys(protect_props), ',')) + local art = game.state:generateRandart(data) + if art then + table.merge(o, art, true, protect_props, nil) + else + print(" ** FAILED to generate randart properties to add to ", o.name, o.uid) + end +end + local wda_cache = {} --- Runs the worldmap directory AI @@ -843,13 +1079,13 @@ function _M:checkDonation(back_insert) -- Total playtime must be over a few hours local total = profile.generic.modules_played and profile.generic.modules_played.tome or 0 - if total + (os.time() - game.real_starttime) < 7 * 60 * 60 then + if total + (os.time() - game.real_starttime) < 4 * 60 * 60 then print("Donation check: total time too low") return end -- Dont ask low level characters, they are probably still pissed to not have progressed further - if game.player.level < 15 then + if game.player.level < 10 then print("Donation check: too low level") return end @@ -1138,7 +1374,6 @@ function _M:entityFilterAlter(zone, level, type, filter) end -- If we request a specific type/subtype, we don't want categories that could make that not happen --- if filter.type or filter.subtype or filter.name then t.money = 0 end if filter.type or filter.subtype or filter.name then t.money = 0 t.lore = 0 end local u = t.uniques or 0 @@ -1193,7 +1428,6 @@ function _M:entityFilterAlter(zone, level, type, filter) filter.special = function(e) return e.type == "money" or e.type == "gem" end elseif r < l then --- print("[TOME ENTITY FILTER] selected Lore", r, m) print("[TOME ENTITY FILTER] selected Lore", r, l) filter.special = function(e) return e.lore and true or false end @@ -1259,18 +1493,6 @@ function _M:entityFilterPost(zone, level, type, e, filter) if _G.type(filter.random_boss) == "boolean" then filter.random_boss = {} else filter.random_boss = table.clone(filter.random_boss, true) end filter.random_boss.level = filter.random_boss.level or zone:level_adjust_level(level, zone, type) - filter.random_boss.class_filter = filter.random_boss.class_filter or function(c) - if e.power_source then - for ps, _ in pairs(e.power_source) do if c.power_source and c.power_source[ps] then return true end end - return false - end - if e.not_power_source then - for ps, _ in pairs(e.not_power_source) do if c.power_source and c.power_source[ps] then return false end end - return true - end - return true - end - e = self:createRandomBoss(e, filter.random_boss) elseif filter.random_elite and not e.unique then if _G.type(filter.random_elite) == "boolean" then filter.random_elite = {} @@ -1286,17 +1508,8 @@ function _M:entityFilterPost(zone, level, type, e, filter) no_loot_randart = true, resources_boost = 1.5, talent_cds_factor = (lev <= 10) and 3 or ((lev <= 20) and 2 or nil), - class_filter = filter.class_filter or function(c) - if e.power_source then - for ps, _ in pairs(e.power_source) do if c.power_source and c.power_source[ps] then return true end end - return false - end - if e.not_power_source then - for ps, _ in pairs(e.not_power_source) do if c.power_source and c.power_source[ps] then return false end end - return true - end - return true - end, + class_filter = filter.class_filter, + no_class_restrictions = filter.no_class_restrictions, level = lev, nb_rares = filter.random_elite.nb_rares or 1, check_talents_level = true, @@ -1306,23 +1519,6 @@ function _M:entityFilterPost(zone, level, type, e, filter) b.inc_damage = b.inc_damage or {} b.inc_damage.all = (b.inc_damage.all or 0) - 40 * (20 - data.level + 1) / 20 end - - -- Drop - for i = 1, data.nb_rares do - local o = game.zone:makeEntity(game.level,"object", {random_object=true}, nil, true) - if o then - b:addObject(b.INVEN_INVEN, o) - game.zone:addEntity(game.level, o, "object") - end - end - if data.user_post then data.user_post(b, data) end - end, - post = function(b, data) - if data.level <= 20 then - b.inc_damage = b.inc_damage or {} - b.inc_damage.all = (b.inc_damage.all or 0) - 40 * (20 - data.level + 1) / 20 - end - -- Drop for i = 1, data.nb_rares do -- generate rares as weak (1 ego) randarts local fil = {lev=lev, egos=1, greater_egos_bias = 0, forbid_power_source=b.not_power_source, @@ -1333,7 +1529,7 @@ function _M:entityFilterPost(zone, level, type, e, filter) } local o = game.state:generateRandart(fil,nil, true) if o then --- print("[entityFilterPost]: Generated random object for", tostring(b.name)) --RE +-- print("[entityFilterPost]: Generated random object for", tostring(b.name)) o.unique, o.randart, o.rare = nil, nil, true b:addObject(b.INVEN_INVEN, o) game.zone:addEntity(game.level, o, "object") @@ -1701,13 +1897,27 @@ function _M:createRandomZone(zbase) return zone, boss end +--- Add character classes to an actor updating stats, talents, and equipment +-- @param b = actor(boss) to update +-- @param data = optional parameters: +-- @param data.force_classes = specific classes to add {Corruptor = true, Bulwark = true, ...} ignores restrictions +-- @param data.nb_classes = random classes to add (in addition to any forced classes) <2> +-- @param data.class_filter = function(cdata) that must return true for any class picked. +-- (cdata = subclass definition in engine.Birther.birth_descriptor_def.subclass) +-- @param data.no_class_restrictions set true to skip class compatibility checks <nil> +-- @param data.add_trees = {["talent tree name 1"]=true, ["talent tree name 2"]=true, ..} additional talent trees to learn +-- @param data.check_talents_level set true to enforce talent level restrictions <nil> +-- @param data.auto_sustain set true to activate sustained talents at birth <nil> +-- @param data.forbid_equip set true for no equipment <nil> +-- @param data.loot_quality = drop table to use <"boss"> +-- @param data.drop_equipment set true to force dropping of equipment <nil> +-- @param instant set true to force instant learning of talents and generating golem <nil> function _M:applyRandomClass(b, data, instant) if not data.level then data.level = b.level end ------------------------------------------------------------ -- Apply talents from classes ------------------------------------------------------------ - -- Apply a class local Birther = require "engine.Birther" b.learn_tids = {} @@ -1720,7 +1930,18 @@ function _M:applyRandomClass(b, data, instant) if not mclass then return end print("Adding to random boss class", class.name, mclass.name) - if config.settings.cheat then b.desc = (b.desc or "").."\nClass: "..class.name end + -- add class to list and build inherent power sources + b.descriptor = b.descriptor or {} + b.descriptor.classes = b.descriptor.classes or {} + table.append(b.descriptor.classes, {class.name}) + + -- build inherent power sources and forbidden power sources + -- b.forbid_power_source --> b.not_power_source used for classes + b.power_source = table.merge(b.power_source or {}, class.power_source or {}) + b.not_power_source = table.merge(b.not_power_source or {}, class.not_power_source or {}) + -- update power source parameters with the new class + b.not_power_source, b.power_source = self:updatePowers(self:attrPowers(b, b.not_power_source), b.power_source) +print(" power types: not_power_source =", table.concat(table.keys(b.not_power_source),","), "power_source =", table.concat(table.keys(b.power_source),",")) -- Add stats if b.auto_stats then @@ -1745,6 +1966,7 @@ function _M:applyRandomClass(b, data, instant) for i, d in ipairs(resolver[1]) do d.name = nil d.ego_chance = nil + d.forbid_power_source=b.not_power_source d.tome_drops = data.loot_quality or "boss" d.force_drop = (data.drop_equipment == nil) and true or data.drop_equipment end @@ -1787,7 +2009,6 @@ function _M:applyRandomClass(b, data, instant) ok = false end end - if ok then list[t.id] = true end end end @@ -1812,27 +2033,50 @@ function _M:applyRandomClass(b, data, instant) end end end + return true end - -- Select two classes + -- Select classes local classes = Birther.birth_descriptor_def.subclass local list = {} local force_classes = data.force_classes and table.clone(data.force_classes) - for name, cdata in pairs(classes) do + for name, cdata in ipairs(classes) do if force_classes and force_classes[cdata.name] then apply_class(table.clone(cdata, true)) force_classes[cdata.name] = nil elseif not cdata.not_on_random_boss and (not cdata.random_rarity or rng.chance(cdata.random_rarity)) and (not data.class_filter or data.class_filter(cdata))then list[#list+1] = cdata end end - for i = 1, data.nb_classes or 2 do + local to_apply = data.nb_classes or 2 + while to_apply > 0 do local c = rng.tableRemove(list) - if not c then break end - apply_class(table.clone(c, true)) + if not c then break end --repeat attempts until list is exhausted + if data.no_class_restrictions or self:checkPowers(b, c) then -- recheck power restricts here to account for any previously picked classes + if apply_class(table.clone(c, true)) then to_apply = to_apply - 1 end + else + print(" class", c.name, " rejected due to power source") + end end end +--- Creates a random Boss (or elite) actor +-- @param base = base actor to add classes/talents to +-- calls _M:applyRandomClass(b, data, instant) to add classes, talents, and equipment based on class descriptors +-- handles data.nb_classes, data.force_classes, data.class_filter, ... +-- optional parameters: +-- @param data.level = minimum level range for actor generation <1> +-- @param data.rank = rank <3.5-4> +-- @param data.life_rating = function(b.life_rating) <1.7 * base.life_rating + 4-9> +-- @param data.resources_boost = multiplier for maximum resource pool sizes <3> +-- @param data.talent_cds_factor = multiplier for all talent cooldowns <1> +-- @param data.ai = ai_type <"tactical" if rank>3 or base.ai> +-- @param data.ai_tactic = tactical weights table for the tactical ai <nil - generated based on talents> +-- @param data.no_loot_randart set true to not drop a randart <nil> +-- @param data.on_die set true to run base.rng_boss_on_die and base.rng_boss_on_die_custom on death <nil> +-- @param data.name_scheme <randart_name_rules.default> +-- @param data.post = function(b, data) to run last to finish generation function _M:createRandomBoss(base, data) local b = base:clone() data = data or {level=1} + data.nb_classes = data.nb_classes or 2 ------------------------------------------------------------ -- Basic stuff, name, rank, ... @@ -1850,6 +2094,8 @@ function _M:createRandomBoss(base, data) else b.name = name.." the "..b.name end + print("Creating random boss ", b.name, data.level, "level", data.nb_classes, "classes") + if data.force_classes then print(" * forcing classes:",table.concat(table.keys(data.force_classes),",")) end b.unique = b.name b.randboss = true local boss_id = "RND_BOSS_"..b.name:upper():gsub("[^A-Z]", "_") @@ -1874,7 +2120,7 @@ function _M:createRandomBoss(base, data) -- Force resolving some stuff if type(b.max_life) == "table" and b.max_life.__resolver then b.max_life = resolvers.calc[b.max_life.__resolver](b.max_life, b, b, b, "max_life", {}) end - -- All bosses have alll body parts .. yes snake bosses can use archery and so on .. + -- All bosses have all body parts .. yes snake bosses can use archery and so on .. -- This is to prevent them from having unusable talents b.inven = {} b.body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, FINGER = 2, NECK = 1, LITE = 1, BODY = 1, HEAD = 1, CLOAK = 1, HANDS = 1, BELT = 1, FEET = 1, TOOL = 1, QUIVER = 1, QS_QUIVER = 1 } @@ -1889,12 +2135,6 @@ function _M:createRandomBoss(base, data) b.autolevel = "random_boss" b.auto_stats = {} - -- Always smart - if data.ai then b.ai = data.ai - else b.ai = (b.rank > 3) and "tactical" or b.ai - end - b.ai_state = { talent_in=1, ai_move=data.ai_move or "move_astar" } - -- Remove default equipment, if any local todel = {} for k, resolver in pairs(b) do if type(resolver) == "table" and resolver.__resolver and (resolver.__resolver == "equip" or resolver.__resolver == "drops") then todel[#todel+1] = k end end @@ -1946,15 +2186,27 @@ function _M:createRandomBoss(base, data) end end - -- Cheat a bit with ressources + -- Cheat a bit with resources self.max_mana = self.max_mana * (self._rndboss_resources_boost or 3) self.mana_regen = self.mana_regen + 1 self.max_vim = self.max_vim * (self._rndboss_resources_boost or 3) self.vim_regen = self.vim_regen + 1 + self.soul_regen = self.soul_regen + 0.5 self.max_stamina = self.max_stamina * (self._rndboss_resources_boost or 3) self.stamina_regen = self.stamina_regen + 1 self.max_psi = self.max_psi * (self._rndboss_resources_boost or 3) self.psi_regen = self.psi_regen + 2 self.equilibrium_regen = self.equilibrium_regen - 2 self:resetToFull() end + -- Update AI + if data.ai then b.ai = data.ai + else b.ai = (b.rank > 3) and "tactical" or b.ai + end + b.ai_state = { talent_in=1, ai_move=data.ai_move or "move_astar" } + if data.ai_tactic then + b.ai_tactic = data.ai_tactic + else + b[#b+1] = resolvers.talented_ai_tactic() --calculate ai_tactic table based on talents + end + -- Anything else if data.post then data.post(b, data) end @@ -1998,7 +2250,7 @@ end function _M:canEventGridRadius(level, x, y, radius, min) local list = {} for i = -radius, radius do for j = -radius, radius do - if game.state:canEventGrid(level, x+i, y+j) then list[#list+1] = {x=x+i, y=y+j} end + if game.state:canEventGrid(level, x+i, y+j) then list[#list+1] = {x=x+i, y=y+j, bx=x, by=y} end end end if #list < min then return false diff --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua index 4757058efcd96ada8a3a52c55ff05ed0031b8005..e9ff8aa25d844545c99cbd5580a68c281aa63489 100644 --- a/game/modules/tome/class/Object.lua +++ b/game/modules/tome/class/Object.lua @@ -66,7 +66,7 @@ function _M:getRequirementDesc(who) if self.require.stat and self.require.stat.str then self.require.stat.mag, self.require.stat.str = self.require.stat.str, nil end - + local desc = base_getRequirementDesc(self, who) self.require = oldreq @@ -171,6 +171,13 @@ end --- Describes an attribute, to expand object name function _M:descAttribute(attr) + local power = function(c) + if config.settings.tome.advanced_weapon_stats then + return math.floor(Combat.combatDamagePower({}, self.combat)*100).."% power" + else + return c.dam.."-"..(c.dam*(c.damrange or 1.1)).." power" + end + end if attr == "MASTERY" then local tms = {} for ttn, i in pairs(self.wielder.talents_types_mastery) do @@ -194,20 +201,20 @@ function _M:descAttribute(attr) return ("%s%0.2f/turn"):format(i > 0 and "+" or "-", math.abs(i)) elseif attr == "COMBAT" then local c = self.combat - return c.dam.."-"..(c.dam*(c.damrange or 1.1)).." power, "..(c.apr or 0).." apr" + return power(c)..", "..(c.apr or 0).." apr" elseif attr == "COMBAT_AMMO" then local c = self.combat - return c.shots_left.."/"..math.floor(c.capacity)..", "..c.dam.."-"..(c.dam*(c.damrange or 1.1)).." power, "..(c.apr or 0).." apr" + return c.shots_left.."/"..math.floor(c.capacity)..", "..power(c)..", "..(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, "..("%d"):format((c.apr or 0)).." apr, "..DamageType:get(c.damtype).name.." damage" + return power(c)..", "..("%d"):format((c.apr or 0)).." apr, "..DamageType:get(c.damtype).name.." damage" elseif attr == "COMBAT_ELEMENT" then local c = self.combat - return c.dam.."-"..(c.dam*(c.damrange or 1.1)).." power, "..("%d"):format((c.apr or 0)).." apr, "..DamageType:get(c.element or DamageType.PHYSICAL).name.." element" + return power(c)..", "..("%d"):format((c.apr or 0)).." apr, "..DamageType:get(c.element or DamageType.PHYSICAL).name.." element" elseif attr == "SHIELD" then local c = self.special_combat if c and (game.player:knowTalentType("technique/shield-offense") or game.player:knowTalentType("technique/shield-defense") or game.player:attr("show_shield_combat")) then - return c.dam.." dam, "..c.block.." block" + return power(c)..", "..c.block.." block" else return c.block.." block" end @@ -311,7 +318,7 @@ function _M:getName(t) if not t.no_add_name and (self.been_reshaped or self.been_imbued) then name = (type(self.been_reshaped) == "string" and self.been_reshaped or "") .. name .. (type(self.been_imbued) == "string" and self.been_imbued or "") end - + if not self:isIdentified() and not t.force_id and self:getUnidentifiedName() then name = self:getUnidentifiedName() end -- To extend later @@ -432,8 +439,13 @@ function _M:getTextualDesc(compare_with, use_actor) local add = false ret:add(text) local outformatres - if type(outformat) == "function" then outformatres = outformat() - else outformatres = outformat:format(((item1[field] or 0) + (add_table[field] or 0)) * mod) end + local resvalue = ((item1[field] or 0) + (add_table[field] or 0)) * mod + if type(outformat) == "function" then + local unworn_base = + (item1.wielded and resvalue) or + table.get(items, 1, infield, field) + outformatres = outformat(resvalue, unworn_base) + else outformatres = outformat:format(resvalue) end if isinversed then ret:add(((item1[field] or 0) + (add_table[field] or 0)) > 0 and {"color","RED"} or {"color","LIGHT_GREEN"}, outformatres, {"color", "LAST"}) else @@ -453,8 +465,10 @@ function _M:getTextualDesc(compare_with, use_actor) add = true if items[i][infield][field] ~= (item1[field] or 0) then local outformatres - if type(outformat) == "function" then outformatres = outformat() - else outformatres = outformat:format(((item1[field] or 0) - items[i][infield][field]) * mod) end + local resvalue = ((item1[field] or 0) - items[i][infield][field]) * mod + if type(outformat) == "function" then + outformatres = outformat(resvalue, resvalue) + else outformatres = outformat:format(resvalue) end if isdiffinversed then ret:add(items[i][infield][field] < (item1[field] or 0) and {"color","RED"} or {"color","LIGHT_GREEN"}, outformatres, {"color", "LAST"}) else @@ -474,6 +488,21 @@ function _M:getTextualDesc(compare_with, use_actor) end end + -- included - if we should include the value in the present total. + -- total_call - function to call on the actor to get the current total + local compare_scaled = function(item1, items, infield, change_field, results, outformat, text, included, mod, isinversed, isdiffinversed, add_table) + local out = function(base_change, unworn_base) + local from, to = 0, base_change + if unworn_base then + from = from - unworn_base + to = to - unworn_base + end + local scale_change = use_actor:getAttrChange(change_field, from, to, unpack(results)) + return outformat:format(base_change, scale_change) + end + return compare_fields(item1, items, infield, change_field, out, text, mod, isinversed, isdiffinversed, add_table) + end + local compare_table_fields = function(item1, items, infield, field, outformat, text, kfunct, mod, isinversed, filter) mod = mod or 1 isinversed = isinversed or false @@ -548,7 +577,9 @@ function _M:getTextualDesc(compare_with, use_actor) combat = combat[field] or {} compare_with = compare_with or {} local dm = {} - for stat, i in pairs(combat.dammod or {}) do + local dammod = {} + if next(combat.dammod or {}) then dammod = use_actor:getDammod(combat) end + for stat, i in pairs(dammod) do local name = Stats.stats_def[stat].short_name:capitalize() if use_actor:knowTalent(use_actor.T_STRENGTH_OF_PURPOSE) then if name == "Str" then name = "Mag" end @@ -578,7 +609,11 @@ function _M:getTextualDesc(compare_with, use_actor) else power_diff = ("(%s)"):format(power_diff) end - desc:add(("Base power: %.1f - %.1f"):format((combat.dam or 0) + (add_table.dam or 0), ((combat.damrange or (1.1 - (add_table.damrange or 0))) + (add_table.damrange or 0)) * ((combat.dam or 0) + (add_table.dam or 0)))) + if config.settings.tome.advanced_weapon_stats then + desc:add(("Power: %3d%% Range: %.1fx"):format(use_actor:combatDamagePower(combat, add_table.dam) * 100, use_actor:combatDamageRange(combat, add_table.damrange))) + else + desc:add(("Base power: %.1f - %.1f"):format((combat.dam or 0) + (add_table.dam or 0), ((combat.damrange or (1.1 - (add_table.damrange or 0))) + (add_table.damrange or 0)) * ((combat.dam or 0) + (add_table.dam or 0)))) + end desc:merge(power_diff:toTString()) desc:add(true) desc:add(("Uses stat%s: %s"):format(#dm > 1 and "s" or "",table.concat(dm, ', ')), true) @@ -868,19 +903,19 @@ function _M:getTextualDesc(compare_with, use_actor) local desc_wielder = function(w, compare_with, field) w = w or {} w = w[field] or {} - compare_fields(w, compare_with, field, "combat_atk", "%+d", "Accuracy: ") + compare_scaled(w, compare_with, field, "combat_atk", {"combatAttack"}, "%+d (%+d effective)", "Accuracy: ") compare_fields(w, compare_with, field, "combat_apr", "%+d", "Armour penetration: ") compare_fields(w, compare_with, field, "combat_physcrit", "%+.1f%%", "Physical crit. chance: ") - compare_fields(w, compare_with, field, "combat_dam", "%+d", "Physical power: ") + compare_scaled(w, compare_with, field, "combat_dam", {"combatPhysicalpower"}, "%+d (%+d effective)", "Physical power: ") compare_fields(w, compare_with, field, "combat_armor", "%+d", "Armour: ") compare_fields(w, compare_with, field, "combat_armor_hardiness", "%+d%%", "Armour Hardiness: ") - compare_fields(w, compare_with, field, "combat_def", "%+d", "Defense: ") - compare_fields(w, compare_with, field, "combat_def_ranged", "%+d", "Ranged Defense: ") + compare_scaled(w, compare_with, field, "combat_def", {"combatDefense", true}, "%+d (%+d effective)", "Defense: ") + compare_scaled(w, compare_with, field, "combat_def_ranged", {"combatDefenseRanged", true}, "%+d (%+d effective)", "Ranged Defense: ") compare_fields(w, compare_with, field, "fatigue", "%+d%%", "Fatigue: ", 1, true, true) - compare_fields(w, compare_with, field, "ammo_reload_speed", "%+d", "Ammo reloads per turns: ") + compare_fields(w, compare_with, field, "ammo_reload_speed", "%+d", "Ammo reloads per turn: ") local dt_string = tstring{} @@ -1018,12 +1053,12 @@ function _M:getTextualDesc(compare_with, use_actor) local any_esp = false local esps_compare = {} for i, v in ipairs(compare_with or {}) do - if v[field] and v[field].esp_all then + if v[field] and v[field].esp_all and v[field].esp_all > 0 then esps_compare["All"] = esps_compare["All"] or {} esps_compare["All"][1] = true any_esp = true end - for type, i in pairs(v[field] and (v[field].esp or {}) or {}) do + for type, i in pairs(v[field] and (v[field].esp or {}) or {}) do if i and i > 0 then local _, _, t, st = type:find("^([^/]+)/?(.*)$") local esp = "" if st and st ~= "" then @@ -1034,17 +1069,17 @@ function _M:getTextualDesc(compare_with, use_actor) esps_compare[esp] = esps_compare[esp] or {} esps_compare[esp][1] = true any_esp = true - end + end end end local esps = {} - if w.esp_all then + if w.esp_all and w.esp_all > 0 then esps[#esps+1] = "All" esps_compare[esps[#esps]] = esps_compare[esps[#esps]] or {} esps_compare[esps[#esps]][2] = true any_esp = true end - for type, i in pairs(w.esp or {}) do + for type, i in pairs(w.esp or {}) do if i and i > 0 then local _, _, t, st = type:find("^([^/]+)/?(.*)$") if st and st ~= "" then esps[#esps+1] = t:capitalize().."/"..st:capitalize() @@ -1054,7 +1089,7 @@ function _M:getTextualDesc(compare_with, use_actor) esps_compare[esps[#esps]] = esps_compare[esps[#esps]] or {} esps_compare[esps[#esps]][2] = true any_esp = true - end + end end if any_esp then desc:add("Grants telepathy: ") for esp, isin in pairs(esps_compare) do @@ -1087,7 +1122,7 @@ function _M:getTextualDesc(compare_with, use_actor) desc:add(("Talent master%s: "):format(any_mastery > 1 and "ies" or "y")) for ttn, ttid in pairs(masteries) do local tt = Talents.talents_types_def[ttn] - if tt then + if tt then local cat = tt.type:gsub("/.*", "") local name = cat:capitalize().." / "..tt.name:capitalize() local diff = (ttid[2] or 0) - (ttid[1] or 0) @@ -1143,18 +1178,18 @@ function _M:getTextualDesc(compare_with, use_actor) local learn_talents = {} for i, v in ipairs(compare_with or {}) do if v[field] and v[field].learn_talent then - for tid, tl in pairs(v[field].learn_talent) do + for tid, tl in pairs(v[field].learn_talent) do if tl > 0 then learn_talents[tid] = learn_talents[tid] or {} learn_talents[tid][1] = tl any_learn_talent = any_learn_talent + 1 - end + end end end end - for tid, tl in pairs(w.learn_talent or {}) do + for tid, tl in pairs(w.learn_talent or {}) do if tl > 0 then learn_talents[tid] = learn_talents[tid] or {} learn_talents[tid][2] = tl any_learn_talent = any_learn_talent + 1 - end + end end if any_learn_talent > 0 then desc:add(("Talent%s granted: "):format(any_learn_talent > 1 and "s" or "")) for tid, tl in pairs(learn_talents) do @@ -1209,9 +1244,9 @@ function _M:getTextualDesc(compare_with, use_actor) compare_fields(w, compare_with, field, "inc_stealth", "%+d", "Stealth bonus: ") compare_fields(w, compare_with, field, "max_encumber", "%+d", "Maximum encumbrance: ") - compare_fields(w, compare_with, field, "combat_physresist", "%+d", "Physical save: ") - compare_fields(w, compare_with, field, "combat_spellresist", "%+d", "Spell save: ") - compare_fields(w, compare_with, field, "combat_mentalresist", "%+d", "Mental save: ") + compare_scaled(w, compare_with, field, "combat_physresist", {"combatPhysicalResist", true}, "%+d (%+d effective)", "Physical save: ") + compare_scaled(w, compare_with, field, "combat_spellresist", {"combatSpellResist", true}, "%+d (%+d effective)", "Spell save: ") + compare_scaled(w, compare_with, field, "combat_mentalresist", {"combatMentalResist", true}, "%+d (%+d effective)", "Mental save: ") compare_fields(w, compare_with, field, "blind_immune", "%+d%%", "Blindness immunity: ", 100) compare_fields(w, compare_with, field, "poison_immune", "%+d%%", "Poison immunity: ", 100) @@ -1235,6 +1270,7 @@ function _M:getTextualDesc(compare_with, use_actor) compare_fields(w, compare_with, field, "mana_regen", "%+.2f", "Mana each turn: ") compare_fields(w, compare_with, field, "hate_regen", "%+.2f", "Hate each turn: ") compare_fields(w, compare_with, field, "psi_regen", "%+.2f", "Psi each turn: ") + compare_fields(w, compare_with, field, "vim_regen", "%+.2f", "Vim each turn: ") compare_fields(w, compare_with, field, "positive_regen_ref_mod", "%+.2f", "P.Energy each turn: ") compare_fields(w, compare_with, field, "negative_regen_ref_mod", "%+.2f", "N.Energy each turn: ") @@ -1243,6 +1279,9 @@ function _M:getTextualDesc(compare_with, use_actor) compare_fields(w, compare_with, field, "equilibrium_regen_when_hit", "%+.2f", "Equilibrium when hit: ") compare_fields(w, compare_with, field, "psi_regen_when_hit", "%+.2f", "Psi when hit: ") compare_fields(w, compare_with, field, "hate_regen_when_hit", "%+.2f", "Hate when hit: ") + compare_fields(w, compare_with, field, "vim_regen_when_hit", "%+.2f", "Vim when hit: ") + + compare_fields(w, compare_with, field, "vim_on_melee", "%+.2f", "Vim when hitting in melee: ") compare_fields(w, compare_with, field, "mana_on_crit", "%+.2f", "Mana when firing critical spell: ") compare_fields(w, compare_with, field, "vim_on_crit", "%+.2f", "Vim when firing critical spell: ") @@ -1254,6 +1293,7 @@ function _M:getTextualDesc(compare_with, use_actor) compare_fields(w, compare_with, field, "hate_per_kill", "+%0.2f", "Hate per kill: ") compare_fields(w, compare_with, field, "psi_per_kill", "+%0.2f", "Psi per kill: ") + compare_fields(w, compare_with, field, "vim_on_death", "%+.2f", "Vim per kill: ") compare_fields(w, compare_with, field, "die_at", "%+.2f life", "Only die when reaching: ", 1, true, true) compare_fields(w, compare_with, field, "max_life", "%+.2f", "Maximum life: ") @@ -1267,11 +1307,11 @@ function _M:getTextualDesc(compare_with, use_actor) compare_fields(w, compare_with, field, "max_negative", "%+.2f", "Maximum neg.energy: ") compare_fields(w, compare_with, field, "max_air", "%+.2f", "Maximum air capacity: ") - compare_fields(w, compare_with, field, "combat_spellpower", "%+d", "Spellpower: ") + compare_scaled(w, compare_with, field, "combat_spellpower", {"combatSpellpower"}, "%+d (%+d effective)", "Spellpower: ") compare_fields(w, compare_with, field, "combat_spellcrit", "%+d%%", "Spell crit. chance: ") compare_fields(w, compare_with, field, "spell_cooldown_reduction", "%d%%", "Lowers spell cool-downs by: ", 100) - compare_fields(w, compare_with, field, "combat_mindpower", "%+d", "Mindpower: ") + compare_scaled(w, compare_with, field, "combat_mindpower", {"combatMindpower"}, "%+d (%+d effective)", "Mindpower: ") compare_fields(w, compare_with, field, "combat_mindcrit", "%+d%%", "Mental crit. chance: ") compare_fields(w, compare_with, field, "lite", "%+d", "Light radius: ") @@ -1448,6 +1488,16 @@ function _M:getTextualDesc(compare_with, use_actor) desc_wielder(self, compare_with, "carrier") end + if self.is_tinker then + if self.on_type then desc:add("Attach on item of type '", {"color","ORANGE"}, self.on_type, {"color", "LAST"}, "'", true) end + if self.on_slot then desc:add("Attach on item worn on slot '", {"color","ORANGE"}, self.on_slot:lower():gsub('_', ' '), {"color", "LAST"}, "'", true) end + + if self.object_tinker and self.object_tinker.wielder then + desc:add({"color","YELLOW"}, "When attach to an other item:", {"color", "LAST"}, true) + desc_wielder(self.object_tinker, compare_with, "wielder") + end + end + if self.special_desc then local d = self:special_desc() desc:add({"color", "ROYAL_BLUE"}) @@ -1491,6 +1541,13 @@ function _M:getTextualDesc(compare_with, use_actor) end end + local latent = table.get(self.color_attributes, 'damage_type') + if latent then + latent = DamageType:get(latent) or {} + desc:add({"color","YELLOW",}, "Latent Damage Type: ", {"color","LAST",}, + latent.text_color or "#WHITE#", latent.name:capitalize(), {"color", "LAST",}, true) + end + if self.inscription_data and self.inscription_talent then use_actor.__inscription_data_fake = self.inscription_data local t = self:getTalentFromId("T_"..self.inscription_talent.."_1") @@ -1659,9 +1716,9 @@ function _M:getDesc(name_param, compare_with, never_compare, use_actor) if self.encumber then desc:add({"color",0x67,0xAD,0x00}, ("%0.2f Encumbrance."):format(self.encumber), {"color", "LAST"}) end - if self.ego_bonus_mult then - desc:add(true, {"color",0x67,0xAD,0x00}, ("%0.2f Ego Multiplier."):format(1 + self.ego_bonus_mult), {"color", "LAST"}) - end + -- if self.ego_bonus_mult then + -- desc:add(true, {"color",0x67,0xAD,0x00}, ("%0.2f Ego Multiplier."):format(1 + self.ego_bonus_mult), {"color", "LAST"}) + -- end local could_compare = false if not name_param.force_compare and not core.key.modState("ctrl") then @@ -1745,6 +1802,7 @@ function _M:getPriceFlags() if w.mana_regen_when_hit then price = price + w.mana_regen_when_hit * 3 end if w.psi_regen_when_hit then price = price + w.psi_regen_when_hit * 3 end if w.hate_regen_when_hit then price = price + w.hate_regen_when_hit * 3 end + if w.vim_regen_when_hit then price = price + w.vim_regen_when_hit * 3 end if w.mana_on_crit then price = price + w.mana_on_crit * 3 end if w.vim_on_crit then price = price + w.vim_on_crit * 3 end if w.psi_on_crit then price = price + w.psi_on_crit * 3 end @@ -1921,3 +1979,15 @@ function _M:addedToLevel(level, x, y) self.__price_level_mod = util.getval(level.data.objects_cost_modifier, self) end end + +function _M:getTinker() + return self.tinker +end + +function _M:canAttachTinker(tinker, override) + if not tinker.is_tinker then return end + if tinker.on_type and tinker.on_type ~= rawget(self, "type") then return end + if tinker.on_slot and tinker.on_slot ~= self.slot then return end + if self.tinker and not override then return end + return true +end diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index dbe0fdc9c091671996dbfdbf4d916031068a425d..5bebc15e0570f326e93d330feae3cd27f50572b7 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -868,6 +868,7 @@ function _M:onRestStart() self:setEffect(self.EFF_SPACETIME_TUNING, duration, {power=power}) end end + self:fireTalentCheck("callbackOnRest", "start") end --- We stopped resting @@ -880,6 +881,7 @@ function _M:onRestStop() self:attr("mana_regen", -self.resting.mana_regen) self.resting.mana_regen = nil end + self:fireTalentCheck("callbackOnRest", "stop") end --- Can we continue resting ? @@ -921,6 +923,7 @@ function _M:restCheck() if self:getMana() < self:getMaxMana() and self.mana_regen > 0 then return true end if self:getStamina() < self:getMaxStamina() and self.stamina_regen > 0 then return true end if self:getPsi() < self:getMaxPsi() and self.psi_regen > 0 then return true end + if self:getVim() < self:getMaxVim() and self.vim_regen > 0 then return true end if self:getEquilibrium() > self:getMinEquilibrium() and self.equilibrium_regen < 0 then return true end if self.life < self.max_life and self.life_regen> 0 then return true end for act, def in pairs(game.party.members) do if game.level:hasEntity(act) and not act.dead then @@ -928,7 +931,7 @@ function _M:restCheck() end end if ammo and ammo.combat.shots_left < ammo.combat.capacity then return true end - if self:fireTalentCheck("callbackOnRest") then return true end + if self:fireTalentCheck("callbackOnRest", "check") then return true end else return true end @@ -940,9 +943,10 @@ function _M:restCheck() if self.resting.wait_cooldowns then for tid, cd in pairs(self.talents_cd) do - if self:isTalentActive(self.T_CONDUIT) and (tid == self.T_KINETIC_AURA or tid == self.T_CHARGED_AURA or tid == self.T_THERMAL_AURA) then +-- if self:isTalentActive(self.T_CONDUIT) and (tid == self.T_KINETIC_AURA or tid == self.T_CHARGED_AURA or tid == self.T_THERMAL_AURA) then -- nothing - elseif self.talents_auto[tid] then +-- else + if self.talents_auto[tid] then -- nothing else if cd > 0 then return true end @@ -986,6 +990,8 @@ function _M:runCheck(ignore_memory) return false, ("hostile spotted to the %s (%s%s)"):format(dir or "???", spotted[1].actor.name, game.level.map:isOnScreen(spotted[1].x, spotted[1].y) and "" or " - offscreen") end + if self:fireTalentCheck("callbackOnRun") then return false, "talent prevented" end + if self.air_regen < 0 and self.air < 0.75 * self.max_air then return false, "losing breath!" end -- Notice any noticeable terrain @@ -1139,7 +1145,6 @@ function _M:playerPickup() end) else local o = self:pickupFloor(1, true) - self:sortInven() if o and type(o) == "table" then self:useEnergy() o.__new_pickup = true @@ -1217,7 +1222,7 @@ function _M:playerUseItem(object, item, inven) self:breakStepUp() self:breakStealth() self:breakLightningSpeed() - -- self:breakPsionicChannel() + self:breakSpacetimeTuning() return true end @@ -1225,7 +1230,7 @@ function _M:playerUseItem(object, item, inven) self:breakStealth() self:breakLightningSpeed() self:breakReloading() - -- self:breakPsionicChannel() + self:breakSpacetimeTuning() self.changed = true end) local ok, ret = coroutine.resume(co) diff --git a/game/modules/tome/class/Projectile.lua b/game/modules/tome/class/Projectile.lua index 32514ece23f2a7736d1f9d84e9ef956776fc2e30..c70d40a3226a0dee1bdedda9eeb83dcf5d15301e 100644 --- a/game/modules/tome/class/Projectile.lua +++ b/game/modules/tome/class/Projectile.lua @@ -36,7 +36,7 @@ function _M:move(x, y, force) local moved = engine.Projectile.move(self, x, y, force) if moved and not force and ox and oy and (ox ~= self.x or oy ~= self.y) and config.settings.tome.smooth_move > 0 then - self:setMoveAnim(ox, oy, config.settings.tome.smooth_move, nil) + self:setMoveAnim(ox, oy, config.settings.tome.smooth_move, 0) end return moved diff --git a/game/modules/tome/class/Store.lua b/game/modules/tome/class/Store.lua index 2884a5e86f218833f479f1670fed0e025023071d..219b2a51859507d5634c9ead9084d19f90a2e646 100644 --- a/game/modules/tome/class/Store.lua +++ b/game/modules/tome/class/Store.lua @@ -113,6 +113,7 @@ end -- @param nb number of items (if stacked) to sell -- @return true if allowed to sell function _M:trySell(who, o, item, nb) + if o.__tagged then return end local price = self:getObjectPrice(o, "sell") if price <= 0 or nb <= 0 then return end price = math.min(price * nb, self.store.purse * nb) @@ -131,7 +132,7 @@ function _M:onBuy(who, o, item, nb, before) local price = self:getObjectPrice(o, "buy") if who.money >= price * nb then who:incMoney(- price * nb) - game.log("Bought: %s for %0.2f gold.", o:getName{do_color=true}, price * nb) + game.log("Bought: %s %s for %0.2f gold.", nb>1 and nb or "", o:getName{do_color=true, no_count = true}, price * nb) end end @@ -150,7 +151,7 @@ function _M:onSell(who, o, item, nb, before) price = math.min(price * nb, self.store.purse * nb) who:incMoney(price) o:forAllStack(function(so) so.__force_store_forget = true end) -- Make sure the store does forget about it when it restocks - game.log("Sold: %s for %0.2f gold.", o:getName{do_color=true}, price) + game.log("Sold: %s %s for %0.2f gold.", nb>1 and nb or "", o:getName{do_color=true, no_count = true}, price) end --- Override the default diff --git a/game/modules/tome/class/WorldNPC.lua b/game/modules/tome/class/WorldNPC.lua index d698ade3b33d735b4fefccd46a693b43709b08af..0ddfc02b6354a3a3cc84cde184a1a6d8a03a8eb4 100644 --- a/game/modules/tome/class/WorldNPC.lua +++ b/game/modules/tome/class/WorldNPC.lua @@ -53,8 +53,6 @@ function _M:bumpInto(target, x, y) elseif target.cant_be_moved and self.cant_be_moved and target.x and target.y and self.x and self.y then -- Displace local tx, ty, sx, sy = target.x, target.y, self.x, self.y - target.x = nil target.y = nil - self.x = nil self.y = nil target:move(sx, sy, true) self:move(tx, ty, true) end diff --git a/game/modules/tome/class/Zone.lua b/game/modules/tome/class/Zone.lua index 9d060973574c91a8f3e070bf234882174fedc3cb..0b6091cbdde85d6df449c27196ddddc22a63b3ff 100644 --- a/game/modules/tome/class/Zone.lua +++ b/game/modules/tome/class/Zone.lua @@ -50,6 +50,8 @@ function _M:onLoadZoneFile(basedir) local f = loadfile(basedir.."events.lua") setfenv(f, setmetatable({self=self}, {__index=_G})) self.events = f() + + self:triggerHook{"Zone:loadEvents", zone=self.short_name, events=self.events} end end diff --git a/game/modules/tome/class/interface/Archery.lua b/game/modules/tome/class/interface/Archery.lua index 5cc7fb821e071eb858f5fa68a2ef87d6bf0aa059..d3bb3277211308c7d52ec37dbcaf661e67e49f8c 100644 --- a/game/modules/tome/class/interface/Archery.lua +++ b/game/modules/tome/class/interface/Archery.lua @@ -429,12 +429,9 @@ local function archery_projectile(tx, ty, tg, self, tmp) -- Temporal cast if hitted and self:knowTalent(self.T_WEAPON_FOLDING) and self:isTalentActive(self.T_WEAPON_FOLDING) then local dam = self:callTalent(self.T_WEAPON_FOLDING, "getDamage") - local burst_damage = 0 - local burst_radius = 0 if self:knowTalent(self.T_FRAYED_THREADS) then - burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") - burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") - dam = dam - burst_damage + local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") + local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.TEMPORAL, burst_damage) end if dam > 0 and not target.dead then @@ -444,54 +441,30 @@ local function archery_projectile(tx, ty, tg, self, tmp) if hitted and self:knowTalent(self.T_IMPACT) and self:isTalentActive(self.T_IMPACT) then local dam = self:callTalent(self.T_IMPACT, "getDamage") local power = self:callTalent(self.T_IMPACT, "getApplyPower") - local burst_damage = 0 - local burst_radius = 0 if self:knowTalent(self.T_FRAYED_THREADS) then - burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") - burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") - dam = dam - burst_damage - self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=dam/2, power_check=power}) + local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") + local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") + self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=burst_damage/2, power_check=power}) end if dam > 0 and not target.dead then DamageType:get(DamageType.IMPACT).projector(self, target.x, target.y, DamageType.IMPACT, {dam=dam, daze=dam/2, power_check=power}, tmp) end end - -- Conduit (Psi) - if hitted and not target.dead and self:knowTalent(self.T_CONDUIT) and self:isTalentActive(self.T_CONDUIT) and self:attr("use_psi_combat") then - local t = self:getTalentFromId(self.T_CONDUIT) - --t.do_combat(self, t, target) - local mult = 1 + 0.2*(self:getTalentLevel(t)) - local auras = self:isTalentActive(t.id) - if auras.k_aura_on then - local k_aura = self:getTalentFromId(self.T_KINETIC_AURA) - local k_dam = mult * k_aura.getAuraStrength(self, k_aura) - DamageType:get(DamageType.PHYSICAL).projector(self, target.x, target.y, DamageType.PHYSICAL, k_dam, tmp) - end - if auras.t_aura_on then - local t_aura = self:getTalentFromId(self.T_THERMAL_AURA) - local t_dam = mult * t_aura.getAuraStrength(self, t_aura) - DamageType:get(DamageType.FIRE).projector(self, target.x, target.y, DamageType.FIRE, t_dam, tmp) - end - if auras.c_aura_on then - local c_aura = self:getTalentFromId(self.T_CHARGED_AURA) - local c_dam = mult * c_aura.getAuraStrength(self, c_aura) - DamageType:get(DamageType.LIGHTNING).projector(self, target.x, target.y, DamageType.LIGHTNING, c_dam, tmp) - end - end - - - -- Regen on being hit - if hitted and not target.dead and target:attr("stamina_regen_when_hit") then target:incStamina(target.stamina_regen_when_hit) end - if hitted and not target.dead and target:attr("mana_regen_when_hit") then target:incMana(target.mana_regen_when_hit) end - if hitted and not target.dead and target:attr("equilibrium_regen_when_hit") then target:incEquilibrium(-target.equilibrium_regen_when_hit) end - if hitted and not target.dead and target:attr("psi_regen_when_hit") then target:incPsi(target.psi_regen_when_hit) end - if hitted and not target.dead and target:attr("hate_regen_when_hit") then target:incHate(target.hate_regen_when_hit) end - - -- Resource regen on hit - if hitted and self:attr("stamina_regen_on_hit") then self:incStamina(self.stamina_regen_on_hit) end - if hitted and self:attr("mana_regen_on_hit") then self:incMana(self.mana_regen_on_hit) end + if self ~= target then + -- Regen on being hit + if hitted and not target.dead and target:attr("stamina_regen_when_hit") then target:incStamina(target.stamina_regen_when_hit) end + if hitted and not target.dead and target:attr("mana_regen_when_hit") then target:incMana(target.mana_regen_when_hit) end + if hitted and not target.dead and target:attr("equilibrium_regen_when_hit") then target:incEquilibrium(-target.equilibrium_regen_when_hit) end + if hitted and not target.dead and target:attr("psi_regen_when_hit") then target:incPsi(target.psi_regen_when_hit) end + if hitted and not target.dead and target:attr("hate_regen_when_hit") then target:incHate(target.hate_regen_when_hit) end + if hitted and not target.dead and target:attr("vim_regen_when_hit") then target:incVim(target.vim_regen_when_hit) end + -- Resource regen on hit + if hitted and self:attr("stamina_regen_on_hit") then self:incStamina(self.stamina_regen_on_hit) end + if hitted and self:attr("mana_regen_on_hit") then self:incMana(self.mana_regen_on_hit) end + end + -- Ablative armor if hitted and not target.dead and target:attr("carbon_spikes") then if target.carbon_armor >= 1 then diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index 18d7618dc44502c22f5b106201a3f32e1fded0c2..af00e7a266205c26fce0c24bd920f9e0579afb14 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -58,18 +58,17 @@ function _M:bumpInto(target, x, y) -- Displace local tx, ty, sx, sy = target.x, target.y, self.x, self.y - target.x = nil target.y = nil - self.x = nil self.y = nil - target:move(sx, sy, true) self:move(tx, ty, true) if target.describeFloor then target:describeFloor(target.x, target.y, true) end if self.describeFloor then self:describeFloor(self.x, self.y, true) end + local energy = game.energy_to_act * self:combatMovementSpeed(x, y) if self:attr("bump_swap_speed_divide") then - self:useEnergy(game.energy_to_act * self:combatMovementSpeed(x, y) / self:attr("bump_swap_speed_divide")) - self.did_energy = true + energy = energy / self:attr("bump_swap_speed_divide") end + self:useEnergy(energy) + self.did_energy = true end end end @@ -89,6 +88,7 @@ function _M:attackTarget(target, damtype, mult, noenergy, force_unharmed) -- Break before we do the blow, because it might start step up, we dont want to insta-cancel it self:breakStepUp() + self:breakSpacetimeTuning() if self:attr("feared") then if not noenergy then @@ -119,12 +119,15 @@ function _M:attackTarget(target, damtype, mult, noenergy, force_unharmed) local ret = target:callTalent(target.T_INTUITIVE_SHOTS, "proc", self) if ret then return false end end - - if not target.turn_procs.warding_weapon and target:knowTalent(target.T_WARDING_WEAPON) and target:getTalentLevelRaw(target.T_WARDING_WEAPON) >= 5 - and rng.percent(target:callTalent(target.T_WARDING_WEAPON, "getChance")) and target:getPsi() >= 15 then - target:setEffect(target.EFF_WEAPON_WARDING, 1, {}) - target.turn_procs.warding_weapon = true - target:incPsi(-15) + + if not target.turn_procs.warding_weapon and target:knowTalent(target.T_WARDING_WEAPON) and target:getTalentLevelRaw(target.T_WARDING_WEAPON) >= 5 + and rng.percent(target:callTalent(target.T_WARDING_WEAPON, "getChance")) then + local t = self:getTalentFromId(self.T_WARDING_WEAPON) + if target:getPsi() >= t.psi then + target:setEffect(target.EFF_WEAPON_WARDING, 1, {}) + target.turn_procs.warding_weapon = true + target:incPsi(-t.psi) + end end -- Change attack type if using gems @@ -132,7 +135,8 @@ function _M:attackTarget(target, damtype, mult, noenergy, force_unharmed) local gems = self:getInven(self.INVEN_GEM) local types = {} for i = 1, #gems do - if gems[i] and gems[i].attack_type then types[#types+1] = gems[i].attack_type end + local damtype = table.get(gems[i], 'color_attributes', 'damage_type') + if damtype then table.insert(types, damtype) end end if #types > 0 then damtype = rng.table(types) @@ -350,6 +354,10 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) damtype = damtype or (weapon and weapon.damtype) or DamageType.PHYSICAL mult = mult or 1 + if self:attr("force_melee_damtype") then + damtype = self.force_melee_damtype + end + --Life Steal if weapon and weapon.lifesteal then self:attr("lifesteal", weapon.lifesteal) @@ -403,6 +411,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) local hitted = false local crit = false local evaded = false + local old_target_life = target.life if target:knowTalent(target.T_SKIRMISHER_BUCKLER_EXPERTISE) then local t = target:getTalentFromId(target.T_SKIRMISHER_BUCKLER_EXPERTISE) @@ -540,6 +549,8 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) target:fireTalentCheck("callbackOnMeleeHit", self, dam) hitted = true + + if self:attr("vim_on_melee") and self ~= target then self:incVim(self:attr("vim_on_melee")) end else self:logCombat(target, "#Source# misses #Target#.") target:fireTalentCheck("callbackOnMeleeMiss", self, dam) @@ -620,9 +631,8 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) local burst_damage = 0 local burst_radius = 0 if self:knowTalent(self.T_FRAYED_THREADS) then - burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") - burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") - dam = dam - burst_damage + local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") + local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.TEMPORAL, burst_damage) end if dam > 0 and not target.dead then @@ -632,13 +642,10 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) if hitted and self:knowTalent(self.T_IMPACT) and self:isTalentActive(self.T_IMPACT) then local dam = self:callTalent(self.T_IMPACT, "getDamage") local power = self:callTalent(self.T_IMPACT, "getApplyPower") - local burst_damage = 0 - local burst_radius = 0 if self:knowTalent(self.T_FRAYED_THREADS) then - burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") - burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") - dam = dam - burst_damage - self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=dam/2, power_check=power}) + local burst_damage = dam * self:callTalent(self.T_FRAYED_THREADS, "getPercent") + local burst_radius = self:callTalent(self.T_FRAYED_THREADS, "getRadius") + self:project({type="ball", radius=burst_radius, friendlyfire=false}, target.x, target.y, DamageType.IMPACT, {dam=burst_damage, daze=burst_damage/2, power_check=power}) end if dam > 0 and not target.dead then DamageType:get(DamageType.IMPACT).projector(self, target.x, target.y, DamageType.IMPACT, {dam=dam, daze=dam/2, power_check=power}, tmp) @@ -680,18 +687,15 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) -- Shattering Impact if hitted and self:attr("shattering_impact") and (not self.shattering_impact_last_turn or self.shattering_impact_last_turn < game.turn) then local dam = dam * self.shattering_impact - local invuln = target.invulnerable game.logSeen(target, "The shattering blow creates a shockwave!") - target.invulnerable = 1 -- Target already hit, don't damage it twice - self:project({type="ball", radius=1, selffire=false}, target.x, target.y, DamageType.PHYSICAL, dam) - target.invulnerable = invuln + self:project({type="ball", radius=1, selffire=false, act_exclude={[target.uid]=true}}, target.x, target.y, DamageType.PHYSICAL, dam) -- don't hit target with the AOE self:incStamina(-8) self.shattering_impact_last_turn = game.turn end -- Damage Backlash - if dam > 0 and self.attr and self:attr("damage_backfire") then - local hurt = math.min(dam, target.life) * self.damage_backfire / 100 + if dam > 0 and self:attr("damage_backfire") then + local hurt = math.min(dam, old_target_life) * self.damage_backfire / 100 if hurt > 0 then self:takeHit(hurt, self) end @@ -782,20 +786,20 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) -- Psi Auras local psiweapon = self:getInven("PSIONIC_FOCUS") and self:getInven("PSIONIC_FOCUS")[1] if psiweapon and psiweapon.combat and psiweapon.subtype ~= "mindstar" then - if hitted and not target.dead and self:knowTalent(self.T_KINETIC_AURA) and self:isTalentActive(self.T_KINETIC_AURA) and (self.use_psi_combat or self:hasEffect(EFF_TRANSCENDENT_TELEKINESIS)) then + if hitted and not target.dead and self:knowTalent(self.T_KINETIC_AURA) and self:isTalentActive(self.T_KINETIC_AURA) and self.use_psi_combat then local t = self:getTalentFromId(self.T_KINETIC_AURA) t.do_combat(self, t, target) end - if hitted and not target.dead and self:knowTalent(self.T_THERMAL_AURA) and self:isTalentActive(self.T_THERMAL_AURA) and (self.use_psi_combat or self:hasEffect(EFF_TRANSCENDENT_TELEKINESIS)) then + if hitted and not target.dead and self:knowTalent(self.T_THERMAL_AURA) and self:isTalentActive(self.T_THERMAL_AURA) and self.use_psi_combat then local t = self:getTalentFromId(self.T_THERMAL_AURA) t.do_combat(self, t, target) end - if hitted and not target.dead and self:knowTalent(self.T_CHARGED_AURA) and self:isTalentActive(self.T_CHARGED_AURA) and (self.use_psi_combat or self:hasEffect(EFF_TRANSCENDENT_TELEKINESIS)) then + if hitted and not target.dead and self:knowTalent(self.T_CHARGED_AURA) and self:isTalentActive(self.T_CHARGED_AURA) and self.use_psi_combat then local t = self:getTalentFromId(self.T_CHARGED_AURA) t.do_combat(self, t, target) end end - + -- Static dis-Charge if hitted and not target.dead and self:hasEffect(self.EFF_STATIC_CHARGE) then local eff = self:hasEffect(self.EFF_STATIC_CHARGE) @@ -872,16 +876,29 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) end -- Regen on being hit - if hitted and not target.dead and target:attr("stamina_regen_when_hit") then target:incStamina(target.stamina_regen_when_hit) end - if hitted and not target.dead and target:attr("mana_regen_when_hit") then target:incMana(target.mana_regen_when_hit) end - if hitted and not target.dead and target:attr("equilibrium_regen_when_hit") then target:incEquilibrium(-target.equilibrium_regen_when_hit) end - if hitted and not target.dead and target:attr("psi_regen_when_hit") then target:incPsi(target.psi_regen_when_hit) end - if hitted and not target.dead and target:attr("hate_regen_when_hit") then target:incHate(target.hate_regen_when_hit) end + if self ~= target then + if hitted and not target.dead and target:attr("stamina_regen_when_hit") then target:incStamina(target.stamina_regen_when_hit) end + if hitted and not target.dead and target:attr("mana_regen_when_hit") then target:incMana(target.mana_regen_when_hit) end + if hitted and not target.dead and target:attr("equilibrium_regen_when_hit") then target:incEquilibrium(-target.equilibrium_regen_when_hit) end + if hitted and not target.dead and target:attr("psi_regen_when_hit") then target:incPsi(target.psi_regen_when_hit) end + if hitted and not target.dead and target:attr("hate_regen_when_hit") then target:incHate(target.hate_regen_when_hit) end + if hitted and not target.dead and target:attr("vim_regen_when_hit") then target:incVim(target.vim_regen_when_hit) end - -- Resource regen on hit - if hitted and self:attr("stamina_regen_on_hit") then self:incStamina(self.stamina_regen_on_hit) end - if hitted and self:attr("mana_regen_on_hit") then self:incMana(self.mana_regen_on_hit) end - if hitted and self:attr("psi_regen_on_hit") then self:incPsi(self.psi_regen_on_hit) end + -- Resource regen on hit + if hitted and self:attr("stamina_regen_on_hit") then self:incStamina(self.stamina_regen_on_hit) end + if hitted and self:attr("mana_regen_on_hit") then self:incMana(self.mana_regen_on_hit) end + if hitted and self:attr("psi_regen_on_hit") then self:incPsi(self.psi_regen_on_hit) end + end + + -- Ablative armor + if hitted and not target.dead and target:attr("carbon_spikes") then + if target.carbon_armor >= 1 then + target.carbon_armor = target.carbon_armor - 1 + else + -- Deactivate without loosing energy + target:forceUseTalent(target.T_CARBON_SPIKES, {ignore_energy=true}) + end + end if hitted and not target.dead and target:knowTalent(target.T_STONESHIELD) then local t = target:getTalentFromId(target.T_STONESHIELD) @@ -890,12 +907,6 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) target:incEquilibrium(-math.min(dam * e, em)) end - -- Ablative Armor - if hitted and not target.dead and target:attr("carbon_spikes") then - local t = target:getTalentFromId(target.T_CARBON_SPIKES) - t.do_carbonLoss(target, t) - end - -- Set Up if not hitted and not target.dead and not evaded and not target:attr("stunned") and not target:attr("dazed") and not target:attr("stoned") and target:hasEffect(target.EFF_DEFENSIVE_MANEUVER) then local t = target:getTalentFromId(target.T_SET_UP) @@ -1039,7 +1050,7 @@ function _M:attackTargetWith(target, weapon, damtype, mult, force_dam) self:attr("silent_heal", -1) end - return self:combatSpeed(weapon), hitted + return self:combatSpeed(weapon), hitted, dam end _M.weapon_talents = { @@ -1058,6 +1069,10 @@ _M.weapon_talents = { } --- Static! +-- Training Talents can have the following fields: +-- getMasteryPriority(self, t, kind) - Only the talent with the highest is used. Defaults to the talent level. +-- getDamage(self, t, kind) - Extra physical power granted. Defaults to level * 10. +-- getPercentInc(self, t, kind) - Percentage increase to damage overall. Defaults to sqrt(level / 5) / 2. function _M:addCombatTraining(kind, tid) local wt = _M.weapon_talents if not wt[kind] then wt[kind] = tid return end @@ -1076,35 +1091,42 @@ function _M:combatGetTraining(weapon) if not weapon.talented then return nil end if not _M.weapon_talents[weapon.talented] then return nil end if type(_M.weapon_talents[weapon.talented]) == "table" then - local ktid, max = _M.weapon_talents[weapon.talented][1], self:getTalentLevel(_M.weapon_talents[weapon.talented][1]) - for i, tid in ipairs(_M.weapon_talents[weapon.talented]) do + local get_priority = function(tid) + local t = self:getTalentFromId(tid) + if t.getMasteryPriority then return util.getval(t.getMasteryPriority, self, t, weapon.talented) end + return self:getTalentLevel(t) + end + local max_tid -- = _M.weapon_talents[weapon.talented][1] + local max_priority = -math.huge -- get_priority(max_tid) + for _, tid in ipairs(_M.weapon_talents[weapon.talented]) do if self:knowTalent(tid) then - if self:getTalentLevel(tid) > max then - ktid = tid - max = self:getTalentLevel(tid) + local priority = get_priority(tid) + if priority > max_priority then + max_tid = tid + max_priority = priority end end end - return self:getTalentFromId(ktid) + return self:getTalentFromId(max_tid) else return self:getTalentFromId(_M.weapon_talents[weapon.talented]) end end ---- Checks weapon training -function _M:combatCheckTraining(weapon) - if not weapon then return 0 end - if not weapon.talented then return 0 end - if not _M.weapon_talents[weapon.talented] then return 0 end - if type(_M.weapon_talents[weapon.talented]) == "table" then - local max = 0 - for i, tid in ipairs(_M.weapon_talents[weapon.talented]) do - max = math.max(max, self:getTalentLevel(tid)) - end - return max - else - return self:getTalentLevel(_M.weapon_talents[weapon.talented]) - end +-- Gets the added damage for a weapon based on training. +function _M:combatTrainingDamage(weapon) + local t = self:combatGetTraining(weapon) + if not t then return 0 end + if t.getDamage then return util.getval(t.getDamage, self, t, weapon.talented) end + return self:getTalentLevel(t) * 10 +end + +-- Gets the percent increase for a weapon based on training. +function _M:combatTrainingPercentInc(weapon) + local t = self:combatGetTraining(weapon) + if not t then return 0 end + if t.getPercentInc then return util.getval(t.getPercentInc, self, t, weapon.talented) end + return math.sqrt(self:getTalentLevel(t) / 5) / 2 end --- Gets the defense @@ -1176,12 +1198,12 @@ function _M:combatArmor() add = add + ga.getArmor(self, ga) end end - if self:knowTalent(self.T_CARBON_SPIKES) and self:isTalentActive(self.T_CARBON_SPIKES) then - add = add + self.carbon_armor - end if self:knowTalent(self.T_ARMOUR_OF_SHADOWS) and not game.level.map.lites(self.x, self.y) then add = add + self:callTalent(self.T_ARMOUR_OF_SHADOWS,"ArmourBonus") end + if self:knowTalent(self.T_CARBON_SPIKES) and self:isTalentActive(self.T_CARBON_SPIKES) then + add = add + self.carbon_armor + end return self.combat_armor + add end @@ -1297,9 +1319,9 @@ function _M:combatCrit(weapon) end --- Gets the damage range -function _M:combatDamageRange(weapon) +function _M:combatDamageRange(weapon, add) weapon = weapon or self.combat or {} - return (self.combat_damrange or 0) + (weapon.damrange or 1.1) + return (self.combat_damrange or 0) + (weapon.damrange or 1.1) + (add or 0) end --- Scale damage values @@ -1478,22 +1500,46 @@ function _M:combatStatLimit(stat, limit, low, high) end end +--- Gets the dammod table for a given weapon. +function _M:getDammod(combat) + combat = combat or self.combat or {} + + local dammod = table.clone(combat.dammod or {str = 0.6}, true) + + local sub = function(from, to) + dammod[to] = (dammod[from] or 0) + (dammod[to] or 0) + dammod[from] = nil + end + + if combat.talented == 'knife' and self:knowTalent('T_LETHALITY') then sub('str', 'cun') end + if combat.talented and self:knowTalent('T_STRENGTH_OF_PURPOSE') then sub('str', 'mag') end + if self:attr 'use_psi_combat' then + sub('str', 'wil') + sub('dex', 'cun') + end + + -- Add stuff like lethality here. + local hd = {"Combat:getDammod:subs", combat=combat, dammod=dammod, sub=sub} + if self:triggerHook(hd) then dammod = hd.dammod end + + local add = function(stat, val) + dammod[stat] = (dammod[stat] or 0) + val + end + + if self:knowTalent(self.T_SUPERPOWER) then add('wil', 0.3) end + if self:knowTalent(self.T_ARCANE_MIGHT) then add('mag', 0.5) end + + return dammod +end + --- Gets the damage function _M:combatDamage(weapon, adddammod) weapon = weapon or self.combat or {} - local sub_cun_to_str = false - if weapon.talented and weapon.talented == "knife" and self:knowTalent(Talents.T_LETHALITY) then sub_cun_to_str = true end - local sub_mag_to_str = false - if weapon.talented and self:knowTalent(Talents.T_STRENGTH_OF_PURPOSE) then sub_mag_to_str = true end + local dammod = self:getDammod(weapon) local totstat = 0 - local dammod = weapon.dammod or {str=0.6} for stat, mod in pairs(dammod) do - if sub_cun_to_str and stat == "str" then stat = "cun" end - if sub_mag_to_str and stat == "str" then stat = "mag" end - if self:attr("use_psi_combat") and stat == "str" then stat = "wil" end - if self:attr("use_psi_combat") and stat == "dex" then stat = "cun" end totstat = totstat + self:getStat(stat) * mod end if adddammod then @@ -1506,22 +1552,20 @@ function _M:combatDamage(weapon, adddammod) totstat = totstat * (0.8 + self:callTalent(self.T_RESONANT_FOCUS, "bonus")/100) end - if self:knowTalent(self.T_SUPERPOWER) then - totstat = totstat + self:getStat("wil") * 0.3 - end + local talented_mod = 1 + self:combatTrainingPercentInc(weapon) - if self:knowTalent(self.T_ARCANE_MIGHT) then - totstat = totstat + self:getStat("mag") * 0.5 - end - - local talented_mod = math.sqrt(self:combatCheckTraining(weapon) / 5) / 2 + 1 - - local power = math.max((weapon.dam or 1), 1) - power = (math.sqrt(power / 10) - 1) * 0.5 + 1 + local power = self:combatDamagePower(weapon) -- print(("[COMBAT DAMAGE] power(%f) totstat(%f) talent_mod(%f)"):format(power, totstat, talented_mod)) return self:rescaleDamage(0.3*(self:combatPhysicalpower(nil, weapon) + totstat) * power * talented_mod) end +--- Gets the 'power' portion of the damage +function _M:combatDamagePower(weapon_combat, add) + if not weapon_combat then return 1 end + local power = math.max((weapon_combat.dam or 1) + (add or 0), 1) + return (math.sqrt(power / 10) - 1) * 0.5 + 1 +end + function _M:combatPhysicalpower(mod, weapon, add) mod = mod or 1 add = add or 0 @@ -1547,14 +1591,14 @@ function _M:combatPhysicalpower(mod, weapon, add) if inven and inven[1] then weapon = self:getObjectCombat(inven[1], "mainhand") else weapon = self.combat end end - add = add + 10 * self:combatCheckTraining(weapon) - + add = add + self:combatTrainingDamage(weapon) + local str = self:getStr() if self:knowTalent(Talents.T_STRENGTH_OF_PURPOSE) then str = self:getMag() end - local d = math.max(0, self.combat_dam + add) + str -- allows strong debuffs to offset strength + local d = math.max(0, (self.combat_dam or 0) + add + str) -- allows strong debuffs to offset strength if self:attr("dazed") then d = d / 2 end if self:attr("scoured") then d = d / 1.2 end @@ -1586,7 +1630,7 @@ function _M:combatSpellpower(mod, add) local am = 1 if self:attr("spellpower_reduction") then am = 1 / (1 + self:attr("spellpower_reduction")) end - local d = (self.combat_spellpower > 0 and self.combat_spellpower or 0) + add + self:getMag() + local d = math.max(0, (self.combat_spellpower or 0) + add + self:getMag()) if self:attr("dazed") then d = d / 2 end if self:attr("scoured") then d = d / 1.2 end @@ -1732,7 +1776,7 @@ function _M:physicalCrit(dam, weapon, target, atk, def, add_chance, crit_power_a if self:attr("stealth") and self:knowTalent(self.T_SHADOWSTRIKE) and not target:canSee(self) then -- bug fix chance = 100 self.turn_procs.shadowstrike_crit = self:callTalent(self.T_SHADOWSTRIKE,"getMultiplier") - crit_power_add = crit_power_add + self.turn_procs.shadowstrike_crit + crit_power_add = crit_power_add + self.turn_procs.shadowstrike_crit end if self:isAccuracyEffect(weapon, "axe") then @@ -1880,7 +1924,8 @@ function _M:combatMindpower(mod, add) add = add + self:attr("psychometry_power") end - local d = (self.combat_mindpower > 0 and self.combat_mindpower or 0) + add + self:getWil() * 0.7 + self:getCun() * 0.4 + local d = math.max(0, (self.combat_mindpower or 0) + add + self:getWil() * 0.7 + self:getCun() * 0.4) + if self:attr("dazed") then d = d / 2 end if self:attr("scoured") then d = d / 1.2 end diff --git a/game/modules/tome/class/interface/PartyLore.lua b/game/modules/tome/class/interface/PartyLore.lua index 2852e7a4b3c7ce95642141dc10db1102b34283f0..8823555ebb05688bdf85d70b067d57e5cde79c1c 100644 --- a/game/modules/tome/class/interface/PartyLore.lua +++ b/game/modules/tome/class/interface/PartyLore.lua @@ -20,6 +20,7 @@ require "engine.class" local Dialog = require "engine.ui.Dialog" local LorePopup = require "mod.dialogs.LorePopup" +local slt2 = require "slt2" module(..., package.seeall, class.make) @@ -65,7 +66,16 @@ function _M:getLore(lore, silent) self.lore_known = self.lore_known or {} self.additional_lore = self.additional_lore or {} if not silent then assert(self.lore_defs[lore] or self.additional_lore[lore], "bad lore id "..lore) end - return self.lore_defs[lore] or self.additional_lore[lore] + local l = self.lore_defs[lore] or self.additional_lore[lore] + if not l then return end + l = table.clone(l) + + if l.template then + local tpl = slt2.loadstring(l.lore) + l.lore = slt2.render(tpl, {player=self:findMember{main=true}, self=self}) + end + + return l end function _M:additionalLore(id, name, category, lore) diff --git a/game/modules/tome/class/interface/TooltipsData.lua b/game/modules/tome/class/interface/TooltipsData.lua index 00265d6f6cc41f4f87f02b1c201ae36e8bec829b..6171a93766ed57bb367608c23dcddac8efac065d 100644 --- a/game/modules/tome/class/interface/TooltipsData.lua +++ b/game/modules/tome/class/interface/TooltipsData.lua @@ -112,7 +112,7 @@ All afflicted talents are based on Hate, and many are more effective at higher l TOOLTIP_PARADOX = [[#GOLD#Paradox#LAST# Paradox represents how stable the current timeline is. As your Paradox grows so does your Spellpower, but you may be shifted to a more stable timeline when you attempt to use your magic. -When your Paradox is above 600 the shifts will become more violent. +When your modified Paradox is above 600 the shifts will become more violent. Your control over chronomancy spells increases with your Willpower. ]] diff --git a/game/modules/tome/class/uiset/Minimalist.lua b/game/modules/tome/class/uiset/Minimalist.lua index 85314c459a22c02288f7215a138ddf8b1eccf8a5..cb19d2a35cdd1600aa0587170931fc1646b3f2f3 100644 --- a/game/modules/tome/class/uiset/Minimalist.lua +++ b/game/modules/tome/class/uiset/Minimalist.lua @@ -33,6 +33,7 @@ local Tooltip = require "mod.class.Tooltip" local TooltipsData = require "mod.class.interface.TooltipsData" local Dialog = require "engine.ui.Dialog" local Map = require "engine.Map" +local FontPackage = require "engine.FontPackage" module(..., package.seeall, class.inherit(UISet, TooltipsData)) @@ -130,10 +131,8 @@ ammo_shot = {core.display.loadImage("/data/gfx/ui/resources/ammo_shot.png"):glTe _M['ammo_shadow_alchemist-gem'] = {core.display.loadImage("/data/gfx/ui/resources/ammo_shadow_alchemist-gem.png"):glTexture()} _M['ammo_alchemist-gem'] = {core.display.loadImage("/data/gfx/ui/resources/ammo_alchemist-gem.png"):glTexture()} -font_sha = core.display.newFont("/data/font/DroidSans.ttf", 14, true) -font_sha:setStyle("bold") -sfont_sha = core.display.newFont("/data/font/DroidSans.ttf", 12, true) -sfont_sha:setStyle("bold") +font_sha = FontPackage:get("resources_normal", true) +sfont_sha = FontPackage:get("resources_small", true) icon_green = { core.display.loadImage("/data/gfx/ui/talent_frame_ok.png"):glTexture() } icon_yellow = { core.display.loadImage("/data/gfx/ui/talent_frame_sustain.png"):glTexture() } @@ -343,18 +342,10 @@ end function _M:activate() Shader:setDefault("textoutline", "textoutline") - local size, size_mono, font, font_mono, font_mono_h, font_h - if config.settings.tome.fonts.type == "fantasy" then - size = ({normal=16, small=14, big=18})[config.settings.tome.fonts.size] - size_mono = ({normal=14, small=10, big=16})[config.settings.tome.fonts.size] - font = "/data/font/DroidSans.ttf" - font_mono = "/data/font/DroidSansMono.ttf" - else - size = ({normal=12, small=10, big=14})[config.settings.tome.fonts.size] - size_mono = ({normal=12, small=10, big=14})[config.settings.tome.fonts.size] - font = "/data/font/Vera.ttf" - font_mono = "/data/font/VeraMono.ttf" - end + local font, size = FontPackage:getFont("default") + local font_mono, size_mono = FontPackage:getFont("mono_small", "mono") + local font_mono_h, font_h + local f = core.display.newFont(font, size) font_h = f:lineSkip() f = core.display.newFont(font_mono, size_mono) @@ -1549,7 +1540,7 @@ end function _M:displayParty(scale, bx, by) if game.player.changed and next(self.party) then - for a, d in pairs(self.party) do if not game.party:hasMember(a) then game.mouse:unregisterZone(d[2]) print("==UNREG part ", d[1].name, d[2]) end end + for a, d in pairs(self.party) do if not game.party:hasMember(a) then game.mouse:unregisterZone(d[2]) end end self.party = {} end diff --git a/game/modules/tome/data/birth/classes/celestial.lua b/game/modules/tome/data/birth/classes/celestial.lua index c3e2c4c02d4d6546310c78921970e718ced1dc03..96514a5baa761f180fd7b66c04bd35cadc88fb31 100644 --- a/game/modules/tome/data/birth/classes/celestial.lua +++ b/game/modules/tome/data/birth/classes/celestial.lua @@ -37,7 +37,7 @@ newBirthDescriptor{ }, copy = { class_start_check = function(self) - if self.descriptor.world == "Maj'Eyal" and (self.descriptor.race == "Human" or self.descriptor.race == "Elf") then + if self.descriptor.world == "Maj'Eyal" and (self.descriptor.race == "Human" or self.descriptor.race == "Elf") and not self._forbid_start_override then self.celestial_race_start_quest = self.starting_quest self.default_wilderness = {"zone-pop", "ruined-gates-of-morning"} self.starting_zone = "town-gates-of-morning" diff --git a/game/modules/tome/data/birth/classes/chronomancer.lua b/game/modules/tome/data/birth/classes/chronomancer.lua index 149154ecf1d4f778a093e655c380841358f07f68..64cea8be3aff7e34c314610936a1d5820f184e9d 100644 --- a/game/modules/tome/data/birth/classes/chronomancer.lua +++ b/game/modules/tome/data/birth/classes/chronomancer.lua @@ -37,7 +37,7 @@ newBirthDescriptor{ copy = { -- Chronomancers start in Point Zero class_start_check = function(self) - if self.descriptor.world == "Maj'Eyal" and (self.descriptor.race ~= "Undead" and self.descriptor.race ~= "Dwarf" and self.descriptor.race ~= "Yeek") then + if self.descriptor.world == "Maj'Eyal" and (self.descriptor.race ~= "Undead" and self.descriptor.race ~= "Dwarf" and self.descriptor.race ~= "Yeek") and not self._forbid_start_override then self.chronomancer_race_start_quest = self.starting_quest self.default_wilderness = {"zone-pop", "angolwen-portal"} self.starting_zone = "town-point-zero" @@ -69,25 +69,32 @@ newBirthDescriptor{ random_rarity = 2, stats = { mag=5, wil=2, con=2, }, talents_types = { - ["chronomancy/age-manipulation"]={true, 0.3}, - -- ["chronomancy/anomalies"]={true, 0}, - ["chronomancy/chronomancy"]={true, 0.3}, - ["chronomancy/energy"]={true, 0.3}, + -- class ["chronomancy/fate-threading"]={true, 0.3}, ["chronomancy/gravity"]={true, 0.3}, - ["chronomancy/matter"]={true, 0.3}, - ["chronomancy/paradox"]={false, 0.3}, + ["chronomancy/spacetime-folding"]={true, 0.3}, ["chronomancy/speed-control"]={true, 0.3}, - ["chronomancy/timeline-threading"]={false, 0.3}, + ["chronomancy/timeline-threading"]={true, 0.3}, ["chronomancy/timetravel"]={true, 0.3}, + + -- locked class + ["chronomancy/spellbinding"]={false, 0.3}, + ["chronomancy/spatial-tears"]={false, 0.3}, + + -- generic + ["chronomancy/chronomancy"]={true, 0.3}, + ["chronomancy/fate-weaving"]={true, 0.3}, ["chronomancy/spacetime-weaving"]={true, 0.3}, - ["cunning/survival"]={false, 0}, + + -- locked generic + ["chronomancy/energy"]={false, 0.3}, + ["cunning/survival"]={false, 0}, }, talents = { - [ActorTalents.T_DISENTANGLE] = 1, + [ActorTalents.T_TEMPORAL_BOLT] = 1, [ActorTalents.T_DIMENSIONAL_STEP] = 1, - [ActorTalents.T_DUST_TO_DUST] = 1, - [ActorTalents.T_TURN_BACK_THE_CLOCK] = 1, + [ActorTalents.T_RETHREAD] = 1, + [ActorTalents.T_DISENTANGLE] = 1, }, copy = { max_life = 90, @@ -119,21 +126,25 @@ newBirthDescriptor{ -- class ["chronomancy/blade-threading"]={true, 0.3}, ["chronomancy/bow-threading"]={true, 0.3}, - ["chronomancy/fate-threading"]={true, 0.3}, + ["chronomancy/fate-threading"]={true, 0.1}, ["chronomancy/spacetime-folding"]={true, 0.3}, - ["chronomancy/speed-control"]={true, 0.1}, + ["chronomancy/speed-control"]={true, 0.3}, ["chronomancy/guardian"]={true, 0.3}, + -- class locked ["chronomancy/threaded-combat"]={false, 0.3}, ["chronomancy/temporal-hounds"]={false, 0.3}, ["chronomancy/timetravel"]={false, 0.1}, -- generic - ["cunning/survival"]={false, 0}, ["technique/combat-training"]={true, 0.3}, + ["chronomancy/chronomancy"]={true, 0.3}, - ["chronomancy/fate-weaving"]={false, 0.1}, ["chronomancy/spacetime-weaving"]={true, 0.3}, + + -- generic locked + ["chronomancy/fate-weaving"]={false, 0.1}, + ["cunning/survival"]={false, 0}, }, birth_example_particles = "temporal_focus", talents = { diff --git a/game/modules/tome/data/birth/classes/mage.lua b/game/modules/tome/data/birth/classes/mage.lua index bcf139442ae55678c7d6b37463ae3df4c861f764..e801ea881da2920755968fabf9a880e7985b3d3f 100644 --- a/game/modules/tome/data/birth/classes/mage.lua +++ b/game/modules/tome/data/birth/classes/mage.lua @@ -200,7 +200,7 @@ newBirthDescriptor{ copy = { -- Mages start in angolwen class_start_check = function(self) - if self.descriptor.world == "Maj'Eyal" and (self.descriptor.race == "Human" or self.descriptor.race == "Elf" or self.descriptor.race == "Halfling") then + if self.descriptor.world == "Maj'Eyal" and (self.descriptor.race == "Human" or self.descriptor.race == "Elf" or self.descriptor.race == "Halfling") and not self._forbid_start_override then self.archmage_race_start_quest = self.starting_quest self.default_wilderness = {"zone-pop", "angolwen-portal"} self.starting_zone = "town-angolwen" diff --git a/game/modules/tome/data/birth/classes/wilder.lua b/game/modules/tome/data/birth/classes/wilder.lua index 1d67e722b69fa48d8b3920884844903feafe7cd4..c771265df8299e483eed8fc42457566dd1bbf689 100644 --- a/game/modules/tome/data/birth/classes/wilder.lua +++ b/game/modules/tome/data/birth/classes/wilder.lua @@ -176,7 +176,7 @@ newBirthDescriptor{ "#LIGHT_BLUE# * +0 Magic, +5 Willpower, +4 Cunning", "#GOLD#Life per level:#LIGHT_BLUE# -3", }, - power_source = {nature=true}, + power_source = {nature=true, antimagic=true}, random_rarity = 3, getStatDesc = function(stat, actor) if stat == actor.STAT_CUN then diff --git a/game/modules/tome/data/birth/races/dwarf.lua b/game/modules/tome/data/birth/races/dwarf.lua index 8352e107334a134d88958753a8c7cc79e9aa10eb..13e5e35e2b4842b686bd0cd3aba5f1a9080f78b2 100644 --- a/game/modules/tome/data/birth/races/dwarf.lua +++ b/game/modules/tome/data/birth/races/dwarf.lua @@ -60,8 +60,8 @@ newBirthDescriptor{ {priority=2, name="Donut [donator only]", donator=true, on_actor=function(actor) if actor.moddable_tile then actor.moddable_tile_ornament={female="donut_"..(actor.is_redhead and "redhead_" or "").."01"} end end, check=function(birth) return birth.descriptors_by_type.sex == "Female" end}, }, cosmetic_race_human_redhead = { - {priority=1, name="Redhead [donator only]", donator=true, on_actor=function(actor) if actor.moddable_tile then actor.is_redhead = true actor.moddable_tile_base = "base_redhead_01.png" actor.moddable_tile_ornament2={male="beard_redhead_02"} end end, check=function(birth) return birth.descriptors_by_type.sex == "Male" end}, - {priority=1, name="Redhead [donator only]", donator=true, on_actor=function(actor) if actor.moddable_tile then actor.is_redhead = true actor.is_redhead = true actor.moddable_tile_base = "base_redhead_01.png" actor.moddable_tile_ornament2={female="braid_redhead_01"} end end, check=function(birth) return birth.descriptors_by_type.sex == "Female" end}, + {priority=1, name="Redhead [donator only]", donator=true, reset=function(actor) actor.is_redhead=false end, on_actor=function(actor) if actor.moddable_tile then actor.is_redhead = true actor.moddable_tile_base = "base_redhead_01.png" actor.moddable_tile_ornament2={male="beard_redhead_02"} end end, check=function(birth) return birth.descriptors_by_type.sex == "Male" end}, + {priority=1, name="Redhead [donator only]", donator=true, reset=function(actor) actor.is_redhead=false end, on_actor=function(actor) if actor.moddable_tile then actor.is_redhead = true actor.is_redhead = true actor.moddable_tile_base = "base_redhead_01.png" actor.moddable_tile_ornament2={female="braid_redhead_01"} end end, check=function(birth) return birth.descriptors_by_type.sex == "Female" end}, }, cosmetic_bikini = { {name="Bikini [donator only]", donator=true, on_actor=function(actor, birther, last) diff --git a/game/modules/tome/data/chats/escort-quest.lua b/game/modules/tome/data/chats/escort-quest.lua index e0ebbba00f98e8e6a2a16d180f21d2448f584853..e17579281c1cf2a9b929e88b6945c9fd7e500cdf 100644 --- a/game/modules/tome/data/chats/escort-quest.lua +++ b/game/modules/tome/data/chats/escort-quest.lua @@ -52,7 +52,7 @@ local reward_types = { types = { ["wild-gift/call"] = 0.8, }, - saves = { mind = 4 }, + saves = { mental = 4 }, talents = { [Talents.T_NATURE_TOUCH] = 1, [Talents.T_EARTH_S_EYES] = 1, @@ -153,7 +153,7 @@ local reward_types = { [Talents.T_TRACK] = 1, [Talents.T_HEAVE] = 1, }, - saves = { spell = 4, mind = 4 }, + saves = { spell = 4, mental = 4 }, stats = { [Stats.STAT_CUN] = 1, [Stats.STAT_WIL] = 2, @@ -164,11 +164,11 @@ local reward_types = { types = { ["chronomancy/chronomancy"] = 0.8, }, - talents = { --- Note to Darkgod; burn this comment after reading - [Talents.T_CONTINGENCY] = 1, --- Quick explanation; Precognition is really similar to track now so there's the overlap argument - [Talents.T_SEE_THE_THREADS] = 1, --- The other thought is that these are both some pretty cool effects and may help to hook players that otherwise - -- [Talents.T_PRECOGNITION] = 1, --- wouldn't stick around long enough to unlock Chronomancers. Anyway that's my thoughts on it, if it doesn't feel right - }, --- feel free to change it. + talents = { + [Talents.T_CONTINGENCY] = 1, + -- [Talents.T_SEE_THE_THREADS] = 1, + [Talents.T_PRECOGNITION] = 1, + }, stats = { [Stats.STAT_MAG] = 2, [Stats.STAT_CUN] = 1, @@ -215,8 +215,8 @@ if quest.to_zigur and reward.antimagic then reward = reward.antimagic reward.is_ game.player:registerEscorts(quest.to_zigur and "zigur" or "saved") -local saves_name = { mind="mental", spell="spell", phys="physical"} -local saves_tooltips = { mind="MENTAL", spell="SPELL", phys="PHYS"} +local saves_name = { mental="mental", spell="spell", phys="physical"} +local saves_tooltips = { mental="MENTAL", spell="SPELL", phys="PHYS"} local function generate_rewards() local answers = {} diff --git a/game/modules/tome/data/chats/last-hope-lost-merchant.lua b/game/modules/tome/data/chats/last-hope-lost-merchant.lua index a6868b12c747c5a205c092d99c6a8ed157ee503c..ff581b337a7ccde4568be9995b8782315fd8b5ea 100644 --- a/game/modules/tome/data/chats/last-hope-lost-merchant.lua +++ b/game/modules/tome/data/chats/last-hope-lost-merchant.lua @@ -123,12 +123,8 @@ local maker_list = function() local dname = nil if type(name) == "table" then name, dname = name[1], name[2] end local not_ps, force_themes - if player:attr("forbid_arcane") then -- no magic gear for antimatic characters - not_ps = {arcane=true} - force_themes = {'antimagic'} - else -- no antimagic gear for characters with arcane-powered classes or undeads - if player:attr("has_arcane_knowledge") or player:attr("undead") then not_ps = {antimagic=true} end - end + not_ps = game.state:attrPowers(player) -- make sure randart is compatible with player + if not_ps.arcane then force_themes = {'antimagic'} end local o, ok local tries = 100 @@ -160,7 +156,7 @@ local maker_list = function() game.log("#CRIMSON#Your timetravel has no effect on pre-determined outcomes such as this.") game._chronoworlds = nil end - game:saveGame() + if not config.settings.cheat then game:saveGame() end newChat{ id="naming", text = "Do you want to name your item?\n"..tostring(art:getTextualDesc()), diff --git a/game/modules/tome/data/chats/zigur-trainer.lua b/game/modules/tome/data/chats/zigur-trainer.lua index 52f73d4009140735ae2dcef409159cb4693d29d9..242096da52420de637b471256a57a7ae43a6a984 100644 --- a/game/modules/tome/data/chats/zigur-trainer.lua +++ b/game/modules/tome/data/chats/zigur-trainer.lua @@ -48,6 +48,7 @@ local remove_magic = function(npc, player) end end player:attr("forbid_arcane", 1) + player:attr("zigur_follower", 1) player.changed = true end diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua index 2e4da2aa6e88675b79b0cde135813bd4040eaa07..c725e81696866fe12fb19450e457574e8d055892 100644 --- a/game/modules/tome/data/damage_types.lua +++ b/game/modules/tome/data/damage_types.lua @@ -17,6 +17,9 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org +local print = print +if not config.settings.cheat then print = function() end end + -- The basic stuff used to damage a grid setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) if not game.level.map:isBound(x, y) then return 0 end @@ -127,7 +130,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) dam = t.doForgeShield(type, dam, t, target, src) if lastdam - dam > 0 then game:delayedLogDamage(src, target, 0, ("%s(%d blocked)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", lastdam-dam), false) end end - + -- Increases damage local mind_linked = false if src.inc_damage then @@ -243,17 +246,6 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) --target.T_STONE_FORTRESS could be checked/applied here (ReduceDamage function in Dwarven Fortress talent) - -- Damage Smearing - if dam > 0 and type ~= DamageType.TEMPORAL and target.isTalentActive and target:isTalentActive(target.T_DAMAGE_SMEARING) then - local percent = target:callTalent(target.T_DAMAGE_SMEARING, "getPercent") - local duration = target:callTalent(target.T_DAMAGE_SMEARING, "getDuration") - local smear = dam * percent - local type = DamageType.TEMPORAL - target:setEffect(target.EFF_SMEARED, duration, {src=src, power=smear/duration, no_ct_effect=true}) - game:delayedLogDamage(src, target, 0, ("%s(%d smeared)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", smear), false) - dam = dam - smear - end - -- affinity healing, we store it to apply it after damage is resolved local affinity_heal = 0 if target.damage_affinity then @@ -325,13 +317,6 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) if lastdam - dam > 0 then game:delayedLogDamage(src, target, 0, ("%s(%d antimagic)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", lastdam - dam), false) end end - if dam > 0 and target.isTalentActive and target:isTalentActive(target.T_ENERGY_DECOMPOSITION) then - local t = target:getTalentFromId(target.T_ENERGY_DECOMPOSITION) - lastdam = dam - dam = t.on_damage(target, t, type, dam) - if lastdam - dam > 0 then game:delayedLogDamage(src, target, 0, ("%s(%d dissipated)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", lastdam - dam), false) end - end - -- Flat damage reduction ("armour") if dam > 0 and target.flat_damage_armor then local dec = math.min(dam, (target.flat_damage_armor.all or 0) + (target.flat_damage_armor[type] or 0)) @@ -359,7 +344,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) print("[PROJECTOR] numbed dam", dam) end if src:attr("generic_damage_penalty") then - dam = dam - dam * src:attr("generic_damage_penalty") / 100 + dam = dam - dam * math.min(100, src:attr("generic_damage_penalty")) / 100 print("[PROJECTOR] generic dam", dam) end @@ -369,6 +354,11 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) local def = src.tempeffect_def[src.EFF_CURSE_OF_MISFORTUNE] dam = def.doUnfortunateEnd(src, eff, target, dam) end + + if src and src.hasEffect and src:hasEffect(src.EFF_SEAL_FATE) then + src:callEffect(src.EFF_SEAL_FATE, "doDamage", target) + end + if src:attr("crushing_blow") and (dam * (1.25 + (src.combat_critical_power or 0)/200)) > target.life then dam = dam * (1.25 + (src.combat_critical_power or 0)/200) @@ -464,31 +454,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) end -- Braided damage if dam > 0 and target:hasEffect(target.EFF_BRAIDED) then - local p = target:hasEffect(target.EFF_BRAIDED) - local braid_damage = dam * p.power/ 100 - if p.braid_one and not p.braid_one.dead and p.braid_one:hasEffect(p.braid_one.EFF_BRAIDED) then - game:delayedLogMessage(p.src, p.braid_one, "braided", "#CRIMSON##Source# damages #Target# through the Braid!") - game:delayedLogDamage(p.src, p.braid_one, braid_damage, ("#PINK#%d braided #LAST#"):format(braid_damage), false) - p.braid_one:takeHit(braid_damage, p.src) - end - if p.braid_two and not p.braid_two.dead and p.braid_two:hasEffect(p.braid_two.EFF_BRAIDED) then - game:delayedLogMessage(p.src, p.braid_two, "braided", "#CRIMSON##Source# damages #Target# through the Braid!") - game:delayedLogDamage(p.src, p.braid_two, braid_damage, ("#PINK#%d braided #LAST#"):format(braid_damage), false) - p.braid_two:takeHit(braid_damage, p.src) - end - end - - if dam > 0 and src ~= target and target.knowTalent and target:knowTalent(target.T_SPIN_FATE) then - if target.turn_procs and not target.turn_procs.spin_fate then - target:callTalent(target.T_SPIN_FATE, "doSpinFate") - if target.hasEffect and target:hasEffect(target.EFF_WEBS_OF_FATE) and not target.turn_procs.webs_of_fate then - target.turn_procs.webs_of_fate = true - elseif target.hasEffect and target:hasEffect(target.EFF_SEAL_FATE) and not target.turn_procs.seal_fate then - target.turn_procs.seal_fate = true - else - target.turn_procs.spin_fate = true - end - end + game:onTickEnd(function()target:callEffect(target.EFF_BRAIDED, "doBraid", dam)end) end if target.knowTalent and target:knowTalent(target.T_RESOLVE) then local t = target:getTalentFromId(target.T_RESOLVE) t.on_absorb(target, t, type, dam) end @@ -543,8 +509,9 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) if dam > 0 and source_talent then local t = source_talent - if src:attr("spellshock_on_damage") and target:checkHit(src:combatSpellpower(), target:combatPhysicalResist(), 0, 95, 15) and not target:hasEffect(target.EFF_SPELLSHOCKED) then - target:crossTierEffect(target.EFF_SPELLSHOCKED, src:combatSpellpower()) + local spellshock = src:attr("spellshock_on_damage") + if spellshock and t.is_spell and target:checkHit(src:combatSpellpower(1, spellshock), target:combatSpellResist(), 0, 95, 15) and not target:hasEffect(target.EFF_SPELLSHOCKED) then + target:crossTierEffect(target.EFF_SPELLSHOCKED, src:combatSpellpower(1, spellshock)) end if src.__projecting_for then @@ -604,7 +571,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) end end end - + if src.turn_procs and src.turn_procs.is_crit then if src.knowTalent and src:knowTalent(src.T_ELEMENTAL_SURGE) then src:triggerTalent(src.T_ELEMENTAL_SURGE, nil, target, type, dam) @@ -612,7 +579,7 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr) src.turn_procs.is_crit = nil end - + if src.turn_procs and not src.turn_procs.dazing_damage and src.hasEffect and src:hasEffect(src.EFF_DAZING_DAMAGE) then if target:canBe("stun") then local power = math.max(src:combatSpellpower(), src:combatMindpower(), src:combatPhysicalpower()) @@ -693,10 +660,11 @@ newDamageType{ return dam end end + local a = game.level.map(x, y, Map.ACTOR) + local acheive = a and src.player and not a.training_dummy and a ~= src local realdam = DamageType.defaultProjector(src, x, y, type, dam) - if realdam > 0 then - local a = game.level.map(x, y, Map.ACTOR) - if src.player and a and not a.training_dummy then world:gainAchievement("PYROMANCER", src, realdam) end + if realdam > 0 and acheive then + world:gainAchievement("PYROMANCER", src, realdam) end return realdam end, @@ -706,10 +674,11 @@ newDamageType{ name = "cold", type = "COLD", text_color = "#1133F3#", antimagic_resolve = true, projector = function(src, x, y, type, dam) + local a = game.level.map(x, y, Map.ACTOR) + local acheive = a and src.player and not a.training_dummy and a ~= src local realdam = DamageType.defaultProjector(src, x, y, type, dam) - if realdam > 0 then - local a = game.level.map(x, y, Map.ACTOR) - if src.player and a and not a.training_dummy then world:gainAchievement("CRYOMANCER", src, realdam) end + if realdam > 0 and acheive then + world:gainAchievement("CRYOMANCER", src, realdam) end if realdam > 0 and src:attr("cold_freezes") and rng.percent(src.cold_freezes) then DamageType:get(DamageType.FREEZE).projector(src, x, y, DamageType.FREEZE, {dur=2, hp=70+dam*1.5}) @@ -1028,6 +997,29 @@ newDamageType{ return init_dam end, } + +-- Fire damage + DOT + 25% chance of Fireflash +newDamageType{ + name = "stunning fire", type = "FIRE_STUN", text_color = "#LIGHT_RED#", + projector = function(src, x, y, type, dam) + local chance = 25 + local dur = 3 + local perc = 50 + if _G.type(dam) == "table" then dam, dur, perc = dam.dam, dam.dur, (dam.initial or perc) end + local init_dam = dam * perc / 100 + if init_dam > 0 then DamageType:get(DamageType.FIRE).projector(src, x, y, DamageType.FIRE, init_dam) end + local target = game.level.map(x, y, Map.ACTOR) + if target then + dam = dam - init_dam + target:setEffect(target.EFF_BURNING, dur, {src=src, power=dam / dur, no_ct_effect=true}) + if rng.percent(chance) then + DamageType:get(DamageType.FLAMESHOCK).projector(src, x, y, DamageType.FLAMESHOCK, {dur=3, dam=15, apply_power=src:combatMindpower()}) + end + end + return init_dam + end, +} + newDamageType{ name = "fire burn", type = "GOLEM_FIREBURN", projector = function(src, x, y, type, dam) @@ -1040,9 +1032,24 @@ newDamageType{ end, } +-- Drain Life... with fire! +newDamageType{ + name = "devouring flames", type = "FIRE_DRAIN", text_color = "#LIGHT_RED#", + projector = function(src, x, y, type, dam) + if _G.type(dam) == "number" then dam = {dam=dam, healfactor=0.1} end + local target = game.level.map(x, y, Map.ACTOR) -- Get the target first to make sure we heal even on kill + local realdam = DamageType:get(DamageType.FIRE).projector(src, x, y, DamageType.FIRE, dam.dam) + if target and realdam > 0 then + src:heal(realdam * dam.healfactor, target) + src:logCombat(target, "#Source# drains life from #Target#!") + end + return realdam + end, +} + -- Darkness + Fire newDamageType{ - name = "shadowflame", type = "SHADOWFLAME", + name = "shadowflame", type = "SHADOWFLAME", text_color = "#BF7F73#", projector = function(src, x, y, type, dam) DamageType:get(DamageType.FIRE).projector(src, x, y, DamageType.FIRE, dam / 2) DamageType:get(DamageType.DARKNESS).projector(src, x, y, DamageType.DARKNESS, dam / 2) @@ -1135,6 +1142,24 @@ newDamageType{ end, } +-- Cold damage + freeze chance + 20% slow +newDamageType{ + name = "slowing ice", type = "ICE_SLOW", text_color = "#1133F3#", + projector = function(src, x, y, type, dam) + local chance = 25 + local target = game.level.map(x, y, Map.ACTOR) + if _G.type(dam) == "table" then chance, dam = dam.chance, dam.dam end + local realdam = DamageType:get(DamageType.COLD).projector(src, x, y, DamageType.COLD, dam) + if target then + target:setEffect(target.EFF_SLOW, 3, {power=0.2, no_ct_effect=true}) + if rng.percent(chance) then + DamageType:get(DamageType.FREEZE).projector(src, x, y, DamageType.FREEZE, {dur=2, hp=70+dam*1.5}) + end + end + return realdam + end, +} + -- Cold damage + freeze chance, increased if wet newDamageType{ name = "ice storm", type = "ICE_STORM", text_color = "#1133F3#", @@ -1766,9 +1791,10 @@ newDamageType{ -- Name: item - theme - debuff/effect --- Log entries are pretty limited currently because it can be quite spammy with the default messages already +-- Log entries are included with the damage line as "<#color#..effect ...%chance>" to minimize spam, where the % indicates the chance for the special effect to occur (not its strength) as applicable newDamageType{ name = "item mind gloom", type = "ITEM_MIND_GLOOM", + text_color = "#YELLOW#", tdesc = function(dam, oldDam) parens = "" dam = dam or 0 @@ -1780,12 +1806,14 @@ newDamageType{ parens = (" (#RED#%d%%#LAST#)"):format(diff) end end - return ("* #LIGHT_GREEN#%d%%#LAST# chance to cause #YELLOW#random insanity#LAST#%s") + return ("* #LIGHT_GREEN#%d%%#LAST# chance to cause #YELLOW#random gloom#LAST#%s") :format(dam, parens) end, projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) - if target and rng.percent(dam) then + if target then + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% gloom chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if rng.percent(dam) then local check = math.max(src:combatAttack(), src:combatSpellpower(), src:combatMindpower()) if not src:checkHit(check, target:combatMentalResist()) then return end local effect = rng.range(1, 3) @@ -1806,11 +1834,13 @@ newDamageType{ end end end + end end, } newDamageType{ name = "item darkness numbing", type = "ITEM_DARKNESS_NUMBING", + text_color = "#GREY#", tdesc = function(dam, oldDam) parens = "" dam = dam or 0 @@ -1827,16 +1857,20 @@ newDamageType{ end, projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) - if target and rng.percent(dam) then + if target then + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% dark numbing chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if rng.percent(dam) then local check = math.max(src:combatAttack(), src:combatSpellpower(), src:combatMindpower()) local reduction = 15 target:setEffect(target.EFF_ITEM_NUMBING_DARKNESS, 4, {reduce = reduction, apply_power=check, no_ct_effect=true}) end + end end, } newDamageType{ name = "item temporal energize", type = "ITEM_TEMPORAL_ENERGIZE", + text_color = "#LIGHT_STEEL_BLUE#", tdesc = function(dam, oldDam) parens = "" dam = dam or 0 @@ -1881,21 +1915,25 @@ newDamageType{ parens = (" (#RED#%d%%#LAST#)"):format(diff) end end - return ("* #LIGHT_GREEN#%d%%#LAST# chance to #GREEN#corrode armor#LAST#%s") + return ("* #LIGHT_GREEN#%d%%#LAST# chance to #GREEN#corrode armour#LAST#%s") :format(dam, parens) end, projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) - if target and rng.percent(dam) then + if target then + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% corrode armour chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if rng.percent(dam) then local check = math.max(src:combatAttack(), src:combatSpellpower(), src:combatMindpower()) --local param = { atk=dam/3, armor=dam/3, defense=dam/3, src=src, apply_power = check, no_ct_effect=true } target:setEffect(target.EFF_ITEM_ACID_CORRODE, 5, {pct = 0.3, no_ct_effect = true, apply_power = check}) end + end end, } newDamageType{ name = "item light blind", type = "ITEM_LIGHT_BLIND", + text_color = "#YELLOW#", tdesc = function(dam, oldDam) parens = "" dam = dam or 0 @@ -1912,7 +1950,9 @@ newDamageType{ end, projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) - if target and rng.percent(dam) then + if target then + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% blind chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if rng.percent(dam) then if target:canBe("blind") then local check = math.max(src:combatAttack(), src:combatSpellpower(), src:combatMindpower()) target:setEffect(target.EFF_BLINDED, 4, {apply_power=(check), no_ct_effect=true}) @@ -1920,11 +1960,13 @@ newDamageType{ --game.logSeen(target, "%s resists the blinding light!", target.name:capitalize()) end end + end end, } newDamageType{ name = "item lightning daze", type = "ITEM_LIGHTNING_DAZE", + text_color = "#ROYAL_BLUE#", tdesc = function(dam, oldDam) parens = "" dam = dam or 0 @@ -1941,15 +1983,15 @@ newDamageType{ end, projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) - if target and rng.percent(dam) then + if target then + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% daze chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if rng.percent(dam) then if target:canBe("stun") then local check = math.max(src:combatAttack(), src:combatSpellpower(), src:combatMindpower()) - --game:onTickEnd(function() target:setEffect(target.EFF_DAZED, 3, {src=src, apply_power=dam.power_check or math.max(src:combatSpellpower(), src:combatMindpower(), src:combatAttack())}) end) -- Do it at the end so we don't break our own daze - game:onTickEnd(function() target:setEffect(target.EFF_DAZED, 4, {apply_power=check, no_ct_effect=true}) end) - else - --game.logSeen(target, "%s resists the daze!", target.name:capitalize()) + game:onTickEnd(function() target:setEffect(target.EFF_DAZED, 4, {apply_power=check, no_ct_effect=true}) end) --onTickEnd to avoid breaking the daze end end + end end, } @@ -1971,11 +2013,14 @@ newDamageType{ end, projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) - if target and rng.percent(dam) and target:canBe("disease") then + if target then + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% disease chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if rng.percent(dam) and target:canBe("disease") then local check = math.max(src:combatSpellpower(), src:combatMindpower(), src:combatAttack()) local disease_power = math.min(30, dam / 2) target:setEffect(target.EFF_ITEM_BLIGHT_ILLNESS, 5, {reduce = disease_power}) end + end end, } @@ -2025,7 +2070,7 @@ newDamageType{ name = "item nature slow", type = "ITEM_NATURE_SLOW", text_color = "#LIGHT_GREEN#", tdesc = function(dam, oldDam) parens = "" - dam = dam or 0 + dam = math.min(60, dam or 0) if oldDam then diff = dam - oldDam if diff > 0 then @@ -2040,7 +2085,9 @@ newDamageType{ projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) if target then - target:setEffect(target.EFF_SLOW, 3, {power= math.min(0.6, dam / 100), no_ct_effect=true}) + game:delayedLogDamage(src, target, 0, ("%s(slow %d%%%%)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + target:setEffect(target.EFF_SLOW, 3, {power= math.min(60, dam / 100), no_ct_effect=true}) +-- target:setEffect(target.EFF_SLOW, 3, {power= math.min(0.6, dam / 100), no_ct_effect=true}) end end, } @@ -2065,14 +2112,15 @@ newDamageType{ projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) if target then + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% scour chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if rng.percent(dam) then target:setEffect(target.EFF_ITEM_ANTIMAGIC_SCOURED, 3, {pct = 0.2, no_ct_effect=true}) end + end end, } - - ------------------------------------------------------------------------------------ -- gBlind @@ -2388,7 +2436,6 @@ newDamageType{ dam = dam - dam / 12 local target = game.level.map(x, y, Map.ACTOR) if target and target:canBe("cut") then - -- Set on fire! target:setEffect(target.EFF_CUT, 5, {src=src, power=dam / 11, no_ct_effect=true}) end end, @@ -2416,22 +2463,28 @@ newDamageType{ newDamageType{ name = "gravity", type = "GRAVITY", projector = function(src, x, y, type, dam) + if _G.type(dam) == "number" then dam = {dam=dam} end local target = game.level.map(x, y, Map.ACTOR) if not target then return end - if target and target:attr("never_move") then - dam = dam * 1.5 + if target then + if target:isTalentActive(target.T_GRAVITY_LOCUS) then return end + if dam.slow then + target:setEffect(target.EFF_GRAVITY_SLOW, dam.dur, {dam.slow, apply_power=apply, no_ct_effect=true}) + end + if dam.anti then + target:setEffect(target.EFF_ANTI_GRAVITY, dam.dur, {apply_power=apply, no_ct_effect=true}) + end end - DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam) + DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam.dam) end, } newDamageType{ name = "gravity pin", type = "GRAVITYPIN", projector = function(src, x, y, type, dam) - DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam) local target = game.level.map(x, y, Map.ACTOR) local reapplied = false - if target then + if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then -- silence the apply message if the target already has the effect for eff_id, p in pairs(target.tmp) do local e = target.tempeffect_def[eff_id] @@ -2444,6 +2497,7 @@ newDamageType{ else game.logSeen(target, "%s resists the pin!", target.name:capitalize()) end + DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam) end end, } @@ -2503,7 +2557,7 @@ newDamageType{ if target:canBe("pin") and rng.percent(dam.pin) then target:setEffect(target.EFF_PINNED, 4, {apply_power=src:combatMindpower()}, true) else - game.logSeen(target, "%s resists!", target.name:capitalize()) + game.logSeen(target, "%s resists the pinning!", target.name:capitalize()) end end end, @@ -2540,7 +2594,7 @@ newDamageType{ if target:canBe("confusion") and rng.percent(dam.chance) then target:setEffect(target.EFF_CONFUSED, 2, {apply_power=src:combatMindpower(), power=dam.power}, true) else - game.logSeen(target, "%s resists!", target.name:capitalize()) + game.logSeen(target, "%s resists the confusion!", target.name:capitalize()) end end end, @@ -2557,7 +2611,7 @@ newDamageType{ elseif target:canBe("silence") then target:setEffect(target.EFF_SILENCED, 2, {apply_power=src:combatSpellpower(), min_dur=1}, true) else - game.logSeen(target, "%s resists!", target.name:capitalize()) + game.logSeen(target, "%s resists the silence!", target.name:capitalize()) end end end, @@ -2636,7 +2690,7 @@ newDamageType{ if target:canBe("stun") then target:setEffect(target.EFF_FROZEN, dam, {hp=70 + src:combatMindpower() * 10, apply_power=src:combatMindpower()}) else - game.logSeen(target, "%s resists!", target.name:capitalize()) + game.logSeen(target, "%s resists the freezing!", target.name:capitalize()) end end end, @@ -2741,17 +2795,6 @@ newDamageType{ end, } -newDamageType{ - name = "temporal echo", type = "TEMPORAL_ECHO", - projector = function(src, x, y, type, dam) - local target = game.level.map(x, y, Map.ACTOR) - if target then - dam = (target.max_life - target.life) * dam - DamageType:get(DamageType.TEMPORAL).projector(src, x, y, DamageType.TEMPORAL, dam) - end - end, -} - newDamageType{ name = "draining physical", type = "DEVOUR_LIFE", projector = function(src, x, y, type, dam) @@ -2809,7 +2852,7 @@ newDamageType{ if target:canBe("pin") then target:setEffect(target.EFF_PINNED, 5, {no_ct_effect=true}) else - game.logSeen(target, "%s resists!", target.name:capitalize()) + game.logSeen(target, "%s resists entanglement!", target.name:capitalize()) end end end, @@ -2827,7 +2870,7 @@ newDamageType{ target:setEffect(target.EFF_MANAWORM, 5, {power=dam * 5, src=src, no_ct_effect=true}) src:disappear(src) else - game.logSeen(target, "%s is unaffected.", target.name:capitalize()) + game.logSeen(target, "%s has no mana to burn.", target.name:capitalize()) end end return realdam @@ -2860,7 +2903,7 @@ newDamageType{ if target:canBe(what) then target:setEffect(what == "blind" and target.EFF_BANE_BLINDED or target.EFF_BANE_CONFUSED, math.ceil(dam.dur), {src=src, power=50, dam=dam.dam, apply_power=src:combatSpellpower()}) else - game.logSeen(target, "%s resists the bane!", target.name:capitalize()) + game.logSeen(target, "%s resists the baneful energy!", target.name:capitalize()) end end end, @@ -2904,12 +2947,13 @@ newDamageType{ newDamageType{ name = "% chance to summon an orc spirit", type = "GARKUL_INVOKE", + text_color = "#SALMON#", projector = function(src, x, y, type, dam) - if not rng.percent(dam) then return end local target = game.level.map(x, y, engine.Map.ACTOR) if not target then return end - if game.party:hasMember(src) and game.party:findMember{type="garkul spirit"} then return end + game:delayedLogDamage(src, target, 0, ("%s<%d%%%% orc summon chance>#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", dam), false) + if not rng.percent(dam) then return end -- Find space local x, y = util.findFreeGrid(src.x, src.y, 5, true, {[engine.Map.ACTOR]=true}) @@ -3199,7 +3243,7 @@ newDamageType{ if target:canBe("disarm") then target:setEffect(target.EFF_DISARMED, dam.dur or 3, {src=src, apply_power=src:combatMindpower()}) else - game.logSeen(target, "%s resists!", target.name:capitalize()) + game.logSeen(target, "%s resists disarming!", target.name:capitalize()) end end return realdam @@ -3279,22 +3323,38 @@ newDamageType{ projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) if target then - target:knockback(src.x, src.y, dam.push, nil, function(g, x, y) - if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move", target) then - DamageType:get(DamageType.PHYSICAL).projector(src, target.x, target.y, DamageType.PHYSICAL, dam.dam) - if target:canBe("pin") then - target:setEffect(target.EFF_PINNED, dam.dur, {apply_power=src:combatMindpower()}) - else - game.logSeen(src, "%s resists!", target.name:capitalize()) + if target:canBe("knockback") then + target:knockback(src.x, src.y, dam.push, nil, function(g, x, y) + if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move", target) then + DamageType:get(DamageType.PHYSICAL).projector(src, target.x, target.y, DamageType.PHYSICAL, dam.dam) + if target:canBe("pin") then + target:setEffect(target.EFF_PINNED, dam.dur, {apply_power=src:combatMindpower()}) + else + game.logSeen(src, "%s resists pinning!", target.name:capitalize()) + end end - end - end) - return dam.dam + end) + return dam.dam + else + DamageType:get(DamageType.PHYSICAL).projector(src, target.x, target.y, DamageType.PHYSICAL, dam.dam) + game.logSeen(src, "%s resists the shove!", target.name:capitalize()) + return dam.dam + end end end, } +-- Prevents Teleportation newDamageType{ + name = "dimensional anchor", type = "DIMENSIONAL_ANCHOR", + projector = function(src, x, y, type, dam) + if _G.type(dam) == "number" then dam = {dam=dam, apply_power=apply_power or src:combatSpellpower()} end + local target = game.level.map(x, y, Map.ACTOR) + if target then + target:setEffect(target.EFF_DIMENSIONAL_ANCHOR, 1, {damage=dam.dam, src=src, apply_power=dam.apply_power, no_ct_effect=true}) + end + end, +}newDamageType{ name = "brain storm", type = "BRAINSTORM", projector = function(src, x, y, type, dam) local target = game.level.map(x, y, Map.ACTOR) @@ -3305,7 +3365,7 @@ newDamageType{ else game.logSeen(target, "%s resists the mind attack!", target.name:capitalize()) end - + if src:hasEffect(src.EFF_TRANSCENDENT_TELEKINESIS) then if target:canBe("blind") then target:setEffect(target.EFF_BLINDED, 4, {apply_power=src:combatMindpower()}) @@ -3346,3 +3406,63 @@ newDamageType{ end end, } + +-- Temporal/Physical damage with possible chance to debalitate +newDamageType{ + name = "warp", type = "WARP", + projector = function(src, x, y, type, dam) + local target = game.level.map(x, y, Map.ACTOR) + if not target then return end + if _G.type(dam) == "number" then dam = {dam=dam, chance=chance or 0, dur=dur or 3, apply_power=apply_power or src:combatSpellpower()} end + + -- Factor in fractured space bonuses + local fracture = false + if src.isTalentActive and src:isTalentActive(src.T_FRACTURED_SPACE) then + fracture = src:isTalentActive(src.T_FRACTURED_SPACE) + end + if fracture then + dam.chance = math.min(100, dam.chance + (src:callTalent(src.T_FRACTURED_SPACE, "getChance")*fracture.charges)) + dam.dam = dam.dam * (1 + (src:callTalent(src.T_FRACTURED_SPACE, "getDamage")*fracture.charges)/100) + end + + -- Deal Damage + DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, dam.dam / 2) + DamageType:get(DamageType.TEMPORAL).projector(src, x, y, DamageType.TEMPORAL, dam.dam / 2) + + -- Increase fracture charges and refresh decay rate + if fracture then + fracture.charges = math.min(6, fracture.charges + 1) + fracture.decay = 0 + end + + -- Pull random effect + if rng.percent(dam.chance) then + local effect = rng.range(1, 4) + if effect == 1 then + if target:canBe("stun") then + target:setEffect(target.EFF_STUNNED, dam.dur, {apply_power=dam.apply_power}) + else + game.logSeen(target, "%s resists the stun!", target.name:capitalize()) + end + elseif effect == 2 then + if target:canBe("blind") then + target:setEffect(target.EFF_BLINDED, dam.dur, {apply_power=dam.apply_power}) + else + game.logSeen(target, "%s resists the blindness!", target.name:capitalize()) + end + elseif effect == 3 then + if target:canBe("pin") then + target:setEffect(target.EFF_PINNED, dam.dur, {apply_power=dam.apply_power}) + else + game.logSeen(target, "%s resists the pin!", target.name:capitalize()) + end + elseif effect == 4 then + if target:canBe("confusion") then + target:setEffect(target.EFF_CONFUSED, dam.dur, {power=50, apply_power=dam.apply_power}) + else + game.logSeen(target, "%s resists the confusion!", target.name:capitalize()) + end + end + end + end, +} \ No newline at end of file diff --git a/game/modules/tome/data/factions.lua b/game/modules/tome/data/factions.lua index ec3031d37467fc999d2296c18876c0aee8eb2ce7..7493645bce680867ba09f06240abb9558dddfd01 100644 --- a/game/modules/tome/data/factions.lua +++ b/game/modules/tome/data/factions.lua @@ -19,36 +19,37 @@ -- CSV export local src = [[ -,Enemies,Undead,Allied Kingdoms,Shalore,Thalore,Iron Throne,The Way,Angolwen,Keepers of Reality,Dreadfell,,Temple of Creation|H,Water lair|H,Assassin lair|H,Rhalore,Zigur,,Vargh Republic,Sunwall,Orc Pride,,Sandworm Burrowers,Victim,Slavers,,Sorcerers,Fearscape,,Sher'Tul -Enemies,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Undead,-1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Allied Kingdoms,-1,-1,,,,,,,,,,,,,,,,,,,,,,,,,,, -Shalore,-1,-1,0.5,,,,,,,,,,,,,,,,,,,,,,,,,, -Thalore,-1,-1,0.7,0.2,,,,,,,,,,,,,,,,,,,,,,,,, -Iron Throne,-1,-1,0.2,0.2,0.2,,,,,,,,,,,,,,,,,,,,,,,, -The Way,-1,-1,0,0,0,0,,,,,,,,,,,,,,,,,,,,,,, -Angolwen,-1,-1,,,,,,,,,,,,,,,,,,,,,,,,,,, -Keepers of Reality,-1,-1,,,,,,0.2,,,,,,,,,,,,,,,,,,,,, -Dreadfell,,-1,-1,-1,-1,-1,-1,-1,-1,,,,,,,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Temple of Creation|H,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Water lair|H,-1,,,,,,,,,,,-1,,,,,,,,,,,,,,,,, -Assassin lair|H,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Rhalore,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,-1,-1,-1,,,,,,,,,,,,,,, -Zigur,-1,-1,1,1,1,1,0.2,-1,0,-1,,,,,-1,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Vargh Republic,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,-1,,-1,-1,-1,,,,,,,,,,,,, -Sunwall,-1,-1,,,,,,,,-1,,,,-1,-1,,,-1,,,,,,,,,,, -Orc Pride,,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,,,-1,-1,,-1,-1,,,,,,,,,, -,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Sandworm Burrowers,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Victim,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Slavers,-1,,,,,,,,,,,,,,,,,,,,,,,,,,,, -,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Sorcerers,,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,,,-1,-1,,-1,-1,1,,,,,,,,, -Fearscape,,-1,-1,-1,-1,-1,-1,-1,-1,,,-1,-1,-1,-1,-1,,-1,-1,,,,,,,,,, -,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Sher'Tul,,,,,,,,,,,,,,,,,,,,,,,,,,,-1,, +,Enemies,Undead,Allied Kingdoms,Shalore,Thalore,Iron Throne,The Way,Angolwen,Keepers of Reality,Dreadfell,,Temple of Creation|H,Water lair|H,Assassin lair|H,Rhalore,Zigur,,Vargh Republic,Sunwall,Orc Pride,,Sandworm Burrowers,Victim,Slavers,,Sorcerers,Fearscape,,Sher'Tul,Cosmic Fauna +Enemies,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Undead,-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Allied Kingdoms,-1,-1,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Shalore,-1,-1,0.5,,,,,,,,,,,,,,,,,,,,,,,,,,, +Thalore,-1,-1,0.7,0.2,,,,,,,,,,,,,,,,,,,,,,,,,, +Iron Throne,-1,-1,0.2,0.2,0.2,,,,,,,,,,,,,,,,,,,,,,,,, +The Way,-1,-1,0,0,0,0,,,,,,,,,,,,,,,,,,,,,,,, +Angolwen,-1,-1,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Keepers of Reality,-1,-1,,,,,,0,2,,,,,,,,,,,,,,,,,,,,,, +Dreadfell,,-1,-1,-1,-1,-1,-1,-1,-1,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Temple of Creation|H,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Water lair|H,-1,,,,,,,,,,,-1,,,,,,,,,,,,,,,,,, +Assassin lair|H,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Rhalore,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,-1,-1,-1,,,,,,,,,,,,,,,, +Zigur,-1,-1,1,1,1,1,0.2,-1,0,-1,,,,,-1,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Vargh Republic,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,-1,,-1,-1,-1,,,,,,,,,,,,,, +Sunwall,-1,-1,,,,,,,,-1,,,,-1,-1,,,-1,,,,,,,,,,,, +Orc Pride,,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,,,-1,-1,,-1,-1,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Sandworm Burrowers,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Victim,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Slavers,-1,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Sorcerers,,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,,,-1,-1,,-1,-1,1,,,,,,,,,, +Fearscape,,-1,-1,-1,-1,-1,-1,-1,-1,,,-1,-1,-1,-1,-1,,-1,-1,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Sher'Tul,,,,,,,,,,,,,,,,,,,,,,,,,,,-1,,, +Cosmic Fauna,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,-1,-1,-1,-1,-1,,-1,-1,-1,,-1,-1,-1,,-1,-1,,-1, ]] local facts = {} diff --git a/game/modules/tome/data/general/npcs/construct.lua b/game/modules/tome/data/general/npcs/construct.lua index 3e55d8510559420c39d50242351daa3fc896406f..5da7e28625c129b26257b30ef341948c94e274b2 100644 --- a/game/modules/tome/data/general/npcs/construct.lua +++ b/game/modules/tome/data/general/npcs/construct.lua @@ -52,6 +52,7 @@ newEntity{ [Talents.T_ARMOUR_TRAINING]={base=2, every=10, max=5}, }, not_power_source = {nature=true}, + power_source = {arcane=true}, } newEntity{ base = "BASE_NPC_CONSTRUCT", diff --git a/game/modules/tome/data/general/npcs/horror.lua b/game/modules/tome/data/general/npcs/horror.lua index 14d4d275ff6d6ac382f6d90c623a369c88283ac8..5032846273b772ed477829ca236e728145ba775a 100644 --- a/game/modules/tome/data/general/npcs/horror.lua +++ b/game/modules/tome/data/general/npcs/horror.lua @@ -433,6 +433,7 @@ newEntity{ base = "BASE_NPC_HORROR", {type="horror", subtype="eldritch", name="luminous horror", number=2, no_subescort=true}, }, ingredient_on_death = "LUMINOUS_HORROR_DUST", + power_source = {arcane=true}, } newEntity{ base = "BASE_NPC_HORROR", @@ -468,6 +469,7 @@ newEntity{ base = "BASE_NPC_HORROR", }, resolvers.sustains_at_birth(), + power_source = {arcane=true}, make_escort = { {type="horror", subtype="eldritch", name="luminous horror", number=1, no_subescort=true}, @@ -598,6 +600,7 @@ newEntity{ base = "BASE_NPC_HORROR", [Talents.T_SLIME_WAVE]={base=2, every=8, max=7}, [Talents.T_TENTACLE_GRAB]={base=2, every=7, max=6}, }, + power_source = {antimagic=true}, } newEntity{ base = "BASE_NPC_HORROR", @@ -704,7 +707,7 @@ With each slow breath it takes reality distorts around it. Blue twirls into red }, resolvers.inscriptions(2, {"regeneration infusion", "phase door rune"}, nil, true), -- Really has a phase door rune :P - + power_source = {psionic=true}, resolvers.sustains_at_birth(), -- Used to track if he's awake or spawning projections @@ -797,6 +800,7 @@ newEntity{ base = "BASE_NPC_HORROR", define_as = "DREAM_SEED", }, resolvers.sustains_at_birth(), + power_source = {psionic=true}, -- Remove ourselves from the dream seed limit on_die = function(self) diff --git a/game/modules/tome/data/general/npcs/losgoroth.lua b/game/modules/tome/data/general/npcs/losgoroth.lua index 9de25491cae2f9911405560e91cd32e077ee174a..dc0fb8ebcf877af829c5e485de71b97778bea0ff 100644 --- a/game/modules/tome/data/general/npcs/losgoroth.lua +++ b/game/modules/tome/data/general/npcs/losgoroth.lua @@ -25,6 +25,7 @@ newEntity{ blood_color = colors.DARK_GREY, display = "E", color=colors.DARK_GREY, desc = [[Losgoroth are mighty void elementals, native to the void between the stars. They are rarely seen on a planet's surface.]], + faction = "cosmic-fauna", combat = { dam=resolvers.levelup(resolvers.mbonus(40, 15), 1, 1.2), atk=15, apr=15, dammod={mag=0.8}, damtype=DamageType.ARCANE }, diff --git a/game/modules/tome/data/general/npcs/orc-gorbat.lua b/game/modules/tome/data/general/npcs/orc-gorbat.lua index 5d11a3110e1b3fe25dbd19c22895c86867c982bb..219d9bdb32e0e5196abecfcfd87b1240db3fd5ff 100644 --- a/game/modules/tome/data/general/npcs/orc-gorbat.lua +++ b/game/modules/tome/data/general/npcs/orc-gorbat.lua @@ -172,4 +172,5 @@ newEntity{ base = "BASE_NPC_ORC_GORBAT", [Talents.T_MANA_CLASH]={base=4, every=5, max=9}, }, resolvers.racial(), + power_source = {antimagic=true}, } diff --git a/game/modules/tome/data/general/npcs/ritch.lua b/game/modules/tome/data/general/npcs/ritch.lua index 21bff9a4b22cbaa98683debc2e644ca20d03df58..c9c3f0378c83112811bde6a26a518a31bd9ccfe0 100644 --- a/game/modules/tome/data/general/npcs/ritch.lua +++ b/game/modules/tome/data/general/npcs/ritch.lua @@ -83,7 +83,7 @@ newEntity{ base = "BASE_NPC_RITCH", } newEntity{ base = "BASE_NPC_RITCH", - name = "ritch hive mother", color=colors.light_RED, + name = "ritch hive mother", color=colors.LIGHT_RED, level_range = {32, nil}, exp_worth = 1, rarity = 4, max_life = 250, diff --git a/game/modules/tome/data/general/npcs/shivgoroth.lua b/game/modules/tome/data/general/npcs/shivgoroth.lua index 3f2cae7b52e005fcae5325ea3d585c86f699d197..b2aa052779428937c8b3b67e275e803dfc199dec 100644 --- a/game/modules/tome/data/general/npcs/shivgoroth.lua +++ b/game/modules/tome/data/general/npcs/shivgoroth.lua @@ -34,7 +34,7 @@ newEntity{ rank = 2, size_category = 3, levitation = 1, - + power_source = {arcane=true}, autolevel = "dexmage", ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, }, diff --git a/game/modules/tome/data/general/npcs/thieve.lua b/game/modules/tome/data/general/npcs/thieve.lua index 5826850a6124c0ebcb37a83299d9cc3dc3e2d1a9..57575741436e8bf632cf37e668228ae0588078f6 100644 --- a/game/modules/tome/data/general/npcs/thieve.lua +++ b/game/modules/tome/data/general/npcs/thieve.lua @@ -182,4 +182,5 @@ newEntity{ base = "BASE_NPC_THIEF", define_as = "THIEF_ASSASSIN", resolvers.sustains_at_birth(), autolevel = "rogue", + power_source = {technique=true, arcane=true}, } diff --git a/game/modules/tome/data/general/objects/boss-artifacts-far-east.lua b/game/modules/tome/data/general/objects/boss-artifacts-far-east.lua index de4306f8d4c362724845010ce01803f693cac00b..319c3488a2925e362f783ac19cdf063e93ab942a 100644 --- a/game/modules/tome/data/general/objects/boss-artifacts-far-east.lua +++ b/game/modules/tome/data/general/objects/boss-artifacts-far-east.lua @@ -27,6 +27,8 @@ newEntity{ base = "BASE_KNIFE", define_as = "LIFE_DRINKER", unique = true, name = "Life Drinker", image = "object/artifact/dagger_life_drinker.png", unided_name = "blood coated dagger", + moddable_tile = "special/%s_dagger_life_drinker", + moddable_tile_big = true, desc = [[Black blood for foul deeds. This dagger serves evil.]], level_range = {40, 50}, rarity = 300, @@ -62,6 +64,8 @@ newEntity{ base = "BASE_TRIDENT", define_as = "TRIDENT_TIDES", unided_name = "ever-dripping trident", name = "Trident of the Tides", unique=true, image = "object/artifact/trident_of_the_tides.png", + moddable_tile = "special/%s_trident_of_the_tides", + moddable_tile_big = true, desc = [[The power of the tides rush through this trident. Tridents require the exotic weapons mastery talent to use correctly.]], require = { stat = { str=35 }, }, diff --git a/game/modules/tome/data/general/objects/boss-artifacts-maj-eyal.lua b/game/modules/tome/data/general/objects/boss-artifacts-maj-eyal.lua index 1deaf4d1146602dd4b9a67c8cf61250875a7fbb8..630a14ec93ee002685c56147025c72ef813f61eb 100644 --- a/game/modules/tome/data/general/objects/boss-artifacts-maj-eyal.lua +++ b/game/modules/tome/data/general/objects/boss-artifacts-maj-eyal.lua @@ -28,6 +28,8 @@ newEntity{ base = "BASE_LONGSWORD", power_source = {arcane=true}, define_as = "LONGSWORD_WINTERTIDE", unided_name = "glittering longsword", image="object/artifact/wintertide.png", name = "Wintertide", unique=true, + moddable_tile = "special/%s_wintertide", + moddable_tile_big = true, desc = [[The air seems to freeze around the blade of this sword, draining all heat from the area. It is said the Conclave created this weapon for their warmaster during the dark times of the first allure war.]], require = { stat = { str=35 }, }, @@ -551,6 +553,8 @@ newEntity{ base = "BASE_WARAXE", define_as = "MALEDICTION", unided_name = "pestilent waraxe", name = "Malediction", unique=true, image = "object/artifact/axe_malediction.png", + moddable_tile = "special/%s_axe_malediction", + moddable_tile_big = true, desc = [[The land withers and crumbles wherever this cursed axe rests.]], require = { stat = { str=55 }, }, level_range = {35, 45}, @@ -791,6 +795,7 @@ newEntity{ base = "BASE_SHIELD", define_as = "SANGUINE_SHIELD", unided_name = "bloody shield", name = "Sanguine Shield", unique=true, image = "object/artifact/sanguine_shield.png", + moddable_tile = "special/%s_hand_sanguine_shield", moddable_tile_big = true, desc = [[Though tarnished and spattered with blood, the emblem of the Sun still manages to shine through on this shield.]], require = { stat = { str=39 }, }, level_range = {35, 45}, @@ -1028,6 +1033,8 @@ newEntity{ base = "BASE_WARAXE", define_as = "SKULLCLEAVER", unided_name = "crimson waraxe", name = "Skullcleaver", unique=true, image = "object/artifact/axe_skullcleaver.png", + moddable_tile = "special/%s_axe_skullcleaver", + moddable_tile_big = true, desc = [[A small but sharp axe, with a handle made of polished bone. The blade has chopped through the skulls of many, and has been stained a deep crimson.]], require = { stat = { str=18 }, }, level_range = {5, 12}, @@ -1056,6 +1063,7 @@ newEntity{ base = "BASE_DIGGER", desc = [[A huge tooth taken from the Mouth, in the Deep Bellow.]], level_range = {5, 12}, cost = 50, + rarity = 200, material_level = 1, digspeed = 12, wielder = { @@ -1390,6 +1398,8 @@ newEntity{ base = "BASE_TRIDENT", define_as = "TRIDENT_STREAM", unided_name = "ornate trident", name = "The River's Fury", unique=true, image = "object/artifact/the_rivers_fury.png", + moddable_tile = "special/%s_the_rivers_fury", + moddable_tile_big = true, desc = [[This gorgeous and ornate trident was wielded by Lady Nashva, and when you hold it, you can faintly hear the roar of a rushing river.]], require = { stat = { str=12 }, }, level_range = {1, 10}, @@ -1424,6 +1434,8 @@ newEntity{ base = "BASE_KNIFE", unique = true, name = "Unerring Scalpel", image = "object/artifact/unerring_scalpel.png", unided_name = "long sharp scalpel", + moddable_tile = "special/%s_unerring_scalpel", + moddable_tile_big = true, desc = [[This scalpel was used by the dread sorcerer Kor'Pul when he began learning the necromantic arts in the Age of Dusk. Many were the bodies, living and dead, that became unwilling victims of his terrible experiments.]], level_range = {1, 12}, rarity = 200, diff --git a/game/modules/tome/data/general/objects/boss-artifacts.lua b/game/modules/tome/data/general/objects/boss-artifacts.lua index 81c58ac7f0b0c55404917a9f9f152b758a5edc71..c52301d702c92390a0c97731bae5cb20d1501eb2 100644 --- a/game/modules/tome/data/general/objects/boss-artifacts.lua +++ b/game/modules/tome/data/general/objects/boss-artifacts.lua @@ -46,6 +46,8 @@ newEntity{ base = "BASE_BATTLEAXE", define_as = "KHULMANAR_WRATH", name = "Khulmanar's Wrath", color = colors.DARK_RED, image = "object/artifact/hellfire.png", unided_name = "firey blackened battleaxe", unique = true, + moddable_tile = "special/hellfire", + moddable_tile_big = true, desc = [[Blackened with soot and covered in spikes, this battleaxe roars with the flames of the Fearscape. Given by Urh'Rok himself to his general, this powerful weapon can burn even the most resilient of foes.]], level_range = {37, 50}, rarity = 300, @@ -113,6 +115,8 @@ newEntity{ base = "BASE_LONGSWORD", define_as = "RIFT_SWORD", unique = true, name = "Blade of Distorted Time", image = "object/artifact/blade_of_distorted_time.png", unided_name = "time-warped sword", + moddable_tile = "special/%s_blade_of_distorted_time", + moddable_tile_big = true, desc = [[The remnants of a damaged timeline, this blade shifts and fades at random.]], level_range = {30, 50}, rarity = 220, @@ -162,6 +166,8 @@ newEntity{ base = "BASE_BATTLEAXE", define_as = "GAPING_MAW", name = "The Gaping Maw", color = colors.SLATE, image = "object/artifact/battleaxe_the_gaping_maw.png", unided_name = "huge granite battleaxe", unique = true, + moddable_tile = "special/battleaxe_the_gaping_maw", + moddable_tile_big = true, desc = [[This huge granite battleaxe is as much mace as it is axe. The shaft is made of blackened wood tightly bound in drakeskin leather and the sharpened granite head glistens with a viscous green fluid.]], level_range = {38, 50}, rarity = 300, diff --git a/game/modules/tome/data/general/objects/egos/amulets.lua b/game/modules/tome/data/general/objects/egos/amulets.lua index bab1a1a31235a99dc87432e5df333a3897f8b992..47dbf0ab682a2a1b78c44572e7840932593ba4ef 100644 --- a/game/modules/tome/data/general/objects/egos/amulets.lua +++ b/game/modules/tome/data/general/objects/egos/amulets.lua @@ -229,7 +229,7 @@ newEntity{ }, charm_power = resolvers.mbonus_material(70, 30), charm_power_def = {add=15, max=50, floor=true}, - resolvers.charm("teleports you randomly (rad %d)", 15, function(self, who) + resolvers.charm("teleport you randomly (rad %d)", 15, function(self, who) game.level.map:particleEmitter(who.x, who.y, 1, "teleport") who:teleportRandom(who.x, who.y, self:getCharmPower(who)) game.level.map:particleEmitter(who.x, who.y, 1, "teleport") diff --git a/game/modules/tome/data/general/objects/gem.lua b/game/modules/tome/data/general/objects/gem.lua index 56e2ade5710da3afe76772ca1f882869ae23b7ce..627122315bce6f4195c501d6745d24f46015104d 100644 --- a/game/modules/tome/data/general/objects/gem.lua +++ b/game/modules/tome/data/general/objects/gem.lua @@ -30,15 +30,41 @@ newEntity{ desc = [[Gems can be sold for money or used in arcane rituals.]], } -local colors_attacks = { - black = DamageType.ACID, - blue = DamageType.LIGHTNING, - green = DamageType.NATURE, - red = DamageType.FIRE, - violet = DamageType.ARCANE, - white = DamageType.COLD, - yellow = DamageType.LIGHT, -} +local gem_color_attributes = { + black = { + damage_type = 'ACID', + alt_damage_type = 'ACID_DISARM', + particle = 'acid',}, + blue = { + damage_type = 'LIGHTNING', + alt_damage_type = 'LIGHTNING_DAZE', + particle = 'lightning_explosion',}, + green = { + damage_type = 'NATURE', + alt_damage_type = 'SPYDRIC_POISON', + particle = 'slime',}, + red = { + damage_type = 'FIRE', + alt_damage_type = 'FLAMESHOCK', + particle = 'flame',}, + violet = { + damage_type = 'ARCANE', + alt_damage_type = 'ARCANE_SILENCE', + particle = 'manathrust',}, + white = { + damage_type = 'COLD', + alt_damage_type = 'ICE', + particle = 'freeze',}, + yellow = { + damage_type = 'LIGHT', + alt_damage_type = 'LIGHT_BLIND', + particle = 'light',},} + +-- Let addons alter colors before the gems are created. This is weird, +-- since there's no base class to call it for, since it's just a +-- table. To bind: +-- class.bindHook('GemColorAttributes', function(gem_color_attributes) ... end) +class.triggerHook(gem_color_attributes, {'GemColorAttributes',}) local function newGem(name, image, cost, rarity, color, min_level, max_level, tier, power, imbue, bomb) -- Gems, randomly lootable @@ -50,7 +76,8 @@ local function newGem(name, image, cost, rarity, color, min_level, max_level, ti material_level = tier, imbue_powers = imbue, wielder = imbue, - attack_type = colors_attacks[color], + attack_type = gem_color_attributes[color].damage_type, -- deprecated, hopefully nothing's still using it. + color_attributes = gem_color_attributes[color], } -- Alchemist gems, not lootable, only created by talents newEntity{ base = "BASE_GEM", define_as = "ALCHEMIST_GEM_"..name:gsub(" ", "_"):upper(), name = "alchemist "..name:lower(), type='alchemist-gem', subtype = color, diff --git a/game/modules/tome/data/general/objects/world-artifacts-far-east.lua b/game/modules/tome/data/general/objects/world-artifacts-far-east.lua index 680a245616b8da29e262be96f95b66598e062930..8a82870191c1aa11a21230996db5f270c65da3f9 100644 --- a/game/modules/tome/data/general/objects/world-artifacts-far-east.lua +++ b/game/modules/tome/data/general/objects/world-artifacts-far-east.lua @@ -162,6 +162,7 @@ newEntity{ base = "BASE_GEM", unique = true, unided_name = "unearthly black stone", name = "Goedalath Rock", subtype = "demonic", image = "object/artifact/goedalath_rock.png", + define_as = 'GOEDALATH_ROCK', color = colors.PURPLE, level_range = {42, 50}, desc = [[A small rock that seems from beyond this world, vibrating with a fierce energy. It feels warped and terrible and evil... and yet oh so powerful.]], @@ -187,7 +188,7 @@ newEntity{ base = "BASE_GEM", on_melee_hit = {[DamageType.DARKNESS] = 34}, healing_factor = 0.5, }, -} + color_attributes = {damage_type = 'SHADOWFLAME',},} newEntity{ base = "BASE_CLOAK", power_source = {arcane=true}, define_as = "THREADS_FATE", @@ -230,6 +231,8 @@ newEntity{ base = "BASE_LONGSWORD", define_as = "BLOODEDGE", unique = true, name = "Blood-Edge", image = "object/artifact/sword_blood_edge.png", unided_name = "red crystalline sword", + moddable_tile = "special/%s_sword_blood_edge", + moddable_tile_big = true, level_range = {36, 48}, color=colors.RED, rarity = 260, @@ -344,6 +347,8 @@ newEntity{ base = "BASE_LONGSWORD", define_as = "SWORD_DAWN", unique = true, name = "Dawn's Blade", unided_name = "shining longsword", + moddable_tile = "special/%s_dawn_blade", + moddable_tile_big = true, level_range = {35, 42}, color=colors.YELLOW, image = "object/artifact/dawn_blade.png", rarity = 260, @@ -396,7 +401,7 @@ newEntity{ base = "BASE_LONGSWORD", define_as = "SWORD_DAWN", inc_damage_type={ undead=25, demon=25, - }, + }, }, on_wear = function(self, who) if who.descriptor and who.descriptor.subclass == "Sun Paladin" then @@ -444,6 +449,8 @@ newEntity{ base = "BASE_KNIFE", define_as = "MANDIBLE_UNGOLMOR", unique = true, name = "Mandible of Ungolmor", image = "object/artifact/mandible_of_ungolmor.png", unided_name = "curved, serrated black dagger", + moddable_tile = "special/%s_mandible_of_ungolmor", + moddable_tile_big = true, desc = [[This obsidian-crafted, curved blade is studded with the deadly fangs of the Ungolmor. It seems to drain light from the world around it.]], level_range = {40, 50}, rarity = 270, @@ -480,6 +487,8 @@ newEntity{ base = "BASE_KNIFE", define_as = "KINETIC_SPIKE", unique = true, name = "Kinetic Spike", image = "object/artifact/kinetic_spike.png", unided_name = "bladeless hilt", + moddable_tile = "special/%s_kinetic_spike", + moddable_tile_big = true, desc = [[A simple, rudely crafted stone hilt, this object manifests a blade of wavering, nearly invisible force, like a heat haze, as you grasp it. Despite its simple appearance, it is capable of shearing through solid granite, in the hands of those with the necessary mental fortitude to use it properly.]], level_range = {42, 50}, rarity = 310, @@ -550,13 +559,11 @@ newEntity{ base = "BASE_STAFF", [Talents.T_PARADOX_CLONE] = 7, [Talents.T_TEMPORAL_CLONE] = 5, [Talents.T_TIME_STOP] = 5, - -- [Talents.T_GRAVITY_WELL] = 6, -- thematically wrong [Talents.T_BODY_REVERSION] = 2, }, talents_types_mastery = { - ["chronomancy/timeline-threading"] = 0.2, + ["chronomancy/fate-threading"] = 0.2, ["chronomancy/timetravel"] = 0.2, - ["chronomancy/paradox"] = 0.2, ["spell/temporal"] = 0.2, }, }, diff --git a/game/modules/tome/data/general/objects/world-artifacts-maj-eyal.lua b/game/modules/tome/data/general/objects/world-artifacts-maj-eyal.lua index efddca6f26197f26dcdc191af6f363c5474cfb33..aed7c6a7d734da2d35b88b5f989be40df10da840 100644 --- a/game/modules/tome/data/general/objects/world-artifacts-maj-eyal.lua +++ b/game/modules/tome/data/general/objects/world-artifacts-maj-eyal.lua @@ -268,6 +268,8 @@ newEntity{ base = "BASE_LONGSWORD", unique = true, name = "Spellblade", image = "object/artifact/weapon_spellblade.png", unided_name = "glowing long sword", + moddable_tile = "special/%s_weapon_spellblade", + moddable_tile_big = true, level_range = {40, 45}, color=colors.AQUAMARINE, rarity = 250, @@ -425,6 +427,8 @@ newEntity{ base = "BASE_KNIFE", unique = true, name = "Orc Feller", image = "object/artifact/dagger_orc_feller.png", unided_name = "shining dagger", + moddable_tile = "special/%s_dagger_orc_feller", + moddable_tile_big = true, desc = [[During the invasion of Eldoral the Halfling Rogue Herah is said to have slain over one hundred orcs while defending a group of refugees.]], level_range = {40, 50}, rarity = 300, @@ -462,6 +466,8 @@ newEntity{ base = "BASE_MACE", unique = true, name = "Nature's Vengeance", color = colors.BROWN, image = "object/artifact/mace_natures_vengeance.png", unided_name = "thick wooden mace", + moddable_tile = "special/%s_mace_natures_vengeance", + moddable_tile_big = true, desc = [[This thick-set mace was used by the Spellhunter Vorlan, who crafted it from the wood of an ancient oak that was uprooted during the Spellblaze. Many were the wizards and witches felled by this weapon, brought to justice for the crimes they committed against nature.]], level_range = {20, 34}, rarity = 340, @@ -579,6 +585,8 @@ newEntity{ base = "BASE_BATTLEAXE", unique = true, unided_name = "viciously sharp battle axe", name = "Drake's Bane", image = "object/artifact/axe_drakes_bane.png", + moddable_tile = "special/axe_drakes_bane", + moddable_tile_big = true, color = colors.RED, desc = [[The killing of Kroltar, mightiest of wyrms, took seven months and the lives of 20,000 dwarven warriors. Finally the beast was worn down and mastersmith Gruxim, standing atop the bodies of his fallen comrades, was able slit its throat with this axe crafted purely for the purpose of penetrating the wyrm's hide.]], require = { stat = { str=45 }, }, @@ -606,6 +614,8 @@ newEntity{ base = "BASE_WARAXE", unique = true, name = "Blood-Letter", image = "object/artifact/weapon_axe_blood_letter.png", unided_name = "glacial hatchet", + moddable_tile = "special/%s_weapon_axe_blood_letter", + moddable_tile_big = true, desc = [[A hand axe carved out of the most frozen parts of the northern wasteland.]], level_range = {25, 35}, rarity = 235, @@ -858,6 +868,8 @@ newEntity{ base = "BASE_LONGSWORD", unique = true, name = "Witch-Bane", color = colors.LIGHT_STEEL_BLUE, image = "object/artifact/sword_witch_bane.png", unided_name = "an ivory handled voratun longsword", + moddable_tile = "special/%s_sword_witch_bane", + moddable_tile_big = true, desc = [[A thin voratun blade with an ivory handle wrapped in purple cloth. The weapon is nearly as legendary as its former owner, Marcus Dunn, and was thought to have been destroyed after Marcus was slain near the end of the Spellhunt. It seems somebody well versed in antimagic could use it to its fullest potential.]], level_range = {38, 50}, @@ -1037,6 +1049,8 @@ newEntity{ base = "BASE_KNIFE", -- Thanks Grayswandir! unique = true, name = "Spellblaze Shard", image = "object/artifact/spellblaze_shard.png", unided_name = "crystalline dagger", + moddable_tile = "special/%s_spellblaze", + moddable_tile_big = true, desc = [[This jagged crystal glows with an unnatural light. A strap of cloth is wrapped around one end, as a handle.]], level_range = {12, 25}, rarity = 200, @@ -1067,6 +1081,8 @@ newEntity{ base = "BASE_KNIFE", --Razakai's idea, slightly modified unique = true, name = "Mercy", image = "object/artifact/mercy.png", unided_name = "wickedly sharp dagger", + moddable_tile = "special/%s_mercy", + moddable_tile_big = true, desc = [[This dagger was used by a nameless healer during the Age of Dusk. The plagues that ravaged his town were beyond the ability of mortal man to treat, so he took to using his dagger to as an act of mercy when faced with hopeless patients. Despite his good intentions, it is now cursed with dark power, letting it kill in a single stroke against those already weakened.]], level_range = {30, 40}, rarity = 250, diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index fe14ae59e2e63378a7392d5da612475c9fdc6a21..69910685db2e44cb9b61564b2bbb54fb1cab7d4b 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -673,6 +673,8 @@ newEntity{ base = "BASE_KNIFE", unique = true, name = "Silent Blade", image = "object/artifact/dagger_silent_blade.png", unided_name = "shining dagger", + moddable_tile = "special/%s_dagger_silent_blade", + moddable_tile_big = true, desc = [[A thin, dark dagger that seems to meld seamlessly into the shadows.]], level_range = {23, 28}, rarity = 200, @@ -695,6 +697,8 @@ newEntity{ base = "BASE_KNIFE", define_as = "ART_PAIR_MOON", unique = true, name = "Moon", image = "object/artifact/dagger_moon.png", unided_name = "crescent blade", + moddable_tile = "special/%s_dagger_moon", + moddable_tile_big = true, desc = [[A viciously curved blade that a folk story says is made from a material that originates from the moon. Devouring the light around it, it fades.]], level_range = {20, 30}, rarity = 200, @@ -731,6 +735,8 @@ newEntity{ base = "BASE_KNIFE", define_as = "ART_PAIR_STAR", unique = true, name = "Star", unided_name = "jagged blade", image = "object/artifact/dagger_star.png", + moddable_tile = "special/%s_dagger_star", + moddable_tile_big = true, desc = [[Legend tells of a blade, shining bright as a star. Forged from a material fallen from the skies, it glows.]], level_range = {20, 30}, rarity = 200, @@ -834,6 +840,8 @@ newEntity{ base = "BASE_MACE", unique = true, name = "Crooked Club", color = colors.GREEN, image = "object/artifact/weapon_crooked_club.png", unided_name = "weird club", + moddable_tile = "special/%s_weapon_crooked_club", + moddable_tile_big = true, desc = [[An oddly twisted club with a hefty weight on the end. There's something very strange about it.]], level_range = {12, 20}, rarity = 192, @@ -1270,6 +1278,8 @@ newEntity{ base = "BASE_BATTLEAXE", unique = true, unided_name = "crude iron battle axe", name = "Crude Iron Battle Axe of Kroll", color = colors.GREY, image = "object/artifact/crude_iron_battleaxe_of_kroll.png", + moddable = "special/crude_iron_battleaxe_of_kroll", + moddable_tile_big = true, desc = [[Made in times before the Dwarves learned beautiful craftsmanship, the rough appearance of this axe belies its great power. Only Dwarves may harness its true strength, however.]], require = { stat = { str=50 }, }, level_range = {39, 46}, @@ -1415,6 +1425,9 @@ newEntity{ base = "BASE_LIGHT_ARMOR", unique = true, name = "Skin of Many", image = "object/artifact/robe_skin_of_many.png", unided_name = "stitched skin armour", + moddable_tile = "special/skin_of_many", + moddable_tile2 = "special/skin_of_many_legs", + moddable_tile_big = true, desc = [[The stitched-together skins of many creatures. Some eyes and mouths still decorate the robe, and some still live, screaming in tortured agony.]], color = colors.BROWN, level_range = {12, 22}, @@ -1589,6 +1602,8 @@ newEntity{ base = "BASE_MACE", power_source = {nature=true}, name = "Ureslak's Femur", define_as = "URESLAK_FEMUR", image="object/artifact/club_ureslaks_femur.png", unided_name = "a strangely colored bone", unique = true, + moddable_tile = "special/%s_club_ureslaks_femur", + moddable_tile_big = true, desc = [[A shortened femur of the mighty prismatic dragon, this erratic club still pulses with Ureslak's volatile nature.]], level_range = {42, 50}, require = { stat = { str=45, dex=30 }, }, @@ -1657,6 +1672,8 @@ newEntity{ base = "BASE_WARAXE", power_source = {psionic=true}, unique = true, unided_name = "razor sharp war axe", name = "Razorblade, the Cursed Waraxe", color = colors.LIGHT_BLUE, image = "object/artifact/razorblade_the_cursed_waraxe.png", + moddable_tile = "special/%s_razorblade_the_cursed_waraxe", + moddable_tile_big = true, desc = [[This mighty axe can cleave through armour like the sharpest swords, yet hit with all the impact of a heavy club. It is said the wielder will slowly grow mad. This, however, has never been proven - no known possessor of this item has lived to tell the tale.]], require = { stat = { str=42 }, }, @@ -1683,6 +1700,8 @@ newEntity{ base = "BASE_LONGSWORD", define_as = "ART_PAIR_TWSWORD", unique = true, name = "Sword of Potential Futures", image = "object/artifact/sword_of_potential_futures.png", unided_name = "under-wrought blade", + moddable_tile = "special/%s_sword_of_potential_futures", + moddable_tile_big = true, desc = [[Legend has it this blade is one of a pair: twin blades forged in the earliest of days of the Wardens. To an untrained wielder it is less than perfect; to a Warden, it represents the untapped potential of time.]], level_range = {20, 30}, rarity = 250, @@ -1734,6 +1753,8 @@ newEntity{ base = "BASE_KNIFE", define_as = "ART_PAIR_TWDAG", unique = true, name = "Dagger of the Past", image = "object/artifact/dagger_of_the_past.png", unided_name = "rusted blade", + moddable_tile = "special/%s_dagger_of_the_past", + moddable_tile_big = true, desc = [[Legend has it this blade is one of a pair: twin blades forged in the earliest of days of the Wardens. To an untrained wielder it is less than perfect; to a Warden, it represents the opportunity to learn from the mistakes of the past.]], level_range = {20, 30}, rarity = 250, @@ -2562,6 +2583,8 @@ newEntity{ base = "BASE_KNIFE", unique = true, name = "Umbral Razor", image = "object/artifact/dagger_silent_blade.png", unided_name = "shadowy dagger", + moddable_tile = "special/%s_dagger_silent_blade", + moddable_tile_big = true, desc = [[This dagger seems to be formed of pure shadows, with a strange miasma surrounding it.]], level_range = {12, 25}, rarity = 200, @@ -2935,6 +2958,8 @@ newEntity{ base = "BASE_LONGSWORD", define_as="CORPUS", unique = true, name = "Corpathus", image = "object/artifact/corpus.png", unided_name = "bound sword", + moddable_tile = "special/%s_corpus", + moddable_tile_big = true, desc = [[Thick straps encircle this blade. Jagged edges like teeth travel down the blade, bisecting it. It fights to overcome the straps, but lacks the strength.]], level_range = {20, 30}, rarity = 250, @@ -3029,6 +3054,8 @@ newEntity{ base = "BASE_LONGSWORD", unique = true, name = "Anmalice", image = "object/artifact/anima.png", define_as = "ANIMA", unided_name = "twisted blade", + moddable_tile = "special/%s_anmalice", + moddable_tile_big = true, desc = [[The eye on the hilt of this blade seems to glare at you, piercing your soul and mind. Tentacles surround the hilt, latching onto your hand.]], level_range = {30, 40}, rarity = 250, @@ -3114,6 +3141,8 @@ newEntity{ base = "BASE_LONGSWORD", define_as="MORRIGOR", power_source = {arcane=true, unknown=true}, unique = true, sentient = true, name = "Morrigor", image = "object/artifact/morrigor.png", + moddable_tile = "special/%s_morrigor", + moddable_tile_big = true, unided_name = "jagged, segmented, sword", desc = [[This heavy, ridged blade emanates magical power, yet as you grasp the handle an icy chill runs its course through your spine. You feel the disembodied presence of all those slain by it. In unison, they demand company.]], level_range = {20, 30}, @@ -3425,6 +3454,7 @@ newEntity{ base = "BASE_SHIELD", unique = true, name = "Summertide", unided_name = "shining gold shield", image = "object/artifact/summertide.png", + moddable_tile = "special/%s_hand_summertide", moddable_tile_big = true, level_range = {38, 50}, color=colors.GOLD, rarity = 350, @@ -3640,7 +3670,7 @@ newEntity{ base = "BASE_LEATHER_BOOT", --Thanks Grayswandir! wielder = { combat_def = 6, fatigue = 1, - spellpower=5, + combat_spellpower=5, inc_stats = { [Stats.STAT_MAG] = 8, [Stats.STAT_CUN] = 8,}, resists={ [DamageType.ARCANE] = 12, @@ -5051,6 +5081,8 @@ newEntity{ base = "BASE_BATTLEAXE", unique = true, unided_name = "gore stained battleaxe", name = "Eksatin's Ultimatum", color = colors.GREY, image = "object/artifact/eskatins_ultimatum.png", + moddable_tile = "special/eskatins_ultimatum", + moddable_tile_big = true, desc = [[This gore-stained battleaxe was once used by an infamously sadistic king, who took the time to personally perform each and every execution he ordered. He kept a vault of every head he ever removed, each and every one of them carefully preserved. When he was overthrown, his own head was added as the centrepiece of the vault, which was maintained as a testament to his cruelty.]], require = { stat = { str=50 }, }, level_range = {39, 46}, @@ -5375,6 +5407,8 @@ newEntity{ base = "BASE_KNIFE", --Thanks FearCatalyst/FlarePusher! unique = true, name = "Spelldrinker", image = "object/artifact/spelldrinker.png", unided_name = "eerie black dagger", + moddable_tile = "special/%s_spelldrinker", + moddable_tile_big = true, desc = [[Countless mages have fallen victim to the sharp sting of this blade, betrayed by those among them with greed for ever greater power. Passed on and on, this blade has developed a thirst of its own.]], level_range = {20, 30}, @@ -5438,6 +5472,8 @@ newEntity{ base = "BASE_LONGSWORD", --Thanks BadBadger? unique = true, name = "Twilight's Edge", image = "object/artifact/twilights_edge.png", unided_name = "shining long sword", + moddable_tile = "special/%s_twilights_edge", + moddable_tile_big = true, level_range = {32, 42}, color=colors.GREY, rarity = 250, @@ -5500,6 +5536,8 @@ newEntity{ base = "BASE_LONGSWORD", unique = true, name = "Acera", unided_name = "corroded sword", image = "object/artifact/acera.png", + moddable_tile = "special/%s_acera", + moddable_tile_big = true, level_range = {25, 35}, color=colors.GREEN, rarity = 300, @@ -5567,6 +5605,8 @@ newEntity{ base = "BASE_LONGSWORD", power_source = {technique=true, psionic=true}, define_as = "BUTCHER", name = "Butcher", unique=true, image="object/artifact/butcher.png", unided_name = "blood drenched shortsword", color=colors.CRIMSON, + moddable_tile = "special/%s_butcher", + moddable_tile_big = true, desc = [[Be it corruption, madness or eccentric boredom, the halfling butcher by the name of Caleb once took to eating his kin instead of cattle. His spree was never ended and nobody knows where he disappeared to. Only the blade remained, stuck fast in bloodied block. Beneath, a carving said "This was fun, let's do it again some time."]], require = { stat = { str=40 }, }, level_range = {36, 48}, @@ -5929,6 +5969,8 @@ newEntity{ base = "BASE_LONGSWORD", --For whatever artists draws this: it's a ra unique = true, name = "Punae's Blade", unided_name = "thin blade", image = "object/artifact/punaes_blade.png", + moddable_tile = "special/%s_punaes_blade", + moddable_tile_big = true, level_range = {28, 38}, color=colors.GREY, rarity = 300, @@ -6076,6 +6118,8 @@ newEntity{ base = "BASE_KNIFE", --Shibari's #1 unique = true, name = "Shantiz the Stormblade", unided_name = "thin stormy blade", image = "object/artifact/shantiz_the_stromblade.png", + moddable_tile = "special/%s_shantiz_the_stromblade", + moddable_tile_big = true, level_range = {18, 33}, material_level = 3, rarity = 300, @@ -6137,6 +6181,8 @@ newEntity{ base = "BASE_KNIFE", unique = true, name = "Swordbreaker", image = "object/artifact/swordbreaker.png", unided_name = "hooked blade", + moddable_tile = "special/%s_swordbreaker", + moddable_tile_big = true, desc = [[This ordinary blade is made of fine, sturdy voratun and outfitted with jagged hooks along the edge. This simple appearance belies a great power - the hooked maw of this dagger broke many a blade and the stride of many would-be warriors.]], level_range = {20, 30}, rarity = 250, @@ -6208,6 +6254,7 @@ newEntity{ base = "BASE_GREATMAUL", color = colors.BLUE, name = "Tirakai's Maul", image = "object/artifact/tirakais_maul.png", desc = [[This massive hammer is formed from a thick mass of strange crystalline growths. In the side of the hammer itself you see an empty slot; it looks like a gem of your own could easily fit inside it.]], + moddable_tile = "special/tirakais_maul", moddable_tile_big = true, gemDesc = "None", -- Defined by the elemental properties and used by special_desc special_desc = function(self) -- You'll want to color this and such @@ -6225,6 +6272,17 @@ newEntity{ base = "BASE_GREATMAUL", damrange=1.3, dammod = {str=1.2, mag=0.1}, }, + -- executed for specific gems. + -- key corresponds to: gem.define_as or gem.name + unique_gems = { + GOEDALATH_ROCK = function(maul, gem) + maul.combat.damtype = 'SHADOWFLAME' + table.mergeAdd(maul.wielder, { + inc_damage = {FIRE = 3 * gem.material_level, DARKNESS = 3 * gem.material_level,}, + resists_pen = {all = 2 * gem.material_level},}, + true) + maul.gemDesc = "Demonic" + end,}, max_power = 10, power_regen = 1, use_power = { name = "imbue the hammer with a gem of your choice", power = 10, use = function(self, who) @@ -6260,105 +6318,52 @@ newEntity{ base = "BASE_GREATMAUL", self.act = mod.class.Object.act self.talent_on_spell = nil - - self.material_level=gem.material_level - local scalingFactor = self.material_level - self.combat.dam = 8 + (12 * scalingFactor) - self.combat.apr = (3 * scalingFactor) - self.combat.physcrit = 4 + (2 * scalingFactor) + local scalingFactor = gem.material_level + local combatFactor = math.max(scalingFactor, 2) -- Prevent tier 1 gems from degrading the maul + + self.material_level=combatFactor + + self.combat.dam = 8 + (12 * combatFactor) + self.combat.apr = (3 * combatFactor) + self.combat.physcrit = 4 + (2 * combatFactor) self.combat.dammod = {str=1.2, mag=0.1} self.combat.damrange = 1.3 - + self.wielder = { inc_stats = {[Stats.STAT_MAG] = (2 * scalingFactor), [Stats.STAT_CUN] = (2 * scalingFactor), [Stats.STAT_DEX] = (2 * scalingFactor),}, } - + -- Each element merges its effect into the combat/wielder tables (or anything else) after the base stats are scaled -- You can modify damage and such here too but you should probably make static tables instead of merging - if gem.subtype =="black" then -- Acid - self.combat.damtype = DamageType.ACID - table.mergeAdd(self.wielder, {inc_damage = { [DamageType.ACID] = 4 * scalingFactor} }, true) - - self.combat.burst_on_crit = {[DamageType.ACID_DISARM] = 12 * scalingFactor,} - self.gemDesc = "Acid" - end - if gem.subtype =="blue" then -- Lightning - self.combat.damtype = DamageType.LIGHTNING - table.mergeAdd(self.wielder, { - inc_damage = { [DamageType.LIGHTNING] = 4 * scalingFactor} - - }, true) - self.combat.burst_on_crit = {[DamageType.LIGHTNING_DAZE] = 12 * scalingFactor,} - self.gemDesc = "Lightning" - end - if gem.subtype =="green" then -- Nature - self.combat.damtype = DamageType.NATURE - table.mergeAdd(self.wielder, { - inc_damage = { [DamageType.NATURE] = 4 * scalingFactor} - - }, true) - self.combat.burst_on_crit = {[DamageType.SPYDRIC_POISON] = 12 * scalingFactor,} - self.gemDesc = "Nature" - end - if gem.subtype =="red" then -- Fire - self.combat.damtype = DamageType.FIRE - table.mergeAdd(self.wielder, { - inc_damage = { [DamageType.FIRE] = 4 * scalingFactor}, - }, true) - self.combat.burst_on_crit = {[DamageType.FLAMESHOCK] = 12 * scalingFactor,} - self.gemDesc = "Fire" - end - if gem.subtype =="violet" then -- Arcane - self.combat.damtype = DamageType.ARCANE - table.mergeAdd(self.wielder, { - inc_damage = { [DamageType.ARCANE] = 4 * scalingFactor} - - }, true) - self.combat.burst_on_crit = {[DamageType.ARCANE_SILENCE] = 12 * scalingFactor,} - self.gemDesc = "Arcane" - end - if gem.subtype =="white" then -- Cold - self.combat.damtype = DamageType.COLD - table.mergeAdd(self.wielder, { - inc_damage = { [DamageType.COLD] = 4 * scalingFactor} - - }, true) - self.combat.burst_on_crit = {[DamageType.ICE] = 12 * scalingFactor,} - self.gemDesc = "Cold" - end - if gem.subtype =="yellow" then -- Light - self.combat.damtype = DamageType.LIGHT - table.mergeAdd(self.wielder, { - inc_damage = { [DamageType.LIGHT] = 4 * scalingFactor} - - }, true) - self.combat.burst_on_crit = {[DamageType.LIGHT_BLIND] = 12 * scalingFactor,} - self.gemDesc = "Light" - end - if gem.subtype == "multi-hued" then -- Some but not all artifacts, if you want to do artifact specific effects make conditionals by name, don't use this + + if gem.on_tirakai_maul_equip then + gem:on_tirakai_maul_equip(self) + elseif self.unique_gems[gem.define_as or gem.name] then + self.unique_gems[gem.define_as or gem.name](self, gem) + elseif gem.color_attributes then + self.combat.damtype = gem.color_attributes.damage_type + table.mergeAdd(self.wielder, + {inc_damage = {[gem.color_attributes.damage_type] = 4 * scalingFactor},}, + true) + self.combat.burst_on_crit = {[gem.color_attributes.alt_damage_type] = 12 * scalingFactor,} + self.gemDesc = gem.color_attributes.desc or gem.color_attributes.damage_type:lower():capitalize() + else -- Backup for weird artifacts. table.mergeAdd(self.combat, {convert_damage = {[DamageType.COLD] = 25, [DamageType.FIRE] = 25, [DamageType.LIGHTNING] = 25, [DamageType.ARCANE] = 25,} }, true) table.mergeAdd(self.wielder, { inc_damage = { all = 2 * scalingFactor}, resists_pen = { all = 2 * scalingFactor}, - }, true) - self.gemDesc = "Unique" - end - if gem.subtype == "demonic" then -- Goedalath Rock - self.combat.damtype = DamageType.SHADOWFLAME - table.mergeAdd(self.wielder, { - inc_damage = { [DamageType.FIRE] = 3 * scalingFactor, [DamageType.DARKNESS] = 3 * scalingFactor,}, - resists_pen = { all = 2 * scalingFactor}, - }, true) - self.gemDesc = "Demonic" + }, true) + self.gemDesc = 'Unique' end + game.logPlayer(who, "You imbue your %s with %s.", self:getName{do_colour=true, no_count=true}, gem:getName{do_colour=true, no_count=true}) --self.name = (gem.name .. " of Divinity") - + table.mergeAdd(self.wielder, gem.imbue_powers, true) - + if gem.talent_on_spell then self.talent_on_spell = self.talent_on_spell or {} table.append(self.talent_on_spell, gem.talent_on_spell) @@ -6828,3 +6833,73 @@ newEntity{ }, } ]=] + +-- Semi-random Artifacts +newEntity{ base = "BASE_LEATHER_CAP", define_as = "DECAYED_VISAGE", + power_source = {arcane=true}, + unique = true, + name = "Decayed Visage", + unided_name = "mask of mummified skin", image = "object/artifact/bone_runed_skull.png", + level_range = {24, 32}, + color=colors.GRAY, + rarity = 200, + desc = [[A desiccated mask of human skin, all that remains of a necromancer from the Age of Pyre who failed to achieve lichdom. The transformative process partially succeeded, leaving him unable to die as his body slowly rotted from the inside out over several years. Now his spirit resides within this last bit of mummified flesh, still hungering for eternal life.]], + cost = 200, + material_level=3, + encumber = 1, + require = { stat = { mag=25, wil=20 }, }, + wielder = { + on_melee_hit = {[DamageType.DRAIN_VIM]=10}, + max_vim = 25, + }, + max_power = 45, power_regen = 1, + use_talent = { id = Talents.T_VIMSENSE, level = 2, power = 25 }, + finish = function(self, zone, level) -- add the blood magic ego, another arcane or psionic powered ego, and some random powers (at level 25) + game.state:addRandartProperties(self, {lev = 25, egos = 2, + force_egos = {"of blood magic"}, +-- forbid_power_source = {antimagic=true}, power_source = {arcane = true, psionic=true}, + power_source = {arcane = true, psionic=true}, + force_themes = {"dark", "arcane", "blight"}}) + end, +} + +newEntity{ base = "BASE_GREATMAUL", define_as = "DREAM_MALLEUS", + power_source = {technique=true, psionic=true}, + unique = true, + name = "Dream Malleus", color = colors.UMBER, image = "object/artifact/unstoppable_mauler.png", + unided_name = "keening hammer", + desc = [[A large shimmering maul that seems to produce a ringing in your ears. It is both as malleable as thought and as hard as the strongest steel.]], + level_range = {25, 40}, + rarity = 300, + require = { stat = { str=25, wil=25 }, }, + cost = 250, + material_level = 4, + combat = { + dam = 56, + apr = 5, + dammod = {str=1.1, wil=1.2}, + melee_project={[DamageType.MIND] = 10} + }, + wielder = { + combat_mindpower = 15, + combat_mindcrit = 5, + psi_regen = 0.1, + inc_damage={ + [DamageType.MIND] = 10, + [DamageType.PHYSICAL] = 10, + }, + learn_talent = {[Talents.T_DREAM_HAMMER] = 3, [Talents.T_HAMMER_TOSS] = 3}, + talent_cd_reduction={[Talents.T_HAMMER_TOSS]=2}, + }, + finish = function(self, zone, level) -- add the projection and thought-forged egos, plus another possibly physical or mental themed ego, and 25 points of physical or mental themed random powers + game.state:addRandartProperties(self, {lev = 0, nb_points_add=25, egos = 3, + force_egos = {"of projection", "thought-forged"}, + force_themes = {'physical', 'mental'}, + ego_special = function(e) -- reject egos (charms) that could overwrite the ability of the projection ego + for i, r in ipairs(e) do + if r.__resolver == "charm" then return false end + end + return true + end}) + end, +} diff --git a/game/modules/tome/data/gfx/particles/bolt_paradox.lua b/game/modules/tome/data/gfx/particles/bolt_paradox.lua new file mode 100644 index 0000000000000000000000000000000000000000..ff1885d3623c24934478c30a1687341196cd5bb3 --- /dev/null +++ b/game/modules/tome/data/gfx/particles/bolt_paradox.lua @@ -0,0 +1,51 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +return { generator = function() + local radius = 0 + local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local ad = rng.float(0, 360) + local a = math.rad(ad) + local r = rng.float(0, sradius / 6) + local x = r * math.cos(a) + local y = r * math.sin(a) + local bx = math.floor(x / engine.Map.tile_w) + local by = math.floor(y / engine.Map.tile_h) + local static = rng.percent(40) + + return { + trail = 1, + life = 6, + size = 3, sizev = 0, sizea = 0, + + x = x, xv = 0, xa = 0, + y = y, yv = 0, ya = 0, + dir = a, dirv = 0, dira = 0, + vel = sradius / 2 / 6, velv = 0, vela = 0, + + r = rng.range(250, 255)/255, rv = 0, ra = 0, + g = rng.range(120, 128)/255, gv = 0, ga = 0, + b = rng.range(114, 147)/255, bv = 0, ba = 0, + a = rng.range(80, 220)/255, av = 0, aa = 0, + } +end, }, +function(self) + self.ps:emit(30) +end, +30*6 \ No newline at end of file diff --git a/game/modules/tome/data/gfx/particles/farportal_vortex.lua b/game/modules/tome/data/gfx/particles/farportal_vortex.lua index c7c637e32b47e6c95a8cee2a6a0fa2ed265dcb82..88343d45706e9143f713437ae5e19fccffeb2599 100644 --- a/game/modules/tome/data/gfx/particles/farportal_vortex.lua +++ b/game/modules/tome/data/gfx/particles/farportal_vortex.lua @@ -18,22 +18,26 @@ -- darkgod@te4.org base_size = 64 +size = size or 256 return { - system_rotation = 0, system_rotationv = 0.5, + system_rotation = 0, system_rotationv = rot or 0.5, generator = function() + + return { + life = 100, + size = size, sizev = 0, sizea = 0, - base = 1000, - - angle = { 0, 0 }, anglev = { 0, 0 }, anglea = { 0, 0 }, - - life = { 100, 100 }, - size = { 256, 256 }, sizev = {0, 0}, sizea = {0, 0}, - - r = {255, 255}, rv = {0, 0}, ra = {0, 0}, - g = {255, 255}, gv = {0, 0}, ga = {0, 0}, - b = {255, 255}, bv = {0, 0}, ba = {0, 0}, - a = {255, 255}, av = {0, 0}, aa = {0, 0}, + x = x or 0, xv = 0, xa = 0, + y = y or 0, yv = 0, ya = 0, + dir = 0, dirv = 0, dira = 0, + vel = 0, velv = 0, vela = 0, + r = 1, rv = 0, ra = 0, + g = 1, gv = 0, ga = 0, + b = 1, bv = 0, ba = 0, + a = 1, av = 0, aa = 0, + } +end }, function(self) self.ps:emit(1) end, 1, vortex or "shockbolt/terrain/farportal-blue-vortex", true diff --git a/game/modules/tome/data/gfx/particles/gravity_well2.lua b/game/modules/tome/data/gfx/particles/gravity_well2.lua index 5269b709db98c1458dfd4a4426c73cab264057a5..78d478369162b97f74ecbedd875887b43b0b883c 100644 --- a/game/modules/tome/data/gfx/particles/gravity_well2.lua +++ b/game/modules/tome/data/gfx/particles/gravity_well2.lua @@ -17,7 +17,7 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org -use_shader = {type="distort", power=0.01, power_time=1000, blacken=0, power_amp=0.3} alterscreen = true +use_shader = {type="distort", power=0.01, power_time=1000 * (speed_factor or 1), blacken=0, power_amp=0.3} alterscreen = true base_size = 64 return { @@ -26,7 +26,7 @@ return { return { trail = 0, life = 32, - size = 70, sizev = 0, sizea = 0, + size = 70 * (size_factor or 1), sizev = 0, sizea = 0, x = 0, xv = 0, xa = 0, y = 0, yv = 0, ya = 0, diff --git a/game/modules/tome/data/gfx/particles/shader_ring_rotating.lua b/game/modules/tome/data/gfx/particles/shader_ring_rotating.lua index 6e575cd880536f3ee359c74c1f7b05687061f60b..8daaff5a0c292fa0f53ef94056f2f82eeef169d3 100644 --- a/game/modules/tome/data/gfx/particles/shader_ring_rotating.lua +++ b/game/modules/tome/data/gfx/particles/shader_ring_rotating.lua @@ -20,6 +20,11 @@ base_size = 32 local nb = 0 +x = x or 0 +y = y or 0 +life = life or nil + +local first = true return { blend_mode=additive and core.particles.BLEND_SHINY or nil, @@ -29,11 +34,11 @@ return { return { trail = 0, - life = 100, + life = life or 100, size = fs, sizev = 0, sizea = 0, - x = 0, xv = 0, xa = 0, - y = 0, yv = 0, ya = 0, + x = x * base_size, xv = 0, xa = 0, + y = y * base_size, yv = 0, ya = 0, dir = 0, dirv = dirv, dira = 0, vel = 0, velv = 0, vela = 0, @@ -44,6 +49,7 @@ return { } end, }, function(self) - self.ps:emit(1) + if first or not life then self.ps:emit(1) end + first = false end, 1, "particles_images/"..(img or "square"), true diff --git a/game/modules/tome/data/gfx/particles/temporal_teleport.lua b/game/modules/tome/data/gfx/particles/temporal_teleport.lua index 5de134fdd73c3f63a8555107f29fe0cfa5c8fb44..65395be791aa029da9722f883c091ed388431274 100644 --- a/game/modules/tome/data/gfx/particles/temporal_teleport.lua +++ b/game/modules/tome/data/gfx/particles/temporal_teleport.lua @@ -27,9 +27,9 @@ return { life = { 20, 30 }, size = { 3, 7 }, sizev = {0, 0}, sizea = {0, 0}, - r = {70, 250}, rv = {0, 10}, ra = {0, 0}, - g = {130, 250}, gv = {0, 0}, ga = {0, 0}, - b = {180, 0}, bv = {0, 10}, ba = {0, 0}, + r = {0, 176}, rv = {0, 10}, ra = {0, 0}, + g = {196, 255}, gv = {0, 0}, ga = {0, 0}, + b = {222, 255}, bv = {0, 10}, ba = {0, 0}, a = {25, 255}, av = {0, 0}, aa = {0, 0}, }, function(self) diff --git a/game/modules/tome/data/gfx/particles/trail_paradox.lua b/game/modules/tome/data/gfx/particles/trail_paradox.lua new file mode 100644 index 0000000000000000000000000000000000000000..c443c258465b5df2368d18b14fd7a96a0834d4a9 --- /dev/null +++ b/game/modules/tome/data/gfx/particles/trail_paradox.lua @@ -0,0 +1,56 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +local nb = 0 + +return { generator = function() + local radius = 0 + local sradius = (radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2 + local ad = rng.float(0, 360) + local a = math.rad(ad) + local r = rng.float(0.1, sradius / 2) + local x = r * math.cos(a) + local y = r * math.sin(a) + local bx = math.floor(x / engine.Map.tile_w) + local by = math.floor(y / engine.Map.tile_h) + local static = rng.percent(40) + + return { + trail = 1, + life = 3 + 9 * (sradius - r) / sradius, + size = 3, sizev = 0, sizea = 0, + + x = x, xv = 0, xa = 0, + y = y, yv = 0, ya = 0, + dir = 0, dirv = 0, dira = 0, + vel = 0, velv = 0, vela = 0, + + r = rng.range(250, 255)/255, rv = 0, ra = 0, + g = rng.range(120, 128)/255, gv = 0.005, ga = 0.0005, + b = rng.range(114, 147)/255, bv = 0, ba = 0, + a = rng.range(80, 220)/255, av = static and -0.034 or 0, aa = 0.005, + } +end, }, +function(self) + if nb < 1 then + self.ps:emit(30) + end + nb = nb + 1 +end, +30 diff --git a/game/modules/tome/data/gfx/particles_images/alchie_acid.png b/game/modules/tome/data/gfx/particles_images/alchie_acid.png new file mode 100644 index 0000000000000000000000000000000000000000..003277acbd53fe8330a5e32476da6f8d41bb329c Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/alchie_acid.png differ diff --git a/game/modules/tome/data/gfx/particles_images/alchie_lightning.png b/game/modules/tome/data/gfx/particles_images/alchie_lightning.png new file mode 100644 index 0000000000000000000000000000000000000000..0141abffd171572dc8b3612beed195a895a59973 Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/alchie_lightning.png differ diff --git a/game/modules/tome/data/gfx/particles_images/bone_shield.png b/game/modules/tome/data/gfx/particles_images/bone_shield.png index d682483c560816eac44109a7dfd7543b195889c6..aba04fcc319627601ce81df21ba97c2697ea3930 100644 Binary files a/game/modules/tome/data/gfx/particles_images/bone_shield.png and b/game/modules/tome/data/gfx/particles_images/bone_shield.png differ diff --git a/game/modules/tome/data/gfx/particles_images/corona_02.png b/game/modules/tome/data/gfx/particles_images/corona_02.png new file mode 100644 index 0000000000000000000000000000000000000000..db7d143ca9e7741869a2141fb919574e9ebcba40 Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/corona_02.png differ diff --git a/game/modules/tome/data/gfx/particles_images/corona_03.png b/game/modules/tome/data/gfx/particles_images/corona_03.png new file mode 100644 index 0000000000000000000000000000000000000000..28c1606f4d2fd394e99b7c5ebba056ccd6324994 Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/corona_03.png differ diff --git a/game/modules/tome/data/gfx/particles_images/corona_04.png b/game/modules/tome/data/gfx/particles_images/corona_04.png new file mode 100644 index 0000000000000000000000000000000000000000..3898afb4de8d491848123614aab368a7d4697216 Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/corona_04.png differ diff --git a/game/modules/tome/data/gfx/particles_images/darkest_light.png b/game/modules/tome/data/gfx/particles_images/darkest_light.png new file mode 100644 index 0000000000000000000000000000000000000000..026cfc4a0cdb345dee4ce411c487f8f11d2b309e Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/darkest_light.png differ diff --git a/game/modules/tome/data/gfx/particles_images/healred.png b/game/modules/tome/data/gfx/particles_images/healred.png new file mode 100644 index 0000000000000000000000000000000000000000..4289a4170310f2a24e9083959b8f434990ad268e Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/healred.png differ diff --git a/game/modules/tome/data/gfx/particles_images/spatial_fragment.png b/game/modules/tome/data/gfx/particles_images/spatial_fragment.png new file mode 100644 index 0000000000000000000000000000000000000000..a666db2d2dd99c08b079780a253eb9be573cd9a8 Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/spatial_fragment.png differ diff --git a/game/modules/tome/data/gfx/particles_images/temporal_bolt.png b/game/modules/tome/data/gfx/particles_images/temporal_bolt.png new file mode 100644 index 0000000000000000000000000000000000000000..0ffab2efe3eab1d2ee2ebe373a28a36ec5a54d66 Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/temporal_bolt.png differ diff --git a/game/modules/tome/data/gfx/shaders/awesomeaura.frag b/game/modules/tome/data/gfx/shaders/awesomeaura.frag index 82a42679b2d02a0e775ff08bd292389d81c7ce2d..c55d6bfddedaa3cf3d06a43d356176d6ede7c80d 100644 --- a/game/modules/tome/data/gfx/shaders/awesomeaura.frag +++ b/game/modules/tome/data/gfx/shaders/awesomeaura.frag @@ -144,7 +144,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/boneshield.frag b/game/modules/tome/data/gfx/shaders/boneshield.frag index 1b4b04653e9da131127175ba8ee142ce75faba19..13c1c8b13df3015de6ff219c7fe5c945fa94a35c 100644 --- a/game/modules/tome/data/gfx/shaders/boneshield.frag +++ b/game/modules/tome/data/gfx/shaders/boneshield.frag @@ -14,7 +14,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/firearcs.frag b/game/modules/tome/data/gfx/shaders/firearcs.frag new file mode 100644 index 0000000000000000000000000000000000000000..6038cbc8760f2d3b0e67f8c1df3316218a2e9950 --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/firearcs.frag @@ -0,0 +1,547 @@ +uniform sampler2D tex; +uniform float tick; +uniform float tick_start; +uniform float timeScale; + +uniform float isPermanent; +uniform float side; + +float shrinkPhase; + +float snoise( vec3 v ); +float rand(int offset) +{ + return snoise(vec3(float(offset) * 0.5 + 0.7, 0.0, 0.0)) * 0.5 + 0.5; +} + +vec4 Uberblend(vec4 col0, vec4 col1); + +struct Ray +{ + vec3 point; + vec3 dir; +}; +struct Plane +{ + vec3 point; + vec3 normal; +}; +vec3 GetRayPlaneIntersection(Ray ray, Plane plane) +{ + float mult = 1.0 / dot(ray.dir, plane.normal); + return ray.point + ray.dir * (dot(plane.point, plane.normal) - dot(ray.point, plane.normal)) * mult; +} + +struct Intersection +{ + vec4 color; + float depth; +}; + +vec4 GetCheckboardColor(vec2 pos); + +struct FireSettings +{ + float freqMult; + float stretchMult; + float ampMult; + vec2 scrollSpeed; //3.0 + float evolutionSpeed; //0.1 +}; + +vec4 GetFireColor(float currTime, vec2 pos, float palettePos, FireSettings settings); + + +float GetHeight(vec2 pos) +{ + /*float len = dot(pos, pos); + return 0.3-len * 3.0;*/ + float len = length(pos) * 15.0; + return (len * len / 15.0 - len * len * len * len / 650.0) / 15.0 + pos.y * 0.1; + /*float len = length(pos); + if(len > 0.2) + { + len -= 0.2; + return 0.05 + -(len * len * len * len) * 15.0 + pos.y * 0.1; + }else + { + float ratio = len / 0.2; + return 0.05 - (ratio - 1.0) * (ratio - 1.0) * 0.2 + pos.y * 0.1; + }*/ +} + + + +Intersection GetVortexIntersection(float currTime, Ray eyeRay) +{ + vec3 point = vec3(0.05, 0.0, -0.25); + float startMin = -1.1; + float startMax = 2.0; + float minLen = startMin; + float maxLen = startMax; + float phase = 0.5 + clamp(currTime * 1.0, 0.0, 0.5); + for(int i = 0; i < 15; i++) + { + /*g(x) = 0; + f(x) = g(x) + x; + x = f(x) -> g(x) = 0;*/ + float midLen = (minLen + maxLen) / 2.0; + vec3 rayPoint = eyeRay.point + eyeRay.dir * midLen - point; + vec3 surfacePoint = vec3(rayPoint.x, GetHeight(rayPoint.xz), rayPoint.z); + if(rayPoint.y > surfacePoint.y) + { + /*Intersection res; + res.depth = 0.0; + res.color = GetCheckboardColor((eyeRay.point + eyeRay.dir * res.depth).xz); + return res;*/ + + minLen = midLen; + } + else + { + /*Intersection res; + res.depth = 10.0; + res.color = GetCheckboardColor((eyeRay.point + eyeRay.dir * res.depth).xz); + return res;*/ + maxLen = midLen; + } + } + Intersection res; + res.depth = (minLen + maxLen) / 2.0; + vec3 surfacePoint = (eyeRay.point + eyeRay.dir * res.depth - point); + if(surfacePoint.z > 0.0 && side == 1.0 || surfacePoint.z < 0.0 && side == 2.0) + { + res.depth = 1e5; + res.color = vec4(0.0, 0.0, 0.0, 0.0); + return res; + } + if(abs(res.depth - startMin) < 1e-2 || abs(res.depth - startMax) < 1e-2) + { + res.depth = 1e5; + res.color = vec4(0.0, 0.0, 0.0, 0.0); + }else + { + float pi = 3.1415; + vec2 polarPoint = vec2(atan(surfacePoint.z, surfacePoint.x) / (2.0 * pi) + 0.5, length(surfacePoint.xz) * 1.6); + polarPoint.y /= phase; + float shrinkMult = 1.0 - pow(clamp(1.0-shrinkPhase, 0.01, 0.99), 10.0); + //polarPoint.y /= shrinkMult * 0.3 + 0.7; + + polarPoint.y = (polarPoint.y - 0.45) / 0.55; + res.color = GetCheckboardColor(polarPoint); + + FireSettings fireSettings; + fireSettings.freqMult = 10.0; + fireSettings.stretchMult = 7.0; + fireSettings.ampMult = 0.4; + fireSettings.scrollSpeed = vec2(5.0*0.0, 1.0); + fireSettings.evolutionSpeed = 1.0; + polarPoint.y = (polarPoint.y + 0.0) / 1.0; + if(polarPoint.y < 0.02) polarPoint.y = 0.02; + polarPoint.y = 1.0 - polarPoint.y; + res.color = GetFireColor(currTime, polarPoint, 0.25, fireSettings); + res.color.a *= 1.0 - pow(clamp(1.0-shrinkPhase, 0.0, 1.0), 10.0); +// res.color = GetCheckboardColor(polarPoint); + } + return res; +} + + +struct ArcSettings +{ + float radius; + float width; + float length; + float growthTime; + Plane plane; + vec4 color; +}; + +Intersection GetFireArcIntersection(float currTime, Ray ray, ArcSettings arcSettings) +{ + Intersection res; + res.depth = 1e5; + res.color = vec4(0.0, 0.0, 0.0, 0.0); + vec3 xVector = normalize(cross(arcSettings.plane.normal, vec3(0.0, 1.0, 0.0))); + vec3 zVector = arcSettings.plane.normal; + vec3 yVector = cross(xVector, zVector); + + vec3 intersection = GetRayPlaneIntersection(ray, arcSettings.plane); + if(intersection.y < 0.0) return res; + if(intersection.z > 0.0 && side == 1.0 || intersection.z < 0.0 && side == 2.0) + return res; + + vec2 planarIntersection = vec2(dot(xVector, (intersection - arcSettings.plane.point)), dot(yVector, (intersection - arcSettings.plane.point))); + float radiusLen = length(planarIntersection); + + float pi = 3.1415; + vec2 polarPos = vec2(atan(planarIntersection.x, planarIntersection.y), radiusLen); + + //return GetCheckboardColor(polarPos); + + vec2 planarPos; + float cosAlpha = dot(arcSettings.plane.normal, vec3(0.0, 1.0, 0.0)); + float height = arcSettings.plane.point.y / sqrt(1.0 - cosAlpha * cosAlpha); + + planarPos.x = (polarPos.x + pi) / (2.0 * pi); + planarPos.y = (polarPos.y - (arcSettings.radius - arcSettings.width)) / arcSettings.width; + float sideRatio = arcSettings.length * arcSettings.radius / arcSettings.width; + + + float yScale = 1.0 - planarPos.x / arcSettings.length; + +// planarPos.y = 1.0 - (1.0 - planarPos.y) / (1.0 - pow(clamp(1.0 - yScale, 0.0, 1.0), 2.0)); + if(yScale < 0.0) + return res; + if(planarPos.y > 0.5) + planarPos.y = 0.5 + (planarPos.y - 0.5) / yScale; + else + planarPos.y = 0.5 - (0.5 - planarPos.y) / yScale; + + FireSettings fireSettings; + fireSettings.freqMult = 10.0 * sideRatio * arcSettings.width * 3.1; + fireSettings.stretchMult = 18.0 * sideRatio; + fireSettings.ampMult = 0.5; + fireSettings.scrollSpeed = vec2(5.0*0.0, 3.0); + fireSettings.evolutionSpeed = 1.5; + + float ratio1 = 0.3; + float ratio2 = 0.4; + + + float timeOffset = 0.0; + res.depth = dot((intersection - ray.point), ray.dir); + if(planarPos.y > ratio2) + { + planarPos.y -= ratio2; + planarPos.y /= 1.0 - ratio2; + timeOffset = 10.0; + }else + if(planarPos.y < ratio1) + { + planarPos.y = ratio1 - planarPos.y; + planarPos.y /= ratio1; + timeOffset = 0.0; + }else + if(planarPos.y > ratio1 && planarPos.y < ratio2 && planarPos.x > 0.0 && planarPos.x < 1.0) + { + res.color = texture2D(tex, vec2(0.25, 0.99)); + return res; + } + //return vec4(0.0, 0.0, 1.0, 1.0); + + planarPos.x -= planarPos.y * 0.02; + + res.color = GetFireColor(currTime + timeOffset, vec2(planarPos.x, 1.0 - planarPos.y), 0.25, fireSettings); + //res.color = GetCheckboardColor(planarPos); + return res; +} + +#define arcsCount 8 +void main(void) +{ + vec2 pos = gl_TexCoord[0].xy; + pos.y = 1.0 - pos.y; + + float currTime = max(0.0, tick - tick_start) / 1000.0 * timeScale; + shrinkPhase = 1.0; + if(isPermanent < 0.5) + shrinkPhase = clamp(1.5 - currTime * 1.0, 0.0, 1.0); + + + /*FireSettings fireSettings; + fireSettings.freqMult = 4.0; + fireSettings.stretchMult = 2.0; + fireSettings.ampMult = 0.4; + fireSettings.scrollSpeed = vec2(5.0*0, 0.0); + fireSettings.scrollSpeed.y = 0.0; + fireSettings.evolutionSpeed = 1.0; + gl_FragColor = GetFireWallColor(currTime, pos * 2.0 - 0.5, 4.0, 0.6, 0.25, 0.2, fireSettings); + return;*/ + + if(shrinkPhase < 1e-3) + { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + return; + } + float pi = 3.1415; + vec3 groundAxis = normalize(vec3(0.5, -0.6, -0.9)); + vec3 groundPos = vec3(0.0, 0.0, 0.0); + + float defRadius = 0.25; + + Ray eyeRay; + eyeRay.point.xy = pos - vec2(0.5, 0.5) + vec2(0.0, 0.25); //0.5 + eyeRay.point.z = -2.0; + eyeRay.dir = normalize(vec3(0.0, -0.0, 0.8)); + Intersection sphereIntersection; + sphereIntersection.depth = 1e5; + sphereIntersection.color = vec4(0.0, 0.0, 0.0, 0.0); + Intersection vortexIntersection = GetVortexIntersection(currTime * 0.5, eyeRay); + + ArcSettings arcs[arcsCount]; + arcs[0].plane.point = vec3(0.27, 0.20, 0.0); + arcs[0].plane.normal = normalize(vec3(0.3, -0.3, 0.5)); + arcs[0].radius = 1.5 * defRadius; + arcs[0].width = 1.3 * defRadius; + arcs[0].length = 0.65; + arcs[0].growthTime = 0.3; + arcs[0].color = vec4(0.6, 0.6, 0.6, 1.0); + + arcs[1].plane.point = vec3(0.13, 0.20, 0.0); + arcs[1].plane.normal = normalize(vec3(0.45, -0.3, 0.5)); + arcs[1].radius = 1.4 * defRadius; + arcs[1].width = 0.5 * defRadius; + arcs[1].length = 0.6; + arcs[1].growthTime = 0.4; + arcs[1].color = vec4(0.6, 0.6, 0.8, 1.0); + + arcs[7].plane.point = vec3(0.05, 0.20, 0.0); + arcs[7].plane.normal = normalize(vec3(0.6, -0.3, 0.5)); + arcs[7].radius = 2.7 * defRadius; + arcs[7].width = 2.8 * defRadius; + arcs[7].length = 0.6; + arcs[7].growthTime = 0.0; + arcs[7].color = vec4(0.6, 0.6, 0.8, 1.0); + + //////// + arcs[2].plane.point = vec3(-0.2, 0.1, 0.0); + arcs[2].plane.normal = normalize(vec3(0.12, 0.3, -0.11)); + arcs[2].radius = 1.8 * defRadius; + arcs[2].width = 1.3 * defRadius; + arcs[2].length = 0.7; + arcs[2].growthTime = 0.4; + arcs[2].color = vec4(0.6, 0.6, 0.8, 1.0); + + arcs[3].plane.point = vec3(-0.2, 0.13, 0.0); + arcs[3].plane.normal = normalize(vec3(0.2, 0.3, -0.13)); + arcs[3].radius = 2.1 * defRadius; + arcs[3].width = 2.2 * defRadius; + arcs[3].length = 0.7; + arcs[3].growthTime = 0.7; + arcs[3].color = vec4(0.6, 0.6, 0.8, 1.0); + + arcs[4].plane.point = vec3(-0.2, 0.16, 0.0); + arcs[4].plane.normal = normalize(vec3(0.23, 0.3, -0.11)); + arcs[4].radius = 1.6 * defRadius; + arcs[4].width = 0.8 * defRadius; + arcs[4].length = 0.7; + arcs[4].growthTime = 0.6; + arcs[4].color = vec4(0.6, 0.6, 0.8, 1.0); + + arcs[5].plane.point = vec3(-0.0, 0.17, 0.0); + arcs[5].plane.normal = normalize(vec3(0.25, 0.3, -0.15)); + arcs[5].radius = 2.9 * defRadius; + arcs[5].width = 3.0 * defRadius; + arcs[5].length = 0.6; + arcs[5].growthTime = 0.0; + arcs[5].color = vec4(0.6, 0.6, 0.8, 1.0); + +// arcs[6].plane.point = vec3(-0.1, 0.25, 0.0); + arcs[6].plane.point = vec3(-0.1, 0.25, -0.35); + arcs[6].plane.normal = normalize(vec3(0.35, 0.3, -0.13)); + arcs[6].radius = 1.4 * defRadius; + arcs[6].width = 0.5 * defRadius; + arcs[6].length = 0.6; + arcs[6].growthTime = 0.6; + arcs[6].color = vec4(0.6, 0.6, 0.8, 1.0); + + vec4 resultColor = sphereIntersection.color; + float resultDepth = sphereIntersection.depth; + if(vortexIntersection.depth < sphereIntersection.depth) + { + resultColor = Uberblend(resultColor, vortexIntersection.color); + resultDepth = vortexIntersection.depth; + } + float growthTimeMult = 0.7; + shrinkPhase = 1.0 - pow(1.0 - shrinkPhase, 3.0); + for(int arcIndex = 0; arcIndex < arcsCount; arcIndex++) + { + float phase = clamp((currTime - arcs[arcIndex].growthTime * growthTimeMult) * 1.0, 0.0, 1.0); + float lengthMult = 1.0 - pow(1.0 - phase, 10.0); + arcs[arcIndex].length *= lengthMult; + arcs[arcIndex].radius *= 0.7 + shrinkPhase * 0.3; + arcs[arcIndex].width *= shrinkPhase; + + Intersection arcIntersection= GetFireArcIntersection( + currTime * 0.5 + float(arcIndex) * 3.7, + eyeRay, + arcs[arcIndex])/* * arcs[arcIndex].color*/; + arcIntersection.color.a *= 1.0 - pow(1.0 - shrinkPhase, 5.0); + if(arcIntersection.depth < resultDepth && arcIntersection.color.a > 0.5) + { + resultDepth = arcIntersection.depth; + resultColor = Uberblend(resultColor, arcIntersection.color); + } + } + + //resultColor.a *= 0.3; + gl_FragColor = resultColor; + //gl_FragColor = sphereIntersection.color; + /*if(abs(vortexIntersection.depth - 2.0) < 1e-2) + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + else + gl_FragColor = vec4((vortexIntersection.depth - 1.0) * 2.0, 0.0, 0.0, 1.0);*/ + + //vec4 color = GetFireColor(tick / 5000.0, pos, 0.25, settings); + //vec3 shininessColor = color.rgb * color.a * 0.1; + //gl_FragColor = vec4(color.rgb * color.a + shininessColor, color.a); +} + + +vec4 permute( vec4 x ) { + + return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 ); + +} + +vec4 taylorInvSqrt( vec4 r ) { + + return 1.79284291400159 - 0.85373472095314 * r; + +} + +float snoise( vec3 v ) { + + const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 ); + const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 ); + + // First corner + + vec3 i = floor( v + dot( v, C.yyy ) ); + vec3 x0 = v - i + dot( i, C.xxx ); + + // Other corners + + vec3 g = step( x0.yzx, x0.xyz ); + vec3 l = 1.0 - g; + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + + vec3 x1 = x0 - i1 + 1.0 * C.xxx; + vec3 x2 = x0 - i2 + 2.0 * C.xxx; + vec3 x3 = x0 - 1. + 3.0 * C.xxx; + + // Permutations + + i = mod( i, 289.0 ); + vec4 p = permute( permute( permute( + i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) ) + + i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) ) + + i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) ); + + // Gradients + // ( N*N points uniformly over a square, mapped onto an octahedron.) + + float n_ = 1.0 / 7.0; // N=7 + + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N) + + vec4 x_ = floor( j * ns.z ); + vec4 y_ = floor( j - 7.0 * x_ ); // mod(j,N) + + vec4 x = x_ *ns.x + ns.yyyy; + vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs( x ) - abs( y ); + + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); + + + vec4 s0 = floor( b0 ) * 2.0 + 1.0; + vec4 s1 = floor( b1 ) * 2.0 + 1.0; + vec4 sh = -step( h, vec4( 0.0 ) ); + + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + vec3 p0 = vec3( a0.xy, h.x ); + vec3 p1 = vec3( a0.zw, h.y ); + vec3 p2 = vec3( a1.xy, h.z ); + vec3 p3 = vec3( a1.zw, h.w ); + + // Normalise gradients + + vec4 norm = taylorInvSqrt( vec4( dot( p0, p0 ), dot( p1, p1 ), dot( p2, p2 ), dot( p3, p3 ) ) ); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + + vec4 m = max( 0.6 - vec4( dot( x0, x0 ), dot( x1, x1 ), dot( x2, x2 ), dot( x3, x3 ) ), 0.0 ); + m = m * m; + return 42.0 * dot( m*m, vec4( dot( p0, x0 ), dot( p1, x1 ), + dot( p2, x2 ), dot( p3, x3 ) ) ); + +} + +float GetFireDelta(float currTime, vec2 pos, FireSettings settings) +{ + //firewall + float delta = 0.0; +// pos.y += (1.0 - pos.y) * 0.5; + //pos.y += 0.5; + pos.y /= settings.stretchMult; + pos *= settings.freqMult; + pos += currTime * settings.scrollSpeed; + +// pos.y -= currTime * 3.0; + delta += snoise(vec3(pos * 1.0, currTime * 1.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 2.0, currTime * 2.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 4.0, currTime * 4.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 8.0, currTime * 8.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 16.0, currTime * 16.0 * settings.evolutionSpeed)) * 0.5; + + return delta; +} + +vec4 GetFireColor(float currTime, vec2 pos, float palettePos, FireSettings settings) +{ + vec4 fireColor = vec4(0.0, 0.0, 0.0, 0.0); + if(pos.x > 0.0 && pos.x < 1.0 && pos.y > 0.0 && pos.y < 1.0) + { + /*if(mod(pos.x, 0.1) < 0.05 ^ mod(pos.y, 0.1) < 0.05) + return vec4(pos.x, pos.y, 0, 1); + else + return vec4(0, 0, 0, 1);*/ + float delta = GetFireDelta(currTime, pos, settings); + delta *= min(1.0, max(0.0, 1.0 * (1.0 - pos.y))); + delta *= min(1.0, max(0.0, 1.0 * (0.0 + pos.y))); + vec2 displacedPoint = vec2(palettePos, pos.y) + vec2(0, delta * settings.ampMult); + displacedPoint.y = min(0.99, displacedPoint.y); + displacedPoint.y = max(0.01, displacedPoint.y); + + fireColor = texture2D(tex, displacedPoint); + } + return fireColor; +} + +vec4 Uberblend(vec4 col0, vec4 col1) +{ +// return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); +// return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); + return vec4( + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), + min(1.0, col0.a + col1.a)); +} + + +vec4 GetCheckboardColor(vec2 pos) +{ + vec4 col = vec4(0.0, 0.0, 0.0, 0.0); + if(pos.x > 0.0 && pos.x < 1.0 && pos.y > 0.0 && pos.y < 1.0) + { + if(mod(pos.x, 0.1) < 0.05 ^^ mod(pos.y, 0.1) < 0.05) + col = vec4(1.0, 1.0, 1.0, 1.0); + else + col = vec4(0.5, 0.5, 0.5, 1.0); + } + return col; +} \ No newline at end of file diff --git a/game/modules/tome/data/gfx/shaders/firearcs.lua b/game/modules/tome/data/gfx/shaders/firearcs.lua new file mode 100644 index 0000000000000000000000000000000000000000..3bb6ac41f6c95dfdbc49279bdbf8bdee61726547 --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/firearcs.lua @@ -0,0 +1,33 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +return { + frag = "firearcs", + vert = nil, + args = { + tex = { texture = 0 }, + ellipsoidalFactor = ellipsoidalFactor or {1,1}, --1 is perfect circle, >1 is ellipsoidal + timeScale = timeScale or 2.0, + isPermanent = isPermanent or 0.0, + }, + resetargs = { + tick_start = function() return core.game.getFrameTime() end, + }, + clone = false, +} diff --git a/game/modules/tome/data/gfx/shaders/fireball.frag b/game/modules/tome/data/gfx/shaders/fireball.frag index 8e87760921ffc58fa7684fd2c013312c3e491b1c..0309d0a1dd0ac30119820f527d1dfd9dd6e97d32 100644 --- a/game/modules/tome/data/gfx/shaders/fireball.frag +++ b/game/modules/tome/data/gfx/shaders/fireball.frag @@ -15,7 +15,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/firesurge.frag b/game/modules/tome/data/gfx/shaders/firesurge.frag new file mode 100644 index 0000000000000000000000000000000000000000..fba7d12abb8a38a9afc95098c8c1f8d24782ca72 --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/firesurge.frag @@ -0,0 +1,391 @@ +uniform sampler2D tex; +uniform float tick; +uniform float tick_start; +uniform float timeScale; + +uniform float isPermanent; +uniform float coreRadiusMult; + +float shrinkPhase; + +float snoise( vec3 v ); +float rand(int offset) +{ + return snoise(vec3(float(offset) * 0.5 + 0.7, 0.0, 0.0)) * 0.5 + 0.5; +} + +vec4 Uberblend(vec4 col0, vec4 col1); + +struct Ray +{ + vec3 point; + vec3 dir; +}; +struct Plane +{ + vec3 point; + vec3 normal; +}; +vec3 GetRayPlaneIntersection(Ray ray, Plane plane) +{ + float mult = 1.0 / dot(ray.dir, plane.normal); + return ray.point + ray.dir * (dot(plane.point, plane.normal) - dot(ray.point, plane.normal)) * mult; +} + +struct Intersection +{ + vec4 color; + float depth; +}; + +vec4 GetCheckboardColor(vec2 pos); + +struct FireSettings +{ + float freqMult; + float stretchMult; + float ampMult; + vec2 scrollSpeed; //3.0 + float evolutionSpeed; //0.1 +}; + +vec4 GetFireColor(float currTime, vec2 pos, float palettePos, FireSettings settings); + +vec4 GetFireWallColor(float currTime, vec2 pos, int layersCount, float layerWidth, float palettePos, float scrollSpeed, FireSettings fireSettings) +{ + float startPos = currTime * scrollSpeed; + float layerDist = 1.0 / float(layersCount); + float flameSkip = (floor((1.0 + layerWidth) / (layerDist)) + 1.0) * layerDist; + + vec4 resultColor = vec4(0.0, 0.0, 0.0, 0.0); + int actualLayersCount = int(flameSkip / layerDist); + + if(pos.y < startPos - layerWidth) + resultColor = Uberblend(texture2D(tex, vec2(palettePos, 0.99)), resultColor); + + for(int layerNumber = 0; layerNumber < actualLayersCount; layerNumber++) + { + int topIndex = int(floor(max(0.0, (startPos - flameSkip) / layerDist + 1.0))); + int layerIndex = int(mod(float(layerNumber + topIndex), float(actualLayersCount))); + float flamePos = startPos - float(layerIndex) * layerDist; +// flamePos = fmod(flamePos + layerDist, 1.0 + layerDist) - layerDist; + if(flamePos > 0.0) + flamePos = mod(flamePos, flameSkip); + vec2 resultPos = vec2(pos.x, 1.0 - (flamePos - pos.y) / layerWidth); + resultPos.y = 1.0 - resultPos.y; + vec4 flameColor = GetFireColor(currTime + float(layerIndex) * 10.0, resultPos, 0.25, fireSettings); + resultColor = Uberblend(resultColor, flameColor); +// resultColor = Uberblend(resultColor, GetCheckboardColor(resultPos) * (0.2)); + } + + return resultColor; +} + + +Intersection GetSphereIntersection(float currTime, Ray eyeRay) +{ + vec3 point = vec3(0.0, 0.2, 0.0); + float radius = 0.17 * coreRadiusMult; + + radius = 0.45; + point = vec3(0.0, 0.0, 0.0); + + vec3 xVector = normalize(cross(eyeRay.dir, vec3(0.0, 1.0, 0.0))); + vec3 yVector = normalize(cross(eyeRay.dir, xVector)); + + float scrollSpeed = 2.0; + + vec2 localCoords = vec2(dot(xVector, eyeRay.point - point), dot(yVector, eyeRay.point - point)); + float sinAng = length(localCoords) / radius; + if(sinAng < 1.0) + { + float ang = asin(sinAng); + vec2 planarCoords = normalize(localCoords) * ang; + + Intersection res; + res.depth = dot(eyeRay.dir, point - eyeRay.point) - radius * cos(ang); + + FireSettings fireSettings; + fireSettings.freqMult = 4.0; + fireSettings.stretchMult = 2.0; + fireSettings.ampMult = 0.4; + fireSettings.scrollSpeed = vec2(5.0*0.0, 0.0); + fireSettings.evolutionSpeed = 1.5; +// planarCoords.y = 1.0 - planarCoords.y; + vec2 flamePos = planarCoords * 0.3 + vec2(0.5, 0.5); + flamePos.y = 1.0 - flamePos.y; + res.color = GetFireWallColor(currTime + 0.2, flamePos, 4, 0.6, 0.25, scrollSpeed, fireSettings); +// res.color = GetCheckboardColor(flamePos); + vec3 intersectionPoint = eyeRay.point + eyeRay.dir * res.depth; + res.color.rgb *= 1.0 + abs(intersectionPoint.z) * 1.5; + if(res.color.a < 0.5) res.depth = 1e5; + res.color.a *= 1.0 - pow(1.0 - shrinkPhase, 5.0); + + return res; + }else + { + float width = radius * 0.25; + float pi = 3.1415; + vec2 polarPoint = vec2((atan(localCoords.x, -localCoords.y)) / (2.0 * pi) + 0.5, (length(localCoords) - radius + width * 0.2) / width); + float bottomMult = min(polarPoint.x, 1.0 - polarPoint.x); + bottomMult = 1.0 - pow(1.0 - bottomMult, 10.0); + { + float ratio = clamp(currTime * scrollSpeed * 0.8 + 0.1, 0.0, 1.0); + if(polarPoint.x < 0.5) + { + polarPoint.x = 0.0 + (polarPoint.x) / ratio; + if(polarPoint.x > 0.5) + { + Intersection res; + res.depth = 1e5; + res.color = vec4(0.0, 0.0, 0.0, 0.0); + return res; + } + } + else + { + polarPoint.x = 1.0 + (polarPoint.x - 1.0) / ratio; + if(polarPoint.x < 0.5) + { + Intersection res; + res.depth = 1e5; + res.color = vec4(0.0, 0.0, 0.0, 0.0); + return res; + } + } + //polarPoint.y /= abs(polarPoint.x - 0.5) + ratio * ratio * ratio; + } + //polarPoint.x *= 2.1; + + float ratio = (1.0 - polarPoint.y) * 0.3; + polarPoint.x = polarPoint.x + (-polarPoint.x + 0.5) * ratio; + polarPoint.y /= bottomMult; + polarPoint.y = 1.0 - polarPoint.y; + + if(polarPoint.x > 0.0 && polarPoint.x < 1.0 && polarPoint.y > 0.0 && polarPoint.y < 1.0) + { + FireSettings fireSettings; + fireSettings.freqMult = 10.0; + fireSettings.stretchMult = 20.0; + fireSettings.ampMult = 0.4; + fireSettings.scrollSpeed = vec2(5.0*0.0, 1.0); + fireSettings.evolutionSpeed = 1.0; + + Intersection res; + res.color = GetFireColor(currTime, polarPoint, 0.25, fireSettings); + res.depth = -eyeRay.point.z; + if(res.color.a < 0.5) res.depth = 1e5; + res.color.a *= 1.0 - pow(1.0 - shrinkPhase, 5.0); + return res; + }else + { + Intersection res; + res.depth = 1e5; + res.color = vec4(0.0, 0.0, 0.0, 0.0); + return res; + } + } +} + +#define arcsCount 8 +void main(void) +{ + vec2 pos = gl_TexCoord[0].xy; + pos.y = 1.0 - pos.y; + + float currTime = max(0.0, tick - tick_start) / 1000.0 * timeScale; + shrinkPhase = 1.0; + if(isPermanent < 0.5) + shrinkPhase = clamp(1.5 - currTime * 1.0, 0.0, 1.0); + + + /*FireSettings fireSettings; + fireSettings.freqMult = 4.0; + fireSettings.stretchMult = 2.0; + fireSettings.ampMult = 0.4; + fireSettings.scrollSpeed = vec2(5.0*0, 0.0); + fireSettings.scrollSpeed.y = 0.0; + fireSettings.evolutionSpeed = 1.0; + gl_FragColor = GetFireWallColor(currTime, pos * 2.0 - 0.5, 4.0, 0.6, 0.25, 0.2, fireSettings); + return;*/ + + if(shrinkPhase < 1e-3) + { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + return; + } + float pi = 3.1415; + vec3 groundAxis = normalize(vec3(0.5, -0.6, -0.9)); + vec3 groundPos = vec3(0.0, 0.0, 0.0); + + float defRadius = 0.25; + + Ray eyeRay; + eyeRay.point.xy = pos - vec2(0.5, 0.5) + vec2(0.0, 0.25); //0.5 + eyeRay.point.z = -2.0; + eyeRay.dir = normalize(vec3(0.0, -0.0, 0.8)); + eyeRay.point.xy = pos - vec2(0.5, 0.5); + + Intersection sphereIntersection; + sphereIntersection = GetSphereIntersection(currTime * 0.5, eyeRay); + gl_FragColor = sphereIntersection.color; + return; +} + + +vec4 permute( vec4 x ) { + + return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 ); + +} + +vec4 taylorInvSqrt( vec4 r ) { + + return 1.79284291400159 - 0.85373472095314 * r; + +} + +float snoise( vec3 v ) { + + const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 ); + const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 ); + + // First corner + + vec3 i = floor( v + dot( v, C.yyy ) ); + vec3 x0 = v - i + dot( i, C.xxx ); + + // Other corners + + vec3 g = step( x0.yzx, x0.xyz ); + vec3 l = 1.0 - g; + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + + vec3 x1 = x0 - i1 + 1.0 * C.xxx; + vec3 x2 = x0 - i2 + 2.0 * C.xxx; + vec3 x3 = x0 - 1. + 3.0 * C.xxx; + + // Permutations + + i = mod( i, 289.0 ); + vec4 p = permute( permute( permute( + i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) ) + + i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) ) + + i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) ); + + // Gradients + // ( N*N points uniformly over a square, mapped onto an octahedron.) + + float n_ = 1.0 / 7.0; // N=7 + + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N) + + vec4 x_ = floor( j * ns.z ); + vec4 y_ = floor( j - 7.0 * x_ ); // mod(j,N) + + vec4 x = x_ *ns.x + ns.yyyy; + vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs( x ) - abs( y ); + + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); + + + vec4 s0 = floor( b0 ) * 2.0 + 1.0; + vec4 s1 = floor( b1 ) * 2.0 + 1.0; + vec4 sh = -step( h, vec4( 0.0 ) ); + + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + vec3 p0 = vec3( a0.xy, h.x ); + vec3 p1 = vec3( a0.zw, h.y ); + vec3 p2 = vec3( a1.xy, h.z ); + vec3 p3 = vec3( a1.zw, h.w ); + + // Normalise gradients + + vec4 norm = taylorInvSqrt( vec4( dot( p0, p0 ), dot( p1, p1 ), dot( p2, p2 ), dot( p3, p3 ) ) ); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + + vec4 m = max( 0.6 - vec4( dot( x0, x0 ), dot( x1, x1 ), dot( x2, x2 ), dot( x3, x3 ) ), 0.0 ); + m = m * m; + return 42.0 * dot( m*m, vec4( dot( p0, x0 ), dot( p1, x1 ), + dot( p2, x2 ), dot( p3, x3 ) ) ); + +} + +float GetFireDelta(float currTime, vec2 pos, FireSettings settings) +{ + //firewall + float delta = 0.0; +// pos.y += (1.0 - pos.y) * 0.5; + //pos.y += 0.5; + pos.y /= settings.stretchMult; + pos *= settings.freqMult; + pos += currTime * settings.scrollSpeed; + +// pos.y -= currTime * 3.0; + delta += snoise(vec3(pos * 1.0, currTime * 1.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 2.0, currTime * 2.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 4.0, currTime * 4.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 8.0, currTime * 8.0 * settings.evolutionSpeed)) * 1.5; + delta += snoise(vec3(pos * 16.0, currTime * 16.0 * settings.evolutionSpeed)) * 0.5; + + return delta; +} + +vec4 GetFireColor(float currTime, vec2 pos, float palettePos, FireSettings settings) +{ + vec4 fireColor = vec4(0.0, 0.0, 0.0, 0.0); + if(pos.x > 0.0 && pos.x < 1.0 && pos.y > 0.0 && pos.y < 1.0) + { + /*if(mod(pos.x, 0.1) < 0.05 ^ mod(pos.y, 0.1) < 0.05) + return vec4(pos.x, pos.y, 0, 1); + else + return vec4(0, 0, 0, 1);*/ + float delta = GetFireDelta(currTime, pos, settings); + delta *= min(1.0, max(0.0, 1.0 * (1.0 - pos.y))); + delta *= min(1.0, max(0.0, 1.0 * (0.0 + pos.y))); + vec2 displacedPoint = vec2(palettePos, pos.y) + vec2(0, delta * settings.ampMult); + displacedPoint.y = min(0.99, displacedPoint.y); + displacedPoint.y = max(0.01, displacedPoint.y); + + fireColor = texture2D(tex, displacedPoint); + } + return fireColor; +} + +vec4 Uberblend(vec4 col0, vec4 col1) +{ +// return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); +// return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); + return vec4( + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), + min(1.0, col0.a + col1.a)); +} + + +vec4 GetCheckboardColor(vec2 pos) +{ + vec4 col = vec4(0.0, 0.0, 0.0, 0.0); + if(pos.x > 0.0 && pos.x < 1.0 && pos.y > 0.0 && pos.y < 1.0) + { + if(mod(pos.x, 0.1) < 0.05 ^^ mod(pos.y, 0.1) < 0.05) + col = vec4(1.0, 1.0, 1.0, 1.0); + else + col = vec4(0.5, 0.5, 0.5, 1.0); + } + return col; +} \ No newline at end of file diff --git a/game/modules/tome/data/gfx/shaders/firesurge.lua b/game/modules/tome/data/gfx/shaders/firesurge.lua new file mode 100644 index 0000000000000000000000000000000000000000..64bdd7bafaf2e869bd84e660210dd1fb712957ca --- /dev/null +++ b/game/modules/tome/data/gfx/shaders/firesurge.lua @@ -0,0 +1,35 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +return { + frag = "firesurge", + vert = nil, + args = { + tex = { texture = 0 }, + ellipsoidalFactor = ellipsoidalFactor or {1,1}, --1 is perfect circle, >1 is ellipsoidal + side = noup or 0, + timeScale = timeScale or 2.0, + isPermanent = isPermanent or 0.0, + coreRadiusMult = coreRadiusMult or 1.0, + }, + resetargs = { + tick_start = function() return core.game.getFrameTime() end, + }, + clone = false, +} diff --git a/game/modules/tome/data/gfx/shaders/healing.frag b/game/modules/tome/data/gfx/shaders/healing.frag index f13cb8f3e20f29f037aaebf317ba3c7728595dc3..b83b83ff6fabedbde36524a5e321c09e7e36548b 100644 --- a/game/modules/tome/data/gfx/shaders/healing.frag +++ b/game/modules/tome/data/gfx/shaders/healing.frag @@ -23,7 +23,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/runicshield.frag b/game/modules/tome/data/gfx/shaders/runicshield.frag index 43711a0a56660eaea00d9cf1e82aa409d31fd98e..e0b34f457f9016224cdce9c87042a22f27429ac7 100644 --- a/game/modules/tome/data/gfx/shaders/runicshield.frag +++ b/game/modules/tome/data/gfx/shaders/runicshield.frag @@ -152,7 +152,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/spinningwinds.frag b/game/modules/tome/data/gfx/shaders/spinningwinds.frag index 5da5dd62a5783f424a208013ed542b3e667623a0..030e6f6abdf3f38df678e5f0aa81797073fa13c3 100644 --- a/game/modules/tome/data/gfx/shaders/spinningwinds.frag +++ b/game/modules/tome/data/gfx/shaders/spinningwinds.frag @@ -21,7 +21,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/spinningwinds2.frag b/game/modules/tome/data/gfx/shaders/spinningwinds2.frag index c9713d6cc033f6bd2fbb018373a255423f315a72..e98ee10fb49427c12ea8228b861fea3d596b329f 100644 --- a/game/modules/tome/data/gfx/shaders/spinningwinds2.frag +++ b/game/modules/tome/data/gfx/shaders/spinningwinds2.frag @@ -140,7 +140,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/starfall.frag b/game/modules/tome/data/gfx/shaders/starfall.frag index 57f75d2c570b29016ef914708a9204b114a3b20d..c62ce4e4e388b4f34edef11c6b7ae85a108e2197 100644 --- a/game/modules/tome/data/gfx/shaders/starfall.frag +++ b/game/modules/tome/data/gfx/shaders/starfall.frag @@ -10,7 +10,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shaders/volumetric_aura.frag b/game/modules/tome/data/gfx/shaders/volumetric_aura.frag index c4bc67c2abdefd104a31cb9febfa446ca746defd..483739f717105fbe4014efb042f13b1da6e19ef0 100644 --- a/game/modules/tome/data/gfx/shaders/volumetric_aura.frag +++ b/game/modules/tome/data/gfx/shaders/volumetric_aura.frag @@ -9,7 +9,7 @@ vec4 Uberblend(vec4 col0, vec4 col1) // return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); // return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a)); return vec4( - (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) + + (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-2) + (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) + (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) + (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb), diff --git a/game/modules/tome/data/gfx/shockbolt/attachements.lua b/game/modules/tome/data/gfx/shockbolt/attachements.lua index c7af7fe28d57cae0284f3ac56357f54a702494f5..d4135fbf0245a299175a6d62edaa6e8745a54b3e 100644 --- a/game/modules/tome/data/gfx/shockbolt/attachements.lua +++ b/game/modules/tome/data/gfx/shockbolt/attachements.lua @@ -2,95 +2,95 @@ tiles={} dolls={} tiles["npc/humanoid_shalore_elven_guard.png"] = { base=64, feet = {x=33, y=61}, + hand1 = {x=13, y=31}, back = {x=32, y=15}, belly = {x=32, y=28}, - hand1 = {x=13, y=31}, head = {x=31, y=5}, } -tiles["npc/humanoid_human_gladiator.png"] = { base=64, - feet = {x=35, y=58}, - hand1 = {x=13, y=35}, - hand2 = {x=53, y=36}, - back = {x=34, y=19}, - belly = {x=35, y=31}, - head = {x=32, y=7}, +tiles["npc/humanoid_human_alchemist.png"] = { base=64, + feet = {x=35, y=59}, + hand1 = {x=10, y=32}, + hand2 = {x=51, y=34}, + belly = {x=34, y=32}, + back = {x=33, y=19}, + head = {x=33, y=5}, } tiles["npc/humanoid_human_high_gladiator.png"] = { base=64, feet = {x=30, y=61}, hand1 = {x=8, y=13}, hand2 = {x=55, y=33}, - back = {x=34, y=17}, belly = {x=33, y=30}, + back = {x=34, y=17}, head = {x=30, y=7}, } tiles["npc/humanoid_shalore_elvala_guard.png"] = { base=64, feet = {x=32, y=61}, + hand1 = {x=13, y=30}, back = {x=31, y=15}, belly = {x=31, y=27}, - hand1 = {x=13, y=30}, head = {x=31, y=5}, } tiles["npc/humanoid_naga_slasul.png"] = { base=64, feet = {x=32, y=61}, hand1 = {x=11, y=30}, hand2 = {x=46, y=-2}, - belly = {x=23, y=14}, back = {x=23, y=3}, + belly = {x=23, y=14}, head = {x=24, y=-12}, } tiles["npc/humanoid_human_human_citizen.png"] = { base=64, feet = {x=35, y=59}, hand1 = {x=11, y=13}, hand2 = {x=47, y=30}, - back = {x=36, y=16}, belly = {x=36, y=28}, + back = {x=36, y=16}, head = {x=36, y=5}, } tiles["npc/humanoid_yeek_yeek_psionic.png"] = { base=64, feet = {x=33, y=61}, hand1 = {x=31, y=38}, hand2 = {x=33, y=38}, - belly = {x=32, y=34}, back = {x=32, y=23}, + belly = {x=32, y=34}, head = {x=32, y=12}, } tiles["npc/humanoid_orc_vor__grand_geomancer_of_the_pride.png"] = { base=64, feet = {x=32, y=60}, hand1 = {x=4, y=34}, hand2 = {x=58, y=39}, - belly = {x=32, y=35}, back = {x=33, y=20}, + belly = {x=32, y=35}, head = {x=34, y=9}, } tiles["npc/humanoid_human_ziguranth_wyrmic.png"] = { base=64, feet = {x=32, y=59}, hand1 = {x=12, y=32}, hand2 = {x=51, y=34}, - belly = {x=33, y=30}, back = {x=32, y=16}, + belly = {x=33, y=30}, head = {x=32, y=4}, } tiles["npc/humanoid_shalore_elven_warrior.png"] = { base=64, feet = {x=32, y=61}, + hand1 = {x=13, y=31}, back = {x=31, y=14}, belly = {x=31, y=26}, - hand1 = {x=13, y=31}, head = {x=31, y=5}, } tiles["npc/humanoid_shalore_elandar.png"] = { base=64, feet = {x=34, y=59}, hand1 = {x=13, y=26}, hand2 = {x=50, y=-12}, - belly = {x=33, y=22}, back = {x=30, y=8}, + belly = {x=33, y=22}, head = {x=29, y=-2}, } tiles["npc/humanoid_human_high_sun_paladin_rodmour.png"] = { base=64, feet = {x=32, y=59}, hand1 = {x=12, y=35}, hand2 = {x=48, y=36}, - back = {x=32, y=15}, belly = {x=32, y=29}, + back = {x=32, y=15}, head = {x=32, y=3}, } dolls.race_runic_golem = dolls.race_runic_golem or {} @@ -98,32 +98,32 @@ dolls.race_runic_golem.all = { base=64, feet = {x=54, y=43}, hand1 = {x=29, y=18}, hand2 = {x=22, y=28}, - belly = {x=43, y=49}, back = {x=28, y=48}, + belly = {x=43, y=49}, head = {x=39, y=29}, } tiles["npc/humanoid_human_assassin_lord.png"] = { base=64, feet = {x=35, y=61}, hand1 = {x=10, y=10}, hand2 = {x=56, y=34}, - back = {x=31, y=16}, belly = {x=32, y=28}, + back = {x=31, y=16}, head = {x=32, y=5}, } -tiles["npc/humanoid_human_rogue.png"] = { base=64, - feet = {x=36, y=58}, - hand1 = {x=8, y=36}, - hand2 = {x=55, y=29}, - belly = {x=30, y=31}, - back = {x=30, y=21}, - head = {x=27, y=7}, +tiles["npc/humanoid_human_hexer.png"] = { base=64, + feet = {x=33, y=61}, + hand1 = {x=26, y=25}, + hand2 = {x=37, y=25}, + belly = {x=32, y=37}, + back = {x=31, y=16}, + head = {x=31, y=3}, } tiles["npc/humanoid_dwarf_norgan.png"] = { base=64, feet = {x=41, y=57}, hand1 = {x=15, y=40}, hand2 = {x=47, y=19}, - back = {x=32, y=23}, belly = {x=35, y=37}, + back = {x=32, y=23}, head = {x=27, y=8}, } dolls.race_human = dolls.race_human or {} @@ -131,64 +131,64 @@ dolls.race_human.male = { base=64, feet = {x=32, y=60}, hand1 = {x=15, y=32}, hand2 = {x=48, y=34}, - belly = {x=32, y=28}, back = {x=32, y=15}, + belly = {x=32, y=28}, head = {x=30, y=3}, } tiles["npc/humanoid_yaech_murgol__the_yaech_lord.png"] = { base=64, feet = {x=56, y=58}, hand1 = {x=7, y=28}, hand2 = {x=20, y=32}, - belly = {x=38, y=39}, back = {x=38, y=31}, + belly = {x=38, y=39}, head = {x=32, y=16}, } -tiles["npc/humanoid_halfling_protector_myssil.png"] = { base=64, - feet = {x=32, y=60}, - hand1 = {x=16, y=43}, - hand2 = {x=45, y=39}, - back = {x=30, y=22}, - belly = {x=31, y=34}, - head = {x=30, y=7}, +tiles["npc/humanoid_human_the_possessed.png"] = { base=64, + feet = {x=30, y=62}, + hand1 = {x=9, y=17}, + hand2 = {x=47, y=37}, + back = {x=33, y=17}, + belly = {x=33, y=35}, + head = {x=33, y=4}, } tiles["npc/humanoid_halfling_halfling_guard.png"] = { base=64, feet = {x=38, y=59}, hand1 = {x=8, y=28}, hand2 = {x=54, y=43}, - back = {x=33, y=24}, belly = {x=36, y=36}, + back = {x=33, y=24}, head = {x=30, y=10}, } tiles["npc/humanoid_thalore_mindworm.png"] = { base=64, feet = {x=32, y=62}, hand1 = {x=21, y=36}, hand2 = {x=44, y=35}, - belly = {x=32, y=30}, back = {x=31, y=13}, + belly = {x=32, y=30}, head = {x=31, y=6}, } tiles["npc/humanoid_shalore_grand_corruptor.png"] = { base=64, feet = {x=25, y=61}, hand1 = {x=11, y=34}, hand2 = {x=53, y=17}, - belly = {x=28, y=31}, back = {x=28, y=19}, + belly = {x=28, y=31}, head = {x=28, y=10}, } -tiles["npc/humanoid_shalore_elven_blood_mage.png"] = { base=64, - feet = {x=32, y=59}, - hand1 = {x=15, y=36}, - hand2 = {x=46, y=36}, - belly = {x=30, y=33}, - back = {x=30, y=17}, - head = {x=30, y=7}, +tiles["npc/humanoid_human_harno__herald_of_last_hope.png"] = { base=64, + feet = {x=32, y=61}, + hand1 = {x=16, y=37}, + hand2 = {x=45, y=36}, + belly = {x=31, y=31}, + back = {x=30, y=15}, + head = {x=29, y=4}, } tiles["npc/humanoid_human_slave_combatant.png"] = { base=64, feet = {x=33, y=60}, hand1 = {x=18, y=37}, hand2 = {x=47, y=32}, - belly = {x=31, y=29}, back = {x=30, y=17}, + belly = {x=31, y=29}, head = {x=29, y=6}, } dolls.race_elf = dolls.race_elf or {} @@ -196,79 +196,81 @@ dolls.race_elf.male = { base=64, feet = {x=32, y=61}, hand1 = {x=19, y=32}, hand2 = {x=44, y=34}, - belly = {x=33, y=27}, back = {x=32, y=15}, + belly = {x=33, y=27}, head = {x=30, y=4}, } tiles["npc/humanoid_male_sluttymaid.png"] = { base=64, feet = {x=30, y=60}, hand1 = {x=24, y=30}, hand2 = {x=41, y=21}, - belly = {x=35, y=24}, back = {x=35, y=11}, + belly = {x=35, y=24}, head = {x=35, y=0}, } tiles["npc/humanoid_elf_limmir_the_jeweler.png"] = { base=64, feet = {x=31, y=60}, hand1 = {x=11, y=37}, hand2 = {x=49, y=9}, - back = {x=31, y=19}, belly = {x=31, y=30}, + back = {x=31, y=19}, head = {x=32, y=4}, } tiles["npc/canine_warg.png"] = { base=64, feet = {x=15, y=18}, hand1 = {x=48, y=22}, hand2 = {x=46, y=48}, - belly = {x=12, y=32}, back = {x=26, y=51}, + belly = {x=12, y=32}, head = {x=33, y=13}, } -tiles["npc/humanoid_human_cryomancer.png"] = { base=64, - feet = {x=37, y=59}, - hand1 = {x=22, y=6}, - hand2 = {x=44, y=32}, - back = {x=35, y=16}, - belly = {x=36, y=28}, - head = {x=34, y=5}, +tiles["npc/humanoid_human_pyromancer.png"] = { base=64, + feet = {x=35, y=59}, + hand1 = {x=19, y=7}, + hand2 = {x=41, y=32}, + back = {x=32, y=17}, + belly = {x=34, y=29}, + head = {x=32, y=4}, } tiles["npc/acid_ant.png"] = { base=64, feet = {x=36, y=57}, back = {x=45, y=37}, hand2 = {x=51, y=17}, - hand1 = {x=32, y=22}, belly = {x=11, y=33}, + hand1 = {x=32, y=22}, head = {x=15, y=13}, } tiles["npc/humanoid_shalore_elven_corruptor.png"] = { base=64, feet = {x=34, y=60}, hand1 = {x=12, y=17}, hand2 = {x=48, y=34}, - belly = {x=31, y=33}, back = {x=31, y=20}, + belly = {x=31, y=33}, head = {x=31, y=10}, } -tiles["npc/humanoid_human_townsfolk_village_idiot01_64.png"] = { base=64, - back = {x=33, y=24}, - feet = {x=35, y=58}, - belly = {x=37, y=34}, - head = {x=32, y=8}, +tiles["npc/humanoid_human_townsfolk_farmer_maggot01_64.png"] = { base=64, + feet = {x=36, y=60}, + hand1 = {x=11, y=31}, + hand2 = {x=51, y=25}, + back = {x=35, y=25}, + belly = {x=36, y=37}, + head = {x=34, y=11}, } dolls.race_halfling = dolls.race_halfling or {} dolls.race_halfling.female = { base=64, feet = {x=34, y=62}, hand1 = {x=12, y=38}, hand2 = {x=50, y=40}, - back = {x=32, y=24}, belly = {x=34, y=36}, + back = {x=32, y=24}, head = {x=32, y=12}, } tiles["npc/humanoid_elf_elven_sun_mage.png"] = { base=64, feet = {x=35, y=62}, hand1 = {x=13, y=28}, hand2 = {x=50, y=37}, - back = {x=32, y=16}, belly = {x=34, y=35}, + back = {x=32, y=16}, head = {x=31, y=4}, } dolls.race_halfling = dolls.race_halfling or {} @@ -276,25 +278,25 @@ dolls.race_halfling.male = { base=64, feet = {x=34, y=61}, hand1 = {x=14, y=39}, hand2 = {x=50, y=39}, - back = {x=32, y=25}, belly = {x=33, y=36}, + back = {x=32, y=25}, head = {x=32, y=14}, } -tiles["npc/humanoid_human_townsfolk_battlescarred_veteran01_64.png"] = { base=64, - feet = {x=35, y=57}, - hand1 = {x=16, y=39}, - hand2 = {x=52, y=39}, - belly = {x=33, y=30}, - back = {x=33, y=19}, - head = {x=32, y=7}, +tiles["npc/humanoid_human_fryjia_loren.png"] = { base=64, + feet = {x=32, y=58}, + hand1 = {x=17, y=31}, + hand2 = {x=45, y=41}, + belly = {x=29, y=32}, + back = {x=29, y=21}, + head = {x=29, y=8}, } dolls.race_ghoul = dolls.race_ghoul or {} dolls.race_ghoul.all = { base=64, feet = {x=33, y=61}, hand1 = {x=20, y=37}, hand2 = {x=48, y=38}, - belly = {x=33, y=38}, back = {x=34, y=20}, + belly = {x=33, y=38}, head = {x=33, y=8}, } tiles["npc/animal_feline_snow_cat.png"] = { base=64, @@ -304,47 +306,46 @@ tiles["npc/humanoid_human_master_alchemist.png"] = { base=64, feet = {x=35, y=59}, hand1 = {x=7, y=18}, hand2 = {x=57, y=35}, - belly = {x=37, y=28}, back = {x=34, y=18}, + belly = {x=37, y=28}, head = {x=29, y=8}, } -tiles["npc/humanoid_human_human_farmer.png"] = { base=64, - feet = {x=32, y=60}, - hand1 = {x=11, y=20}, - hand2 = {x=44, y=38}, - back = {x=29, y=18}, - belly = {x=31, y=31}, - head = {x=29, y=5}, +tiles["npc/humanoid_yeek_yeek_commoner_04.png"] = { base=64, + feet = {x=33, y=60}, + hand1 = {x=17, y=40}, + hand2 = {x=50, y=44}, + back = {x=32, y=25}, + belly = {x=32, y=36}, + head = {x=32, y=8}, } -tiles["npc/humanoid_human_lumberjack.png"] = { base=64, - feet = {x=33, y=61}, - hand1 = {x=17, y=37}, - hand2 = {x=44, y=37}, - belly = {x=33, y=31}, - back = {x=32, y=17}, - head = {x=32, y=4}, +tiles["npc/humanoid_human_spectator.png"] = { base=64, + feet = {x=37, y=59}, + hand1 = {x=50, y=37}, + back = {x=35, y=17}, + belly = {x=36, y=29}, + head = {x=34, y=5}, } tiles["npc/humanoid_shalore_elven_elite_warrior.png"] = { base=64, feet = {x=33, y=62}, + hand1 = {x=13, y=32}, back = {x=31, y=15}, belly = {x=31, y=27}, - hand1 = {x=13, y=32}, head = {x=32, y=6}, } tiles["npc/humanoid_human_riala_shalarak.png"] = { base=64, feet = {x=33, y=59}, hand1 = {x=17, y=7}, hand2 = {x=58, y=30}, - belly = {x=34, y=27}, back = {x=35, y=17}, + belly = {x=34, y=27}, head = {x=33, y=5}, } tiles["npc/humanoid_yaech_slaver.png"] = { base=64, feet = {x=46, y=58}, hand1 = {x=16, y=28}, hand2 = {x=51, y=10}, - belly = {x=38, y=34}, back = {x=37, y=25}, + belly = {x=38, y=34}, head = {x=28, y=17}, } dolls.race_human = dolls.race_human or {} @@ -352,33 +353,33 @@ dolls.race_human.female = { base=64, feet = {x=33, y=60}, hand1 = {x=20, y=33}, hand2 = {x=46, y=33}, - back = {x=33, y=16}, belly = {x=34, y=28}, + back = {x=33, y=16}, head = {x=32, y=3}, } -tiles["npc/humanoid_orc_orc_greatmother.png"] = { base=64, - feet = {x=35, y=59}, - hand1 = {x=4, y=-4}, - hand2 = {x=58, y=2}, - belly = {x=30, y=-3}, - back = {x=32, y=-40}, - head = {x=33, y=-55}, +tiles["npc/humanoid_orc_orc_necromancer.png"] = { base=64, + feet = {x=32, y=59}, + hand1 = {x=7, y=39}, + hand2 = {x=53, y=36}, + back = {x=28, y=17}, + belly = {x=30, y=32}, + head = {x=29, y=6}, } -tiles["npc/humanoid_yeek_yeek_commoner_02.png"] = { base=64, - feet = {x=31, y=59}, - hand1 = {x=10, y=20}, - hand2 = {x=23, y=35}, - belly = {x=32, y=35}, - back = {x=30, y=21}, - head = {x=31, y=11}, +tiles["npc/humanoid_elenulach_thief.png"] = { base=64, + feet = {x=30, y=60}, + hand1 = {x=18, y=37}, + hand2 = {x=42, y=37}, + belly = {x=30, y=32}, + back = {x=30, y=17}, + head = {x=30, y=6}, } dolls.race_dwarf = dolls.race_dwarf or {} dolls.race_dwarf.male = { base=64, feet = {x=34, y=61}, back = {x=32, y=25}, hand2 = {x=55, y=39}, - hand1 = {x=10, y=39}, belly = {x=34, y=34}, + hand1 = {x=10, y=39}, head = {x=32, y=10}, } dolls.race_dwarf = dolls.race_dwarf or {} @@ -386,8 +387,8 @@ dolls.race_dwarf.female = { base=64, feet = {x=33, y=59}, back = {x=33, y=25}, hand2 = {x=55, y=39}, - hand1 = {x=9, y=39}, belly = {x=33, y=37}, + hand1 = {x=9, y=39}, head = {x=33, y=9}, } dolls.race_skeleton = dolls.race_skeleton or {} @@ -395,8 +396,8 @@ dolls.race_skeleton.all = { base=64, feet = {x=32, y=62}, hand1 = {x=15, y=33}, hand2 = {x=47, y=34}, - back = {x=31, y=16}, belly = {x=32, y=26}, + back = {x=31, y=16}, head = {x=30, y=4}, } dolls.race_yeek = dolls.race_yeek or {} @@ -404,1240 +405,1239 @@ dolls.race_yeek.all = { base=64, feet = {x=32, y=62}, hand1 = {x=16, y=39}, hand2 = {x=46, y=40}, - back = {x=31, y=26}, belly = {x=32, y=36}, + back = {x=31, y=26}, head = {x=31, y=12}, } -tiles["npc/humanoid_orc_orc_blood_mage.png"] = { base=64, - feet = {x=34, y=59}, - hand1 = {x=11, y=36}, - hand2 = {x=57, y=37}, - belly = {x=34, y=32}, - back = {x=33, y=17}, - head = {x=32, y=5}, +tiles["npc/humanoid_orc_orc_pyromancer.png"] = { base=64, + feet = {x=28, y=61}, + hand1 = {x=6, y=30}, + hand2 = {x=59, y=15}, + back = {x=30, y=17}, + belly = {x=28, y=30}, + head = {x=40, y=5}, } tiles["npc/humanoid_human_aluin_the_fallen.png"] = { base=64, feet = {x=31, y=60}, hand1 = {x=18, y=36}, hand2 = {x=41, y=38}, - back = {x=29, y=18}, belly = {x=29, y=29}, + back = {x=29, y=18}, head = {x=28, y=5}, } -tiles["npc/humanoid_yeek_yeek_commoner_06.png"] = { base=64, - feet = {x=30, y=61}, - hand1 = {x=10, y=22}, - hand2 = {x=23, y=36}, - belly = {x=31, y=36}, - back = {x=31, y=22}, - head = {x=30, y=13}, -} -tiles["npc/humanoid_orc_orc_fighter.png"] = { base=64, - feet = {x=30, y=60}, - hand1 = {x=7, y=8}, - hand2 = {x=57, y=39}, - belly = {x=31, y=34}, - back = {x=35, y=20}, - head = {x=39, y=10}, +tiles["npc/humanoid_human_human_guard.png"] = { base=64, + feet = {x=33, y=60}, + hand1 = {x=19, y=29}, + hand2 = {x=46, y=25}, + belly = {x=31, y=30}, + back = {x=30, y=17}, + head = {x=28, y=5}, } -tiles["npc/humanoid_human_linaniil_supreme_archmage.png"] = { base=64, - feet = {x=38, y=60}, - hand1 = {x=10, y=7}, - hand2 = {x=49, y=29}, - back = {x=35, y=1}, - belly = {x=37, y=19}, - head = {x=31, y=-13}, +tiles["npc/humanoid_orc_orc_soldier.png"] = { base=64, + feet = {x=36, y=62}, + hand1 = {x=9, y=39}, + hand2 = {x=58, y=36}, + back = {x=29, y=21}, + belly = {x=34, y=34}, + head = {x=25, y=9}, +} +tiles["npc/humanoid_human_lost_merchant.png"] = { base=64, + feet = {x=35, y=59}, + hand1 = {x=16, y=47}, + back = {x=32, y=24}, + belly = {x=35, y=36}, + head = {x=30, y=5}, } tiles["npc/humanoid_elf_elven_archer.png"] = { base=64, feet = {x=32, y=61}, hand1 = {x=13, y=32}, hand2 = {x=51, y=32}, - back = {x=32, y=15}, belly = {x=32, y=28}, + back = {x=32, y=15}, head = {x=32, y=5}, } -tiles["npc/humanoid_halfling_halfling_citizen.png"] = { base=64, - feet = {x=31, y=58}, - hand1 = {x=13, y=38}, - hand2 = {x=48, y=41}, - back = {x=30, y=22}, - belly = {x=31, y=33}, - head = {x=30, y=8}, +tiles["npc/humanoid_human_subject_z.png"] = { base=64, + feet = {x=32, y=59}, + hand1 = {x=8, y=11}, + hand2 = {x=55, y=11}, + back = {x=31, y=15}, + belly = {x=31, y=27}, + head = {x=32, y=3}, } -tiles["npc/humanoid_human_sand_wyrmic.png"] = { base=64, - feet = {x=29, y=60}, - hand1 = {x=10, y=38}, - hand2 = {x=58, y=17}, - belly = {x=31, y=30}, - back = {x=32, y=19}, - head = {x=37, y=8}, +tiles["npc/humanoid_human_trickster.png"] = { base=64, + feet = {x=36, y=60}, + back = {x=35, y=17}, + hand2 = {x=5, y=32}, + belly = {x=37, y=29}, + head = {x=30, y=6}, } -tiles["npc/humanoid_human_spectator02.png"] = { base=64, - feet = {x=30, y=59}, - hand1 = {x=18, y=37}, - hand2 = {x=41, y=37}, - belly = {x=31, y=30}, - back = {x=30, y=16}, - head = {x=30, y=5}, +tiles["npc/humanoid_human_fire_wyrmic.png"] = { base=64, + feet = {x=31, y=60}, + hand1 = {x=12, y=30}, + hand2 = {x=49, y=36}, + belly = {x=28, y=28}, + back = {x=28, y=19}, + head = {x=24, y=9}, } -tiles["npc/humanoid_human_arcane_blade.png"] = { base=64, - feet = {x=26, y=61}, - hand1 = {x=17, y=3}, - hand2 = {x=38, y=35}, - back = {x=27, y=22}, - belly = {x=27, y=29}, - head = {x=29, y=13}, +tiles["npc/humanoid_human_enthralled_slave.png"] = { base=64, + feet = {x=33, y=58}, + hand1 = {x=16, y=35}, + hand2 = {x=47, y=36}, + belly = {x=32, y=29}, + back = {x=32, y=18}, + head = {x=31, y=5}, } -tiles["npc/humanoid_human_necromancer.png"] = { base=64, - feet = {x=27, y=61}, - hand1 = {x=18, y=16}, - hand2 = {x=40, y=41}, - belly = {x=32, y=32}, - back = {x=34, y=17}, - head = {x=36, y=3}, +tiles["npc/humanoid_human_rej_arkatis.png"] = { base=64, + feet = {x=34, y=59}, + hand1 = {x=24, y=17}, + hand2 = {x=38, y=19}, + back = {x=33, y=15}, + belly = {x=33, y=26}, + head = {x=32, y=4}, } -tiles["npc/humanoid_orc_orc_mother.png"] = { base=64, - feet = {x=33, y=59}, - hand1 = {x=10, y=37}, - hand2 = {x=53, y=35}, - belly = {x=30, y=31}, - back = {x=30, y=9}, - head = {x=28, y=-6}, +tiles["npc/humanoid_orc_orc_archer.png"] = { base=64, + feet = {x=30, y=60}, + hand1 = {x=20, y=29}, + hand2 = {x=56, y=30}, + back = {x=30, y=20}, + belly = {x=31, y=34}, + head = {x=31, y=10}, } -tiles["npc/humanoid_human_townsfolk_squinteyed_rogue01_64.png"] = { base=64, - feet = {x=33, y=57}, - hand1 = {x=11, y=28}, - hand2 = {x=53, y=38}, - belly = {x=33, y=33}, - back = {x=33, y=20}, - head = {x=30, y=7}, +tiles["npc/humanoid_player_default.png"] = { base=64, + feet = {x=33, y=60}, + hand1 = {x=21, y=35}, + hand2 = {x=44, y=36}, + back = {x=32, y=14}, + belly = {x=33, y=28}, + head = {x=32, y=5}, } tiles["npc/humanoid_halfling_sm_halfling.png"] = { base=64, feet = {x=33, y=61}, hand1 = {x=10, y=14}, hand2 = {x=52, y=11}, - back = {x=30, y=23}, belly = {x=30, y=35}, + back = {x=30, y=23}, head = {x=31, y=7}, } -tiles["npc/humanoid_orc_orc_elite_fighter.png"] = { base=64, - feet = {x=31, y=60}, - hand1 = {x=11, y=37}, - hand2 = {x=53, y=29}, - belly = {x=31, y=33}, - back = {x=29, y=16}, - head = {x=29, y=4}, +tiles["npc/humanoid_orc_orc_master_wyrmic.png"] = { base=64, + feet = {x=39, y=60}, + hand1 = {x=16, y=40}, + hand2 = {x=58, y=36}, + back = {x=33, y=21}, + belly = {x=36, y=32}, + head = {x=30, y=13}, } tiles["npc/humanoid_human_bandit.png"] = { base=64, feet = {x=39, y=56}, hand1 = {x=8, y=40}, hand2 = {x=51, y=26}, - back = {x=30, y=16}, belly = {x=33, y=28}, + back = {x=30, y=16}, head = {x=29, y=5}, } tiles["npc/humanoid_elf_star_crusader.png"] = { base=64, feet = {x=34, y=60}, hand1 = {x=11, y=30}, hand2 = {x=51, y=33}, - back = {x=34, y=17}, belly = {x=34, y=30}, + back = {x=34, y=17}, head = {x=34, y=4}, } tiles["npc/humanoid_elf_high_chronomancer_zemekkys.png"] = { base=64, feet = {x=33, y=61}, hand1 = {x=11, y=36}, hand2 = {x=51, y=38}, - back = {x=32, y=19}, belly = {x=32, y=29}, + back = {x=32, y=19}, head = {x=30, y=6}, } tiles["npc/humanoid_yaech_yaech_psion.png"] = { base=64, feet = {x=5, y=58}, hand1 = {x=55, y=27}, hand2 = {x=42, y=32}, - belly = {x=25, y=40}, back = {x=23, y=31}, + belly = {x=25, y=40}, head = {x=33, y=15}, } tiles["npc/humanoid_orc_orc_corruptor.png"] = { base=64, feet = {x=35, y=61}, hand1 = {x=15, y=39}, hand2 = {x=51, y=37}, - belly = {x=34, y=36}, back = {x=31, y=17}, + belly = {x=34, y=36}, head = {x=28, y=6}, } tiles["npc/humanoid_halfling_halfling_gardener.png"] = { base=64, feet = {x=33, y=58}, hand1 = {x=17, y=36}, hand2 = {x=48, y=38}, - back = {x=31, y=22}, belly = {x=31, y=32}, + back = {x=31, y=22}, head = {x=29, y=8}, } -tiles["npc/humanoid_human_argoniel.png"] = { base=64, - feet = {x=30, y=59}, - hand1 = {x=9, y=2}, - hand2 = {x=57, y=9}, - back = {x=32, y=10}, - belly = {x=32, y=22}, - head = {x=32, y=-3}, +tiles["npc/humanoid_human_cutpurse.png"] = { base=64, + feet = {x=36, y=59}, + hand1 = {x=9, y=34}, + hand2 = {x=46, y=34}, + belly = {x=30, y=29}, + back = {x=29, y=18}, + head = {x=28, y=7}, } tiles["npc/humanoid_orc_fiery_orc_wyrmic.png"] = { base=64, feet = {x=33, y=60}, hand1 = {x=20, y=20}, hand2 = {x=57, y=36}, - belly = {x=32, y=31}, back = {x=30, y=21}, + belly = {x=32, y=31}, head = {x=25, y=9}, } tiles["npc/humanoid_orc_ukruk_the_fierce.png"] = { base=64, feet = {x=34, y=60}, + hand1 = {x=10, y=38}, back = {x=28, y=17}, belly = {x=29, y=33}, - hand1 = {x=10, y=38}, head = {x=25, y=8}, } -tiles["npc/humanoid_orc_orc_warrior.png"] = { base=64, - feet = {x=35, y=61}, - hand1 = {x=8, y=38}, - hand2 = {x=57, y=35}, - belly = {x=34, y=34}, - back = {x=29, y=20}, - head = {x=25, y=9}, +tiles["npc/humanoid_yeek_yeek_commoner_03.png"] = { base=64, + feet = {x=32, y=59}, + hand1 = {x=17, y=42}, + hand2 = {x=50, y=44}, + back = {x=32, y=24}, + belly = {x=33, y=36}, + head = {x=31, y=12}, } -tiles["npc/humanoid_orc_orc_child.png"] = { base=64, - feet = {x=31, y=55}, - hand1 = {x=8, y=36}, - hand2 = {x=48, y=35}, - belly = {x=30, y=32}, - back = {x=29, y=24}, - head = {x=25, y=12}, +tiles["npc/humanoid_orc_young_orc.png"] = { base=64, + feet = {x=35, y=60}, + hand1 = {x=14, y=10}, + hand2 = {x=52, y=40}, + back = {x=32, y=19}, + belly = {x=35, y=32}, + head = {x=30, y=8}, } -tiles["npc/humanoid_orc_brotoq_the_reaver.png"] = { base=64, - feet = {x=38, y=59}, - hand1 = {x=14, y=31}, - hand2 = {x=58, y=33}, - belly = {x=38, y=26}, - back = {x=38, y=15}, - head = {x=35, y=3}, +tiles["npc/humanoid_dwarf_dwarven_earthwarden.png"] = { base=64, + feet = {x=34, y=58}, + back = {x=36, y=22}, + hand2 = {x=51, y=36}, + belly = {x=35, y=37}, + hand1 = {x=20, y=37}, + head = {x=35, y=7}, } -tiles["npc/humanoid_human_spectator.png"] = { base=64, - feet = {x=37, y=59}, - back = {x=35, y=17}, - belly = {x=36, y=29}, - hand1 = {x=50, y=37}, - head = {x=34, y=5}, +tiles["npc/humanoid_orc_orc_mage_hunter.png"] = { base=64, + feet = {x=32, y=61}, + hand1 = {x=6, y=34}, + hand2 = {x=57, y=31}, + back = {x=28, y=20}, + belly = {x=29, y=32}, + head = {x=27, y=8}, } -tiles["npc/humanoid_human_shady_cornac_man.png"] = { base=64, - feet = {x=35, y=60}, - hand1 = {x=19, y=35}, - hand2 = {x=49, y=35}, - belly = {x=33, y=29}, - back = {x=32, y=17}, - head = {x=29, y=4}, +tiles["npc/humanoid_naga_naga_tidewarden.png"] = { base=64, + feet = {x=32, y=61}, + hand1 = {x=31, y=12}, + back = {x=32, y=-3}, + belly = {x=32, y=9}, + head = {x=34, y=-15}, } tiles["npc/humanoid_dwarf_dwarven_guard.png"] = { base=64, feet = {x=30, y=59}, hand1 = {x=13, y=40}, hand2 = {x=40, y=22}, - belly = {x=29, y=36}, back = {x=29, y=23}, + belly = {x=29, y=36}, head = {x=28, y=6}, } -tiles["npc/humanoid_human_human_guard.png"] = { base=64, - feet = {x=33, y=60}, - hand1 = {x=19, y=29}, - hand2 = {x=46, y=25}, - back = {x=30, y=17}, - belly = {x=31, y=30}, - head = {x=28, y=5}, +tiles["npc/humanoid_halfling_protector_myssil.png"] = { base=64, + feet = {x=32, y=60}, + hand1 = {x=16, y=43}, + hand2 = {x=45, y=39}, + belly = {x=31, y=34}, + back = {x=30, y=22}, + head = {x=30, y=7}, } -tiles["npc/humanoid_yaech_yaech_mindslayer.png"] = { base=64, - feet = {x=58, y=58}, - hand1 = {x=7, y=28}, - hand2 = {x=21, y=32}, - belly = {x=39, y=40}, - back = {x=37, y=31}, - head = {x=28, y=19}, +tiles["npc/humanoid_human_townsfolk_blubbering_idiot01_64.png"] = { base=64, + feet = {x=33, y=59}, + hand1 = {x=19, y=41}, + hand2 = {x=47, y=40}, + back = {x=31, y=19}, + belly = {x=31, y=33}, + head = {x=29, y=7}, } tiles["npc/humanoid_human_townsfolk_meanlooking_mercenary01_64.png"] = { base=64, feet = {x=35, y=58}, + hand1 = {x=8, y=29}, back = {x=36, y=19}, belly = {x=36, y=30}, - hand1 = {x=8, y=29}, head = {x=33, y=6}, } -tiles["npc/humanoid_orc_orc_master_assassin.png"] = { base=64, - feet = {x=31, y=60}, - hand1 = {x=4, y=39}, - hand2 = {x=59, y=32}, - belly = {x=30, y=30}, - back = {x=29, y=18}, - head = {x=28, y=8}, +tiles["npc/humanoid_orc_brotoq_the_reaver.png"] = { base=64, + feet = {x=38, y=59}, + hand1 = {x=14, y=31}, + hand2 = {x=58, y=33}, + back = {x=38, y=15}, + belly = {x=38, y=26}, + head = {x=35, y=3}, } -tiles["npc/humanoid_human_storm_wyrmic.png"] = { base=64, - feet = {x=34, y=60}, - hand1 = {x=15, y=32}, - hand2 = {x=53, y=37}, - belly = {x=31, y=31}, - back = {x=31, y=21}, - head = {x=27, y=9}, +tiles["npc/humanoid_yaech_yaech_hunter.png"] = { base=64, + feet = {x=6, y=7}, + hand1 = {x=33, y=50}, + hand2 = {x=59, y=37}, + back = {x=34, y=29}, + belly = {x=23, y=29}, + head = {x=46, y=19}, } tiles["npc/humanoid_shalore_shalore_rune_master.png"] = { base=64, feet = {x=33, y=61}, hand1 = {x=17, y=34}, hand2 = {x=48, y=34}, - belly = {x=33, y=30}, back = {x=32, y=14}, + belly = {x=33, y=30}, head = {x=32, y=4}, } tiles["npc/humanoid_thalore_thalore_wilder.png"] = { base=64, feet = {x=27, y=59}, hand1 = {x=11, y=31}, hand2 = {x=44, y=30}, - belly = {x=28, y=23}, back = {x=29, y=11}, + belly = {x=28, y=23}, head = {x=29, y=-1}, } tiles["npc/humanoid_orc_rak_shor_cultist.png"] = { base=64, feet = {x=32, y=61}, hand1 = {x=5, y=39}, hand2 = {x=58, y=34}, - belly = {x=30, y=35}, back = {x=29, y=19}, + belly = {x=30, y=35}, head = {x=29, y=8}, } -tiles["npc/humanoid_halfling_halfling_slinger.png"] = { base=64, - feet = {x=27, y=57}, - hand1 = {x=16, y=15}, - hand2 = {x=55, y=30}, - back = {x=33, y=32}, - belly = {x=31, y=42}, - head = {x=36, y=19}, -} -tiles["npc/humanoid_human_melnela.png"] = { base=64, - feet = {x=33, y=59}, - hand1 = {x=17, y=8}, - hand2 = {x=58, y=30}, - belly = {x=35, y=31}, - back = {x=35, y=19}, - head = {x=33, y=6}, +tiles["npc/humanoid_orc_orc_grand_master_assassin.png"] = { base=64, + feet = {x=33, y=61}, + hand1 = {x=4, y=32}, + hand2 = {x=60, y=39}, + back = {x=35, y=18}, + belly = {x=34, y=30}, + head = {x=37, y=7}, } tiles["npc/humanoid_human_tempest.png"] = { base=64, feet = {x=26, y=59}, hand1 = {x=19, y=31}, hand2 = {x=41, y=6}, - belly = {x=26, y=30}, back = {x=27, y=16}, + belly = {x=26, y=30}, head = {x=28, y=4}, } +tiles["npc/humanoid_yeek_yeek_commoner_01.png"] = { base=64, + feet = {x=33, y=61}, + hand1 = {x=14, y=39}, + hand2 = {x=48, y=38}, + back = {x=30, y=19}, + belly = {x=33, y=32}, + head = {x=30, y=10}, +} tiles["npc/humanoid_elf_fillarel_aldaren.png"] = { base=64, feet = {x=25, y=59}, hand1 = {x=10, y=28}, hand2 = {x=49, y=7}, - back = {x=24, y=3}, belly = {x=24, y=15}, + back = {x=24, y=3}, head = {x=23, y=-12}, } -tiles["npc/humanoid_orc_grushnak__battlemaster_of_the_pride.png"] = { base=64, - feet = {x=30, y=61}, - hand1 = {x=10, y=40}, - hand2 = {x=56, y=36}, - belly = {x=30, y=32}, - back = {x=30, y=18}, - head = {x=30, y=11}, -} -tiles["npc/humanoid_orc_orc_master_wyrmic.png"] = { base=64, - feet = {x=39, y=60}, - hand1 = {x=16, y=40}, - hand2 = {x=58, y=36}, - belly = {x=36, y=32}, - back = {x=33, y=21}, - head = {x=30, y=13}, +tiles["npc/humanoid_human_townsfolk_mangy_looking_leper01_64.png"] = { base=64, + feet = {x=36, y=57}, + hand1 = {x=17, y=23}, + hand2 = {x=18, y=35}, + back = {x=32, y=18}, + belly = {x=36, y=31}, + head = {x=28, y=5}, } -tiles["npc/humanoid_orc_orc_mage_hunter.png"] = { base=64, - feet = {x=32, y=61}, - hand1 = {x=6, y=34}, - hand2 = {x=57, y=31}, - belly = {x=29, y=32}, - back = {x=28, y=20}, - head = {x=27, y=8}, +tiles["npc/humanoid_orc_orc_warrior.png"] = { base=64, + feet = {x=35, y=61}, + hand1 = {x=8, y=38}, + hand2 = {x=57, y=35}, + back = {x=29, y=20}, + belly = {x=34, y=34}, + head = {x=25, y=9}, } -tiles["npc/humanoid_yeek_yeek_commoner_03.png"] = { base=64, - feet = {x=32, y=59}, - hand1 = {x=17, y=42}, - hand2 = {x=50, y=44}, - belly = {x=33, y=36}, - back = {x=32, y=24}, - head = {x=31, y=12}, +tiles["npc/humanoid_orc_orc_greatmother.png"] = { base=64, + feet = {x=35, y=59}, + hand1 = {x=4, y=-4}, + hand2 = {x=58, y=2}, + back = {x=32, y=-40}, + belly = {x=30, y=-3}, + head = {x=33, y=-55}, } -tiles["npc/humanoid_yeek_yeek_commoner_05.png"] = { base=64, - feet = {x=31, y=61}, - hand1 = {x=12, y=22}, - hand2 = {x=24, y=35}, - belly = {x=32, y=34}, +tiles["npc/humanoid_yeek_yeek_commoner_02.png"] = { base=64, + feet = {x=31, y=59}, + hand1 = {x=10, y=20}, + hand2 = {x=23, y=35}, back = {x=30, y=21}, - head = {x=30, y=12}, + belly = {x=32, y=35}, + head = {x=31, y=11}, } -tiles["npc/humanoid_naga_naga_tidewarden.png"] = { base=64, - feet = {x=32, y=61}, - back = {x=32, y=-3}, - belly = {x=32, y=9}, - hand1 = {x=31, y=12}, - head = {x=34, y=-15}, +tiles["npc/humanoid_human_storm_wyrmic.png"] = { base=64, + feet = {x=34, y=60}, + hand1 = {x=15, y=32}, + hand2 = {x=53, y=37}, + back = {x=31, y=21}, + belly = {x=31, y=31}, + head = {x=27, y=9}, +} +tiles["npc/humanoid_naga_naga_tidecaller.png"] = { base=64, + feet = {x=33, y=61}, + hand1 = {x=12, y=15}, + hand2 = {x=47, y=-14}, + back = {x=27, y=-6}, + belly = {x=30, y=7}, + head = {x=26, y=-17}, } tiles["npc/humanoid_yeek_yeek_summoner.png"] = { base=64, feet = {x=33, y=61}, hand1 = {x=14, y=38}, hand2 = {x=48, y=37}, - belly = {x=32, y=36}, back = {x=31, y=23}, + belly = {x=32, y=36}, head = {x=30, y=10}, } tiles["npc/humanoid_thalore_ziguranth_summoner.png"] = { base=64, feet = {x=35, y=61}, hand1 = {x=12, y=17}, hand2 = {x=49, y=39}, - belly = {x=33, y=26}, back = {x=34, y=15}, + belly = {x=33, y=26}, head = {x=32, y=4}, } -tiles["npc/humanoid_human_townsfolk_mangy_looking_leper01_64.png"] = { base=64, - feet = {x=36, y=57}, - hand1 = {x=17, y=23}, - hand2 = {x=18, y=35}, +tiles["npc/humanoid_orc_orc_cryomancer.png"] = { base=64, + feet = {x=35, y=60}, + hand1 = {x=5, y=14}, + hand2 = {x=57, y=30}, + back = {x=29, y=17}, belly = {x=36, y=31}, - back = {x=32, y=18}, - head = {x=28, y=5}, + head = {x=23, y=7}, } -tiles["npc/humanoid_human_ice_wyrmic.png"] = { base=64, - feet = {x=30, y=61}, - back = {x=32, y=19}, - hand2 = {x=58, y=17}, - hand1 = {x=9, y=36}, - belly = {x=32, y=33}, - head = {x=36, y=11}, +tiles["npc/humanoid_yaech_yaech_mindslayer.png"] = { base=64, + feet = {x=58, y=58}, + hand1 = {x=7, y=28}, + hand2 = {x=21, y=32}, + back = {x=37, y=31}, + belly = {x=39, y=40}, + head = {x=28, y=19}, } -tiles["npc/humanoid_orc_orc_cryomancer.png"] = { base=64, - feet = {x=35, y=60}, - hand1 = {x=5, y=14}, - hand2 = {x=57, y=30}, - belly = {x=36, y=31}, - back = {x=29, y=17}, - head = {x=23, y=7}, +tiles["npc/humanoid_orc_orc_blood_mage.png"] = { base=64, + feet = {x=34, y=59}, + hand1 = {x=11, y=36}, + hand2 = {x=57, y=37}, + back = {x=33, y=17}, + belly = {x=34, y=32}, + head = {x=32, y=5}, } -tiles["npc/humanoid_human_pyromancer.png"] = { base=64, - feet = {x=35, y=59}, - hand1 = {x=19, y=7}, - hand2 = {x=41, y=32}, - belly = {x=34, y=29}, +tiles["npc/humanoid_human_lumberjack.png"] = { base=64, + feet = {x=33, y=61}, + hand1 = {x=17, y=37}, + hand2 = {x=44, y=37}, back = {x=32, y=17}, + belly = {x=33, y=31}, head = {x=32, y=4}, } tiles["npc/humanoid_naga_lady_zoisla_the_tidebringer.png"] = { base=64, feet = {x=36, y=61}, hand1 = {x=9, y=-28}, hand2 = {x=49, y=-20}, - belly = {x=30, y=-6}, back = {x=30, y=-19}, + belly = {x=30, y=-6}, head = {x=30, y=-33}, } tiles["npc/humanoid_yeek_yeek_mindslayer.png"] = { base=64, feet = {x=31, y=60}, hand1 = {x=23, y=34}, hand2 = {x=24, y=41}, - belly = {x=30, y=37}, back = {x=31, y=22}, + belly = {x=30, y=37}, head = {x=31, y=11}, } tiles["npc/humanoid_human_great_gladiator.png"] = { base=64, feet = {x=30, y=60}, hand1 = {x=17, y=35}, hand2 = {x=47, y=32}, - back = {x=29, y=11}, belly = {x=31, y=25}, + back = {x=29, y=11}, head = {x=29, y=0}, } tiles["npc/humanoid_orc_gorbat__supreme_wyrmic_of_the_pride.png"] = { base=64, feet = {x=38, y=60}, hand1 = {x=18, y=14}, hand2 = {x=53, y=45}, - belly = {x=36, y=38}, back = {x=34, y=25}, + belly = {x=36, y=38}, head = {x=33, y=12}, } tiles["npc/humanoid_human_townsfolk_singing_happy_drunk01_64.png"] = { base=64, feet = {x=36, y=58}, hand1 = {x=9, y=40}, hand2 = {x=52, y=16}, - belly = {x=30, y=31}, back = {x=27, y=21}, + belly = {x=30, y=31}, head = {x=23, y=10}, } -tiles["npc/humanoid_orc_orc_soldier.png"] = { base=64, - feet = {x=36, y=62}, - hand1 = {x=9, y=39}, - hand2 = {x=58, y=36}, - belly = {x=34, y=34}, - back = {x=29, y=21}, - head = {x=25, y=9}, -} -tiles["npc/humanoid_human_townsfolk_farmer_maggot01_64.png"] = { base=64, - feet = {x=36, y=60}, - hand1 = {x=11, y=31}, - hand2 = {x=51, y=25}, - belly = {x=36, y=37}, - back = {x=35, y=25}, - head = {x=34, y=11}, +tiles["npc/humanoid_orc_orc_fighter.png"] = { base=64, + feet = {x=30, y=60}, + hand1 = {x=7, y=8}, + hand2 = {x=57, y=39}, + back = {x=35, y=20}, + belly = {x=31, y=34}, + head = {x=39, y=10}, } dolls.race_orc = dolls.race_orc or {} dolls.race_orc.all = { base=64, feet = {x=34, y=62}, hand1 = {x=13, y=37}, hand2 = {x=52, y=38}, - back = {x=32, y=23}, belly = {x=33, y=34}, + back = {x=32, y=23}, head = {x=32, y=9}, } tiles["npc/humanoid_orc_krogar.png"] = { base=64, feet = {x=32, y=59}, hand1 = {x=14, y=0}, hand2 = {x=53, y=13}, - belly = {x=29, y=35}, back = {x=29, y=25}, + belly = {x=29, y=35}, head = {x=30, y=11}, } -tiles["npc/humanoid_yeek_yeek_commoner_01.png"] = { base=64, - feet = {x=33, y=61}, - hand1 = {x=14, y=39}, - hand2 = {x=48, y=38}, - belly = {x=33, y=32}, - back = {x=30, y=19}, - head = {x=30, y=10}, +tiles["npc/humanoid_orc_orc_mother.png"] = { base=64, + feet = {x=33, y=59}, + hand1 = {x=10, y=37}, + hand2 = {x=53, y=35}, + back = {x=30, y=9}, + belly = {x=30, y=31}, + head = {x=28, y=-6}, } -tiles["npc/humanoid_human_fire_wyrmic.png"] = { base=64, - feet = {x=31, y=60}, - hand1 = {x=12, y=30}, - hand2 = {x=49, y=36}, - back = {x=28, y=19}, - belly = {x=28, y=28}, - head = {x=24, y=9}, +tiles["npc/humanoid_orc_grushnak__battlemaster_of_the_pride.png"] = { base=64, + feet = {x=30, y=61}, + hand1 = {x=10, y=40}, + hand2 = {x=56, y=36}, + back = {x=30, y=18}, + belly = {x=30, y=32}, + head = {x=30, y=11}, } -tiles["npc/humanoid_human_enthralled_slave.png"] = { base=64, - feet = {x=33, y=58}, - hand1 = {x=16, y=35}, - hand2 = {x=47, y=36}, - back = {x=32, y=18}, - belly = {x=32, y=29}, - head = {x=31, y=5}, +tiles["npc/humanoid_human_cryomancer.png"] = { base=64, + feet = {x=37, y=59}, + hand1 = {x=22, y=6}, + hand2 = {x=44, y=32}, + belly = {x=36, y=28}, + back = {x=35, y=16}, + head = {x=34, y=5}, } -tiles["npc/humanoid_human_rej_arkatis.png"] = { base=64, - feet = {x=34, y=59}, - hand1 = {x=24, y=17}, - hand2 = {x=38, y=19}, - belly = {x=33, y=26}, - back = {x=33, y=15}, - head = {x=32, y=4}, +tiles["npc/humanoid_halfling_halfling_slinger.png"] = { base=64, + feet = {x=27, y=57}, + hand1 = {x=16, y=15}, + hand2 = {x=55, y=30}, + belly = {x=31, y=42}, + back = {x=33, y=32}, + head = {x=36, y=19}, } tiles["npc/humanoid_shalore_rhaloren_inquisitor.png"] = { base=64, feet = {x=33, y=61}, hand1 = {x=9, y=17}, hand2 = {x=56, y=22}, - belly = {x=31, y=26}, back = {x=30, y=14}, + belly = {x=31, y=26}, head = {x=28, y=6}, } tiles["npc/humanoid_human_multihued_wyrmic.png"] = { base=64, feet = {x=33, y=59}, hand1 = {x=13, y=39}, hand2 = {x=49, y=38}, - belly = {x=31, y=30}, back = {x=30, y=17}, + belly = {x=31, y=30}, head = {x=30, y=8}, } tiles["npc/humanoid_human_agrimley_the_hermit.png"] = { base=64, feet = {x=35, y=60}, hand1 = {x=23, y=36}, hand2 = {x=54, y=22}, - back = {x=35, y=17}, belly = {x=35, y=30}, + back = {x=35, y=17}, head = {x=35, y=5}, } tiles["npc/humanoid_human_meranas__herald_of_angolwen.png"] = { base=64, feet = {x=34, y=60}, hand1 = {x=16, y=40}, hand2 = {x=52, y=39}, - belly = {x=35, y=31}, back = {x=34, y=21}, + belly = {x=35, y=31}, head = {x=31, y=4}, } -tiles["npc/humanoid_yaech_yaech_hunter.png"] = { base=64, - feet = {x=6, y=7}, - hand1 = {x=33, y=50}, - hand2 = {x=59, y=37}, - belly = {x=23, y=29}, - back = {x=34, y=29}, - head = {x=46, y=19}, +tiles["npc/humanoid_orc_orc_elite_fighter.png"] = { base=64, + feet = {x=31, y=60}, + hand1 = {x=11, y=37}, + hand2 = {x=53, y=29}, + back = {x=29, y=16}, + belly = {x=31, y=33}, + head = {x=29, y=4}, } tiles["npc/humanoid_orc_kra_tor_the_gluttonous.png"] = { base=64, feet = {x=38, y=57}, hand1 = {x=20, y=18}, hand2 = {x=38, y=-2}, - belly = {x=29, y=27}, back = {x=27, y=14}, + belly = {x=29, y=27}, head = {x=23, y=1}, } -tiles["npc/humanoid_human_cutpurse.png"] = { base=64, - feet = {x=36, y=59}, - hand1 = {x=9, y=34}, - hand2 = {x=46, y=34}, - back = {x=29, y=18}, - belly = {x=30, y=29}, - head = {x=28, y=7}, +tiles["npc/humanoid_human_gladiator.png"] = { base=64, + feet = {x=35, y=58}, + hand1 = {x=13, y=35}, + hand2 = {x=53, y=36}, + belly = {x=35, y=31}, + back = {x=34, y=19}, + head = {x=32, y=7}, } -tiles["npc/humanoid_human_the_possessed.png"] = { base=64, - feet = {x=30, y=62}, - hand1 = {x=9, y=17}, - hand2 = {x=47, y=37}, - belly = {x=33, y=35}, - back = {x=33, y=17}, - head = {x=33, y=4}, +tiles["npc/humanoid_human_ice_wyrmic.png"] = { base=64, + feet = {x=30, y=61}, + back = {x=32, y=19}, + hand2 = {x=58, y=17}, + belly = {x=32, y=33}, + hand1 = {x=9, y=36}, + head = {x=36, y=11}, } tiles["npc/humanoid_human_townsfolk_filthy_street_urchin01_64.png"] = { base=64, feet = {x=35, y=59}, hand1 = {x=12, y=32}, hand2 = {x=49, y=40}, - belly = {x=33, y=32}, back = {x=31, y=21}, + belly = {x=33, y=32}, head = {x=27, y=7}, } tiles["npc/humanoid_orc_icy_orc_wyrmic.png"] = { base=64, feet = {x=34, y=61}, hand1 = {x=21, y=21}, hand2 = {x=57, y=36}, - belly = {x=32, y=32}, back = {x=32, y=20}, + belly = {x=32, y=32}, head = {x=26, y=7}, } tiles["npc/humanoid_human_urkis__the_high_tempest.png"] = { base=64, feet = {x=35, y=60}, hand1 = {x=13, y=32}, hand2 = {x=51, y=29}, - belly = {x=33, y=24}, back = {x=33, y=9}, + belly = {x=33, y=24}, head = {x=33, y=-4}, } -tiles["npc/humanoid_halfling_master_slinger.png"] = { base=64, - feet = {x=39, y=59}, - hand1 = {x=15, y=42}, - hand2 = {x=47, y=10}, - back = {x=31, y=27}, - belly = {x=32, y=38}, - head = {x=28, y=14}, +tiles["npc/humanoid_yeek_yeek_commoner_05.png"] = { base=64, + feet = {x=31, y=61}, + hand1 = {x=12, y=22}, + hand2 = {x=24, y=35}, + back = {x=30, y=21}, + belly = {x=32, y=34}, + head = {x=30, y=12}, } -tiles["npc/humanoid_orc_orc_assassin.png"] = { base=64, - feet = {x=36, y=60}, - hand1 = {x=4, y=30}, - hand2 = {x=40, y=3}, - belly = {x=42, y=29}, - back = {x=33, y=20}, - head = {x=24, y=11}, +tiles["npc/humanoid_human_sand_wyrmic.png"] = { base=64, + feet = {x=29, y=60}, + hand1 = {x=10, y=38}, + hand2 = {x=58, y=17}, + back = {x=32, y=19}, + belly = {x=31, y=30}, + head = {x=37, y=8}, } tiles["npc/humanoid_yaech_blood_master.png"] = { base=64, feet = {x=22, y=60}, hand1 = {x=6, y=17}, hand2 = {x=50, y=27}, - belly = {x=31, y=32}, back = {x=29, y=25}, + belly = {x=31, y=32}, head = {x=36, y=14}, } tiles["npc/humanoid_dwarf_dwarven_summoner.png"] = { base=64, feet = {x=38, y=59}, hand1 = {x=14, y=44}, hand2 = {x=54, y=44}, - belly = {x=39, y=41}, back = {x=39, y=24}, + belly = {x=39, y=41}, head = {x=39, y=7}, } -tiles["npc/humanoid_human_trickster.png"] = { base=64, - feet = {x=36, y=60}, - back = {x=35, y=17}, - hand2 = {x=5, y=32}, - belly = {x=37, y=29}, - head = {x=30, y=6}, +tiles["npc/humanoid_human_argoniel.png"] = { base=64, + feet = {x=30, y=59}, + hand1 = {x=9, y=2}, + hand2 = {x=57, y=9}, + belly = {x=32, y=22}, + back = {x=32, y=10}, + head = {x=32, y=-3}, } tiles["npc/humanoid_human_townsfolk_boilcovered_wretch01_64.png"] = { base=64, feet = {x=35, y=58}, hand1 = {x=19, y=38}, hand2 = {x=50, y=39}, - belly = {x=34, y=34}, back = {x=34, y=21}, + belly = {x=34, y=34}, head = {x=28, y=10}, } -tiles["npc/humanoid_human_human_sun_paladin.png"] = { base=64, - feet = {x=32, y=60}, - hand1 = {x=14, y=32}, - hand2 = {x=46, y=32}, - back = {x=31, y=17}, - belly = {x=31, y=29}, - head = {x=31, y=5}, +tiles["npc/humanoid_human_derth_guard.png"] = { base=64, + feet = {x=37, y=59}, + hand1 = {x=10, y=18}, + hand2 = {x=48, y=33}, + belly = {x=36, y=29}, + back = {x=34, y=17}, + head = {x=34, y=5}, } tiles["npc/humanoid_orc_orc_high_cryomancer.png"] = { base=64, feet = {x=36, y=60}, hand1 = {x=7, y=17}, hand2 = {x=57, y=31}, - belly = {x=37, y=32}, back = {x=33, y=22}, + belly = {x=37, y=32}, head = {x=26, y=8}, } -tiles["npc/humanoid_human_derth_guard.png"] = { base=64, - feet = {x=37, y=59}, - hand1 = {x=10, y=18}, - hand2 = {x=48, y=33}, +tiles["npc/humanoid_human_necromancer.png"] = { base=64, + feet = {x=27, y=61}, + hand1 = {x=18, y=16}, + hand2 = {x=40, y=41}, back = {x=34, y=17}, - belly = {x=36, y=29}, - head = {x=34, y=5}, + belly = {x=32, y=32}, + head = {x=36, y=3}, } tiles["npc/humanoid_orc_orc_elite_berserker.png"] = { base=64, feet = {x=34, y=61}, hand1 = {x=19, y=21}, hand2 = {x=54, y=31}, - belly = {x=30, y=32}, back = {x=33, y=17}, + belly = {x=30, y=32}, head = {x=33, y=7}, } -tiles["npc/humanoid_human_spectator03.png"] = { base=64, - feet = {x=32, y=61}, - hand1 = {x=16, y=38}, - hand2 = {x=44, y=38}, - belly = {x=30, y=30}, - back = {x=30, y=19}, - head = {x=30, y=4}, +tiles["npc/humanoid_yeek_yeek_commoner_06.png"] = { base=64, + feet = {x=30, y=61}, + hand1 = {x=10, y=22}, + hand2 = {x=23, y=36}, + back = {x=31, y=22}, + belly = {x=31, y=36}, + head = {x=30, y=13}, +} +tiles["npc/humanoid_human_townsfolk_battlescarred_veteran01_64.png"] = { base=64, + feet = {x=35, y=57}, + hand1 = {x=16, y=39}, + hand2 = {x=52, y=39}, + back = {x=33, y=19}, + belly = {x=33, y=30}, + head = {x=32, y=7}, +} +tiles["npc/humanoid_human_townsfolk_pitiful_looking_beggar01_64.png"] = { base=64, + feet = {x=41, y=60}, + hand1 = {x=25, y=32}, + belly = {x=41, y=33}, + back = {x=40, y=20}, + head = {x=31, y=10}, } tiles["npc/humanoid_shalore_elven_cultist.png"] = { base=64, feet = {x=29, y=60}, hand1 = {x=8, y=9}, hand2 = {x=52, y=9}, - belly = {x=31, y=28}, back = {x=30, y=16}, + belly = {x=31, y=28}, head = {x=31, y=6}, } -tiles["npc/humanoid_human_townsfolk_pitiful_looking_beggar01_64.png"] = { base=64, - feet = {x=41, y=60}, - belly = {x=41, y=33}, - back = {x=40, y=20}, - hand1 = {x=25, y=32}, - head = {x=31, y=10}, -} tiles["npc/humanoid_shalore_elven_tempest.png"] = { base=64, feet = {x=35, y=60}, hand1 = {x=11, y=9}, hand2 = {x=49, y=36}, - belly = {x=31, y=28}, back = {x=32, y=16}, + belly = {x=31, y=28}, head = {x=33, y=7}, } -tiles["npc/humanoid_dwarf_ziguranth_warrior.png"] = { base=64, - feet = {x=34, y=59}, - hand1 = {x=18, y=43}, - hand2 = {x=50, y=44}, - back = {x=35, y=22}, - belly = {x=35, y=36}, - head = {x=35, y=7}, -} -tiles["npc/humanoid_human_bandit_lord.png"] = { base=64, - feet = {x=34, y=59}, - hand1 = {x=20, y=20}, - hand2 = {x=38, y=21}, +tiles["npc/humanoid_human_assassin.png"] = { base=64, + feet = {x=35, y=59}, + hand1 = {x=12, y=16}, + hand2 = {x=51, y=33}, + belly = {x=34, y=32}, back = {x=31, y=16}, - belly = {x=32, y=30}, - head = {x=30, y=5}, + head = {x=29, y=5}, } tiles["npc/humanoid_shalore_archmage_tarelion.png"] = { base=64, feet = {x=33, y=59}, hand1 = {x=17, y=26}, hand2 = {x=45, y=26}, - belly = {x=33, y=18}, back = {x=31, y=0}, + belly = {x=33, y=18}, head = {x=30, y=-11}, } +tiles["npc/humanoid_human_bandit_lord.png"] = { base=64, + feet = {x=34, y=59}, + hand1 = {x=20, y=20}, + hand2 = {x=38, y=21}, + belly = {x=32, y=30}, + back = {x=31, y=16}, + head = {x=30, y=5}, +} tiles["npc/humanoid_human_celia.png"] = { base=64, feet = {x=28, y=59}, hand1 = {x=15, y=16}, hand2 = {x=50, y=-20}, - back = {x=29, y=-16}, belly = {x=28, y=2}, + back = {x=29, y=-16}, head = {x=29, y=-33}, } -tiles["npc/humanoid_human_blood_mage.png"] = { base=64, - feet = {x=32, y=61}, - hand1 = {x=13, y=42}, - hand2 = {x=51, y=18}, - back = {x=29, y=19}, - belly = {x=30, y=30}, - head = {x=28, y=5}, -} tiles["npc/humanoid_shalore_elven_mage.png"] = { base=64, feet = {x=31, y=61}, hand1 = {x=11, y=35}, hand2 = {x=50, y=39}, - belly = {x=31, y=30}, back = {x=31, y=18}, + belly = {x=31, y=30}, head = {x=30, y=6}, } +tiles["npc/humanoid_human_blood_mage.png"] = { base=64, + feet = {x=32, y=61}, + hand1 = {x=13, y=42}, + hand2 = {x=51, y=18}, + belly = {x=30, y=30}, + back = {x=29, y=19}, + head = {x=28, y=5}, +} tiles["npc/humanoid_human_geomancer.png"] = { base=64, feet = {x=26, y=59}, hand1 = {x=19, y=31}, hand2 = {x=42, y=6}, - back = {x=28, y=15}, belly = {x=27, y=28}, + back = {x=28, y=15}, head = {x=28, y=2}, } tiles["npc/humanoid_orc_orc_summoner.png"] = { base=64, feet = {x=35, y=61}, hand1 = {x=9, y=35}, hand2 = {x=57, y=34}, - belly = {x=32, y=34}, back = {x=28, y=18}, + belly = {x=32, y=34}, head = {x=27, y=7}, } tiles["npc/humanoid_human_fallen_sun_paladin_aeryn.png"] = { base=64, feet = {x=33, y=58}, hand1 = {x=15, y=20}, hand2 = {x=45, y=25}, - back = {x=32, y=0}, belly = {x=32, y=14}, + back = {x=32, y=0}, head = {x=31, y=-12}, } tiles["npc/humanoid_yeek_yeek_commoner_07.png"] = { base=64, feet = {x=33, y=61}, hand1 = {x=14, y=39}, hand2 = {x=48, y=36}, - belly = {x=33, y=37}, back = {x=30, y=23}, + belly = {x=33, y=37}, head = {x=31, y=11}, } -tiles["npc/humanoid_human_assassin.png"] = { base=64, - feet = {x=35, y=59}, - hand1 = {x=12, y=16}, - hand2 = {x=51, y=33}, - back = {x=31, y=16}, - belly = {x=34, y=32}, - head = {x=29, y=5}, -} tiles["npc/humanoid_orc_warmaster_gnarg.png"] = { base=64, feet = {x=40, y=60}, hand1 = {x=60, y=27}, hand2 = {x=58, y=28}, - belly = {x=43, y=29}, back = {x=32, y=18}, + belly = {x=43, y=29}, head = {x=21, y=9}, } tiles["npc/humanoid_yeek_yeek_wayist.png"] = { base=64, feet = {x=37, y=59}, hand1 = {x=14, y=34}, hand2 = {x=58, y=36}, - belly = {x=37, y=35}, back = {x=37, y=24}, + belly = {x=37, y=35}, head = {x=38, y=13}, } +tiles["npc/humanoid_dwarf_ziguranth_warrior.png"] = { base=64, + feet = {x=34, y=59}, + hand1 = {x=18, y=43}, + hand2 = {x=50, y=44}, + belly = {x=35, y=36}, + back = {x=35, y=22}, + head = {x=35, y=7}, +} tiles["npc/humanoid_human_high_slinger.png"] = { base=64, feet = {x=29, y=59}, hand1 = {x=15, y=36}, hand2 = {x=57, y=18}, - back = {x=31, y=15}, belly = {x=30, y=27}, + back = {x=31, y=15}, head = {x=33, y=6}, } tiles["npc/humanoid_human_thief.png"] = { base=64, feet = {x=27, y=60}, hand1 = {x=8, y=27}, hand2 = {x=43, y=30}, - belly = {x=27, y=30}, back = {x=24, y=19}, + belly = {x=27, y=30}, head = {x=18, y=7}, } tiles["npc/humanoid_shalore_mean_looking_elven_guard.png"] = { base=64, feet = {x=33, y=60}, + hand1 = {x=14, y=31}, back = {x=31, y=14}, belly = {x=32, y=27}, - hand1 = {x=14, y=31}, head = {x=31, y=6}, } -tiles["npc/humanoid_human_subject_z.png"] = { base=64, - feet = {x=32, y=59}, - hand1 = {x=8, y=11}, - hand2 = {x=55, y=11}, - belly = {x=31, y=27}, - back = {x=31, y=15}, - head = {x=32, y=3}, -} -tiles["npc/humanoid_human_hexer.png"] = { base=64, - feet = {x=33, y=61}, - hand1 = {x=26, y=25}, - hand2 = {x=37, y=25}, - back = {x=31, y=16}, - belly = {x=32, y=37}, - head = {x=31, y=3}, +tiles["npc/humanoid_human_linaniil_supreme_archmage.png"] = { base=64, + feet = {x=38, y=60}, + hand1 = {x=10, y=7}, + hand2 = {x=49, y=29}, + belly = {x=37, y=19}, + back = {x=35, y=1}, + head = {x=31, y=-13}, } tiles["npc/humanoid_orc_orc_baby.png"] = { base=64, feet = {x=41, y=40}, hand1 = {x=12, y=43}, hand2 = {x=30, y=44}, - belly = {x=39, y=26}, back = {x=31, y=21}, + belly = {x=39, y=26}, head = {x=19, y=22}, } -tiles["npc/humanoid_human_shadowblade.png"] = { base=64, - feet = {x=33, y=59}, - hand1 = {x=8, y=15}, - hand2 = {x=56, y=40}, - belly = {x=28, y=31}, - back = {x=34, y=21}, - head = {x=37, y=9}, +tiles["npc/humanoid_orc_orc_child.png"] = { base=64, + feet = {x=31, y=55}, + hand1 = {x=8, y=36}, + hand2 = {x=48, y=35}, + back = {x=29, y=24}, + belly = {x=30, y=32}, + head = {x=25, y=12}, } tiles["npc/humanoid_human_last_hope_guard.png"] = { base=64, feet = {x=31, y=60}, hand1 = {x=15, y=32}, hand2 = {x=45, y=28}, - belly = {x=30, y=26}, back = {x=31, y=15}, + belly = {x=30, y=26}, head = {x=31, y=5}, } -tiles["npc/humanoid_orc_young_orc.png"] = { base=64, - feet = {x=35, y=60}, - hand1 = {x=14, y=10}, - hand2 = {x=52, y=40}, - belly = {x=35, y=32}, - back = {x=32, y=19}, - head = {x=30, y=8}, -} -tiles["npc/humanoid_elenulach_thief.png"] = { base=64, - feet = {x=30, y=60}, +tiles["npc/humanoid_human_spectator02.png"] = { base=64, + feet = {x=30, y=59}, hand1 = {x=18, y=37}, - hand2 = {x=42, y=37}, - back = {x=30, y=17}, - belly = {x=30, y=32}, - head = {x=30, y=6}, + hand2 = {x=41, y=37}, + back = {x=30, y=16}, + belly = {x=31, y=30}, + head = {x=30, y=5}, } -tiles["npc/humanoid_human_harno__herald_of_last_hope.png"] = { base=64, - feet = {x=32, y=61}, - hand1 = {x=16, y=37}, - hand2 = {x=45, y=36}, - back = {x=30, y=15}, - belly = {x=31, y=31}, +tiles["npc/humanoid_orc_orc_high_pyromancer.png"] = { base=64, + feet = {x=29, y=60}, + hand1 = {x=9, y=32}, + hand2 = {x=58, y=18}, + back = {x=30, y=20}, + belly = {x=28, y=33}, + head = {x=39, y=10}, +} +tiles["npc/humanoid_human_shady_cornac_man.png"] = { base=64, + feet = {x=35, y=60}, + hand1 = {x=19, y=35}, + hand2 = {x=49, y=35}, + back = {x=32, y=17}, + belly = {x=33, y=29}, head = {x=29, y=4}, } tiles["npc/humanoid_dwarf_dwarven_paddlestriker.png"] = { base=64, feet = {x=39, y=60}, hand1 = {x=15, y=37}, hand2 = {x=57, y=33}, - belly = {x=35, y=35}, back = {x=33, y=22}, + belly = {x=35, y=35}, head = {x=33, y=6}, } -tiles["npc/humanoid_human_tannen.png"] = { base=64, - feet = {x=37, y=60}, - hand1 = {x=10, y=16}, - hand2 = {x=56, y=14}, - belly = {x=35, y=28}, - back = {x=31, y=15}, - head = {x=29, y=4}, +tiles["npc/humanoid_human_reaver.png"] = { base=64, + feet = {x=34, y=59}, + hand1 = {x=8, y=37}, + hand2 = {x=52, y=31}, + back = {x=29, y=17}, + belly = {x=29, y=29}, + head = {x=28, y=6}, } -tiles["npc/humanoid_orc_orc_high_pyromancer.png"] = { base=64, - feet = {x=29, y=60}, - hand1 = {x=9, y=32}, - hand2 = {x=58, y=18}, - belly = {x=28, y=33}, - back = {x=30, y=20}, - head = {x=39, y=10}, +tiles["npc/humanoid_halfling_master_slinger.png"] = { base=64, + feet = {x=39, y=59}, + hand1 = {x=15, y=42}, + hand2 = {x=47, y=10}, + belly = {x=32, y=38}, + back = {x=31, y=27}, + head = {x=28, y=14}, } tiles["npc/humanoid_human_apprentice_mage.png"] = { base=64, feet = {x=30, y=61}, hand1 = {x=15, y=38}, hand2 = {x=42, y=34}, - back = {x=29, y=16}, belly = {x=29, y=29}, + back = {x=29, y=16}, head = {x=29, y=5}, } -tiles["npc/humanoid_player_default.png"] = { base=64, - feet = {x=33, y=60}, - hand1 = {x=21, y=35}, - hand2 = {x=44, y=36}, - belly = {x=33, y=28}, - back = {x=32, y=14}, - head = {x=32, y=5}, +tiles["npc/humanoid_human_human_sun_paladin.png"] = { base=64, + feet = {x=32, y=60}, + hand1 = {x=14, y=32}, + hand2 = {x=46, y=32}, + belly = {x=31, y=29}, + back = {x=31, y=17}, + head = {x=31, y=5}, } tiles["npc/humanoid_human_townsfolk_aimless_looking_merchant01_64.png"] = { base=64, feet = {x=37, y=58}, hand1 = {x=14, y=48}, hand2 = {x=52, y=23}, - belly = {x=35, y=38}, back = {x=31, y=24}, - head = {x=29, y=6}, -} -tiles["npc/humanoid_orc_orc_archer.png"] = { base=64, - feet = {x=30, y=60}, - hand1 = {x=20, y=29}, - hand2 = {x=56, y=30}, - belly = {x=31, y=34}, - back = {x=30, y=20}, - head = {x=31, y=10}, + belly = {x=35, y=38}, + head = {x=29, y=6}, +} +tiles["npc/humanoid_yeek_yeek_commoner_08.png"] = { base=64, + feet = {x=33, y=61}, + hand1 = {x=14, y=39}, + hand2 = {x=48, y=36}, + back = {x=31, y=21}, + belly = {x=33, y=34}, + head = {x=31, y=11}, } tiles["npc/humanoid_dwarf_lumberjack.png"] = { base=64, feet = {x=34, y=60}, hand1 = {x=17, y=16}, hand2 = {x=49, y=41}, - belly = {x=35, y=38}, back = {x=34, y=27}, + belly = {x=35, y=38}, head = {x=34, y=8}, } -tiles["npc/humanoid_dwarf_dwarven_earthwarden.png"] = { base=64, - feet = {x=34, y=58}, - back = {x=36, y=22}, - hand2 = {x=51, y=36}, - hand1 = {x=20, y=37}, - belly = {x=35, y=37}, - head = {x=35, y=7}, +tiles["npc/humanoid_human_spectator03.png"] = { base=64, + feet = {x=32, y=61}, + hand1 = {x=16, y=38}, + hand2 = {x=44, y=38}, + back = {x=30, y=19}, + belly = {x=30, y=30}, + head = {x=30, y=4}, } -tiles["npc/humanoid_naga_naga_tidecaller.png"] = { base=64, - feet = {x=33, y=61}, - hand1 = {x=12, y=15}, - hand2 = {x=47, y=-14}, - belly = {x=30, y=7}, - back = {x=27, y=-6}, - head = {x=26, y=-17}, +tiles["npc/humanoid_human_townsfolk_squinteyed_rogue01_64.png"] = { base=64, + feet = {x=33, y=57}, + hand1 = {x=11, y=28}, + hand2 = {x=53, y=38}, + back = {x=33, y=20}, + belly = {x=33, y=33}, + head = {x=30, y=7}, } -tiles["npc/humanoid_human_lost_merchant.png"] = { base=64, - feet = {x=35, y=59}, - back = {x=32, y=24}, - belly = {x=35, y=36}, - hand1 = {x=16, y=47}, - head = {x=30, y=5}, +tiles["npc/humanoid_human_townsfolk_village_idiot01_64.png"] = { base=64, + belly = {x=37, y=34}, + feet = {x=35, y=58}, + back = {x=33, y=24}, + head = {x=32, y=8}, +} +tiles["npc/humanoid_human_slinger.png"] = { base=64, + feet = {x=38, y=59}, + hand1 = {x=10, y=18}, + hand2 = {x=50, y=36}, + back = {x=34, y=17}, + belly = {x=37, y=27}, + head = {x=34, y=5}, } tiles["npc/humanoid_human_valfred_loren.png"] = { base=64, feet = {x=29, y=57}, hand1 = {x=19, y=36}, hand2 = {x=45, y=22}, - belly = {x=29, y=25}, back = {x=29, y=17}, + belly = {x=29, y=25}, head = {x=28, y=6}, } tiles["npc/humanoid_elf_anorithil.png"] = { base=64, feet = {x=39, y=60}, hand1 = {x=12, y=15}, hand2 = {x=55, y=30}, - back = {x=38, y=18}, belly = {x=40, y=29}, + back = {x=38, y=18}, head = {x=29, y=4}, } tiles["npc/humanoid_orc_golbug_the_destroyer.png"] = { base=64, feet = {x=29, y=58}, hand1 = {x=9, y=35}, hand2 = {x=51, y=33}, - belly = {x=29, y=25}, back = {x=30, y=12}, + belly = {x=29, y=25}, head = {x=32, y=-1}, } -tiles["npc/humanoid_orc_orc_pyromancer.png"] = { base=64, - feet = {x=28, y=61}, - hand1 = {x=6, y=30}, - hand2 = {x=59, y=15}, - belly = {x=28, y=30}, - back = {x=30, y=17}, - head = {x=40, y=5}, +tiles["npc/humanoid_human_high_sun_paladin_aeryn.png"] = { base=64, + feet = {x=33, y=58}, + hand1 = {x=15, y=19}, + hand2 = {x=45, y=25}, + belly = {x=33, y=16}, + back = {x=32, y=1}, + head = {x=31, y=-12}, } -tiles["npc/humanoid_orc_orc_necromancer.png"] = { base=64, - feet = {x=32, y=59}, - hand1 = {x=7, y=39}, - hand2 = {x=53, y=36}, - belly = {x=30, y=32}, - back = {x=28, y=17}, - head = {x=29, y=6}, +tiles["npc/humanoid_human_martyr.png"] = { base=64, + feet = {x=37, y=60}, + hand1 = {x=17, y=29}, + hand2 = {x=46, y=31}, + back = {x=29, y=14}, + belly = {x=30, y=27}, + head = {x=28, y=3}, } -tiles["npc/humanoid_yeek_yeek_commoner_04.png"] = { base=64, - feet = {x=33, y=60}, - hand1 = {x=17, y=40}, - hand2 = {x=50, y=44}, - belly = {x=32, y=36}, - back = {x=32, y=25}, - head = {x=32, y=8}, +tiles["npc/humanoid_halfling_halfling_citizen.png"] = { base=64, + feet = {x=31, y=58}, + hand1 = {x=13, y=38}, + hand2 = {x=48, y=41}, + belly = {x=31, y=33}, + back = {x=30, y=22}, + head = {x=30, y=8}, } -tiles["npc/humanoid_human_fryjia_loren.png"] = { base=64, - feet = {x=32, y=58}, - hand1 = {x=17, y=31}, - hand2 = {x=45, y=41}, - back = {x=29, y=21}, - belly = {x=29, y=32}, - head = {x=29, y=8}, +tiles["npc/humanoid_human_melnela.png"] = { base=64, + feet = {x=33, y=59}, + hand1 = {x=17, y=8}, + hand2 = {x=58, y=30}, + back = {x=35, y=19}, + belly = {x=35, y=31}, + head = {x=33, y=6}, } tiles["npc/humanoid_orc_orc_grand_summoner.png"] = { base=64, feet = {x=29, y=60}, hand1 = {x=9, y=8}, hand2 = {x=55, y=35}, - belly = {x=32, y=30}, back = {x=34, y=18}, + belly = {x=32, y=30}, head = {x=36, y=7}, } tiles["npc/humanoid_orc_rak_shor__grand_necromancer_of_the_pride.png"] = { base=64, feet = {x=29, y=60}, hand1 = {x=6, y=39}, hand2 = {x=54, y=32}, - belly = {x=28, y=35}, back = {x=28, y=20}, + belly = {x=28, y=35}, head = {x=27, y=7}, } tiles["npc/humanoid_thalore_thalore_hunter.png"] = { base=64, feet = {x=31, y=59}, hand1 = {x=18, y=38}, hand2 = {x=52, y=31}, - belly = {x=33, y=30}, back = {x=33, y=18}, + belly = {x=33, y=30}, head = {x=32, y=8}, } tiles["npc/humanoid_human_ben_cruthdar__the_cursed.png"] = { base=64, feet = {x=36, y=61}, hand1 = {x=17, y=33}, hand2 = {x=46, y=19}, - back = {x=29, y=17}, belly = {x=30, y=32}, + back = {x=29, y=17}, head = {x=28, y=4}, } -tiles["npc/humanoid_human_high_sun_paladin_aeryn.png"] = { base=64, - feet = {x=33, y=58}, - hand1 = {x=15, y=19}, - hand2 = {x=45, y=25}, - back = {x=32, y=1}, - belly = {x=33, y=16}, - head = {x=31, y=-12}, +tiles["npc/humanoid_orc_orc_assassin.png"] = { base=64, + feet = {x=36, y=60}, + hand1 = {x=4, y=30}, + hand2 = {x=40, y=3}, + back = {x=33, y=20}, + belly = {x=42, y=29}, + head = {x=24, y=11}, } -tiles["npc/humanoid_human_slinger.png"] = { base=64, - feet = {x=38, y=59}, - hand1 = {x=10, y=18}, - hand2 = {x=50, y=36}, - belly = {x=37, y=27}, - back = {x=34, y=17}, - head = {x=34, y=5}, +tiles["npc/humanoid_human_human_farmer.png"] = { base=64, + feet = {x=32, y=60}, + hand1 = {x=11, y=20}, + hand2 = {x=44, y=38}, + belly = {x=31, y=31}, + back = {x=29, y=18}, + head = {x=29, y=5}, } dolls.race_elf = dolls.race_elf or {} dolls.race_elf.female = { base=64, feet = {x=34, y=61}, hand1 = {x=19, y=33}, hand2 = {x=46, y=33}, - belly = {x=34, y=27}, back = {x=33, y=14}, + belly = {x=34, y=27}, head = {x=31, y=3}, } tiles["npc/humanoid_naga_naga_nereid.png"] = { base=64, feet = {x=32, y=62}, hand1 = {x=25, y=40}, hand2 = {x=59, y=31}, - belly = {x=38, y=28}, back = {x=37, y=17}, + belly = {x=38, y=28}, head = {x=35, y=5}, } -tiles["npc/humanoid_yeek_yeek_commoner_08.png"] = { base=64, - feet = {x=33, y=61}, - hand1 = {x=14, y=39}, - hand2 = {x=48, y=36}, - belly = {x=33, y=34}, - back = {x=31, y=21}, - head = {x=31, y=11}, +tiles["npc/humanoid_orc_orc_master_assassin.png"] = { base=64, + feet = {x=31, y=60}, + hand1 = {x=4, y=39}, + hand2 = {x=59, y=32}, + back = {x=29, y=18}, + belly = {x=30, y=30}, + head = {x=28, y=8}, } tiles["npc/humanoid_human_homeless_fighter.png"] = { base=64, feet = {x=35, y=61}, hand1 = {x=6, y=34}, hand2 = {x=57, y=34}, - back = {x=33, y=25}, belly = {x=33, y=34}, + back = {x=33, y=25}, head = {x=32, y=9}, } -tiles["npc/humanoid_human_townsfolk_blubbering_idiot01_64.png"] = { base=64, - feet = {x=33, y=59}, - hand1 = {x=19, y=41}, - hand2 = {x=47, y=40}, - belly = {x=31, y=33}, - back = {x=31, y=19}, - head = {x=29, y=7}, +tiles["npc/humanoid_shalore_elven_blood_mage.png"] = { base=64, + feet = {x=32, y=59}, + hand1 = {x=15, y=36}, + hand2 = {x=46, y=36}, + back = {x=30, y=17}, + belly = {x=30, y=33}, + head = {x=30, y=7}, } tiles["npc/humanoid_orc_orc_berserker.png"] = { base=64, feet = {x=45, y=60}, hand1 = {x=11, y=36}, hand2 = {x=40, y=17}, - belly = {x=30, y=31}, back = {x=20, y=19}, + belly = {x=30, y=31}, head = {x=17, y=9}, } tiles["npc/humanoid_yaech_yaech_diver.png"] = { base=64, feet = {x=52, y=3}, hand1 = {x=9, y=56}, hand2 = {x=40, y=57}, - belly = {x=39, y=31}, back = {x=30, y=36}, + belly = {x=39, y=31}, head = {x=16, y=39}, } -tiles["npc/humanoid_human_martyr.png"] = { base=64, +tiles["npc/humanoid_human_tannen.png"] = { base=64, feet = {x=37, y=60}, - hand1 = {x=17, y=29}, - hand2 = {x=46, y=31}, - belly = {x=30, y=27}, - back = {x=29, y=14}, - head = {x=28, y=3}, + hand1 = {x=10, y=16}, + hand2 = {x=56, y=14}, + back = {x=31, y=15}, + belly = {x=35, y=28}, + head = {x=29, y=4}, } tiles["npc/humanoid_female_sluttymaid.png"] = { base=64, feet = {x=31, y=59}, hand1 = {x=24, y=30}, hand2 = {x=41, y=20}, - back = {x=35, y=12}, belly = {x=35, y=23}, + back = {x=35, y=12}, head = {x=36, y=0}, } tiles["npc/humanoid_halfling_derth_guard.png"] = { base=64, feet = {x=28, y=60}, hand1 = {x=22, y=13}, hand2 = {x=47, y=44}, - back = {x=34, y=31}, belly = {x=33, y=40}, + back = {x=34, y=31}, head = {x=37, y=16}, } tiles["npc/humanoid_orc_massok_the_dragonslayer.png"] = { base=64, feet = {x=34, y=58}, hand1 = {x=13, y=33}, hand2 = {x=55, y=35}, - belly = {x=33, y=28}, back = {x=32, y=12}, + belly = {x=33, y=28}, head = {x=29, y=-3}, } -tiles["npc/humanoid_human_reaver.png"] = { base=64, - feet = {x=34, y=59}, - hand1 = {x=8, y=37}, - hand2 = {x=52, y=31}, - belly = {x=29, y=29}, - back = {x=29, y=17}, - head = {x=28, y=6}, +tiles["npc/humanoid_human_arcane_blade.png"] = { base=64, + feet = {x=26, y=61}, + hand1 = {x=17, y=3}, + hand2 = {x=38, y=35}, + belly = {x=27, y=29}, + back = {x=27, y=22}, + head = {x=29, y=13}, } -tiles["npc/humanoid_human_alchemist.png"] = { base=64, - feet = {x=35, y=59}, - hand1 = {x=10, y=32}, - hand2 = {x=51, y=34}, - back = {x=33, y=19}, - belly = {x=34, y=32}, - head = {x=33, y=5}, +tiles["npc/humanoid_human_rogue.png"] = { base=64, + feet = {x=36, y=58}, + hand1 = {x=8, y=36}, + hand2 = {x=55, y=29}, + back = {x=30, y=21}, + belly = {x=30, y=31}, + head = {x=27, y=7}, } -tiles["npc/humanoid_orc_orc_grand_master_assassin.png"] = { base=64, - feet = {x=33, y=61}, - hand1 = {x=4, y=32}, - hand2 = {x=60, y=39}, - belly = {x=34, y=30}, - back = {x=35, y=18}, - head = {x=37, y=7}, +tiles["npc/humanoid_human_shadowblade.png"] = { base=64, + feet = {x=33, y=59}, + hand1 = {x=8, y=15}, + hand2 = {x=56, y=40}, + back = {x=34, y=21}, + belly = {x=28, y=31}, + head = {x=37, y=9}, } tiles["npc/humanoid_human_sun_paladin_guren.png"] = { base=64, feet = {x=35, y=60}, + hand1 = {x=21, y=35}, back = {x=30, y=15}, belly = {x=31, y=29}, - hand1 = {x=21, y=35}, head = {x=30, y=3}, } diff --git a/game/modules/tome/data/gfx/shockbolt/object/artifact/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/object/artifact/battleaxe_the_gaping_maw.png index ac7e122279686df7da85ce1e1e3f5bc81de8716c..93cffb9653b0c29d5d0ad56b2ff96f4fc49e46ce 100644 Binary files a/game/modules/tome/data/gfx/shockbolt/object/artifact/battleaxe_the_gaping_maw.png and b/game/modules/tome/data/gfx/shockbolt/object/artifact/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/object/artifact/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/object/artifact/tirakais_maul.png index 8ef0accf0a0b5c86bdd2c8fb477cf582658b896c..7a9b0464d9cb25f7a94a02bba0e41bb713da8859 100644 Binary files a/game/modules/tome/data/gfx/shockbolt/object/artifact/tirakais_maul.png and b/game/modules/tome/data/gfx/shockbolt/object/artifact/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad7034353f2784c2f079b226608e258df3714e2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..7579af9b8fb5eabb9b470e4e3f713b192a55ec9d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..e7202e81a4b938dada395c6940c85e26286fc9d9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..4226dd7d1df236b9e608534a48c0ec6d16af19a0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..662dc49edb9b17d28c5cdac4eb0a9f4d17c265f7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..bf760466a1e5f7733314555d98077a6705e95cde Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..81c38a37d37315430b52b9a8a0ada0a8e2e88131 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..9c5a8b2d8fa680281b7725b5524cdf56b6e0e885 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..bf73cdb716ce4d5254f5776fe2755cd2c8986330 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..c1b8553fcbeaa0b64d7ea47c76bd142a49a42e9b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..003a029b967c4bf05f00da3f05a03a302f66457c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..492bbb859eac6c807d14589f3d407822afffa1a7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..89934be00d42b9f5858e13de1c324f77966a4f34 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..79005dc74e6a284a1b6afb4397357141c97f8d35 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..1f3996bdea828dcfd789330396226c01aaee243f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..d91dd2c8686eefa5aad916c776195331e78aa563 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..8ed19e1285b7d2e5929b1f2006a692c207c99dd3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ae6d182097308c3492e2f58d3c4a277320247f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..3be092e2d82ed03c1b8bec8c1a662ad9c01c7d6a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee70049acf85e593e6ca0e34d27d17433927725 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2208c04c3f4148897e1c89151071f1a3005b4b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..c631fb9cac35e8cc1b4e35f5e8cc5e354305d549 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..55222f083eb730b674e38792152da6950afd9aa0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..a46f1185ae4221bc1af2eeb7f5b59c08af18a843 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd258492ede13bc4a1d44547546ff41fc96c2da Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..da82d308fc28934a85a59aa7d4be01e741adc3e3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad3e1e9814d7cbb95241cf9ba02ea1ca5770638 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..3af14372ffa286c14974f7db3000b1e346292b21 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..94ccf7ba149c4555ac65f19a0a1a2a17df6668a1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..e3fa3df042df5671bdfdaf3b24115bd84a14284f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..790a497569d8632b59579b2376da0a90dac7e431 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..48da7180ede3fd978940d00d977272fe2444edb0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..401327bc71c153ef6fe55c55e95e468406e06985 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..585cd9c6334da8fffc2265e19f3a6d28500b3452 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..a734ea8890accec52eb9ce03587740bcf3a3616b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fac5e9dc7828918bf9bd6ce4a74c0feb35dfb6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..48bfebe9c908ddc34adc305c88ff6cfc55b47cbb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d00d649be1ecefbc82227fa3a48ba1fa3f5006 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..58b7fe9b979a8694c6d47300e6ed4f4724df5f6b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..3518862c1bb3c8ed2976c015627a93d7150160a2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..38baed7fcbb306bfa5c94d5690d4a44254d0eda3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..ea7d8de795d61f2a04273343d208de0527edb6f6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..3756ea164a252a50802f36a9d7ee73ca6ff30b2b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..590f7776d3777f94bb8dcd895614d11c7884ea91 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..cf529b2a717a62eeb8f1dbc376dc5445e87d018d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..0ccfad5b07176ac2ea3be61c17eef798f9800781 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..4e201e65730d76e456e46bbefd4cb2edf8519c68 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..740e73b959c577aee413ef5c48b8b822c5b8d564 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..4d71d83aad4cd596293791bbee299f73925919a9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5f39d4d18f3b6a67339d2a3dc66d373f54812a62 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..f75faacec42b972951ef9774a4e48e6ed62c70b4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..e4388e232812647aa6ab5b8ed1320636ba3f4128 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..38d9db2d271c1b9be1b9dd55472434e4556f01ff Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..2042f274e0702a32f92a2659b30e2dff751c3f5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..86add165b8e6ae329b0fc83aabe8a180288e091c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..1586157e23094bc19c8e0ad21c1b8eb3f715717a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc546fb7610e700deae77457a906fa48797005f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7894c8e5d613588349c7a3a673721de0fc1805 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..34c9197ae39d049aee1d75f054a2355ea00d4d0e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..32a354384c82f7abb060afd01d26966045238330 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..0cf804c4ce9483b68601ead144c1a97fb020d082 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..45ad9b507f2669edde9452a1467a4e68ae0157b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a4a4fde52f20587193976911a80f0faae4cf42 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..20c4ea2e2dad8df91ee4f6f2058c8b1c0386ac93 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd7acf36fa89223bca5239a9a45fad307cb49a9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..c341cac68a9949774de371700b4d1dbd0dcc308b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..78b0f309b52d4da7d208f034cf2641738948bdec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..426a94e5231e062f49d88165594d7a2b2aac7b4b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..705be38ad5b36d49ec8261646ff0091a937c618c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..109aafe7b2fc8a6b638c197e5f4ab8d46c6ce461 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..6ec6aab3771c99f8715310f856de2dd9938f9063 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..8caed562997c1ef0279f6139112e81a1d6d1cb62 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..92a5a3e9537689d0d5cc6be77ab4793b021832b0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_steel.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_steel.png new file mode 100644 index 0000000000000000000000000000000000000000..578e8ad4a4f9842c09d930103d28afa9a9405e4d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_steel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9a88ec02d8b2ce5029ba1a334688e0f5ed7957 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..c726d7f390aa71ad0073ca339b960da4912b485d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..183d1b7ce3dba28be31a9959a378d99bd53603e5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..cb692f205c843f57a2dea5653250e1c8c3ed8872 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..7968a6d91c68d6ff45b3a7fd2a907422dadc0511 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..604e18f3d75006c9f17f861d6acc2dd1df4ec28a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..7deafa34a31919da95789acaeb0e79149f8c9188 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..850c077fc78f213b594f0cc93cfae3b2484cf89e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..506777115d2ebc65b25b5bb67b179996b2c86771 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..d90e84c02c3bcc2e5b77f6740680b4371618825d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..ab33f3a050e54126c4f7a0a875aeaa3a2339d007 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..e79574ea784233bf6c7f68aa7710e925169c6493 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..dafbe677c708be44fb3dcad301dc59341d2bfe5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_female/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad7034353f2784c2f079b226608e258df3714e2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..7579af9b8fb5eabb9b470e4e3f713b192a55ec9d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..e7202e81a4b938dada395c6940c85e26286fc9d9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..4226dd7d1df236b9e608534a48c0ec6d16af19a0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..662dc49edb9b17d28c5cdac4eb0a9f4d17c265f7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..bf760466a1e5f7733314555d98077a6705e95cde Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..81c38a37d37315430b52b9a8a0ada0a8e2e88131 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..9c5a8b2d8fa680281b7725b5524cdf56b6e0e885 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..bf73cdb716ce4d5254f5776fe2755cd2c8986330 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..c1b8553fcbeaa0b64d7ea47c76bd142a49a42e9b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..003a029b967c4bf05f00da3f05a03a302f66457c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..492bbb859eac6c807d14589f3d407822afffa1a7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..89934be00d42b9f5858e13de1c324f77966a4f34 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..79005dc74e6a284a1b6afb4397357141c97f8d35 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..1f3996bdea828dcfd789330396226c01aaee243f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..d91dd2c8686eefa5aad916c776195331e78aa563 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..8ed19e1285b7d2e5929b1f2006a692c207c99dd3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ae6d182097308c3492e2f58d3c4a277320247f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..3be092e2d82ed03c1b8bec8c1a662ad9c01c7d6a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee70049acf85e593e6ca0e34d27d17433927725 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2208c04c3f4148897e1c89151071f1a3005b4b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..c631fb9cac35e8cc1b4e35f5e8cc5e354305d549 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..55222f083eb730b674e38792152da6950afd9aa0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..a46f1185ae4221bc1af2eeb7f5b59c08af18a843 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd258492ede13bc4a1d44547546ff41fc96c2da Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..da82d308fc28934a85a59aa7d4be01e741adc3e3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad3e1e9814d7cbb95241cf9ba02ea1ca5770638 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..3af14372ffa286c14974f7db3000b1e346292b21 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..94ccf7ba149c4555ac65f19a0a1a2a17df6668a1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..e3fa3df042df5671bdfdaf3b24115bd84a14284f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..790a497569d8632b59579b2376da0a90dac7e431 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..48da7180ede3fd978940d00d977272fe2444edb0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..401327bc71c153ef6fe55c55e95e468406e06985 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..585cd9c6334da8fffc2265e19f3a6d28500b3452 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..a734ea8890accec52eb9ce03587740bcf3a3616b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fac5e9dc7828918bf9bd6ce4a74c0feb35dfb6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..48bfebe9c908ddc34adc305c88ff6cfc55b47cbb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d00d649be1ecefbc82227fa3a48ba1fa3f5006 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..58b7fe9b979a8694c6d47300e6ed4f4724df5f6b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..3518862c1bb3c8ed2976c015627a93d7150160a2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..38baed7fcbb306bfa5c94d5690d4a44254d0eda3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..ea7d8de795d61f2a04273343d208de0527edb6f6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..3756ea164a252a50802f36a9d7ee73ca6ff30b2b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..590f7776d3777f94bb8dcd895614d11c7884ea91 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..cf529b2a717a62eeb8f1dbc376dc5445e87d018d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..0ccfad5b07176ac2ea3be61c17eef798f9800781 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..4e201e65730d76e456e46bbefd4cb2edf8519c68 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..740e73b959c577aee413ef5c48b8b822c5b8d564 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..4d71d83aad4cd596293791bbee299f73925919a9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5f39d4d18f3b6a67339d2a3dc66d373f54812a62 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..f75faacec42b972951ef9774a4e48e6ed62c70b4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..e4388e232812647aa6ab5b8ed1320636ba3f4128 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..38d9db2d271c1b9be1b9dd55472434e4556f01ff Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..2042f274e0702a32f92a2659b30e2dff751c3f5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..86add165b8e6ae329b0fc83aabe8a180288e091c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..1586157e23094bc19c8e0ad21c1b8eb3f715717a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc546fb7610e700deae77457a906fa48797005f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7894c8e5d613588349c7a3a673721de0fc1805 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..34c9197ae39d049aee1d75f054a2355ea00d4d0e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..32a354384c82f7abb060afd01d26966045238330 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..0cf804c4ce9483b68601ead144c1a97fb020d082 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..45ad9b507f2669edde9452a1467a4e68ae0157b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a4a4fde52f20587193976911a80f0faae4cf42 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..20c4ea2e2dad8df91ee4f6f2058c8b1c0386ac93 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd7acf36fa89223bca5239a9a45fad307cb49a9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..c341cac68a9949774de371700b4d1dbd0dcc308b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..78b0f309b52d4da7d208f034cf2641738948bdec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..426a94e5231e062f49d88165594d7a2b2aac7b4b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..705be38ad5b36d49ec8261646ff0091a937c618c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..109aafe7b2fc8a6b638c197e5f4ab8d46c6ce461 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..6ec6aab3771c99f8715310f856de2dd9938f9063 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..8caed562997c1ef0279f6139112e81a1d6d1cb62 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..92a5a3e9537689d0d5cc6be77ab4793b021832b0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_steel.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_steel.png new file mode 100644 index 0000000000000000000000000000000000000000..578e8ad4a4f9842c09d930103d28afa9a9405e4d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_steel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9a88ec02d8b2ce5029ba1a334688e0f5ed7957 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..c726d7f390aa71ad0073ca339b960da4912b485d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..183d1b7ce3dba28be31a9959a378d99bd53603e5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..cb692f205c843f57a2dea5653250e1c8c3ed8872 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..7968a6d91c68d6ff45b3a7fd2a907422dadc0511 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..604e18f3d75006c9f17f861d6acc2dd1df4ec28a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..7deafa34a31919da95789acaeb0e79149f8c9188 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..850c077fc78f213b594f0cc93cfae3b2484cf89e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..506777115d2ebc65b25b5bb67b179996b2c86771 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..d90e84c02c3bcc2e5b77f6740680b4371618825d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..ab33f3a050e54126c4f7a0a875aeaa3a2339d007 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..e79574ea784233bf6c7f68aa7710e925169c6493 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..dafbe677c708be44fb3dcad301dc59341d2bfe5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/dwarf_male/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..638eaf7d6eebc2e92d8027d9fba09167a2c1adde Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..9b45147f31d9be74c5af0a1b600b023c9fa01f0f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..900bceb3aa52ef0956d9acb3a9f6ff411dc12835 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..5da606e80201c26465d6be09ad6aa5d2de3f7bc6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..e689231f37bc29246e9e805791c7af02e9e828a3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..8e90c3a438b923a6490751dd1e1abb103aa75d2c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..dba2859c56eda197e74554ebfbe7e46a3761078c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..3617307a19a011b4db99e262fca26f38569c8d8c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..3299c399b56fa7837e88d2ea4d010d3e1e67ffcd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..4f8ea0ea816be1839525fc3976792efeb295ec4d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..223ede83cc02373b6b2a56a70885f9ad3c67b904 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..fab1db9cc1e1e316161873051567dbb013b924d5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..d2a5b20c3ea9641951544980c9c8e39a6f106f46 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..ab886d64ee5114a7539604b9f4f76d45dd6ca74f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..2b1ca9e4509bea15e4b969d5893aaee53720f00d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..a5898c0056926be71f61efc010fa3bfcfff4b9db Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..6295fe6821416835d463205dd74f8d5e4ce5b7ec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad304190b63aed647395fe5ffddae7e0b55c821 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..8bf5baf54d94361b5075498fc2f5ebd5506606d9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..f3bc9b2c2f4efa2162aa1e7fef7d12a21a1bebf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..a1516b5e030e5e57e70eafb4902134b1c6f8dbe7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..a9061bf3d1c30527c32f557087b93478fc7c681c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..152c752c4a2f86be268f98492ecd3f3dd925f284 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..2580bf3abe7f87ed5a9155970197c1dd724f343d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..7a0de0ae685ee096aa176fe7ea6721962a5a5114 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..7254ef022e8ee2c79bd28cd5e45dae9c056057f4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..220fd2d5602c9b2bde17d09c9e766cecac0c7748 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..199c59bb727997bd4c4fc2002e75aa6040369c5f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..88fa3daee97bc7fa56018eaa47720b8305e0205c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..6473cc48ecd84c67af556233e7c9510b5f3a63b9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..83f6b7620515bca0320a55bf59b78cb27695df42 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..b8b339602777f02fb501b16fe27643ecc868c6ba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..89fb5fafeb6838622924312745db7c3d2ad111b3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..a79bd4d862f49ec51723623e14ce2b456e172877 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..e56f7ebc0f352fc436847c557a271d631d33d2eb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..fb59caab9cb90c27b67a0bdf8ff13f9d857de1b8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..4bcccf0182f56a94d7c0ad266e13e601cb8e4010 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..4e751681cf3f768ee51575190cfb68da7eb8fe71 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6ab0df8e55af26e3710feaa057c0a938bf61aa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb21be25e08b47bfffa6c9a8599d21c730bc372 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..adc96c0c9b01a788608041b5f331a30b5e130bac Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..77f1f6267b6bf3455c451213c6673c39073e6abf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..8e931c7b37a310eeecd427d83ac02df8a81e99a7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..75810909b0fb0c41456c7d105083249c29a58fd8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..a91e682cbe6726885b18818704d74795356e5649 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..089d926682ae6b9a3ef854d2fdb44c2a74d1b95a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..95473956d6a5e33f6d509eb75c959a910f7882e7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..03a51297647d7fcedb305ad0108a9400ab50dd6e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..e81dd12d10f69f784f53e069df2e73b92fd23bc4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..840e300f30126292ae4380c875bb013bd6652903 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..b203199f49f07d565c6f6734ddbe5f4572c2f4bc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..454770d22ae2b3ea931776f51463764bf7f405e3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..1ab1c43d5acba686dcbd440e744877b068f963ec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..f862725e817739b605ea5f262803208b804823cf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..805e6eb6e8e68c49c1daa72a296945ee39f49fc3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..35c11755c7ab7987eb2e2931a90af4b66b3d705e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c480a351aa35d71f0e0db7f7cab3a2f36165d231 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..36e04a296a05f9403b56e364cca016d564e8cdf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..5532b182f95663b9a8239f3adac6dc0f0909b7e8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..89ce10417bac7797d52e63250a5443bb51adb15c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..47dcb751da8b11bd2b1bc608ed1fdc89a95bce94 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc6239fe884cae062f010a66c8f1ac19fa29bce Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..105fc85a5c931c2bf430e91d8fda1cbef70d0885 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..8efa67eadcfa9aa7252be53c257a2c5bb72e5882 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..7407724a78010b5e1d46e028a154da9a320b3d1e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3dca212b8d232427250fd0b8ac6ec62c3fcdb5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..44508ba0c72b779f1a6b56c6b418d681dd9a568e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..f4eb68d20641bcbb4a0d30e6623e44d72bb0a8b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..58547ae306c46baa3e7fdc059f0edd37ed850ff0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..ad4e916af95a53d82464f2b872f67c8cc320a8cf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..b7978a3c1facc52d6699068009a8b522f2bbc712 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..edcf2625276ec54e5dbc8063fc96dbb29e5fb2b1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..2ee41f4c4735cab7a23d1b86aab75b6bd1e4fd43 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..1b8e05121959a0dd8ebdfbe9d3f89f2829b755f9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..433aed5879ecb0f627f195c4e34ee530a96e77fc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..486e5f04ec29e151c8610868cf10021ae20e62cf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..3686d37861579f9e623a3468725512d9f7e4fc6c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..d745fc62e30d631a6ea64edcf4ab79f4a0a8bcba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..33b87aa9e16539d04260b80f1beb5f8b5af6b057 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..5c116d2266eff64d28665134c2fa4ad8a2a57efb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..593fda6de401089e840375af6c545dd0ddde3f7c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..fb9608fe3ac3a359a0fc4545849cfa30f5af81f8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..1303b3fe8c067b63b3bdd441ecd88bb6ba02332c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..c4afcb07cfc16a66f0eeec9b8199e7ada65844fe Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..587fdbf179307fdbc0a0bdf3844b7d90014fd2ff Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..190679d60d836790f394437a3ae3f7df62d53a27 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_female/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..e6078fdd9633643181b58583de92c57e1fc8f03d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..a3ef182aae3c751dea30b027c098d8983210bdcd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..561d50ca33f0dd3d78388e2509d6d9580c0b8520 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..c021f0c759bdfbb838d244b15494f717797b4243 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..c1deb0f37ea51cb40c503cd55634e4e0661b1977 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..c22087cdc0bbb934e9484ba46015260624542753 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..5b141f5223c6efede9ff7c6b1813c29251c1e715 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..be9fbf76af535b939c4ca9e623255f2d4f1a98b9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf6da52a8e5c48c412a5ac6e8744d9c8b5e738c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..8154bf9214a52687582af0c313b5ca2aad5db38e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..fe3faf450b9de53a9598cc1449f6c705f94426e1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..d41384ffed34d5f05175a39aecfe49016efbb4ff Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..67a5b437acd7c01ac581a5aa56067867cfa9d3cc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..b55fa78bb213c875de894974808fa0c27b5eb2fa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..33487c1329aa833aa4969ea92a8cf87db31afddf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..dfca81e63b78c6e8a1ec85db7943e7a3eacd2841 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..95d1d63218a8643bcc136457d6b374c21e99bd37 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..9663067c7042138ed1227eafbaa1f915f4f07083 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..c763363bdc430ebd8a1e988c35b73c4861a3ecbd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..43e60db570a28e1f7d89cfab479dcb0da4ef6115 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..ab1760ad70abd2fa76f04c1dc5ee702a1c8f8554 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..c5cc38a3ef8f6bd695eed29d26757c50f447cdf9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..198dcd717c278844b1fd9215a8cc644a49087f55 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..f18d918ec64e1f49d3dc358509fafa77221b894a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..8411313bea9da06a0d3aff248abdca7c32441aa0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..04b0effaf557918e0b750e7fd9a680be1540b873 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e43237708cd56456ce7ba62e42de9fa245964d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..ba35fed17a0021dfdd7720eb931e8bb18ccdbc17 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..4b1459ca41f8386d8e6f71457b5473fb23d26949 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..e08a44b07784a3f530fb8e9ebf5ec90124ad2301 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..2f3928558f4f0ddaebb6409b8f64815681785d1d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..e08b6bc984a0f06dde54ce604c01e214fbc2103f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..404118c617662c8d7c1eb97cd9577b49d614c6bf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..0c6f23d835bd593f4f04f772e6eb90f785b3c9a1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..c97c062d79896994e43a623b1ca419ef07cf2faf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..56ac2614bae9adc40af4bb87744b3b7a7890502c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..733347fbc5068901ea3e3b4cefa0e2aa29908835 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..6d41c89b43f762747d320ae85f49ee532c36da9a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..4e505444f57ea9faf766a41953b53d08b5267668 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..30ab0ffba79d50554ccc2d3857be3f8fd8522871 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..dea79fed9f77e4a2c4d892389dd3cda8f924fdae Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..3361293f3883d6258b8a729db3d5f76781230d64 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..a925adf9203b48f8811649b742ee7659933166aa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..3b1f490bebb9a5f315423b0153f086be55337173 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..481feb5a05c00b7cae1ad75935e0ce95e52a2d0a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..b92c048c385e41ed7da31019c003f2b9bcfecfce Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..92abf535dae2e27d645babee183065073ad5afe9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..2e1ade2530ad12a7b32dc42f11d035895d3f6ea0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..67c72d6988c58574bcaa5562d89795111c675b12 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..b352122e32ed569696c62a9c3b72fa953807f9a4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..40733f4941aeeb78fa85a8980beacecc7be3cc02 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..9b7d21783d1c8de5d9e55a52661d2cf3c477d386 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..0b133016c545c865002fbf2519787d7bfb45c6db Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..01b584e5db39abb44da9f9b70b1da5b33ff2726d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..8f4b56e34f2b7e0d7eb0307588a6cbbaf67a9879 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..be3ecf86c71136a36cdef30e5a0e9baa78c88cf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..8e094bce2ca72f5535b26b8e329eb9f9bf7b2026 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..0d2eb2b3d8bdf4e39927c3595312ad69a92c73df Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..423066b6f766384e9e690a1df560008a2dd4f6ed Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac10b1630804d0bf09e8f39f7a33ca5dfa6e366 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..4c5582b8219f15bbee433858733596d315d2eaee Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..aca10cccfd6fcab0b0353f2cdfcd2557a9033cec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..36e59a1553e6ec2f01cbbf6d6f0c7b64b2e5f2ab Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..23e9257e1e1fa67be9c66ab505b545ba577d5cd0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..3d18024a1f50d3e2ae1fb7a2aa98f592196662b8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..b7e8879e9e7958cb34c08559e0a74c01cde91f69 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..497876837f875f01211fddfa70373d2d75f84bd7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..0d9a5a1b2c52cea9a2799da6db25ab93c594ab0b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1a71fc39c7cb99d9e5e830d153ebf9df9e36a3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..c40f70795482b224f4c347cc0afbe52633c57023 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..3fcda329f7778ccc18ec47eba6f2b406d052af49 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..18519f7484b9fd53c0b87554732b2612547f4d04 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..63b93bd974d018109d9c05a0cb2cb9797e986303 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..1f2db3343b6affc11e03e8025b73f7c13bc37ee4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..1f89de8dba4b5179ae80d98ec3460b2bbcb80fa0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd0aed8ff6f435e6223646f3861e8a7b7bbe15a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..93ebadb164883d89d9f3de1c38d00d49f6070fa6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..2d84d2f5c5c43780df4ff1c0cc0f6e816d13781d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..0f5418ffda6ee684c33b0bbf2abb88f211287206 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0a97ed711d1974d42f4baf3fd378eb8c2693d4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..b2caa0dbce03330cd9b046c43e1c502cc97802f4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3a32d7c74ff4a96e18c001b6de89508ca84b57 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b9105fc6741d5a490504080df02158fd77f335 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..8cc9861de7aef38b5fd3c6b661763c604eee9f15 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..889c0d0c5e621af2fa2485470fb139f02b950254 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..6a730b172ffdc56ab510e2a92e51340e96d5d8f5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/elf_male/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..4bf22ebf8f515a866d586e4f1acd05ebc9682379 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..32396820bb14707666cba0334e06ca5e9c8d4215 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..0b16a7ccd24026e4e8ed73af4783f3531e69b849 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..b786a94b67d436cd82a0a3af28a6702675701b2c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..0c7498cf8d1832a301f93af0127886bb2d50ebf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..71dd540399da4360488c5d050e3620fb8f88adf2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..6e621f636b7c2c67ef2dcc81b798cdba11bae508 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..b33d59502e053768dea7b32446a41f23935a48fd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..6a7417410259b29b8d6439cfb9910fdeeb3b58fb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..9b9d068803980046e1480a6c2cde65635921c779 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..bd45c567256d2afe0ef0dee8e2fd80b61301d036 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..3f1cd76158ddb88626d7a7c7061dbfa79d92fe78 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..437a71fe15fca118294f4932d45764fc0a9e2a00 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..e636bae493a070a9c46f04b2b1fc0d92ee39d686 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..51825e5b559dccc2095e07fa0a6f86cbf0fe7d46 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..b075b7a2a9b6445c6c777bbaf096c8d03178cb90 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..cf99672e005c25a270745df0def9e806533ed292 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..49dbd63cc30b684b2896199f418b0009797a3ea3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..68f4aac385611c488b6bd51da949c4c4d891089b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..1cb6759af0ec7d3ec58b41fd88c650d980d77ef1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..000f77783f3856f05313a21754beab6f3a6f4e10 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc9963e7af29174e4268308185e1e326005d335 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..100cdd758f7dd2f908bbde06d683c1db6b9d511c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..fd69a1cee23ca9b21b0ebb682eed30923fd80a9b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..b83538077cac910266e0cb618fd5558cdb1fdd32 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..923226a8bd6b56cbb43cd4652e07866cc5765ade Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..83e0a32ca27b8246da39e0c27615651c38367a4d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..f56bf50f816c609ec10106892723bc233d742dbb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..7abf230e6ad34a659f69ce9dc2e0bc2da94dc4a8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..34cacb9f5adb15db6286bf510bf32e704c5a1f9a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..41383ebe96c962330b766bb7e47849c467073c2d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..fc5cc57210dcdbbd69b67bddb86876fbb6d3deb9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..e5015e7291bc4cbb6a78362f6980024795b7e0ab Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..4d30e357d9ab52c5d217143756a8752d79bd0151 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..a19d1b496da53350e5941c88151919cc01e20e58 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..afc18de08ca464297ed06224047e0a933e91eac7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..756d4af7a246fe068ef2971365abcd1c0e8b4415 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc73fc8d531b11e7774dbae7cc6954f6392d107 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..034511006c6201f4344f9956ec03cfca8e8ea67e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..8eb0467d6b40428ece2bffea69eb3c322747a9a6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3466ef75a9b2559a0b9499ad80e79f6b6a82c8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..ccc49f21ca4d4b610cda4b3a40af49d14d89798a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..b99bd2c9f8cfa32493042d4bba6daccf440dd3bd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..a129628ed033060a0265c820a7103d765d8ed2fa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..02160d644f13e7071a5584da5808a8e303dccc89 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..7aed9c7a080ab4e5c3a0724bdae0a22128695bdd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..34eaf7a5f6107237dfa6561fec0a8f7eaf0f856c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..54a59dcd998a68fbcd0a7d120e479fb134489770 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..1010eed734bb1f22513ecb35a732bd59c3c3b739 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6ecdde64d0943238147cdd67fa8f3fc087e096b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..df66c0be24806b996dd79fde2f1980ee03df6232 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..097bca27afc85b1d7116389a0e244e10679370c2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..ae2510c18b3eca8b2c6ab7112bae0c105595a536 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..78c03fde790fc3d6059c499f52558a167b77db0a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..760c356c544c5fd378282d487ada560e62effd26 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..597bf315a0399b802ab6a6226386d6b67b2edfb2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..8b0c77f6d154f00a52bbe63fd256a4f06e0f8c54 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..408b6db3f4a7ba8a12052cc9bf207321fd3d5117 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..5e01c804989868b2e3bc515b67004d538fcced28 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..8ac7ee5945df092d72ace36e1c468f3d7a7faaf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..1dacbf380be024ce71e7b65f6c94be4d9c76669b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..21a5e6166577e6aae1523506b8045a632437e6ba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..99866aab7ece26a4d519e464e474fad68872f73b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..1e0cf88b8652e5b52805e14f0f0cd8d647256703 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..83986359fa991d3bb1cc2ff43ca7594cda0c944b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..f59cd5cb5fbdead2593f19eb198c3aab8180fcaa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f476017f6b6e52d8257b283f4edfe1255e19b2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..b3d10a155db54c585b8fab9124607410db6eeece Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..e81d8332724956e6d9bc62afd5c8e0716b11948e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..3d94e24a5488464fa7fe1fd107a92ba408f6018c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..b3d8fd93f4e99778ee8210c3ed6ef104624b1580 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..53d360c5f4b54b479e8c231a7dfc9f4ae38e183c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..f007f4ff9a27ab096f12cf0bbe336f87d43d50fc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..759befdd6e5cca28fab42e8cde6325c5608e09c0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..4ddbef001f404a38a102bf0e173cd55955f55f56 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..bbc51a681d8507cb27bd0b270281ea4d1b6b1416 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7e09fb93cb0149a573ae3b187810716c372e9e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..fca691334b0a661ff5325b4e12b2789d414ffb9b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..28d29ff4c47a9d007a78d69a64f500d267079778 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..c418b798d0bc9a4d81deceb1f4611b074383cb1f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..f36961dce36d6ecb879ed081df90c51232d5be31 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..60395d27fcdad21a71ae5840a2bf031ce64de5ff Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..9b60e232167d8c25b7655a684a33941260d90b2d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..96b86e9c134b4dfb09457130910835924a77dfd9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..c2e4996a2858dfb5ce6cf65193cffc400a8d9cf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..0648bee61da6dddd35d39855dc4784208b934c78 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/ghoul/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..0496dbbf98da223dab87753bcc11cce0dfdc1f0a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..8cdd88c0bd5f3fbff732949e722d59e8cfe3a0ba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..6e6a50f88f9ccaec099f150a932c88b0b819ec57 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..6f8b9ef2e649eb4d38548a6b48767d3ec63ca186 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..eefa4a1eeaacd1c502d86be13da212436bfb203f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..aa47ded8ca2a213a5e4ba27851052f1775d2b171 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..dffb1a4a7bd3e43cbffbfb482f65161ac9a66b55 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..3881210762855e98b90c656da94106310c16fe3f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..aed91c5d57fac7a31d9e9aa360447cacc49e97a0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..f200928926c7f6684252fb8be398ec2a67de0718 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..992046fd5d183c3371c3b00c1d1e3fd5d3a4df36 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..9170a5ac897ec216d7da0993e3a317cc8c682600 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..87cc659bb03ec9816a6cea6371ad2931e40d8614 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..6c6de77b5301589244c2e81e6080725101ced5c3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..b7f5504e8ed0e52dceb92bc80e104558c15a1eca Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..ce6b4ba93f0f41fa5840ef8ab2aacbbe891c3810 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..43f0de531c901b5e7c034877a5409d31cc67fe88 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..6e734133daa22b999243b4c479cc54e9ea0a3921 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2ffc9a96963d849ef2d5083f10ae45105aa5ce Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd13a3da985ea473d41df36677dea45cfc93383 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..7e6088cb3a75883afacdd006d88c4fc58840125d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..37c643a6e78f9bf7b075f031aa6f1a255299166e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..24dc04349598f4566bdee4ef10c568e0d53ad95d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..d73bb37e392699738aa62e723f6c27727e103bf7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..aeb0c93ffc78ccfc988ba6f873b7b36268cec877 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..dd97922d32363e328e01486fd76a288375b89ce8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..262fdbcc97eb17838a4124037358b841216e5be2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..a5903a4718e71cf856004ef43480af1af70d0ca2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..0ca7375284d60b7454338a8baf6379fc5e416aea Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..0239aa8228f600657c3afd302799313d039942b1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..7988930710667d7e0c2f43265b2324c2963cbb32 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..e64342934aa0aaaa29ff4cf2865f816bb1a022ab Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..28e3908cae438618d7f045d94be7599052c998d6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..55b36108a4835b172a184c410ca438c832d625b7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..a05f726b8758a09ed862bcbe4b5546a24c622ab6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..9715ddc4a6323c31d71f3926e1f0ff7a831b6ab1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..50fdf16de43d5f5d73eaa4101648e9ef549649fb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..fafd00cdcdc5a49a1bb6af23d357c91dbb131897 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..8f0c1db242bbc7546cdc9cd8f6b416eb4e9c9238 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..56d74e7f2bc1108816af6d141a9bb9aebf247d26 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..a065f5a6e6fbbb1ba20f6d633596b8e4ad22974a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..edc00629ecb99490a1867a3c81eb1db3dc982dfa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..16a06b36e3d9a56f641f41fc3f2b587220611472 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..00d774ea66a98d3659c3e5a4e7ce429b1e147516 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..ba9fa7e27b3a4f7d07eca0ccaa3444df0cdd4259 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..39f73e7a4d493324bef99514433644abed3a54a5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..fa972f0c06a99b0f0bb3fbefd84ab76b24332359 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..59d45e9e5a7a8556fe0c41b862109981c7d0071d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c69555ca5e76a9c8e529f0159cfb9acff2c0ed Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..29bc0cf999d7dcd1f2d0051a73b5a59b1472ec58 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..edaf2dfb431751ae19684fc0ca46a1c4964847ba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..e837355509a5f2d828d69df3d47c3ec119ad75bc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..13450b92cca499a7c9e8b8d6c3e9c7396113af38 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..ee5d7a703fe5787310b7a15f396123bcef9296d6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..2b36a9370dba9389747bbf5511bde7165da7b529 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..782341ae30fa0ad9f36195805c35de90271c4632 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..1c25f3b4691d7dc1b582b715c685e6c1f8cebada Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad173c45d36c3df692b5a1f5aaa78f3489c455e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..05515b89f06cef10b337f5af7d2e6296b90d97d9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..6e6a9ba286f990e11597922962edb5733042ae47 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..04d4e724009d514ec59b0db4abeb8091a13caf3f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..9a90c4ed5178bec2cbfa378c525e0c0a59220ea7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..db8836c957e6d86b68c445f4890c0da7ce86222b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..2395348a07f09bc6d4173d7f5bab1b275cb219f2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..8f405bc2e34d8871347cc67e30f591c239f78ba0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..3d83a6c0c420aef5dcd9e7a49fadeeb24c0eb70b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..a0187a460879b16bea56d033d5762ab3e3f512ae Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..25cb908b1e19abef9a08830e7f2937a1cd080a6c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..5c5dd72652ea797db54f3a473be90d2dc47935a8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..4c533db92050eb45c5e521201d767f20db0c398c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..575ec205cc0a867140c7845f7c7fe74c76ec73d4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..98616c9c279f1e65025a091ce594971eca75e834 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..206ed87a87615597150b8a3a6d4e3e33fc6acfa2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..7952888a5815e740c6172d9e1ce458fa64e2915b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..d3d25b83cc45c14dda81649bb946c783a116764a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..f058ecc09aa88db55769bc298ff50691594c7c4a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..ec4acc853941f0d0122ba9428589961d5200f8e1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..1b590dfec0689d028424c22ce0d86ef14796cd87 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..7527a1b4ec7f03297da4ff4e9089bf70f86785ca Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..b06f80a4673cf389245557b8a344687001bec55b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..535e057039dd2c5135e2d1c38fced59c5455fb4c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..18f9569bb06f903390e7e52d3c97b5c9ef59c8c2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..3399d3695f0a872611496a2e3f81642131b14713 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..9f1b2c971e1666729d5b75a50376543530a991c0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..96838a7f4295e588b4a956903c886f09d7a80f07 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..18153f496aae528005a6404514e3e643eb450491 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_female/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..0496dbbf98da223dab87753bcc11cce0dfdc1f0a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..8cdd88c0bd5f3fbff732949e722d59e8cfe3a0ba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..6e6a50f88f9ccaec099f150a932c88b0b819ec57 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..6f8b9ef2e649eb4d38548a6b48767d3ec63ca186 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..eefa4a1eeaacd1c502d86be13da212436bfb203f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..aa47ded8ca2a213a5e4ba27851052f1775d2b171 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..dffb1a4a7bd3e43cbffbfb482f65161ac9a66b55 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..3881210762855e98b90c656da94106310c16fe3f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..aed91c5d57fac7a31d9e9aa360447cacc49e97a0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..f200928926c7f6684252fb8be398ec2a67de0718 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..992046fd5d183c3371c3b00c1d1e3fd5d3a4df36 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..9170a5ac897ec216d7da0993e3a317cc8c682600 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..87cc659bb03ec9816a6cea6371ad2931e40d8614 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..6c6de77b5301589244c2e81e6080725101ced5c3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..b7f5504e8ed0e52dceb92bc80e104558c15a1eca Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..ce6b4ba93f0f41fa5840ef8ab2aacbbe891c3810 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..43f0de531c901b5e7c034877a5409d31cc67fe88 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..6e734133daa22b999243b4c479cc54e9ea0a3921 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2ffc9a96963d849ef2d5083f10ae45105aa5ce Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd13a3da985ea473d41df36677dea45cfc93383 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..7e6088cb3a75883afacdd006d88c4fc58840125d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..37c643a6e78f9bf7b075f031aa6f1a255299166e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..24dc04349598f4566bdee4ef10c568e0d53ad95d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..d73bb37e392699738aa62e723f6c27727e103bf7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..aeb0c93ffc78ccfc988ba6f873b7b36268cec877 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..dd97922d32363e328e01486fd76a288375b89ce8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..262fdbcc97eb17838a4124037358b841216e5be2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..a5903a4718e71cf856004ef43480af1af70d0ca2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..0ca7375284d60b7454338a8baf6379fc5e416aea Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..0239aa8228f600657c3afd302799313d039942b1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..7988930710667d7e0c2f43265b2324c2963cbb32 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..e64342934aa0aaaa29ff4cf2865f816bb1a022ab Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..28e3908cae438618d7f045d94be7599052c998d6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..55b36108a4835b172a184c410ca438c832d625b7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..a05f726b8758a09ed862bcbe4b5546a24c622ab6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..9715ddc4a6323c31d71f3926e1f0ff7a831b6ab1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..50fdf16de43d5f5d73eaa4101648e9ef549649fb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..fafd00cdcdc5a49a1bb6af23d357c91dbb131897 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..8f0c1db242bbc7546cdc9cd8f6b416eb4e9c9238 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..56d74e7f2bc1108816af6d141a9bb9aebf247d26 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..a065f5a6e6fbbb1ba20f6d633596b8e4ad22974a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..edc00629ecb99490a1867a3c81eb1db3dc982dfa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..16a06b36e3d9a56f641f41fc3f2b587220611472 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..00d774ea66a98d3659c3e5a4e7ce429b1e147516 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..ba9fa7e27b3a4f7d07eca0ccaa3444df0cdd4259 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..39f73e7a4d493324bef99514433644abed3a54a5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..fa972f0c06a99b0f0bb3fbefd84ab76b24332359 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..59d45e9e5a7a8556fe0c41b862109981c7d0071d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c69555ca5e76a9c8e529f0159cfb9acff2c0ed Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..29bc0cf999d7dcd1f2d0051a73b5a59b1472ec58 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..edaf2dfb431751ae19684fc0ca46a1c4964847ba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..e837355509a5f2d828d69df3d47c3ec119ad75bc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..13450b92cca499a7c9e8b8d6c3e9c7396113af38 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..ee5d7a703fe5787310b7a15f396123bcef9296d6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..2b36a9370dba9389747bbf5511bde7165da7b529 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..782341ae30fa0ad9f36195805c35de90271c4632 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..1c25f3b4691d7dc1b582b715c685e6c1f8cebada Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad173c45d36c3df692b5a1f5aaa78f3489c455e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..05515b89f06cef10b337f5af7d2e6296b90d97d9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..6e6a9ba286f990e11597922962edb5733042ae47 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..04d4e724009d514ec59b0db4abeb8091a13caf3f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..9a90c4ed5178bec2cbfa378c525e0c0a59220ea7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..db8836c957e6d86b68c445f4890c0da7ce86222b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..2395348a07f09bc6d4173d7f5bab1b275cb219f2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..8f405bc2e34d8871347cc67e30f591c239f78ba0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..3d83a6c0c420aef5dcd9e7a49fadeeb24c0eb70b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..a0187a460879b16bea56d033d5762ab3e3f512ae Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..25cb908b1e19abef9a08830e7f2937a1cd080a6c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..5c5dd72652ea797db54f3a473be90d2dc47935a8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..4c533db92050eb45c5e521201d767f20db0c398c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..575ec205cc0a867140c7845f7c7fe74c76ec73d4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..98616c9c279f1e65025a091ce594971eca75e834 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..206ed87a87615597150b8a3a6d4e3e33fc6acfa2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..7952888a5815e740c6172d9e1ce458fa64e2915b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..d3d25b83cc45c14dda81649bb946c783a116764a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..f058ecc09aa88db55769bc298ff50691594c7c4a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..ec4acc853941f0d0122ba9428589961d5200f8e1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..1b590dfec0689d028424c22ce0d86ef14796cd87 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..7527a1b4ec7f03297da4ff4e9089bf70f86785ca Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..b06f80a4673cf389245557b8a344687001bec55b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..535e057039dd2c5135e2d1c38fced59c5455fb4c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..18f9569bb06f903390e7e52d3c97b5c9ef59c8c2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..3399d3695f0a872611496a2e3f81642131b14713 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..9f1b2c971e1666729d5b75a50376543530a991c0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..96838a7f4295e588b4a956903c886f09d7a80f07 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..18153f496aae528005a6404514e3e643eb450491 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/halfling_male/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..638eaf7d6eebc2e92d8027d9fba09167a2c1adde Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..9b45147f31d9be74c5af0a1b600b023c9fa01f0f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..900bceb3aa52ef0956d9acb3a9f6ff411dc12835 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..5da606e80201c26465d6be09ad6aa5d2de3f7bc6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..e689231f37bc29246e9e805791c7af02e9e828a3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..8e90c3a438b923a6490751dd1e1abb103aa75d2c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..dba2859c56eda197e74554ebfbe7e46a3761078c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..3617307a19a011b4db99e262fca26f38569c8d8c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..3299c399b56fa7837e88d2ea4d010d3e1e67ffcd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..4f8ea0ea816be1839525fc3976792efeb295ec4d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..223ede83cc02373b6b2a56a70885f9ad3c67b904 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..fab1db9cc1e1e316161873051567dbb013b924d5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..d2a5b20c3ea9641951544980c9c8e39a6f106f46 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..ab886d64ee5114a7539604b9f4f76d45dd6ca74f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..2b1ca9e4509bea15e4b969d5893aaee53720f00d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..a5898c0056926be71f61efc010fa3bfcfff4b9db Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..6295fe6821416835d463205dd74f8d5e4ce5b7ec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad304190b63aed647395fe5ffddae7e0b55c821 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..8bf5baf54d94361b5075498fc2f5ebd5506606d9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..f3bc9b2c2f4efa2162aa1e7fef7d12a21a1bebf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..a1516b5e030e5e57e70eafb4902134b1c6f8dbe7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..a9061bf3d1c30527c32f557087b93478fc7c681c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..152c752c4a2f86be268f98492ecd3f3dd925f284 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..2580bf3abe7f87ed5a9155970197c1dd724f343d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..7a0de0ae685ee096aa176fe7ea6721962a5a5114 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..7254ef022e8ee2c79bd28cd5e45dae9c056057f4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..220fd2d5602c9b2bde17d09c9e766cecac0c7748 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..199c59bb727997bd4c4fc2002e75aa6040369c5f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..88fa3daee97bc7fa56018eaa47720b8305e0205c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..6473cc48ecd84c67af556233e7c9510b5f3a63b9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..83f6b7620515bca0320a55bf59b78cb27695df42 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..b8b339602777f02fb501b16fe27643ecc868c6ba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..89fb5fafeb6838622924312745db7c3d2ad111b3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..a79bd4d862f49ec51723623e14ce2b456e172877 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..e56f7ebc0f352fc436847c557a271d631d33d2eb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..fb59caab9cb90c27b67a0bdf8ff13f9d857de1b8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..4bcccf0182f56a94d7c0ad266e13e601cb8e4010 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..4e751681cf3f768ee51575190cfb68da7eb8fe71 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6ab0df8e55af26e3710feaa057c0a938bf61aa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb21be25e08b47bfffa6c9a8599d21c730bc372 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..adc96c0c9b01a788608041b5f331a30b5e130bac Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..77f1f6267b6bf3455c451213c6673c39073e6abf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..8e931c7b37a310eeecd427d83ac02df8a81e99a7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..75810909b0fb0c41456c7d105083249c29a58fd8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..a91e682cbe6726885b18818704d74795356e5649 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..089d926682ae6b9a3ef854d2fdb44c2a74d1b95a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..95473956d6a5e33f6d509eb75c959a910f7882e7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..03a51297647d7fcedb305ad0108a9400ab50dd6e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..e81dd12d10f69f784f53e069df2e73b92fd23bc4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..840e300f30126292ae4380c875bb013bd6652903 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..b203199f49f07d565c6f6734ddbe5f4572c2f4bc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..454770d22ae2b3ea931776f51463764bf7f405e3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..1ab1c43d5acba686dcbd440e744877b068f963ec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..f862725e817739b605ea5f262803208b804823cf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..805e6eb6e8e68c49c1daa72a296945ee39f49fc3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..35c11755c7ab7987eb2e2931a90af4b66b3d705e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c480a351aa35d71f0e0db7f7cab3a2f36165d231 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..36e04a296a05f9403b56e364cca016d564e8cdf5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..5532b182f95663b9a8239f3adac6dc0f0909b7e8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..89ce10417bac7797d52e63250a5443bb51adb15c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..47dcb751da8b11bd2b1bc608ed1fdc89a95bce94 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc6239fe884cae062f010a66c8f1ac19fa29bce Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..105fc85a5c931c2bf430e91d8fda1cbef70d0885 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..8efa67eadcfa9aa7252be53c257a2c5bb72e5882 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..7407724a78010b5e1d46e028a154da9a320b3d1e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..5f3dca212b8d232427250fd0b8ac6ec62c3fcdb5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..44508ba0c72b779f1a6b56c6b418d681dd9a568e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..f4eb68d20641bcbb4a0d30e6623e44d72bb0a8b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..58547ae306c46baa3e7fdc059f0edd37ed850ff0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..ad4e916af95a53d82464f2b872f67c8cc320a8cf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..b7978a3c1facc52d6699068009a8b522f2bbc712 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..edcf2625276ec54e5dbc8063fc96dbb29e5fb2b1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..2ee41f4c4735cab7a23d1b86aab75b6bd1e4fd43 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..1b8e05121959a0dd8ebdfbe9d3f89f2829b755f9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..433aed5879ecb0f627f195c4e34ee530a96e77fc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..486e5f04ec29e151c8610868cf10021ae20e62cf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..3686d37861579f9e623a3468725512d9f7e4fc6c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..d745fc62e30d631a6ea64edcf4ab79f4a0a8bcba Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..33b87aa9e16539d04260b80f1beb5f8b5af6b057 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..5c116d2266eff64d28665134c2fa4ad8a2a57efb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..593fda6de401089e840375af6c545dd0ddde3f7c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..fb9608fe3ac3a359a0fc4545849cfa30f5af81f8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..1303b3fe8c067b63b3bdd441ecd88bb6ba02332c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..c4afcb07cfc16a66f0eeec9b8199e7ada65844fe Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..587fdbf179307fdbc0a0bdf3844b7d90014fd2ff Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_female/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..190679d60d836790f394437a3ae3f7df62d53a27 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_female/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..335e5a0b53cbd6ca27187e29db66238e7a361f70 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..3d57b1da30179750fc2c0799f32c8c46e6940cda Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..1b563525691c0e104687065b0e695f316fa5daf8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..bbbc274c240d1a1b850fe4669f6ca65677ed8254 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..369090aff0357bf8ab787902eb02ba8f0fe2999a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..29b1e2a4ca7676e26db31eb9a58b91b2707cc25c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..033491185e4e785fb67ca59500a541c991ff36ad Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..1dd7de3acae746c06f3d822bde39040d3bf36715 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..8b036cf565d205b19c4f2764c996cd318015a269 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..23645b31fee746f1793638b9b2fd6bcacb78b57b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f69201aac6eb01581fef2f060134b2c8e4b4f6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..e96106a74ab415570983f75134da47dd727cf97f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..30cd0312f4fadaa8d1f7ef4fd37f120f1bd58ade Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..1b8356c68e6b0e898a6f1c9f7d36f00ed98521a3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..6b7189f8750c0574643ca9bc7d23ae857573fcc2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..72efc32982c64d412c8e65da6f9ae82644a181ac Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..1fbfd94d3081b18b4f7edf4c207eb4a27008f620 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c2a581355e23756525a800c07184c060f865db5d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..c66c633fd2570a8f0014fd991543579a6ead70eb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..336e28fd51eaec47304587f9107ea2922bf0ac35 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca2fd9614dd55d519c75d330ae66ef59e34a1a5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..b46ae4584c13e5175693eb101d58ad6958d9a645 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..e3c27f857f7e9a818493427454e9204638866e3c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..daefc1f5bbc5af4e99422cf4e4880cb00404994c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..9bb6019ae7c37fdc92c0093ce14a0323b5137228 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..3c8ed2f16f44c74c7092250afaef7833d1140419 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..94e8e7ce6b0832da11dc2df7563e8f82bfc8380c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b49be83476cec5edc469ee6b91dc3cca66670d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ac217e97ee8b775bd14bc297f8ffe82611ac7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..d9fa78a46532d60538bcdd5b05b7f80392ba8cbf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd998908bde568561616e35d331fc7994337b12 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..73cf6ee010f30f7158fbbaca12ea575bb722079c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..706ebb85451c78548b81907ac354920c6725bc5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..cbfc1f34444ba83025294a0dee8518817d84bcea Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..b48eed834fbe3ff5e1e7f5648b04f2079f390565 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..ac24870f8ba41e92b8f2934ce7c9a29d03428523 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..690e2b3a448dc912bcd1cb622b97085abc43fb7a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..f9ad57d395a46750539bf9433b3c040ddebc2829 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..600cb1386201e9d95f7901637661db6004779d32 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..03e582d212336c85d4881fdf795f6630e920af9f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..2024309d5f72a00bd65b7e2d642037cdcab5377d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..7140f9a41b5da0ddbbc1815a6bbd57f2811a4855 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..e84d5b61e3617681155a13e167b11519c9708b27 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..32b4ac4b21cbd77808467ec7b2ffa7b8041198ed Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..9ddc36f955cb77a415e7a3299af10f705254007f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..0637a3accf6aa093868add5c6fcf5214ae6e1099 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..e71ef4d6b31cb98b78a4cf40fad15bff9e04880d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..42c2f02226b72ebf4ec208306b9de1a8212d6be6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..443aacb6b9aa32c703e32d5429bebc70f98aa89b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..f47f287a9b663462ecedefdd45e7821a64ea152f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..5492c8ac3ac4b8deb88da48ee0faf96051e58276 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..a91e9689f4ed7e53c47793301751cdef92539175 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd8f29bdbf8da42f3f019c3871a980f317fa957 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6908a1e7221ea8f0931600a35b0219d2a79e66 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..2bda7fa7aa5c384b110b14366b3ad23a8b49f714 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..32b6b337b1dc85c4e80f5d13f1ef1e68a72e117e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..ab7a088a71deae157e0a6e9dc6c97608a4ad7afc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..78ee2e654c3b0a17f5b334ea28d4344d7523b032 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..5149bee0a09cb3eadb934333c1f4d7f36a399954 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..38a6d104e3230d4ff26931ddcec73090ca9ebc59 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..f9cc5c45f62db024fdab9b398195ae7e66f0d2e1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..d16d4d88dfd3d721f8413e9749c7b2743ac390fa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..04f087624223db8da03c32bf9db16d1c8dd652fb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..3e68b867dd7126ef86fb63333204fc0e6ae5de60 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..94f840b12ec883ffc13d867a8a4d4b8df3f3e8b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..c5ef7613f79cecc3369c144ddc9e2694badb1a68 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..cbfecaec41ec39c0a82fbf09fbf00f1cc640343b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..defe25cf1219363490b80d16ee57deb5b191bb1a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..d3f358158b7ff419b1db03cf196821f8e652aa7a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..72ecc4fa93b47d130fe0329ba1f563471615fc8a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..c0170b27b6152541633eb0e9e65ce5492da91b32 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..67fecf7ff902236b1e59c7bb77b890d75e8a7d8b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..54588d347a615f49d03514003a2d2dbc614b5b34 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..43005b7ef094580e6389c9794ddc84c054a29405 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..5b904249ddbeb091a898986882162a121d10e6c8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..65968ee8198d9c90a73eda4ea9a2d55e6d0936f7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..b27c75b8b12e473db339bc69dc4606d03e4eec4a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2c41bd20eb6d2b33b621c36db953df49757668 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..b559677a149ef6f27fbaa528f7fcddb34f52ee43 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..45b97344fd5e40c32db09dbf2695d7177143f3da Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..086abc4b83a29bd08d6694cb758e67def0aac547 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..dbb27e7a8547c5b4b61e76cc6149ec14bb6b5101 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..a3937dc383f3149f18063511e783ae2bec3eddd3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..93873de243fc3401e9ebaa32fa6737bd6a4bcacb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..5a250617097a2244da9997e0c7bb2f4d8423b447 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/human_male/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..84aae6e12d7769dc0f331560f229609e695f7c6f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/human_male/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c230cd3141f2da46b499424d2e5f25c4e58252 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..2b5a2c2d9ef9a361e0d249c8e1d2b6380fa4d5d5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..87f58989160aaa99bc373115534f69c1eb62bb18 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..2f807c5163104f50fe105bd8eac3bfbe11753c0f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..d787b17d4bb715d6ddb29f806018c087ab9174b6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..895bf864d94bcbf9d848b4295758416c8cb6038b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..050ad28176402a3019e6e526742e37089287d69b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..30de5b8d08c576b292449815cee8d76c4505d296 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..f71c188eabdaee79d750a56ac890b67315d91c91 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..cff0e5f54c3fc59954ca1c4a63b7f08d8c3eb4d7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..29ab31c6e09c2498b72d5cab6932bf57dc227f0a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..846f121569907b0ddcc05101924a1f48c9a7e04d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..82a66e86b57dab3274fbe415a2b6a21a139df418 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..a01bc0ece8b49572bf52b9535a480ed1fbc1d013 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..2f09a177a9fb54920e6753c75ce8c401827d9138 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..4e44f610d734bf6e34171cfe566c4ff1a43c137a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..2ed2ba7ae19a1bfcab6083ec0b148d5baa7c6e58 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f7ca0e3f9b3f2b0ac63a70597299f401740061 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..97366c76765459c1b26dbd786046f39208fefcc1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..a51da3a8eca3e7d0b2d804ee5c4ed304c2d50604 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..d8fa929d9aba290e4cc98a1f7a183fc03518b579 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..c5c4a0786484ff3033c43121fc7b5c77c3fbe643 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..1c2e61596aaa893abda750d9d70835907bb35c62 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..4c950e7f1577b33310ae9f5e4dc10dadeac72b05 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..313d25c9c7b3a0336d846ee418f877e54e453f29 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..cf269d814f263565bcb345eb5b8326ade8bdfa40 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..9a45d81db7c38df72855099e8f0004057de9725c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..155a449e77be0546ddc26c5df166ab0c9f590765 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..fcde880e5692c5ea7dce020940daebcaad4d718f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..8479d54a18fa372e15fe0380bbeb0e8026cf00e9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..2740bf63d235ef23da4d63855001b459594ee5c7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..79986975c1a7c8ea0daf17671e66fbf582f9c3e7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..0b96478bb44ff86f3b72684fd7b66ef46f7c8c4b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..936bac541a6af2cab0e139ab1785f925a94804cb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..0e0c5d561c5e7ba810f6fd52e15f614fffffc983 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..165e62a5157a9bb3db3376b948676edfcd421563 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..07569358f0d03f5e2e8264dbb17454339c998d75 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..3698b973b3e35ead1f934d71ec081d91e52388f3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..a2233ca4bddb00285097b1303f179e488c4ccf0d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..4b2d672995e457c4e95d473707f59836612feb18 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f505645f4e45e28dbfa943ecfafc03c6a8ccd0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..58ae199964cd599563aa25261ef4a6ce2d580b39 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..5b08665a3d04f1035a45d6838d61a27ca35b9b18 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..5dc8a4c88c78f9c9b464500d2dfb37b4a79aa65a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..06a10a81b02a39888b7ce6656e9ac1761ac7b50f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..444afce1bb5be751120859ee7a057ed74e1bb44e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..0908d0d0f0bc964335d9c62c7f531ee7f52598b4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..55c19748359562ba1b419f76de99ef7f1a577f11 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..25446888e61ac392008350b268e0528ae8de0277 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..ce8d4e969aca80fd411d80117f2b7c3bfa84ce14 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..50a5d1d4727101f99e4b5c2a8d75e953e2bce35b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..96e037adae55efa776ec64d271acf7a955bd556f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..86bbd3280cc81d0645ea75130ee4452595590b55 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dffdb4c3c38e50b4126061bec3ae8ea2ce0fc99 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..17c1b4e63a177106e200b82ab3de1b0e641f78d6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..e7964b82a61a9f0684bbd69000e21e988d10d442 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..db352a47f5221f70bdbf2f5e2588940c6bfe4498 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..9783899c19043236cc546ebbdd8a6a77a2bef500 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..ea5abf439660caf3f459b87ee61af63e2f8913c8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..5dae69c8188bbdca344bc14b84071a0b5c83f115 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf5cbd195c8dbcfaab3034b6d225e985d7f0bbb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..275f3d0bcd2bea49ef840e8fd02e56fdcc8beca1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..ab6d45a84696d8a464d95fe2d59ece52e1ed3a27 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..599918d6b2eaffd1f81cf12b7a6ae452a5233761 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..f30ae49b1d0dcb97806f4919dff12b9ce091844b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..01dc8e4f59df437c40101c065e311ce32032fae6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..5a4dc7d31f11481b9b249c79b94d131823a474e0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..b33cf209b125e99489019a694225d009493bae09 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..fe83b76b226285d76d27e6582acf6003099aa0b0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..62167be0aedffd1e01e96ae505259f5d78681206 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..6d8d709b28ca05ceaf88bac68855777f9f66919c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..dcc00bab084d966554e8b11243a71533c91db422 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..00ba13ae7323a3f1187086d6e74298332a0503ca Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..bd0dd97b0dca352241a2c8a2d4cdc79b0071141f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..f6ef91f7204c9ea1b3b418bfda09b09470952499 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..ef289881abd14a7c31d84372c24a50dde86ed87a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..67c20b32b99407ab2ade14d5ef0b99745cd67906 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..f5c5aecb7f9447616d2d467d396d10c7ee503a1b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..9a54908d2a273e048d69b3fc5fd036f73c3f0303 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..38f0525f1a84b67bdb02ecfd86d86e97e812d35e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..9224b780badb052d47b96eedced8fd88e2d7bdc8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..658a001ffc6d4a2244ea55c45af7744cbdc57b3d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..9ed9802b373cdfabfae3d9f34c19c231f4e8277b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..84926050f8eb3f6304bafb84d6799f91902c2257 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..99900f47bf9996767ff799e6dc6a8865fa4c5651 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/orc/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/orc/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..6932b8cc0915a86cc865bb1489c9585aa5ef681c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/orc/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad7034353f2784c2f079b226608e258df3714e2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..7579af9b8fb5eabb9b470e4e3f713b192a55ec9d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..e7202e81a4b938dada395c6940c85e26286fc9d9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..4226dd7d1df236b9e608534a48c0ec6d16af19a0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..662dc49edb9b17d28c5cdac4eb0a9f4d17c265f7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..bf760466a1e5f7733314555d98077a6705e95cde Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..81c38a37d37315430b52b9a8a0ada0a8e2e88131 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..9c5a8b2d8fa680281b7725b5524cdf56b6e0e885 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..bf73cdb716ce4d5254f5776fe2755cd2c8986330 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..c1b8553fcbeaa0b64d7ea47c76bd142a49a42e9b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..003a029b967c4bf05f00da3f05a03a302f66457c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..492bbb859eac6c807d14589f3d407822afffa1a7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..89934be00d42b9f5858e13de1c324f77966a4f34 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..79005dc74e6a284a1b6afb4397357141c97f8d35 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..1f3996bdea828dcfd789330396226c01aaee243f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..d91dd2c8686eefa5aad916c776195331e78aa563 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..8ed19e1285b7d2e5929b1f2006a692c207c99dd3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ae6d182097308c3492e2f58d3c4a277320247f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..3be092e2d82ed03c1b8bec8c1a662ad9c01c7d6a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee70049acf85e593e6ca0e34d27d17433927725 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2208c04c3f4148897e1c89151071f1a3005b4b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..c631fb9cac35e8cc1b4e35f5e8cc5e354305d549 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..55222f083eb730b674e38792152da6950afd9aa0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..a46f1185ae4221bc1af2eeb7f5b59c08af18a843 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd258492ede13bc4a1d44547546ff41fc96c2da Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..da82d308fc28934a85a59aa7d4be01e741adc3e3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad3e1e9814d7cbb95241cf9ba02ea1ca5770638 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..3af14372ffa286c14974f7db3000b1e346292b21 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..94ccf7ba149c4555ac65f19a0a1a2a17df6668a1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..e3fa3df042df5671bdfdaf3b24115bd84a14284f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..790a497569d8632b59579b2376da0a90dac7e431 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..48da7180ede3fd978940d00d977272fe2444edb0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..401327bc71c153ef6fe55c55e95e468406e06985 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..585cd9c6334da8fffc2265e19f3a6d28500b3452 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..a734ea8890accec52eb9ce03587740bcf3a3616b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fac5e9dc7828918bf9bd6ce4a74c0feb35dfb6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..48bfebe9c908ddc34adc305c88ff6cfc55b47cbb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d00d649be1ecefbc82227fa3a48ba1fa3f5006 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..58b7fe9b979a8694c6d47300e6ed4f4724df5f6b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..3518862c1bb3c8ed2976c015627a93d7150160a2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..38baed7fcbb306bfa5c94d5690d4a44254d0eda3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..ea7d8de795d61f2a04273343d208de0527edb6f6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..3756ea164a252a50802f36a9d7ee73ca6ff30b2b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..590f7776d3777f94bb8dcd895614d11c7884ea91 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..cf529b2a717a62eeb8f1dbc376dc5445e87d018d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..0ccfad5b07176ac2ea3be61c17eef798f9800781 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..4e201e65730d76e456e46bbefd4cb2edf8519c68 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..740e73b959c577aee413ef5c48b8b822c5b8d564 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..4d71d83aad4cd596293791bbee299f73925919a9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5f39d4d18f3b6a67339d2a3dc66d373f54812a62 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..f75faacec42b972951ef9774a4e48e6ed62c70b4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..e4388e232812647aa6ab5b8ed1320636ba3f4128 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..38d9db2d271c1b9be1b9dd55472434e4556f01ff Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..2042f274e0702a32f92a2659b30e2dff751c3f5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..86add165b8e6ae329b0fc83aabe8a180288e091c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..1586157e23094bc19c8e0ad21c1b8eb3f715717a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc546fb7610e700deae77457a906fa48797005f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7894c8e5d613588349c7a3a673721de0fc1805 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..34c9197ae39d049aee1d75f054a2355ea00d4d0e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..32a354384c82f7abb060afd01d26966045238330 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..0cf804c4ce9483b68601ead144c1a97fb020d082 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..45ad9b507f2669edde9452a1467a4e68ae0157b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a4a4fde52f20587193976911a80f0faae4cf42 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..20c4ea2e2dad8df91ee4f6f2058c8b1c0386ac93 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd7acf36fa89223bca5239a9a45fad307cb49a9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..c341cac68a9949774de371700b4d1dbd0dcc308b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..78b0f309b52d4da7d208f034cf2641738948bdec Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..426a94e5231e062f49d88165594d7a2b2aac7b4b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..705be38ad5b36d49ec8261646ff0091a937c618c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..109aafe7b2fc8a6b638c197e5f4ab8d46c6ce461 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..6ec6aab3771c99f8715310f856de2dd9938f9063 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..8caed562997c1ef0279f6139112e81a1d6d1cb62 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..92a5a3e9537689d0d5cc6be77ab4793b021832b0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_steel.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_steel.png new file mode 100644 index 0000000000000000000000000000000000000000..578e8ad4a4f9842c09d930103d28afa9a9405e4d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_steel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9a88ec02d8b2ce5029ba1a334688e0f5ed7957 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..c726d7f390aa71ad0073ca339b960da4912b485d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..183d1b7ce3dba28be31a9959a378d99bd53603e5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..cb692f205c843f57a2dea5653250e1c8c3ed8872 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..7968a6d91c68d6ff45b3a7fd2a907422dadc0511 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..604e18f3d75006c9f17f861d6acc2dd1df4ec28a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..7deafa34a31919da95789acaeb0e79149f8c9188 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..850c077fc78f213b594f0cc93cfae3b2484cf89e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..506777115d2ebc65b25b5bb67b179996b2c86771 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..d90e84c02c3bcc2e5b77f6740680b4371618825d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..ab33f3a050e54126c4f7a0a875aeaa3a2339d007 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..e79574ea784233bf6c7f68aa7710e925169c6493 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..dafbe677c708be44fb3dcad301dc59341d2bfe5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/runic_golem/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..335e5a0b53cbd6ca27187e29db66238e7a361f70 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..3d57b1da30179750fc2c0799f32c8c46e6940cda Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..1b563525691c0e104687065b0e695f316fa5daf8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..bbbc274c240d1a1b850fe4669f6ca65677ed8254 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..369090aff0357bf8ab787902eb02ba8f0fe2999a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..29b1e2a4ca7676e26db31eb9a58b91b2707cc25c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..033491185e4e785fb67ca59500a541c991ff36ad Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..1dd7de3acae746c06f3d822bde39040d3bf36715 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..8b036cf565d205b19c4f2764c996cd318015a269 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..23645b31fee746f1793638b9b2fd6bcacb78b57b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f69201aac6eb01581fef2f060134b2c8e4b4f6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..e96106a74ab415570983f75134da47dd727cf97f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..30cd0312f4fadaa8d1f7ef4fd37f120f1bd58ade Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..1b8356c68e6b0e898a6f1c9f7d36f00ed98521a3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..6b7189f8750c0574643ca9bc7d23ae857573fcc2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..72efc32982c64d412c8e65da6f9ae82644a181ac Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..1fbfd94d3081b18b4f7edf4c207eb4a27008f620 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..c2a581355e23756525a800c07184c060f865db5d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..c66c633fd2570a8f0014fd991543579a6ead70eb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..336e28fd51eaec47304587f9107ea2922bf0ac35 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca2fd9614dd55d519c75d330ae66ef59e34a1a5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..b46ae4584c13e5175693eb101d58ad6958d9a645 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..e3c27f857f7e9a818493427454e9204638866e3c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..daefc1f5bbc5af4e99422cf4e4880cb00404994c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..9bb6019ae7c37fdc92c0093ce14a0323b5137228 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..3c8ed2f16f44c74c7092250afaef7833d1140419 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..94e8e7ce6b0832da11dc2df7563e8f82bfc8380c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b49be83476cec5edc469ee6b91dc3cca66670d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ac217e97ee8b775bd14bc297f8ffe82611ac7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..d9fa78a46532d60538bcdd5b05b7f80392ba8cbf Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd998908bde568561616e35d331fc7994337b12 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..73cf6ee010f30f7158fbbaca12ea575bb722079c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..706ebb85451c78548b81907ac354920c6725bc5e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..cbfc1f34444ba83025294a0dee8518817d84bcea Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..b48eed834fbe3ff5e1e7f5648b04f2079f390565 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..ac24870f8ba41e92b8f2934ce7c9a29d03428523 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..690e2b3a448dc912bcd1cb622b97085abc43fb7a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..f9ad57d395a46750539bf9433b3c040ddebc2829 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..600cb1386201e9d95f7901637661db6004779d32 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..03e582d212336c85d4881fdf795f6630e920af9f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..2024309d5f72a00bd65b7e2d642037cdcab5377d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..7140f9a41b5da0ddbbc1815a6bbd57f2811a4855 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..e84d5b61e3617681155a13e167b11519c9708b27 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..32b4ac4b21cbd77808467ec7b2ffa7b8041198ed Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..9ddc36f955cb77a415e7a3299af10f705254007f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..0637a3accf6aa093868add5c6fcf5214ae6e1099 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..e71ef4d6b31cb98b78a4cf40fad15bff9e04880d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..42c2f02226b72ebf4ec208306b9de1a8212d6be6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..443aacb6b9aa32c703e32d5429bebc70f98aa89b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..f47f287a9b663462ecedefdd45e7821a64ea152f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..5492c8ac3ac4b8deb88da48ee0faf96051e58276 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..a91e9689f4ed7e53c47793301751cdef92539175 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd8f29bdbf8da42f3f019c3871a980f317fa957 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6908a1e7221ea8f0931600a35b0219d2a79e66 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..2bda7fa7aa5c384b110b14366b3ad23a8b49f714 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..32b6b337b1dc85c4e80f5d13f1ef1e68a72e117e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..ab7a088a71deae157e0a6e9dc6c97608a4ad7afc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..78ee2e654c3b0a17f5b334ea28d4344d7523b032 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..5149bee0a09cb3eadb934333c1f4d7f36a399954 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..38a6d104e3230d4ff26931ddcec73090ca9ebc59 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..f9cc5c45f62db024fdab9b398195ae7e66f0d2e1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..d16d4d88dfd3d721f8413e9749c7b2743ac390fa Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..04f087624223db8da03c32bf9db16d1c8dd652fb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..3e68b867dd7126ef86fb63333204fc0e6ae5de60 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..94f840b12ec883ffc13d867a8a4d4b8df3f3e8b5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..c5ef7613f79cecc3369c144ddc9e2694badb1a68 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..cbfecaec41ec39c0a82fbf09fbf00f1cc640343b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..defe25cf1219363490b80d16ee57deb5b191bb1a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..d3f358158b7ff419b1db03cf196821f8e652aa7a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..72ecc4fa93b47d130fe0329ba1f563471615fc8a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..c0170b27b6152541633eb0e9e65ce5492da91b32 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..67fecf7ff902236b1e59c7bb77b890d75e8a7d8b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..54588d347a615f49d03514003a2d2dbc614b5b34 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..43005b7ef094580e6389c9794ddc84c054a29405 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..5b904249ddbeb091a898986882162a121d10e6c8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..65968ee8198d9c90a73eda4ea9a2d55e6d0936f7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..b27c75b8b12e473db339bc69dc4606d03e4eec4a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2c41bd20eb6d2b33b621c36db953df49757668 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..b559677a149ef6f27fbaa528f7fcddb34f52ee43 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..45b97344fd5e40c32db09dbf2695d7177143f3da Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..086abc4b83a29bd08d6694cb758e67def0aac547 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..dbb27e7a8547c5b4b61e76cc6149ec14bb6b5101 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..a3937dc383f3149f18063511e783ae2bec3eddd3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..93873de243fc3401e9ebaa32fa6737bd6a4bcacb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..5a250617097a2244da9997e0c7bb2f4d8423b447 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..84aae6e12d7769dc0f331560f229609e695f7c6f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/skeleton/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/axe_drakes_bane.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/axe_drakes_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..af8b56d777050c34822c622b8b27f1aeb4e967f9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/axe_drakes_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/battleaxe_the_gaping_maw.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/battleaxe_the_gaping_maw.png new file mode 100644 index 0000000000000000000000000000000000000000..3c7ea98f0d7f516209607134e0204f8d95832fc2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/battleaxe_the_gaping_maw.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/crude_iron_battleaxe_of_kroll.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/crude_iron_battleaxe_of_kroll.png new file mode 100644 index 0000000000000000000000000000000000000000..ae75a8b293b7b8cf6e1801254a7eb7caabf61b7c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/crude_iron_battleaxe_of_kroll.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/eskatins_ultimatum.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/eskatins_ultimatum.png new file mode 100644 index 0000000000000000000000000000000000000000..4b7285d4ead10f4f825a2b845c8acd2b907b7875 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/eskatins_ultimatum.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/hellfire.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/hellfire.png new file mode 100644 index 0000000000000000000000000000000000000000..647f0879ea0ebbb004217bf25e138cb576568d91 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/hellfire.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_acera.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..193c43073ddfb6aeed36ae7945ebe07825f98d3c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..a65a274d30cdf14a78ec39c5bd39a0ac6d980b1f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..c1838b431f83496f5238945b1a0d5cbcc4393a92 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..b195d1c96895f03768e528d6b23bb4879dd9fc0f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..07d51ea9fe3c07031450d5d16d514e0824e7d32b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8464b58be44441082f322ac84e374d3190632b17 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..834def6fbb84b8df13d2c64a1779fff49ee88e7b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..3754000b11d421bba7bd92d4564123df217a0b7b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..69179bd4689a1cfb0b31596d17f76305324ae02b Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..9e4d069aa21348b4ff15fd0e5a862706ab873ecc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..a9a85f601f3bcd87a94e4411ba04b5d542871e0c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..d001a4ab1df1f9d52a874a39b072cf5ea4f69ae0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc6f03d0ab37fcc210de0cc1421b35828c52f67 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..90a631e13740bb4e8e79a8858bf20730914e3c53 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..1a903cc0d77521beb5f7aab8c56fe2ba73b992e5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..a684abbe5c25460485b5345e98055699f257353f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..b92bebd2d8a4ed1014509a422b2d6f7fced49c14 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..ff36576b7bb58d6ab14c42fb5d7aeb28bf496757 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..cc52d63adad42c324a7e2aadaacaa67d671da033 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..d5198b8d8545f94bbe46551426b7c6bd6c819b5f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b993d000946226bcc205c1d626ec3fa263d02c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..f08811ef7892bfb5584205a35ffd655a1d56731e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..2a16f0e8ca3ec11af8548fb4a165a4e08b10afb8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..7e4abe1177db8b82ad20e14c788de40bef8cba2a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..a726167f79520e7bed210f25a01e02c9601914af Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..191fb3856afbbb3744a07af61851b05c640dcdc1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..059fd342447844ab2e9641677978b29ff9936e16 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..29f70e565e0b0c10c583737337e9afeeadf6ba20 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..ca531ab68fa05e35a957cffcb9d7af5e2f323726 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..9780a5a11898cf93fcc76d7b6c9a0f4495b8d19d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..e8f29f7d512b6417b9e589cb0cb9ff86d8fe0edd Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f6aa795f7002742fb405d00eca553a8467a72d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..af9aa1ef3474d88d4c7b9d20315c907dc55e71f6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd898a79476bad0a622ed11e1decd8f59ef7277 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..d11271b213a32dd4d39749d0761227133e9000ae Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..ccb11b08c26037cc67b44c1856fbadba80ee930e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..5c1a36ddb656d4a728513bc3eba720be0a0d9f68 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..5b05cc0391913023585e7a42c0594ef67e832093 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..fd550cf13b7450d5092e2dfa4c94dbd2fc06370e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/left_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_acera.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_acera.png new file mode 100644 index 0000000000000000000000000000000000000000..a4d56bbcaa41e72704f18aea9b43623105af8df8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_acera.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_anmalice.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_anmalice.png new file mode 100644 index 0000000000000000000000000000000000000000..61252ce32789c02ba6a22217b4d3a1bf4470f050 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_anmalice.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_axe_malediction.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_axe_malediction.png new file mode 100644 index 0000000000000000000000000000000000000000..91d02351c9c14d97b5e0b28b4eb82435c036bedb Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_axe_malediction.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_axe_skullcleaver.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_axe_skullcleaver.png new file mode 100644 index 0000000000000000000000000000000000000000..104604b5332e08a12d44f5b503ad248ca95a7ee0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_axe_skullcleaver.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_blade_of_distorted_time.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_blade_of_distorted_time.png new file mode 100644 index 0000000000000000000000000000000000000000..a880f8e0b82b1c946068f149495b9060618544e4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_blade_of_distorted_time.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_butcher.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_butcher.png new file mode 100644 index 0000000000000000000000000000000000000000..26d55d59dbb0c0f29c7d40decd724462ecbaad63 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_butcher.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_club_ureslaks_femur.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_club_ureslaks_femur.png new file mode 100644 index 0000000000000000000000000000000000000000..de79eee8386a6067d74306ec3f9513d503dc2476 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_club_ureslaks_femur.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_corpus.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_corpus.png new file mode 100644 index 0000000000000000000000000000000000000000..16c850110bf4e32b279fdba43a3db1ed671e1a9d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_corpus.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_life_drinker.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_life_drinker.png new file mode 100644 index 0000000000000000000000000000000000000000..420916c895d95243a463f22268a0a2df35438ac0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_life_drinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_moon.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_moon.png new file mode 100644 index 0000000000000000000000000000000000000000..6af272c04333883e79f17aff7cd9bbd86158cabe Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_moon.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_of_the_past.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_of_the_past.png new file mode 100644 index 0000000000000000000000000000000000000000..68deb0128fabf5bee669ceb7f778b974acbf5ac1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_of_the_past.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_orc_feller.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_orc_feller.png new file mode 100644 index 0000000000000000000000000000000000000000..447a607edf16ace8103652c4d3817affb835f5f6 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_orc_feller.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_silent_blade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_silent_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..302ff69031f609b36e30b3ab111716b7c74b4e09 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_silent_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_star.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_star.png new file mode 100644 index 0000000000000000000000000000000000000000..31073ab56c7f17f37e963867154ca06b208a86f7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dagger_star.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dawn_blade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dawn_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..4363907dfa7374c100a0a143bada9cca7a0c8e9c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_dawn_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_hand_sanguine_shield.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_hand_sanguine_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..51bd65ea48d0225c326fec5977e6f561b5ebef99 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_hand_sanguine_shield.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_hand_summertide.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_hand_summertide.png new file mode 100644 index 0000000000000000000000000000000000000000..0252d2cbfdaa47c74287c40debe7636bf4024b15 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_hand_summertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_kinetic_spike.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_kinetic_spike.png new file mode 100644 index 0000000000000000000000000000000000000000..ca85f19a046d697bbdbc4a30456860cd67980974 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_kinetic_spike.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mace_natures_vengeance.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mace_natures_vengeance.png new file mode 100644 index 0000000000000000000000000000000000000000..df574480124f5a613557b2ed22fb3b542dfb6819 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mace_natures_vengeance.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mandible_of_ungolmor.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mandible_of_ungolmor.png new file mode 100644 index 0000000000000000000000000000000000000000..ab63436dcf7c79b495ba20c71208efcafb45c53c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mandible_of_ungolmor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mercy.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mercy.png new file mode 100644 index 0000000000000000000000000000000000000000..a4e4d703d7f591a5ba1bfa6ba8aa0d527aeeddd0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_mercy.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_morrigor.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_morrigor.png new file mode 100644 index 0000000000000000000000000000000000000000..384c9cf4ca6b3b558f9a174f466a58facdd5a841 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_morrigor.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_punaes_blade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_punaes_blade.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d6cdc32892bb3274d8e4028c60332696d240b4 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_punaes_blade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_razorblade_the_cursed_waraxe.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_razorblade_the_cursed_waraxe.png new file mode 100644 index 0000000000000000000000000000000000000000..f35070e4f5d268ca9639f75285282e3ba12c26ad Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_razorblade_the_cursed_waraxe.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_shantiz_the_stromblade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_shantiz_the_stromblade.png new file mode 100644 index 0000000000000000000000000000000000000000..06a747ecd299a61d83d76c0914b72fb6f713e9b2 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_shantiz_the_stromblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_spellblaze.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_spellblaze.png new file mode 100644 index 0000000000000000000000000000000000000000..53d637af1b203eb22ca05728485afbbb0e4103b3 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_spellblaze.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_spelldrinker.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_spelldrinker.png new file mode 100644 index 0000000000000000000000000000000000000000..bedefb329f3b3198630999c4877398888d2cf9e8 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_spelldrinker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_blood_edge.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_blood_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..247121e82d60b360804d0f4eecada89bf71627b7 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_blood_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_of_potential_futures.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_of_potential_futures.png new file mode 100644 index 0000000000000000000000000000000000000000..1db3a20e94ed2f160c768520419be244756cbdab Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_of_potential_futures.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_witch_bane.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_witch_bane.png new file mode 100644 index 0000000000000000000000000000000000000000..71fc01a17adba642090c6cce81c3ce3e4501ebf9 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_sword_witch_bane.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_swordbreaker.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_swordbreaker.png new file mode 100644 index 0000000000000000000000000000000000000000..50985a12b1461041b2f70042cc113f2cc76039a0 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_swordbreaker.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_the_rivers_fury.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_the_rivers_fury.png new file mode 100644 index 0000000000000000000000000000000000000000..2766817ee101d733c7baf188fa8b4ee192aaf55e Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_the_rivers_fury.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_trident_of_the_tides.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_trident_of_the_tides.png new file mode 100644 index 0000000000000000000000000000000000000000..b3fb773ecb8068604a2eecbdfcd3af4284337e4f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_trident_of_the_tides.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_twilights_edge.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_twilights_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..d8e86fd3705051fb56db3c99849e372f24c79dbc Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_twilights_edge.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_unerring_scalpel.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_unerring_scalpel.png new file mode 100644 index 0000000000000000000000000000000000000000..ba3d19a4bba0381bd46b7b2ec4d322d487b6c762 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_unerring_scalpel.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_axe_blood_letter.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_axe_blood_letter.png new file mode 100644 index 0000000000000000000000000000000000000000..ac041baf66b4a954cd9af9039f255edc3ae7f497 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_axe_blood_letter.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_crooked_club.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_crooked_club.png new file mode 100644 index 0000000000000000000000000000000000000000..84af409f7ddf4363b27194e35d2fa37cadf49ade Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_crooked_club.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_spellblade.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_spellblade.png new file mode 100644 index 0000000000000000000000000000000000000000..0567f5fd9d586044cbe66de1731ec66d19ea5ce5 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_weapon_spellblade.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_wintertide.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_wintertide.png new file mode 100644 index 0000000000000000000000000000000000000000..5223e9daf51d3994b86a6f1ab355f209a16167c1 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/right_wintertide.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/skin_of_many.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/skin_of_many.png new file mode 100644 index 0000000000000000000000000000000000000000..5d154d39f847f7a551aa4855ff011b09e4f32b17 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/skin_of_many.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/skin_of_many_legs.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/skin_of_many_legs.png new file mode 100644 index 0000000000000000000000000000000000000000..db83f18db7b7105a4055b26a75e33fbaa0d55f9d Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/skin_of_many_legs.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/player/yeek/special/tirakais_maul.png b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/tirakais_maul.png new file mode 100644 index 0000000000000000000000000000000000000000..492be886ff722850c274011e33ae882d1792e753 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/player/yeek/special/tirakais_maul.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_01.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_01.png new file mode 100644 index 0000000000000000000000000000000000000000..f7faecde70ab285d38b2767d9f35642dff19582a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_01.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_02.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_02.png new file mode 100644 index 0000000000000000000000000000000000000000..a8407740aef3247a22dd19681f7798cd0dc78551 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_02.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_03.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_03.png new file mode 100644 index 0000000000000000000000000000000000000000..37d51af69fb3b9b8270090de89481dcc0658a30c Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_03.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_04.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_04.png new file mode 100644 index 0000000000000000000000000000000000000000..34effaa41d50f87ec8e879e5dad28f19b8691a9a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_blue_04.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_01.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_01.png new file mode 100644 index 0000000000000000000000000000000000000000..cbf85a7f483427aaf0faba4c89f70acae01f845a Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_01.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_02.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_02.png new file mode 100644 index 0000000000000000000000000000000000000000..6d087dc041682fbed1bc6407b97232707f7da60f Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_02.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_03.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_03.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec3e0c40fa9c7c255c4a537865998b5976aeb71 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_03.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_04.png b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_04.png new file mode 100644 index 0000000000000000000000000000000000000000..3d9a404462c3010db185d61c206ac82924616f34 Binary files /dev/null and b/game/modules/tome/data/gfx/shockbolt/trap/chronomine_red_04.png differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_01.png b/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_01.png deleted file mode 100644 index c6964845c5d5e5cee71c8ce6c7bf5e935b478ce3..0000000000000000000000000000000000000000 Binary files a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_01.png and /dev/null differ diff --git a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_02.png b/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_02.png deleted file mode 100644 index 88f787802024f201258a852ea35241e2d2c338b8..0000000000000000000000000000000000000000 Binary files a/game/modules/tome/data/gfx/shockbolt/trap/trap_warpmine_02.png and /dev/null differ diff --git a/game/modules/tome/data/gfx/talents/banish.png b/game/modules/tome/data/gfx/talents/banish.png index 8dcf8a09e7ecc1af4a57fdc5ece5e499dbea88c1..4be985d8817006a5ea8d64776125fde7e5915eae 100644 Binary files a/game/modules/tome/data/gfx/talents/banish.png and b/game/modules/tome/data/gfx/talents/banish.png differ diff --git a/game/modules/tome/data/gfx/talents/braid_lifelines.png b/game/modules/tome/data/gfx/talents/braid_lifelines.png new file mode 100644 index 0000000000000000000000000000000000000000..f486ca16e3e626d84e44e72a91418e20e1c496e6 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/braid_lifelines.png differ diff --git a/game/modules/tome/data/gfx/talents/cease_to_exist.png b/game/modules/tome/data/gfx/talents/cease_to_exist.png index d06442d3522a6067e20689f1efa2fbae86f06834..1b1fab471aaafecaf1e1c8117ddcaae88b13046f 100644 Binary files a/game/modules/tome/data/gfx/talents/cease_to_exist.png and b/game/modules/tome/data/gfx/talents/cease_to_exist.png differ diff --git a/game/modules/tome/data/gfx/talents/damage_smearing.png b/game/modules/tome/data/gfx/talents/damage_smearing.png index 0795f2634ad83c14d74ca0241718d03b6f07424f..2824e199284aca281eeeb3f1a8ab0a097243dc4c 100644 Binary files a/game/modules/tome/data/gfx/talents/damage_smearing.png and b/game/modules/tome/data/gfx/talents/damage_smearing.png differ diff --git a/game/modules/tome/data/gfx/talents/dimensional_anchor.png b/game/modules/tome/data/gfx/talents/dimensional_anchor.png index 13631e942c011c64603118117696e48d42b86001..b83df653229172b6faf094aa019d953bf785e9df 100644 Binary files a/game/modules/tome/data/gfx/talents/dimensional_anchor.png and b/game/modules/tome/data/gfx/talents/dimensional_anchor.png differ diff --git a/game/modules/tome/data/gfx/talents/dimensional_shift.png b/game/modules/tome/data/gfx/talents/dimensional_shift.png new file mode 100644 index 0000000000000000000000000000000000000000..a3a3f2e3c7c839615c3f9f7f1d0b0dfae7ba0972 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/dimensional_shift.png differ diff --git a/game/modules/tome/data/gfx/talents/dimensional_step.png b/game/modules/tome/data/gfx/talents/dimensional_step.png index 1a5ec6cfcaa24e3ef8d423a4e8e6213091ec2a96..203657d98d94d531379bd4934b481341099833db 100644 Binary files a/game/modules/tome/data/gfx/talents/dimensional_step.png and b/game/modules/tome/data/gfx/talents/dimensional_step.png differ diff --git a/game/modules/tome/data/gfx/talents/discontinuity.png b/game/modules/tome/data/gfx/talents/discontinuity.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb4e35106a124e7b35d40018dd7bef4d8d37e0e Binary files /dev/null and b/game/modules/tome/data/gfx/talents/discontinuity.png differ diff --git a/game/modules/tome/data/gfx/talents/echoes_from_the_past.png b/game/modules/tome/data/gfx/talents/echoes_from_the_past.png index 9b013bf3c8fc01fd3fd40f6bcb2d1e56fd2273ee..1370f0481a851736ab4e959263113504aee5ca0d 100644 Binary files a/game/modules/tome/data/gfx/talents/echoes_from_the_past.png and b/game/modules/tome/data/gfx/talents/echoes_from_the_past.png differ diff --git a/game/modules/tome/data/gfx/talents/empower.png b/game/modules/tome/data/gfx/talents/empower.png new file mode 100644 index 0000000000000000000000000000000000000000..fa2f4c042119607d0c128f4578a545407a6fd7e0 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/empower.png differ diff --git a/game/modules/tome/data/gfx/talents/energy_absorption.png b/game/modules/tome/data/gfx/talents/energy_absorption.png index e196a0475a05d6efe9bf247891cab2e862e7f7cd..aaea4e17c21152f433d3f1bffaeedbb38883e14b 100644 Binary files a/game/modules/tome/data/gfx/talents/energy_absorption.png and b/game/modules/tome/data/gfx/talents/energy_absorption.png differ diff --git a/game/modules/tome/data/gfx/talents/energy_decomposition.png b/game/modules/tome/data/gfx/talents/energy_decomposition.png index ac7fd54da287366c2a144d54059a701e643dab03..b480066e721148758d7c336b47f363da2662ed94 100644 Binary files a/game/modules/tome/data/gfx/talents/energy_decomposition.png and b/game/modules/tome/data/gfx/talents/energy_decomposition.png differ diff --git a/game/modules/tome/data/gfx/talents/entropy.png b/game/modules/tome/data/gfx/talents/entropy.png new file mode 100644 index 0000000000000000000000000000000000000000..af9afe0b78ff555944d648432ed5ab1a3aa50c1d Binary files /dev/null and b/game/modules/tome/data/gfx/talents/entropy.png differ diff --git a/game/modules/tome/data/gfx/talents/extension.png b/game/modules/tome/data/gfx/talents/extension.png new file mode 100644 index 0000000000000000000000000000000000000000..050833e330fb249bebe115b491733f6a21e882b0 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/extension.png differ diff --git a/game/modules/tome/data/gfx/talents/fateweaver.png b/game/modules/tome/data/gfx/talents/fateweaver.png index 3a0ae723fc5b3a8af99faf4c50b4cb6c4d1c471a..c1cfbed29aecde24d08b39080affdf321e8a7151 100644 Binary files a/game/modules/tome/data/gfx/talents/fateweaver.png and b/game/modules/tome/data/gfx/talents/fateweaver.png differ diff --git a/game/modules/tome/data/gfx/talents/fractured_space.png b/game/modules/tome/data/gfx/talents/fractured_space.png new file mode 100644 index 0000000000000000000000000000000000000000..7b5338e75a13c3d1109b716f62c2035d221781c7 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/fractured_space.png differ diff --git a/game/modules/tome/data/gfx/talents/frost_grab.png b/game/modules/tome/data/gfx/talents/frost_grab.png new file mode 100644 index 0000000000000000000000000000000000000000..c93533b8e1acf7bd367171a6706f8a58e87118cc Binary files /dev/null and b/game/modules/tome/data/gfx/talents/frost_grab.png differ diff --git a/game/modules/tome/data/gfx/talents/gravity_locus.png b/game/modules/tome/data/gfx/talents/gravity_locus.png new file mode 100644 index 0000000000000000000000000000000000000000..1ed4b10b269de270ef08a98e8f763a868ec5e4a1 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/gravity_locus.png differ diff --git a/game/modules/tome/data/gfx/talents/gravity_spike.png b/game/modules/tome/data/gfx/talents/gravity_spike.png index a986352e653a5b88e1058e71e471bde61867d3d4..8a6e7b09c4f6775e3db14ebe6477b48eef8bd418 100644 Binary files a/game/modules/tome/data/gfx/talents/gravity_spike.png and b/game/modules/tome/data/gfx/talents/gravity_spike.png differ diff --git a/game/modules/tome/data/gfx/talents/gravity_well.png b/game/modules/tome/data/gfx/talents/gravity_well.png index b374335def95a3798280179a3c9a5770a829f215..4dd85288e0ef465cb65692269174294077a3f634 100644 Binary files a/game/modules/tome/data/gfx/talents/gravity_well.png and b/game/modules/tome/data/gfx/talents/gravity_well.png differ diff --git a/game/modules/tome/data/gfx/talents/matrix.png b/game/modules/tome/data/gfx/talents/matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..91200083eace7401c16943a80b30b9afaac7efaf Binary files /dev/null and b/game/modules/tome/data/gfx/talents/matrix.png differ diff --git a/game/modules/tome/data/gfx/talents/phase_pulse.png b/game/modules/tome/data/gfx/talents/phase_pulse.png new file mode 100644 index 0000000000000000000000000000000000000000..0085e5161bdfadff2a626634e166e143b39aec69 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/phase_pulse.png differ diff --git a/game/modules/tome/data/gfx/talents/phase_shift.png b/game/modules/tome/data/gfx/talents/phase_shift.png index 8e5480bdf4cc6faffcc6ba34dadbeaec3efccf70..2cc474bb2d431d825ea80898644f06efc95d11d0 100644 Binary files a/game/modules/tome/data/gfx/talents/phase_shift.png and b/game/modules/tome/data/gfx/talents/phase_shift.png differ diff --git a/game/modules/tome/data/gfx/talents/polarity_shift.png b/game/modules/tome/data/gfx/talents/polarity_shift.png new file mode 100644 index 0000000000000000000000000000000000000000..51a9c0583c651c8b97bbc9b9175c3c67ae32dcc7 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/polarity_shift.png differ diff --git a/game/modules/tome/data/gfx/talents/quicken.png b/game/modules/tome/data/gfx/talents/quicken.png new file mode 100644 index 0000000000000000000000000000000000000000..6ac9df4f244bee41c6a7785f21734b1f648084f3 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/quicken.png differ diff --git a/game/modules/tome/data/gfx/talents/redux.png b/game/modules/tome/data/gfx/talents/redux.png index 34a7bb4dbcab6b6595f3adf70dafff9b6294e9cc..7ae45948d9b28d77854f9e485bf68e56b17f120f 100644 Binary files a/game/modules/tome/data/gfx/talents/redux.png and b/game/modules/tome/data/gfx/talents/redux.png differ diff --git a/game/modules/tome/data/gfx/talents/repulsion_blast.png b/game/modules/tome/data/gfx/talents/repulsion_blast.png index 4ed66410bf53ce914cb41f85a661132b459085a1..5556b8f7fbeaa5e3c44dda9e95bac04897aa22f9 100644 Binary files a/game/modules/tome/data/gfx/talents/repulsion_blast.png and b/game/modules/tome/data/gfx/talents/repulsion_blast.png differ diff --git a/game/modules/tome/data/gfx/talents/rethread.png b/game/modules/tome/data/gfx/talents/rethread.png index 3f53a62d6595abf1755a058f030806d61a8dafea..41fdca00a7bd57c5503239ff36ed4ca17a3a6142 100644 Binary files a/game/modules/tome/data/gfx/talents/rethread.png and b/game/modules/tome/data/gfx/talents/rethread.png differ diff --git a/game/modules/tome/data/gfx/talents/seal_fate.png b/game/modules/tome/data/gfx/talents/seal_fate.png index 629d3fdcddce8916d5b12e32405c9b799cccf6a5..3258ceabba00e268e241d538d6401b89051b000e 100644 Binary files a/game/modules/tome/data/gfx/talents/seal_fate.png and b/game/modules/tome/data/gfx/talents/seal_fate.png differ diff --git a/game/modules/tome/data/gfx/talents/spatial_fragments.png b/game/modules/tome/data/gfx/talents/spatial_fragments.png new file mode 100644 index 0000000000000000000000000000000000000000..b44f8480cee0d961924105c21a8bb204e2fe519e Binary files /dev/null and b/game/modules/tome/data/gfx/talents/spatial_fragments.png differ diff --git a/game/modules/tome/data/gfx/talents/spatial_tether.png b/game/modules/tome/data/gfx/talents/spatial_tether.png new file mode 100644 index 0000000000000000000000000000000000000000..a7144cefc368706fb9e0addedaa8d4f94bff6f99 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/spatial_tether.png differ diff --git a/game/modules/tome/data/gfx/talents/sphere_of_destruction.png b/game/modules/tome/data/gfx/talents/sphere_of_destruction.png new file mode 100644 index 0000000000000000000000000000000000000000..237f62b5b78300ee98a39ca59249376a543bde9e Binary files /dev/null and b/game/modules/tome/data/gfx/talents/sphere_of_destruction.png differ diff --git a/game/modules/tome/data/gfx/talents/spin_fate.png b/game/modules/tome/data/gfx/talents/spin_fate.png index a95fe19d9122b4933f281802dbdd37fec873f1b9..9fb6a19b51833415c41aebf3e5c8d2a2031c309d 100644 Binary files a/game/modules/tome/data/gfx/talents/spin_fate.png and b/game/modules/tome/data/gfx/talents/spin_fate.png differ diff --git a/game/modules/tome/data/gfx/talents/spint_fate.png b/game/modules/tome/data/gfx/talents/spint_fate.png deleted file mode 100644 index 25f7666e887a6e27d523eb2e116324e3f1f9eee1..0000000000000000000000000000000000000000 Binary files a/game/modules/tome/data/gfx/talents/spint_fate.png and /dev/null differ diff --git a/game/modules/tome/data/gfx/talents/swap.png b/game/modules/tome/data/gfx/talents/swap.png index afb0b6a4a934df7c7d81dde32b03782236e5d54e..9f90da389b4a557bd88d4808cad0630fbedd5967 100644 Binary files a/game/modules/tome/data/gfx/talents/swap.png and b/game/modules/tome/data/gfx/talents/swap.png differ diff --git a/game/modules/tome/data/gfx/talents/temporal_bolt.png b/game/modules/tome/data/gfx/talents/temporal_bolt.png new file mode 100644 index 0000000000000000000000000000000000000000..f4bb163da34d6b9213ddd8959484c53f2ab5266b Binary files /dev/null and b/game/modules/tome/data/gfx/talents/temporal_bolt.png differ diff --git a/game/modules/tome/data/gfx/talents/temporal_clone.png b/game/modules/tome/data/gfx/talents/temporal_clone.png index 69f316bbdd60df547f97602658c861095d4477b6..27871178fac6951c2276ccf10a5ef098d4ac49df 100644 Binary files a/game/modules/tome/data/gfx/talents/temporal_clone.png and b/game/modules/tome/data/gfx/talents/temporal_clone.png differ diff --git a/game/modules/tome/data/gfx/talents/temporal_fugue.png b/game/modules/tome/data/gfx/talents/temporal_fugue.png index c3a614cc0436d9a57ce1ed054ebc48634109c722..2f0a4d4680122ab2dd6267cc5e1f95fd82b39d80 100644 Binary files a/game/modules/tome/data/gfx/talents/temporal_fugue.png and b/game/modules/tome/data/gfx/talents/temporal_fugue.png differ diff --git a/game/modules/tome/data/gfx/talents/temporal_reprieve.png b/game/modules/tome/data/gfx/talents/temporal_reprieve.png index 95fc78fc24d2b9b472ac985d96034902916914b5..b31b9f88e35c13e70bade128ecaa75aa452405a4 100644 Binary files a/game/modules/tome/data/gfx/talents/temporal_reprieve.png and b/game/modules/tome/data/gfx/talents/temporal_reprieve.png differ diff --git a/game/modules/tome/data/gfx/talents/temporal_wake.png b/game/modules/tome/data/gfx/talents/temporal_wake.png index bbfed84cd90360e718a3548016311d26914b29ec..278bb53e28d4e665ef6893fd975fa4f83c37a9e1 100644 Binary files a/game/modules/tome/data/gfx/talents/temporal_wake.png and b/game/modules/tome/data/gfx/talents/temporal_wake.png differ diff --git a/game/modules/tome/data/gfx/talents/time_skip.png b/game/modules/tome/data/gfx/talents/time_skip.png index 5ead5acaaf2bc0eca84ee38592af06766a072b8f..5b59b98746d469aa7dcd7d667ea8304ab4b92ac7 100644 Binary files a/game/modules/tome/data/gfx/talents/time_skip.png and b/game/modules/tome/data/gfx/talents/time_skip.png differ diff --git a/game/modules/tome/data/gfx/talents/warp.png b/game/modules/tome/data/gfx/talents/warp.png new file mode 100644 index 0000000000000000000000000000000000000000..470815ea4e452f4ab4197c0d996929ce2fd7af9c Binary files /dev/null and b/game/modules/tome/data/gfx/talents/warp.png differ diff --git a/game/modules/tome/data/gfx/talents/warp_mastery.png b/game/modules/tome/data/gfx/talents/warp_mastery.png new file mode 100644 index 0000000000000000000000000000000000000000..b9a1d90c628701939b6e1930b86e38c86cb37fc7 Binary files /dev/null and b/game/modules/tome/data/gfx/talents/warp_mastery.png differ diff --git a/game/modules/tome/data/gfx/talents/warp_mine.png b/game/modules/tome/data/gfx/talents/warp_mine.png deleted file mode 100644 index 43b830b5ceee0fd929e580c2e8e8cc4de8cfd27f..0000000000000000000000000000000000000000 Binary files a/game/modules/tome/data/gfx/talents/warp_mine.png and /dev/null differ diff --git a/game/modules/tome/data/gfx/talents/warp_mine_away.png b/game/modules/tome/data/gfx/talents/warp_mine_away.png index 5268bb01dac6cecce5348f7432881c4c99029e5b..f08f5ff1961ba9331a4847ea400bd1abef670676 100644 Binary files a/game/modules/tome/data/gfx/talents/warp_mine_away.png and b/game/modules/tome/data/gfx/talents/warp_mine_away.png differ diff --git a/game/modules/tome/data/gfx/talents/warp_mine_toward.png b/game/modules/tome/data/gfx/talents/warp_mine_toward.png index e8ac5358c4b2ecd9e8ca9c5a7ef768d1f3d75c67..5c509a8f16c21423284337f167508b1b2eb16d1e 100644 Binary files a/game/modules/tome/data/gfx/talents/warp_mine_toward.png and b/game/modules/tome/data/gfx/talents/warp_mine_toward.png differ diff --git a/game/modules/tome/data/gfx/talents/warp_mines.png b/game/modules/tome/data/gfx/talents/warp_mines.png index 2c5d2d55c7c492f051132af2503cd1457ac848eb..31d9e2210e3a8237a70b26417676a82532f643a6 100644 Binary files a/game/modules/tome/data/gfx/talents/warp_mines.png and b/game/modules/tome/data/gfx/talents/warp_mines.png differ diff --git a/game/modules/tome/data/gfx/talents/webs_of_fate.png b/game/modules/tome/data/gfx/talents/webs_of_fate.png index aa8e3b51b14f93486ce54eda5d39bbd305b43045..71803764fa6797423c5407f345ebfc34a873f96f 100644 Binary files a/game/modules/tome/data/gfx/talents/webs_of_fate.png and b/game/modules/tome/data/gfx/talents/webs_of_fate.png differ diff --git a/game/modules/tome/data/gfx/talents/wormhole.png b/game/modules/tome/data/gfx/talents/wormhole.png index 1f85b3fcaee1b4ee5778908cc7fcece7f986d607..f34fe5e73422a3183267fb223ba0587eb145e655 100644 Binary files a/game/modules/tome/data/gfx/talents/wormhole.png and b/game/modules/tome/data/gfx/talents/wormhole.png differ diff --git a/game/modules/tome/data/lore/arena.lua b/game/modules/tome/data/lore/arena.lua index 1962aac0adb8a1f643d46ba9a1fdec00c2902e7d..c81c62c432ea828fe60aa6df52b73f3f6346b90e 100644 --- a/game/modules/tome/data/lore/arena.lua +++ b/game/modules/tome/data/lore/arena.lua @@ -7,14 +7,14 @@ newLore{ #{normal}#The rules of the arena are simple. Every wave, the gates will open, releasing enemies. When the gates close, you will be given a bonus score. -The bonus will #YELLOW#decrease every turn #WHITE#, but #YELLOW#enemies you kill at this point#WHITE#, will +The bonus will #YELLOW#decrease every turn #LAST#, but #YELLOW#enemies you kill at this point#LAST#, will raise your bonus. When you defeat all enemies, you will be granted your bonus score, multiplied by your bonus multiplier. Every few rounds, a miniboss round will come. After two miniboss rounds, one out of four bosses will appear. -#YELLOW#Kill enemies before the gates close #WHITE#to raise your score multiplier!. -#YELLOW#Kill several enemies at once or very strong enemies #WHITE#to please the audience and increase your rank!. -#YELLOW#Collect gold #WHITE#to increase your score and resources at the end of each round. +#YELLOW#Kill enemies before the gates close #LAST#to raise your score multiplier!. +#YELLOW#Kill several enemies at once or very strong enemies #LAST#to please the audience and increase your rank!. +#YELLOW#Collect gold #LAST#to increase your score and resources at the end of each round. #YELLOW#Kill enemies fast to get better scores! #YELLOW#Take advantage of the trap at the center of the arena! Good luck! diff --git a/game/modules/tome/data/lore/lore.lua b/game/modules/tome/data/lore/lore.lua index e4b054bc2195aaab84d7f5af5d74047a6ff1256a..6c5619646ebc79d2b333a9b3cea2d84d9bd6faa4 100644 --- a/game/modules/tome/data/lore/lore.lua +++ b/game/modules/tome/data/lore/lore.lua @@ -49,3 +49,4 @@ load("/data/lore/high-peak.lua") load("/data/lore/arena.lua") load("/data/lore/keepsake.lua") load("/data/lore/ardhungol.lua") +load("/data/lore/tannen.lua") diff --git a/game/modules/tome/data/lore/tannen.lua b/game/modules/tome/data/lore/tannen.lua new file mode 100644 index 0000000000000000000000000000000000000000..23f1b3deb13145bace5e4260fbfd105dbfd01643 --- /dev/null +++ b/game/modules/tome/data/lore/tannen.lua @@ -0,0 +1,82 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +-------------------------------------------------------------------------- +-- Tannen lore +-------------------------------------------------------------------------- + +newLore{ + id = "tannen-level1", + category = "tannen's tower", + name = "Welcome to your cell", + lore = [[A Note to the Adventurer: + +I am truly sorry about this; had circumstances been different, we could've been allies, my weapons and magic letting you fight the hordes of threats to Maj'Eyal. As it stands, however, your continued existence is highly inconvenient to me. + +See, my funding comes from the merchants of Last Hope, who want a functioning portal and will stop paying once they have one; furthermore, my portal research has proved to be a very useful cover story for the other experiments I'm conducting here. Now that you've gotten a portal working, and will want to use it in full view of the city, I'm out of a job - and there is so much more knowledge for me to find! The medical applications of necromancy, for one - the only other source of data on this subject went missing shortly after her husband's death, and everyone else has a Ziguranth-like fervor against the idea of using that magic baseline in any way whatsoever. Also, selective drake breeding programs, experiments on zealots to shatter their anti-magic barriers, studying demons to learn more about their altered biologies and steal that magic for our own militaries... I could go on, but I'd rather not waste enchanted ink. The point is, you using the portal without further injury would mean an end to my experiments. Ideally, you need to disappear, but if you were to emerge from my basement, wounded and raving with wild eyes about impossible beasts in the basement of my humble tower... well, that'd just further my case that portals need more research to be used safely, wouldn't it? + +#{italic}#As you move on to the next paragraph, you notice that everything above it has faded away, and the scroll is beginning to grow warm in your hands.#{normal}# + +Worry not, though - you can still help the world, by devoting yourself to my quest for knowledge! My sources have told me that you've grown exponentially more powerful since you first started making a name for yourself, and you continue to cause prodigious amounts of destruction. The modified portal I used on you had about a 33% chance of draining some of the power you've accumulated; if you're feeling exhausted, simply lay down and wait for my imp servants to collect you for further testing. If you aren't, however: I've sealed the way up, and placed the switches in each of the subject holding pens on this floor. You can fight your way through them, and I'll be watching to see how a creature of your power fares against different types of foes. I'm sure you'll want to get to the top floor and kill me in revenge, and I encourage you to try! You may die, but the data you provide will be immortal - and if I can have my drolem subdue you non-lethally, I'll send you back to the bottom and repeat the experiment with tweaked parameters. We can keep repeating this until I find your breaking point, or figure out what makes you so special, at which point you'll be held until I can work on a reliable method of controlling your mind. And on the off chance that you do actually manage to kill me... try not to do too much damage to my notes, please? Knowledge and progress are bigger matters than either of us; don't let your spite doom thousands of lives that could rely on my research in the future. + +Oh, and I can't risk this note getting into anyone else's hands, can I? It's going to catch fire a few seconds after you're done reading. Please drop it before it does - you having burnt hands isn't part of the experiment. + +#{italic}#You throw the paper away. After about fifteen seconds, it fizzles, then bursts into flame, not even leaving ashes behind.#{normal}#]], +} + +newLore{ + id = "tannen-level2", + category = "tannen's tower", + name = "Personal note (1)", + lore = [[Angolwen is too timid. Too paranoid about repeating the mistakes of the past. Too cautious and infuriatingly non-pragmatic. Too prone to avoiding the "little sins," ignoring the big picture. I've told them again and again that our ancestors' mistake wasn't trying to use the Sher'Tul portals, it was trying to weaponize them before we understood how they worked - couldn't we have plundered the Nargol facility for more data, or started kidnapping orc scouting parties as experimental subjects? Couldn't we have tried to learn more about the Sher'Tul _before_ desperation pushed us into blowing up half of the world? No, that was "forbidden" magic, "too powerful for mortals to tamper with," right up until we realized we needed all that power after all. Like a pacifist finally picking up a flail to defend himself, only to bash himself in the head with it, our lack of familiarity led to catastrophe. + +My arguments have just gotten me blank stares and an increasing amount of whispering behind my back (aside from a young couple whose abrupt departure might've been inspired by a particularly passionate rant in response to a lecture on "ethics"). Some of them have even resorted to deflecting my arguments, blaming them on my lack of magical power and saying I've spent too much time working on my drolem! They think they can inspire the world and protect it from danger without getting their hands dirty; the orc invasions proved that, no, they can't, and I fear that if the demons ever start arriving in full force, we'll be even more woefully unprepared for it. We even cower from the Ziguranth, no matter how many people die from diseases our healers could cure if they could roam freely, and no matter how easily we could find a way around their defenses if we started capturing a few of their agents. + +Well, I won't have it. I've been selling potions and inscriptions on the side for a couple of years now, in spite of Angolwen's regulations, and managed to amass enough money to set up a laboratory far away from Angolwen. There, I intend to do the experiments my cowardly, squeamish peers won't. I'm sure they won't approve, but I'm beyond caring - there are certain things we need to know before it's too late, and if a few zealots or criminals die in the process, it won't matter when my data saves countless lives in the long run. Construction begins tomorrow - I've got three separate sets of contractors lined up to work on it, so none know the tower's full layout, and I've made arrangements with powerful merchants in Last Hope so I can conduct some of my experiments publicly (they want working portals for trade, and I convinced them I won't cause another Spellblaze trying to make one), providing a convenient cover story for the more... controversial experiments. I can have my drolem carry in sensitive equipment so I don't have to answer any problematic questions. I will NOT escape one band of stuck-up fools just to have another confiscate bone-giants which I spent a fortune on. +]], +} + +newLore{ + id = "tannen-level3", + category = "tannen's tower", + name = "Personal note (2)", + lore = [[ +Well... that was interesting. + +Setting up a portal took surprisingly little effort - conjured replicas of a Blood-Runed Athame and a Resonating Diamond work perfectly for setting up a portal, as it turns out, even if it burns them out after making just one. This solution wouldn't work for the Orb of Many Ways, given that if it shorts out after one use, that means you're trapped on the other side of the portal, so I started working on a more tangible replica. I noticed that most of them were disrupted by some form of interference, maybe echoes from the Spellblaze; my latest attempt at a more permanent orb was more of a curiosity than anything, an attempt to use those waves as constructive interference to lock onto their source. I tossed a bandit through (my drolem's flight and relative silence have proven to be very convenient for securing test subjects!) with it tied to him, expecting him to pop into some magical maelstrom, quickly teleport back, and promptly die of terrible burn wounds (like the last four). + +I didn't get a bandit back. I got an imp. + +My drolem grabbed it with its jaws, as usual, but I told it to back off on seeing the imp wasn't armed and wasn't even trying to cast any spells. I apologized, treated its wounds, then asked it to explain what happened to my test subject. Apparently, the demons have been running their own portal experiments! The test subject was unharmed when he popped up in their laboratory; after the demons "questioned" him (boiling tar was involved), they realized that another inquisitive mind was on the other side, and hoped to communicate and possibly trade data with that person. I've managed to make more progress than the demons have - either I'm a genius, or I've severely overestimated the threat the demons pose! + +I've "officially" agreed to their deal - they'll supply me with materials, subjects, and what little progress they've made, and in return I've developed a modified orb that'll give them readings when it's used in a portal. Needless to say, the readings are all bogus, and if they actually try to use those readings to make a second portal of their own, it'll blow them to bits. The data they've given me has been VERY useful! I've used it to construct a second altar from scratch - it still has some links to the demonic realm, but a single teleport with a genuine Orb of Many Ways will recalibrate it and cut those off completely, letting me get away scot-free with my knowledge and treasures, and preventing the demons from reaching me again. + +Now, I just need to get a genuine orb before the demons catch on...]], +} + +newLore{ + id = "tannen-level4", + category = "tannen's tower", + name = "Demon Orders", + lore = [[Order to the Portal Excursion Team: + +This egotistical human has proven to be very valuable. We gave him only a very limited amount of data, and yet he still thinks he has the upper hand, thinking he could trick us by giving us faulty information. No matter - the plans we gave him for a portal altar are feeding their measurements straight to us every time he uses them. Every time he runs an experiment, we get much closer to devising a way to penetrate Eyal's shield; even the data we already have is enough to take us through one at a time, with the prohibitive limitation of creating a new Orb of Many Ways for each prospective invader. + +Your orders are to play along. Keep giving him what he says he wants - more components, more captured Eyalites, more assistants. Above all, let him think he's got the upper hand - feign ineptitude, and never let on that we know the data he's giving us is false. Compared to what we're getting in return, it's a bargain.]], +} diff --git a/game/modules/tome/data/maps/zones/tannen-tower-2.lua b/game/modules/tome/data/maps/zones/tannen-tower-2.lua index 4ff707a6012798cd76f016d5817363e40b1735ae..f432937a44e06e7cf7d3de87aaaa135496fd96c8 100644 --- a/game/modules/tome/data/maps/zones/tannen-tower-2.lua +++ b/game/modules/tome/data/maps/zones/tannen-tower-2.lua @@ -28,6 +28,7 @@ defineTile("+", "DOOR") defineTile("<", "TUP") defineTile(">", "TDOWN") defineTile(".", "FLOOR") +defineTile("l", "FLOOR", "NOTE3") -- addSpot section @@ -41,7 +42,7 @@ XXXX..XXX...X...XXX>.XXXX XXX..XX.....X.....XX..XXX XXX.XX......+......XX.XXX XX..X.......X.......X..XX -XX.XX.......X.......XX.XX +XX.XX..l....X.......XX.XX XX.X......XXXX.......X.XX X..X......+..XX......X..X X.XX.....XX...XX.....XX.X diff --git a/game/modules/tome/data/maps/zones/tannen-tower-3.lua b/game/modules/tome/data/maps/zones/tannen-tower-3.lua index 10558d3a719e6775079c9b0c1d1deb17ddd1d719..edd0c701d3b54ffba3828c98148ee2d67e660d58 100644 --- a/game/modules/tome/data/maps/zones/tannen-tower-3.lua +++ b/game/modules/tome/data/maps/zones/tannen-tower-3.lua @@ -28,6 +28,7 @@ defineTile("~", "DEEP_WATER") defineTile("<", "TUP") defineTile(">", "TDOWN") defineTile(".", "FLOOR") +defineTile("l", "DEEP_WATER", "NOTE2") -- addSpot section @@ -47,7 +48,7 @@ X~~XXX~X~X~~~~~X~X~XXX~~X X~XXXX~X~X~XXX~X~X~XXXX~X X~XXXX~X~X~~>X~X~X~XXXX~X X~XXXX~X~XXXXX~X~X~XXXX~X -X~~XXX.X~~~~~~~X~X~XXX~~X +X~~XXX.X~~~l~~~X~X~XXX~~X XX~XXX~XXXXXXXXX~X~XXX~XX XX.XXX~~~~~~~~~~~X~XXX~XX XX~~XXXXXXXXXXXXXX~XX~.XX diff --git a/game/modules/tome/data/maps/zones/tannen-tower-4.lua b/game/modules/tome/data/maps/zones/tannen-tower-4.lua index 0cff83b4108d964a21ec9679a8ca613c0dc5962c..b0fdc2167744677454b92639369ec8f0520b179f 100644 --- a/game/modules/tome/data/maps/zones/tannen-tower-4.lua +++ b/game/modules/tome/data/maps/zones/tannen-tower-4.lua @@ -39,6 +39,7 @@ defineTile(".", "FLOOR") defineTile(" ", "OLD_FLOOR") defineTile("!", "WALL") defineTile("T", "TREE") +defineTile("l", "FLOOR", "NOTE1") -- ASCII map section return [[ @@ -57,7 +58,7 @@ X+XXXXXXXXXX*XXXXXXXXXX+X X.+........*<*........+.X X+XXXXXXXXXX*XXX+XXXXXX+X X..XX.....&X.XX,,,XXXX..X -XX.XX......X.XEE,EEXXX.XX +XX.XX....l.X.XEE,EEXXX.XX XX.XX......X.XEETEEXXX.XX XX..X....$$X.XEE,EEXX..XX XXX.XXXXXX+X.XX,,,XXX.XXX diff --git a/game/modules/tome/data/talents.lua b/game/modules/tome/data/talents.lua index 11a01db61630adc575f7986f72988111064f46a6..cd5d58f8e6a112efe7ffa6f5e38a5e7a73a9c033 100644 --- a/game/modules/tome/data/talents.lua +++ b/game/modules/tome/data/talents.lua @@ -22,14 +22,17 @@ local Entity = require "engine.Entity" local oldNewTalent = Talents.newTalent Talents.newTalent = function(self, t) - assert(engine.interface.ActorTalents.talents_types_def[t.type[1]], "No talent category "..tostring(t.type[1]).." for talent "..t.name) - if engine.interface.ActorTalents.talents_types_def[t.type[1]].generic then t.generic = true end - if engine.interface.ActorTalents.talents_types_def[t.type[1]].no_silence then t.no_silence = true end - if engine.interface.ActorTalents.talents_types_def[t.type[1]].is_spell then t.is_spell = true end - if engine.interface.ActorTalents.talents_types_def[t.type[1]].is_mind then t.is_mind = true end - if engine.interface.ActorTalents.talents_types_def[t.type[1]].is_nature then t.is_nature = true end - if engine.interface.ActorTalents.talents_types_def[t.type[1]].is_unarmed then t.is_unarmed = true end - if engine.interface.ActorTalents.talents_types_def[t.type[1]].autolearn_mindslayer then t.autolearn_mindslayer = true end + local tt = engine.interface.ActorTalents.talents_types_def[t.type[1]] + assert(tt, "No talent category "..tostring(t.type[1]).." for talent "..t.name) + if tt.generic then t.generic = true end + if tt.no_silence then t.no_silence = true end + if tt.is_spell then t.is_spell = true end + if tt.is_mind then t.is_mind = true end + if tt.is_nature then t.is_nature = true end + if tt.is_antimagic then t.is_antimagic = true end + if tt.is_unarmed then t.is_unarmed = true end + if tt.autolearn_mindslayer then t.autolearn_mindslayer = true end + if tt.speed and not t.speed then t.speed = tt.speed end if t.tactical then local tacts = {} diff --git a/game/modules/tome/data/talents/celestial/chants.lua b/game/modules/tome/data/talents/celestial/chants.lua index e8d0d4ac7ec4041f92e87551c8b86d4067b026e0..4210aeb0f412946bbec95827417c35d7e6c376dc 100644 --- a/game/modules/tome/data/talents/celestial/chants.lua +++ b/game/modules/tome/data/talents/celestial/chants.lua @@ -17,15 +17,6 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org -local function cancelChants(self) - local chants = {self.T_CHANT_OF_FORTITUDE, self.T_CHANT_OF_FORTRESS, self.T_CHANT_OF_RESISTANCE, self.T_CHANT_OF_LIGHT} - for i, t in ipairs(chants) do - if self:isTalentActive(t) then - self:forceUseTalent(t, {ignore_energy=true}) - end - end -end - -- Synergizes with melee classes (escort), Weapon of Wrath, healing mod (avoid overheal > healing efficiency), and low spellpower newTalent{ name = "Chant of Fortitude", @@ -42,11 +33,11 @@ newTalent{ getResists = function(self, t) return self:combatTalentSpellDamage(t, 5, 70) end, getLifePct = function(self, t) return self:combatTalentLimit(t, 1, 0.05, 0.20) end, -- Limit < 100% bonus getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 5, 25) end, + sustain_slots = 'celestial_chant', activate = function(self, t) - cancelChants(self) local power = t.getResists(self, t) game:playSoundNear(self, "talents/spell_generic2") - + local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.LIGHT]=t.getDamageOnMeleeHit(self, t)}), phys = self:addTemporaryValue("combat_physresist", power), @@ -95,8 +86,8 @@ newTalent{ getDamageChange = function(self, t) return -self:combatTalentLimit(t, 50, 14, 30) -- Limit < 50% damage reduction end, + sustain_slots = 'celestial_chant', activate = function(self, t) - cancelChants(self) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.LIGHT]=t.getDamageOnMeleeHit(self, t)}), @@ -137,8 +128,8 @@ newTalent{ range = 10, getResists = function(self, t) return self:combatTalentSpellDamage(t, 5, 25) end, getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 5, 25) end, + sustain_slots = 'celestial_chant', activate = function(self, t) - cancelChants(self) local power = t.getResists(self, t) game:playSoundNear(self, "talents/spell_generic2") local ret = { @@ -182,8 +173,8 @@ newTalent{ getLightDamageIncrease = function(self, t) return self:combatTalentSpellDamage(t, 20, 50) end, getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 5, 25) end, getLite = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6, "log")) end, + sustain_slots = 'celestial_chant', activate = function(self, t) - cancelChants(self) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.LIGHT]=t.getDamageOnMeleeHit(self, t)}), diff --git a/game/modules/tome/data/talents/celestial/eclipse.lua b/game/modules/tome/data/talents/celestial/eclipse.lua index 52e43d15e261b31c11dc13f03b9103e27e973021..b2a2b4f6c26e500e296da9ac2c8e08fb6bfc8ace 100644 --- a/game/modules/tome/data/talents/celestial/eclipse.lua +++ b/game/modules/tome/data/talents/celestial/eclipse.lua @@ -42,6 +42,7 @@ newTalent{ tactical = { BUFF = 2 }, positive = 10, negative = 10, + fixed_cooldown = true, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9)) end, getResistancePenetration = function(self, t) return self:combatLimit(self:getCun()*self:getTalentLevel(t), 100, 5, 0, 55, 500) end, -- Limit to <100% getCooldownReduction = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end, @@ -113,11 +114,12 @@ newTalent{ end end, activate = function(self, t) - local ret = { - } + local ret = {} + ret.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, oversize=1.7, a=155, appear=8, speed=0, img="corona_02", radius=0})) return ret end, deactivate = function(self, t, p) + self:removeParticles(p.particle) return true end, info = function(self, t) @@ -162,6 +164,7 @@ newTalent{ self:removeAllMOs() game.level.map:updateMap(self.x, self.y) end + ret.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, oversize=1.7, a=155, appear=8, speed=0, img="darkest_light", radius=0})) self:resetCanSeeCacheOf() return ret end, @@ -177,6 +180,7 @@ newTalent{ self:removeTemporaryValue("negative_regen_ref", p.drain) self:removeTemporaryValue("positive_at_rest_disable", p.pstop) self:removeTemporaryValue("negative_at_rest_disable", p.nstop) + self:removeParticles(p.particle) local tg = {type="ball", range=0, selffire=true, radius= t.getRadius(self, t), talent=t} self:project(tg, self.x, self.y, DamageType.LITE, 1) tg.selffire = false diff --git a/game/modules/tome/data/talents/celestial/hymns.lua b/game/modules/tome/data/talents/celestial/hymns.lua index 5e2c8e3f8876ad30012e4cb811f8153c64c4d376..644fa2afaed546f2893f424cb48e2e9baa879a16 100644 --- a/game/modules/tome/data/talents/celestial/hymns.lua +++ b/game/modules/tome/data/talents/celestial/hymns.lua @@ -17,15 +17,6 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org -local function cancelHymns(self) - local hymns = {self.T_HYMN_OF_SHADOWS, self.T_HYMN_OF_DETECTION, self.T_HYMN_OF_PERSEVERANCE, self.T_HYMN_OF_MOONLIGHT} - for i, t in ipairs(hymns) do - if self:isTalentActive(t) then - self:forceUseTalent(t, {ignore_energy=true}) - end - end -end - newTalent{ name = "Hymn of Shadows", type = {"celestial/hymns", 1}, @@ -40,8 +31,8 @@ newTalent{ range = 10, getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end, getDarknessDamageIncrease = function(self, t) return self:combatTalentSpellDamage(t, 5, 25) end, + sustain_slots = 'celestial_hymn', activate = function(self, t) - cancelHymns(self) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.DARKNESS]= t.getDamageOnMeleeHit(self, t)}), @@ -83,8 +74,8 @@ newTalent{ getSeeInvisible = function(self, t) return self:combatTalentSpellDamage(t, 2, 35) end, getSeeStealth = function(self, t) return self:combatTalentSpellDamage(t, 2, 15) end, getInfraVisionPower = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, + sustain_slots = 'celestial_hymn', activate = function(self, t) - cancelHymns(self) game:playSoundNear(self, "talents/spell_generic2") local ret = { onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.DARKNESS]= t.getDamageOnMeleeHit(self, t)}), @@ -130,8 +121,8 @@ newTalent{ range = 10, getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end, getImmunities = function(self, t) return self:combatTalentLimit(t, 1, 0.22, 0.5) end, -- Limit < 100% + sustain_slots = 'celestial_hymn', activate = function(self, t) - cancelHymns(self) local dam = self:combatTalentSpellDamage(t, 5, 25) game:playSoundNear(self, "talents/spell_generic2") local ret = { @@ -205,8 +196,8 @@ newTalent{ self:incNegative(-drain) end end, + sustain_slots = 'celestial_hymn', activate = function(self, t) - cancelHymns(self) game:playSoundNear(self, "talents/spell_generic") game.logSeen(self, "#DARK_GREY#A shroud of shadow dances around %s!", self.name) return { diff --git a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua index cb7fe1773cbc2b3a1d7124033baf9b3fa6934433..93bca691760f307855c65b78a5ff1cf48528ce53 100644 --- a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua +++ b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua @@ -29,12 +29,13 @@ newTalent{ reflectable = true, requires_target = true, proj_speed = 5, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self, t)) end, getDamageStat = function(self, t) return 2 + math.ceil(t.getDamage(self, t) / 15) end, action = function(self, t) local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="temporal_bolt"}} local x, y = self:getTarget(tg) if not x or not y then return nil end + self:projectile(tg, x, y, DamageType.CLOCK, self:spellCrit(t.getDamage(self, t))) game:playSoundNear(self, "talents/spell_generic2") @@ -64,7 +65,7 @@ newTalent{ } newTalent{ - name = "Temporal Fugue", + name = "Temporal Fugue Old", type = {"chronomancy/age-manipulation", 2}, require = chrono_req2, points = 5, @@ -114,7 +115,7 @@ newTalent{ target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 135, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 135, getParadoxSpellpower(self, t)) end, getDuration = function(self, t) return 5 + math.ceil(self:getTalentLevel(t)) end, direct_hit = true, requires_target = true, @@ -163,7 +164,7 @@ newTalent{ return nb end }, is_heal = true, - getHeal = function(self, t) return self:combatTalentSpellDamage(t, 40, 440, getParadoxSpellpower(self)) end, + getHeal = function(self, t) return self:combatTalentSpellDamage(t, 40, 440, getParadoxSpellpower(self, t)) end, getRemoveCount = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end, action = function(self, t) self:attr("allow_on_heal", 1) diff --git a/game/modules/tome/data/talents/chronomancy/anomalies.lua b/game/modules/tome/data/talents/chronomancy/anomalies.lua index 7636f689e2611ef651e379ad94c91a8f5ea7f1aa..270b3e9c16f89bf1d25c142c3a050ae630832dcc 100644 --- a/game/modules/tome/data/talents/chronomancy/anomalies.lua +++ b/game/modules/tome/data/talents/chronomancy/anomalies.lua @@ -25,37 +25,37 @@ local Object = require "engine.Object" local Trap = require "mod.class.Trap" -- All anomaly damage functions come from here with a bit of randomness thrown in to mix it up -getAnomalyDamage = function(self) - local dam = self:combatScale(getParadoxSpellpower(self), 20, 10, 220, 100, 0.75) +getAnomalyDamage = function(self, t) + local dam = self:combatScale(getParadoxSpellpower(self, t), 20, 10, 220, 100, 0.75) return rng.avg(dam / 3, dam, 3) end -getAnomalyDamageAoE = function(self) - local dam = self:combatScale(getParadoxSpellpower(self), 10, 10, 50, 100, 0.75) +getAnomalyDamageAoE = function(self, t) + local dam = self:combatScale(getParadoxSpellpower(self, t), 10, 10, 50, 100, 0.75) return rng.avg(dam / 3, dam, 3) end -- Here we have Effect Power and Anomaly duration, similar to damage but less random -getAnomalyEffectPower = function(self) - local dam = self:combatScale(getParadoxSpellpower(self), 10, 10, 50, 100, 0.75) +getAnomalyEffectPower = function(self, t) + local dam = self:combatScale(getParadoxSpellpower(self, t), 10, 10, 50, 100, 0.75) return math.ceil(rng.avg(dam / 2, dam, 3)) end -getAnomalyDuration = function(self) - local dam = self:combatScale(getParadoxSpellpower(self), 4, 10, 12, 100, 0.75) +getAnomalyDuration = function(self, t) + local dam = self:combatScale(getParadoxSpellpower(self, t), 4, 10, 12, 100, 0.75) return math.ceil(rng.avg(dam / 2, dam, 3)) end -- Determines the anomaly range based on current spellpower -- Generally we just use range = 10 for anomalies, this is for stuff with much longer range (such as teleports) -getAnomalyRange = function(self) - local range = math.floor(self:combatLimit(getParadoxSpellpower(self), 80, 20, 20, 40, 100)) +getAnomalyRange = function(self, t) + local range = math.floor(self:combatLimit(getParadoxSpellpower(self, t), 80, 20, 20, 40, 100)) return range end -- Determines the anomaly radius based on current spellpower -getAnomalyRadius = function(self) - local radius = math.floor(self:combatLimit(getParadoxSpellpower(self),8, 2, 20, 6, 100)) +getAnomalyRadius = function(self, t) + local radius = math.floor(self:combatLimit(getParadoxSpellpower(self, t), 6, 2, 20, 4, 100)) return radius end @@ -102,7 +102,7 @@ end -- For this to work all anomalies have to have no_energy set to true in the talent table, otherwise it will use two turns checkAnomalyTargeting = function(self, t, tg) local x, y = self.x, self.y - if self:knowTalent(self.T_BIAS_WEAVE) and rng.percent(self:callTalent(self.T_BIAS_WEAVE, "getTargetChance")) then + if self:knowTalent(self.T_BIAS_WEAVE) and rng.percent(self:callTalent(self.T_BIAS_WEAVE, "getTargetChance")) and not self:attr("anomaly_forced_target") then if self == game.player then game.bignews:saySimple(180, "#STEEL_BLUE#Targeting %s", t.name) end @@ -121,12 +121,6 @@ end -- Check for effects when hit by an anomaly -- This is called before immunity is checked checkAnomalyTriggers = function(self, target) - if target:knowTalent(target.T_WEBS_OF_FATE) then - target:forceUseTalent(target.T_WEBS_OF_FATE, {ignore_cd=true, ignore_energy=true, ignore_ressources=true}) - end - if target:knowTalent(target.T_SEAL_FATE) then - target:forceUseTalent(target.T_SEAL_FATE, {ignore_cd=true, ignore_energy=true, ignore_ressources=true}) - end if target:hasEffect(target.EFF_TRIM_THREADS) then local eff = target:hasEffect(target.EFF_TRIM_THREADS) eff.src:callTalent(eff.src.T_TRIM_THREADS, "doAnomaly", target, eff) @@ -142,9 +136,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -186,9 +180,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, - range = function(self, t) return getAnomalyRange(self) end, - radius = function(self, t) return getAnomalyRadius(self) end, + cooldown =0, + range = function(self, t) return getAnomalyRange(self, t) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -231,9 +225,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -244,7 +238,7 @@ newTalent{ action = function(self, t) local tg = self:getTalentTarget(t) local x, y = checkAnomalyTargeting(self, t, tg) - local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t), true) + local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRange(t), true) -- Randomly take targets if #tgts <= 0 then return nil end @@ -286,9 +280,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -309,7 +303,7 @@ newTalent{ checkAnomalyTriggers(self, self) if a:canBe("anomaly") then - self:setEffect(self.EFF_DISPLACEMENT_SHIELD, getAnomalyDuration(self)*2, {power=getAnomalyDamage(self)*4, target=a, chance=50}) + self:setEffect(self.EFF_DISPLACEMENT_SHIELD, getAnomalyDuration(self, t)*2, {power=getAnomalyDamage(self, t)*4, target=a, chance=50}) end game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport") @@ -319,7 +313,7 @@ newTalent{ end, info = function(self, t) return ([[50%% chance that damage the caster takes will be warped to a set target. - Once the maximum damage (%d) is absorbed, the time runs out, or the target dies, the shield will crumble.]]):format(getAnomalyDamage(self)*2) + Once the maximum damage (%d) is absorbed, the time runs out, or the target dies, the shield will crumble.]]):format(getAnomalyDamage(self, t)*2) end, } @@ -331,7 +325,7 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, direct_hit = true, no_energy = true, @@ -356,7 +350,7 @@ newTalent{ image = "terrain/wormhole.png", display = '&', color_r=255, color_g=255, color_b=255, back_color=colors.STEEL_BLUE, message = "@Target@ moves onto the wormhole.", - temporary = getAnomalyDuration(self), + temporary = getAnomalyDuration(self, t), x = start_x, y = start_y, canAct = false, energy = {value=0}, @@ -364,12 +358,12 @@ newTalent{ summoned_by = self, -- "summoner" is immune to it's own traps triggered = function(self, x, y, who) if who == self.summoned_by or who:canBe("teleport") then - -- since we're using a precise teleport we'll look for a free grid first - local tx, ty = util.findFreeGrid(self.dest.x, self.dest.y, 5, true, {[engine.Map.ACTOR]=true}) - if tx and ty then - if not who:teleportRandom(tx, ty, 0) then - game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize()) - end + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") + if not who:teleportRandom(self.dest.x, self.dest.y, 3, 1) then + game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize()) + elseif who ~= self.summoned_by then + who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power}) + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") end else game.logSeen(who, "%s ignores the wormhole.", who.name:capitalize()) @@ -423,9 +417,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -444,7 +438,7 @@ newTalent{ table.remove(tgts, id) checkAnomalyTriggers(self, a) - a:setEffect(a.EFF_PROBABILITY_TRAVEL, getAnomalyDuration(self)*2, {power=getAnomalyDuration(self)}) + a:setEffect(a.EFF_PROBABILITY_TRAVEL, getAnomalyDuration(self, t)*2, {power=getAnomalyDuration(self, t)}) game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport") game:playSoundNear(self, "talents/spell_generic") @@ -454,7 +448,7 @@ newTalent{ end, info = function(self, t) return ([[Allows up to five targets in a radius of %d to travel up to %d tiles through walls.]]): - format(getAnomalyDuration(self)*2, getAnomalyDuration(self)) + format(getAnomalyDuration(self, t)*2, getAnomalyDuration(self, t)) end, } @@ -466,9 +460,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -487,7 +481,7 @@ newTalent{ table.remove(tgts, id) checkAnomalyTriggers(self, a) - a:setEffect(a.EFF_BLINK, getAnomalyDuration(self)*2, {power=getAnomalyDuration(self)}) + a:setEffect(a.EFF_BLINK, getAnomalyDuration(self, t)*2, {power=getAnomalyDuration(self, t)}) game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport") game:playSoundNear(self, "talents/spell_generic") @@ -497,7 +491,7 @@ newTalent{ end, info = function(self, t) return ([[Up to five targets in a radius of %d are teleporting %d tiles every turn.]]): - format(self:getTalentRadius(t), getAnomalyDuration(self)) + format(self:getTalentRadius(t), getAnomalyDuration(self, t)) end, } @@ -509,16 +503,16 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, requires_target = true, no_energy = true, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} end, - getSummonTime = function(self, t) return math.ceil(getAnomalyDuration(self)*2) end, + getSummonTime = function(self, t) return math.ceil(getAnomalyDuration(self, t)*2) end, message = "Some innocent bystanders have been teleported into the fight.", action = function(self, t) local tg = self:getTalentTarget(t) @@ -618,16 +612,16 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} end, - getSlow = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self)) / 100) end, + getSlow = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self, t)) / 100) end, message = "@Source@ creates a bubble of slow time.", action = function(self, t) local tg = self:getTalentTarget(t) @@ -641,7 +635,7 @@ newTalent{ checkAnomalyTriggers(self, a) if a:canBe("anomaly") then - a:setEffect(a.EFF_SLOW, getAnomalyDuration(self), {power=t.getSlow(self, t), no_ct_effect=true}) + a:setEffect(a.EFF_SLOW, getAnomalyDuration(self, t), {power=t.getSlow(self, t), no_ct_effect=true}) end game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y}) game:playSoundNear(self, "talents/spell_generic") @@ -662,16 +656,16 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), nowarning=true} end, - getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self)) / 100) end, + getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self, t)) / 100) end, message = "@Source@ creates a bubble of fast time.", action = function(self, t) local tg = self:getTalentTarget(t) @@ -684,7 +678,7 @@ newTalent{ table.remove(tgts, id) checkAnomalyTriggers(self, a) - a:setEffect(a.EFF_SPEED, getAnomalyDuration(self), {power=t.getHaste(self, t)}) + a:setEffect(a.EFF_SPEED, getAnomalyDuration(self, t), {power=t.getHaste(self, t)}) game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y}) game:playSoundNear(self, "talents/spell_generic") end @@ -705,9 +699,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -727,7 +721,7 @@ newTalent{ checkAnomalyTriggers(self, a) if a:canBe("anomaly") and a:canBe("stun") then - a:setEffect(a.EFF_STUNNED, getAnomalyDuration(self)/2, {no_ct_effect=true, apply_power=getParadoxSpellpower(self)}) + a:setEffect(a.EFF_STUNNED, getAnomalyDuration(self, t)/2, {no_ct_effect=true, apply_power=getParadoxSpellpower(self, t)}) end game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y}) game:playSoundNear(self, "talents/spell_generic") @@ -749,9 +743,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -771,7 +765,7 @@ newTalent{ checkAnomalyTriggers(self, a) if a:canBe("anomaly") then - a:setEffect(a.EFF_TIME_PRISON, getAnomalyDuration(self), {no_ct_effect=true}) + a:setEffect(a.EFF_TIME_PRISON, getAnomalyDuration(self, t), {no_ct_effect=true}) end game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y}) game:playSoundNear(self, "talents/spell_generic") @@ -793,9 +787,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -814,7 +808,7 @@ newTalent{ table.remove(tgts, id) checkAnomalyTriggers(self, a) - a:setEffect(a.EFF_TIME_SHIELD, getAnomalyDuration(self), {power=getAnomalyDamage(self)*4, dot_dur=5, time_reducer=40}) + a:setEffect(a.EFF_TIME_SHIELD, getAnomalyDuration(self, t), {power=getAnomalyDamage(self, t)*4, dot_dur=5, time_reducer=40}) game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y}) game:playSoundNear(self, "talents/spell_generic") end @@ -835,9 +829,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -856,7 +850,7 @@ newTalent{ table.remove(tgts, id) checkAnomalyTriggers(self, a) - a:setEffect(a.EFF_INVIGORATE, getAnomalyDuration(self), {power=getAnomalyEffectPower(self)/10}) + a:setEffect(a.EFF_INVIGORATE, getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)/10}) game.level.map:particleEmitter(x, y, tg.radius, "ball_temporal", {radius=self:getTalentRadius(t), tx=x, ty=y}) game:playSoundNear(self, "talents/spell_generic") end @@ -877,9 +871,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -893,20 +887,22 @@ newTalent{ local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t)) -- Randomly take targets - if #tgts <= 0 then return end + if #tgts <= 0 then return true end local a, id = rng.table(tgts) table.remove(tgts, id) checkAnomalyTriggers(self, a) local x, y = util.findFreeGrid(a.x, a.y, 3, true, {[Map.ACTOR]=true}) if not x then - return + return true end if a:attr("summon_time") then return end - local m = makeParadoxClone(self, a, getAnomalyDuration(self)*2) + local m = makeParadoxClone(self, a, getAnomalyDuration(self, t)*2) m.ai_state = { talent_in=2, ally_compassion=10} game.zone:addEntity(game.level, m, "actor", x, y) game.level.map:particleEmitter(x, y, 1, "generic_teleport", {rm=60, rM=130, gm=20, gM=110, bm=90, bM=130, am=70, aM=180}) + + return true end, info = function(self, t) return ([[Clones a random creature within range.]]):format() @@ -921,9 +917,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown = 0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -937,8 +933,8 @@ newTalent{ -- Add a lasting map effect game.level.map:addEffect(self, - x, y, getAnomalyDuration(self), - DamageType.TEMPORAL, getAnomalyDamageAoE(self), + x, y, getAnomalyDuration(self, t)/2, + DamageType.TEMPORAL, getAnomalyDamageAoE(self, t), tg.radius, 5, nil, engine.MapEffect.new{alpha=85, color_br=200, color_bg=200, color_bb=0, effect_shader="shader_images/paradox_effect.png"}, @@ -947,8 +943,10 @@ newTalent{ return true end, info = function(self, t) - return ([[Creates a temporal storm for %d turns that deals %d temporal damage each turn.]]) - :format(getAnomalyDuration(self), damDesc(self, DamageType.TEMPORAL, getAnomalyDamageAoE(self))) + local duration = self:combatScale(getParadoxSpellpower(self, t), 4, 10, 12, 100, 0.75)/2 + local damage = self:combatScale(getParadoxSpellpower(self, t), 10, 10, 50, 100, 0.75) + return ([[Creates a temporal storm for %d to %d turns that deals between %0.2f and %0.2f temporal damage each turn.]]) + :format(duration/2, duration, damDesc(self, DamageType.TEMPORAL, damage/3), damDesc(self, DamageType.TEMPORAL, damage)) end, } @@ -961,9 +959,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1005,9 +1003,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1037,7 +1035,7 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, radius = 1, direct_hit = true, @@ -1070,7 +1068,7 @@ newTalent{ show_tooltip = true, block_move = true, block_sight = true, - temporary = getAnomalyDuration(self), + temporary = getAnomalyDuration(self, t), x = x + i, y = y + j, canAct = false, act = function(self) @@ -1113,9 +1111,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1146,7 +1144,7 @@ newTalent{ if not target.talents_cd[tid] and t.mode == "activated" and not t.innate then tids[#tids+1] = t end end for i = 1, rng.avg(3, 6, 3) do - local power = getAnomalyDuration(self) + local power = getAnomalyDuration(self, t) local t = rng.tableRemove(tids) if not t then break end target.talents_cd[t.id] = rng.range(2, power) @@ -1160,7 +1158,7 @@ newTalent{ end, info = function(self, t) return ([[Places between three and six talents of up to 5 targets in a radius %d ball on cooldown for up to %d turns.]]): - format(getAnomalyRadius(self), getAnomalyDuration(self)) + format(getAnomalyRadius(self, t), getAnomalyDuration(self, t)) end, } @@ -1172,9 +1170,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1195,7 +1193,7 @@ newTalent{ checkAnomalyTriggers(self, a) if a:canBe("anomaly") and a:canBe("pin") then - a:setEffect(self.EFF_PINNED, getAnomalyDuration(self), {apply_power=getParadoxSpellpower(self)}) + a:setEffect(self.EFF_PINNED, getAnomalyDuration(self, t), {apply_power=getParadoxSpellpower(self, t)}) end end @@ -1216,9 +1214,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1231,8 +1229,8 @@ newTalent{ local x, y = checkAnomalyTargeting(self, t, tg) -- Don't bury the player - if self ~= game.player and not game.player:knowTalent(game.player.T_DIG_OBJECT) then - return + if not game.player:knowTalent(game.player.T_DIG_OBJECT) then + return true end self:doQuake(tg, x, y) @@ -1240,50 +1238,7 @@ newTalent{ end, info = function(self, t) return ([[Causes an earthquake in a radius of %d.]]): - format(getAnomalyRadius(self)) - end, -} - -newTalent{ - name = "Anomaly Calcify", - type = {"chronomancy/anomalies", 1}, - anomaly_type = "physical", - type_no_req = true, - no_unlearn_last = true, - points = 1, - paradox = 0, - cooldown = 1, - range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, - direct_hit = true, - no_energy = true, - requires_target = true, - target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} - end, - message = "@Source@ calcifies several targets.", - action = function(self, t) - local tg = self:getTalentTarget(t) - local x, y = checkAnomalyTargeting(self, t, tg) - local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t)) - - -- Randomly take targets - for i = 1, rng.avg(1, 5, 3) do - if #tgts <= 0 then break end - local a, id = rng.table(tgts) - table.remove(tgts, id) - checkAnomalyTriggers(self, a) - - if a:canBe("anomaly") and a:canBe("stun") and a:canBe("stone") and a:canBe("instakill")then - a:setEffect(a.EFF_STONED, getAnomalyDuration(self), {apply_power=getParadoxSpellpower(self)}) - game.level.map:particleEmitter(a.x, a.y, 1, "archery") - end - end - return true - end, - info = function(self, t) - return ([[Turns up to 5 targets in a radius %d ball to stone for %d turns.]]): - format(getAnomalyRadius(self), getAnomalyDuration(self)) + format(getAnomalyRadius(self, t)) end, } @@ -1295,9 +1250,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1317,14 +1272,14 @@ newTalent{ table.remove(tgts, id) checkAnomalyTriggers(self, a) - a:setEffect(self.EFF_FLAWED_DESIGN, getAnomalyDuration(self), {power=getAnomalyEffectPower(self)}) + a:setEffect(self.EFF_FLAWED_DESIGN, getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)}) game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport") game:playSoundNear(self, "talents/spell_generic") end return true end, info = function(self, t) - return ([[Reduces the resistances of up to five targets in a ball of radius %d by %d%%.]]):format(self:getTalentRadius(t), getAnomalyEffectPower(self)) + return ([[Reduces the resistances of up to five targets in a ball of radius %d by %d%%.]]):format(self:getTalentRadius(t), getAnomalyEffectPower(self, t)) end, } @@ -1336,9 +1291,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1351,7 +1306,7 @@ newTalent{ local x, y = checkAnomalyTargeting(self, t, tg) local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRange(t)) - local dam = getAnomalyRadius(self) -- not a typo, very low damage since this isn't a major anomaly + local dam = getAnomalyRadius(self, t) -- not a typo, very low damage since this isn't a major anomaly -- Randomly take targets for i = 1, rng.avg(3, 6, 3) do @@ -1398,9 +1353,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1412,7 +1367,7 @@ newTalent{ local tg = self:getTalentTarget(t) local tgts = getAnomalyTargets(self, t, self.x, self.y, "ACTOR", self:getTalentRange(t)) - local movedam = self:spellCrit(getAnomalyDamageAoE(self)) + local movedam = self:spellCrit(getAnomalyDamage(self, t)) -- Randomly take targets for i = 1, rng.avg(3, 6, 3) do @@ -1448,6 +1403,49 @@ newTalent{ end, } +newTalent{ + name = "Anomaly Calcify", + type = {"chronomancy/anomalies", 1}, + anomaly_type = "major", + type_no_req = true, + no_unlearn_last = true, + points = 1, + paradox = 0, + cooldown =0, + range = 10, + radius = function(self, t) return getAnomalyRadius(self, t) end, + direct_hit = true, + no_energy = true, + requires_target = true, + target = function(self, t) + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} + end, + message = "@Source@ calcifies several targets.", + action = function(self, t) + local tg = self:getTalentTarget(t) + local x, y = checkAnomalyTargeting(self, t, tg) + local tgts = getAnomalyTargets(self, t, x, y, "ACTOR", self:getTalentRadius(t)) + + -- Randomly take targets + for i = 1, rng.avg(1, 5, 3) do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + checkAnomalyTriggers(self, a) + + if a:canBe("anomaly") and a:canBe("stun") and a:canBe("stone") and a:canBe("instakill")then + a:setEffect(a.EFF_STONED, getAnomalyDuration(self, t), {apply_power=getParadoxSpellpower(self, t)}) + game.level.map:particleEmitter(a.x, a.y, 1, "archery") + end + end + return true + end, + info = function(self, t) + return ([[Turns up to 5 targets in a radius %d ball to stone for %d turns.]]): + format(getAnomalyRadius(self, t), getAnomalyDuration(self, t)) + end, +} + newTalent{ name = "Anomaly Call", type = {"chronomancy/anomalies", 1}, @@ -1456,9 +1454,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 50, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1499,16 +1497,16 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, target = function(self, t) return {type="hit", range=self:getTalentRange(t), talent=t} end, - getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self)) / 100) end, + getHaste = function(self, t) return 1 - 1 / (1 + (getAnomalyEffectPower(self, t)) / 100) end, message = "The odds have tilted.", action = function(self, t) local tg = self:getTalentTarget(t) @@ -1520,15 +1518,15 @@ newTalent{ table.remove(tgts, id) checkAnomalyTriggers(self, a) - a:setEffect(self.EFF_SPEED, getAnomalyDuration(self), {power=t.getHaste(self, t)}) - a:setEffect(self.EFF_REGENERATION, getAnomalyDuration(self), {power=getAnomalyEffectPower(self)}) - a:setEffect(self.EFF_PAIN_SUPPRESSION, getAnomalyDuration(self), {power=getAnomalyEffectPower(self)}) + a:setEffect(self.EFF_SPEED, getAnomalyDuration(self, t), {power=t.getHaste(self, t)}) + a:setEffect(self.EFF_REGENERATION, getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)}) + a:setEffect(self.EFF_PAIN_SUPPRESSION, getAnomalyDuration(self, t), {power=getAnomalyEffectPower(self, t)}) game.level.map:particleEmitter(a.x, a.y, 1, "temporal_teleport") game:playSoundNear(self, "talents/spell_generic") return true end, info = function(self, t) - return ([[Substantially toughens and hastes one target for %d turns.]]):format(getAnomalyDuration(self)) + return ([[Substantially toughens and hastes one target for %d turns.]]):format(getAnomalyDuration(self, t)) end, } @@ -1540,9 +1538,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1558,7 +1556,7 @@ newTalent{ return end if self:attr("summon_time") then return end - local m = makeParadoxClone(self, self, getAnomalyDuration(self)*2) + local m = makeParadoxClone(self, self, getAnomalyDuration(self, t)*2) game.zone:addEntity(game.level, m, "actor", x, y) m.faction = "enemies" m.summoner = nil @@ -1569,7 +1567,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Clones the caster.]]):format(getAnomalyDuration(self)) + return ([[Clones the caster.]]):format(getAnomalyDuration(self, t)) end, } @@ -1581,9 +1579,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1607,7 +1605,7 @@ newTalent{ return end if a:attr("summon_time") then return end - local m = makeParadoxClone(self, a, getAnomalyDuration(self)*2) + local m = makeParadoxClone(self, a, getAnomalyDuration(self, t)*2) game.zone:addEntity(game.level, m, "actor", x, y) m.ai_state = { talent_in=1, ally_compassion=10} game.level.map:particleEmitter(x, y, 1, "generic_teleport", {rm=60, rM=130, gm=20, gM=110, bm=90, bM=130, am=70, aM=180}) @@ -1616,7 +1614,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Clones all creatures in a radius of 10.]]):format(getAnomalyDuration(self)) + return ([[Clones all creatures in a radius of 10.]]):format(getAnomalyDuration(self, t)) end, } @@ -1628,9 +1626,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1662,9 +1660,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1676,7 +1674,7 @@ newTalent{ local tg = self:getTalentTarget(t) local tgts = getAnomalyTargets(self, t, self.x, self.y, "ACTOR", self:getTalentRange(t)) - local movedam = self:spellCrit(getAnomalyDamage(self)) -- High damage + local movedam = self:spellCrit(getAnomalyDamage(self, t)) -- High damage -- Randomly take targets if #tgts <= 0 then return end @@ -1720,9 +1718,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1734,8 +1732,9 @@ newTalent{ local tg = self:getTalentTarget(t) local tgts = getAnomalyTargets(self, t, self.x, self.y, "ACTOR", self:getTalentRange(t)) - local movedam = self:spellCrit(getAnomalyDamageAoE(self)) - local dam = self:spellCrit(getAnomalyDamage(self)/2) + local movedam = self:spellCrit(getAnomalyDamage(self, t)) + local dam = self:spellCrit(getAnomalyDamage(self, t)/2) + local apply_power = getParadoxSpellpower(self, t) -- Randomly take targets for i = 1, rng.avg(3, 6, 3) do @@ -1748,7 +1747,7 @@ newTalent{ local proj = require("mod.class.Projectile"):makeHoming( self, {particle="bolt_lightning", trail="lightningtrail"}, - {speed=2, name="Tornado", dam=dam, movedam=movedam, start_x=orig_x, start_y=orig_y}, + {speed=2, name="Tornado", dam=dam, movedam=movedam, start_x=orig_x, start_y=orig_y, apply=apply_power}, target, self:getTalentRange(t), function(self, src) @@ -1760,7 +1759,7 @@ newTalent{ src:project({type="ball", radius=1, x=self.x, y=self.y}, self.x, self.y, DT.LIGHTNING, self.def.dam) src:project({type="ball", radius=1, x=self.x, y=self.y}, self.x, self.y, DT.MINDKNOCKBACK, self.def.dam) if target:canBe("stun") then - target:setEffect(target.EFF_STUNNED, 4, {apply_power=getParadoxSpellpower(src)}) + target:setEffect(target.EFF_STUNNED, 4, {apply_power=self.def.apply}) else game.logSeen(target, "%s resists the tornado!", target.name:capitalize()) end @@ -1796,9 +1795,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1860,7 +1859,7 @@ newTalent{ local target = game.level.map(px, py, engine.Map.ACTOR) if target then if target:canBe("stun") then - target:setEffect(target.EFF_STUNNED, 3, {apply_power=src:combatSpellpower()}) + target:setEffect(target.EFF_STUNNED, 3, {apply_power=getParadoxSpellpower(src, t)}) else game.logSeen(target, "%s resists the stun!", target.name:capitalize()) end @@ -1871,7 +1870,7 @@ newTalent{ end end - local dam = self:spellCrit(getAnomalyDamage(self)) + local dam = self:spellCrit(getAnomalyDamage(self, t)) meteor(self, x, y, dam) return true @@ -1890,9 +1889,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, @@ -1923,7 +1922,7 @@ newTalent{ end return true end, - temporary = getAnomalyDuration(self)*10, + temporary = getAnomalyDuration(self, t)*10, x = tx, y = ty, canAct = false, energy = {value=0}, @@ -1972,9 +1971,9 @@ newTalent{ no_unlearn_last = true, points = 1, paradox = 0, - cooldown = 1, + cooldown =0, range = 10, - radius = function(self, t) return getAnomalyRadius(self) end, + radius = function(self, t) return getAnomalyRadius(self, t) end, direct_hit = true, no_energy = true, requires_target = true, diff --git a/game/modules/tome/data/talents/chronomancy/blade-threading.lua b/game/modules/tome/data/talents/chronomancy/blade-threading.lua index ab1997ab060f68eee7b28875bc29eff107115744..75472cf5a8618d06b1ee183a1f0ce7e0f297ec9c 100644 --- a/game/modules/tome/data/talents/chronomancy/blade-threading.lua +++ b/game/modules/tome/data/talents/chronomancy/blade-threading.lua @@ -28,9 +28,10 @@ newTalent{ paradox = function (self, t) return getParadoxCost(self, t, 10) end, tactical = { ATTACK = {weapon = 2}, DISABLE = 3 }, requires_target = true, - no_energy = "fake", + speed = "weapon", getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, + getWarp = function(self, t) return 7 + self:combatSpellpower(0.092) * self:combatTalentScale(t, 1, 7) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end, on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "dual") then if not silent then game.logPlayer(self, "You require two weapons to use this talent.") end return false end return true end, action = function(self, t) local dam, swap = doWardenWeaponSwap(self, t, t.getDamage(self, t)) @@ -39,49 +40,26 @@ newTalent{ local x, y, target = self:getTarget(tg) if not x or not y or not target then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end if core.fov.distance(self.x, self.y, x, y) > 1 then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end - local hitted = self:attackTarget(target, DamageType.MATTER, dam) - + + -- Hit? + local hitted = self:attackTarget(target, nil, dam, true) + + -- Project our warp if hitted then - local chance = rng.range(1, 4) - if chance == 1 then - if target:canBe("stun") then - target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)}) - else - game.logSeen(target, "%s resists the stun!", target.name:capitalize()) - end - elseif chance == 2 then - if target:canBe("blind") then - target:setEffect(target.EFF_BLINDED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)}) - else - game.logSeen(target, "%s resists the blindness!", target.name:capitalize()) - end - elseif chance == 3 then - if target:canBe("pin") then - target:setEffect(target.EFF_PINNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)}) - else - game.logSeen(target, "%s resists the pin!", target.name:capitalize()) - end - elseif chance == 4 then - if target:canBe("confusion") then - target:setEffect(target.EFF_CONFUSED, t.getDuration(self, t), {power=50, apply_power=getParadoxSpellpower(self)}) - else - game.logSeen(target, "%s resists the confusion!", target.name:capitalize()) - end - end - + self:project({type="hit"}, target.x, target.y, DamageType.WARP, {dam=self:spellCrit(t.getWarp(self, t)), chance=100, dur=t.getDuration(self, t), apply_power=getParadoxSpellpower(self, t)}) game.level.map:particleEmitter(target.x, target.y, 1, "generic_discharge", {rm=64, rM=64, gm=134, gM=134, bm=170, bM=170, am=35, aM=90}) - --game:playSoundNear(self, "talents/distortion") - end - + end + return true end, info = function(self, t) local damage = t.getDamage(self, t) * 100 local duration = t.getDuration(self, t) - return ([[Attack the target with your melee weapons for %d%%. Half of this damage will be temporal and half physical. - If the attack hits the target may be stunned, blinded, pinned, or confused for %d turns. - Chance of applying a random effect improves with your Spellpower.]]) - :format(damage, duration) + local warp = t.getWarp(self, t) + return ([[Attack the target with your melee weapons for %d%%. + If the attack hits you'll warp the target, dealing %0.2f temporal and %0.2f physical damage, and may stun, blind, pin, or confuse them for %d turns. + The bonus damage improves with your Spellpower.]]) + :format(damage, damDesc(self, DamageType.TEMPORAL, warp/2), damDesc(self, DamageType.PHYSICAL, warp/2), duration) end } @@ -117,19 +95,17 @@ newTalent{ paradox = function (self, t) return getParadoxCost(self, t, 15) end, tactical = { ATTACKAREA = {weapon = 2}, DISABLE = 3 }, requires_target = true, - no_energy = "fake", + speed = "weapon", getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, - getPower = function(self, t) return self:combatTalentSpellDamage(t, 50, 150, getParadoxSpellpower(self)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end, + getPower = function(self, t) return self:combatTalentSpellDamage(t, 50, 150, getParadoxSpellpower(self, t)) end, on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "dual") then if not silent then game.logPlayer(self, "You require two weapons to use this talent.") end return false end return true end, action = function(self, t) local dam, swap = doWardenWeaponSwap(self, t, t.getDamage(self, t)) local tg = {type="hit", range=self:getTalentRange(t), talent=t} local x, y, target = self:getTarget(tg) if not x or not y or not target then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end - local hit1 = false - local hit2 = false - local hit3 = false + local braid_targets = {} -- do the braid if core.fov.distance(self.x, self.y, x, y) == 1 then @@ -138,35 +114,30 @@ newTalent{ local lx, ly = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).left) local rx, ry = util.coordAddDir(self.x, self.y, util.dirSides(dir, self.x, self.y).right) local lt, rt = game.level.map(lx, ly, Map.ACTOR), game.level.map(rx, ry, Map.ACTOR) - + -- target hit - hit1 = self:attackTarget(target, nil, damage) + local hit1 = self:attackTarget(target, nil, damage, true) + if hit1 then braid_targets[#braid_targets+1] = target end --left hit - if lt then - hit2 = self:attackTarget(lt, nil, damage, true) + if lt and self:reactionToward(lt) < 0 then + local hit2 = self:attackTarget(lt, nil, damage, true) + if hit2 then braid_targets[#braid_targets+1] = lt end end --right hit - if rt then - hit3 = self:attackTarget(rt, nil, damage, true) + if rt and self:reactionToward(rt) < 0 then + local hit3 = self:attackTarget(rt, nil, damage, true) + if hit3 then braid_targets[#braid_targets+1] = rt end end - -- Braid them; no save - if hit1 then - if hit2 or hit3 then - target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, braid_one=lt or nil, braid_two=rt or nil}) - end - end - if hit2 then - if hit1 or hit3 then - lt:setEffect(lt.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, braid_one=target or nil, braid_two=rt or nil}) - end - end - if hit3 then - if hit1 or hit2 then - rt:setEffect(rt.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, braid_one=target or nil, braid_two=lt or nil}) + -- if we hit more than one, braid them + if #braid_targets > 1 then + for i = 1, #braid_targets do + local target = braid_targets[i] + target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getPower(self, t), src=self, targets=braid_targets}) end end + else if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil @@ -195,8 +166,7 @@ newTalent{ tactical = { ATTACKAREA = {weapon = 2}, ATTACK = {weapon = 2}, }, requires_target = true, is_teleport = true, - direct_hit = true, - no_energy = "fake", + speed = "weapon", getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.6, 1.2) end, getTeleports = function(self, t) return self:getTalentLevel(t) >= 5 and 2 or 1 end, on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "dual") then if not silent then game.logPlayer(self, "You require two weapons to use this talent.") end return false end return true end, @@ -208,7 +178,7 @@ newTalent{ -- Hit the target if core.fov.distance(self.x, self.y, target.x, target.y) > 1 then if swap then doWardenWeaponSwap(self, t, nil, "bow") end return nil end - local hitted = self:attackTarget(target, nil, dam) + local hitted = self:attackTarget(target, nil, dam, true) if hitted then -- Get available targets @@ -219,6 +189,7 @@ newTalent{ local a = game.level.map(x, y, Map.ACTOR) if a and self:reactionToward(a) < 0 and self:hasLOS(a.x, a.y) then tgts[#tgts+1] = a + print("Temporal Assault Target %s", a.name) end end end @@ -229,7 +200,7 @@ newTalent{ local a, id = rng.table(tgts) -- since we're using a precise teleport we'll look for a free grid first local tx2, ty2 = util.findFreeGrid(a.x, a.y, 5, true, {[Map.ACTOR]=true}) - if tx2 and ty2 then + if tx2 and ty2 and not a.dead then game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") if not self:teleportRandom(tx2, ty2, 0) then attempts = attempts - 1 diff --git a/game/modules/tome/data/talents/chronomancy/bow-threading.lua b/game/modules/tome/data/talents/chronomancy/bow-threading.lua index 042fbe96e221f9065820c0a23ea2513cdcbaa842..b2fe1e6afcffabbf5d36397daf6eb74f2dcf54b5 100644 --- a/game/modules/tome/data/talents/chronomancy/bow-threading.lua +++ b/game/modules/tome/data/talents/chronomancy/bow-threading.lua @@ -29,7 +29,7 @@ newTalent{ tactical = { BUFF = 2 }, points = 5, getDamage = function(self, t) return 7 + self:combatSpellpower(0.092) * self:combatTalentScale(t, 1, 7) end, - getApplyPower = function(self, t) return getParadoxSpellpower(self) end, + getApplyPower = function(self, t) return getParadoxSpellpower(self, t) end, activate = function(self, t) return {} end, @@ -52,7 +52,7 @@ newTalent{ tactical = { ATTACK = {weapon = 2} }, requires_target = true, range = archery_range, - no_energy = "fake", + speed = 'archery', getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.1, 2.2) end, getParadoxReduction = function(self, t) return math.floor(self:combatTalentScale(t, 10, 20)) end, on_pre_use = function(self, t, silent) if not doWardenPreUse(self, "bow") then if not silent then game.logPlayer(self, "You require a bow to use this talent.") end return false end return true end, @@ -62,9 +62,9 @@ newTalent{ action = function(self, t) local dam, swap = doWardenWeaponSwap(self, t, t.getDamage(self, t)) - local targets = self:archeryAcquireTargets(nil, {one_shot=true, infinite=true}) + local targets = self:archeryAcquireTargets({type="bolt"}, {one_shot=true, infinite=true, no_energy = true}) if not targets then if swap then doWardenWeaponSwap(self, t, nil, "blade") end return end - self:archeryShoot(targets, t, nil, {mult=dam, damtype=DamageType.TEMPORAL}) + self:archeryShoot(targets, t, {type="bolt"}, {mult=dam, damtype=DamageType.TEMPORAL}) return true end, @@ -88,9 +88,9 @@ newTalent{ requires_target = true, range = archery_range, radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.7)) end, - no_energy = "fake", + speed = 'archery', getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end, - getDamageAoE = function(self, t) return self:combatTalentSpellDamage(t, 10, 170, getParadoxSpellpower(self)) end, + getDamageAoE = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self, t)) end, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t} end, @@ -101,17 +101,45 @@ newTalent{ if not x or not y then return nil end local _ _, _, _, x, y = self:canProject(tg, x, y) - local dam = self:spellCrit(t.getDamageAoE(self, t)) - local grids = self:project(tg, x, y, function(px, py) + local tgts = {} + self:project(tg, x, y, function(px, py) local target = game.level.map(px, py, Map.ACTOR) - if not target then return end - local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) - if tx and ty and target:canBe("knockback") then - target:move(tx, ty, true) - game.logSeen(target, "%s is drawn in by the singularity!", target.name:capitalize()) + if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then + -- If we've already moved this target don't move it again + for _, v in pairs(tgts) do + if v == target then + return + end + end + + -- Do our Knockback + local can = function(target) + if target:checkHit(getParadoxSpellpower(self, t), target:combatPhysicalResist(), 0, 95) and target:canBe("knockback") then -- Deprecated Checkhit call + return true + else + game.logSeen(target, "%s resists the knockback!", target.name:capitalize()) + end + end + if can(target) then + target:pull(x, y, tg.radius, can) + tgts[#tgts+1] = target + game.logSeen(target, "%s is drawn in by the singularity!", target.name:capitalize()) + target:crossTierEffect(target.EFF_OFFBALANCE, getParadoxSpellpower(self, t)) + end end end) - self:project(tg, x, y, DamageType.GRAVITY, self:spellCrit(dam)) + + -- 25% bonus damage per target beyond the first + local dam = self:spellCrit(t.getDamageAoE(self, t)) + dam = dam + math.min(dam, dam*(#tgts-1)/4) + + -- Project our damage last based on number of targets hit + self:project(tg, x, y, function(px, py) + local dist_factor = 1 + (core.fov.distance(x, y, px, py)/5) + local damage = dam/dist_factor + DamageType:get(DamageType.GRAVITY).projector(self, px, py, DamageType.GRAVITY, damage) + end) + game.level.map:particleEmitter(x, y, tg.radius, "gravity_spike", {radius=tg.radius, allow=core.shader.allow("distort")}) game:playSoundNear(self, "talents/earth") @@ -127,9 +155,9 @@ newTalent{ tg.type = "bolt" -- switch our targeting back to a bolt - local targets = self:archeryAcquireTargets(self:getTalentTarget(t), {one_shot=true, x=x, y=y}) + local targets = self:archeryAcquireTargets(tg, {one_shot=true, x=x, y=y, no_energy = true}) if not targets then return end - self:archeryShoot(targets, t, nil, {mult=dam}) + self:archeryShoot(targets, t, {type="bolt"}, {mult=dam}) return true end, @@ -137,9 +165,11 @@ newTalent{ local damage = t.getDamage(self, t) * 100 local radius = self:getTalentRadius(t) local aoe = t.getDamageAoE(self, t) - return ([[Fire a shot doing %d%% damage. When the arrow reaches its destination it will draw in creatures in a radius of %d and inflict %0.2f additional physical damage. - The additional damage scales with your Spellpower and inflicts 50%% extra damage to pinned targets.]]) - :format(damage, radius, damDesc(self, DamageType.PHYSICAL, aoe)) + return ([[Fire a shot doing %d%% damage. When the arrow reaches its destination it will draw in creatures in a radius of %d and inflict %0.2f physical damage. + Each target moved beyond the first deals an additional %0.2f physical damage (up to %0.2f bonus damage). + Targets take reduced damage the further they are from the epicenter (20%% less per tile). + The additional damage scales with your Spellpower.]]) + :format(damage, radius, damDesc(self, DamageType.PHYSICAL, aoe), damDesc(self, DamageType.PHYSICAL, aoe/4), damDesc(self, DamageType.PHYSICAL, aoe)) end } @@ -154,7 +184,7 @@ newTalent{ requires_target = true, range = archery_range, radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.7)) end, - no_energy = "fake", + speed = 'archery', getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end, getClones = function(self, t) return self:getTalentLevel(t) >= 5 and 3 or self:getTalentLevel(t) >= 3 and 2 or 1 end, target = function(self, t) @@ -176,7 +206,7 @@ newTalent{ return nil end - local targets = self:archeryAcquireTargets(self:getTalentTarget(t), {one_shot=true, x=x, y=y}) + local targets = self:archeryAcquireTargets(self:getTalentTarget(t), {one_shot=true, x=x, y=y, no_energy = true}) if not targets then return end self:archeryShoot(targets, t, {type="bolt", friendlyfire=false, friendlyblock=false}, {mult=dam}) @@ -204,6 +234,7 @@ newTalent{ m.arrow_stitched_clone = true m.generic_damage_penalty = 50 m.energy.value = 1000 + m:attr("archery_pass_friendly", 1) m.on_act = function(self) if not self.shoot_target.dead then self:forceUseTalent(self.T_ARROW_STITCHING, {force_level=t.leve, ignore_cd=true, ignore_energy=true, force_target=self.shoot_target, ignore_ressources=true, silent=true}) diff --git a/game/modules/tome/data/talents/chronomancy/chronomancer.lua b/game/modules/tome/data/talents/chronomancy/chronomancer.lua index 9da046ab2d22c7b3dfeffebb9c2045464c6bf329..d99be34c9b5d4c018b87307e23c59e40ebe96be5 100644 --- a/game/modules/tome/data/talents/chronomancy/chronomancer.lua +++ b/game/modules/tome/data/talents/chronomancy/chronomancer.lua @@ -24,18 +24,18 @@ newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronoma newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/threaded-combat", name = "Threaded Combat", min_lev = 10, description = "A blend of ranged and dual-weapon combat." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/temporal-hounds", name = "Temporal Hounds", min_lev = 10, description = "Call temporal hounds to aid you in combat." } -newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/energy", name = "energy", generic = true, description = "Manipulate raw energy by addition or subtraction." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/fate-threading", name = "Fate Threading", description = "Manipulate the threads of fate." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/gravity", name = "gravity", description = "Call upon the force of gravity to crush, push, and pull your foes." } -newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/matter", name = "matter", description = "Change and shape matter itself." } -newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/paradox", name = "paradox", min_lev = 10, description = "Create loopholes in the laws of spacetime." } -newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/speed-control", name = "Speed Control", description = "Control how fast objects and creatures move through spacetime." } -newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timeline-threading", name = "Timeline Threading", min_lev = 10, description = "Examine and alter the timelines that make up the spacetime continuum." } -newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timetravel", name = "Time Travel", min_lev = 10, description = "Travel through time yourself, or send your foes into the future." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spacetime-folding", name = "Spacetime Folding", description = "Mastery of folding points in space." } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/speed-control", name = "Speed Control", description = "Control how fast objects and creatures move through spacetime." } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timeline-threading", name = "Timeline Threading", description = "Examine and alter the timelines that make up the spacetime continuum." } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/timetravel", name = "timetravel", description = "Directly manipulate the flow of time" } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spatial-tears", name = "Spatial Tears", min_lev = 10, description = "Manipulate topological defects in the fabric of spacetime." } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spellbinding", name = "Spellbinding", min_lev = 10, description = "Manipulate chronomantic spells." } -- Generic Chronomancy newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/chronomancy", name = "Chronomancy", generic = true, description = "Allows you to glimpse the future, or become more aware of the present." } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/energy", name = "energy", generic = true, min_lev = 10, description = "Manipulate raw energy by addition or subtraction." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/fate-weaving", name = "Fate Weaving", generic = true, description = "Weave the threads of fate." } newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/spacetime-weaving", name = "Spacetime Weaving", generic = true, description = "Weave the threads of spacetime." } @@ -43,11 +43,11 @@ newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronoma newTalentType{ no_silence=true, is_spell=true, type="chronomancy/other", name = "Other", generic = true, description = "Miscellaneous Chronomancy effects." } newTalentType{ no_silence=true, is_spell=true, type="chronomancy/age-manipulation", name = "Age Manipulation", description = "Manipulate the age of creatures you encounter." } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/matter", name = "matter", description = "Change and shape matter itself." } +newTalentType{ allow_random=true, no_silence=true, is_spell=true, type="chronomancy/paradox", name = "paradox", description = "Break the laws of spacetime." } newTalentType{ no_silence=true, is_spell=true, type="chronomancy/temporal-archery", name = "Temporal Archery", description = "A blend of chronomancy and ranged combat." } newTalentType{ no_silence=true, is_spell=true, type="chronomancy/temporal-combat", name = "Temporal Combat", description = "A blend of chronomancy and physical combat." } - - -- Anomalies are not learnable but can occur instead of an intended spell when paradox gets to high. newTalentType{ no_silence=true, is_spell=true, type="chronomancy/anomalies", name = "anomalies", description = "Spacetime anomalies that can randomly occur when paradox is to high." } @@ -127,6 +127,7 @@ load("/data/talents/chronomancy/gravity.lua") load("/data/talents/chronomancy/matter.lua") load("/data/talents/chronomancy/paradox.lua") load("/data/talents/chronomancy/speed-control.lua") +load("/data/talents/chronomancy/spellbinding.lua") load("/data/talents/chronomancy/temporal-archery.lua") load("/data/talents/chronomancy/temporal-combat.lua") load("/data/talents/chronomancy/guardian.lua") @@ -136,6 +137,7 @@ load("/data/talents/chronomancy/timeline-threading.lua") load("/data/talents/chronomancy/timetravel.lua") load("/data/talents/chronomancy/spacetime-folding.lua") load("/data/talents/chronomancy/spacetime-weaving.lua") +load("/data/talents/chronomancy/spatial-tears.lua") -- Loads many functions and misc. talents load("/data/talents/chronomancy/other.lua") diff --git a/game/modules/tome/data/talents/chronomancy/chronomancy.lua b/game/modules/tome/data/talents/chronomancy/chronomancy.lua index 3fd24d8e57655f61daa57ec09833f96fe14c5372..9526791660f0e475b3df6c7fff37dcca92dc1855 100644 --- a/game/modules/tome/data/talents/chronomancy/chronomancy.lua +++ b/game/modules/tome/data/talents/chronomancy/chronomancy.lua @@ -17,7 +17,7 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org --- EDGE TODO: Icons, Particles, Timed Effect Particles +-- EDGE TODO: Particles, Timed Effect Particles newTalent{ name = "Precognition", @@ -27,8 +27,8 @@ newTalent{ paradox = function (self, t) return getParadoxCost(self, t, 10) end, cooldown = 20, no_npc_use = true, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 14)) end, - range = function(self, t) return 10 + math.min(self:combatTalentSpellDamage(t, 10, 20, getParadoxSpellpower(self))) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 14))) end, + range = function(self, t) return 10 + math.min(self:combatTalentSpellDamage(t, 10, 20, getParadoxSpellpower(self, t))) end, action = function(self, t) -- Foresight bonuses local defense = 0 @@ -86,13 +86,16 @@ newTalent{ sustain_paradox = 36, mode = "sustained", no_sustain_autoreset = true, - cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 15, 45, 25)) end, -- Limit >15 + cooldown = 50, + getTrigger= function(self, t) return self:combatTalentScale(t, 0.25, 0.45, 0.6) end, tactical = { DEFEND = 2 }, - no_npc_use = true, + no_npc_use = true, -- so rares don't learn useless talents + allow_temporal_clones = true, -- let clones copy it anyway so they can benefit from the effects + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- but don't let them cast it callbackOnHit = function(self, t, cb) local p = self:isTalentActive(t.id) local life_after = self.life - cb.value - local cont_trigger = self.max_life * 0.3 + local cont_trigger = self.max_life * t.getTrigger(self, t) -- Cast our contingent spell if p and p.rest_count <= 0 and cont_trigger > life_after then @@ -100,15 +103,18 @@ newTalent{ local cont_id = self:getTalentFromId(cont_t) local t_level = math.min(self:getTalentLevel(t), self:getTalentLevel(cont_t)) - -- Make sure we still know the talent and that the preuse conditions apply - if t_level == 0 or not self:preUseTalent(cont_id, true, true) then + -- Make sure we still know the talent and that the pre-use conditions apply + if t_level == 0 or not self:knowTalent(cont_id) or not self:preUseTalent(cont_id, true, true) then game.logPlayer(self, "#LIGHT_RED#Your Contingency has failed to cast %s!", self:getTalentFromId(cont_t).name) else - self:forceUseTalent(cont_t, {ignore_ressources=true, ignore_cd=true, ignore_energy=true, force_level=t_level}) game.logPlayer(self, "#STEEL_BLUE#Your Contingency triggered %s!", self:getTalentFromId(cont_t).name) + p.rest_count = self:getTalentCooldown(t) + game:onTickEnd(function() + if not self.dead then + self:forceUseTalent(cont_t, {ignore_ressources=true, ignore_cd=true, ignore_energy=true, force_level=t_level}) + end + end) end - - p.rest_count = self:getTalentCooldown(t) end return cb.value @@ -124,13 +130,9 @@ newTalent{ return tostring(math.ceil(val)), fnt end, activate = function(self, t) - local d = require("mod.dialogs.talents.ChronomancyContingency").new(self) - game:registerDialog(d) - local co = coroutine.running() - d.unload = function() coroutine.resume(co, d.contingecy_talent) end - if not coroutine.yield() then return nil end - local talent = d.contingecy_talent - + local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyContingency").new(self)) + if not talent then return nil end + return { talent = talent, rest_count = 0 } @@ -139,14 +141,15 @@ newTalent{ return true end, info = function(self, t) + local trigger = t.getTrigger(self, t) * 100 local cooldown = self:getTalentCooldown(t) local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None" - return ([[Choose an activatable spell that's not targeted. When you take damage that reduces your life below 30%% the spell will automatically cast. + return ([[Choose an activatable spell that's not targeted. When you take damage that reduces your life below %d%% the spell will automatically cast. This spell will cast even if it is currently on cooldown, will not consume a turn or resources, and uses the talent level of Contingency or its own, whichever is lower. This effect can only occur once every %d turns and takes place after the damage is resolved. Current Contingency Spell: %s]]): - format(cooldown, talent) + format(trigger, cooldown, talent) end, } @@ -157,8 +160,10 @@ newTalent{ points = 5, paradox = function (self, t) return getParadoxCost(self, t, 20) end, cooldown = 50, - no_npc_use = true, - getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 10, 25)) end, + no_npc_use = true, -- so rares don't learn useless talents + allow_temporal_clones = true, -- let clones copy it anyway so they can benefit from the effects + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- but don't let them cast it + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(self:getTalentLevel(t), 10, 25))) end, on_pre_use = function(self, t, silent) if checkTimeline(self) then if not silent then @@ -195,7 +200,7 @@ newTalent{ return ([[You peer into three possible futures, allowing you to explore each for %d turns. When the effect expires, you'll choose which of the three futures becomes your present. If you know Foresight you'll gain additional defense and chance to shrug off critical hits (equal to your Foresight values) while See the Threads is active. This spell splits the timeline. Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful. - Note that seeing visions of your own death can still be fatal. + If you die in any thread you'll revert the timeline to the point when you first cast the spell and the effect will end. This spell may only be used once per zone level.]]) :format(duration) end, diff --git a/game/modules/tome/data/talents/chronomancy/energy.lua b/game/modules/tome/data/talents/chronomancy/energy.lua index 89418eaefbcab9e4a82496b9f3f038d0bde41a44..8c0e321de999ee077f9d6f662154a17922f35eae 100644 --- a/game/modules/tome/data/talents/chronomancy/energy.lua +++ b/game/modules/tome/data/talents/chronomancy/energy.lua @@ -17,23 +17,27 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org +-- EDGE TODO: Particles, Timed Effect Particles + newTalent{ name = "Energy Decomposition", type = {"chronomancy/energy",1}, mode = "sustained", - require = chrono_req1, + require = chrono_req_high1, points = 5, - sustain_paradox = 20, + sustain_paradox = 24, cooldown = 10, tactical = { BUFF = 2 }, - getAbsorption = function(self, t) return self:combatTalentSpellDamage(t, 5, 150, getParadoxSpellpower(self)) end, -- Increase shield strength - on_damage = function(self, t, damtype, dam) - if not DamageType:get(damtype).antimagic_resolve then return dam end - local absorb = t.getAbsorption(self, t) - -- works like armor with 30% hardiness for projected energy effects - dam = math.max(dam * 0.3 - absorb, 0) + (dam * 0.7) + getDecomposition = function(self, t) return self:combatTalentSpellDamage(t, 5, 150, getParadoxSpellpower(self, t)) end, -- Increase shield strength + callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr) + local decomp = t.getDecomposition(self, t) + local lastdam = dam + + -- works like armor with 30% hardiness + dam = math.max(dam * 0.3 - decomp, 0) + (dam * 0.7) print("[PROJECTOR] after static reduction dam", dam) - return dam + game:delayedLogDamage(src or self, self, 0, ("%s(%d dissipated)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", lastdam - dam), false) + return {dam=dam} end, activate = function(self, t) game:playSoundNear(self, "talents/heal") @@ -46,67 +50,41 @@ newTalent{ return true end, info = function(self, t) - local absorption = t.getAbsorption(self, t) - return ([[Partially dissipates all incoming energy damage (all except mind and physical damage), reducing it by 30%%, up to a maximum of %d. - The maximum damage reduction will scale with your Spellpower.]]):format(absorption) - end, -} - -newTalent{ - name = "Entropic Field", - type = {"chronomancy/energy",2}, - mode = "sustained", - require = chrono_req2, - points = 5, - sustain_paradox = 20, - cooldown = 10, - tactical = { BUFF = 2 }, - getPower = function(self, t) return math.min(90, 10 + self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self))) end, - activate = function(self, t) - game:playSoundNear(self, "talents/heal") - return { - particle = self:addParticles(Particles.new("time_shield", 1)), - phys = self:addTemporaryValue("resists", {[DamageType.PHYSICAL]=t.getPower(self, t)/2}), - proj = self:addTemporaryValue("slow_projectiles", t.getPower(self, t)), - } - end, - deactivate = function(self, t, p) - self:removeParticles(p.particle) - self:removeTemporaryValue("resists", p.phys) - self:removeTemporaryValue("slow_projectiles", p.proj) - return true - end, - info = function(self, t) - local power = t.getPower(self, t) - return ([[You encase yourself in a field that slows incoming projectiles by %d%%, and increases your physical resistance by %d%%. - The effect will scale with your Spellpower.]]):format(power, power / 2) + local decomp = t.getDecomposition(self, t) + return ([[Partially dissipates all incoming damage, reducing it by 30%%, up to a maximum of %d. + The maximum damage reduction will scale with your Spellpower.]]):format(decomp) end, } newTalent{ name = "Energy Absorption", - type = {"chronomancy/energy", 3}, - require = chrono_req3, + type = {"chronomancy/energy", 2}, + require = chrono_req_high2, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 20) end, - cooldown = 10, + paradox = function (self, t) return getParadoxCost(self, t, 10) end, + cooldown = 6, + fixed_cooldown = true, tactical = { DISABLE = 2 }, direct_hit = true, requires_target = true, - range = 6, + range = 10, getTalentCount = function(self, t) - return 1 + math.floor(self:combatTalentScale(math.max(1, self:getTalentLevel(t)), 0.5, 2.5, "log")) + return 1 + math.floor(self:combatTalentLimit(t, 3, 0, 2)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self, t)) end, getCooldown = function(self, t) return math.ceil(self:combatTalentScale(t, 1, 2.6)) end, + target = function (self, t) + return {type="hit", range=self:getTalentRange(t), talent=t} + end, action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t)} + local tg = self:getTalentTarget(t) local tx, ty = self:getTarget(tg) if not tx or not ty then return nil end local _ _, tx, ty = self:canProject(tg, tx, ty) local target = game.level.map(tx, ty, Map.ACTOR) if not target then return end - if not self:checkHit(self:combatSpellpower(), target:combatSpellResist()) then + if not self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist()) then game.logSeen(target, "%s resists!", target.name:capitalize()) return true end @@ -124,7 +102,7 @@ newTalent{ local t = rng.tableRemove(tids) if not t then break end target.talents_cd[t.id] = cdr - game.logSeen(target, "%s's %s is disrupted by the Energy Absorption!", target.name:capitalize(), t.name) + game.logSeen(target, "%s's %s is disrupted by the Energy Drain!", target.name:capitalize(), t.name) count = count + 1 end @@ -132,17 +110,22 @@ newTalent{ local tids = {} for tid, _ in pairs(self.talents_cd) do local tt = self:getTalentFromId(tid) - if tt.type[1]:find("^chronomancy/") then - tids[#tids+1] = tid + if tt.type[1]:find("^chronomancy/") and not tt.fixed_cooldown then + tids[#tids+1] = tt end end + for i = 1, count do if #tids == 0 then break end local tid = rng.tableRemove(tids) - self.talents_cd[tid] = self.talents_cd[tid] - cdr + self:alterTalentCoolingdown(tid, - cdr) end + + -- Deal our damage in one lump sum + self:project(tg, target.x, target.y, DamageType.TEMPORAL, self:spellCrit(count * t.getDamage(self, t))) end - target:crossTierEffect(target.EFF_SPELLSHOCKED, self:combatSpellpower()) + + target:crossTierEffect(target.EFF_SPELLSHOCKED, getParadoxSpellpower(self, t)) game.level.map:particleEmitter(tx, ty, 1, "generic_charge", {rm=10, rM=110, gm=10, gM=50, bm=20, bM=125, am=25, aM=255}) game.level.map:particleEmitter(self.x, self.y, 1, "generic_charge", {rm=200, rM=255, gm=200, gM=255, bm=200, bM=255, am=125, aM=125}) game:playSoundNear(self, "talents/spell_generic") @@ -151,33 +134,73 @@ newTalent{ info = function(self, t) local talentcount = t.getTalentCount(self, t) local cooldown = t.getCooldown(self, t) + local damage = t.getDamage(self, t) return ([[You sap the target's energy and add it to your own, placing up to %d random talents on cooldown for %d turns. - For each talent put on cooldown, you reduce the cooldown of one of your chronomancy talents currently on cooldown by %d turns.]]): - format(talentcount, cooldown, cooldown) + For each talent put on cooldown, you reduce the cooldown of one of your chronomancy talents currently on cooldown by %d turns and deal %0.2f temporal damage to the target. + The damage done will scale with your Spellpower.]]): + format(talentcount, cooldown, cooldown, damDesc(self, DamageType.TEMPORAL, damage)) end, } newTalent{ name = "Redux", - type = {"chronomancy/energy",4}, - require = chrono_req4, + type = {"chronomancy/energy",3}, + require = chrono_req_high3, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 40) end, - cooldown = 12, + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 24, tactical = { BUFF = 2 }, - no_energy = true, - getMaxLevel = function(self, t) return self:getTalentLevel(t) end, + fixed_cooldown = true, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 2, 4))) end, + getMaxCooldown = function(self, t) return math.floor(self:combatTalentScale(t, 3, 8)) end, action = function(self, t) -- effect is handled in actor postUse - self:setEffect(self.EFF_REDUX, 5, {}) + self:setEffect(self.EFF_REDUX, t.getDuration(self, t), {max_cd=t.getMaxCooldown(self, t)}) game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) - local maxlevel = t.getMaxLevel(self, t) - return ([[You may recast the next activated chronomancy spell (up to talent level %0.1f) that you cast within the next 5 turns on the turn following its initial casting. - The Paradox cost of the initial spell will be paid each time it is cast, and the second casting will still consume a turn. - This spell takes no time to cast.]]): - format(maxlevel) + local duration = t.getDuration(self, t) + local cooldown = t.getMaxCooldown(self, t) + return ([[For the next %d turns chronomancy spells with a cooldown of %d or less have a cooldown of one.]]): + format(duration, cooldown) end, } + +newTalent{ + name = "Entropy", + type = {"chronomancy/energy",4}, + require = chrono_req_high4, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 12, + tactical = { ATTACK = { TEMPORAL = 2 }, DEBUFF=3 }, + range = 10, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 100, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end, + target = function(self, t) + return {type="hit", range=self:getTalentRange(t), talent=t} + end, + requires_target = true, + direct_hit = true, + action = function(self, t) + local tg = self:getTalentTarget(t) + local x, y, target = self:getTarget(tg) + if not x or not y or not target then return nil end + local _ _, x, y = self:canProject(tg, x, y) + + local damage = self:spellCrit(t.getDamage(self, t)) + target:setEffect(target.EFF_ENTROPY, t.getDuration(self, t), {power=damage, src=self, apply_power=getParadoxSpellpower(self, t)}) + + game:playSoundNear(self, "talents/dispel") + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local duration = t.getDuration(self, t) + return ([[Over the next %d turns all beneficial timed effects on the target tick twice as fast. + Each timed effect affected by this talent deals %0.2f temporal damage to the target. + The damage will scale with your Spellpower.]]):format(duration, damDesc(self, DamageType.TEMPORAL, damage)) + end, +} \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/fate-threading.lua b/game/modules/tome/data/talents/chronomancy/fate-threading.lua index 71cb732e3305c4f163641bd52045713e5e904031..9d42d04a953c474db9d8f5ec15c70121633510ac 100644 --- a/game/modules/tome/data/talents/chronomancy/fate-threading.lua +++ b/game/modules/tome/data/talents/chronomancy/fate-threading.lua @@ -20,19 +20,111 @@ -- EDGE TODO: Particles, Timed Effect Particles newTalent{ - name = "Trim Threads", + name = "Disentangle", type = {"chronomancy/fate-threading", 1}, require = chrono_req1, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 10) end, + cooldown = 12, + tactical = { PARADOX = 2 }, + getReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self, t)) end, + getParadoxMulti = function(self, t) return self:combatTalentLimit(t, 2, 0.10, .75) end, + anomaly_type = "no-major", + no_energy = true, + passives = function(self, t, p) + self:talentTemporaryValue(p, "anomaly_paradox_recovery", t.getParadoxMulti(self, t)) + end, + action = function(self, t) + local reduction = self:spellCrit(t.getReduction(self, t)) + self:paradoxDoAnomaly(reduction, t.anomaly_type, "forced") + return true + end, + info = function(self, t) + local reduction = t.getReduction(self, t) + local paradox = 100 * t.getParadoxMulti(self, t) + return ([[Disentangle the timeline, reducing your Paradox by %d and creating an anomaly. This spell will never produce a major anomaly. + Additionally you recover %d%% more Paradox from random anomalies (%d%% total). + The Paradox reduction will increase with your Spellpower.]]):format(reduction, paradox, paradox + 200) + end, +} + +newTalent{ + name = "Preserve Pattern", + type = {"chronomancy/fate-threading", 2}, + require = chrono_req2, + mode = "sustained", + sustain_paradox = 0, + points = 5, + cooldown = 10, + tactical = { DEFEND = 2 }, + getPercent = function(self, t) return self:combatTalentLimit(t, 50, 10, 30)/100 end, -- Limit < 50% + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 6))) end, + getConversionRatio = function(self, t) return 200 / self:combatTalentSpellDamage(t, 60, 600) end, + damage_feedback = function(self, t, p, src) + if p.particle and p.particle._shader and p.particle._shader.shad and src and src.x and src.y then + local r = -rng.float(0.2, 0.4) + local a = math.atan2(src.y - self.y, src.x - self.x) + p.particle._shader:setUniform("impact", {math.cos(a) * r, math.sin(a) * r}) + p.particle._shader:setUniform("impact_tick", core.game.getTime()) + end + end, + iconOverlay = function(self, t, p) + local val = p.rest_count or 0 + if val <= 0 then return "" end + local fnt = "buff_font" + return tostring(math.ceil(val)), fnt + end, + doPerservePattern = function(self, t, src, dam) + local absorb = dam * t.getPercent(self, t) + local paradox = absorb*t.getConversionRatio(self, t) + self:setEffect(self.EFF_PRESERVE_PATTERN, t.getDuration(self, t), {paradox=paradox/t.getDuration(self, t), no_ct_effect=true}) + game:delayedLogMessage(self, nil, "preserve pattern", "#LIGHT_BLUE##Source# converts damage to paradox!") + game:delayedLogDamage(src, self, 0, ("#LIGHT_BLUE#(%d converted)#LAST#"):format(absorb), false) + dam = dam - absorb + + return dam + end, + activate = function(self, t) + game:playSoundNear(self, "talents/arcane") + + local particle + if core.shader.active(4) then + particle = self:addParticles(Particles.new("shader_shield", 1, {size_factor=1.4, img="runicshield"}, {type="runicshield", shieldIntensity=0.14, ellipsoidalFactor=1, scrollingSpeed=-1, time_factor=12000, bubbleColor={1, 0.5, 0.3, 0.2}, auraColor={1, 0.5, 0.3, 1}})) + else + particle = self:addParticles(Particles.new("time_shield", 1)) + end + + return { + particle = particle, + } + end, + deactivate = function(self, t, p) + self:removeParticles(p.particle) + return true + end, + info = function(self, t) + local ratio = t.getPercent(self, t) * 100 + local absorb = t.getConversionRatio(self, t) * 100 + local duration = t.getDuration(self, t) + return ([[While active, %d%% of all damage you take increases your Paradox by %d%% of the damage absorbed over %d turns. + The amount of Paradox damage you recieve will be reduced by your Spellpower.]]): + format(ratio, absorb, duration) + end, +} + +newTalent{ + name = "Trim Threads", + type = {"chronomancy/fate-threading", 3}, + require = chrono_req3, + points = 5, cooldown = 4, tactical = { ATTACKAREA = { TEMPORAL = 2 } }, range = 10, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 2.5)) end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 2)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, 4) end, + getReduction = function(self, t) return self:getTalentLevel(t) * 2 end, target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t} + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, nowarning=true, talent=t} end, requires_target = true, direct_hit = true, @@ -50,60 +142,37 @@ newTalent{ self:project(tg, x, y, function(px, py) local target = game.level.map(px, py, Map.ACTOR) if not target then return end - target:setEffect(target.EFF_TRIM_THREADS, 3, {power=damage/3, src=self, apply_power=getParadoxSpellpower(self)}) + target:setEffect(target.EFF_TRIM_THREADS, t.getDuration(self, t), {power=damage/4, src=self, reduction=t.getReduction(self, t), apply_power=getParadoxSpellpower(self, t)}) end) game.level.map:particleEmitter(x, y, tg.radius, "temporal_flash", {radius=tg.radius}) - game:playSoundNear(self, "talents/dispel") + game:playSoundNear(self, "talents/tidalwave") return true end, info = function(self, t) local damage = t.getDamage(self, t) + local duration = t.getDuration(self, t) local radius = self:getTalentRadius(t) - return ([[Deals %0.2f temporal damage over three turns to all targets in a radius of %d. If the target is hit by an Anomaly the remaining damage will be done instantly. - The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), radius) - end, -} - -newTalent{ - name = "Disentangle", - type = {"chronomancy/fate-threading", 2}, - require = chrono_req2, - points = 5, - cooldown = 12, - tactical = { PARADOX = 2 }, - getReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)) end, - getParadoxMulti = function(self, t) return self:combatTalentLimit(t, 2, 0.10, .75) end, - anomaly_type = "no-major", - no_energy = true, - passives = function(self, t, p) - self:talentTemporaryValue(p, "anomaly_paradox_recovery", t.getParadoxMulti(self, t)) - end, - action = function(self, t) - local reduction = self:spellCrit(t.getReduction(self, t)) - self:paradoxDoAnomaly(reduction, t.anomaly_type, "forced") - return true - end, - info = function(self, t) local reduction = t.getReduction(self, t) - local paradox = 100 * t.getParadoxMulti(self, t) - return ([[Disentangle the timeline, reducing your Paradox by %d and creating an anomaly. This spell will never produce a major anomaly. - Additionally you recover %d%% more Paradox from random anomalies (%d%% total). - The Paradox reduction will increase with your Spellpower.]]):format(reduction, paradox, paradox + 200) + return ([[Deals %0.2f temporal damage over %d turns to all other targets in a radius of %d. If the target is slain before the effect expires you'll recover %d Paradox. + If the target is hit by an Anomaly the remaining damage will be done instantly. + The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration, radius, reduction) end, } newTalent{ name = "Bias Weave", - type = {"chronomancy/fate-threading", 3}, - require = chrono_req3, + type = {"chronomancy/fate-threading", 4}, + require = chrono_req4, points = 5, cooldown = 10, -- Anomaly biases can be set manually for monsters -- Use the following format anomaly_bias = { type = "teleport", chance=50} - on_pre_use = function(self, t, silent) if not self == game.player then return false end return true end, + no_npc_use = true, -- so rares don't learn useless talents + allow_temporal_clones = true, -- let clones copy it anyway so they can benefit from the effects + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- but don't let them cast it getBiasChance = function(self, t) return self:combatTalentLimit(t, 100, 10, 75) end, getTargetChance = function(self, t) return self:combatTalentLimit(t, 100, 10, 75) end, getAnomalySpeed = function(self, t) return self:combatTalentLimit(t, 1, 0.10, .75) end, @@ -141,72 +210,4 @@ newTalent{ Additionally random anomalies only cost you %d%% of a turn rather than a full turn when they occur. Major anomalies, those occuring when your modified Paradox is over 600, are not affected by this talent.]]):format(target_chance, bias_chance, anomaly_recovery) end, -} - -newTalent{ - name = "Preserve Pattern", - type = {"chronomancy/fate-threading", 4}, - require = chrono_req4, - points = 5, - mode = "sustained", - sustain_paradox = 48, - cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 15, 45, 25)) end, -- Limit >15 - tactical = { DEFEND = 2 }, - getHeal = function(self, t) return self.max_life * self:combatTalentLimit(t, 1.5, 0.09, 0.4) end, -- Limit < 150% max life (to survive a large string of hits between turns) - callbackOnHit = function(self, t, cb) - local p = self:isTalentActive(t.id) - - if p and p.rest_count <= 0 and cb.value >= self.life then - -- Save them from the hit and heal up - cb.value = 0 - self.life = 1 - self:heal(t.getHeal(self, t)) - - -- Make them invulnerable so they don't die to anomalies - game.logSeen(self, "#STEEL_BLUE#%s is rapidly shunted into another timeline!#LAST#", self.name:capitalize()) - local invuln = self.invulnerable - self.invulnerable = 1 - - -- Make some anomalies and remove invulnerability - for i = 1, rng.avg(3, 6, 3) do self:paradoxDoAnomaly(0, nil, "forced", self, true) end - self.invulnerable = invuln - - -- Set the counter on the sustain - p.rest_count = self:getTalentCooldown(t) - if self.player then world:gainAchievement("AVOID_DEATH", self) end - end - - return cb.value - end, - callbackOnActBase = function(self, t) - local p = self:isTalentActive(t.id) - if p.rest_count > 0 then p.rest_count = p.rest_count - 1 end - end, - iconOverlay = function(self, t, p) - local val = p.rest_count or 0 - if val <= 0 then return "" end - local fnt = "buff_font" - return tostring(math.ceil(val)), fnt - end, - activate = function(self, t) - game:playSoundNear(self, "talents/heal") - local ret = { rest_count = 0 } - --[[if core.shader.active(4) then - ret.particle = self:addParticles(Particles.new("shader_ring_rotating", 1, {toback=true, a=0.6, rotation=0, radius=2, img="flamesgeneric"}, {type="sunaura", time_factor=6000})) - else - ret.particle = self:addParticles(Particles.new("golden_shield", 1)) - end]] - return ret - end, - deactivate = function(self, t, p) - -- self:removeParticles(p.particle) - return true - end, - info = function(self, t) - local heal = t.getHeal(self, t) - local cooldown = self:getTalentCooldown(t) - return ([[Any attack that would drop you below 1 hit point instead triggers Preserve Pattern, setting your life to 1, then healing you for %d. - This effect causes three to six anomalies to occur as you move from timeline to timeline until you find one in which you're still alive. - These anomalies may not be targeted though they may be biased. This effect can only occur once every %d turns.]]):format(heal, cooldown) - end, } \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/fate-weaving.lua b/game/modules/tome/data/talents/chronomancy/fate-weaving.lua index 4ca142407891f225b00ed4426164c212930d6076..4b94a54eb4d3f3dc7dbc5836b17cde2235bc2e7e 100644 --- a/game/modules/tome/data/talents/chronomancy/fate-weaving.lua +++ b/game/modules/tome/data/talents/chronomancy/fate-weaving.lua @@ -26,13 +26,31 @@ newTalent{ mode = "passive", points = 5, getSaveBonus = function(self, t) return math.ceil(self:combatTalentScale(t, 2, 8, 0.75)) end, - doSpinFate = function(self, t) - local save_bonus = t.getSaveBonus(self, t) - local resists = self:knowTalent(self.T_FATEWEAVER) and self:callTalent(self.T_FATEWEAVER, "getResist") or 0 - - self:setEffect(self.EFF_SPIN_FATE, 3, {save_bonus=save_bonus, resists=resists, spin=1, max_spin=3}) + callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr) + if dam > 0 and src ~= self then + if self.turn_procs and not self.turn_procs.spin_fate then + + self:setEffect(self.EFF_SPIN_FATE, 3, {save_bonus=t.getSaveBonus(self, t), spin=1, max_spin=3}) + + -- Set our turn procs, we do spin_fate last since it's the only one checked above + if self.hasEffect and self:hasEffect(self.EFF_WEBS_OF_FATE) and not self.turn_procs.spin_webs then + self.turn_procs.spin_webs = true + elseif self.hasEffect and self:hasEffect(self.EFF_SEAL_FATE) and not self.turn_procs.spin_seal then + self.turn_procs.spin_seal = true + else + self.turn_procs.spin_fate = true + end + + -- Reduce damage if we know Fateweaver + if self:knowTalent(self.T_FATEWEAVER) then + local reduction = dam * self:callTalent(self.T_FATEWEAVER, "getReduction") + dam = dam - reduction + game:delayedLogDamage(src, self, 0, ("%s(%d fatewever)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", reduction), false) + end + end + end - return true + return {dam=dam} end, info = function(self, t) local save = t.getSaveBonus(self, t) @@ -41,7 +59,6 @@ newTalent{ format(save, save * 3) end, } - newTalent{ name = "Webs of Fate", type = {"chronomancy/fate-weaving", 2}, @@ -50,15 +67,16 @@ newTalent{ paradox = function (self, t) return getParadoxCost(self, t, 10) end, cooldown = 12, tactical = { BUFF = 2, CLOSEIN = 2, ESCAPE = 2 }, - getPower = function(self, t) return self:combatTalentSpellDamage(t, 20, 100, getParadoxSpellpower(self))/100 end, - getDuration = function(self, t) return 5 end, + getPower = function(self, t) return paradoxTalentScale(self, t, 15, 30, 50)/100 end, + getDuration = function(self, t) return getExtensionModifier(self, t, 5) end, + no_energy = true, action = function(self, t) local effs = {} -- Find all pins for eff_id, p in pairs(self.tmp) do local e = self.tempeffect_def[eff_id] - if e.subtype.pin then + if e.subtype.pin or e.subtype.stun then effs[#effs+1] = {"effect", eff_id} end end @@ -73,27 +91,23 @@ newTalent{ end -- Set our power based on current spin - local move = t.getPower(self, t) - local pin = t.getPower(self, t)/2 + local imm = t.getPower(self, t) local eff = self:hasEffect(self.EFF_SPIN_FATE) if eff then - move = move * (1 + eff.spin/3) - pin = pin * (1 + eff.spin/3) + imm = imm * (1 + eff.spin/3) end - pin = math.min(1, pin) -- Limit 100% - self:setEffect(self.EFF_WEBS_OF_FATE, t.getDuration(self, t), {move=move, pin=pin}) + self:setEffect(self.EFF_WEBS_OF_FATE, t.getDuration(self, t), {imm=imm}) return true end, info = function(self, t) local power = t.getPower(self, t) * 100 local duration = t.getDuration(self, t) - return ([[Activate to remove pins. You also gain %d%% movement speed and %d%% pin immunity for %d turns. - If you have Spin Fate active these bonuses will be increased by 33%% per spin (up to a maximum of %d%% and %d%% respectively). - This spell will automatically cast when you're hit by most anomalies. This will not consume a turn or put the spell on cooldown. - While Webs of Fate is active you may gain one additional spin per turn. - These bonuses will scale with your Spellpower.]]):format(power, math.min(100, power/2), duration, power * 2, math.min(100, power/2 * 2)) + return ([[Activate to remove pins and stuns. You also gain %d%% pin and stun immunity for %d turns. + If you have Spin Fate active these bonuses will be increased by 33%% per spin (up to a maximum of %d%%). + While Webs of Fate is active you may gain one additional spin per turn. These bonuses will scale with your Spellpower.]]) + :format(power, duration, power * 2) end, } @@ -103,11 +117,12 @@ newTalent{ require = chrono_req3, mode = "passive", points = 5, - getResist = function(self, t) return self:combatTalentScale(t, 2, 8, 0.75) end, + getReduction = function(self, t) return paradoxTalentScale(self, t, 10, 30, 40)/100 end, info = function(self, t) - local resist = t.getResist(self, t) - return ([[You now gain %0.1f%% resist all when you gain spin with Spin Fate (up to a maximum of %0.1f%% resist all at three spin).]]): - format(resist, resist*3) + local reduction = t.getReduction(self, t)*100 + return ([[When Spin Fate is triggered you reduce the triggering damage by %d%%. + This effect scales with your Spellpower.]]): + format(reduction) end, } @@ -119,27 +134,18 @@ newTalent{ paradox = function (self, t) return getParadoxCost(self, t, 20) end, cooldown = 24, tactical = { BUFF = 2 }, - getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 25, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return 5 end, + getDuration = function(self, t) return getExtensionModifier(self, t, 5) end, + getProcs = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end, + no_energy = true, action = function(self, t) - -- Set our power based on current spin - local crits = t.getPower(self, t) - local eff = self:hasEffect(self.EFF_SPIN_FATE) - if eff then - crits = crits * (1 + eff.spin/3) - end - - self:setEffect(self.EFF_SEAL_FATE, t.getDuration(self, t), {crit=crits}) - + self:setEffect(self.EFF_SEAL_FATE, t.getDuration(self, t), {procs=t.getProcs(self, t)}) return true end, info = function(self, t) - local power = t.getPower(self, t) + local procs = t.getProcs(self, t) local duration = t.getDuration(self, t) - return ([[Activate to increase critical hit chance and critical damage by %d%% for five turns. - If you have Spin Fate active these bonuses will be increased by 33%% per spin (up to a maximum of %d%%). - This spell will automatically cast when you're hit by most anomalies. This will not consume a turn or put the spell on cooldown. - While Seal Fate is active you may gain one additional spin per turn. - These bonuses will scale with your Spellpower.]]):format(power, power * 2) + return ([[Activate to Seal Fate for %d turns. When you damage a target while Seal Fate is active you have a 50%% chance to increase the duration of one detrimental status effect on it by one turn. + If you have Spin Fate active the chance twill be increased by 33%% per Spin (to a maximum of 100%% at three Spin.) + This can occur at most %d times per turn. While Seal Fate is active you may gain one additional spin per turn.]]):format(duration, procs) end, } \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/gravity.lua b/game/modules/tome/data/talents/chronomancy/gravity.lua index f76e1f7f77240ff1d318a43f1b3e45a5fe55b53e..37c7e8861290cd7c33477e59e0745efed6a76a30 100644 --- a/game/modules/tome/data/talents/chronomancy/gravity.lua +++ b/game/modules/tome/data/talents/chronomancy/gravity.lua @@ -17,27 +17,71 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org +-- EDGE TODO: Particles, Timed Effect Particles + newTalent{ name = "Repulsion Blast", type = {"chronomancy/gravity",1}, require = chrono_req1, points = 5, paradox = function (self, t) return getParadoxCost(self, t, 10) end, - cooldown = 4, + cooldown = 3, tactical = { ATTACKAREA = {PHYSICAL = 2}, ESCAPE = 2 }, range = 0, radius = function(self, t) return math.floor(self:combatTalentScale(t, 4.5, 6.5)) end, requires_target = true, direct_hit = true, target = function(self, t) - return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=false, talent=t} + return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 170, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.REPULSION, self:spellCrit(t.getDamage(self, t))) + + -- Project our base damage + local dam = self:spellCrit(t.getDamage(self, t)) + local bonus_dam = dam/2 + self:project(tg, x, y, DamageType.GRAVITY, dam) + + -- Do our knockback + local tgts = {} + local grids = self:project(tg, x, y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then + -- If we've already moved this target don't move it again + for _, v in pairs(tgts) do + if v == target then + return + end + end + + local hit = target:checkHit(getParadoxSpellpower(self, t), target:combatPhysicalResist(), 0, 95) and target:canBe("knockback") + + if hit then + local slam = false + local dist = self:getTalentRadius(t) + 1 - core.fov.distance(self.x, self.y, px, py) + target:knockback(self.x, self.y, dist, false, function(g, x, y) + -- Deal our bonus damage + if game.level.map:checkAllEntities(x, y, "block_move", target) then + slam = true + self:project({type="hit"}, target.x, target.y, DamageType.GRAVITY, bonus_dam) + self:project({type="hit"}, x, y, DamageType.GRAVITY, bonus_dam) + game.logSeen(target, "%s slams into something solid!", target.name:capitalize()) + end + end) + + tgts[#tgts+1] = target + if not slam then game.logSeen(target, "%s is knocked back!", target.name:capitalize()) end + target:crossTierEffect(target.EFF_OFFBALANCE, getParadoxSpellpower(self, t)) + else + game.logSeen(target, "%s resists the knockback!", target.name:capitalize()) + end + + end + end) + game.level.map:particleEmitter(self.x, self.y, tg.radius, "gravity_breath", {radius=tg.radius, tx=x-self.x, ty=y-self.y, allow=core.shader.allow("distort")}) game:playSoundNear(self, "talents/earth") return true @@ -45,9 +89,9 @@ newTalent{ info = function(self, t) local damage = t.getDamage(self, t) local radius = self:getTalentRadius(t) - return ([[Sends out a blast wave of gravity in a %d radius cone, dealing %0.2f physical damage and knocking back creatures caught in the area. This deals 50%% extra damage to pinned targets, in addition to the knockback. - The blast wave may hit targets more then once, depending on the radius and the knockback effect. - The damage will scale with your Spellpower.]]): + return ([[Sends out a blast wave of gravity in a radius %d cone, dealing %0.2f base physical damage and knocking back targets caught in the area. + Targets knocked into walls or other targets take 50%% additional damage and deal 50%% damage to targets they're knocked into. + Closer targets will be knocked back further and the damage will scale with your Spellpower.]]): format(radius, damDesc(self, DamageType.PHYSICAL, t.getDamage(self, t))) end, } @@ -58,31 +102,61 @@ newTalent{ require = chrono_req2, points = 5, paradox = function (self, t) return getParadoxCost(self, t, 20) end, - cooldown = 6, + cooldown = 4, tactical = { ATTACKAREA = {PHYSICAL = 2}, DISABLE = 2 }, range = 10, radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.7)) end, direct_hit = true, requires_target = true, target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 170, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 290, getParadoxSpellpower(self, t)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end local _ _, _, _, x, y = self:canProject(tg, x, y) - local grids = self:project(tg, x, y, function(px, py) + + local tgts = {} + self:project(tg, x, y, function(px, py) local target = game.level.map(px, py, Map.ACTOR) - if not target then return end - local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) - if tx and ty and target:canBe("knockback") then - target:move(tx, ty, true) - game.logSeen(target, "%s is drawn in by the gravity spike!", target.name:capitalize()) + if target and not target:isTalentActive(target.T_GRAVITY_LOCUS) then + -- If we've already moved this target don't move it again + for _, v in pairs(tgts) do + if v == target then + return + end + end + + -- Do our Knockback + local can = function(target) + if target:checkHit(getParadoxSpellpower(self, t), target:combatPhysicalResist(), 0, 95) and target:canBe("knockback") then -- Deprecated Checkhit call + return true + else + game.logSeen(target, "%s resists the knockback!", target.name:capitalize()) + end + end + if can(target) then + target:pull(x, y, tg.radius, can) + tgts[#tgts+1] = target + game.logSeen(target, "%s is drawn in by the singularity!", target.name:capitalize()) + target:crossTierEffect(target.EFF_OFFBALANCE, getParadoxSpellpower(self, t)) + end end end) - self:project(tg, x, y, DamageType.GRAVITY, self:spellCrit(t.getDamage(self, t))) + + -- 25% bonus damage per target beyond the first + local dam = self:spellCrit(t.getDamage(self, t)) + dam = dam + math.min(dam, dam*(#tgts-1)/4) + + -- Project our damage last based on number of targets hit + self:project(tg, x, y, function(px, py) + local dist_factor = 1 + (core.fov.distance(x, y, px, py)/5) + local damage = dam/dist_factor + DamageType:get(DamageType.GRAVITY).projector(self, px, py, DamageType.GRAVITY, damage) + end) + game.level.map:particleEmitter(x, y, tg.radius, "gravity_spike", {radius=tg.radius, allow=core.shader.allow("distort")}) game:playSoundNear(self, "talents/earth") @@ -91,53 +165,49 @@ newTalent{ info = function(self, t) local damage = t.getDamage(self, t) local radius = self:getTalentRadius(t) - return ([[Creates a gravity spike in a radius of %d that moves all targets towards the spells center and inflicts %0.2f physical damage. This deals 50%% extra damage to pinned targets, in addition to the knockback. - The damage dealt will scale with your Spellpower.]]):format(radius, damDesc(self, DamageType.PHYSICAL, t.getDamage(self, t))) + return ([[Creates a gravity spike in a radius of %d that moves all targets towards the spell's center and inflicts %0.2f physical damage. + Each target moved beyond the first deals an additional %0.2f physical damage (up to %0.2f bonus damage). + Targets take reduced damage the further they are from the epicenter (20%% less per tile). + The damage dealt will scale with your Spellpower.]]) + :format(radius, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.PHYSICAL, damage/4), damDesc(self, DamageType.PHYSICAL, damage)) end, } newTalent{ - name = "Repulsion Field", + name = "Gravity Locus", type = {"chronomancy/gravity",3}, require = chrono_req3, + mode = "sustained", + sustain_paradox = 24, + cooldown = 10, + tactical = { BUFF = 2 }, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 30) end, - cooldown = 14, - tactical = { ATTACKAREA = {PHYSICAL = 2}, ESCAPE = 2 }, - range = 0, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 3.5)) end, - target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false} + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end, + getSlow = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80) end, + callbackOnMeleeHit = function(self, t, target) + if not self.dead and self:isTalentActive(self.T_GRAVITY_LOCUS) then + self:project({type="hit", talent=t}, target.x, target.y, DamageType.GRAVITY, {dam=t.getDamage(self, t), anti=true, dur=2, apply=getParadoxSpellpower(self, t)}) + end end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 80, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end, - direct_hit = true, - requires_target = true, - action = function(self, t) - local tg = self:getTalentTarget(t) - game.level.map:addEffect(self, - self.x, self.y, t.getDuration(self, t), - DamageType.REPULSION, t.getDamage(self, t), - tg.radius, - 5, nil, - engine.MapEffect.new{color_br=200, color_bg=120, color_bb=0, effect_shader="shader_images/paradox_effect.png"}, - function(e) - e.x = e.src.x - e.y = e.src.y - return true - end, - tg.selffire - ) - game:playSoundNear(self, "talents/cloud") + activate = function(self, t) + game:playSoundNear(self, "talents/heal") + local particle = Particles.new("ultrashield", 1, {rm=204, rM=220, gm=102, gM=120, bm=0, bM=0, am=35, aM=90, radius=0.5, density=10, life=28, instop=100}) + return { + proj = self:addTemporaryValue("slow_projectiles", t.getSlow(self, t)), + particle = self:addParticles(particle) + } + end, + deactivate = function(self, t, p) + self:removeTemporaryValue("slow_projectiles", p.proj) + self:removeParticles(p.particle) return true end, info = function(self, t) local damage = t.getDamage(self, t) - local duration = t.getDuration(self, t) - local radius = self:getTalentRadius(t) - return ([[You surround yourself with a radius %d distortion of gravity, knocking back and dealing %0.2f physical damage to all creatures inside it. The effect lasts %d turns. Deals 50%% extra damage to pinned targets, in addition to the knockback. - The blast wave may hit targets more then once, depending on the radius and the knockback effect. - The damage will scale with your Spellpower.]]):format(radius, damDesc(self, DamageType.PHYSICAL, damage), duration) + local proj = t.getSlow(self, t) + return ([[Create a gravity field around you that slows incoming projectiles by %d%% and protects you from all gravity effects. + While this spell is active creatures that hit you in melee combat will take %0.2f physical damage and have their knockback resistance reduced by half for two turns. + The projectile slowing and damage will scale with your spellpower.]]):format(proj, damDesc(self, DamageType.PHYSICAL, damage)) end, } @@ -146,31 +216,31 @@ newTalent{ type = {"chronomancy/gravity", 4}, require = chrono_req4, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 40) end, - cooldown = 24, + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 12, tactical = { ATTACKAREA = {PHYSICAL = 2}, DISABLE = 2 }, range = 10, radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end, direct_hit = true, requires_target = true, target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self, t)) end, + getSlow = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80)/100 end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 4, 8))) end, action = function(self, t) - local duration = t.getDuration(self,t) - local radius = self:getTalentRadius(t) - local dam = t.getDamage(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end local _ _, _, _, x, y = self:canProject(tg, x, y) + -- Add a lasting map effect + local dam = self:spellCrit(t.getDamage(self, t)) game.level.map:addEffect(self, - x, y, duration, - DamageType.GRAVITYPIN, dam, - radius, + x, y, t.getDuration(self,t), + DamageType.GRAVITY, {dam=dam, dur=1, slow=t.getSlow(self, t), apply=getParadoxSpellpower(self, t)}, + self:getTalentRadius(t), 5, nil, {type="gravity_well"}, nil, self:spellFriendlyFire() @@ -182,7 +252,8 @@ newTalent{ local damage = t.getDamage(self, t) local duration = t.getDuration(self, t) local radius = self:getTalentRadius(t) - return ([[Increases local gravity of an area, doing %0.2f physical damage with a chance to pin in a radius of %d each turn for %d turns. - The damage done will scale with your Spellpower.]]):format(damDesc(self, DamageType.PHYSICAL, damage), radius, duration) + local slow = t.getSlow(self, t) + return ([[Increases local gravity in a radius of %d for %d turns, dealing %0.2f physical damage and slowing the movement speed of all affected targets by %d%%. + The damage done will scale with your Spellpower.]]):format(radius, duration, damDesc(self, DamageType.PHYSICAL, damage), slow*100) end, } diff --git a/game/modules/tome/data/talents/chronomancy/guardian.lua b/game/modules/tome/data/talents/chronomancy/guardian.lua index c6ce9167dbd812a8efc6fbcfbec665d24ffe0144..26f1b68d3d3e2c1ce23f726533a812366b4e85c0 100644 --- a/game/modules/tome/data/talents/chronomancy/guardian.lua +++ b/game/modules/tome/data/talents/chronomancy/guardian.lua @@ -17,7 +17,7 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org --- EDGE TODO: Icons, Particles, Timed Effect Particles +-- EDGE TODO: Particles, Timed Effect Particles newTalent{ name = "Strength of Purpose", @@ -46,9 +46,8 @@ newTalent{ cooldown = 24, fixed_cooldown = true, tactical = { HEAL = 1 }, - getDuration = function(self, t) return math.floor(self:combatTalentLimit(self:getTalentLevel(t), 14, 4, 8)) end, -- Limit < 14 - getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end, - getNumber = function(self, t) return math.ceil(self:getTalentLevel(t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentLimit(self:getTalentLevel(t), 14, 4, 8))) end, -- Limit < 14 + getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end, action = function(self, t) self:setEffect(self.EFF_INVIGORATE, t.getDuration(self,t), {power=t.getPower(self, t)}) return true @@ -67,24 +66,24 @@ newTalent{ require = chrono_req3, points = 5, mode = "passive", - getSplit = function(self, t) return math.min(100, self:combatTalentSpellDamage(t, 20, 50, getParadoxSpellpower(self)))/100 end, - getDuration = function(self, t) return 2 end, + getSplit = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80)/100 end, + getDuration = function(self, t) return getExtensionModifier(self, t, 2) end, getLifeTrigger = function(self, t) return self:combatTalentLimit(t, 10, 40, 24) end, remove_on_clone = true, callbackOnHit = function(self, t, cb, src) local split = cb.value * t.getSplit(self, t) -- If we already split this turn pass damage to our clone - if self.turn_procs.double_edge and self.turn_procs.double_edge ~= self and not self.turn_procs.double_edge.dead then + if self.turn_procs.double_edge and self.turn_procs.double_edge ~= self and game.level:hasEntity(self.turn_procs.double_edge) then split = split/2 -- split the damage - game:delayedLogDamage(src, self.turn_procs.double_edge, split, ("#PINK#%d displaced#LAST#"):format(split), false) + game:delayedLogDamage(src, self.turn_procs.double_edge, split, ("#STEEL_BLUE#(%d shared)#LAST#"):format(split), nil) cb.value = cb.value - split self.turn_procs.double_edge:takeHit(split, src) end -- Do our split - if self.max_life and cb.value >= self.max_life * t.getLifeTrigger(self, t) * 0.01 and not self.turn_procs.double_edge then + if self.max_life and cb.value >= self.max_life * (t.getLifeTrigger(self, t)/100) and not self.turn_procs.double_edge then -- Look for space first local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) if tx and ty then @@ -93,17 +92,6 @@ newTalent{ -- clone our caster local m = makeParadoxClone(self, self, t.getDuration(self, t)) - -- remove some talents; note most of this is handled by makeParadoxClone already but we don't want to keep splitting - local tids = {} - for tid, _ in pairs(m.talents) do - local t = m:getTalentFromId(tid) - if t.remove_on_clone then tids[#tids+1] = t end - end - for i, t in ipairs(tids) do - if t.mode == "sustained" and m:isTalentActive(t.id) then m:forceUseTalent(t.id, {ignore_energy=true, silent=true}) end - m.talents[t.id] = nil - end - -- add our clone game.zone:addEntity(game.level, m, "actor", tx, ty) m.ai_state = { talent_in=2, ally_compassion=10 } @@ -125,7 +113,8 @@ newTalent{ self.turn_procs.double_edge = m m:takeHit(split, src) m:setTarget(src or nil) - game:delayedLogDamage(src, m, split, ("#PINK#%d displaced#LAST#"):format(split), false) + game:delayedLogMessage(self, nil, "guardian_damage", "#STEEL_BLUE##Source# shares damage with %s guardian!", string.his_her(self)) + game:delayedLogDamage(src or self, self, 0, ("#STEEL_BLUE#(%d shared)#LAST#"):format(split), nil) else game.logPlayer(self, "Not enough space to summon warden!") @@ -154,9 +143,9 @@ newTalent{ tactical = { ATTACK = {weapon = 2}, DISABLE = 3 }, requires_target = true, range = archery_range, - no_energy = "fake", + speed = function(self, t) return self:hasArcheryWeapon("bow") and "archery" or "weapon" end, getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.5) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end, on_pre_use = function(self, t, silent) if self:attr("disarmed") then if not silent then game.logPlayer(self, "You require a weapon to use this talent.") end return false end return true end, archery_onhit = function(self, t, target, x, y) target:setEffect(target.EFF_BREACH, t.getDuration(self, t), {}) @@ -166,19 +155,19 @@ newTalent{ if self:hasArcheryWeapon("bow") then -- Ranged attack - local targets = self:archeryAcquireTargets(nil, {one_shot=true}) + local targets = self:archeryAcquireTargets({type="bolt"}, {one_shot=true, no_energy = true}) if not targets then return end - self:archeryShoot(targets, t, nil, {mult=t.getDamage(self, t)}) + self:archeryShoot(targets, t, {type="bolt"}, {mult=t.getDamage(self, t)}) elseif mainhand then -- Melee attack local tg = {type="hit", range=self:getTalentRange(t), talent=t} local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if core.fov.distance(self.x, self.y, x, y) > 1 then return nil end - local hitted = self:attackTarget(target, nil, t.getDamage(self, t)) + local hitted = self:attackTarget(target, nil, t.getDamage(self, t), true) if hitted then - target:setEffect(target.EFF_BREACH, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)}) + target:setEffect(target.EFF_BREACH, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self, t)}) end else game.logPlayer(self, "You cannot use Breach without an appropriate weapon!") diff --git a/game/modules/tome/data/talents/chronomancy/induced-phenomena.lua b/game/modules/tome/data/talents/chronomancy/induced-phenomena.lua new file mode 100644 index 0000000000000000000000000000000000000000..0c094ad7b3de06add0ac76148c735a621286d749 --- /dev/null +++ b/game/modules/tome/data/talents/chronomancy/induced-phenomena.lua @@ -0,0 +1,339 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +-- EDGE TODO: Particles, Timed Effect Particles + +newTalent{ + name = "Cosmic Cycle", + type = {"chronomancy/induced-phenomena", 1}, + require = chrono_req_high1, + points = 5, + sustain_paradox = 36, + mode = "sustained", + no_sustain_autoreset = true, + cooldown = 12, + tactical = { BUFF = 2}, + range = 0, + radius = function(self, t) local p=self:isTalentActive(self.T_COSMIC_CYCLE) return p and p.radius or 0 end, + target = function(self, t) + return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t} + end, + iconOverlay = function(self, t, p) + local val = p.radius or 0 + if val <= 0 then return "" end + local fnt = "buff_font" + return tostring(math.ceil(val)), fnt + end, + getWillMultiplier = function(self, t) return self:combatTalentLimit(t, 100, 17, 50)/100 end, --Limit < 100% + getResistPen = function(self, t) return self:combatTalentLimit(t, 100, 17, 50) end, --Limit < 100% + callbackOnActBase = function(self, t) + local p = self:isTalentActive(self.T_COSMIC_CYCLE) + if not p then return end + p.old_tgts = p.new_tgts + p.new_tgts = {} + + -- Update our target table + self:project(self:getTalentTarget(t), self.x, self.y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target then return end + p.new_tgts[#p.new_tgts+1] = target + end) + + if self:knowTalent(self.T_REVERSE_CAUSALITY) then + self:callTalent(self.T_REVERSE_CAUSALITY, "doReverseCausality") + end + + if self:knowTalent(self.T_EPOCH) then + if p.mode == "expansion" then + self:callTalent(self.T_EPOCH, "doAging") + else + self:callTalent(self.T_EPOCH, "doRegression") + end + end + end, + doExpansion = function(self, t, radius) + local p = self:isTalentActive(self.T_COSMIC_CYCLE) + + -- Update Radius + p.radius = radius or p.radius + self:removeParticles(p.particle) + p.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, a=55, appear=0, speed=0.2, img="cycle_expansion", radius=p.radius})) + + if p.mode ~= "expansion" then + -- change mode + p.mode = "expansion" + + -- Update temporary values + self:removeTemporaryValue("paradox_will_multi", p.will) + p.resist = self:addTemporaryValue("resists_pen", {[DamageType.TEMPORAL] = t.getResistPen(self, t)}) + end + + game.logPlayer(self, "#LIGHT_BLUE#Your cosmic cycle expands.") + + end, + doContraction = function(self, t, radius) + local p = self:isTalentActive(self.T_COSMIC_CYCLE) + + -- Change radius and particles + p.radius = radius or p.radius + self:removeParticles(p.particle) + p.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, a=55, appear=0, speed=-0.2, img="cycle_contraction", radius=p.radius})) + + if p.mode ~= "contraction" then + p.mode = "contraction" + + -- Update temporary values + self:removeTemporaryValue("resists_pen", p.resist) + p.will = self:addTemporaryValue("paradox_will_multi", t.getWillMultiplier(self, t)) + end + + game.logPlayer(self, "#LIGHT_RED#Your cosmic cycle contracts.") + + end, + doCosmicCycle = function(self, t) + game:onTickEnd(function() + local p = self:isTalentActive(self.T_COSMIC_CYCLE) + if not p then return end + local radius = math.floor(self:getParadox()/100) + + -- Cycle between expansion and contraction + if radius > p.radius then + t.doExpansion(self, t, radius) + elseif radius < p.radius then + t.doContraction(self, t, radius) + end + end) + end, + activate = function(self, t) + local radius = math.floor(self:getParadox()/100) + local will = self:addTemporaryValue("paradox_will_multi", 0) + local resist = self:addTemporaryValue("resists_pen", {[DamageType.TEMPORAL] = t.getResistPen(self, t)}) + + local ret ={ + mode = "expansion", radius = radius, new_tgts ={}, old_tgts ={}, resist = resist, will = will, + particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, a=55, appear=0, speed=0.2, img="cycle_expansion", radius=radius})) + } + game:playSoundNear(self, "talents/arcane") + return ret + end, + deactivate = function(self, t, p) + self:removeParticles(p.particle) + self:removeTemporaryValue("resists_pen", p.resist) + self:removeTemporaryValue("paradox_will_multi", p.will) + return true + end, + info = function(self, t) + local paradox = t.getResistPen(self, t) + local will = t.getWillMultiplier(self, t) * 100 + return ([[Tune yourself into the ebb and flow of spacetime. When your Paradox crosses a 100 point threshold, your Cosmic Cycle gains or loses one radius. + While Cosmic Cycle is expanding, your temporal resistance penetration will be increased by %d%%. While it's contracting, your Willpower for Paradox calculations will be increased by %d%%.]]): + format(paradox, will) + end, +} + +newTalent{ + name = "Polarity Shift", + type = {"chronomancy/induced-phenomena",2}, + require = chrono_req_high2, + points = 5, + cooldown = 6, + tactical = { BUFF = 2, ATTACKAREA = {TEMPORAL = 2} }, + direct_hit = true, + requires_target = true, + on_pre_use = function(self, t, silent) if not self:isTalentActive(self.T_COSMIC_CYCLE) then if not silent then game.logPlayer(self, "You must have Cosmic Cycle active to use this talent.") end return false end return true end, + radius = function(self, t) local p=self:isTalentActive(self.T_COSMIC_CYCLE) return p and p.radius or 0 end, + target = function(self, t) + return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, friendlyfire=false, talent=t} + end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 100) end, + getBraid = function(self, t) return self:combatTalentSpellDamage(t, 25, 50, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end, + action = function(self, t) + local p = self:isTalentActive(self.T_COSMIC_CYCLE) + local tg = self:getTalentTarget(t) + + if p.mode == "contraction" then + self:callTalent(self.T_COSMIC_CYCLE, "doExpansion") + + local dam = self:spellCrit(t.getDamage(self, t)) + self:project(tg, self.x, self.y, function(tx, ty) + local target = game.level.map(tx, ty, engine.Map.ACTOR) + if not target then return end + + local proj = require("mod.class.Projectile"):makeHoming( + self, + {particle="arrow", particle_args={tile=("particles_images/alt_temporal_bolt_0%d"):format(rng.range(1, 7)), proj_x=tx, proj_y=ty, src_x=self.x, src_y=self.y}, trail="trail_paradox"}, + {speed=1, name="Polarity Bolt", dam=dam, movedam=dam}, + target, + self:getTalentRadius(t), + function(self, src) + local DT = require("engine.DamageType") + DT:get(DT.TEMPORAL).projector(src, self.x, self.y, DT.TEMPORAL, self.def.movedam) + end, + function(self, src, target) + local DT = require("engine.DamageType") + DT:get(DT.TEMPORAL).projector(src, self.x, self.y, DT.TEMPORAL, self.def.movedam) + end + ) + game.zone:addEntity(game.level, proj, "projectile", self.x, self.y) + end) + + else + self:callTalent(self.T_COSMIC_CYCLE, "doContraction") + + -- Get our targets + local braid_targets = {} + self:project(tg, self.x, self.y, function(tx, ty) + local target = game.level.map(tx, ty, engine.Map.ACTOR) + if target then + braid_targets[#braid_targets+1] = target + end + end) + + -- if we hit more than one, braid them + if #braid_targets > 1 then + for i = 1, #braid_targets do + local target = braid_targets[i] + target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getBraid(self, t), src=self, targets=braid_targets}) + end + end + end + + game:playSoundNear(self, "talents/spell_generic") + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local braid = t.getBraid(self, t) + local duration = t.getDuration(self, t) + return ([[Reverses the polarity of your Cosmic Cycle. If it's currently contracting, it will begin to expand, firing a homing missile at each target within the radius that deals %0.2f temporal damage. + If it's currently expanding, it will begin to contract, braiding the lifelines of all targets within the radius for %d turns. Braided targets take %d%% of all damage dealt to other braided targets. + The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration, braid) + end, +} + +newTalent{ + name = "Reverse Causality", + type = {"chronomancy/induced-phenomena", 3}, + require = chrono_req_high3, + points = 5, + mode = "passive", + getHeal = function(self, t) return self:combatTalentSpellDamage(t, 15, 70, getParadoxSpellpower(self, t)) end, + getReduction= function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end, + doReverseCausality = function(self, t) + local p = self:isTalentActive(self.T_COSMIC_CYCLE) + if not p then return end + + local keys = table.compareKeys(p.new_tgts, p.old_tgts) + if p.mode == "expansion" then + local heal = self:spellCrit(t.getHeal(self, t)) + heal = heal * #keys.left + if heal > 0 then + self:heal(heal, self) + end + else + local effs = {} + for eff_id, p in pairs(self.tmp) do + local e = self.tempeffect_def[eff_id] + if e.type ~= "other" and e.status == "detrimental" then + effs[#effs+1] = p + end + end + + for i=1, #keys.right do + local eff = effs[i] + if not eff then break end + eff.dur = eff.dur - t.getReduction(self, t) + if eff.dur <= 0 then + self:removeEffect(eff.effect_id) + effs[i] = nil + end + end + end + + end, + info = function(self, t) + local heal = t.getHeal(self, t) + local reduction = t.getReduction(self, t) + return ([[When a creature enters your expanding Cosmic Cycle, you heal %d life at the start of your next turn. + When a creature leaves your contracting Cosmic Cycle, you reduce the duration of one detrimental effect on you by %d at the start of your next turn. + The healing will scale with your Spellpower.]]):format(heal, reduction) + end, +} + +newTalent{ + name = "Epoch", + type = {"chronomancy/induced-phenomena", 4}, + require = chrono_req_high4, + points = 5, + mode = "passive", + radius = function(self, t) local p=self:isTalentActive(self.T_COSMIC_CYCLE) return p and p.radius or 0 end, + target = function(self, t) + return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, friendlyfire=false, talent=t} + end, + getRegression = function(self, t) return self:combatTalentSpellDamage(t, 5, 25, getParadoxSpellpower(self, t)) end, + getAgingChance = function(self, t) return paradoxTalentScale(self, t, 15, 50, 100) end, + doAging = function(self, t) + local tg = self:getTalentTarget(t) + + self:project(self:getTalentTarget(t), self.x, self.y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target then return end + + if rng.percent(t.getAgingChance(self, t)) then + local effect = rng.range(1, 3) + if effect == 1 then + if target:canBe("blind") then + target:setEffect(target.EFF_BLINDED, 3, {apply_power=getParadoxSpellpower(self, t), no_ct_effect=true}) + else + game.logSeen(target, "%s resists the blindness!", target.name:capitalize()) + end + elseif effect == 2 then + if target:canBe("pin") then + target:setEffect(target.EFF_PINNED, 3, {apply_power=getParadoxSpellpower(self, t), no_ct_effect=true}) + else + game.logSeen(target, "%s resists the pin!", target.name:capitalize()) + end + elseif effect == 3 then + if target:canBe("confusion") then + target:setEffect(target.EFF_CONFUSED, 3, {power=50, apply_power=getParadoxSpellpower(self, t), no_ct_effect=true}) + else + game.logSeen(target, "%s resists the confusion!", target.name:capitalize()) + end + end + end + end) + end, + doRegression = function(self, t) + local tg = self:getTalentTarget(t) + + self:project(self:getTalentTarget(t), self.x, self.y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target then return end + target:setEffect(target.EFF_REGRESSION, 1, {power=t.getRegression(self, t), apply_power=getParadoxSpellpower(self, t), no_ct_effect=true}) + end) + end, + info = function(self, t) + local regress = t.getRegression(self, t) + local aging = t.getAgingChance(self, t) + return ([[While your cosmic cycle is expanding, creatures in its radius have a %d%% chance to suffer the effects of aging; pinning, blinding, or confusing them for 3 turns. + While your cosmic cycle is contracting, creatures in its radius suffer from age regression; reducing their three highest stats by %d. + The chance and stat reduction will scale with your Spellpower.]]):format(aging, regress) + end, +} \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/matter.lua b/game/modules/tome/data/talents/chronomancy/matter.lua index 7347030e75770961c6b7bfee0593b5fa1b8e6428..7d50b7ea94494077b3a90c59f002ac1f9fdfd346 100644 --- a/game/modules/tome/data/talents/chronomancy/matter.lua +++ b/game/modules/tome/data/talents/chronomancy/matter.lua @@ -32,7 +32,7 @@ newTalent{ target = function(self, t) return {type="beam", range=self:getTalentRange(t), talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) @@ -60,8 +60,8 @@ newTalent{ sustain_paradox = 20, cooldown = 12, tactical = { BUFF =2, DEFEND = 2 }, - getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 1, 150, getParadoxSpellpower(self)) end, - getArmor = function(self, t) return math.ceil(self:combatTalentSpellDamage(t, 20, 50, getParadoxSpellpower(self))) end, + getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 1, 150, getParadoxSpellpower(self, t)) end, + getArmor = function(self, t) return math.ceil(self:combatTalentSpellDamage(t, 20, 50, getParadoxSpellpower(self, t))) end, do_carbonRegrowth = function(self, t) local maxspikes = t.getArmor(self, t) if self.carbon_armor < maxspikes then @@ -112,8 +112,8 @@ newTalent{ tactical = { ATTACK = 2 }, requires_target = true, direct_hit = true, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 60, getParadoxSpellpower(self)) end, - getExplosion = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 60, getParadoxSpellpower(self, t)) end, + getExplosion = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end, action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) @@ -152,7 +152,7 @@ newTalent{ target = function(self, t) return {type="hit", range=self:getTalentRange(t), talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300, getParadoxSpellpower(self, t)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y, target = self:getTarget(tg) @@ -196,5 +196,4 @@ newTalent{ Quantum Spike deals 50%% additional damage to targets affected by temporal destabilization and/or continuum destabilization. The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage/2), damDesc(self, DamageType.PHYSICAL, damage/2)) end, -} - +} \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/other.lua b/game/modules/tome/data/talents/chronomancy/other.lua index 2196d55db789506603e09f2e56c65c051e20f10a..cce458a64dfe9dfda013534619ba8742af710a92 100644 --- a/game/modules/tome/data/talents/chronomancy/other.lua +++ b/game/modules/tome/data/talents/chronomancy/other.lua @@ -23,10 +23,8 @@ -- Note that 300 is the optimal balance -- Caps at -50% and +50% getParadoxModifier = function (self) - local pm = util.bound(math.sqrt(self:getParadox() / 300), 0.5, 1.5) --- local pm = math.sqrt(self:getParadox()/300) --- pm = math.min(1.5, pm) --- pm = math.max(0.5, pm) + local paradox = self:getParadox() + local pm = util.bound(math.sqrt(paradox / 300), 0.5, 1.5) return pm end @@ -37,13 +35,42 @@ getParadoxCost = function (self, t, value) end -- Paradox Spellpower (regulates spellpower for chronomancy) -getParadoxSpellpower = function(self, mod, add) +getParadoxSpellpower = function(self, t, mod, add) local pm = getParadoxModifier(self) - local mod = mod or 0 - local spellpower = self:combatSpellpower(pm + mod, add) + local mod = mod or 1 + + -- Empower? + local p = self:isTalentActive(self.T_EMPOWER) + if p and p.talent == t.id then + pm = pm + self:callTalent(self.T_EMPOWER, "getPower") + end + + local spellpower = self:combatSpellpower(mod * pm, add) return spellpower end +-- Paradox Talent scaling based on Spellpower (thanks grayswandir) +paradoxTalentScale = function(self, t, low, high, limit) + local low_power = 50 + local high_power = 150 + return self:combatLimit( + self:combatTalentSpellDamage(t, low_power, high_power, getParadoxSpellpower(self, t)), + limit, + low, low_power, + high, high_power) +end + +-- Extension Spellbinding +getExtensionModifier = function(self, t, value) + local mod = 1 + local p = self:isTalentActive(self.T_EXTENSION) + if p and p.talent == t.id then + mod = mod + self:callTalent(self.T_EXTENSION, "getPower") + end + value = math.ceil(value * mod) + return value +end + --- Warden weapon functions -- Checks for weapons in main and quickslot doWardenPreUse = function(self, weapon, silent) @@ -130,11 +157,15 @@ makeParadoxClone = function(self, target, duration) m.puuid = nil m.on_acquire_target = nil m.no_inventory_access = true + m.no_levelup_access = true m.on_takehit = nil m.seen_by = nil m.can_talk = nil m.clone_on_hit = nil m.self_resurrect = nil + m.escort_quest = nil + m.unused_talents = 0 + m.unused_generics = 0 if m.talents.T_SUMMON then m.talents.T_SUMMON = nil end if m.talents.T_MULTIPLY then m.talents.T_MULTIPLY = nil end @@ -146,12 +177,11 @@ makeParadoxClone = function(self, target, duration) local tids = {} for tid, _ in pairs(m.talents) do local t = m:getTalentFromId(tid) - if t.no_npc_use then tids[#tids+1] = t end - if t.remove_on_clone then tids[#tids+1] = t end + if (t.no_npc_use and not t.allow_temporal_clones) or t.remove_on_clone then tids[#tids+1] = t end end for i, t in ipairs(tids) do if t.mode == "sustained" and m:isTalentActive(t.id) then m:forceUseTalent(t.id, {ignore_energy=true, silent=true}) end - m.talents[t.id] = nil + m:unlearnTalentFull(t.id) end -- remove timed effects @@ -233,10 +263,10 @@ newTalent{ info = function(self, t) local duration = t.getDuration(self, t) local preference = self.preferred_paradox - local spellpower = getParadoxSpellpower(self) + local spellpower = getParadoxSpellpower(self, t) local after_will, will_modifier, sustain_modifier = self:getModifiedParadox() local anomaly = self:paradoxFailChance() - return ([[Use to set your preferred Paradox. While resting you'll adjust your Paradox towards this number over %d turns. + return ([[Use to set your preferred Paradox. While resting or waiting you'll adjust your Paradox towards this number over %d turns. The time it takes you to adjust your Paradox scales down with your Spellpower to a minimum of 10 turns. Preferred Paradox : %d @@ -265,7 +295,7 @@ newTalent{ return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=self:spellFriendlyFire(), talent=t} end, getDuration = function(self, t) return math.ceil(self:combatTalentScale(self:getTalentLevel(t), 2.3, 4.3)) end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 170, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 170, getParadoxSpellpower(self, t)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) @@ -303,8 +333,8 @@ newTalent{ target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} end, - getSlow = function(self, t) return math.min(10 + self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self))/ 100 , 0.6) end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 60, getParadoxSpellpower(self)) end, + getSlow = function(self, t) return math.min(10 + self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t))/ 100 , 0.6) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 60, getParadoxSpellpower(self, t)) end, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, action = function(self, t) local tg = self:getTalentTarget(t) @@ -334,72 +364,6 @@ newTalent{ end, } -newTalent{ - name = "Swap", - type = {"chronomancy/other", 1}, - require = chrono_req1, - points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 10) end, - cooldown = 10, - tactical = { DISABLE = 2, }, - requires_target = true, - direct_hit = true, - range = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, - getConfuseDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 3, 7)) end, - getConfuseEfficency = function(self, t) return math.min(50, self:getTalentLevelRaw(t) * 10) end, - action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t)} - local tx, ty, target = self:getTarget(tg) - if not tx or not ty then return nil end - if tx then - local _ _, tx, ty = self:canProject(tg, tx, ty) - if tx then - target = game.level.map(tx, ty, Map.ACTOR) - if not target then return nil end - end - end - - -- checks for spacetime mastery hit bonus - local power = getParadoxSpellpower(self) - if self:knowTalent(self.T_SPACETIME_MASTERY) then - power = getParadoxSpellpower(self) * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower")) - end - - if target:canBe("teleport") and self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) then - -- first remove the target so the destination tile is empty - game.level.map:remove(target.x, target.y, Map.ACTOR) - local px, py - px, py = self.x, self.y - if self:teleportRandom(tx, ty, 0) then - -- return the target at the casters old location - game.level.map(px, py, Map.ACTOR, target) - self.x, self.y, target.x, target.y = target.x, target.y, px, py - game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport") - game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") - target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)}) - -- confuse them - self:project(tg, target.x, target.y, DamageType.CONFUSION, { dur = t.getConfuseDuration(self, t), dam = t.getConfuseEfficency(self, t), }) - else - -- return the target without effect - game.level.map(target.x, target.y, Map.ACTOR, target) - game.logSeen(self, "The spell fizzles!") - end - else - game.logSeen(target, "%s resists the swap!", target.name:capitalize()) - end - - game:playSoundNear(self, "talents/teleport") - return true - end, - info = function(self, t) - local range = self:getTalentRange(t) - local duration = t.getConfuseDuration(self, t) - local power = t.getConfuseEfficency(self, t) - return ([[You manipulate the spacetime continuum in such a way that you switch places with another creature with in a range of %d. The targeted creature will be confused (power %d%%) for %d turns. - The spell's hit chance will increase with your Spellpower.]]):format (range, power, duration) - end, -} - newTalent{ name = "Spacetime Mastery", type = {"chronomancy/other", 1}, @@ -518,4 +482,597 @@ newTalent{ This spell takes no time to cast.]]): format(power, duration) end, +} + +newTalent{ + name = "Gather the Threads", + type = {"chronomancy/other", 1}, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 10) end, + cooldown = 12, + tactical = { BUFF = 2 }, + getThread = function(self, t) return self:combatTalentScale(t, 7, 30, 0.75) end, + getReduction = function(self, t) return self:combatTalentScale(t, 3.6, 15, 0.75) end, + action = function(self, t) + self:setEffect(self.EFF_GATHER_THE_THREADS, 5, {power=t.getThread(self, t), reduction=t.getReduction(self, t)}) + game:playSoundNear(self, "talents/spell_generic2") + return true + end, + info = function(self, t) + local primary = t.getThread(self, t) + local reduction = t.getReduction(self, t) + return ([[You begin to gather energy from other timelines. Your Spellpower will increase by %0.2f on the first turn and %0.2f more each additional turn. + The effect ends either when you cast a spell, or after five turns. + Eacn turn the effect is active, your Paradox will be reduced by %d. + This spell will not break Spacetime Tuning, nor will it be broken by activating Spacetime Tuning.]]):format(primary + (primary/5), primary/5, reduction) + end, +} + +newTalent{ + name = "Entropic Field", + type = {"chronomancy/other",1}, + mode = "sustained", + points = 5, + sustain_paradox = 20, + cooldown = 10, + tactical = { BUFF = 2 }, + getPower = function(self, t) return math.min(90, 10 + self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t))) end, + activate = function(self, t) + game:playSoundNear(self, "talents/heal") + return { + particle = self:addParticles(Particles.new("time_shield", 1)), + phys = self:addTemporaryValue("resists", {[DamageType.PHYSICAL]=t.getPower(self, t)/2}), + proj = self:addTemporaryValue("slow_projectiles", t.getPower(self, t)), + } + end, + deactivate = function(self, t, p) + self:removeParticles(p.particle) + self:removeTemporaryValue("resists", p.phys) + self:removeTemporaryValue("slow_projectiles", p.proj) + return true + end, + info = function(self, t) + local power = t.getPower(self, t) + return ([[You encase yourself in a field that slows incoming projectiles by %d%%, and increases your physical resistance by %d%%. + The effect will scale with your Spellpower.]]):format(power, power / 2) + end, +} + +newTalent{ + name = "Fade From Time", + type = {"chronomancy/other", 1}, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 24, + tactical = { DEFEND = 2, CURE = 2 }, + getResist = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end, + getdurred = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)), 100, 0, 0, 32.9, 32.9) end, -- Limit < 100% + action = function(self, t) + -- fading managed by FADE_FROM_TIME effect in mod.data.timed_effects.other.lua + self:setEffect(self.EFF_FADE_FROM_TIME, 10, {power=t.getResist(self, t), durred=t.getdurred(self,t)}) + game:playSoundNear(self, "talents/heal") + return true + end, + info = function(self, t) + local resist = t.getResist(self, t) + local dur = t.getdurred(self,t) + return ([[You partially remove yourself from the timeline for 10 turns. + This increases your resistance to all damage by %d%%, reduces the duration of all detrimental effects on you by %d%%, and reduces all damage you deal by 20%%. + The resistance bonus, effect reduction, and damage penalty will gradually lose power over the duration of the spell. + The effects scale with your Spellpower.]]): + format(resist, dur) + end, +} + +newTalent{ + name = "Paradox Clone", + type = {"chronomancy/other", 1}, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 50) end, + cooldown = 50, + tactical = { ATTACK = 1, DISABLE = 2 }, + range = 2, + requires_target = true, + no_npc_use = true, + getDuration = function(self, t) return math.floor(self:combatTalentLimit(self:getTalentLevel(t), 50, 4, 8)) end, -- Limit <50 + getModifier = function(self, t) return rng.range(t.getDuration(self,t)*2, t.getDuration(self, t)*4) end, + action = function (self, t) + if checkTimeline(self) == true then + return + end + + local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} + local tx, ty = self:getTarget(tg) + if not tx or not ty then return nil end + local _ _, tx, ty = self:canProject(tg, tx, ty) + if not tx or not ty then return nil end + + local x, y = util.findFreeGrid(tx, ty, 2, true, {[Map.ACTOR]=true}) + if not x then + game.logPlayer(self, "Not enough space to summon!") + return + end + + local sex = game.player.female and "she" or "he" + local m = require("mod.class.NPC").new(self:cloneFull{ + no_drops = true, + faction = self.faction, + summoner = self, summoner_gain_exp=true, + exp_worth = 0, + summon_time = t.getDuration(self, t), + ai_target = {actor=nil}, + ai = "summoned", ai_real = "tactical", + ai_tactic = resolvers.tactic("ranged"), ai_state = { talent_in=1, ally_compassion=10}, + desc = [[The real you... or so ]]..sex..[[ says.]] + }) + m:removeAllMOs() + m.make_escort = nil + m.on_added_to_level = nil + + m.energy.value = 0 + m.player = nil + m.puuid = nil + m.max_life = m.max_life + m.life = util.bound(m.life, 0, m.max_life) + m.forceLevelup = function() end + m.die = nil + m.on_die = nil + m.on_acquire_target = nil + m.seen_by = nil + m.can_talk = nil + m.on_takehit = nil + m.no_inventory_access = true + m.clone_on_hit = nil + m.remove_from_party_on_death = true + + -- Remove some talents + local tids = {} + for tid, _ in pairs(m.talents) do + local t = m:getTalentFromId(tid) + if t.no_npc_use then tids[#tids+1] = t end + end + for i, t in ipairs(tids) do + m.talents[t.id] = nil + end + + game.zone:addEntity(game.level, m, "actor", x, y) + game.level.map:particleEmitter(x, y, 1, "temporal_teleport") + game:playSoundNear(self, "talents/teleport") + + if game.party:hasMember(self) then + game.party:addMember(m, { + control="no", + type="minion", + title="Paradox Clone", + orders = {target=true}, + }) + end + + self:setEffect(self.EFF_IMMINENT_PARADOX_CLONE, t.getDuration(self, t) + t.getModifier(self, t), {}) + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + return ([[You summon your future self to fight alongside you for %d turns. At some point in the future, you'll be pulled into the past to fight alongside your past self after the initial effect ends. + This spell splits the timeline. Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.]]):format(duration) + end, +} + +newTalent{ + name = "Displace Damage", + type = {"chronomancy/other", 1}, + mode = "sustained", + require = chrono_req1, + sustain_paradox = 48, + cooldown = 10, + tactical = { BUFF = 2 }, + points = 5, + -- called by _M:onTakeHit function in mod\class\Actor.lua to perform the damage displacment + getDisplaceDamage = function(self, t) return self:combatTalentLimit(t, 25, 5, 15)/100 end, -- Limit < 25% + range = 10, + callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr) + if dam > 0 and src ~= self then + -- find available targets + local tgts = {} + local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), true) + for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do + local a = game.level.map(x, y, Map.ACTOR) + if a and self:reactionToward(a) < 0 then + tgts[#tgts+1] = a + end + end end + + -- Displace the damage + local a = rng.table(tgts) + if a then + local displace = dam * t.getDisplaceDamage(self, t) + game:delayedLogMessage(self, a, "displace_damage"..(a.uid or ""), "#PINK##Source# displaces some damage onto #Target#!") + DamageType.defaultProjector(self, a.x, a.y, type, displace, tmp, true) + dam = dam - displace + end + end + + return {dam=dam} + end, + activate = function(self, t) + return {} + end, + deactivate = function(self, t, p) + return true + end, + info = function(self, t) + local displace = t.getDisplaceDamage(self, t) * 100 + return ([[You bend space around you, displacing %d%% of any damage you receive onto a random enemy within range. + ]]):format(displace) + end, +} + +newTalent{ + name = "Repulsion Field", + type = {"chronomancy/other",1}, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 30) end, + cooldown = 14, + tactical = { ATTACKAREA = {PHYSICAL = 2}, ESCAPE = 2 }, + range = 0, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 3.5)) end, + target = function(self, t) + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false} + end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 8, 80, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end, + direct_hit = true, + requires_target = true, + action = function(self, t) + local tg = self:getTalentTarget(t) + game.level.map:addEffect(self, + self.x, self.y, t.getDuration(self, t), + DamageType.REPULSION, t.getDamage(self, t), + tg.radius, + 5, nil, + engine.MapEffect.new{color_br=200, color_bg=120, color_bb=0, effect_shader="shader_images/paradox_effect.png"}, + function(e) + e.x = e.src.x + e.y = e.src.y + return true + end, + tg.selffire + ) + game:playSoundNear(self, "talents/cloud") + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local duration = t.getDuration(self, t) + local radius = self:getTalentRadius(t) + return ([[You surround yourself with a radius %d distortion of gravity, knocking back and dealing %0.2f physical damage to all creatures inside it. The effect lasts %d turns. Deals 50%% extra damage to pinned targets, in addition to the knockback. + The blast wave may hit targets more then once, depending on the radius and the knockback effect. + The damage will scale with your Spellpower.]]):format(radius, damDesc(self, DamageType.PHYSICAL, damage), duration) + end, +} + +newTalent{ + name = "Temporal Clone", + type = {"chronomancy/other", 1}, + points = 5, + cooldown = 12, + paradox = function (self, t) return getParadoxCost(self, t, 15) end, + tactical = { ATTACK = 2, DISABLE = 2 }, + requires_target = true, + range = 10, + remove_on_clone = true, + target = function (self, t) + return {type="hit", range=self:getTalentRange(t), talent=t, nowarning=true} + end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 12))) end, + getDamagePenalty = function(self, t) return 60 - math.min(self:combatTalentSpellDamage(t, 0, 20, getParadoxSpellpower(self, t)), 30) end, + action = function(self, t) + local tg = self:getTalentTarget(t) + local x, y, target = self:getTarget(tg) + if not x or not y then return nil end + local _ _, _, _, x, y = self:canProject(tg, x, y) + if not x or not y then return nil end + local target = game.level.map(x, y, Map.ACTOR) + if not target then return end + + -- Find space + local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) + if not tx then + game.logPlayer(self, "Not enough space to summon!") + return + end + + -- Rank Penalty + local duration = t.getDuration(self, t) + if target.rank > 1 then duration = math.ceil(t.getDuration(self, t)/(target.rank/2)) end + + -- Clone the target + local m = makeParadoxClone(self, target, duration) + -- Add and change some values + m.faction = self.faction + m.summoner = self + m.generic_damage_penalty = t.getDamagePenalty(self, t) + m.max_life = m.max_life * (100 - t.getDamagePenalty(self, t))/100 + m.life = m.max_life + m.remove_from_party_on_death = true + + -- Handle some AI stuff + m.ai_state = { talent_in=2, ally_compassion=10 } + + game.zone:addEntity(game.level, m, "actor", tx, ty) + + -- Set our target + if self:reactionToward(target) < 0 then + m:setTarget(target) + end + + if game.party:hasMember(self) then + game.party:addMember(m, { + control="no", + type="temporal-clone", + title="Temporal Clone", + orders = {target=true}, + }) + end + + game.level.map:particleEmitter(tx, ty, 1, "temporal_teleport") + game:playSoundNear(self, "talents/spell_generic") + + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + local damage_penalty = t.getDamagePenalty(self, t) + return ([[Clones the target creature for up to %d turns. The duration of the effect will be divided by half the target's rank, and the target will have have %d%% of its normal life and deal %d%% less damage. + If you clone a hostile creature the clone will target the creature it was cloned from. + The life and damage penalties will be lessened by your Spellpower.]]): + format(duration, 100 - damage_penalty, damage_penalty) + end, +} + +newTalent{ + name = "Paradox Mastery", + type = {"chronomancy/other", 1}, + mode = "passive", + points = 5, + -- Static history bonus handled in timetravel.lua, backfire calcs performed by _M:getModifiedParadox function in mod\class\Actor.lua + WilMult = function(self, t) return self:combatTalentScale(t, 0.15, 0.5) end, + stabilityDuration = function(self, t) return math.floor(self:combatTalentScale(t, 0.4, 2.7, "log")) end, --This is still used by an older talent, leave it here for backwards compatability + passives = function(self, t, p) + self:talentTemporaryValue(p, "paradox_will_mutli", t.WilMult(self, t)) + end, + info = function(self, t) + return ([[You've learned to focus your control over the spacetime continuum, and quell anomalous effects. Increases your effective Willpower for anomaly calculations by %d%%.]]): + format(t.WilMult(self, t) * 100) + end, +} + +newTalent{ + name = "Damage Smearing", + type = {"chronomancy/other", 1}, + mode = "sustained", + sustain_paradox = 48, + cooldown = 24, + tactical = { DEFEND = 2 }, + points = 5, + getPercent = function(self, t) return self:combatTalentLimit(t, 50, 10, 30)/100 end, -- Limit < 50% + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 6))) end, + callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr) + if dam > 0 and type ~= DamageType.TEMPORAL then + local smear = dam * t.getPercent(self, t) + self:setEffect(self.EFF_DAMAGE_SMEARING, t.getDuration(self, t), {dam=smear/t.getDuration(self, t), no_ct_effect=true}) + game:delayedLogDamage(src, self, 0, ("%s(%d smeared)#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", smear), false) + dam = dam - smear + end + + return {dam=dam} + end, + activate = function(self, t) + game:playSoundNear(self, "talents/spell_generic") + return {} + end, + deactivate = function(self, t, p) + return true + end, + info = function(self, t) + local percent = t.getPercent(self, t) * 100 + local duration = t.getDuration(self, t) + return ([[You convert %d%% of all non-temporal damage you receive into temporal damage spread out over %d turns. + This damage will bypass resistance and affinity.]]):format(percent, duration) + end, +} + +newTalent{ + name = "Banish", + type = {"chronomancy/other", 1}, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 10) end, + cooldown = 10, + tactical = { ESCAPE = 2 }, + range = 0, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 5.5)) end, + getTeleport = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 8, 16)) end, + target = function(self, t) + return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t} + end, + requires_target = true, + direct_hit = true, + action = function(self, t) + local tg = self:getTalentTarget(t) + local hit = false + + self:project(tg, self.x, self.y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target or target == self then return end + game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport") + if self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) and target:canBe("teleport") then + if not target:teleportRandom(target.x, target.y, self:getTalentRadius(t) * 4, self:getTalentRadius(t) * 2) then + game.logSeen(target, "The spell fizzles on %s!", target.name:capitalize()) + else + target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=getParadoxSpellpower(self, t, 0.3)}) + game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport") + hit = true + end + else + game.logSeen(target, "%s resists the banishment!", target.name:capitalize()) + end + end) + + if not hit then + game:onTickEnd(function() + if not self:attr("no_talents_cooldown") then + self.talents_cd[self.T_BANISH] = self.talents_cd[self.T_BANISH] /2 + end + end) + end + + game:playSoundNear(self, "talents/teleport") + + return true + end, + info = function(self, t) + local radius = self:getTalentRadius(t) + local range = t.getTeleport(self, t) + return ([[Randomly teleports all targets within a radius of %d around you. Targets will be teleported between %d and %d tiles from their current location. + If no targets are teleported the cooldown will be halved. + The chance of teleportion will scale with your Spellpower.]]):format(radius, range / 2, range) + end, +} + +newTalent{ + name = "Swap", + type = {"chronomancy/other", 1}, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 10) end, + cooldown = 10, + tactical = { DISABLE = 1 }, + requires_target = true, + direct_hit = true, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, + getConfuseDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 3, 7)) end, + getConfuseEfficency = function(self, t) return math.min(50, self:getTalentLevelRaw(t) * 10) end, + action = function(self, t) + local tg = {type="hit", range=self:getTalentRange(t)} + local tx, ty, target = self:getTarget(tg) + if not tx or not ty then return nil end + if tx then + local _ _, tx, ty = self:canProject(tg, tx, ty) + if tx then + target = game.level.map(tx, ty, Map.ACTOR) + if not target then return nil end + end + end + + -- Check hit + if target:canBe("teleport") and self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) then + -- Grab the caster's location + local px, py = self.x, self.y + + -- Remove the target so the destination tile is empty + game.level.map:remove(target.x, target.y, Map.ACTOR) + + -- Try to teleport to the target's old location + if self:teleportRandom(tx, ty, 0) then + -- Put the target back in the caster's old location + game.level.map(px, py, Map.ACTOR, target) + target.x, target.y = px, py + + -- confuse them + self:project(tg, target.x, target.y, DamageType.CONFUSION, { dur = t.getConfuseDuration(self, t), dam = t.getConfuseEfficency(self, t), apply_power=getParadoxSpellpower(self, t)}) + target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=getParadoxSpellpower(self, t, 0.3)}) + + game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport") + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + else + -- If we can't teleport, return the target + game.level.map(target.x, target.y, Map.ACTOR, target) + game.logSeen(self, "The spell fizzles!") + end + else + game.logSeen(target, "%s resists the swap!", target.name:capitalize()) + end + + game:playSoundNear(self, "talents/teleport") + return true + end, + info = function(self, t) + local range = self:getTalentRange(t) + local duration = t.getConfuseDuration(self, t) + local power = t.getConfuseEfficency(self, t) + return ([[You manipulate the spacetime continuum in such a way that you switch places with another creature with in a range of %d. The targeted creature will be confused (power %d%%) for %d turns. + The spell's hit chance will increase with your Spellpower.]]):format (range, power, duration) + end, +} + +newTalent{ + name = "Temporal Wake", + type = {"chronomancy/other", 1}, + points = 5, + random_ego = "attack", + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 10, + tactical = { ATTACK = {TEMPORAL = 1, PHYSICAL = 1}, CLOSEIN = 2, DISABLE = { stun = 2 } }, + direct_hit = true, + requires_target = true, + is_teleport = true, + target = function(self, t) + return {type="beam", start_x=x, start_y=y, range=self:getTalentRange(t), selffire=false, talent=t} + end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, + action = function(self, t) + local tg = self:getTalentTarget(t) + local x, y, target = self:getTarget(tg) + if not x or not y then return nil end + if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then + game.logSeen(self, "You do not have line of sight.") + return nil + end + local _ _, x, y = self:canProject(tg, x, y) + local ox, oy = self.x, self.y + + -- If we target an actor directly project onto the other side of it (quality of life) + if target then + local dir = util.getDir(x, y, self.x, self.y) + x, y = util.coordAddDir(x, y, dir) + end + + -- since we're using a precise teleport we'll look for a free grid first + local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) + if tx and ty then + if not self:teleportRandom(tx, ty, 0) then + game.logSeen(self, "The teleport fizzles!") + else + local dam = self:spellCrit(t.getDamage(self, t)) + local x, y = ox, oy + self:project(tg, x, y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if target then + -- Deal warp damage first so we don't overwrite a big stun with a little one + DamageType:get(DamageType.WARP).projector(self, px, py, DamageType.WARP, dam) + + -- Try to stun + if target:canBe("stun") then + target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self, t)}) + else + game.logSeen(target, "%s resists the stun!", target.name:capitalize()) + end + end + end) + game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "temporal_lightning", {tx=x-self.x, ty=y-self.y}) + game:playSoundNear(self, "talents/lightning") + end + end + + return true + end, + info = function(self, t) + local stun = t.getDuration(self, t) + local damage = t.getDamage(self, t) + return ([[Violently fold the space between yourself and another point within range. + You teleport to the target location, and leave a temporal wake behind that stuns for %d turns and deals %0.2f temporal and %0.2f physical warp damage to targets in the path. + The damage will scale with your Spellpower.]]): + format(stun, damDesc(self, DamageType.TEMPORAL, damage/2), damDesc(self, DamageType.PHYSICAL, damage/2)) + end, } \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/paradox.lua b/game/modules/tome/data/talents/chronomancy/paradox.lua index 936e39dc76c55e1bab95a1339f9e4f383f31f899..1cba0e3267adfaf7fa6766ce94641b73bbc758b1 100644 --- a/game/modules/tome/data/talents/chronomancy/paradox.lua +++ b/game/modules/tome/data/talents/chronomancy/paradox.lua @@ -17,234 +17,7 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org -newTalent{ - name = "Paradox Mastery", - type = {"chronomancy/paradox", 1}, - mode = "passive", - require = chrono_req_high1, - points = 5, - -- Static history bonus handled in timetravel.lua, backfire calcs performed by _M:getModifiedParadox function in mod\class\Actor.lua - WilMult = function(self, t) return self:combatTalentScale(t, 0.15, 0.5) end, - stabilityDuration = function(self, t) return math.floor(self:combatTalentScale(t, 0.4, 2.7, "log")) end, - getResist = function(self, t) return self:combatTalentScale(t, 10, 35) end, - passives = function(self, t, p) - self:talentTemporaryValue(p, "resists", {[DamageType.TEMPORAL] = t.getResist(self, t)}) - end, - info = function(self, t) - return ([[You've learned to focus your control over the spacetime continuum, and quell anomalous effects. Increases your Temporal resistance by %d%%, extends the duration of the Static History stability effect by %d turns, and increases your effective willpower for failure, anomaly, and backfire calculations by %d%%.]]): - format(t.getResist(self, t), t.stabilityDuration(self, t), t.WilMult(self, t) * 100) - end, -} +-- EDGE TODO: Particles, Timed Effect Particles -newTalent{ - name = "Cease to Exist", - type = {"chronomancy/paradox", 2}, - require = chrono_req_high2, - points = 5, - cooldown = 24, - paradox = function (self, t) return getParadoxCost(self, t, 40) end, - range = 10, - tactical = { ATTACK = 2 }, - requires_target = true, - direct_hit = true, - no_npc_use = true, - getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 5, 9)) end, - getPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end, - -- Resistance reduction handled under CEASE_TO_EXIST in data\timed_effects\magical.lua - getPower = function(self, t) - return self:combatLimit(self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)), 100, 0, 0, 32.9, 32.9) -- Limit < 100% - end, - on_pre_use = function(self, t, silent) - if checkTimeline(self) then - if not silent then - game.logPlayer(self, "The timeline is too fractured to do this now.") - end - return false - end - return true - end, - do_instakill = function(self, t) - -- search for target because it's ID will change when the chrono restore takes place - local tg = false - local grids = core.fov.circle_grids(self.x, self.y, 10, true) - for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do - local a = game.level.map(x, y, Map.ACTOR) - if a and a:hasEffect(a.EFF_CEASE_TO_EXIST) then - tg = a - end - end end - - if tg then - game:onTickEnd(function() - tg:removeEffect(tg.EFF_CEASE_TO_EXIST) - game.logSeen(tg, "#LIGHT_BLUE#%s never existed, this never happened!", tg.name:capitalize()) - tg:die(self) - end) - end - end, - action = function(self, t) - -- get our target - local tg = {type="hit", range=self:getTalentRange(t)} - local tx, ty = self:getTarget(tg) - if not tx or not ty then return nil end - local _ _, tx, ty = self:canProject(tg, tx, ty) - - local target = game.level.map(tx, ty, Map.ACTOR) - if not target then return end - if target == self then - game.logSeen(self, "#LIGHT_STEEL_BLUE#%s tries to remove %sself from existance!", self.name, string.his_her(self)) - self:incParadox(400) - game.level.map:particleEmitter(self.x, self.y, 1, "ball_temporal", {radius=1, tx=self.x, ty=self.y}) - return true - end - - -- does the spell hit? if not nothing happens - if not self:checkHit(self:combatSpellpower(), target:combatSpellResist()) then - game.logSeen(target, "%s resists!", target.name:capitalize()) - return true - end - - -- Manualy start cooldown before the chronoworld is made - game.player:startTalentCooldown(t) - - -- set up chronoworld next, we'll load it when the target dies in class\actor - game:onTickEnd(function() - game:chronoClone("cease_to_exist") - end) - - target:setEffect(target.EFF_CEASE_TO_EXIST, t.getDuration(self,t), {power=t.getPower(self, t)}) - - return true - end, - info = function(self, t) - local duration = t.getDuration(self, t) - local power = t.getPower(self, t) - return ([[Over the next %d turns, you attempt to remove the target from the timeline. Its resistances will be reduced by %d%%, and if you manage to kill it while the spell is in effect, you'll be returned to the point in time you cast this spell and the target will be slain. - This spell splits the timeline. Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful. - The duration will scale with your Paradox and the resistance penalty will scale with your Spellpower.]]) - :format(duration, power) - end, -} -newTalent{ - name = "Fade From Time", - type = {"chronomancy/paradox", 3}, - require = chrono_req_high3, - points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 20) end, - cooldown = 24, - tactical = { DEFEND = 2, CURE = 2 }, - getResist = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end, - getdurred = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)), 100, 0, 0, 32.9, 32.9) end, -- Limit < 100% - action = function(self, t) - -- fading managed by FADE_FROM_TIME effect in mod.data.timed_effects.other.lua - self:setEffect(self.EFF_FADE_FROM_TIME, 10, {power=t.getResist(self, t), durred=t.getdurred(self,t)}) - game:playSoundNear(self, "talents/heal") - return true - end, - info = function(self, t) - local resist = t.getResist(self, t) - local dur = t.getdurred(self,t) - return ([[You partially remove yourself from the timeline for 10 turns. - This increases your resistance to all damage by %d%%, reduces the duration of all detrimental effects on you by %d%%, and reduces all damage you deal by 20%%. - The resistance bonus, effect reduction, and damage penalty will gradually lose power over the duration of the spell. - The effects scale with your Spellpower.]]): - format(resist, dur) - end, -} - -newTalent{ - name = "Paradox Clone", - type = {"chronomancy/paradox", 4}, - require = chrono_req_high4, - points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 50) end, - cooldown = 50, - tactical = { ATTACK = 1, DISABLE = 2 }, - range = 2, - requires_target = true, - no_npc_use = true, - getDuration = function(self, t) return math.floor(self:combatTalentLimit(self:getTalentLevel(t), 50, 4, 8)) end, -- Limit <50 - getModifier = function(self, t) return rng.range(t.getDuration(self,t)*2, t.getDuration(self, t)*4) end, - action = function (self, t) - if checkTimeline(self) == true then - return - end - - local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} - local tx, ty = self:getTarget(tg) - if not tx or not ty then return nil end - local _ _, tx, ty = self:canProject(tg, tx, ty) - if not tx or not ty then return nil end - - local x, y = util.findFreeGrid(tx, ty, 2, true, {[Map.ACTOR]=true}) - if not x then - game.logPlayer(self, "Not enough space to summon!") - return - end - - local sex = game.player.female and "she" or "he" - local m = require("mod.class.NPC").new(self:cloneFull{ - no_drops = true, - faction = self.faction, - summoner = self, summoner_gain_exp=true, - exp_worth = 0, - summon_time = t.getDuration(self, t), - ai_target = {actor=nil}, - ai = "summoned", ai_real = "tactical", - ai_tactic = resolvers.tactic("ranged"), ai_state = { talent_in=1, ally_compassion=10}, - desc = [[The real you... or so ]]..sex..[[ says.]] - }) - m:removeAllMOs() - m.make_escort = nil - m.on_added_to_level = nil - - m.energy.value = 0 - m.player = nil - m.puuid = nil - m.max_life = m.max_life - m.life = util.bound(m.life, 0, m.max_life) - m.forceLevelup = function() end - m.die = nil - m.on_die = nil - m.on_acquire_target = nil - m.seen_by = nil - m.can_talk = nil - m.on_takehit = nil - m.no_inventory_access = true - m.clone_on_hit = nil - m.remove_from_party_on_death = true - - -- Remove some talents - local tids = {} - for tid, _ in pairs(m.talents) do - local t = m:getTalentFromId(tid) - if t.no_npc_use then tids[#tids+1] = t end - end - for i, t in ipairs(tids) do - m.talents[t.id] = nil - end - - game.zone:addEntity(game.level, m, "actor", x, y) - game.level.map:particleEmitter(x, y, 1, "temporal_teleport") - game:playSoundNear(self, "talents/teleport") - - if game.party:hasMember(self) then - game.party:addMember(m, { - control="no", - type="minion", - title="Paradox Clone", - orders = {target=true}, - }) - end - - self:setEffect(self.EFF_IMMINENT_PARADOX_CLONE, t.getDuration(self, t) + t.getModifier(self, t), {}) - return true - end, - info = function(self, t) - local duration = t.getDuration(self, t) - return ([[You summon your future self to fight alongside you for %d turns. At some point in the future, you'll be pulled into the past to fight alongside your past self after the initial effect ends. - This spell splits the timeline. Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful.]]):format(duration) - end, -} diff --git a/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua b/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua index b32c5ab6f6d8093de24029cc64f78d7b2619c853..e8c0b67babef10e84a843f088cbea9c7b36165cb 100644 --- a/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua +++ b/game/modules/tome/data/talents/chronomancy/spacetime-folding.lua @@ -21,15 +21,99 @@ local Trap = require "mod.class.Trap" +makeWarpMine = function(self, t, x, y, type) + -- Mine values + local dam = self:spellCrit(self:callTalent(self.T_WARP_MINES, "getDamage")) + local duration = self:callTalent(self.T_WARP_MINES, "getDuration") + local detect = math.floor(self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8) + local disarm = math.floor(self:callTalent(self.T_WARP_MINES, "trapPower")) + local power = getParadoxSpellpower(self, t) + local dest_power = getParadoxSpellpower(self, t, 0.3) + + -- Our Mines + local mine = Trap.new{ + name = ("warp mine: %s"):format(type), + type = "temporal", id_by_type=true, unided_name = "trap", + display = '^', color=colors.BLUE, image = ("trap/chronomine_%s_0%d.png"):format(type == "toward" and "blue" or "red", rng.avg(1, 4, 3)), + shader = "shadow_simulacrum", shader_args = { color = {0.2, 0.2, 0.2}, base = 0.8, time_factor = 1500 }, + dam = dam, t=t, power = power, dest_power = dest_power, + temporary = duration, + x = x, y = y, type = type, + summoner = self, summoner_gain_exp = true, + disarm_power = disarm, detect_power = detect, + canTrigger = function(self, x, y, who) + if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end + return false + end, + triggered = function(self, x, y, who) + -- Project our damage + self.summoner:project({type="hit",x=x,y=y, talent=t}, x, y, engine.DamageType.WARP, self.dam) + + -- Teleport? + if not who.dead then + -- Does our teleport hit? + local hit = self.summoner:checkHit(self.power, who:combatSpellResist() + (who:attr("continuum_destabilization") or 0)) and who:canBe("teleport") + if hit then + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") + local teleport_done = false + + if self.type == "toward" then + -- since we're using a precise teleport we'll look for a free grid first + local tx, ty = util.findFreeGrid(self.summoner.x, self.summoner.y, 5, true, {[engine.Map.ACTOR]=true}) + if tx and ty then + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") + if not who:teleportRandom(self.summoner.x, self.summoner.y, 1, 0) then + game.logSeen(self, "The teleport fizzles!") + else + teleport_done = true + end + end + elseif self.type == "away" then + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") + if not who:teleportRandom(self.summoner.x, self.summoner.y, 10, 5) then + game.logSeen(self, "The teleport fizzles!") + else + teleport_done = true + end + end + + -- Destabailize? + if teleport_done then + who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power}) + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") + game:playSoundNear(self, "talents/teleport") + end + else + game.logSeen(who, "%s resists the teleport!", who.name:capitalize()) + end + end + + return true, true + end, + canAct = false, + energy = {value=0}, + act = function(self) + self:useEnergy() + self.temporary = self.temporary - 1 + if self.temporary <= 0 then + if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end + game.level:removeEntity(self) + end + end, + } + + return mine +end + newTalent{ name = "Warp Mines", type = {"chronomancy/spacetime-folding", 1}, points = 5, mode = "passive", require = chrono_req1, - getRange = function(self, t) return math.floor(self:combatTalentScale(t, 5, 10, 0.5, 0, 1)) end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph + getRange = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 10))) end, -- Duration of mines trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50 on_learn = function(self, t) local lev = self:getTalentLevelRaw(t) @@ -52,10 +136,10 @@ newTalent{ local disarm = t.trapPower(self,t) local duration = t.getDuration(self, t) return ([[Learn to lay Warp Mines in a radius of 1 out to a range of %d. - Warp Mines teleport targets that trigger them either toward you or away from you depending on the type of mine used and inflict %0.2f temporal and %0.2f physical damage. + Warp Mines teleport targets that trigger them either toward you or away from you depending on the type of mine used and inflict %0.2f physical and %0.2f temporal (warp) damage. The mines are hidden traps (%d detection and %d disarm power based on your Magic) and last for %d turns. The damage caused by your Warp Mines will improve with your Spellpower.]]): - format(range, damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage), detect, disarm, duration) --I5 + format(range, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), detect, disarm, duration) --I5 end, } @@ -65,7 +149,7 @@ newTalent{ points = 1, cooldown = 10, paradox = function (self, t) return getParadoxCost(self, t, 10) end, - tactical = { ATTACKAREA = { TEMPORAL = 2 }, CLOSEIN = 2 }, + tactical = { ATTACKAREA = { TEMPORAL = 1, PHYSICAL = 1 }, CLOSEIN = 2 }, requires_target = true, range = function(self, t) return self:callTalent(self.T_WARP_MINES, "getRange")end, no_unlearn_last = true, @@ -75,76 +159,17 @@ newTalent{ local tx, ty = self:getTarget(tg) if not tx or not ty then return nil end local __, tx, ty = self:canProject(tg, tx, ty) - - - local dam = self:spellCrit(self:callTalent(self.T_WARP_MINES, "getDamage")) - local duration = self:callTalent(self.T_WARP_MINES, "getDuration") - local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8 - local disarm = self:callTalent(self.T_WARP_MINES, "trapPower") - + -- Lay the mines in a ball self:project(tg, tx, ty, function(px, py) local target_trap = game.level.map(px, py, Map.TRAP) if target_trap then return end if game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return end - -- Our Mines - local power = getParadoxSpellpower(self) - local trap = Trap.new{ - name = "warp mine: toward", - type = "temporal", id_by_type=true, unided_name = "trap", - display = '^', color=colors.BLUE, image = "trap/trap_warpmine_01.png", - shader = "shadow_simulacrum", - shader_args = { color = {0.2, 0.2, 0.6}, base = 0.8, time_factor = 1500 }, - dam = dam, power = power, - canTrigger = function(self, x, y, who) - if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end - return false - end, - triggered = function(self, x, y, who) - self:project({type="hit",x=x,y=y}, x, y, engine.DamageType.MATTER, self.dam) - -- Teleport Toward - if not who.dead then - local hit = self.summoner:checkHit(self.power, who:combatSpellResist() + (who:attr("continuum_destabilization") or 0)) and who:canBe("teleport") - if not hit then - game.logSeen(who, "%s resists the teleport!", who.name:capitalize()) - else - game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") - -- since we're using a precise teleport we'll look for a free grid first - local tx, ty = util.findFreeGrid(self.summoner.x, self.summoner.y, 5, true, {[Map.ACTOR]=true}) - if tx and ty then - game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") - if not who:teleportRandom(self.summoner.x, self.summoner.y, 1, 0) then - game.logSeen(self, "The warp fizzles!") - else - who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=power*0.3}) - game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") - end - end - end - end - - return true, true - end, - temporary = duration, - x = px, y = py, - disarm_power = math.floor(disarm), - detect_power = math.floor(detect), - canAct = false, - energy = {value=0}, - act = function(self) - self:useEnergy() - self.temporary = self.temporary - 1 - if self.temporary <= 0 then - if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end - game.level:removeEntity(self) - end - end, - summoner = self, - summoner_gain_exp = true, - } + -- Make our mine + local trap = makeWarpMine(self, t, px, py, "toward") - -- Add mines + -- Add the mine game.level:addEntity(trap) trap:identify(true) trap:setKnown(self, true) @@ -161,11 +186,11 @@ newTalent{ local duration = self:callTalent(self.T_WARP_MINES, "getDuration") local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8 local disarm = self:callTalent(self.T_WARP_MINES, "trapPower") - return ([[Lay Warp Mines in a radius of 1 that teleport enemies to you and inflict %0.2f temporal and %0.2f physical damage. + return ([[Lay Warp Mines in a radius of 1 that teleport enemies to you and inflict %0.2f physical and %0.2f temporal (warp) damage. The mines are hidden traps (%d detection and %d disarm power based on your Magic) and last for %d turns. The damage caused by your Warp Mines will improve with your Spellpower. Using this talent will trigger the cooldown on Warp Mine Away.]]): - format(damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage), detect, disarm, duration) + format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), detect, disarm, duration) end, } @@ -175,7 +200,7 @@ newTalent{ points = 1, cooldown = 10, paradox = function (self, t) return getParadoxCost(self, t, 10) end, - tactical = { ATTACKAREA = { TEMPORAL = 2 }, ESCAPE = 2 }, + tactical = { ATTACKAREA = { TEMPORAL = 1, PHYSICAL = 1 }, ESCAPE = 2 }, requires_target = true, range = function(self, t) return self:callTalent(self.T_WARP_MINES, "getRange") end, no_unlearn_last = true, @@ -185,12 +210,6 @@ newTalent{ local tx, ty = self:getTarget(tg) if not tx or not ty then return nil end local _ _, tx, ty = self:canProject(tg, tx, ty) - - local dam = self:spellCrit(self:callTalent(self.T_WARP_MINES, "getDamage")) - local duration = self:callTalent(self.T_WARP_MINES, "getDuration") - local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8 - local disarm = self:callTalent(self.T_WARP_MINES, "trapPower") - local power = getParadoxSpellpower(self) -- Lay the mines in a ball self:project(tg, tx, ty, function(px, py) @@ -198,57 +217,10 @@ newTalent{ if target_trap then return end if game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") then return end - -- Our Mines - local trap = Trap.new{ - name = "warp mine away", - type = "temporal", id_by_type=true, unided_name = "trap", - display = '^', color=colors.BLUE, image = "trap/trap_warpmine_02.png", - shader = "shadow_simulacrum", - shader_args = { color = {0.6, 0.2, 0.2}, base = 0.8, time_factor = 1500 }, - dam = dam, power = power, - canTrigger = function(self, x, y, who) - if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end - return false - end, - triggered = function(self, x, y, who) - self:project({type="hit",x=x,y=y}, x, y, engine.DamageType.MATTER, self.dam) - -- Teleport Away - if not who.dead then - local hit = self.summoner:checkHit(self.power, who:combatSpellResist() + (who:attr("continuum_destabilization") or 0)) and who:canBe("teleport") - if not hit then - game.logSeen(who, "%s resists the teleport!", who.name:capitalize()) - else - game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") - if not who:teleportRandom(self.summoner.x, self.summoner.y, 10, 5) then - game.logSeen(self, "The warp fizzles!") - else - who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=power*0.3}) - game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") - end - end - end + -- Make our mine + local trap = makeWarpMine(self, t, px, py, "away") - return true, true - end, - temporary = duration, - x = px, y = py, - disarm_power = math.floor(disarm), - detect_power = math.floor(detect), - canAct = false, - energy = {value=0}, - act = function(self) - self:useEnergy() - self.temporary = self.temporary - 1 - if self.temporary <= 0 then - if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end - game.level:removeEntity(self) - end - end, - summoner = self, - summoner_gain_exp = true, - } - - -- Add mines + -- Add the mine game.level:addEntity(trap) trap:identify(true) trap:setKnown(self, true) @@ -265,139 +237,225 @@ newTalent{ local duration = self:callTalent(self.T_WARP_MINES, "getDuration") local detect = self:callTalent(self.T_WARP_MINES, "trapPower") * 0.8 local disarm = self:callTalent(self.T_WARP_MINES, "trapPower") - return ([[Lay Warp Mines in a radius of 1 that teleport enemies away from you and inflict %0.2f temporal and %0.2f physical damage. + return ([[Lay Warp Mines in a radius of 1 that teleport enemies away from you and inflict %0.2f physical and %0.2f temporal (warp) damage. The mines are hidden traps (%d detection and %d disarm power based on your Magic) and last for %d turns. The damage caused by your Warp Mines will improve with your Spellpower. Using this talent will trigger the cooldown on Warp Mine Away.]]): - format(damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage), detect, disarm, duration) + format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), detect, disarm, duration) end, } newTalent{ - name = "Banish", + name = "Wormhole", type = {"chronomancy/spacetime-folding", 2}, require = chrono_req2, points = 5, paradox = function (self, t) return getParadoxCost(self, t, 10) end, cooldown = 10, tactical = { ESCAPE = 2 }, - range = 0, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 5.5)) end, - getTeleport = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 8, 16)) end, - target = function(self, t) - return {type="ball", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t} - end, - direct_hit = true, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, + radius = function(self, t) return math.floor(self:combatTalentLimit(t, 1, 7, 3)) end, -- Limit to radius 1 + requires_target = true, + getDuration = function (self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(self:getTalentLevel(t), 6, 10))) end, + no_npc_use = true, action = function(self, t) - local tg = self:getTalentTarget(t) - local hit = false + -- Target the entrance location + local tg = {type="bolt", nowarning=true, range=1, nolock=true, simple_dir_request=true, talent=t} + local entrance_x, entrance_y = self:getTarget(tg) + if not entrance_x or not entrance_y then return nil end + local _ _, entrance_x, entrance_y = self:canProject(tg, entrance_x, entrance_y) + local trap = game.level.map(entrance_x, entrance_y, engine.Map.TRAP) + if trap or game.level.map:checkEntity(entrance_x, entrance_y, Map.TERRAIN, "block_move") then game.logPlayer(self, "You can't place a wormhole entrance here.") return end - --checks for spacetime mastery hit bonus - local power = getParadoxSpellpower(self) - if self:knowTalent(self.T_SPACETIME_MASTERY) then - power = getParadoxSpellpower(self) * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower")) - end + -- Target the exit location + local tg = {type="hit", nolock=true, pass_terrain=true, nowarning=true, range=self:getTalentRange(t)} + local exit_x, exit_y = self:getTarget(tg) + if not exit_x or not exit_y then return nil end + local _ _, exit_x, exit_y = self:canProject(tg, exit_x, exit_y) + local trap = game.level.map(exit_x, exit_y, engine.Map.TRAP) + if trap or game.level.map:checkEntity(exit_x, exit_y, Map.TERRAIN, "block_move") or core.fov.distance(entrance_x, entrance_y, exit_x, exit_y) < 2 then game.logPlayer(self, "You can't place a wormhole exit here.") return end - self:project(tg, self.x, self.y, function(px, py) - local target = game.level.map(px, py, Map.ACTOR) - if not target or target == self then return end - game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport") - if self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) and target:canBe("teleport") then - if not target:teleportRandom(target.x, target.y, self:getTalentRadius(t) * 4, self:getTalentRadius(t) * 2) then - game.logSeen(target, "The spell fizzles on %s!", target.name:capitalize()) - else - target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=power*0.3}) - game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport") - hit = true - end - else - game.logSeen(target, "%s resists the banishment!", target.name:capitalize()) - end - end) + -- Wormhole values + local power = getParadoxSpellpower(self, t) + local dest_power = getParadoxSpellpower(self, t, 0.3) - if not hit then - game:onTickEnd(function() - if not self:attr("no_talents_cooldown") then - self.talents_cd[self.T_BANISH] = self.talents_cd[self.T_BANISH] /2 - end - end) + -- Our base wormhole + local function makeWormhole(x, y, dest_x, dest_y) + local wormhole = mod.class.Trap.new{ + name = "wormhole", + type = "annoy", subtype="teleport", id_by_type=true, unided_name = "trap", + image = "terrain/wormhole.png", + display = '&', color_r=255, color_g=255, color_b=255, back_color=colors.STEEL_BLUE, + message = "@Target@ moves onto the wormhole.", + temporary = t.getDuration(self, t), + x = x, y = y, dest_x = dest_x, dest_y = dest_y, + radius = self:getTalentRadius(t), + canAct = false, + energy = {value=0}, + disarm = function(self, x, y, who) return false end, + power = power, dest_power = dest_power, + summoned_by = self, -- "summoner" is immune to it's own traps + triggered = function(self, x, y, who) + local hit = who == self.summoned_by or who:checkHit(self.power, who:combatSpellResist()+(who:attr("continuum_destabilization") or 0), 0, 95) and who:canBe("teleport") -- Bug fix, Deprecrated checkhit call + if hit then + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") + if not who:teleportRandom(self.dest_x, self.dest_y, self.radius, 1) then + game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize()) + else + if who ~= self.summoned_by then who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power}) end + game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") + game:playSoundNear(self, "talents/teleport") + end + else + game.logSeen(who, "%s ignores the wormhole.", who.name:capitalize()) + end + return true + end, + act = function(self) + self:useEnergy() + self.temporary = self.temporary - 1 + if self.temporary <= 0 then + game.logSeen(self, "Reality asserts itself and forces the wormhole shut.") + if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end + game.level:removeEntity(self) + end + end, + } + + return wormhole end + + -- Adding the entrance wormhole + local entrance = makeWormhole(entrance_x, entrance_y, exit_x, exit_y) + game.level:addEntity(entrance) + entrance:identify(true) + entrance:setKnown(self, true) + game.zone:addEntity(game.level, entrance, "trap", entrance_x, entrance_y) + entrance.faction = nil + game:playSoundNear(self, "talents/heal") - game:playSoundNear(self, "talents/teleport") + -- Adding the exit wormhole + local exit = makeWormhole(exit_x, exit_y, entrance_x, entrance_y) + exit.x = exit_x + exit.y = exit_y + game.level:addEntity(exit) + exit:identify(true) + exit:setKnown(self, true) + game.zone:addEntity(game.level, exit, "trap", exit_x, exit_y) + exit.faction = nil + -- Linking the wormholes + entrance.dest = exit + exit.dest = entrance + + game.logSeen(self, "%s folds the space between two points.", self.name) return true end, info = function(self, t) + local duration = t.getDuration(self, t) local radius = self:getTalentRadius(t) - local range = t.getTeleport(self, t) - return ([[Randomly teleports all targets within a radius of %d around you. Targets will be teleported between %d and %d tiles from their current location. - If no targets are teleported the cooldown will be halved. - The chance of teleportion will scale with your Spellpower.]]):format(radius, range / 2, range) + local range = self:getTalentRange(t) + return ([[You fold the space between yourself and a second point within a range of %d, creating a pair of wormholes. Any creature stepping on either wormhole will be teleported near the other (radius %d accuracy). + The wormholes will last %d turns and must be placed at least two tiles apart. + The chance of teleporting enemies will scale with your Spellpower.]]) + :format(range, radius, duration) end, } newTalent{ - name = "Temporal Wake", + name = "Spatial Tether", type = {"chronomancy/spacetime-folding", 3}, require = chrono_req3, points = 5, - random_ego = "attack", - paradox = function (self, t) return getParadoxCost(self, t, 20) end, + paradox = function (self, t) return getParadoxCost(self, t, 10) end, cooldown = 10, - tactical = { ATTACK = {TEMPORAL = 1}, CLOSEIN = 2, DISABLE = { stun = 2 } }, - direct_hit = true, + tactical = { DISABLE = 2 }, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, requires_target = true, - is_teleport = true, + getDuration = function (self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(self:getTalentLevel(t), 6, 10))) end, + getChance = function(self, t) return paradoxTalentScale(self, t, 10, 20, 30) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self, t)) end, target = function(self, t) - return {type="beam", range=self:getTalentRange(t), friendlyfire=false, talent=t} + return {type="hit", range=self:getTalentRange(t), nowarning=true, talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, - range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 10, 0.5, 0, 1)) end, + no_energy=true, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then - game.logSeen(self, "You do not have line of sight.") - return nil - end local _ _, x, y = self:canProject(tg, x, y) - local ox, oy = self.x, self.y + local target = game.level.map(x, y, Map.ACTOR) + if not target then return end + if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then return end + + -- Tether values + local power = getParadoxSpellpower(self, t) + local dest_power = getParadoxSpellpower(self, t, 0.3) - -- since we're using a precise teleport we'll look for a free grid first - local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) - if tx and ty then - if not self:teleportRandom(tx, ty, 0) then - game.logSeen(self, "The teleport fizzles!") - else - local dam = self:spellCrit(t.getDamage(self, t)) - local x, y = ox, oy - self:project(tg, x, y, function(px, py) - DamageType:get(DamageType.MATTER).projector(self, px, py, DamageType.MATTER, dam) - local target = game.level.map(px, py, Map.ACTOR) - if target then - if target:canBe("stun") then - target:setEffect(target.EFF_STUNNED, t.getDuration(self, t), {apply_power=getParadoxSpellpower(self)}) + -- Store the old terrain + local oe = game.level.map(target.x, target.y, engine.Map.TERRAIN) + if not oe or oe:attr("temporary") then return true end + + -- Make our tether + local tether = mod.class.Object.new{ + old_feat = oe, type = oe.type, subtype = oe.subtype, + name = "temporal instability", image = oe.image, add_mos = {{image="object/temporal_instability.png"}}, + display = '&', color=colors.LIGHT_BLUE, + temporary = t.getDuration(self, t), + power = power, dest_power = dest_power, chance = t.getChance(self, t), + x = x, y = y, target = target, + summoner = self, summoner_gain_exp = true, + canAct = false, + energy = {value=0}, + act = function(self) + self:useEnergy() + self.temporary = self.temporary - 1 + + -- Teleport + if not self.target.dead and (game.level and game.level:hasEntity(self.target)) then + local hit = self.summoner == self.target or (self.summoner:checkHit(self.power, self.target:combatSpellResist() + (self.target:attr("continuum_destabilization") or 0), 0, 95) and self.target:canBe("teleport")) + if hit and rng.percent(self.chance * core.fov.distance(self.x, self.y, self.target.x, self.target.y)) then + game.level.map:particleEmitter(self.target.x, self.target.y, 1, "temporal_teleport") + -- Since we're using a precise teleport, find a free grit first + local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[engine.Map.ACTOR]=true}) + if not self.target:teleportRandom(tx, ty, 1, 0) then + game.logSeen(self, "The teleport fizzles!") else - game.logSeen(target, "%s resists the stun!", target.name:capitalize()) + if target ~= self.summoner then + self.target:setEffect(self.target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.dest_power}) + end + game.level.map:particleEmitter(self.target.x, self.target.y, 1, "temporal_teleport") + game:playSoundNear(self, "talents/teleport") end + else + game.logSeen(self.target, "%s resists the teleport!", self.target.name:capitalize()) end - end) - game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "temporal_lightning", {tx=x-self.x, ty=y-self.y}) - game:playSoundNear(self, "talents/lightning") - end - end + end + + -- End the effect? + if self.temporary <= 0 then + game.level.map(self.x, self.y, engine.Map.TERRAIN, self.old_feat) + game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y) + game.level:removeEntity(self) + end + end, + } + + -- add our tether to the map + game.level:addEntity(tether) + game.level.map(x, y, Map.TERRAIN, tether) + game.nicer_tiles:updateAround(game.level, x, y) + game.level.map:updateMap(x, y) + game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) - local stun = t.getDuration(self, t) - local damage = t.getDamage(self, t)/2 - return ([[Violently fold the space between yourself and another point within range. - You move to the target location, and leave a temporal wake behind that stuns for %d turns and inflicts %0.2f temporal and %0.2f physical damage to everything in the path. - The damage will scale with your Spellpower.]]): - format(stun, damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage)) + local duration = t.getDuration(self, t) + local chance = t.getChance(self, t) + return ([[Tethers the target to the location for %d turns. For each tile the target moves away from the target location it has a %d%% chance each turn of being teleported back to the tether. + The teleportation chance scales with your Spellpower.]]) + :format(duration, chance) end, } @@ -407,12 +465,12 @@ newTalent{ require = chrono_req4, points = 5, paradox = function (self, t) return getParadoxCost(self, t, 20) end, - cooldown = 24, + cooldown = 12, tactical = { DISABLE = 2 }, - range = 10, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 10))) end, target = function(self, t) return {type="ball", range=self:getTalentRange(t), friendlyfire=false, radius=self:getTalentRadius(t), talent=t} end, @@ -422,16 +480,19 @@ newTalent{ local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - local _ _, x, y = self:canProject(tg, x, y) - - local damage = self:spellCrit(t.getDamage(self, t)) - self:project(tg, x, y, function(px, py) - local target = game.level.map(px, py, Map.ACTOR) - if not target then return end - target:setEffect(target.EFF_DIMENSIONAL_ANCHOR, t.getDuration(self, t), {damage=damage, src=self, apply_power=getParadoxSpellpower(self)}) - end) + local _ _, _, _, x, y = self:canProject(tg, x, y) + + -- Add a lasting map effect + local dam = self:spellCrit(t.getDamage(self, t)) + game.level.map:addEffect(self, + x, y, t.getDuration(self,t), + DamageType.DIMENSIONAL_ANCHOR, {dam=dam, dur=1, src=self, apply=getParadoxSpellpower(self, t)}, + self:getTalentRadius(t), + 5, nil, + {type="temporal_cloud"}, + nil, false, false + ) - game.level.map:particleEmitter(x, y, tg.radius, "ball_teleport", {radius=tg.radius}) game:playSoundNear(self, "talents/teleport") return true @@ -440,7 +501,7 @@ newTalent{ local damage = t.getDamage(self, t)/2 local radius = self:getTalentRadius(t) local duration = t.getDuration(self, t) - return ([[Anchors enemies in a radius of %d. Anchored targets will be prevented from teleporting for %d turns and take %0.2f temporal and %0.2f physical damage on teleport attempts. - The damage will scale with your Spellpower.]]):format(radius, duration, damDesc(self, DamageType.TEMPORAL, damage), damDesc(self, DamageType.PHYSICAL, damage)) + return ([[Create a radius %d anti-telport field for %d turns. Enemies in the field will be anchored, preventing teleportation and taking %0.2f physical and %0.2f temporal (warp) damage on teleport attempts. + The damage will scale with your Spellpower.]]):format(radius, duration, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage)) end, } \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua index 7662ceef24c444b4054e6006d8b11e8793875ec3..07e3effa385fa4238946ba315b77108684292f72 100644 --- a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua +++ b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua @@ -17,7 +17,7 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org --- EDGE TODO: Icons, Particles, Timed Effect Particles +-- EDGE TODO: Particles, Timed Effect Particles newTalent{ name = "Dimensional Step", @@ -27,261 +27,180 @@ newTalent{ paradox = function (self, t) return getParadoxCost(self, t, 10) end, cooldown = 10, tactical = { CLOSEIN = 2, ESCAPE = 2 }, - range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 10, 0.5, 0, 1)) end, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, requires_target = true, target = function(self, t) - return {type="hit", range=self:getTalentRange(t), nolock=true, nowarning=true} + return {type="hit", nolock=true, range=self:getTalentRange(t)} end, direct_hit = true, no_energy = true, is_teleport = true, action = function(self, t) local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) + local x, y, target = self:getTarget(tg) if not x or not y then return nil end - if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then + if not self:hasLOS(x, y) or game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then -- To prevent teleporting through walls game.logSeen(self, "You do not have line of sight.") return nil end - local __, x, y = self:canProject(tg, x, y) - - game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") - - -- since we're using a precise teleport we'll look for a free grid first - local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) - if tx and ty then - if not self:teleportRandom(tx, ty, 0) then - game.logSeen(self, "The spell fizzles!") + local _ _, x, y = self:canProject(tg, x, y) + + -- Swap? + if self:getTalentLevel(t) >= 5 and target then + -- Hit? + if target:canBe("teleport") and self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) then + -- Grab the caster's location + local ox, oy = self.x, self.y + + -- Remove the target so the destination tile is empty + game.level.map:remove(target.x, target.y, Map.ACTOR) + + -- Try to teleport to the target's old location + if self:teleportRandom(x, y, 0) then + -- Put the target back in the caster's old location + game.level.map(ox, oy, Map.ACTOR, target) + target.x, target.y = ox, oy + + game.level.map:particleEmitter(target.x, target.y, 1, "temporal_teleport") + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + else + -- If we can't teleport, return the target + game.level.map(target.x, target.y, Map.ACTOR, target) + game.logSeen(self, "The spell fizzles!") + end + else + game.logSeen(target, "%s resists the swap!", target.name:capitalize()) + end + else + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + -- since we're using a precise teleport we'll look for a free grid first + local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) + if tx and ty then + if not self:teleportRandom(tx, ty, 0) then + game.logSeen(self, "The spell fizzles!") + else + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + end end end - - game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + game:playSoundNear(self, "talents/teleport") - return true end, info = function(self, t) local range = self:getTalentRange(t) - return ([[Teleports you to up to %d tiles away, to a targeted location in line of sight. Additional talent points increase the range. - This spell takes no time to cast.]]):format(range) + return ([[Teleports you to up to %d tiles away, to a targeted location in line of sight. + At talent level 5 you may swap positions with a target creature.]]):format(range) end, } newTalent{ - name = "Phase Shift", + name = "Dimensional Shift", type = {"chronomancy/spacetime-weaving", 2}, mode = "passive", require = chrono_req2, points = 5, - getChance = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end, - doPhaseShift = function(self, t) + getReduction = function(self, t) return math.ceil(self:getTalentLevel(t)) end, + getCount = function(self, t) + return 1 + math.floor(self:combatTalentLimit(t, 3, 0, 2)) + end, + doShift = function(self, t) local effs = {} - -- Go through all effects for eff_id, p in pairs(self.tmp) do local e = self.tempeffect_def[eff_id] - if e.status == "detrimental" and e.type ~= "other" then - effs[#effs+1] = {"effect", eff_id} + if e.type ~= "other" and e.status == "detrimental" and e.subtype ~= "cross tier" then + effs[#effs+1] = p end end - - -- Roll to remove each one - for i = 1, #effs do - if #effs == 0 then break end + + for i=1, t.getCount(self, t) do local eff = rng.tableRemove(effs) - if eff[1] == "effect" and rng.percent(t.getChance(self, t)) then - self:removeEffect(eff[2]) + if not eff then break end + eff.dur = eff.dur - t.getReduction(self, t) + if eff.dur <= 0 then + self:removeEffect(eff.effect_id) end end + end, info = function(self, t) - local chance = t.getChance(self, t) - return ([[When you teleport you have a %d%% chance to remove each detrimental status effect currently affecting you. - Each effect is checked individually and the chance scales with your Spellpower.]]): - format(chance) + local count = t.getCount(self, t) + local reduction = t.getReduction(self, t) + return ([[When you teleport you reduce the duration of up to %d detrimental effects by %d turns.]]): + format(count, reduction) end, } newTalent{ - name = "Wormhole", + name = "Phase Shift", type = {"chronomancy/spacetime-weaving", 3}, require = chrono_req3, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 10) end, - cooldown = 10, - tactical = { ESCAPE = 2 }, - range = 10, - radius = function(self, t) return math.floor(self:combatTalentLimit(t, 0, 7, 3)) end, -- Limit to radius 0 - requires_target = true, - getDuration = function (self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 6, 10)) end, - no_npc_use = true, + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 24, + tactical = { DEFEND = 2 }, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentLimit(t, 25, 3, 7, true))) end, action = function(self, t) - local tg = {type="bolt", nowarning=true, range=1, nolock=true, simple_dir_request=true, talent=t} - local entrance_x, entrance_y = self:getTarget(tg) - if not entrance_x or not entrance_y then return nil end - local _ _, entrance_x, entrance_y = self:canProject(tg, entrance_x, entrance_y) - local trap = game.level.map(entrance_x, entrance_y, engine.Map.TRAP) - if trap or game.level.map:checkEntity(entrance_x, entrance_y, Map.TERRAIN, "block_move") then game.logPlayer(self, "You can't place a wormhole entrance here.") return end - - -- Finding the exit location - -- First, find the center possible exit locations - local x, y, radius, minimum_distance - radius = self:getTalentRadius(t) - minimum_distance = 0 - local tg = {type="ball", nolock=true, pass_terrain=true, nowarning=true, range=self:getTalentRange(t), radius=radius} - x, y = self:getTarget(tg) - print("[Target]", x, y) - if not x then return nil end - -- Make sure the target is within range - if core.fov.distance(self.x, self.y, x, y) > self:getTalentRange(t) then - game.logPlayer(self, "Pick a valid location.") - return false - end - - -- Second, select one of the possible exit locations - local poss = {} - for i = x - radius, x + radius do - for j = y - radius, y + radius do - if game.level.map:isBound(i, j) and - core.fov.distance(x, y, i, j) <= radius and - core.fov.distance(x, y, i, j) >= minimum_distance and - self:canMove(i, j) and not game.level.map(i, j, engine.Map.TRAP) then - poss[#poss+1] = {i,j} - end - end - end - if #poss == 0 then game.logPlayer(self, "No exit location could be found.") return false end - local pos = poss[rng.range(1, #poss)] - local exit_x, exit_y = pos[1], pos[2] - print("[[wormhole]] entrance ", entrance_x, " :: ", entrance_y) - print("[[wormhole]] exit ", exit_x, " :: ", exit_y) - - --checks for spacetime mastery hit bonus - local power = getParadoxSpellpower(self) - if self:knowTalent(self.T_SPACETIME_MASTERY) then - power = power * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower")) - end - - -- Adding the entrance wormhole - local entrance = mod.class.Trap.new{ - name = "wormhole", - type = "annoy", subtype="teleport", id_by_type=true, unided_name = "trap", - image = "terrain/wormhole.png", - display = '&', color_r=255, color_g=255, color_b=255, back_color=colors.STEEL_BLUE, - message = "@Target@ moves onto the wormhole.", - temporary = t.getDuration(self, t), - x = entrance_x, y = entrance_y, - canAct = false, - energy = {value=0}, - disarm = function(self, x, y, who) return false end, - power = power, - summoned_by = self, -- "summoner" is immune to it's own traps - triggered = function(self, x, y, who) - if who == self.summoned_by or who:checkHit(self.power, who:combatSpellResist()+(who:attr("continuum_destabilization") or 0), 0, 95) and who:canBe("teleport") then -- Bug fix, Deprecrated checkhit call - -- since we're using a precise teleport we'll look for a free grid first - local tx, ty = util.findFreeGrid(self.dest.x, self.dest.y, 5, true, {[engine.Map.ACTOR]=true}) - if tx and ty then - game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") - if not who:teleportRandom(tx, ty, 0) then - game.logSeen(who, "%s tries to enter the wormhole but a violent force pushes it back.", who.name:capitalize()) - elseif who ~= self.summoned_by then - who:setEffect(who.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self.destabilization_power}) - game.level.map:particleEmitter(who.x, who.y, 1, "temporal_teleport") - end - end - else - game.logSeen(who, "%s ignores the wormhole.", who.name:capitalize()) - end - return true - end, - act = function(self) - self:useEnergy() - self.temporary = self.temporary - 1 - if self.temporary <= 0 then - game.logSeen(self, "Reality asserts itself and forces the wormhole shut.") - if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end - game.level:removeEntity(self) - end - end, - } - entrance.faction = nil - game.level:addEntity(entrance) - entrance:identify(true) - entrance:setKnown(self, true) - game.zone:addEntity(game.level, entrance, "trap", entrance_x, entrance_y) - game.level.map:particleEmitter(entrance_x, entrance_y, 1, "temporal_teleport") - game:playSoundNear(self, "talents/heal") - - -- Adding the exit wormhole - local exit = entrance:clone() - exit.x = exit_x - exit.y = exit_y - game.level:addEntity(exit) - exit:identify(true) - exit:setKnown(self, true) - game.zone:addEntity(game.level, exit, "trap", exit_x, exit_y) - game.level.map:particleEmitter(exit_x, exit_y, 1, "temporal_teleport") - - -- Linking the wormholes - entrance.dest = exit - exit.dest = entrance - - game.logSeen(self, "%s folds the space between two points.", self.name) + self:setEffect(self.EFF_PHASE_SHIFT, t.getDuration(self, t), {}) + game:playSoundNear(self, "talents/teleport") return true end, info = function(self, t) local duration = t.getDuration(self, t) - local radius = self:getTalentRadius(t) - return ([[You fold the space between yourself and a second point within range (radius %d accuracy), creating a pair of wormholes. Any creature stepping on either wormhole will be teleported to the other. The wormholes will last %d turns. - The chance of teleporting enemies will scale with your Spellpower.]]) - :format(radius, duration) + return ([[Phase shift yourself for %d turns; any damage greater than 10%% of your maximum life will teleport you to an adjacent tile and be reduced by 50%% (can only happen once per turn).]]): + format(duration) end, } newTalent{ - name = "Displace Damage", + name = "Phase Pulse", type = {"chronomancy/spacetime-weaving", 4}, - mode = "sustained", require = chrono_req4, - sustain_paradox = 48, + tactical = { ATTACKAREA = {TEMPORAL = 1, PHYSICAL = 1} }, + mode = "sustained", + sustain_paradox = 36, cooldown = 10, - tactical = { BUFF = 2 }, points = 5, - -- called by _M:onTakeHit function in mod\class\Actor.lua to perform the damage displacment - getDisplaceDamage = function(self, t) return self:combatTalentLimit(t, 25, 5, 15)/100 end, -- Limit < 25% - range = 10, - callbackOnTakeDamage = function(self, t, src, x, y, type, dam, tmp, no_martyr) - if dam > 0 and src ~= self then - -- find available targets - local tgts = {} - local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), true) - for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do - local a = game.level.map(x, y, Map.ACTOR) - if a and self:reactionToward(a) < 0 then - tgts[#tgts+1] = a - end - end end - - -- Displace the damage - local a = rng.table(tgts) - if a then - local displace = dam * t.getDisplaceDamage(self, t) - game:delayedLogMessage(self, a, "displace_damage"..(a.uid or ""), "#PINK##Source# displaces some damage onto #Target#!") - DamageType.defaultProjector(self, a.x, a.y, type, displace, tmp, true) - dam = dam - displace - end - end + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 70, getParadoxSpellpower(self, t)) end, + range = 0, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 3.5)) end, + target = function(self, t) + return {type="ball", range=100, radius=self:getTalentRadius(t), selffire=false, talent=t} + end, + doPulse = function(self, t, ox, oy, fail) + local tg = self:getTalentTarget(t) + local dam = self:spellCrit(t.getDamage(self, t)) + local distance = core.fov.distance(self.x, self.y, ox, oy) + local chance = distance * 10 - return {dam=dam} + if not fail then + dam = dam * (1 + math.min(1, distance/10)) + game:onTickEnd(function() + self:project(tg, ox, oy, DamageType.WARP, dam) + self:project(tg, self.x, self.y, DamageType.WARP, dam) + end) + else + dam = dam *2 + chance = 100 + tg.radius = tg.radius * 2 + game:onTickEnd(function() + self:project(tg, self.x, self.y, DamageType.WARP, dam) + end) + end end, activate = function(self, t) + game:playSoundNear(self, "talents/spell_generic") return {} end, deactivate = function(self, t, p) return true end, info = function(self, t) - local displace = t.getDisplaceDamage(self, t) * 100 - return ([[You bend space around you, displacing %d%% of any damage you receive onto a random enemy within range. - ]]):format(displace) + local damage = t.getDamage(self, t)/2 + local radius = self:getTalentRadius(t) + return ([[When you teleport with Phase Pulse active you deal %0.2f physical and %0.2f temporal (warp) damage to all targets in a radius of %d around you. + For each space you move from your original location the damage is increased by 10%% (to a maximum bonus of 100%%). If the teleport fails, the blast radius and damage will be doubled. + This effect occurs both at the entrance and exist locations and the damage will scale with your Spellpower.]]): + format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage), radius) end, } diff --git a/game/modules/tome/data/talents/chronomancy/spatial-tears.lua b/game/modules/tome/data/talents/chronomancy/spatial-tears.lua new file mode 100644 index 0000000000000000000000000000000000000000..a632e4b1508f9db9b7e06148742a009e884be087 --- /dev/null +++ b/game/modules/tome/data/talents/chronomancy/spatial-tears.lua @@ -0,0 +1,311 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +-- EDGE TODO: Particles, Timed Effect Particles + +local Object = require "mod.class.Object" + +newTalent{ + name = "Spatial Fragments", + type = {"chronomancy/spatial-tears",1}, + require = chrono_req_high1, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 10) end, + cooldown = 3, + tactical = { ATTACK = { TEMPORAL = 1, PHYSICAL = 1 }, }, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, + proj_speed = 4, + target = function(self, t) + return {type="bolt", range=self:getTalentRange(t), talent=t, nowarning=true, display={particle="arrow", particle_args={tile=("particles_images/spatial_fragment"):format(rng.range(1, 4))}}} + end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 100, getParadoxSpellpower(self, t)) end, + action = function(self, t) + local p = self:isTalentActive(self.T_FRACTURED_SPACE) + + local tg = self:getTalentTarget(t) + -- Beam? + local beam = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges >=6 or false + if beam then + tg.type = "beam" + end + local x, y, target = self:getTarget(tg) + if not x or not y then return nil end + if beam then self:isTalentActive(self.T_FRACTURED_SPACE).charges = 0 end + + -- Fire one bolt per available target + if target == self then + -- Find available targets + local tgts = {} + local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), true) + for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do + local a = game.level.map(x, y, Map.ACTOR) + if a and self:reactionToward(a) < 0 then + tgts[#tgts+1] = a + end + end end + + -- Fire a bolt at each one + local dam = self:spellCrit(t.getDamage(self, t)) + for i = 1, 3 do + if #tgts <= 0 then break end + local a, id = rng.table(tgts) + table.remove(tgts, id) + self:projectile(tg, a.x, a.y, DamageType.WARP, dam, nil) + end + else + -- Fire all bolts at one target + local dam = self:spellCrit(t.getDamage(self, t)) + for i = 1, 3 do + self:projectile(tg, x, y, DamageType.WARP, dam, nil) + end + end + + game:playSoundNear(self, "talents/earth") + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t)/2 + return ([[Fire three Spatial Fragments at the target that each inflict %0.2f physical and %0.2f temporal (warp) damage. If you target yourself you'll instead fire one Spatial Fragment at up to three targets within range. + If Fractured Space is fully charged the projectiles will be able to pierce through targets. This will consume your Fractured Space charges. + The damage scales with your Spellpower.]]) + :format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage)) + end, +} + +newTalent{ + name = "Discontinuity", + type = {"chronomancy/spatial-tears", 2}, + require = chrono_req_high2, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 6, + tactical = { ATTACK = { TEMPORAL = 1, PHYSICAL = 1 }, }, + range = 10, + direct_hit = true, + requires_target = true, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, + getLength = function(self, t) return 1 + math.floor(self:combatTalentScale(t, 3, 7)/2)*2 end, + target = function(self, t) + local halflength = math.floor(t.getLength(self,t)/2) + local block = function(_, lx, ly) + return game.level.map:checkAllEntities(lx, ly, "block_move") + end + return {type="wall", range=self:getTalentRange(t), halflength=halflength, talent=t, halfmax_spots=halflength+1, block_radius=block} + end, + action = function(self, t) + local tg = self:getTalentTarget(t) + local x, y = self:getTarget(tg) + if not x or not y then return nil end + local _ _, _, _, x, y = self:canProject(tg, x, y) + if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then return nil end + + local damage = self:spellCrit(t.getDamage(self, t)) + local block = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges >=6 or false + if block then self:isTalentActive(self.T_FRACTURED_SPACE).charges = 0 end + self:project(tg, x, y, function(px, py, tg, self) + local oe = game.level.map(px, py, Map.TERRAIN) + if not oe or oe.special then return end + if not oe or oe:attr("temporary") or game.level.map:checkAllEntities(px, py, "block_move") then return end + local e = Object.new{ + old_feat = oe, + type = "void", subtype = "void", + name = "discontinuity", + display = ' ', image = ("terrain/rift/rift_floor_0%d.png"):format(rng.avg(0, 4, 3)), + _noalpha = false, + always_remember = true, + does_block_move = block, + block_move = block, + pass_projectile = true, + is_void = true, + can_pass = {pass_void=1}, + show_tooltip = true, + temporary = t.getDuration(self, t), + x = px, y = py, + dam = damage, + canAct = false, + act = function(self) + local tg = {type="ball", range=0, friendlyfire=false, radius = 1, talent=t, x=self.x, y=self.y,} + self.summoner.__project_source = self + local grids = self.summoner:project(tg, self.x, self.y, engine.DamageType.WARP, self.dam) + if core.shader.active() then + game.level.map:particleEmitter(self.x, self.y, tg.radius, "starfall", {radius=tg.radius, tx=self.x, ty=self.y}) + else + game.level.map:particleEmitter(self.x, self.y, tg.radius, "shadow_flash", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y}) + end + self.summoner.__project_source = nil + self:useEnergy() + self.temporary = self.temporary - 1 + if self.temporary <= 0 then + game.level.map(self.x, self.y, engine.Map.TERRAIN, self.old_feat) + game.level:removeEntity(self) + game.level.map:updateMap(self.x, self.y) + game.nicer_tiles:updateAround(game.level, self.x, self.y) + end + end, + dig = function(src, x, y, old) + game.level:removeEntity(old) + return nil, old.old_feat + end, + summoner_gain_exp = true, + summoner = self, + } + e.tooltip = mod.class.Grid.tooltip + game.level:addEntity(e) + game.level.map(px, py, Map.TERRAIN, e) + if not block then + game.nicer_tiles:updateAround(game.level, px, py) + game.level.map:updateMap(px, py) + end + end) + + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t)/2 + local length = t.getLength(self, t) + local duration = t.getDuration(self, t) + return ([[Create a void wall of length %d that lasts %d turns. Each turn the wall deals %0.2f physical and %0.2f temporal (warp) damage to all enemies within a radius of 1. + If Fractured Space is fully charged the wall will block movement, but not sight or projectiles. This will consume your Fractured Space charges. + The damage will scale with your Spellpower.]]) + :format(length, duration, damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage)) + end, +} + +newTalent{ + name = "Fractured Space", + type = {"chronomancy/spatial-tears",3}, + require = chrono_req_high3, + mode = "sustained", + sustain_paradox = 24, + cooldown = 10, + tactical = { BUFF = 2 }, + points = 5, + getDamage = function(self, t) return self:combatTalentLimit(t, 100, 10, 75)/12 end, + getChance = function(self, t) return self:combatTalentLimit(t, 100, 10, 75)/6 end, + iconOverlay = function(self, t, p) + local val = p.charges or 0 + if val <= 0 then return "" end + local fnt = "buff_font" + return tostring(math.ceil(val)), fnt + end, + callbackOnActBase = function(self, t) + -- Charge decay + local p = self:isTalentActive(self.T_FRACTURED_SPACE) + p.decay = p.decay + 1 + if p.decay >=2 then + p.decay = 0 + p.charges = math.max(p.charges - 1, 0) + end + end, + activate = function(self, t) + game:playSoundNear(self, "talents/heal") + --local particle = Particles.new("ultrashield", 1, { rm=0, rM=176, gm=196, gM=255, bm=222, bM=255, am=25, aM=125, radius=0.2, density=30, life=28, instop=-40}) + return { + charges = 0, decay = 0 + -- particle = self:addParticles(particle) + } + end, + deactivate = function(self, t, p) + -- self:removeParticles(p.particle) + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + local chance = t.getChance(self, t) + local charges = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges or 0 + return ([[Each time you deal warp damage Fractured Space gains one charge, up to a maximum of six charges. If you're not generating charges one charge will decay every other turn. + Each charge increases warp damage by %d%% and gives your Warp damage a %d%% chance to stun, blind, pin, or confuse affected targets for 3 turns. + If Fractured Space is fully charged, your Spatial Tears talents will consume them when cast and have bonus effects (see indvidual talent descriptions). + + Current damage bonus: %d%% + Current effect chance: %d%%]]):format(damage, chance, damage * charges, chance * charges) + end, +} + +newTalent{ + name = "Sphere of Destruction", + type = {"chronomancy/spatial-tears", 4}, + require = chrono_req_high4, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 18) end, + cooldown = 18, + tactical = { ATTACKAREA = {PHYSICAL = 2, TEMPORAL = 2} }, + range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 1)) end, + radius = 3, + proj_speed = 3, + target = function(self, t) + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} + end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 100, getParadoxSpellpower(self, t)) end, + action = function(self, t) + local tg = {type="beam", range=self:getTalentRange(t), talent=t, display={particle="icestorm"}} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + local _ _, _, _, x, y = self:canProject(tg, x, y) + + -- Store some values inside the target table so they're not lost when we reload + tg.chrono_sphere = {} + tg.chrono_sphere.dam = self:spellCrit(t.getDamage(self, t)) + tg.chrono_sphere.strip = self:isTalentActive(self.T_FRACTURED_SPACE) and self:isTalentActive(self.T_FRACTURED_SPACE).charges >=6 or false + if tg.chrono_sphere.strip then self:isTalentActive(self.T_FRACTURED_SPACE).charges = 0; tg.chrono_sphere.power = getParadoxSpellpower(self, t) end + + -- A beam projectile + self:projectile(tg, x, y, function(px, py, tg, self) + -- That projects balls as it moves + local tg2 = self:getTalentTarget(self:getTalentFromId(self.T_SPHERE_OF_DESTRUCTION)) + self:project(tg2, px, py, function(px2, py2) + local DamageType = require "engine.DamageType" + DamageType:get(DamageType.WARP).projector(self, px2, py2, DamageType.WARP, tg.chrono_sphere.dam) + + -- Do we strip a sustain? + if tg.chrono_sphere.strip then + local target = game.level.map(px2, py2, engine.Map.ACTOR) + if not target then return end + + local effs = {} + -- Go through all sustained spells + for tid, act in pairs(target.sustain_talents) do + if act then + effs[#effs+1] = {"talent", tid} + end + end + + if #effs == 0 then return end + local eff = rng.table(effs) + + if self:checkHit(tg.chrono_sphere.power, target:combatSpellResist(), 0, 95, 5) then + target:crossTierEffect(target.EFF_SPELLSHOCKED, tg.chrono_sphere.power) + target:forceUseTalent(eff[2], {ignore_energy=true}) + end + end + end) + end) + + game:playSoundNear(self, "talents/icestorm") + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t)/2 + return ([[Create a Sphere of Destruction that travels towards the target location, inflicting %0.2f physical and %0.2f temporal (warp) damage in a radius of three. + If Fractured Space is fully charged the Sphere will remove a single sustain from targets it damages. This will consume your Fractured Space charges. + The sphere can hit a single target multiple times in one turn and the damage will scale with your Spellpower.]]) + :format(damDesc(self, DamageType.PHYSICAL, damage), damDesc(self, DamageType.TEMPORAL, damage)) + end, +} diff --git a/game/modules/tome/data/talents/chronomancy/speed-control.lua b/game/modules/tome/data/talents/chronomancy/speed-control.lua index 254cc935943bb6ef9d7963bb4afe78cd8a8612ab..acbd0d107ce320cf708386123ebeeeda24bd9857 100644 --- a/game/modules/tome/data/talents/chronomancy/speed-control.lua +++ b/game/modules/tome/data/talents/chronomancy/speed-control.lua @@ -17,7 +17,7 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org --- EDGE TODO: Icons, Particles, Timed Effect Particles +-- EDGE TODO: Particles, Timed Effect Particles newTalent{ name = "Celerity", @@ -25,19 +25,18 @@ newTalent{ require = chrono_req1, points = 5, mode = "passive", - getSpeed = function(self, t) return self:combatTalentScale(t, 0.1, 0.25, 0.5) end, - doCelerity = function(self, t) - local speed = t.getSpeed(self, t) - - self:setEffect(self.EFF_CELERITY, 3, {speed=speed, charges=1, max_charges=3}) - + getSpeed = function(self, t) return self:combatTalentScale(t, 10, 30)/100 end, + doCelerity = function(self, t, x, y) + if self.x ~= x or self.y ~= x then + local speed = t.getSpeed(self, t) + self:setEffect(self.EFF_CELERITY, 1, {speed=speed, charges=1, max_charges=3}) + end return true end, info = function(self, t) local speed = t.getSpeed(self, t) * 100 - return ([[When you move you gain %d%% movement speed, stacking up to three times. - Performing any action other than movement will break this effect.]]): - format(speed, speed) + return ([[When you move you gain %d%% movement speed for one turn. This effect stacks up to three times. + ]]):format(speed) end, } @@ -48,11 +47,12 @@ newTalent{ points = 5, sustain_paradox = 36, mode = "sustained", + no_sustain_autoreset = true, cooldown = 12, tactical = { ATTACKAREA = 1, DISABLE = 3 }, range = 0, - radius = 3, - getSlow = function(self, t) return math.min(self:combatTalentSpellDamage(t, 10, 25, getParadoxSpellpower(self))/ 100 , 0.6) end, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end, + getSlow = function(self, t) return paradoxTalentScale(self, t, 20, 40, 60)/100 end, target = function(self, t) return {type="ball", range=self:getTalentRange(t), friendlyfire=false, radius = self:getTalentRadius(t), talent=t} end, @@ -70,18 +70,23 @@ newTalent{ else p.charges = math.min(p.charges + 1, 3) end + -- Dilate Time if p.charges > 0 and not self.resting then self:project(self:getTalentTarget(t), self.x, self.y, function(px, py) local target = game.level.map(px, py, Map.ACTOR) if not target then return end - target:setEffect(target.EFF_SLOW, 2, {power=p.power*p.charges, apply_power=self:combatSpellpower(), no_ct_effect=true}) + target:setEffect(target.EFF_SLOW, 1, {power=p.power*p.charges, apply_power=getParadoxSpellpower(self, t), no_ct_effect=true}) end) end end, + updateOnTeleport = function(self, t, x, y) + local p = self:isTalentActive(self.T_TIME_DILATION) + p.x, p.y = x, y + end, activate = function(self, t) local ret ={ - x = self.x, y=self.y, power = t.getSlow(self, t), charges = 0 + x = self.x, y=self.y, power = t.getSlow(self, t)/3, charges = 0 } game:playSoundNear(self, "talents/arcane") return ret @@ -92,9 +97,10 @@ newTalent{ end, info = function(self, t) local slow = t.getSlow(self, t) * 100 - return ([[Time Dilates around you, reducing the speed of all enemies within a radius of three by up to %d%%. This effect builds gradually over three turns and loses %d%% power each time you move. - The speed decrease will scale with your Spellpower]]): - format(slow*3, slow) + local radius = self:getTalentRadius(t) + return ([[Time Dilates around you, reducing the speed of all enemies within a radius of %d by up to %d%%. This effect builds gradually over three turns and loses %d%% power each time you move. + Movement via teleport will not lower the power and the speed decrease will scale with your Spellpower.]]): + format(radius, slow, slow/3) end, } @@ -106,15 +112,23 @@ newTalent{ paradox = function (self, t) return getParadoxCost(self, t, 20) end, cooldown = 24, tactical = { BUFF = 2, CLOSEIN = 2, ESCAPE = 2 }, - getPower = function(self, t) return self:combatScale(self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)), 0, 0, 0.57, 57, 0.75) end, + getPower= function(self, t) return paradoxTalentScale(self, t, 20, 40, 60)/300 end, + getDuration = function(self, t) return getExtensionModifier(self, t, 4) end, + no_energy = true, action = function(self, t) - self:setEffect(self.EFF_SPEED, 4, {power=t.getPower(self, t)}) + local celerity = self:hasEffect(self.EFF_CELERITY) and self:hasEffect(self.EFF_CELERITY).charges or 0 + local dilation = self:isTalentActive(self.T_TIME_DILATION) and self:isTalentActive(self.T_TIME_DILATION).charges or 0 + local move = t.getPower(self, t) * celerity + local speed = t.getPower(self, t) * dilation + + self:setEffect(self.EFF_HASTE, t.getDuration(self, t), {move=move, speed=speed}) return true end, info = function(self, t) - local power = t.getPower(self, t) - return ([[Increases your global speed by %d%% for the next 4 game turns. - The speed increase will scale with your Spellpower.]]):format(100 * power) + local duration = t.getDuration(self, t) + local power = t.getPower(self, t) * 100 + return ([[Increases your movement speed by %d%% per stack of Celerity and your attack, spell, and mind speed by %d%% per stack of Time Dilation for the next %d game turns. + The speed increase will scale with your Spellpower.]]):format(power, power, duration) end, } @@ -123,31 +137,33 @@ newTalent{ type = {"chronomancy/speed-control", 4}, require = chrono_req4, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 48) end, + paradox = function (self, t) return getParadoxCost(self, t, 24) end, cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 10, 45, 25)) end, -- Limit >10 tactical = { BUFF = 2, CLOSEIN = 2, ESCAPE = 2 }, no_energy = true, on_pre_use = function(self, t, silent) local time_dilated = false if self:isTalentActive(self.T_TIME_DILATION) then - local t, p = self:getTalentFromId(self.T_TIME_DILATION), self:isTalentActive(self.T_TIME_DILATION) + local p = self:isTalentActive(self.T_TIME_DILATION) if p.charges == 3 then time_dilated = true end end - if not time_dilated then if not silent then game.logPlayer(self, "Time must be fully dilated in order to stop time.") end return false end return true + if not time_dilated then if not silent then game.logPlayer(self, "Time must be fully dilated in order to cast time stop.") end return false end return true end, - getReduction = function(self, t) return 80 - math.min(self:combatTalentSpellDamage(t, 0, 20, getParadoxSpellpower(self)), 30) end, + getReduction = function(self, t) return 80 - paradoxTalentScale(self, t, 0, 20, 40) end, + getDuration = function(self, t) return getExtensionModifier(self, t, 2) end, action = function(self, t) - self.energy.value = self.energy.value + 2000 - self:setEffect(self.EFF_TIME_STOP, 2, {power=t.getReduction(self, t)}) + self.energy.value = self.energy.value + (t.getDuration(self, t) * 1000) + self:setEffect(self.EFF_TIME_STOP, 1, {power=t.getReduction(self, t)}) game.logSeen(self, "#STEEL_BLUE#%s has stopped time!#LAST#", self.name:capitalize()) return true end, info = function(self, t) + local duration = t.getDuration(self, t) local reduction = t.getReduction(self, t) - return ([[Gain two turns. During this time your damage will be reduced by %d%%. + return ([[Gain %d turns. During this time your damage will be reduced by %d%%. Time must be fully dilated in order to use this talent. - The damage reduction penalty will be lessened by your Spellpower.]]):format(reduction) + The damage reduction penalty will be lessened by your Spellpower.]]):format(duration, reduction) end, } diff --git a/game/modules/tome/data/talents/chronomancy/spellbinding.lua b/game/modules/tome/data/talents/chronomancy/spellbinding.lua new file mode 100644 index 0000000000000000000000000000000000000000..1921cdc7c78bd1952ad84e4875037fb47e59223e --- /dev/null +++ b/game/modules/tome/data/talents/chronomancy/spellbinding.lua @@ -0,0 +1,156 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2014 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 + +-- EDGE TODO: Particles, Timed Effect Particles + +newTalent{ + name = "Empower", + type = {"chronomancy/spellbinding", 1}, + require = chrono_req_high1, + points = 5, + sustain_paradox = 24, + mode = "sustained", + no_sustain_autoreset = true, + cooldown = 10, + no_npc_use = true, + allow_temporal_clones = true, + getPower = function(self, t) return self:combatTalentLimit(t, 1, 0.1, 0.5) end, + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- temporal clones *CAN* learn spellbinding talents but can't change the value + activate = function(self, t) + local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyEmpower").new(self)) + if not talent then return nil end + + return { + talent = talent, rest_count = 0 + } + end, + deactivate = function(self, t, p) + return true + end, + info = function(self, t) + local power = t.getPower(self, t) * 100 + local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None" + return ([[Empowers the selected chronomancy spell, increasing spellpower when casting it by %d%%. + + Current Empowered Spell: %s]]): + format(power, talent) + end, +} + +newTalent{ + name = "Extension", + type = {"chronomancy/spellbinding", 1}, + require = chrono_req_high1, + points = 5, + sustain_paradox = 24, + mode = "sustained", + no_sustain_autoreset = true, + cooldown = 10, + no_npc_use = true, + allow_temporal_clones = true, + getPower = function(self, t) return self:combatTalentLimit(t, 0.5, 0.05, 0.25) end, + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- temporal clones *CAN* learn spellbinding talents but can't change the value + activate = function(self, t) + local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyExtension").new(self)) + if not talent then return nil end + + return { + talent = talent, rest_count = 0 + } + end, + deactivate = function(self, t, p) + return true + end, + info = function(self, t) + local power = t.getPower(self, t) * 100 + local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None" + return ([[Extends the duration of the selected chronomancy spell by %d%%. + + Current Extended Spell: %s]]): + format(power, talent) + end, +} + +newTalent{ + name = "Matrix", + type = {"chronomancy/spellbinding", 1}, + require = chrono_req_high1, + points = 5, + sustain_paradox = 24, + mode = "sustained", + no_sustain_autoreset = true, + cooldown = 10, + no_npc_use = true, + allow_temporal_clones = true, + getPower = function(self, t) return self:combatTalentLimit(t, 0.5, 0.05, 0.25) end, + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- temporal clones *CAN* learn spellbinding talents but can't change the value + activate = function(self, t) + local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyMatrix").new(self)) + if not talent then return nil end + + return { + talent = talent, rest_count = 0 + } + end, + deactivate = function(self, t, p) + return true + end, + info = function(self, t) + local power = t.getPower(self, t) * 100 + local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None" + return ([[Reduces the cooldown of the selected chronomancy spell by %d%%. + + Current Matrix Spell: %s]]): + format(power, talent) + end, +} + +newTalent{ + name = "Quicken", + type = {"chronomancy/spellbinding", 1}, + require = chrono_req_high1, + points = 5, + sustain_paradox = 24, + mode = "sustained", + no_sustain_autoreset = true, + cooldown = 10, + no_npc_use = true, -- so rares don't learn useless talents + allow_temporal_clones = true, -- let clones copy it anyway so they can benefit from the effects + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- but don't let them cast it + getPower = function(self, t) return self:combatTalentLimit(t, 1, 0.1, 0.5) end, + activate = function(self, t) + local talent = self:talentDialog(require("mod.dialogs.talents.ChronomancyQuicken").new(self)) + if not talent then return nil end + + return { + talent = talent, rest_count = 0 + } + end, + deactivate = function(self, t, p) + return true + end, + info = function(self, t) + local power = t.getPower(self, t) * 100 + local talent = self:isTalentActive(t.id) and self:getTalentFromId(self:isTalentActive(t.id).talent).name or "None" + return ([[Reduces the casting speed of the selected chronomancy spell by %d%%. + + Current Quickened Spell: %s]]): + format(power, talent) + end, +} diff --git a/game/modules/tome/data/talents/chronomancy/temporal-archery.lua b/game/modules/tome/data/talents/chronomancy/temporal-archery.lua index 777cf9335558e9f37ab4315b2502cad9971d8f0e..41dac39492c063bff0fe3fa15b622db83b63c225 100644 --- a/game/modules/tome/data/talents/chronomancy/temporal-archery.lua +++ b/game/modules/tome/data/talents/chronomancy/temporal-archery.lua @@ -80,7 +80,7 @@ newTalent{ cooldown = 10, tactical = { BUFF = 2 }, no_energy = true, - getPower = function(self, t) return 10 + self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self)) end, + getPower = function(self, t) return 10 + self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self, t)) end, activate = function(self, t) local power = t.getPower(self, t) return { diff --git a/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua b/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua index 7622814fe18cf7d58f6f8837c4244de08a5317b5..87d8930df7ca122992cbc1419f93a2d301280d74 100644 --- a/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua +++ b/game/modules/tome/data/talents/chronomancy/temporal-hounds.lua @@ -29,7 +29,7 @@ summonTemporalHound = function(self, t) end local m = require("mod.class.NPC").new{ - type = "animal", subtype = "hounds", + type = "animal", subtype = "canine", display = "C", image = "npc/summoner_wardog.png", color=colors.LIGHT_DARK, shader = "shadow_simulacrum", shader_args = { color = {0.6, 0.6, 0.2}, base = 0.8, time_factor = 1500 }, @@ -51,9 +51,10 @@ summonTemporalHound = function(self, t) infravision = 10, combat_armor = 2, combat_def = 4, - combat = { dam=self:getTalentLevel(t) * 10 + rng.avg(12,25), atk=10, apr=10, dammod={str=0.8}, damtype=DamageType.MATTER, sound="creatures/wolves/wolf_attack_1" }, + combat = { dam=self:getTalentLevel(t) * 10 + rng.avg(12,25), atk=10, apr=10, dammod={str=0.8, mag=0.8}, damtype=DamageType.WARP, sound="creatures/wolves/wolf_attack_1" }, summoner = self, summoner_gain_exp=true, + resolvers.sustains_at_birth(), } m:resolve() @@ -130,6 +131,7 @@ summonTemporalHound = function(self, t) }) end + self:attr("summoned_times", 1) end newTalent{ @@ -187,7 +189,7 @@ newTalent{ -- unsummon the hounds :( for _, e in pairs(game.level.entities) do - if e.summoner and e.summoner == self and e.subtype == "hounds" then + if e.summoner and e.summoner == self and e.name == "temporal hound" then e.summon_time = 0 end end @@ -217,7 +219,7 @@ newTalent{ requires_target = true, on_pre_use = function(self, t, silent) local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS) - if not p then + if not p or p.hounds < 1 then if not silent then game.logPlayer(self, "You must have temporal hounds to use this talent.") end @@ -230,7 +232,7 @@ newTalent{ end, direct_hit = true, getDefense = function(self, t) - return self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self)) + return self:combatTalentSpellDamage(t, 10, 40, getParadoxSpellpower(self, t)) end, action = function(self, t) -- Pick our target @@ -244,17 +246,19 @@ newTalent{ local __, x, y = self:canProject(tg, x, y) -- Summon a new Hound - local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS) - local talent = self:getTalentFromId(self.T_TEMPORAL_HOUNDS) - if p.hounds < p.max_hounds then - summonTemporalHound(self, talent) - p.hounds = p.hounds + 1 - end + if self:getTalentLevel(t) >=5 then + local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS) + local talent = self:getTalentFromId(self.T_TEMPORAL_HOUNDS) + if p.hounds < p.max_hounds then + summonTemporalHound(self, talent) + p.hounds = p.hounds + 1 + end + end -- Find our hounds local hnds = {} for _, e in pairs(game.level.entities) do - if e.summoner and e.summoner == self and e.subtype == "hounds" then + if e.summoner and e.summoner == self and e.name == "temporal hound" then hnds[#hnds+1] = e end end @@ -290,7 +294,7 @@ newTalent{ local defense = t.getDefense(self, t) return ([[Command your Temporal Hounds to teleport to the targeted location. If you target a creature with this effect your hounds will set that creature as their target. When you learn this talent, your hounds gain %d defense and %d%% resist all after any teleport. - If you're not at your maximum number of hounds when you cast this spell a new one will be summoned. + At talent level five, if you're not at your maximum number of hounds when you cast this spell a new one will be summoned. The teleportation bonuses scale with your Spellpower.]]):format(defense, defense, defense/2, defense/2) end, } @@ -304,9 +308,9 @@ newTalent{ getImmunities = function(self, t) return self:combatTalentLimit(t, 1, 0.15, 0.50) -- Limit <100% end, - getRegen = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self)) end, - getHaste = function(self, t) return self:combatScale(self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)), 0, 0, 0.57, 57, 0.75) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end, + getRegen = function(self, t) return self:combatTalentSpellDamage(t, 10, 50, getParadoxSpellpower(self, t)) end, + getHaste = function(self, t) return paradoxTalentScale(self, t, 20, 50, 80)/100 end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 2, 6))) end, doBlink = function(self, t, hound) -- Triggered when the hounds is hit local regen, haste = t.getRegen(self, t), t.getHaste(self, t) if hound:hasEffect(hound.EFF_UNRAVEL) then @@ -348,7 +352,7 @@ newTalent{ direct_hit = true, on_pre_use = function(self, t, silent) local p = self:isTalentActive(self.T_TEMPORAL_HOUNDS) - if not p then + if not p or p.hounds < 1 then if not silent then game.logPlayer(self, "You must have temporal hounds to use this talent.") end @@ -359,7 +363,7 @@ newTalent{ getResists = function(self, t) return self:combatTalentLimit(t, 100, 15, 50) -- Limit <100% end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self, t)) end, getDamageStat = function(self, t) return 2 + math.ceil(t.getDamage(self, t) / 15) end, target = function(self, t) return {type="cone", range=0, radius=self:getTalentRadius(t), selffire=false, talent=t} @@ -371,7 +375,7 @@ newTalent{ local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), true) for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do local a = game.level.map(x, y, Map.ACTOR) - if a and a.summoner == self and a.subtype == "hounds" then + if a and a.summoner == self and a.name == "temporal hound" then hounds[#hounds+1] = a tg[#tg+1] = {type="cone", range=0, radius=self:getTalentRadius(t), start_x=a.x, start_y=a.y, selffire=false, talent=t} end @@ -397,7 +401,7 @@ newTalent{ DamageType:get(DamageType.TEMPORAL).projector(a, px, py, DamageType.TEMPORAL, dam) -- Don't turn back the clock other hounds local target = game.level.map(px, py, Map.ACTOR) - if target and target.subtype ~= "hounds" then + if target and target.name ~= "temporal hound" then target:setEffect(target.EFF_TURN_BACK_THE_CLOCK, 3, {power=t.getDamageStat(self, t), apply_power=a:combatSpellpower(), min_dur=1}) end end) diff --git a/game/modules/tome/data/talents/chronomancy/threaded-combat.lua b/game/modules/tome/data/talents/chronomancy/threaded-combat.lua index 482b4bb921b0c97518832938e7a3b549f3d047a1..6501ddbac60aba6450f790710f89f49e26bbe51d 100644 --- a/game/modules/tome/data/talents/chronomancy/threaded-combat.lua +++ b/game/modules/tome/data/talents/chronomancy/threaded-combat.lua @@ -17,7 +17,7 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org --- EDGE TODO: Talents, Icons, Particles, Timed Effect Particles +-- EDGE TODO: Particles, Timed Effect Particles newTalent{ name = "Frayed Threads", @@ -25,12 +25,12 @@ newTalent{ require = chrono_req_high1, mode = "passive", points = 5, - getPercent = function(self, t) return math.min(100, self:combatTalentSpellDamage(t, 20, 80, getParadoxSpellpower(self)))/100 end, + getPercent = function(self, t) return paradoxTalentScale(self, t, 40, 80, 100)/100 end, getRadius = function(self, t) return self:getTalentLevel(t) > 4 and 2 or 1 end, info = function(self, t) local percent = t.getPercent(self, t) * 100 local radius = t.getRadius(self, t) - return ([[Your Weapon Folding and Impact spells now deal %d%% of their damage in a radius of %d. + return ([[Your Weapon Folding and Impact spells now deal an additional %d%% damage in a radius of %d. The damage percent will scale with your Spellpower.]]) :format(percent, radius) end @@ -46,7 +46,7 @@ newTalent{ tactical = { ATTACKAREA = { weapon = 3 } , DISABLE = 3 }, requires_target = true, range = archery_range, - no_energy = "fake", + speed = function(self, t) return self:hasArcheryWeapon("bow") and "archery" or "weapon" end, getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.2, 1.9) end, getCooldown = function(self, t) return self:getTalentLevel(t) >= 5 and 2 or 1 end, on_pre_use = function(self, t, silent) if self:attr("disarmed") then if not silent then game.logPlayer(self, "You require a weapon to use this talent.") end return false end return true end, @@ -62,7 +62,7 @@ newTalent{ for tid, cd in pairs(self.talents_cd) do local tt = self:getTalentFromId(tid) if tt.type[1]:find("^chronomancy/blade") then - self.talents_cd[tid] = cd - t.getCooldown(self, t) + self:alterTalentCoolingdown(tt, - t.getCooldown(self, t)) end end end, @@ -73,7 +73,7 @@ newTalent{ if self:hasArcheryWeapon("bow") then -- Ranged attack - local targets = self:archeryAcquireTargets(tg, {one_shot=true}) + local targets = self:archeryAcquireTargets(tg, {one_shot=true, no_energy = true}) if not targets then return end self:archeryShoot(targets, t, tg, {mult=dam}) elseif mainhand then @@ -81,13 +81,13 @@ newTalent{ self:project(tg, self.x, self.y, function(px, py, tg, self) local target = game.level.map(px, py, Map.ACTOR) if target and target ~= self then - local hit = self:attackTarget(target, nil, dam) + local hit = self:attackTarget(target, nil, dam, true) -- Refresh bow talents if hit then for tid, cd in pairs(self.talents_cd) do local tt = self:getTalentFromId(tid) if tt.type[1]:find("^chronomancy/bow") then - self.talents_cd[tid] = cd - t.getCooldown(self, t) + self:alterTalentCoolingdown(tt, - t.getCooldown(self, t)) end end end @@ -118,7 +118,7 @@ newTalent{ require = chrono_req_high3, mode = "passive", points = 5, - getPercent = function(self, t) return math.min(50, 10 + self:combatTalentSpellDamage(t, 0, 30, getParadoxSpellpower(self)))/100 end, + getPercent = function(self, t) return paradoxTalentScale(self, t, 10, 30, 50)/100 end, info = function(self, t) local percent = t.getPercent(self, t) * 100 return ([[Your Bow Threading and Blade Threading spells now deal %d%% more weapon damage if you did not have the appropriate weapon equipped when you initated the attack. @@ -136,8 +136,8 @@ newTalent{ cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 15, 45, 25)) end, -- Limit >15 tactical = { ATTACK = {weapon = 4} }, range = 10, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 12)) end, - getDamagePenalty = function(self, t) return 60 - math.min(self:combatTalentSpellDamage(t, 0, 20, getParadoxSpellpower(self)), 30) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 6, 12))) end, + getDamagePenalty = function(self, t) return 60 - paradoxTalentScale(self, t, 0, 20, 30) end, requires_target = true, target = function(self, t) return {type="hit", range=self:getTalentRange(t)} @@ -168,7 +168,6 @@ newTalent{ local tids = {} for tid, _ in pairs(m.talents) do local t = m:getTalentFromId(tid) - if t.remove_on_clone then tids[#tids+1] = t end local tt = self:getTalentFromId(tid) if not tt.type[1]:find("^chronomancy/blade") and not tt.type[1]:find("^chronomancy/threaded") and not tt.type[1]:find("^chronomancy/guardian") then tids[#tids+1] = t @@ -181,11 +180,12 @@ newTalent{ m.ai_state = { talent_in=2, ally_compassion=10 } m.generic_damage_penalty = t.getDamagePenalty(self, t) - m:setTarget(target or nil) m.remove_from_party_on_death = true game.zone:addEntity(game.level, m, "actor", tx, ty) + m:setTarget(target or nil) + if game.party:hasMember(self) then game.party:addMember(m, { control="no", @@ -234,7 +234,6 @@ newTalent{ local tids = {} for tid, _ in pairs(m.talents) do local t = m:getTalentFromId(tid) - if t.remove_on_clone then tids[#tids+1] = t end local tt = self:getTalentFromId(tid) if not tt.type[1]:find("^chronomancy/bow") and not tt.type[1]:find("^chronomancy/threaded") and not tt.type[1]:find("^chronomancy/guardian") and not t.innate then tids[#tids+1] = t @@ -248,11 +247,12 @@ newTalent{ m.ai_state = { talent_in=2, ally_compassion=10 } m.generic_damage_penalty = t.getDamagePenalty(self, t) m:attr("archery_pass_friendly", 1) - m:setTarget(target or nil) m.remove_from_party_on_death = true game.zone:addEntity(game.level, m, "actor", tx, ty) + m:setTarget(target or nil) + if game.party:hasMember(self) then game.party:addMember(m, { control="no", @@ -283,6 +283,6 @@ newTalent{ local damage_penalty = t.getDamagePenalty(self, t) return ([[Summons a blade warden and a bow warden from an alternate timeline for %d turns. The wardens are out of phase with this reality and deal %d%% less damage but the bow warden's arrows will pass through friendly targets. Each warden knows all Threaded Combat, Temporal Guardian, and Blade Threading or Bow Threading spells you know. - The damage reduction penalty will be lessened by your Spellpower.]]):format(duration, damage_penalty) + The damage penalty will be lessened by your Spellpower.]]):format(duration, damage_penalty) end, } diff --git a/game/modules/tome/data/talents/chronomancy/timeline-threading.lua b/game/modules/tome/data/talents/chronomancy/timeline-threading.lua index a2d943c2fa4747b54a6d72bfb659e404c09ef317..609beb7d1586925fc8c5243f716557a6aaa05e3e 100644 --- a/game/modules/tome/data/talents/chronomancy/timeline-threading.lua +++ b/game/modules/tome/data/talents/chronomancy/timeline-threading.lua @@ -17,157 +17,453 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org +-- EDGE TODO: Particles, Timed Effect Particles + newTalent{ - name = "Gather the Threads", + name = "Rethread", type = {"chronomancy/timeline-threading", 1}, - require = chrono_req_high1, + require = chrono_req1, points = 5, + cooldown = 4, paradox = function (self, t) return getParadoxCost(self, t, 10) end, - cooldown = 12, - tactical = { BUFF = 2 }, - getThread = function(self, t) return self:combatTalentScale(t, 7, 30, 0.75) end, - getReduction = function(self, t) return self:combatTalentScale(t, 3.6, 15, 0.75) end, + tactical = { ATTACKAREA = {TEMPORAL = 2}, PARADOX = 2 }, + range = 10, + direct_hit = true, + reflectable = true, + requires_target = true, + target = function (self, t) + return {type="bolt", range=self:getTalentRange(t), talent=t} + end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 230, getParadoxSpellpower(self, t)) end, + getTargetCount = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6, "log")) end, action = function(self, t) - self:setEffect(self.EFF_GATHER_THE_THREADS, 5, {power=t.getThread(self, t), reduction=t.getReduction(self, t)}) - game:playSoundNear(self, "talents/spell_generic2") + local tg = self:getTalentTarget(t) + local fx, fy = self:getTarget(tg) + if not fx or not fy then return nil end + + local nb = t.getTargetCount(self, t) + local affected = {} + local braid_targets = {} + local first = nil + + self:project(tg, fx, fy, function(dx, dy) + local actor = game.level.map(dx, dy, Map.ACTOR) + if actor and not affected[actor] then + affected[actor] = true + first = actor + + self:project({type="ball", selffire=false, x=dx, y=dy, radius=10, range=0}, dx, dy, function(bx, by) + local actor = game.level.map(bx, by, Map.ACTOR) + if actor and not affected[actor] and self:reactionToward(actor) < 0 then + affected[actor] = true + end + end) + return true + end + end) + + if not first then return end + local targets = { first } + affected[first] = nil + local possible_targets = table.listify(affected) + + for i = 2, nb do + if #possible_targets == 0 then break end + local act = rng.tableRemove(possible_targets) + targets[#targets+1] = act[1] + end + + local sx, sy = self.x, self.y + local damage = self:spellCrit(t.getDamage(self, t)) + for i, actor in ipairs(targets) do + local tgr = {type="beam", range=self:getTalentRange(t), selffire=false, talent=t, x=sx, y=sy} + self:project(tgr, actor.x, actor.y, function(px, py) + DamageType:get(DamageType.TEMPORAL).projector(self, px, py, DamageType.TEMPORAL, damage) + + -- Get our braid targets + local target = game.level.map(px, py, Map.ACTOR) + if target and not target.dead and self:knowTalent(self.T_BRAID_LIFELINES) then + braid_targets[#braid_targets+1] = target + end + end) + + game.level.map:particleEmitter(sx, sy, math.max(math.abs(actor.x-sx), math.abs(actor.y-sy)), "temporalbeam", {tx=actor.x-sx, ty=actor.y-sy}) + sx, sy = actor.x, actor.y + end + + -- Braid them + if #braid_targets > 1 then + for i = 1, #braid_targets do + local target = braid_targets[i] + local t = self:getTalentFromId(self.T_BRAID_LIFELINES) + target:setEffect(target.EFF_BRAIDED, t.getDuration(self, t), {power=t.getBraid(self, t), src=self, targets=braid_targets}) + end + end + + game:playSoundNear(self, "talents/heal") return true end, info = function(self, t) - local primary = t.getThread(self, t) - local reduction = t.getReduction(self, t) - return ([[You begin to gather energy from other timelines. Your Spellpower will increase by %0.2f on the first turn and %0.2f more each additional turn. - The effect ends either when you cast a spell, or after five turns. - Eacn turn the effect is active, your Paradox will be reduced by %d. - This spell will not break Spacetime Tuning, nor will it be broken by activating Spacetime Tuning.]]):format(primary + (primary/5), primary/5, reduction) + local damage = t.getDamage(self, t) + local targets = t.getTargetCount(self, t) + return ([[Rethread the timeline, dealing %0.2f temporal damage to the target before moving on to a second target. + Rethread can hit up to %d targets up to 10 grids apart, and will never hit the same one twice; nor will it hit the caster. + The damage will increase with your Spellpower.]]): + format(damDesc(self, DamageType.TEMPORAL, damage), targets) end, } newTalent{ - name = "Rethread", + name = "Temporal Fugue", type = {"chronomancy/timeline-threading", 2}, - require = chrono_req_high2, + require = chrono_req2, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 10) end, - cooldown = 4, - tactical = { ATTACK = {TEMPORAL = 2} }, - range = 10, - direct_hit = true, - reflectable = true, - requires_target = true, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200, getParadoxSpellpower(self)) end, - getReduction = function(self, t) return self:combatTalentScale(t, 1.2, 5, 0.75) end, + cooldown = 24, + paradox = function(self, t) return getParadoxCost(self, t, 20) end, + tactical = { ATTACK = 2, DISABLE = 2 }, + remove_on_clone = true, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 8))) end, + on_pre_use = function(self, t, silent) if self:hasEffect(self.EFF_TEMPORAL_FUGUE) then return false end return true end, action = function(self, t) - local tg = {type="beam", range=self:getTalentRange(t), talent=t} - local x, y = self:getTarget(tg) - if not x or not y then return nil end - local _ _, x, y = self:canProject(tg, x, y, t.paradox) - self:project(tg, x, y, DamageType.RETHREAD, {dam=self:spellCrit(t.getDamage(self, t)), reduction = t.getReduction(self, t)}) - game.level.map:particleEmitter(self.x, self.y, tg.radius, "temporalbeam", {tx=x-self.x, ty=y-self.y}) - game:playSoundNear(self, "talents/heal") + local clones = {self} + + -- Clone the caster + local function makeFugueClone(self, t) + local sex = game.player.female and "she" or "he" + local m = makeParadoxClone(self, self, t.getDuration(self, t)) + -- Add and change some values + m.name = self.name + m.desc = [[The real you... or so ]]..sex..[[ says.]] + m.shader = nil + m.shader_args = nil + m.faction = self.faction + m.summoner = self + m.remove_from_party_on_death = true + + -- Handle some AI stuff + m.ai_state = { talent_in=1, ally_compassion=10 } + m.ai_tactic = { closein=0, defend=4, disable=4, escape=4, heal=2, safe_range=3} + + return m + end + + -- Add our clones + for i = 1, 2 do + local tx, ty = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true}) + if tx and ty then + -- Make our clone and add to the party + local m = makeFugueClone(self, t) + if game.party:hasMember(self) then + game.party:addMember(m, { + control="full", + type="fugue clone", + title="Fugue Clone", + orders = {target=true}, + }) + end + + -- and the level + game.zone:addEntity(game.level, m, "actor", tx, ty) + game.level.map:particleEmitter(m.x, m.y, 1, "temporal_teleport") + clones[#clones+1] = m + end + end + + -- No clones? Exit the spell + if #clones < 2 then + game.logPlayer(self, "Not enough space to summon!") + return + end + + -- Link them + for i = 1, #clones do + local target = clones[i] + target:setEffect(target.EFF_TEMPORAL_FUGUE, 10, {targets=clones}) + end + + game:playSoundNear(self, "talents/spell_generic") + return true end, info = function(self, t) - local damage = t.getDamage(self, t) - local reduction = t.getReduction(self, t) - return ([[Creates a wake of temporal energy that deals %0.2f damage in a beam, as you attempt to rethread the timeline. Affected targets may be stunned, blinded, pinned, or confused for 3 turns. - Each target you hit with Rethread will reduce your Paradox by %0.1f. - The damage will increase with your Spellpower.]]): - format(damDesc(self, DamageType.TEMPORAL, damage), reduction) + local duration = t.getDuration(self, t) + return ([[For the next %d turns two alternate versions of you enter your timeline. While the effect is active all damage done by you or your copies is reduced by two thirds and all damage recieved is split between the three of you. + Temporal Fugue does not normally cooldown while active. You may take direct control of your clones.]]): + format(duration) end, } newTalent{ - name = "Temporal Clone", + name = "Braid Lifelines", type = {"chronomancy/timeline-threading", 3}, - require = chrono_req_high3, + require = chrono_req3, + mode = "passive", points = 5, - cooldown = 30, - paradox = function (self, t) return getParadoxCost(self, t, 30) end, - tactical = { ATTACK = 2, DISABLE = 2 }, + getBraid = function(self, t) return self:combatTalentSpellDamage(t, 25, 40, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 3, 7))) end, + info = function(self, t) + local braid = t.getBraid(self, t) + local duration = t.getDuration(self, t) + return ([[Your Rethread now braids the lifelines of all targets it hits for %d turns. Braided targets take %d%% of all damage dealt to other braided targets. + The amount of damage shared will scale with your Spellpower.]]) + :format(duration, braid) + end +} + +newTalent{ + name = "Cease to Exist", + type = {"chronomancy/timeline-threading", 4}, + require = chrono_req4, + points = 5, + cooldown = 24, + paradox = function (self, t) return getParadoxCost(self, t, 25) end, + range = 10, + tactical = { ATTACK = 2 }, requires_target = true, - range = 6, - no_npc_use = true, - getDuration = function(self, t) -- limit < cooldown (30) - return math.floor(self:combatTalentLimit(self:getTalentLevel(t), t.cooldown, 4, 8)) + direct_hit = true, + no_npc_use = true, -- so rares don't learn useless talents + allow_temporal_clones = true, -- let clones copy it anyway so they can benefit from the effects + on_pre_use = function(self, t, silent) if self ~= game.player then return false end return true end, -- but don't let them cast it + getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(t), 5, 9)) end, + getPower = function(self, t) return paradoxTalentScale(self, t, 20, 50, 100) end, + target = function(self, t) + return {type="hit", range=self:getTalentRange(t), talent=t} + end, + on_pre_use = function(self, t, silent) + if checkTimeline(self) then + if not silent then + game.logPlayer(self, "The timeline is too fractured to do this now.") + end + return false + end + return true + end, + do_instakill = function(self, t) + -- search for target because it's ID will change when the chrono restore takes place + local tg = false + local grids = core.fov.circle_grids(self.x, self.y, 10, true) + for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do + local a = game.level.map(x, y, Map.ACTOR) + if a and a:hasEffect(a.EFF_CEASE_TO_EXIST) then + tg = a + end + end end + + if tg then + game:onTickEnd(function() + tg:removeEffect(tg.EFF_CEASE_TO_EXIST) + game.logSeen(tg, "#LIGHT_BLUE#%s never existed, this never happened!", tg.name:capitalize()) + tg:die(self) + end) + end end, - getSize = function(self, t) return 2 + math.ceil(self:getTalentLevelRaw(t) / 2 ) end, action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t), talent=t} - local tx, ty, target = self:getTarget(tg) - if not tx or not ty then return nil end - local _ _, _, _, tx, ty = self:canProject(tg, tx, ty) + -- get our target + local tg = self:getTalentTarget(t) + local tx, ty = self:getTarget(tg) if not tx or not ty then return nil end + local _ _, tx, ty = self:canProject(tg, tx, ty) + local target = game.level.map(tx, ty, Map.ACTOR) - if not target or self:reactionToward(target) >= 0 then return end + if not target then return end - -- Find space - local x, y = util.findFreeGrid(tx, ty, 1, true, {[Map.ACTOR]=true}) - if not x then - game.logPlayer(self, "Not enough space to summon!") - return + if target == self then + game.logSeen(self, "#LIGHT_STEEL_BLUE#%s tries to remove %sself from existance!", self.name, string.his_her(self)) + self:incParadox(400) + game.level.map:particleEmitter(self.x, self.y, 1, "ball_temporal", {radius=1, tx=self.x, ty=self.y}) + return true end - - local allowed = t.getSize(self, t) - - if target.rank >= 3.5 or -- No boss - target:reactionToward(self) >= 0 or -- No friends - target.size_category > allowed - then + + -- does the spell hit? if not nothing happens + if not self:checkHit(getParadoxSpellpower(self, t), target:combatSpellResist()) then game.logSeen(target, "%s resists!", target.name:capitalize()) return true end + + -- Manualy start cooldown before the chronoworld is made + game.player:startTalentCooldown(t) + + -- set up chronoworld next, we'll load it when the target dies in class\actor + game:onTickEnd(function() + game:chronoClone("cease_to_exist") + end) + + -- Set our effect + target:setEffect(target.EFF_CEASE_TO_EXIST, t.getDuration(self,t), {src=self, power=t.getPower(self,t)}) + + return true + end, + info = function(self, t) + local duration = t.getDuration(self, t) + local power = t.getPower(self, t) + return ([[Over the next %d turns, you attempt to remove the target from the timeline, lowering all its resistance by %d%%. + If you manage to kill the target while the spell is in effect, you'll be returned to the point in time you cast this spell and the target will be slain. + This spell splits the timeline. Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful. + The resistance penalty will scale with your Spellpower.]]) + :format(duration, power) + end, +} - local m = target:cloneFull{ - no_drops = true, - faction = self.faction, - summoner = self, summoner_gain_exp=true, - exp_worth = 0, -- bug fix - summon_time = t.getDuration(self, t), - ai_target = {actor=target}, - ai = "summoned", ai_real = target.ai, - } - m:removeAllMOs() - m.make_escort = nil - m.on_added_to_level = nil - - m.energy.value = 0 - m.life = m.life - m.forceLevelup = function() end - -- Handle special things - m.on_die = nil - m.on_acquire_target = nil - m.seen_by = nil - m.can_talk = nil - m.clone_on_hit = nil - if m.talents.T_SUMMON then m.talents.T_SUMMON = nil end - if m.talents.T_MULTIPLY then m.talents.T_MULTIPLY = nil end - +-- This was a really cool affect but the game really assumes that the player is on the map +-- I suspect there's a lot of unforseen bugs in this code but I'm leaving it here incase someone else wants to attempt to do something similar in the future +--[=[newTalent{ + name = "Temporal Fugue", + type = {"chronomancy/timeline-threading", 2}, + require = chrono_req2, + points = 5, + cooldown = 12, + paradox = function (self, t) return getParadoxCost(self, t, 15) end, + tactical = { DISABLE = 3 }, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 4, 8))) end, + getDamagePenalty = function(self, t) return 80 - paradoxTalentScale(self, t, 0, 20, 40) end, + getClones = function(self, t) return 3 end, + on_pre_use = function(self, t, silent) if self ~= game.player and self.fugue_clones then return false end return true end, + action = function(self, t) + -- If the spell is active cancel the effect + if self.fugue_clones then + for _, e in pairs(game.level.entities) do + if e.summoner and e.summoner == self.summoner and e.fugue_clones and e ~= self then + e:die() + end + end + return true + end + + -- Remove the player + local x, y = self.x, self.y + game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport") + game.level:removeEntity(self, true) + + -- Setup our Fugue Clones + local fugue_count = 0 + local function makeFugueClone(self, t, x, y) + local m = makeParadoxClone(self, self, t.getDuration(self, t)) + + -- Flavor :) + local sex = self.female and "she" or "he" + m.name = self.name + m.desc = [[The real ]]..self.name:capitalize()..[[... or so ]]..sex..[[ says.]] + m.shader = nil + m.shader_args = nil + + -- Add and change some values + m.generic_damage_penalty = t.getDamagePenalty(self, t) + m.max_life = m.max_life * (100 - t.getDamagePenalty(self, t))/100 + m.life = m.max_life + m.remove_from_party_on_death = true + m.timetravel_immune = 1 + + -- Handle some AI stuff + m.ai_state = { talent_in=2, ally_compassion=10 } + + -- Track clones when we die + m.on_die = function(self) + -- If we're the last return the caster + if self.fugue_clones <= 1 then + -- don't level up on return + local old_levelup = self.summoner.forceLevelup + local old_check = self.summoner.check + self.summoner.forceLevelup = function() end + self.summoner.check = function() end + game.zone:addEntity(game.level, self.summoner, "actor", self.x, self.y) + self.summoner.forceLevelup = old_levelup + self.summoner.check = old_check + if game.party:hasMember(self) then + game.party:setPlayer(self.summoner) + end + else + -- find fellow clones and update the clone count + for _, e in pairs(game.level.entities) do + if e.summoner and e.summoner == self.summoner and e.fugue_clones then + e.fugue_clones = e.fugue_clones - 1 + if e.fugue_clones <= 1 then + e.summon_time = 0 + end + end + end + end + local add_paradox = math.max(0, self:getParadox() - self.summoner:getParadox()) + self.summoner:incParadox(add_paradox/3) + end + + return m + end + + -- Add the primary clone to the game + local m = makeFugueClone(self, t, x, y) + if game.party:hasMember(self) then + game.party:addMember(m, { + control="full", + type="fugue clone", + title="Fugue Clone", + orders = {target=true}, + }) + end game.zone:addEntity(game.level, m, "actor", x, y) - game.level.map:particleEmitter(x, y, 1, "temporal_teleport") - - -- force target to attack double - local a = game.level.map(tx, ty, Map.ACTOR) - if a and self:reactionToward(a) < 0 then - a:setTarget(m) + local fugue_count = 1 + + -- Swap control to the primary clone + if game.party:hasMember(self) then + game.party:setPlayer(m) + m.summon_time = m.summon_time + 1 end + + m:resetCanSeeCache() + + -- Make the rest of our clones + for i = 1, t.getClones(self, t)-1 do + local m = makeFugueClone(self, t, x, y) + + local poss = {} + local range = t.getClones(self, t) + for i = x - range, x + range do + for j = y - range, y + range do + if game.level.map:isBound(i, j) and + core.fov.distance(x, y, i, j) <= range and -- make sure they're within range + self:canMove(i, j) and self:hasLOS(i, j) then -- keep them in line of sight + poss[#poss+1] = {i,j} + end + end + end + if #poss == 0 then break end + local pos = poss[rng.range(1, #poss)] + tx, ty = pos[1], pos[2] - game:playSoundNear(self, "talents/spell_generic") + -- And add to the party + if game.party:hasMember(self) then + game.party:addMember(m, { + control="full", + type="fugue clone", + title="Fugue Clone", + orders = {target=true}, + }) + end + + -- Add us to the level + game.zone:addEntity(game.level, m, "actor", tx, ty) + game.level.map:particleEmitter(m.x, m.y, 1, "temporal_teleport") + fugue_count = fugue_count + 1 + end + + -- Add the fugue counter + for _, e in pairs(game.level.entities) do + if e.summoner and e.summoner == self then + e.fugue_clones = fugue_count + end + end + + game.level.map:particleEmitter(m.x, m.y, 1, "temporal_teleport") + game:playSoundNear(m, "talents/teleport") + return true end, info = function(self, t) local duration = t.getDuration(self, t) - local allowed = t.getSize(self, t) - local size = "gargantuan" - if allowed < 4 then - size = "medium" - elseif allowed < 5 then - size = "big" - elseif allowed < 6 then - size = "huge" - end - return ([[Pulls a %s size or smaller copy of the target from another timeline; the copy stays for %d turns. The copy and the target will be compelled to attack each other immediately.]]): - format(size, duration) + local damage_penalty = t.getDamagePenalty(self, t) + return ([[Split yourself into three fugue clones. The clones have %d%% of your maximum life, deal %d%% less damage, and last %d turns. + Each clone that dies will increase your Paradox by 33%% of the difference between its Paradox and your own. This will never reduce your Paradox. + When only one clone is left, or if you cast the spell while in the fugue state, the spell will end, returning you to the position of the last active clone. + The life and damage penalties will be reduced by your Spellpower.]]): + format(100 - damage_penalty, damage_penalty, duration) end, -} - - +}]=]-- \ No newline at end of file diff --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua index e416f07b35642eed324e9a017433a67b7cc6f37b..1ef5382c390ef80dd5434336bf180530fced82f1 100644 --- a/game/modules/tome/data/talents/chronomancy/timetravel.lua +++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua @@ -17,101 +17,147 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org --- EDGE TODO: Icons, Particles, Timed Effect Particles +-- EDGE TODO:Particles, Timed Effect Particles newTalent{ - name = "Time Skip", + name = "Temporal Bolt", type = {"chronomancy/timetravel",1}, - require = chrono_req_high1, + require = chrono_req1, points = 5, - cooldown = 4, + cooldown = 3, + fixed_cooldown = true, paradox = function (self, t) return getParadoxCost(self, t, 10) end, - tactical = { ATTACK = {TEMPORAL = 1}, DISABLE = 2 }, + tactical = { ATTACK = {TEMPORAL = 2}, DISABLE = 1 }, range = 10, + reflectable = true, + proj_speed = 3, direct_hit = true, requires_target = true, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 15, 150, getParadoxSpellpower(self, t)) end, target = function(self, t) - return {type="hit", range=self:getTalentRange(t), talent=t} + return {type="hit", range=self:getTalentRange(t), friendlyfire=false, talent=t} end, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 250, getParadoxSpellpower(self)) end, - getDuration = function(self, t) return 2 + math.ceil(self:combatTalentScale(self:getTalentLevel(t), 0.3, 2.3)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - local _ _, x, y = self:canProject(tg, x, y) - local target = game.level.map(x, y, Map.ACTOR) - if not target then return end + + local dam = self:spellCrit(t.getDamage(self, t)) + + -- Hit our initial target; quality of life hack + self:project(tg, x, y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if not target then return end + -- Refresh talent + for tid, cd in pairs(self.talents_cd) do + local tt = self:getTalentFromId(tid) + if tt.type[1]:find("^chronomancy/") and not tt.fixed_cooldown then + self:alterTalentCoolingdown(tt, - 1) + end + end + DamageType:get(DamageType.TEMPORAL).projector(self, x, y, DamageType.TEMPORAL, dam) + end) + + -- Make our homing missile + self:project(tg, x, y, function(x, y) + local proj = require("mod.class.Projectile"):makeHoming( + self, + {particle="arrow", particle_args={tile="particles_images/temporal_bolt", proj_x=self.x, proj_y=self.y, src_x=x, src_y=y}, trail="trail_paradox"}, + {speed=3, name="Temporal Bolt", dam=dam, apply=getParadoxSpellpower(self, t), start_x=x, start_y=y}, + self, self:getTalentRange(t), + function(self, src) + local talent = src:getTalentFromId 'T_TEMPORAL_BOLT' + local target = game.level.map(self.x, self.y, engine.Map.ACTOR) + if target and not target.dead and src:reactionToward(target) < 0 then + local DT = require("engine.DamageType") + local multi = (10 - self.homing.count)/20 + local dam = self.def.dam * (1 + multi) + src:project({type="hit", friendlyfire=false, talent=talent}, self.x, self.y, DT.TEMPORAL, dam) - if target:attr("timetravel_immune") then - game.logSeen(target, "%s is immune!", target.name:capitalize()) - return - end + -- Refresh talent + for tid, cd in pairs(src.talents_cd) do + local tt = src:getTalentFromId(tid) + if tt.type[1]:find("^chronomancy/") and not tt.fixed_cooldown then + src:alterTalentCoolingdown(tt, - 1) + end + end + end + end, + function(self, src) + end + ) + game.zone:addEntity(game.level, proj, "projectile", x, y) + end) + + game:playSoundNear({x=x, y=y}, "talents/distortion") - local power = self:combatSpellpower() * (1 + self:callTalent(self.T_SPACETIME_MASTERY, "getPower")) - local hit = self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) - if not hit then game.logSeen(target, "%s resists!", target.name:capitalize()) return true end - self:project(tg, x, y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) - game.level.map:particleEmitter(x, y, 1, "temporal_thrust") - game:playSoundNear(self, "talents/arcane") - if target.dead or target.player then return true end - target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)}) + return true + end, + info = function(self, t) + local damage = t.getDamage(self, t) + return ([[Pull a bolt of temporal energy back through time. The bolt will home in on your location, dealing %0.2f temporal damage to enemies, and reducing the cooldown of one chronomancy talent on cooldown by one turn per enemy hit. + The bolt gains 5%% damage each time it moves and the damage will scale with your Spellpower.]]): + format(damDesc(self, DamageType.TEMPORAL, damage)) + end, +} + +newTalent{ + name = "Echoes From The Past", + type = {"chronomancy/timetravel", 2}, + require = chrono_req2, + points = 5, + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 10, + tactical = { ATTACKAREA = {TEMPORAL = 2} }, + range = 0, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end, + target = function(self, t) + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} + end, + direct_hit = true, + requires_target = true, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 18, 160, getParadoxSpellpower(self, t)) end, + getPercent = function(self, t) return paradoxTalentScale(self, t, 20, 40, 60)/100 end, + action = function(self, t) + local tg = self:getTalentTarget(t) - -- Replace the target with a temporal instability for a few turns - local oe = game.level.map(target.x, target.y, engine.Map.TERRAIN) - if not oe or oe:attr("temporary") then return true end - local e = mod.class.Object.new{ - old_feat = oe, type = oe.type, subtype = oe.subtype, - name = "temporal instability", image = oe.image, add_mos = {{image="object/temporal_instability.png"}}, - display = '&', color=colors.LIGHT_BLUE, - temporary = t.getDuration(self, t), - canAct = false, - target = target, - act = function(self) - self:useEnergy() - self.temporary = self.temporary - 1 - -- return the rifted actor - if self.temporary <= 0 then - game.level.map(self.target.x, self.target.y, engine.Map.TERRAIN, self.old_feat) - game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y) - game.level:removeEntity(self) - local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true}) - local old_levelup = self.target.forceLevelup - self.target.forceLevelup = function() end - game.zone:addEntity(game.level, self.target, "actor", mx, my) - self.target.forceLevelup = old_levelup - end - end, - summoner_gain_exp = true, summoner = self, - } + local damage = self:spellCrit(t.getDamage(self, t)) + self:project(tg, self.x, self.y, function(px, py) + DamageType:get(DamageType.TEMPORAL).projector(self, px, py, DamageType.TEMPORAL, damage) + + -- Echo + local target = game.level.map(px, py, Map.ACTOR) + if not target then return end + local percent = t.getPercent(self, t)/target.rank + local dam = (target.max_life - target.life) * percent + DamageType:get(DamageType.TEMPORAL).projector(self, px, py, DamageType.TEMPORAL, dam) + end) - game.logSeen(target, "%s has moved forward in time!", target.name:capitalize()) - game.level:removeEntity(target, true) - game.level:addEntity(e) - game.level.map(x, y, Map.TERRAIN, e) - game.nicer_tiles:updateAround(game.level, x, y) - game.level.map:updateMap(x, y) + game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_temporal", {radius=tg.radius}) + game:playSoundNear(self, "talents/teleport") return true end, info = function(self, t) + local percent = t.getPercent(self, t) * 100 + local radius = self:getTalentRadius(t) local damage = t.getDamage(self, t) - local duration = t.getDuration(self, t) - return ([[Inflicts %0.2f temporal damage, if the target fails a spell save. If your target survives, it will be removed from time for %d turns. - The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration) + return ([[Creates a temporal echo in a radius of %d around you. Affected targets will take %0.2f temporal damage, as well as up to %d%% of the difference between their current life and max life as additional temporal damage. + The additional damage will be divided by the target's rank and the damage scales with your Spellpower.]]): + format(radius, damDesc(self, DamageType.TEMPORAL, damage), percent) end, } newTalent{ name = "Temporal Reprieve", - type = {"chronomancy/timetravel", 2}, - require = chrono_req_high2, + type = {"chronomancy/timetravel", 3}, + require = chrono_req3, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 36) end, - cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 10, 45, 25)) end, -- Limit >10 + paradox = function (self, t) return getParadoxCost(self, t, 20) end, + cooldown = 40, no_npc_use = true, - on_pre_use = function(self, t) return self:canBe("planechange") end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end, fixed_cooldown = true, + on_pre_use = function(self, t) return self:canBe("planechange") end, + getDuration = function(self, t) return getExtensionModifier(self, t, math.floor(self:combatTalentScale(t, 2, 6))) end, action = function(self, t) if game.zone.is_temporal_reprieve then game.logPlayer(self, "This talent cannot be used from within the reprieve.") @@ -157,7 +203,7 @@ newTalent{ self.temporal_reprieve_on_die = self.on_die self.on_die = function(self, ...) - self:removeEffect(self.EFF_DREAMSCAPE) + self:removeEffect(self.EFF_TEMPORAL_REPRIEVE) local args = {...} game:onTickEnd(function() if self.temporal_reprieve_on_die then self:temporal_reprieve_on_die(unpack(args)) end @@ -182,175 +228,96 @@ newTalent{ } newTalent{ - name = "Echoes From The Past", - type = {"chronomancy/timetravel", 3}, - require = chrono_req_high3, + name = "Time Skip", + type = {"chronomancy/timetravel",4}, + require = chrono_req4, points = 5, - paradox = function (self, t) return getParadoxCost(self, t, 20) end, - cooldown = 10, - tactical = { ATTACKAREA = {TEMPORAL = 2} }, - range = 0, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end, + cooldown = 4, + paradox = function (self, t) return getParadoxCost(self, t, 10) end, + tactical = { ATTACK = {TEMPORAL = 1}, DISABLE = 2 }, + range = 10, + direct_hit = true, + requires_target = true, target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} + return {type="hit", range=self:getTalentRange(t), talent=t} end, - direct_hit = true, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 18, 160, getParadoxSpellpower(self)) end, - getPercent = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 1, 10, getParadoxSpellpower(self)), 0.5, .1, 0, 0.1575, 5.75) end, -- Limit to <50% + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 25, 250, getParadoxSpellpower(self, t)) end, + getDuration = function(self, t) return getExtensionModifier(self, t, 2 + math.ceil(self:combatTalentScale(self:getTalentLevel(t), 0.3, 2.3))) end, action = function(self, t) local tg = self:getTalentTarget(t) - self:project(tg, self.x, self.y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) - self:project(tg, self.x, self.y, DamageType.TEMPORAL_ECHO, t.getPercent(self, t)) - game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_temporal", {radius=tg.radius}) - game:playSoundNear(self, "talents/teleport") - return true - end, - info = function(self, t) - local percent = t.getPercent(self, t) * 100 - local radius = self:getTalentRadius(t) - local damage = t.getDamage(self, t) - return ([[Creates a temporal echo in a nova around you, in a radius of %d. Affected targets will take %0.2f temporal damage, as well as %d%% of the difference between their current life and max life as additional temporal damage. - The percentage and damage scales with your Spellpower.]]): - format(radius, damDesc(self, DamageType.TEMPORAL, damage), percent) - end, -} - -newTalent{ - name = "Damage Smearing", - type = {"chronomancy/timetravel", 4}, - mode = "sustained", - require = chrono_req_high4, - sustain_paradox = 84, - cooldown = 24, - tactical = { DEFEND = 2 }, - points = 5, - -- called in damage_types to split the damage - getPercent = function(self, t) return self:combatTalentLimit(t, 50, 10, 30)/100 end, -- Limit < 50% - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 6)) end, - activate = function(self, t) - game:playSoundNear(self, "talents/spell_generic") - return {} - end, - deactivate = function(self, t, p) - return true - end, - info = function(self, t) - local percent = t.getPercent(self, t) * 100 - local duration = t.getDuration(self, t) - return ([[You convert %d%% of all non-temporal damage you receive into temporal damage spread out over %d turns. - ]]):format(percent, duration) - end, -} + local x, y = self:getTarget(tg) + if not x or not y then return nil end + local _ _, x, y = self:canProject(tg, x, y) + local target = game.level.map(x, y, Map.ACTOR) + if not target then return end ---[=[ -newTalent{ - name = "Door to the Past", - type = {"chronomancy/timetravel", 4}, - require = chrono_req_high4, no_sustain_autoreset = true, - points = 5, - mode = "sustained", - sustain_paradox = 150, - cooldown = 25, - no_npc_use = true, - getAnomalyCount = function(self, t) return math.ceil(self:getTalentLevel(t)) end, - on_learn = function(self, t) - if not self:knowTalent(self.T_REVISION) then - self:learnTalent(self.T_REVISION, nil, nil, {no_unlearn=true}) - end - end, - on_unlearn = function(self, t) - if not self:knowTalent(t) then - self:unlearnTalent(self.T_REVISION) - end - end, - do_anomalyCount = function(self, t) - if self.dttp_anomaly_count == 0 then - -- check for anomaly - if not game.zone.no_anomalies and not self:attr("no_paradox_fail") and self:paradoxFailChance() then - -- Random anomaly - local ts = {} - for id, t in pairs(self.talents_def) do - if t.type[1] == "chronomancy/anomalies" then ts[#ts+1] = id end - end - if not silent then game.logPlayer(self, "Your Door to the Past has caused an anomaly!") end - self:forceUseTalent(rng.table(ts), {ignore_energy=true}) - end - -- reset count - self.dttp_anomaly_count = t.getAnomalyCount(self, t) - else - self.dttp_anomaly_count = self.dttp_anomaly_count - 1 - end - end, - activate = function(self, t) - if checkTimeline(self) == true then - return + if target:attr("timetravel_immune") then + game.logSeen(target, "%s is immune!", target.name:capitalize()) + return true end - -- set the counter - self.dttp_anomaly_count = t.getAnomalyCount(self, t) - + -- Project our damage + self:project(tg, x, y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) + + game.level.map:particleEmitter(x, y, 1, "temporal_thrust") game:playSoundNear(self, "talents/arcane") - return { - game:onTickEnd(function() - game:chronoClone("revision") - end), - particle = self:addParticles(Particles.new("temporal_aura", 1)), + + -- If they're dead don't remove them from time + if target.dead or target.player then return true end + + -- Check hit + local power = getParadoxSpellpower(self, t) + local hit = self:checkHit(power, target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) + if not hit then game.logSeen(target, "%s resists!", target.name:capitalize()) return true end + + -- Apply spellshock and destabilization + target:crossTierEffect(target.EFF_SPELLSHOCKED, getParadoxSpellpower(self, t)) + target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=getParadoxSpellpower(self, t, 0.3)}) + + -- Placeholder for the actor + local oe = game.level.map(target.x, target.y, engine.Map.TERRAIN) + if not oe or oe:attr("temporary") then return true end + local e = mod.class.Object.new{ + old_feat = oe, type = oe.type, subtype = oe.subtype, + name = "temporal instability", image = oe.image, add_mos = {{image="object/temporal_instability.png"}}, + display = '&', color=colors.LIGHT_BLUE, + temporary = t.getDuration(self, t), + canAct = false, + target = target, + act = function(self) + self:useEnergy() + self.temporary = self.temporary - 1 + -- return the rifted actor + if self.temporary <= 0 then + game.level.map(self.target.x, self.target.y, engine.Map.TERRAIN, self.old_feat) + game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y) + game.level:removeEntity(self) + local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true}) + local old_levelup = self.target.forceLevelup + local old_check = self.target.check + self.target.forceLevelup = function() end + self.target.check = function() end + game.zone:addEntity(game.level, self.target, "actor", mx, my) + self.target.forceLevelup = old_levelup + self.target.check = old_check + end + end, + summoner_gain_exp = true, summoner = self, } - end, - deactivate = function(self, t, p) - if game._chronoworlds then game._chronoworlds = nil end - self.dttp_anomaly_count = nil - self:removeParticles(p.particle) - return true - end, - info = function(self, t) - local count = t.getAnomalyCount(self, t) - return ([[This powerful spell allows you to mark a point in time that you can later return to by casting Revision (which you'll automatically learn upon learning this spell). Maintaining such a doorway causes constant strain on the spacetime continuum and can possibly trigger an anomaly (using your current anomaly chance) once every %d turns. - This spell splits the timeline. Attempting to use another spell that also splits the timeline while this effect is active will be unsuccessful. - Additional talent points will increase the time between anomaly checks.]]): - format(count) - end, -} - -newTalent{ - name = "Revision", - type = {"chronomancy/other", 1}, - type_no_req = true, - points = 1, - message = "@Source@ revises history.", - cooldown = 50, - paradox = 25, - no_npc_use = true, - on_pre_use = function(self, t, silent) if not self:isTalentActive(self.T_DOOR_TO_THE_PAST) then if not silent then game.logPlayer(self, "Door to the Past must be active to use this talent.") end return false end return true end, - no_unlearn_last = true, - action = function(self, t) - - -- Prevent Revision After Death - if game._chronoworlds == nil then - game.logPlayer(game.player, "#LIGHT_RED#Your spell fizzles.") - return - end - - game:onTickEnd(function() - if not game:chronoRestore("revision", true) then - game.logSeen(self, "#LIGHT_RED#The spell fizzles.") - return - end - game.logPlayer(game.player, "#LIGHT_BLUE#You unfold the spacetime continuum to a previous state!") - - -- Manualy start the cooldown of the "old player" - game.player:startTalentCooldown(t) - game.player:incParadox(t.paradox * (1 + (game.player.paradox / 300))) - game.player:forceUseTalent(game.player.T_DOOR_TO_THE_PAST, {ignore_energy=true}) - -- remove anomaly count - if self.dttp_anomaly_count then self.dttp_anomaly_count = nil end - if game._chronoworlds then game._chronoworlds = nil end - end) - + + -- Replace the target with the instability and update the map + game.logSeen(target, "%s has moved forward in time!", target.name:capitalize()) + game.level:removeEntity(target, true) + game.level:addEntity(e) + game.level.map(x, y, Map.TERRAIN, e) + game.nicer_tiles:updateAround(game.level, x, y) + game.level.map:updateMap(x, y) return true end, info = function(self, t) - return ([[Casting Revision will return you to the point in time you created a temporal marker using Door to the Past.]]) - :format() + local damage = t.getDamage(self, t) + local duration = t.getDuration(self, t) + return ([[Inflicts %0.2f temporal damage. If your target survives, it may be removed from time for %d turns. + The damage will scale with your Spellpower.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration) end, -}]=] +} \ No newline at end of file diff --git a/game/modules/tome/data/talents/corruptions/plague.lua b/game/modules/tome/data/talents/corruptions/plague.lua index 80af80bbb786ced8b308f5403f045b82d07e8efd..218be4001567788fe9876050f78ffb7d3ed3b211 100644 --- a/game/modules/tome/data/talents/corruptions/plague.lua +++ b/game/modules/tome/data/talents/corruptions/plague.lua @@ -63,7 +63,7 @@ newTalent{ return true end, info = function(self, t) - return ([[Fires a bolt of pure filth, diseasing your target with a disease doing %0.2f blight damage per turns for 6 turns, and reducing one of its physical stats (strength, constitution, dexterity) by %d. The three diseases can stack. + return ([[Fires a bolt of pure filth, diseasing your target with a disease doing %0.2f blight damage per turn for 6 turns, and reducing one of its physical stats (strength, constitution, dexterity) by %d. The three diseases can stack. Virulent Disease will always try to apply a disease the target does not currently have, and also one that will have the most debilitating effect for the target. The effect will increase with your Spellpower.]]): format(damDesc(self, DamageType.BLIGHT, 7 + self:combatTalentSpellDamage(t, 6, 65)), self:combatTalentSpellDamage(t, 5, 35)) diff --git a/game/modules/tome/data/talents/corruptions/shadowflame.lua b/game/modules/tome/data/talents/corruptions/shadowflame.lua index 7b33bddbdffc0bae5acf4ecd1677245c4468b28a..5358ec7f21e043b756e046a15061b569f246f12c 100644 --- a/game/modules/tome/data/talents/corruptions/shadowflame.lua +++ b/game/modules/tome/data/talents/corruptions/shadowflame.lua @@ -22,20 +22,23 @@ newTalent{ type = {"corruption/shadowflame", 1}, require = corrs_req1, points = 5, - vim = 20, + vim = 12, cooldown = 30, + no_energy = true, tactical = { BUFF = 2, ESCAPE = 1, CLOSEIN = 1 }, getDuration = function(self, t) return math.floor(self:combatTalentLimit(t, 30, 5, 9)) end, -- Limit < 30 (make sure they can't hide forever) + getDefs = function(self, t) return self:combatTalentScale(t, 5, 20), self:combatTalentScale(t, 5, 16) end, action = function(self, t) - self:setEffect(self.EFF_WRAITHFORM, t.getDuration(self, t), {def=self:combatTalentSpellDamage(t, 5, 19), armor=self:combatTalentSpellDamage(t, 5, 15)}) + local def, armor = t.getDefs(self, t) + self:setEffect(self.EFF_WRAITHFORM, t.getDuration(self, t), {def=def, armor=armor}) return true end, info = function(self, t) return ([[Turn into a wraith, allowing you to walk through walls (but not preventing suffocation) for %d turns. - Also increases your Defense and Armour by %d and %d, respectively. + Also increases your defense and armour by %d and %d, respectively. If you are still in a wall when the effect ends you will randomly teleport. - The bonuses will increase with your Spellpower.]]): - format(t.getDuration(self, t), self:combatTalentSpellDamage(t, 5, 19), self:combatTalentSpellDamage(t, 5, 15)) + ]]): + format(t.getDuration(self, t), t.getDefs(self, t)) end, } diff --git a/game/modules/tome/data/talents/cunning/ambush.lua b/game/modules/tome/data/talents/cunning/ambush.lua index f7729f5a3c220a326043d2d5eca808d0fea9d1b6..f3fcb1326714354dfecdc756ba12f11766d4e2da 100644 --- a/game/modules/tome/data/talents/cunning/ambush.lua +++ b/game/modules/tome/data/talents/cunning/ambush.lua @@ -31,6 +31,7 @@ newTalent{ tactical = { DISABLE = {disarm = 2} }, requires_target = true, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end, + speed = "weapon", action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} local x, y, target = self:getTarget(tg) @@ -65,6 +66,7 @@ newTalent{ tactical = { DISABLE = {silence = 2}, CLOSEIN = 2 }, requires_target = true, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 2, 6)) end, + speed = "combat", action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} local x, y, target = self:getTarget(tg) @@ -113,6 +115,7 @@ newTalent{ getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end, getHealth = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 20, 500), 1, 0.2, 0, 0.584, 384) end, -- Limit to < 100% health of summoner getDam = function(self, t) return self:combatLimit(self:combatTalentSpellDamage(t, 10, 500), 1.6, 0.4, 0, 0.761 , 361) end, -- Limit to <160% Nerf? + speed = "spell", action = function(self, t) -- Find space local x, y = util.findFreeGrid(self.x, self.y, 1, true, {[Map.ACTOR]=true}) @@ -228,6 +231,7 @@ newTalent{ getDuration = function(self, t) return math.floor(self:combatTalentLimit(t, 18, 4, 8)) end, -- Limit to <18 getDamageRes = function(self, t) return self:combatTalentScale(t, 15, 35) end, getBlinkRange = function(self, t) return math.floor(self:combatTalentScale(t, 5, 7)) end, + speed = "spell", action = function(self, t) self:setEffect(self.EFF_SHADOW_VEIL, t.getDuration(self, t), {res=t.getDamageRes(self, t), dam=t.getDamage(self, t), range=t.getBlinkRange(self, t)}) return true @@ -243,4 +247,3 @@ newTalent{ format(duration, res, t.getBlinkRange(self, t) ,100 * damage) end, } - diff --git a/game/modules/tome/data/talents/cunning/called-shots.lua b/game/modules/tome/data/talents/cunning/called-shots.lua index 61253a6bf80c7b2aa0143d1f39af4aa39de6827e..b91ad604ff6565b9b730d006f20222c195d03492 100644 --- a/game/modules/tome/data/talents/cunning/called-shots.lua +++ b/game/modules/tome/data/talents/cunning/called-shots.lua @@ -114,6 +114,7 @@ newTalent { damage_multiplier = function(self, t) return self:combatTalentWeaponDamage(t, 1.5, 1.9) end, + speed = "archery", action = fire_shot, info = function(self, t) return ([[Strike your opponent in the knee (or other critical point in an ambulatory appendage) for %d%% weapon damage, knocking them down (%d turn pin) and slowing their movement by %d%% for %d turns afterwards. @@ -159,6 +160,7 @@ newTalent { pen_off(self, t, target, x, y) end, archery_target_parameters = {one_shot = true}, + speed = "archery", action = function(self, t) local tg = {type = "hit"} @@ -173,7 +175,7 @@ newTalent { local target = game.level.map(targets[1].x, targets[1].y, engine.Map.ACTOR) if not target then return end game:delayedLogMessage(self, target, "kill_shot", "#DARK_ORCHID##Source# snipes #Target# (%+d%%%%%%%% weapon bonus for range)!#LAST#", distbonus*100) - + local params = {mult = damage + distbonus} if bonuses.crit_chance then params.crit_chance = bonuses.crit_chance end if bonuses.crit_power then params.crit_power = bonuses.crit_power end @@ -181,7 +183,7 @@ newTalent { self:archeryShoot(targets, t, {type = "hit", speed = 200}, params) return true - end, + end, info = function(self, t) local range = self:getTalentRange(t) return ([[Employ a specialized sniping shot at a target. @@ -220,6 +222,7 @@ newTalent { pen_off(self, t, target, x, y) end, archery_target_parameters = {limit_shots = 1, multishots = 3}, + speed = "archery", action = fire_shot, info = function(self, t) return ([[Fire three shots in quick succession at a vulnerable point on the target (usually the head). diff --git a/game/modules/tome/data/talents/cunning/dirty.lua b/game/modules/tome/data/talents/cunning/dirty.lua index 8d2e6b0e872e640a955f1c7590df08699b162773..dd488d79bb28127115a8349c556fdbb1a3dd2aef 100644 --- a/game/modules/tome/data/talents/cunning/dirty.lua +++ b/game/modules/tome/data/talents/cunning/dirty.lua @@ -31,6 +31,7 @@ newTalent{ requires_target = true, getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.2, 0.7) end, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end, + speed = "weapon", action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} local x, y, target = self:getTarget(tg) @@ -44,7 +45,7 @@ newTalent{ end if not target:hasEffect(target.EFF_STUNNED) then self:logCombat(target, "#Target# resists the stun and #Source# quickly regains its footing!") - self.energy.value = self.energy.value + game.energy_to_act * self:combatSpeed() + self.energy.value = self.energy.value + game.energy_to_act * self:getSpeed("weapon") end end @@ -91,6 +92,7 @@ newTalent{ if self:attr("never_move") then return false end return true end, + speed = "weapon", action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} local x, y, target = self:getTarget(tg) @@ -103,13 +105,11 @@ newTalent{ if not self:canMove(tx,ty,true) or not target:canMove(sx,sy,true) then self:logCombat(target, "Terrain prevents #Source# from switching places with #Target#.") return true - end + end self:setEffect(self.EFF_EVASION, t.getDuration(self, t), {chance=50}) -- Displace if not target.dead then - self.x = nil self.y = nil self:move(tx, ty, true) - target.x = nil target.y = nil target:move(sx, sy, true) end end @@ -138,6 +138,7 @@ newTalent{ getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1, 1.9) end, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end, getSpeedPenalty = function(self, t) return self:combatLimit(self:combatTalentStatDamage(t, "cun", 5, 50), 100, 20, 0, 55.7, 35.7) end, -- Limit < 100% + speed = "weapon", action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} local x, y, target = self:getTarget(tg) @@ -161,4 +162,3 @@ newTalent{ format(100 * damage, duration, speedpen) end, } - diff --git a/game/modules/tome/data/talents/cunning/lethality.lua b/game/modules/tome/data/talents/cunning/lethality.lua index 4c8de35959c67d2e5dce1d6d39281fdbd3f35817..27b3f89e8bbb9ddce2569abb480b61a88357cdbd 100644 --- a/game/modules/tome/data/talents/cunning/lethality.lua +++ b/game/modules/tome/data/talents/cunning/lethality.lua @@ -108,14 +108,18 @@ newTalent{ stamina = 50, cooldown = 50, tactical = { BUFF = 1 }, + fixed_cooldown = true, getTalentCount = function(self, t) return math.floor(self:combatTalentScale(t, 2, 7, "log")) end, getMaxLevel = function(self, t) return self:getTalentLevel(t) end, + speed = "combat", action = function(self, t) local tids = {} for tid, _ in pairs(self.talents_cd) do local tt = self:getTalentFromId(tid) - if tt.type[2] <= t.getMaxLevel(self, t) and (tt.type[1]:find("^cunning/") or tt.type[1]:find("^technique/")) then - tids[#tids+1] = tid + if not tt.fixed_cooldown then + if tt.type[2] <= t.getMaxLevel(self, t) and (tt.type[1]:find("^cunning/") or tt.type[1]:find("^technique/")) then + tids[#tids+1] = tid + end end end for i = 1, t.getTalentCount(self, t) do @@ -133,4 +137,3 @@ newTalent{ format(talentcount, maxlevel) end, } - diff --git a/game/modules/tome/data/talents/cunning/poisons.lua b/game/modules/tome/data/talents/cunning/poisons.lua index aaad91ecf66bf8e0a556b4e5f8db43579ed312a5..84528dd8faec6be2d8dce8b4772a51283d8d5668 100644 --- a/game/modules/tome/data/talents/cunning/poisons.lua +++ b/game/modules/tome/data/talents/cunning/poisons.lua @@ -102,6 +102,7 @@ newTalent{ end tg.archery.mult = self:combatTalentWeaponDamage(t, 0.5 + nb * 0.6, 0.9 + nb * 1) end, + speed = "weapon", action = function(self, t) if not self:hasArcheryWeapon() then local tg = {type="hit", range=self:getTalentRange(t)} @@ -442,4 +443,3 @@ newTalent{ format(damDesc(self, DamageType.NATURE, t.getDOT(self, t)), t.getDuration(self, t), t.getEffect(self, t)) end, } - diff --git a/game/modules/tome/data/talents/cunning/scoundrel.lua b/game/modules/tome/data/talents/cunning/scoundrel.lua index a7c54b10c367f5e66705faaf716c78a2ea1f0c2a..427b3c1eafc148548706d9ed98ab93d52cb06188 100644 --- a/game/modules/tome/data/talents/cunning/scoundrel.lua +++ b/game/modules/tome/data/talents/cunning/scoundrel.lua @@ -51,7 +51,7 @@ newTalent{ mode = "passive", points = 5, getDuration = function(self, t) return math.ceil(self:combatTalentScale(t, 3.3, 5.3)) end, - -- _M:physicalCrit function in mod\class\interface\Combat.lua handles crit penalty + -- _M:physicalCrit function in mod\class\interface\Combat.lua handles crit penalty getCritPenalty = function(self,t) return self:combatTalentScale(t, 10, 30) end, disableChance = function(self,t) return self:combatTalentLimit(t, 100, 8, 20) end, -- Limit <100% getMovePenalty = function(self, t) return self:combatLimit(self:combatTalentStatDamage(t, "cun", 10, 30), 1, 0.05, 0, 0.274, 22.4) end, -- Limit <100% @@ -94,6 +94,7 @@ newTalent{ tactical = { CLOSEIN = 3 }, requires_target = true, range = function(self, t) return math.floor(self:combatTalentScale(t, 6.8, 8.6)) end, + speed = "movement", action = function(self, t) if self:attr("never_move") then game.logPlayer(self, "You can not do that currently.") return end diff --git a/game/modules/tome/data/talents/cunning/survival.lua b/game/modules/tome/data/talents/cunning/survival.lua index fee45c58a3c41a63eb3ec58b5d800a95f779b433..bf1dcfcd8cd12c55c81b86489cc953d552e34f37 100644 --- a/game/modules/tome/data/talents/cunning/survival.lua +++ b/game/modules/tome/data/talents/cunning/survival.lua @@ -44,7 +44,7 @@ newTalent{ require = cuns_req2, mode = "passive", points = 5, - cdReduc = function(tl) + cdReduc = function(tl) if tl <=0 then return 0 end return math.floor(100*tl/(tl+7.5)) --I5 Limit < 100% end, @@ -65,8 +65,8 @@ newTalent{ require = cuns_req3, mode = "passive", points = 5, - -- called by functions _M:combatSeeStealth and _M:combatSeeInvisible functions mod\class\interface\Combat.lua - seePower = function(self, t) return self:combatScale(self:getCun(15, true)*self:getTalentLevel(t), 5, 0, 80, 75) end, --I5 + -- called by functions _M:combatSeeStealth and _M:combatSeeInvisible functions mod\class\interface\Combat.lua + seePower = function(self, t) return self:combatScale(self:getCun(15, true)*self:getTalentLevel(t), 5, 0, 80, 75) end, --I5 info = function(self, t) return ([[You look at your surroundings with more intensity than most people, allowing you to see stealthed or invisible creatures. Increases stealth detection by %d and invisibility detection by %d. @@ -90,6 +90,7 @@ newTalent{ self:combatScale(self:getTalentLevel(t) * (self:getCun(25, true) + self:getDex(25, true)), 0, 0, 55, 250, 0.75) -- Limit evasion chance < 50%, defense bonus ~= 55 at level 50 end, + speed = "combat", action = function(self, t) local dur = t.getDur(self) local chance, def = t.getChanceDef(self,t) diff --git a/game/modules/tome/data/talents/cunning/tactical.lua b/game/modules/tome/data/talents/cunning/tactical.lua index 974e3e614408b836887a46551fce1d9a554983a6..a344ddf04d69a57243323a69f6f60d73470780ff 100644 --- a/game/modules/tome/data/talents/cunning/tactical.lua +++ b/game/modules/tome/data/talents/cunning/tactical.lua @@ -60,7 +60,7 @@ newTalent{ end, } --- Limit counter attacks/turn for balance using a buff (warns attacking players of the talent) +-- Limit counter attacks/turn for balance using a buff (warns attacking players of the talent) -- Talent effect is implemented in _M:attackTargetWith function in mod\class\interface\Combat.lua (includes adjacency check) -- The Effect EFF_COUNTER_ATTACKING is defined in mod.data.timed_effects.physical.lua -- and is refreshed each turn in mod.class.Actor.lua _M:actBase @@ -105,6 +105,7 @@ newTalent{ getPower = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 1, 25) end, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, getDefense = function(self, t) return 5 + self:combatTalentStatDamage(t, "cun", 1, 50) end, + speed = "combat", action = function(self, t) self:setEffect(self.EFF_DEFENSIVE_MANEUVER, t.getDuration(self, t), {power=t.getDefense(self, t)}) return true @@ -128,6 +129,7 @@ newTalent{ cooldown = 30, sustain_stamina = 30, tactical = { BUFF = 2 }, + speed = "combat", getReductionMax = function(self, t) return 5 * math.floor(self:combatTalentLimit(t, 20, 1.4, 7.1)) end, -- Limit to 95% do_weakness = function(self, t, target) target:setEffect(target.EFF_WEAKENED_DEFENSES, 3, {inc = - 5, max = - t.getReductionMax(self, t)}) diff --git a/game/modules/tome/data/talents/cunning/traps.lua b/game/modules/tome/data/talents/cunning/traps.lua index cbc6b835fb5ba8aea6ac336a78b64a3c92c24257..02e9997f1a2c6d4d1686c5674f11aef9a7d330b0 100644 --- a/game/modules/tome/data/talents/cunning/traps.lua +++ b/game/modules/tome/data/talents/cunning/traps.lua @@ -91,6 +91,7 @@ newTalent{ no_npc_use = true, range = function(self, t) return math.floor(self:combatTalentScale(t, 6, 11)) end, getDuration = function(self,t) return math.floor(self:combatTalentScale(t, 5, 9, 0.5, 0, 0, true)) end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local tx, ty, target = self:getTarget(tg) @@ -153,7 +154,7 @@ newTalent{ end, info = function(self, t) local t2 = self:getTalentFromId(self.T_TAUNT) - local rad = t2.radius(self, t) + local rad = t2.radius(self, t) return ([[Project a noisy lure for %d turns that attracts all creatures in a radius %d to it. At level 5, when the lure is destroyed, it will trigger some traps in a radius of 2 around it (check individual traps to see if they are triggered). Use of this talent will not break stealth.]]):format(t.getDuration(self,t), rad) @@ -171,6 +172,7 @@ newTalent{ proj_speed = 10, requires_target = true, range = 10, + speed = "combat", radius = function(self, t) return math.max(0,math.floor(self:combatTalentScale(t, 0.5, 2.5))) end, getSightLoss = function(self, t) return math.floor(self:combatTalentScale(t,1, 6, "log", 0, 4)) end, -- 1@1 6@5 tactical = { DISABLE = { blind = 2 } }, @@ -258,6 +260,7 @@ newTalent{ no_break_stealth = trap_stealth, tactical = { ATTACKAREA = { FIRE = 2 } }, no_unlearn_last = true, + speed = "combat", getDamage = function(self, t) return 30 + self:combatStatScale("cun", 8, 80) * self:callTalent(self.T_TRAP_MASTERY, "getTrapMastery")/20 end, action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} @@ -307,6 +310,7 @@ newTalent{ no_break_stealth = trap_stealth, no_unlearn_last = true, getDamage = function(self, t) return (40 + self:combatStatScale("cun", 10, 100) * self:callTalent(self.T_TRAP_MASTERY, "getTrapMastery")/20)/5 end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local x, y, target = self:getTarget(tg) @@ -359,6 +363,7 @@ newTalent{ no_break_stealth = trap_stealth, no_unlearn_last = true, getDistance = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(self.T_TRAP_MASTERY), 3, 7)) end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local x, y, target = self:getTarget(tg) @@ -419,6 +424,7 @@ newTalent{ no_unlearn_last = true, getDamage = function(self, t) return 60 + self:combatStatScale("cun", 9, 90) * self:callTalent(self.T_TRAP_MASTERY,"getTrapMastery")/20 end, getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(self.T_TRAP_MASTERY), 2.1, 4.43)) end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local x, y, target = self:getTarget(tg) @@ -472,6 +478,7 @@ newTalent{ no_break_stealth = trap_stealth, no_unlearn_last = true, getDamage = function(self, t) return 20 + self:combatStatScale("cun", 10, 100) * self:callTalent(self.T_TRAP_MASTERY,"getTrapMastery")/20 end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local x, y, target = self:getTarget(tg) @@ -523,6 +530,7 @@ newTalent{ no_unlearn_last = true, getDuration = function(self, t) return math.floor(self:combatTalentScale(self:getTalentLevel(self.T_TRAP_MASTERY), 2.5, 4.5)) end, getDamage = function(self, t) return 20 + self:combatStatScale("cun", 5, 50) * self:callTalent(self.T_TRAP_MASTERY,"getTrapMastery") / 30 end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local x, y, target = self:getTarget(tg) @@ -587,6 +595,7 @@ newTalent{ no_break_stealth = trap_stealth, no_unlearn_last = true, getDamage = function(self, t) return 20 + self:combatStatScale("cun", 5, 50) * self:callTalent(self.T_TRAP_MASTERY,"getTrapMastery")/20 end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local x, y, target = self:getTarget(tg) @@ -647,6 +656,7 @@ newTalent{ no_break_stealth = trap_stealth, no_unlearn_last = true, getDamage = function(self, t) return 20 + self:combatStatScale("cun", 5, 50) * self:callTalent(self.T_TRAP_MASTERY,"getTrapMastery") / 40 end, + speed = "combat", action = function(self, t) local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, simple_dir_request=true, talent=t} local x, y, target = self:getTarget(tg) @@ -675,7 +685,7 @@ newTalent{ tgts[target] = true local ox, oy = target.x, target.y engine.DamageType:get(engine.DamageType.TEMPORAL).projector(self.summoner, target.x, target.y, engine.DamageType.TEMPORAL, self.dam) - if target:canBe("knockback") then + if target:canBe("knockback") then target:pull(self.x, self.y, 1) if target.x ~= ox or target.y ~= oy then self.summoner:logCombat(target, "#Target# is pulled towards #Source#'s gravity trap!") diff --git a/game/modules/tome/data/talents/cursed/cursed-aura.lua b/game/modules/tome/data/talents/cursed/cursed-aura.lua index d26501b832e1e47a3ee77005a4a7d183bbc00ef4..03963563b62c836239d3d3b65c46e0e012c992db 100644 --- a/game/modules/tome/data/talents/cursed/cursed-aura.lua +++ b/game/modules/tome/data/talents/cursed/cursed-aura.lua @@ -378,22 +378,10 @@ newTalent{ if game.level.map(x, y, Map.ACTOR) or game.level.map:checkEntity(x, y, game.level.map.TERRAIN, "block_move") then return nil end -- select the item - local d = self:showInventory("Which weapon will be your sentry?", inven, - function(o) - return o.type == "weapon" - end, nil) - d.action = function(o, item) - d.used_talent = true - d.selected_object = o - d.selected_item = item - - return false - end - - local co = coroutine.running() - d.unload = function(self) coroutine.resume(co, self.used_talent, self.selected_object, d.selected_item) end - local used_talent, o, item = coroutine.yield() - if not used_talent then return nil end + local d = self:showInventory("Which weapon will be your sentry?", inven, function(o) return o.type == "weapon" end, nil) + d.action = function(o, item) self:talentDialogReturn(true, o, item) return false end + local ret, o, item = self:talentDialog(d) + if not ret then return nil end local result = self:removeObject(inven, item) diff --git a/game/modules/tome/data/talents/cursed/endless-hunt.lua b/game/modules/tome/data/talents/cursed/endless-hunt.lua index b9e62c77b27165a068c5f6e859037eab99b60df0..ce6cd62ea6f1f925537e64265a93ae1aa60edea8 100644 --- a/game/modules/tome/data/talents/cursed/endless-hunt.lua +++ b/game/modules/tome/data/talents/cursed/endless-hunt.lua @@ -221,22 +221,19 @@ newTalent{ if self ~= game.player and (self:isTalentActive(self.T_CLEAVE) or self:isTalentActive(self.T_REPEL)) then return false end return true end, + sustain_slots = 'cursed_combat_style', activate = function(self, t) - -- deactivate other talents and place on cooldown - if self:isTalentActive(self.T_CLEAVE) then - self:useTalent(self.T_CLEAVE) - elseif self:knowTalent(self.T_CLEAVE) then - local tCleave = self:getTalentFromId(self.T_CLEAVE) - self.talents_cd[self.T_CLEAVE] = tCleave.cooldown - end - - if self:isTalentActive(self.T_REPEL) then - self:useTalent(self.T_REPEL) - elseif self:knowTalent(self.T_REPEL) then + -- Place other talents on cooldown. + if self:knowTalent(self.T_REPEL) and not self:isTalentActive(self.T_REPEL) then local tRepel = self:getTalentFromId(self.T_REPEL) self.talents_cd[self.T_REPEL] = tRepel.cooldown end + if self:knowTalent(self.T_CLEAVE) and not self:isTalentActive(self.T_CLEAVE) then + local tCleave = self:getTalentFromId(self.T_CLEAVE) + self.talents_cd[self.T_CLEAVE] = tCleave.cooldown + end + local movementSpeedChange = t.getMovementSpeedChange(self, t) return { moveId = self:addTemporaryValue("movement_speed", movementSpeedChange), @@ -252,8 +249,8 @@ newTalent{ info = function(self, t) local movementSpeedChange = t.getMovementSpeedChange(self, t) local defenseChange = t.getDefenseChange(self, t, true) - return ([[Let hate fuel your movements. While active, you gain %d%% movement speed. The recklessness of your movement brings you bad luck (Luck -3). - Cleave, Repel and Surge cannot be active simultaneously, and activating one will place the others in cooldown. + return ([[Let hate fuel your movements. While active, you gain %d%% movement speed. The recklessness of your movement brings you bad luck (Luck -3). + Cleave, Repel and Surge cannot be active simultaneously, and activating one will place the others in cooldown. The speed of your movements, combined with the balance and utility of two weapons, gives you %d extra Defense while dual-wielding. Movement speed and dual-wielding Defense both increase with with the Willpower stat.]]):format(movementSpeedChange * 100, defenseChange) end, diff --git a/game/modules/tome/data/talents/cursed/slaughter.lua b/game/modules/tome/data/talents/cursed/slaughter.lua index 65968402248b4f331e3b4417ee8950396f154fef..59f5d03ed125d8e3e22001300c7ba5387fd20527 100644 --- a/game/modules/tome/data/talents/cursed/slaughter.lua +++ b/game/modules/tome/data/talents/cursed/slaughter.lua @@ -303,18 +303,15 @@ newTalent{ if self ~= game.player and (self:isTalentActive(self.T_SURGE) or self:isTalentActive(self.T_REPEL)) then return false end return true end, + sustain_slots = 'cursed_combat_style', activate = function(self, t) - -- deactivate other talents and place on cooldown - if self:isTalentActive(self.T_SURGE) then - self:useTalent(self.T_SURGE) - elseif self:knowTalent(self.T_SURGE) then + -- Place other talents on cooldown. + if self:knowTalent(self.T_SURGE) and not self:isTalentActive(self.T_SURGE) then local tSurge = self:getTalentFromId(self.T_SURGE) self.talents_cd[self.T_SURGE] = tSurge.cooldown end - if self:isTalentActive(self.T_REPEL) then - self:useTalent(self.T_REPEL) - elseif self:knowTalent(self.T_REPEL) then + if self:knowTalent(self.T_REPEL) and not self:isTalentActive(self.T_REPEL) then local tRepel = self:getTalentFromId(self.T_REPEL) self.talents_cd[self.T_REPEL] = tRepel.cooldown end @@ -353,10 +350,9 @@ newTalent{ info = function(self, t) local chance = t.getChance(self, t, 0) local chance2h = t.getChance(self, t, 1) - return ([[While active, every swing of your weapon has a %d%% (if one-handed) or %d%% (if two-handed) chance of striking a second nearby target for %d%% (at 0 Hate) to %d%% (at 100+ Hate) damage (+25%% for two-handed weapons). The recklessness of your attacks brings you bad luck (luck -3). + return ([[While active, every swing of your weapon has a %d%% (if one-handed) or %d%% (if two-handed) chance of striking a second nearby target for %d%% (at 0 Hate) to %d%% (at 100+ Hate) damage (+25%% for two-handed weapons). The recklessness of your attacks brings you bad luck (luck -3). Cleave, Repel and Surge cannot be active simultaneously, and activating one will place the others in cooldown. The Cleave chance and damage increase with your Strength.]]): format(chance, chance2h, t.getDamageMultiplier(self, t, 0) * 100, t.getDamageMultiplier(self, t, 100) * 100) end, } - diff --git a/game/modules/tome/data/talents/cursed/strife.lua b/game/modules/tome/data/talents/cursed/strife.lua index 3cd2ec125dcca94b0ccf05d429f9d687d4d15603..5acfe5dcaf145e001a1430623b9f470e073268ef 100644 --- a/game/modules/tome/data/talents/cursed/strife.lua +++ b/game/modules/tome/data/talents/cursed/strife.lua @@ -378,22 +378,19 @@ newTalent{ if self ~= game.player and (self:isTalentActive(self.T_CLEAVE) or self:isTalentActive(self.T_SURGE)) then return false end return true end, + sustain_slots = 'cursed_combat_style', activate = function(self, t) - -- deactivate other talents and place on cooldown - if self:isTalentActive(self.T_CLEAVE) then - self:useTalent(self.T_CLEAVE) - elseif self:knowTalent(self.T_CLEAVE) then - local tCleave = self:getTalentFromId(self.T_CLEAVE) - self.talents_cd[self.T_CLEAVE] = tCleave.cooldown - end - - if self:isTalentActive(self.T_SURGE) then - self:useTalent(self.T_SURGE) - elseif self:knowTalent(self.T_SURGE) then + -- Place other talents on cooldown. + if self:knowTalent(self.T_SURGE) and not self:isTalentActive(self.T_SURGE) then local tSurge = self:getTalentFromId(self.T_SURGE) self.talents_cd[self.T_SURGE] = tSurge.cooldown end + if self:knowTalent(self.T_CLEAVE) and not self:isTalentActive(self.T_CLEAVE) then + local tCleave = self:getTalentFromId(self.T_CLEAVE) + self.talents_cd[self.T_CLEAVE] = tCleave.cooldown + end + return { luckId = self:addTemporaryValue("inc_stats", { [Stats.STAT_LCK] = -3 }) } @@ -409,7 +406,7 @@ newTalent{ end, info = function(self, t) local chance = t.getChance(self, t) - return ([[Rather than hide from the onslaught, you face down every threat. While active you have a %d%% chance of repelling a melee attack. The recklessness of your defense brings you bad luck (Luck -3). + return ([[Rather than hide from the onslaught, you face down every threat. While active you have a %d%% chance of repelling a melee attack. The recklessness of your defense brings you bad luck (Luck -3). Cleave, Repel and Surge cannot be active simultaneously, and activating one will place the others in cooldown. Repel chance increases with your Strength, and when equipped with a shield.]]):format(chance) end, diff --git a/game/modules/tome/data/talents/gifts/call.lua b/game/modules/tome/data/talents/gifts/call.lua index 9fb8cae32db536079bc9229c06d841ce4ee5335f..f922498b66adafb4c8d31542d5306d08c95f79f6 100644 --- a/game/modules/tome/data/talents/gifts/call.lua +++ b/game/modules/tome/data/talents/gifts/call.lua @@ -146,6 +146,7 @@ newTalent{ cooldown = 50, range = 10, tactical = { BUFF = 2 }, + fixed_cooldown = true, getTalentCount = function(self, t) return math.floor(self:combatTalentScale(t, 2, 7, "log")) end, getMaxLevel = function(self, t) return self:getTalentLevel(t) end, action = function(self, t) @@ -154,8 +155,10 @@ newTalent{ local tids = {} for tid, _ in pairs(self.talents_cd) do local tt = self:getTalentFromId(tid) - if tt.type[2] <= maxlev and tt.type[1]:find("^wild%-gift/") then - tids[#tids+1] = tid + if not tt.fixed_cooldown then + if tt.type[2] <= maxlev and tt.type[1]:find("^wild%-gift/") then + tids[#tids+1] = tid + end end end for i = 1, nb do diff --git a/game/modules/tome/data/talents/gifts/cold-drake.lua b/game/modules/tome/data/talents/gifts/cold-drake.lua index e8502c980551ede85854d9be2fea2bcd23cd3f80..fa66f8f980d9a6060c8c1918c29baab4d1a25ccf 100644 --- a/game/modules/tome/data/talents/gifts/cold-drake.lua +++ b/game/modules/tome/data/talents/gifts/cold-drake.lua @@ -27,24 +27,45 @@ newTalent{ random_ego = "attack", equilibrium = 3, cooldown = 7, - range = 1, + range = 0, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 3)) end, + direct_hit = true, + requires_target = true, tactical = { ATTACK = { COLD = 2 } }, requires_target = true, - on_learn = function(self, t) self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) + 1 end, - on_unlearn = function(self, t) self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) - 1 end, - damagemult = function(self, t) return self:combatTalentScale(t, 1.525, 2.025) end, + on_learn = function(self, t) + self.combat_physresist = self.combat_physresist + 4 + self.combat_spellresist = self.combat_spellresist + 4 + self.combat_mentalresist = self.combat_mentalresist + 4 + self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) + 1 + end, + on_unlearn = function(self, t) + self.combat_physresist = self.combat_physresist - 4 + self.combat_spellresist = self.combat_spellresist - 4 + self.combat_mentalresist = self.combat_mentalresist - 4 + self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) - 1 + end, + damagemult = function(self, t) return self:combatTalentScale(t, 1.6, 2.3) end, + target = function(self, t) + return {type="cone", range=self:getTalentRange(t), selffire=false, radius=self:getTalentRadius(t)} + end, action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t)} - local x, y, target = self:getTarget(tg) - if not x or not y or not target then return nil end - if core.fov.distance(self.x, self.y, x, y) > 1 then return nil end - self:attackTarget(target, (self:getTalentLevel(t) >= 4) and DamageType.ICE or DamageType.COLD, t.damagemult(self, t), true) + local tg = self:getTalentTarget(t) + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, function(px, py, tg, self) + local target = game.level.map(px, py, Map.ACTOR) + if target and target ~= self then + local hit = self:attackTarget(target, DamageType.ICE, self:combatTalentWeaponDamage(t, 1.6, 2.3), true) + end + end) + game:playSoundNear(self, "talents/breath") return true end, info = function(self, t) - return ([[You call upon the mighty claw of a cold drake, doing %d%% weapon damage as cold damage. - At level 4, the attack becomes pure ice, giving a chance to freeze the target. - Each point in cold drake talents also increases your cold resistance by 1%%.]]):format(100 * t.damagemult(self, t)) + return ([[You call upon the mighty claw of a cold drake and rake a wave of freezing cold in front of you, doing %d%% weapon damage as Ice damage in a cone of %d. Ice damage gives a chance of freezing the target. + Every level in Ice Claw additionally raises your Physical, Mental and Spell Saves by 4. + Each point in cold drake talents also increases your cold resistance by 1%%.]]):format(100 * t.damagemult(self, t), self:getTalentRadius(t)) end, } @@ -55,28 +76,33 @@ newTalent{ mode = "sustained", points = 5, cooldown = 10, - sustain_equilibrium = 30, + sustain_equilibrium = 10, range = 10, tactical = { ATTACK = { COLD = 1 }, DEFEND = 2 }, on_learn = function(self, t) self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) + 1 end, on_unlearn = function(self, t) self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) - 1 end, - getDamage = function(self, t) return self:combatTalentStatDamage(t, "wil", 10, 700) / 10 end, - getArmor = function(self, t) return self:combatTalentStatDamage(t, "wil", 6, 600) / 10 end, + getArmor = function(self, t) return self:combatTalentMindDamage(t, 5, 25) end, + getLifePct = function(self, t) return self:combatTalentLimit(t, 1, 0.02, 0.10) end, -- Limit < 100% bonus + getDamageOnMeleeHit = function(self, t) return 10 + self:combatTalentMindDamage(t, 10, 30) end, activate = function(self, t) return { - onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.COLD]=t.getDamage(self, t)}), + onhit = self:addTemporaryValue("on_melee_hit", {[DamageType.COLD]=t.getDamageOnMeleeHit(self, t)}), + life = self:addTemporaryValue("max_life", t.getLifePct(self, t)*self.max_life), armor = self:addTemporaryValue("combat_armor", t.getArmor(self, t)), } end, deactivate = function(self, t, p) - self:removeTemporaryValue("on_melee_hit", p.onhit) + self:removeTemporaryValue("max_life", p.life) self:removeTemporaryValue("combat_armor", p.armor) + self:removeTemporaryValue("on_melee_hit", p.onhit) return true end, info = function(self, t) - return ([[Your skin forms icy scales, damaging anyone that hits you for %0.2f cold damage and increasing your Armour by %d. + local life = t.getLifePct(self, t) + return ([[Your skin forms icy scales and your flesh toughens, increasing your Maximum Life by %d%% and your Armour by %d. + You also deal %0.2f cold damage to any enemies that physically strike you. Each point in cold drake talents also increases your cold resistance by 1%%. - The damage and Armor will scale with your Willpower.]]):format(damDesc(self, DamageType.COLD, t.getDamage(self, t)), t.getArmor(self, t)) + The life increase will scale with your Talent Level, and your Armour and retaliation cold damage will scale with Mindpower.]]):format(life * 100, t.getArmor(self, t), damDesc(self, DamageType.COLD, t.getDamageOnMeleeHit(self, t))) end, } @@ -87,7 +113,7 @@ newTalent{ points = 5, random_ego = "defensive", equilibrium = 10, - cooldown = 30, + cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 5, 30, 10)) end, range = 10, tactical = { DISABLE = 2 }, requires_target = true, @@ -95,6 +121,8 @@ newTalent{ on_unlearn = function(self, t) self.resists[DamageType.COLD] = (self.resists[DamageType.COLD] or 0) - 1 end, getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9)) end, getLength = function(self, t) return 1 + math.floor(self:combatTalentScale(t, 3, 7)/2)*2 end, + getIceDamage = function(self, t) return self:combatTalentMindDamage(t, 3, 15) end, + getIceRadius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 2)) end, action = function(self, t) local halflength = math.floor(t.getLength(self,t)/2) local block = function(_, lx, ly) @@ -103,6 +131,8 @@ newTalent{ local tg = {type="wall", range=self:getTalentRange(t), halflength=halflength, talent=t, halfmax_spots=halflength+1, block_radius=block} local x, y = self:getTarget(tg) if not x or not y then return nil end + local ice_damage = self:mindCrit(t.getIceDamage(self, t)) + local ice_radius = t.getIceRadius(self, t) local _ _, _, _, x, y = self:canProject(tg, x, y) if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then return nil end @@ -123,9 +153,15 @@ newTalent{ block_move = true, block_sight = false, temporary = 4 + self:getTalentLevel(t), - x =px, y = py, + x = px, y = py, canAct = false, + dam = ice_damage, + radius = ice_radius, act = function(self) + local tg = {type="ball", range=0, radius=ice_radius, friendlyfire=false, talent=t, x=self.x, y=self.y} + self.summoner.__project_source = self + self.summoner:project(tg, self.x, self.y, engine.DamageType.ICE, self.dam) + self.summoner.__project_source = nil self:useEnergy() self.temporary = self.temporary - 1 if self.temporary <= 0 then @@ -151,8 +187,11 @@ newTalent{ return true end, info = function(self, t) - return ([[Summons an icy wall of %d length for %d turns. Ice walls are transparent. - Each point in cold drake talents also increases your cold resistance by 1%%.]]):format(3 + math.floor(self:getTalentLevel(t) / 2) * 2, t.getDuration(self, t)) + local icerad = t.getIceRadius(self, t) + local icedam = t.getIceDamage(self, t) + return ([[Summons an icy wall of %d length for %d turns. Ice walls are transparent, but block projectiles and enemies. + Ice walls also emit freezing cold, dealing %0.2f damage for each ice wall within radius %d of an enemy, and with each wall giving a 25%% chance to freeze an enemy. This cold cannot hurt the talent user or their allies. + Each point in cold drake talents also increases your cold resistance by 1%%.]]):format(3 + math.floor(self:getTalentLevel(t) / 2) * 2, t.getDuration(self, t), damDesc(self, DamageType.COLD, icedam), icerad) end, } @@ -179,7 +218,7 @@ newTalent{ local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.ICE, self:mindCrit(self:combatTalentStatDamage(t, "str", 30, 430))) + self:project(tg, x, y, DamageType.ICE_SLOW, self:mindCrit(self:combatTalentStatDamage(t, "str", 30, 500))) game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_cold", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/breath") @@ -190,9 +229,9 @@ newTalent{ return true end, info = function(self, t) - return ([[You breathe ice in a frontal cone of radius %d. Any target caught in the area will take %0.2f cold damage, and has a 25%% to be frozen for a few turns (higher rank enemies will be frozen for a shorter time). + return ([[You breathe ice in a frontal cone of radius %d. Any target caught in the area will take %0.2f cold damage, will be slowed 20%% for three turns, and has a 25%% to be frozen for a few turns (higher rank enemies will be frozen for a shorter time). The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. - Each point in cold drake talents also increases your cold resistance by 1%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.COLD, self:combatTalentStatDamage(t, "str", 30, 430))) + Each point in cold drake talents also increases your cold resistance by 1%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.COLD, self:combatTalentStatDamage(t, "str", 30, 500))) end, } diff --git a/game/modules/tome/data/talents/gifts/fire-drake.lua b/game/modules/tome/data/talents/gifts/fire-drake.lua index 5dee77ded242ecbe391dddb6a631cf8f490ee4b7..c0539d81a19db623df712a50da70b2d20f88bb2c 100644 --- a/game/modules/tome/data/talents/gifts/fire-drake.lua +++ b/game/modules/tome/data/talents/gifts/fire-drake.lua @@ -17,79 +17,95 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org + newTalent{ - name = "Bellowing Roar", + name = "Wing Buffet", type = {"wild-gift/fire-drake", 1}, require = gifts_req1, points = 5, random_ego = "attack", - message = "@Source@ roars!", - equilibrium = 3, - cooldown = 20, + equilibrium = 5, + cooldown = 8, range = 0, - on_learn = function(self, t) self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) + 1 end, - on_unlearn = function(self, t) self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) - 1 end, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, + on_learn = function(self, t) + self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) + 1 + self.combat_atk = self.combat_atk + 4 + self.combat_dam = self.combat_dam + 4 + end, + on_unlearn = function(self, t) + self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) - 1 + self.combat_atk = self.combat_atk - 4 + self.combat_dam = self.combat_dam - 4 + end, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.1, 1.6) end, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 3, 6)) end, + direct_hit = true, + tactical = { DEFEND = { knockback = 2 }, ESCAPE = { knockback = 2 } }, + requires_target = true, target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=false, talent=t} + return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, - tactical = { DEFEND = 1, DISABLE = { confusion = 3 } }, action = function(self, t) local tg = self:getTalentTarget(t) - self:project(tg, self.x, self.y, DamageType.PHYSICAL, self:mindCrit(self:combatTalentStatDamage(t, "str", 40, 400))) - self:project(tg, self.x, self.y, DamageType.CONFUSION, { - dur=3, - dam=40 + 6 * self:getTalentLevel(t), - power_check=function() return self:combatPhysicalpower() end, - resist_check=self.combatPhysicalResist, - }) - game.level.map:particleEmitter(self.x, self.y, self:getTalentRadius(t), "shout", {additive=true, life=10, size=3, distorion_factor=0.5, radius=self:getTalentRadius(t), nb_circles=8, rm=0.8, rM=1, gm=0, gM=0, bm=0.1, bM=0.2, am=0.4, aM=0.6}) + local x, y = self:getTarget(tg) + if not x or not y then return nil end + self:project(tg, x, y, function(px, py, tg, self) + local target = game.level.map(px, py, Map.ACTOR) + if target and target ~= self then + local hit = self:attackTarget(target, DamageType.PHYSKNOCKBACK, self:combatTalentWeaponDamage(t, 1.1, 1.6), true) + end + end) + game:playSoundNear(self, "talents/breath") + + if core.shader.active(4) then + local bx, by = self:attachementSpot("back", true) + self:addParticles(Particles.new("shader_wings", 1, {life=18, x=bx, y=by, fade=-0.006, deploy_speed=14})) + end return true end, info = function(self, t) - local radius = self:getTalentRadius(t) - return ([[You let out a powerful roar that sends your foes into utter confusion for 3 turns in a radius of %d. - The sound wave is so strong, your foes also take %0.2f physical damage. - The damage improves with your Strength. - Each point in fire drake talents also increases your fire resistance by 1%%.]]):format(radius, self:combatTalentStatDamage(t, "str", 40, 400)) + local damage = t.getDamage(self, t) + return ([[You summon a powerful gust of wind, knocking back your foes within a radius of %d up to 3 tiles away and damaging them for %d%% weapon damage. + Every level in Wing Buffet additionally raises your Physical Power and Accuracy by 4, passively. + Each point in fire drake talents also increases your fire resistance by 1%%.]]):format(self:getTalentRadius(t),damage*100) end, } newTalent{ - name = "Wing Buffet", + name = "Bellowing Roar", type = {"wild-gift/fire-drake", 2}, require = gifts_req2, points = 5, random_ego = "attack", - equilibrium = 7, - cooldown = 10, + message = "@Source@ roars!", + equilibrium = 8, + cooldown = 20, range = 0, on_learn = function(self, t) self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) + 1 end, on_unlearn = function(self, t) self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) - 1 end, - radius = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9)) end, - direct_hit = true, - tactical = { DEFEND = { knockback = 2 }, ESCAPE = { knockback = 2 } }, - requires_target = true, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, target = function(self, t) - return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=false, talent=t} end, + tactical = { DEFEND = 1, DISABLE = { confusion = 3 } }, action = function(self, t) local tg = self:getTalentTarget(t) - local x, y = self:getTarget(tg) - if not x or not y then return nil end - self:project(tg, x, y, DamageType.PHYSKNOCKBACK, {dam=self:mindCrit(self:combatTalentStatDamage(t, "str", 15, 90)), dist=4}) - game:playSoundNear(self, "talents/breath") - - if core.shader.active(4) then - local bx, by = self:attachementSpot("back", true) - self:addParticles(Particles.new("shader_wings", 1, {life=18, x=bx, y=by, fade=-0.006, deploy_speed=14})) - end + self:project(tg, self.x, self.y, DamageType.PHYSICAL, self:mindCrit(self:combatTalentStatDamage(t, "str", 30, 380))) + self:project(tg, self.x, self.y, DamageType.CONFUSION, { + dur=3, + dam=20 + 6 * self:getTalentLevel(t), + power_check=function() return self:combatPhysicalpower() end, + resist_check=self.combatPhysicalResist, + }) + game.level.map:particleEmitter(self.x, self.y, self:getTalentRadius(t), "shout", {additive=true, life=10, size=3, distorion_factor=0.5, radius=self:getTalentRadius(t), nb_circles=8, rm=0.8, rM=1, gm=0, gM=0, bm=0.1, bM=0.2, am=0.4, aM=0.6}) return true end, info = function(self, t) - return ([[You summon a powerful gust of wind, knocking back your foes within a radius of %d up to 4 tiles away and damaging them for %d. - The damage will increase with your Strength. - Each point in fire drake talents also increases your fire resistance by 1%%.]]):format(self:getTalentRadius(t), self:combatTalentStatDamage(t, "str", 15, 90)) + local radius = self:getTalentRadius(t) + return ([[You let out a powerful roar that sends your foes into utter confusion for 3 turns in a radius of %d. + The sound wave is so strong, your foes also take %0.2f physical damage. + The damage improves with your Strength. + Each point in fire drake talents also increases your fire resistance by 1%%.]]):format(radius, self:combatTalentStatDamage(t, "str", 30, 380)) end, } @@ -99,26 +115,26 @@ newTalent{ require = gifts_req3, points = 5, random_ego = "attack", - equilibrium = 10, - cooldown = 35, + equilibrium = 6, + cooldown = 20, tactical = { ATTACKAREA = { FIRE = 2 } }, range = 10, - radius = 2, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 2, 5)) end, direct_hit = true, requires_target = true, on_learn = function(self, t) self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) + 1 end, on_unlearn = function(self, t) self.resists[DamageType.FIRE] = (self.resists[DamageType.FIRE] or 0) - 1 end, target = function(self, t) - return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} + return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=false} end, getDamage = function(self, t) - return self:combatTalentStatDamage(t, "wil", 15, 120) + return self:combatTalentMindDamage(t, 15, 60) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9)) end, action = function(self, t) local duration = t.getDuration(self, t) local radius = self:getTalentRadius(t) - local dam = self:mindCrit(t.getDamage(self, t)) + local damage = self:mindCrit(t.getDamage(self, t)) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end @@ -126,11 +142,11 @@ newTalent{ -- Add a lasting map effect game.level.map:addEffect(self, x, y, duration, - DamageType.FIRE, dam, + DamageType.FIRE_DRAIN, {dam=damage, healfactor=0.1}, radius, 5, nil, {type="inferno"}, - nil, true + nil, false ) game:playSoundNear(self, "talents/devouringflame") return true @@ -140,7 +156,8 @@ newTalent{ local radius = self:getTalentRadius(t) local duration = t.getDuration(self, t) return ([[Spit a cloud of flames, doing %0.2f fire damage in a radius of %d each turn for %d turns. - The damage will increase with your Willpower, and can critical. + The flames will ignore the caster, and will drain 10%% of the damage dealt as the flames consume enemies life force and transfer it to the user. + The damage will increase with your Mindpower, and can critical. Each point in fire drake talents also increases your fire resistance by 1%%.]]):format(damDesc(self, DamageType.FIRE, dam), radius, duration) end, } @@ -168,7 +185,7 @@ newTalent{ local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.FIREBURN, {dam=self:mindCrit(self:combatTalentStatDamage(t, "str", 30, 550)), dur=3, initial=70}) + self:project(tg, x, y, DamageType.FIRE_STUN, {dam=self:mindCrit(self:combatTalentStatDamage(t, "str", 30, 650)), dur=3, initial=70}) game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_fire", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) if core.shader.active(4) then @@ -179,8 +196,8 @@ newTalent{ return true end, info = function(self, t) - return ([[You breathe fire in a frontal cone of radius %d. Any target caught in the area will take %0.2f fire damage over 3 turns. + return ([[You breathe fire in a frontal cone of radius %d. Any target caught in the area will take %0.2f fire damage over 3 turns, and has a 25%% chance of being Flameshocked for 3 turns, stunning them. The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. - Each point in fire drake talents also increases your fire resistance by 1%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.FIRE, self:combatTalentStatDamage(t, "str", 30, 550))) + Each point in fire drake talents also increases your fire resistance by 1%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.FIRE, self:combatTalentStatDamage(t, "str", 30, 650))) end, } diff --git a/game/modules/tome/data/talents/gifts/gifts.lua b/game/modules/tome/data/talents/gifts/gifts.lua index 351e7c9770099fcf4d9649e0ec3f03c97ecd3d92..3366a127a60865953817192f6ce764ef9cb5f78f 100644 --- a/game/modules/tome/data/talents/gifts/gifts.lua +++ b/game/modules/tome/data/talents/gifts/gifts.lua @@ -20,7 +20,7 @@ -- Wild Gifts newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/call", name = "call of the wild", generic = true, description = "Be at one with nature." } newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/harmony", name = "harmony", generic = true, description = "Nature heals and cleans you." } -newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/antimagic", name = "antimagic", generic = true, description = "The way to combat magic, or even nullify it." } +newTalentType{ allow_random=true, is_mind=true, is_nature=true, is_antimagic=true, type="wild-gift/antimagic", name = "antimagic", generic = true, description = "The way to combat magic, or even nullify it." } newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/summon-melee", name = "summoning (melee)", description = "The art of calling creatures adept in melee combat to your aid." } newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/summon-distance", name = "summoning (distance)", description = "The art of calling creatures adept in elemental destruction to your aid." } newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/summon-utility", name = "summoning (utility)", description = "The art of calling versatile creatures to your aid." } @@ -41,7 +41,7 @@ newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/ newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/malleable-body", name = "malleable body", description = "Your body's anatomy is starting to blur." } newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/oozing-blades", name = "oozing blades", description = "You channel ooze through your psiblades." } newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/corrosive-blades", name = "corrosive blades", description = "You channel acid through your psiblades." } -newTalentType{ allow_random=true, is_mind=true, is_nature=true, type="wild-gift/eyals-fury", name = "eyal's fury", description = "Unleash nature's fury against foes around you." } +newTalentType{ allow_random=true, is_mind=true, is_nature=true, is_antimagic=true, type="wild-gift/eyals-fury", name = "eyal's fury", description = "Unleash nature's fury against foes around you." } -- Generic requires for gifts based on talent level gifts_req1 = { diff --git a/game/modules/tome/data/talents/gifts/higher-draconic.lua b/game/modules/tome/data/talents/gifts/higher-draconic.lua index d47e0ea5b4bbd7f2c3392cee8382a98b69b46f9f..0edb7c37c892307f2a781cf81bef784519eb17d0 100644 --- a/game/modules/tome/data/talents/gifts/higher-draconic.lua +++ b/game/modules/tome/data/talents/gifts/higher-draconic.lua @@ -23,23 +23,18 @@ newTalent{ require = gifts_req_high1, points = 5, random_ego = "attack", - equilibrium = 20, - cooldown = 16, + equilibrium = 10, + cooldown = 12, range = 1, tactical = { ATTACK = { PHYSICAL = 1, COLD = 1, FIRE = 1, LIGHTNING = 1, ACID = 1 } }, requires_target = true, - getWeaponDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.2, 2.0) end, + getWeaponDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.6, 2.3) end, getBurstDamage = function(self, t) return self:combatTalentMindDamage(t, 20, 230) end, + getPassiveSpeed = function(self, t) return (self:combatTalentScale(t, 2, 10, 0.5)/100) end, radius = function(self, t) return math.floor(self:combatTalentScale(t, 1.5, 3.5)) end, - on_learn = function(self, t) - self.combat_physresist = self.combat_physresist + 1 - self.combat_spellresist = self.combat_spellresist + 1 - self.combat_mentalresist = self.combat_mentalresist + 1 - end, - on_unlearn = function(self, t) - self.combat_physresist = self.combat_physresist - 1 - self.combat_spellresist = self.combat_spellresist - 1 - self.combat_mentalresist = self.combat_mentalresist - 1 + passives = function(self, t, p) + self:talentTemporaryValue(p, "combat_physspeed", t.getPassiveSpeed(self, t)) + self:talentTemporaryValue(p, "combat_mindspeed", t.getPassiveSpeed(self, t)) end, action = function(self, t) @@ -59,13 +54,13 @@ newTalent{ elseif elem == "cold" then self:attackTarget(target, DamageType.ICE, t.getWeaponDamage(self, t), true) local tg = {type="ball", range=1, selffire=false, radius=self:getTalentRadius(t), talent=t} - local grids = self:project(tg, x, y, DamageType.ICE, self:mindCrit(t.getBurstDamage(self, t))) + local grids = self:project(tg, x, y, DamageType.ICE_SLOW, self:mindCrit(t.getBurstDamage(self, t))) game.level.map:particleEmitter(x, y, tg.radius, "ball_ice", {radius=tg.radius, grids=grids, tx=x, ty=y, max_alpha=80}) game:playSoundNear(self, "talents/flame") elseif elem == "fire" then self:attackTarget(target, DamageType.FIREBURN, t.getWeaponDamage(self, t), true) local tg = {type="ball", range=1, selffire=false, radius=self:getTalentRadius(t), talent=t} - local grids = self:project(tg, x, y, DamageType.FIREBURN, self:mindCrit(t.getBurstDamage(self, t))) + local grids = self:project(tg, x, y, DamageType.FIRE_STUN, self:mindCrit(t.getBurstDamage(self, t))) game.level.map:particleEmitter(x, y, tg.radius, "ball_fire", {radius=tg.radius, grids=grids, tx=x, ty=y, max_alpha=80}) game:playSoundNear(self, "talents/flame") elseif elem == "lightning" then @@ -86,10 +81,11 @@ newTalent{ info = function(self, t) local burstdamage = t.getBurstDamage(self, t) local radius = self:getTalentRadius(t) + local speed = t.getPassiveSpeed(self, t) return ([[Unleash raw, chaotic elemental damage upon your enemy. - You strike your enemy for %d%% weapon damage in one of blinding sand, disarming acid, freezing ice, stunning lightning or burning flames, with equal odds. + You strike your enemy for %d%% weapon damage in one of blinding sand, disarming acid, freezing and slowing ice, dazing lightning or stunning flames, with equal odds. Additionally, you will cause a burst that deals %0.2f of that damage to enemies in radius %d, regardless of if you hit with the blow. - Each point in Prismatic Slash increase your Physical, Spell and Mind Saves by 1.]]):format(100 * self:combatTalentWeaponDamage(t, 1.2, 2.0), burstdamage, radius) + Levels in Prismatic Slash increase your Physical and Mental attack speeds by %d%%.]]):format(100 * self:combatTalentWeaponDamage(t, 1.2, 2.0), burstdamage, radius, 100*speed) end, } @@ -105,12 +101,17 @@ newTalent{ tactical = { ATTACKAREA = { poison = 2 } }, range = 0, radius = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9)) end, - direct_hit = true, requires_target = true, - getDamage = function(self, t) return self:combatTalentStatDamage(t, "str", 60, 650) end, - getEffect = function(self, t) return self:combatTalentLimit(t, 100, 18, 50) end, -- Limit < 100% - on_learn = function(self, t) self.resists[DamageType.NATURE] = (self.resists[DamageType.NATURE] or 0) + 2 end, - on_unlearn = function(self, t) self.resists[DamageType.NATURE] = (self.resists[DamageType.NATURE] or 0) - 2 end, + getDamage = function(self, t) return self:combatTalentStatDamage(t, "str", 60, 750) end, + getEffect = function(self, t) return math.ceil(self:combatTalentLimit(t, 50, 10, 20)) end, + on_learn = function(self, t) + self.resists[DamageType.NATURE] = (self.resists[DamageType.NATURE] or 0) + 3 + self.inc_damage[DamageType.NATURE] = (self.inc_damage[DamageType.NATURE] or 0) + 4 + end, + on_unlearn = function(self, t) + self.resists[DamageType.NATURE] = (self.resists[DamageType.NATURE] or 0) - 3 + self.inc_damage[DamageType.NATURE] = (self.inc_damage[DamageType.NATURE] or 0) - 4 + end, target = function(self, t) return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, @@ -118,7 +119,14 @@ newTalent{ local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.INSIDIOUS_POISON, {dam=self:mindCrit(t.getDamage(self,t)), dur=6, heal_factor=t.getEffect(self,t)}) + local dam = self:mindCrit(t.getDamage(self, t)) + self:project(tg, x, y, function(px, py) + local target = game.level.map(px, py, Map.ACTOR) + if target and target:canBe("poison") then + target:setEffect(self.EFF_CRIPPLING_POISON, 6, {src=self, power=dam/6, fail=math.ceil(self:combatTalentLimit(t, 100, 10, 20))}) + end + end) + game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_slime", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/breath") @@ -130,10 +138,10 @@ newTalent{ end, info = function(self, t) local effect = t.getEffect(self, t) - return ([[You breathe insidious poison in a frontal cone of radius %d. Any target caught in the area will take %0.2f nature damage each turn for 6 turns. - The poison also reduces the healing of enemies poisoned by %d%% while it is in effect. + return ([[You breathe crippling poison in a frontal cone of radius %d. Any target caught in the area will take %0.2f nature damage each turn for 6 turns. + The poison also gives enemies a %d%% chance to fail actions more complicated than basic attacks and movement, while it is in effect. The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. - Each point in Venomous Breath also increases your nature resistance by 2%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.NATURE, t.getDamage(self,t)/6), effect) + Each point in Venomous Breath also increases your nature resistance by 3%%, and your nature damage by 4%%.]] ):format(self:getTalentRadius(t), damDesc(self, DamageType.NATURE, t.getDamage(self,t)/6), effect) end, } @@ -145,6 +153,7 @@ newTalent{ mode = "passive", resistKnockback = function(self, t) return self:combatTalentLimit(t, 1, .17, .5) end, -- Limit < 100% resistBlindStun = function(self, t) return self:combatTalentLimit(t, 1, .07, .25) end, -- Limit < 100% + CDreduce = function(self, t) return math.floor(self:combatTalentLimit(t, 8, 1, 6)) end, -- Limit < 8 on_learn = function(self, t) self.inc_stats[self.STAT_CUN] = self.inc_stats[self.STAT_CUN] + 2 end, @@ -152,14 +161,17 @@ newTalent{ self.inc_stats[self.STAT_CUN] = self.inc_stats[self.STAT_CUN] - 2 end, passives = function(self, t, p) + local cdr = t.CDreduce(self, t) self:talentTemporaryValue(p, "knockback_immune", t.resistKnockback(self, t)) self:talentTemporaryValue(p, "stun_immune", t.resistBlindStun(self, t)) self:talentTemporaryValue(p, "blind_immune", t.resistBlindStun(self, t)) + self:talentTemporaryValue(p, "talent_cd_reduction", +{[Talents.T_VENOMOUS_BREATH]=cdr, [Talents.T_ICE_BREATH]=cdr, [Talents.T_FIRE_BREATH]=cdr, [Talents.T_LIGHTNING_BREATH]=cdr, [Talents.T_CORROSIVE_BREATH]=cdr, [Talents.T_SAND_BREATH]=cdr}) end, info = function(self, t) return ([[You have the mental prowess of a Wyrm. - Your Cunning is increased by %d. - You gain %d%% knockback resistance, and your blindness and stun resistances are increased by %d%%.]]):format(2*self:getTalentLevelRaw(t), 100*t.resistKnockback(self, t), 100*t.resistBlindStun(self, t)) + Your Cunning is increased by %d, and your breath attack cooldowns are reduced by %d. + You gain %d%% knockback resistance, and your blindness and stun resistances are increased by %d%%.]]):format(2*self:getTalentLevelRaw(t), t.CDreduce(self, t), 100*t.resistKnockback(self, t), 100*t.resistBlindStun(self, t)) end, } diff --git a/game/modules/tome/data/talents/gifts/mindstar-mastery.lua b/game/modules/tome/data/talents/gifts/mindstar-mastery.lua index 979ed219ff2a575e1eb71aecbfebdeaa1ac2e9f5..d8112133abb5270f5751aab47a73802aedf1db3c 100644 --- a/game/modules/tome/data/talents/gifts/mindstar-mastery.lua +++ b/game/modules/tome/data/talents/gifts/mindstar-mastery.lua @@ -46,6 +46,7 @@ newTalent{ for i, o in ipairs(self:getInven("MAINHAND") or {}) do self:onTakeoff(o, self.INVEN_MAINHAND, true) self:onWear(o, self.INVEN_MAINHAND, true) end for i, o in ipairs(self:getInven("OFFHAND") or {}) do self:onTakeoff(o, self.INVEN_OFFHAND, true) self:onWear(o, self.INVEN_OFFHAND, true) end + for i, o in ipairs(self:getInven("PSIONIC_FOCUS") or {}) do self:onTakeoff(o, self.INVEN_PSIONIC_FOCUS, true) self:onWear(o, self.INVEN_PSIONIC_FOCUS, true) end self:updateModdableTile() return r @@ -55,6 +56,7 @@ newTalent{ for i, o in ipairs(self:getInven("MAINHAND") or {}) do self:onTakeoff(o, self.INVEN_MAINHAND, true) self:checkMindstar(o) self:onWear(o, self.INVEN_MAINHAND, true) end for i, o in ipairs(self:getInven("OFFHAND") or {}) do self:onTakeoff(o, self.INVEN_OFFHAND, true) self:checkMindstar(o) self:onWear(o, self.INVEN_OFFHAND, true) end + for i, o in ipairs(self:getInven("PSIONIC_FOCUS") or {}) do self:onTakeoff(o, self.INVEN_PSIONIC_FOCUS, true) self:checkMindstar(o) self:onWear(o, self.INVEN_PSIONIC_FOCUS, true) end self:updateModdableTile() return true diff --git a/game/modules/tome/data/talents/gifts/sand-drake.lua b/game/modules/tome/data/talents/gifts/sand-drake.lua index b1b3a6c61b732a233b4648de2a6dfb40e9059c79..43ccd4120e3a195b1b7ab5e940695a062ca32094 100644 --- a/game/modules/tome/data/talents/gifts/sand-drake.lua +++ b/game/modules/tome/data/talents/gifts/sand-drake.lua @@ -23,24 +23,29 @@ newTalent{ require = gifts_req1, points = 5, equilibrium = 4, - cooldown = 10, + cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 4, 10, 7)) end, range = 1, no_message = true, - tactical = { ATTACK = { NATURE = 0.5 }, EQUILIBRIUM = 0.5}, + tactical = { ATTACK = { weapon = 1 }, EQUILIBRIUM = 0.5}, requires_target = true, no_npc_use = true, maxSwallow = function(self, t, target) return -- Limit < 50% self:combatLimit(self:getTalentLevel(t)*(self.size_category or 3)/(target.size_category or 3), 50, 13, 1, 25, 5) end, + getPassiveCrit = function(self, t) return self:combatTalentScale(t, 2, 10, 0.5) end, on_learn = function(self, t) self.resists[DamageType.PHYSICAL] = (self.resists[DamageType.PHYSICAL] or 0) + 0.5 end, on_unlearn = function(self, t) self.resists[DamageType.PHYSICAL] = (self.resists[DamageType.PHYSICAL] or 0) - 0.5 end, + passives = function(self, t, p) + self:talentTemporaryValue(p, "combat_physcrit", t.getPassiveCrit(self, t)) + self:talentTemporaryValue(p, "combat_mindcrit", t.getPassiveCrit(self, t)) + end, action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t)} local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if core.fov.distance(self.x, self.y, x, y) > 1 then return nil end self:logCombat(target, "#Source# tries to swallow #Target#!") - local hit = self:attackTarget(target, DamageType.NATURE, self:combatTalentWeaponDamage(t, 1, 1.5), true) + local hit = self:attackTarget(target, DamageType.NATURE, self:combatTalentWeaponDamage(t, 1.6, 2.5), true) if not hit then return true end if (target.life * 100 / target.max_life > t.maxSwallow(self, t, target)) and not target.dead then @@ -64,11 +69,12 @@ newTalent{ return true end, info = function(self, t) - return ([[Attack the target for %d%% nature weapon damage. + return ([[Attack the target for %d%% Nature weapon damage. If the attack brings your target below %d%% life or kills it, you can try to swallow it, killing it automatically and regaining life and equilibrium depending on its level. The chance to swallow depends on your talent level and the relative size of the target. + Levels in Swallow additionally raises your Physical and Mental critical rate by %d%%, passively. Each point in sand drake talents also increases your physical resistance by 0.5%%.]]): - format(100 * self:combatTalentWeaponDamage(t, 1, 1.5), t.maxSwallow(self, t, self)) + format(100 * self:combatTalentWeaponDamage(t, 1.6, 2.5), t.maxSwallow(self, t, self), t.getPassiveCrit(self, t)) end, } @@ -80,30 +86,32 @@ newTalent{ random_ego = "attack", message = "@Source@ shakes the ground!", equilibrium = 4, - cooldown = 30, + cooldown = 20, tactical = { ATTACKAREA = { PHYSICAL = 2 }, DISABLE = { knockback = 2 } }, - range = 10, + range = 1, on_learn = function(self, t) self.resists[DamageType.PHYSICAL] = (self.resists[DamageType.PHYSICAL] or 0) + 0.5 end, on_unlearn = function(self, t) self.resists[DamageType.PHYSICAL] = (self.resists[DamageType.PHYSICAL] or 0) - 0.5 end, radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end, no_npc_use = true, - getDamage = function(self, t) - return self:combatDamage() * 0.8 - end, + getDamage = function(self, t) return self:combatTalentWeaponDamage(t, 1.3, 2.1) end, action = function(self, t) local tg = {type="ball", range=0, selffire=false, radius=self:getTalentRadius(t), talent=t, no_restrict=true} - self:project(tg, self.x, self.y, DamageType.PHYSKNOCKBACK, {dam=self:mindCrit(t.getDamage(self, t)), dist=4}) + self:project(tg, self.x, self.y, function(px, py, tg, self) + local target = game.level.map(px, py, Map.ACTOR) + if target and target ~= self then + local hit = self:attackTarget(target, DamageType.PHYSKNOCKBACK, self:combatTalentWeaponDamage(t, 1.3, 2.1), true) + end + end) self:doQuake(tg, self.x, self.y) return true end, info = function(self, t) local radius = self:getTalentRadius(t) local dam = t.getDamage(self, t) - return ([[You slam your foot onto the ground, shaking the area around you in a radius of %d. - Creatures caught by the quake will be damaged for %d and knocked back up to 4 tiles away. - The terrain will also be moved around within the quake's radius. - The damage will increase with your Strength. - Each point in sand drake talents also increases your physical resistance by 0.5%%.]]):format(radius, dam) + return ([[You slam the ground, shaking the area around you in a radius of %d. + Creatures caught by the quake will be damaged for %d%% weapon damage, and knocked back up to 3 tiles away. + The terrain will also be moved around within the radius, and the user will be shifted to a random square within the radius. + Each point in sand drake talents also increases your physical resistance by 0.5%%.]]):format(radius, dam * 100) end, } @@ -112,20 +120,24 @@ newTalent{ type = {"wild-gift/sand-drake", 3}, require = gifts_req3, points = 5, - equilibrium = 50, - cooldown = 30, + equilibrium = 15, + cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 10, 40, 15)) end, range = 10, + no_energy = function(self, t) if self:getTalentLevel(t) >= 5 then return true else return false end end, tactical = { CLOSEIN = 0.5, ESCAPE = 0.5 }, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 8, 20, 0.5, 0, 2)) end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7, 0.5, 0, 2)) end, + getPenetration = function(self, t) return 10 + self:combatTalentMindDamage(t, 15, 30) end, on_learn = function(self, t) self.resists[DamageType.PHYSICAL] = (self.resists[DamageType.PHYSICAL] or 0) + 0.5 end, on_unlearn = function(self, t) self.resists[DamageType.PHYSICAL] = (self.resists[DamageType.PHYSICAL] or 0) - 0.5 end, action = function(self, t) - self:setEffect(self.EFF_BURROW, t.getDuration(self, t), {}) + self:setEffect(self.EFF_BURROW, t.getDuration(self, t), {power=t.getPenetration(self, t)}) return true end, info = function(self, t) - return ([[Allows you to burrow into walls for %d turns. - Each point in sand drake talents also increases your physical resistance by 0.5%%.]]):format(t.getDuration(self, t)) + return ([[Allows you to burrow into earthen walls for %d turns. + Your powerful digging abilities also allow you to exploit and smash through enemy defensive weaknesses; You ignore %d of target armor and %d%% of enemy physical damage resistance while this is in effect. + At Talent Level 5, this talent can be used instantly, and the cooldown will reduce with levels. + Each point in sand drake talents also increases your physical resistance by 0.5%%.]]):format(t.getDuration(self, t), t.getPenetration(self, t), t.getPenetration(self, t) / 2) end, } @@ -149,9 +161,9 @@ newTalent{ return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, getDamage = function(self, t) - return self:combatTalentStatDamage(t, "str", 30, 400) + return self:combatTalentStatDamage(t, "str", 30, 480) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 4)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) @@ -169,7 +181,7 @@ newTalent{ info = function(self, t) local damage = t.getDamage(self, t) local duration = t.getDuration(self, t) - return ([[You breathe sand in a frontal cone of radius %d. Any target caught in the area will take %0.2f physical damage, and be blinded for %d turns. + return ([[You breathe sand in a frontal cone of radius %d. Any target caught in the area will take %0.2f physical damage, and will be blinded for %d turns. The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. Each point in sand drake talents also increases your physical resistance by 0.5%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.PHYSICAL, damage), duration) end, diff --git a/game/modules/tome/data/talents/gifts/storm-drake.lua b/game/modules/tome/data/talents/gifts/storm-drake.lua index e3b58b2613057597b8b69b6c3388908b6c6fb252..e00da623cbfd79f2a718051c1369d3efb82f959c 100644 --- a/game/modules/tome/data/talents/gifts/storm-drake.lua +++ b/game/modules/tome/data/talents/gifts/storm-drake.lua @@ -25,15 +25,19 @@ newTalent{ require = gifts_req1, points = 5, equilibrium = 10, - cooldown = 26, + cooldown = 25, range = 10, tactical = { CLOSEIN = 2, ESCAPE = 2 }, requires_target = true, no_energy = true, + getPassiveSpeed = function(self, t) return self:combatTalentScale(t, 0.08, 0.4, 0.7) end, getSpeed = function(self, t) return self:combatTalentScale(t, 470, 750, 0.75) end, - getDuration = function(self, t) return math.ceil(self:combatTalentScale(t, 1.1, 2.6)) end, + getDuration = function(self, t) return math.ceil(self:combatTalentScale(t, 1.1, 3.1)) end, on_learn = function(self, t) self.resists[DamageType.LIGHTNING] = (self.resists[DamageType.LIGHTNING] or 0) + 1 end, on_unlearn = function(self, t) self.resists[DamageType.LIGHTNING] = (self.resists[DamageType.LIGHTNING] or 0) - 1 end, + passives = function(self, t, p) + self:talentTemporaryValue(p, "movement_speed", t.getPassiveSpeed(self, t)) + end, on_pre_use = function(self, t) return not self:attr("never_move") end, action = function(self, t) self:setEffect(self.EFF_LIGHTNING_SPEED, self:mindCrit(t.getDuration(self, t)), {power=t.getSpeed(self, t)}) @@ -44,7 +48,8 @@ newTalent{ Also provides 30%% physical damage resistance and 100%% lightning resistance. Any actions other than moving will stop this effect. Note: since you will be moving very fast, game turns will pass very slowly. - Each point in storm drake talents also increases your lightning resistance by 1%%.]]):format(t.getSpeed(self, t), t.getDuration(self, t)) + Levels in Lightning Speed additionally raises your Movement Speed by %d%%, passively. + Each point in storm drake talents also increases your lightning resistance by 1%%.]]):format(t.getSpeed(self, t), t.getDuration(self, t), t.getPassiveSpeed(self, t)*100) end, } @@ -56,7 +61,7 @@ newTalent{ equilibrium = 20, cooldown = 20, range = 0, - radius = 1, + radius = function(self, t) return math.floor(self:combatTalentScale(t, 1, 6)) end, tactical = { ATTACKAREA = { instakill = 5 } }, requires_target = true, on_learn = function(self, t) self.resists[DamageType.LIGHTNING] = (self.resists[DamageType.LIGHTNING] or 0) + 1 end, @@ -67,8 +72,12 @@ newTalent{ getPercent = function(self, t) return self:combatLimit(self:combatTalentMindDamage(t, 10, 45), 90, 0, 0, 31, 31) -- Limit to <90% end, + getDamage = function(self, t) + return self:combatTalentMindDamage(t, 20, 160) + end, action = function(self, t) local tg = self:getTalentTarget(t) + local litdam = self:mindCrit(t.getDamage(self, t)) self:project(tg, self.x, self.y, function(px, py) local target = game.level.map(px, py, Map.ACTOR) if not target then return end @@ -80,7 +89,7 @@ newTalent{ game.logSeen(target, "%s is caught in the static field!", target.name:capitalize()) local perc = t.getPercent(self, t) - if target.rank >= 5 then perc = perc / 3 + if target.rank >= 5 then perc = perc / 2.5 elseif target.rank >= 3.5 then perc = perc / 2 elseif target.rank >= 3 then perc = perc / 1.5 end @@ -88,6 +97,7 @@ newTalent{ local dam = target.life * perc / 100 if target.life - dam < 0 then dam = target.life end target:takeHit(dam, self) + self:project({type="hit", talent=t},target.x,target.y,DamageType.LIGHTNING,litdam) game:delayedLogDamage(self, target, dam, ("#PURPLE#%d STATIC#LAST#"):format(math.ceil(dam))) end, nil, {type="lightning_explosion"}) @@ -96,9 +106,11 @@ newTalent{ end, info = function(self, t) local percent = t.getPercent(self, t) - return ([[Generate an electrical field around you in a radius of 1. Any creature caught inside will lose up to %0.1f%% of its current life (effect decreased for higher creature ranks). - This effect cannot kill creatures. Life loss will increase with your Mindpower. - Each point in storm drake talents also increases your lightning resistance by 1%%.]]):format(percent) + local litdam = t.getDamage(self, t) + return ([[Generate an electrical field around you in a radius of %d. Any creature caught inside will lose up to %0.1f%% of its current life (%0.1f%% if the target is Elite or Rare, %0.1f%% if the target is a Unique or Boss, and %0.1f%% if they are an Elite Boss.). This life drain is irresistable, but can be saved against with physical save. + Additionally, it will deal %0.2f lightning damage afterwards, regardless of target rank. + Current life loss and lightning damage will increase with your Mindpower, and the lightning damage element can critically hit with mental critical chances. + Each point in storm drake talents also increases your lightning resistance by 1%%.]]):format(self:getTalentRadius(t), percent, percent/1.5, percent/2, percent/2.5, damDesc(self, DamageType.LIGHTNING, litdam)) end, } @@ -109,14 +121,16 @@ newTalent{ points = 5, equilibrium = 14, cooldown = 15, - proj_speed = 2, -- This is purely indicative + proj_speed = 4, -- This is purely indicative tactical = { ATTACK = { LIGHTNING = 2 }, DISABLE = { stun = 2 } }, - range = function(self, t) return math.floor(self:combatTalentScale(t, 5, 9)) end, + range = function(self, t) return math.floor(self:combatTalentScale(t, 3, 6)) end, requires_target = true, on_learn = function(self, t) self.resists[DamageType.LIGHTNING] = (self.resists[DamageType.LIGHTNING] or 0) + 1 end, on_unlearn = function(self, t) self.resists[DamageType.LIGHTNING] = (self.resists[DamageType.LIGHTNING] or 0) - 1 end, + getRadius = function(self, t) return math.floor(self:combatTalentScale(t, 2, 4, 0.5, 0, 0, true)) end, + getStunDuration = function(self, t) return self:combatTalentScale(t, 3, 6, 0.5, 0, 0, true) end, action = function(self, t) - local tg = {type="hit", range=self:getTalentRange(t), talent=t} + local tg = {type="hit", range=self:getTalentRange(t), selffire=false, talent=t} local x, y = self:getTarget(tg) if not x or not y then return nil end local _ _, x, y = self:canProject(tg, x, y) @@ -125,11 +139,13 @@ newTalent{ local movedam = self:mindCrit(self:combatTalentMindDamage(t, 10, 110)) local dam = self:mindCrit(self:combatTalentMindDamage(t, 15, 190)) + local rad = t.getRadius(self, t) + local dur = t.getStunDuration(self, t) local proj = require("mod.class.Projectile"):makeHoming( self, {particle="bolt_lightning", trail="lightningtrail"}, - {speed=2, name="Tornado", dam=dam, movedam=movedam}, + {speed=4, name="Tornado", dam=dam, movedam=movedam}, target, self:getTalentRange(t), function(self, src) @@ -138,10 +154,10 @@ newTalent{ end, function(self, src, target) local DT = require("engine.DamageType") - src:project({type="ball", radius=1, x=self.x, y=self.y}, self.x, self.y, DT.LIGHTNING, self.def.dam) - src:project({type="ball", radius=1, x=self.x, y=self.y}, self.x, self.y, DT.MINDKNOCKBACK, self.def.dam) + src:project({type="ball", radius=rad, selffire=false, x=self.x, y=self.y}, self.x, self.y, DT.LIGHTNING, self.def.dam) + src:project({type="ball", radius=rad, selffire=false, x=self.x, y=self.y}, self.x, self.y, DT.MINDKNOCKBACK, self.def.dam) if target:canBe("stun") then - target:setEffect(target.EFF_STUNNED, 4, {apply_power=src:combatMindpower()}) + target:setEffect(target.EFF_STUNNED, dur, {apply_power=src:combatMindpower()}) else game.logSeen(target, "%s resists the tornado!", target.name:capitalize()) end @@ -164,15 +180,19 @@ newTalent{ return true end, info = function(self, t) + local rad = t.getRadius(self, t) + local duration = t.getStunDuration(self, t) return ([[Summons a tornado that moves slowly toward its target, following it if it changes position. Any foe caught in its path takes %0.2f lightning damage. - When it reaches its target, it explodes in a radius of 1 for %0.2f lightning damage and %0.2f physical damage. All affected creatures will be knocked back, and the targeted creature will be stunned for 4 turns. + When it reaches its target, it explodes in a radius of %d for %0.2f lightning damage and %0.2f physical damage. All affected creatures will be knocked back, and the targeted creature will be stunned for %d turns. The blast will ignore the talent user. The tornado will last for %d turns, or until it reaches its target. - Damage will increase with your Mindpower. + Damage will increase with your Mindpower, and the stun chance is based on your Mindpower vs target Physical Save. Each point in storm drake talents also increases your lightning resistance by 1%%.]]):format( damDesc(self, DamageType.LIGHTNING, self:combatTalentMindDamage(t, 10, 110)), + rad, damDesc(self, DamageType.LIGHTNING, self:combatTalentMindDamage(t, 15, 190)), damDesc(self, DamageType.PHYSICAL, self:combatTalentMindDamage(t, 15, 190)), + duration, self:getTalentRange(t) ) end, @@ -198,14 +218,17 @@ newTalent{ return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, getDamage = function(self, t) - return self:combatTalentStatDamage(t, "str", 30, 500) + return self:combatTalentStatDamage(t, "str", 30, 670) + end, + getDaze = function(self, t) + return 20+self:combatTalentMindDamage(t, 10, 30) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end local dam = self:mindCrit(t.getDamage(self, t)) - self:project(tg, x, y, DamageType.LIGHTNING_DAZE, {power_check=self:combatMindpower(), dam=rng.avg(dam / 3, dam, 3)}) + self:project(tg, x, y, DamageType.LIGHTNING_DAZE, {daze=t.getDaze(self, t), power_check=self:combatMindpower(), dam=rng.avg(dam / 3, dam, 3)}) if core.shader.active() then game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_lightning", {radius=tg.radius, tx=x-self.x, ty=y-self.y}, {type="lightning"}) else game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_lightning", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) @@ -221,12 +244,14 @@ newTalent{ end, info = function(self, t) local damage = t.getDamage(self, t) - return ([[You breathe lightning in a frontal cone of radius %d. Any target caught in the area will take %0.2f to %0.2f lightning damage, and can be dazed for 3 turns. - The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. + local daze = t.getDaze(self, t) + return ([[You breathe lightning in a frontal cone of radius %d. Any target caught in the area will take %0.2f to %0.2f lightning damage, and have a %d%% chance to be dazed for 3 turns. + The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. The Daze chance is based on your Mindpower. Each point in storm drake talents also increases your lightning resistance by 1%%.]]):format( self:getTalentRadius(t), damDesc(self, DamageType.LIGHTNING, damage / 3), - damDesc(self, DamageType.LIGHTNING, damage) + damDesc(self, DamageType.LIGHTNING, damage), + daze ) end, } diff --git a/game/modules/tome/data/talents/gifts/venom-drake.lua b/game/modules/tome/data/talents/gifts/venom-drake.lua index 9ab9c7da345c06aebfab5d3a0dfc98599708c7b3..cd5ad1212f80c92aa2417831db4aa4906b855a4d 100644 --- a/game/modules/tome/data/talents/gifts/venom-drake.lua +++ b/game/modules/tome/data/talents/gifts/venom-drake.lua @@ -28,8 +28,14 @@ newTalent{ cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 3, 6.9, 5.5)) end, -- Limit >=3 tactical = { ATTACK = { ACID = 2 } }, range = function(self, t) return math.floor(self:combatTalentScale(t, 5.5, 7.5)) end, - on_learn = function(self, t) self.resists[DamageType.ACID] = (self.resists[DamageType.ACID] or 0) + 1 end, - on_unlearn = function(self, t) self.resists[DamageType.ACID] = (self.resists[DamageType.ACID] or 0) - 1 end, + on_learn = function(self, t) + self.resists[DamageType.ACID] = (self.resists[DamageType.ACID] or 0) + 1 + self.combat_mindpower = self.combat_mindpower + 4 + end, + on_unlearn = function(self, t) + self.resists[DamageType.ACID] = (self.resists[DamageType.ACID] or 0) - 1 + self.combat_mindpower = self.combat_mindpower - 4 + end, direct_hit = function(self, t) if self:getTalentLevel(t) >= 5 then return true else return false end end, requires_target = true, target = function(self, t) @@ -58,6 +64,7 @@ newTalent{ The target will take %0.2f Mindpower-based acid damage. Enemies struck have a 25%% chance to be Disarmed for three turns, as their weapon is rendered useless by an acid coating. At Talent Level 5, this becomes a piercing line of acid. + Every level in Acidic Spray additionally raises your Mindpower by 4, passively. Each point in acid drake talents also increases your acid resistance by 1%%.]]):format(damDesc(self, DamageType.ACID, damage)) end, } @@ -68,15 +75,15 @@ newTalent{ require = gifts_req2, points = 5, random_ego = "attack", - equilibrium = 15, - cooldown = 25, + equilibrium = 10, + cooldown = 15, tactical = { ATTACKAREA = { ACID = 2 } }, range = 0, on_learn = function(self, t) self.resists[DamageType.ACID] = (self.resists[DamageType.ACID] or 0) + 1 end, on_unlearn = function(self, t) self.resists[DamageType.ACID] = (self.resists[DamageType.ACID] or 0) - 1 end, radius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end, requires_target = true, - getDamage = function(self, t) return self:combatTalentMindDamage(t, 10, 70) end, + getDamage = function(self, t) return self:combatTalentMindDamage(t, 15, 70) end, getDuration = function(self, t) return math.floor(self:combatScale(self:combatMindpower(0.04) + self:getTalentLevel(t)/2, 6, 0, 7.67, 5.67)) end, getCorrodeDur = function(self, t) return math.floor(self:combatTalentScale(t, 2.3, 3.8)) end, getAtk = function(self, t) return self:combatTalentMindDamage(t, 2, 20) end, @@ -175,11 +182,14 @@ newTalent{ target = function(self, t) return {type="cone", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, + getDisarm = function(self, t) + return 20+self:combatTalentMindDamage(t, 10, 30) + end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.ACID_DISARM, self:mindCrit(self:combatTalentStatDamage(t, "str", 30, 420))) + self:project(tg, x, y, DamageType.ACID_DISARM, {dam=self:mindCrit(self:combatTalentStatDamage(t, "str", 30, 520)), chance=t.getDisarm(self, t),}) game.level.map:particleEmitter(self.x, self.y, tg.radius, "breath_acid", {radius=tg.radius, tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/breath") @@ -190,9 +200,10 @@ newTalent{ return true end, info = function(self, t) + local disarm = t.getDisarm(self, t) return ([[You breathe acid in a frontal cone of radius %d. Any target caught in the area will take %0.2f acid damage. - Enemies caught in the acid have a 25%% chance of their weapons becoming useless for three turns. - The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. - Each point in acid drake talents also increases your acid resistance by 1%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.ACID, self:combatTalentStatDamage(t, "str", 30, 420))) + Enemies caught in the acid have a %d%% chance of their weapons becoming useless for three turns. + The damage will increase with your Strength, and the critical chance is based on your Mental crit rate. The Disarm chance is based on your Mindpower. + Each point in acid drake talents also increases your acid resistance by 1%%.]]):format(self:getTalentRadius(t), damDesc(self, DamageType.ACID, self:combatTalentStatDamage(t, "str", 30, 520)), disarm) end, } \ No newline at end of file diff --git a/game/modules/tome/data/talents/misc/inscriptions.lua b/game/modules/tome/data/talents/misc/inscriptions.lua index 742d013f3fe99f1f35407254437aac778a08cf43..1fdda0c562ac260aed2b31db3e5979cbfd669f4c 100644 --- a/game/modules/tome/data/talents/misc/inscriptions.lua +++ b/game/modules/tome/data/talents/misc/inscriptions.lua @@ -181,8 +181,11 @@ newInscription{ end, info = function(self, t) local data = self:getInscriptionData(t.short_name) - local what = table.concat(table.keys(data.what), ", ") - return ([[Activate the infusion to cure yourself of %s effects and reduce all damage taken by %d%% for %d turns.]]):format(what, data.power+data.inc_stat, data.dur) + local what = table.concatNice(table.keys(data.what), ", ", " or ") + + return ([[Activate the infusion to cure yourself of one random %s effect and reduce all damage taken by %d%% for %d turns. + +Also removes cross-tier effects of the affected types for free.]]):format(what, data.power+data.inc_stat, data.dur) end, short_info = function(self, t) local data = self:getInscriptionData(t.short_name) @@ -255,8 +258,11 @@ newInscription{ end, info = function(self, t) local data = self:getInscriptionData(t.short_name) - local what = table.concat(table.keys(data.what), ", ") - return ([[Activate the infusion to cure yourself of %s effects and increase affinity for all damage by %d%% for %d turns.]]):format(what, data.power+data.inc_stat, data.dur) + local what = table.concatNice(table.keys(data.what), ", ", " or ") + + return ([[Activate the infusion to cure yourself of one random %s effect and increase affinity for all damage by %d%% for %d turns. + +Also removes cross-tier effects of the affected types for free.]]):format(what, data.power+data.inc_stat, data.dur) end, short_info = function(self, t) local data = self:getInscriptionData(t.short_name) @@ -381,7 +387,7 @@ newInscription{ end, info = function(self, t) local data = self:getInscriptionData(t.short_name) - return ([[Activate the infusion to spit a bolt of poison doing %0.2f nature damage per turns for 7 turns, and reducing the target's healing received by %d%%. + return ([[Activate the infusion to spit a bolt of poison doing %0.2f nature damage per turn for 7 turns, and reducing the target's healing received by %d%%. The sudden stream of natural forces also strips you of one random detrimental magical effect.]]):format(damDesc(self, DamageType.NATURE, data.power + data.inc_stat) / 7, data.heal_factor) end, short_info = function(self, t) @@ -1006,7 +1012,7 @@ newInscription{ if target:attr("timetravel_immune") then game.logSeen(target, "%s is immune!", target.name:capitalize()) - return + return true end local hit = self:checkHit(self:combatSpellpower(), target:combatSpellResist() + (target:attr("continuum_destabilization") or 0)) @@ -1015,6 +1021,7 @@ newInscription{ self:project(tg, x, y, DamageType.TEMPORAL, self:spellCrit(t.getDamage(self, t))) game.level.map:particleEmitter(x, y, 1, "temporal_thrust") game:playSoundNear(self, "talents/arcane") + self:incParadox(-25) if target.dead or target.player then return true end target:setEffect(target.EFF_CONTINUUM_DESTABILIZATION, 100, {power=self:combatSpellpower(0.3)}) @@ -1038,9 +1045,12 @@ newInscription{ game.nicer_tiles:updateAround(game.level, self.target.x, self.target.y) local mx, my = util.findFreeGrid(self.target.x, self.target.y, 20, true, {[engine.Map.ACTOR]=true}) local old_levelup = self.target.forceLevelup + local old_check = self.target.check self.target.forceLevelup = function() end + self.target.check = function() end game.zone:addEntity(game.level, self.target, "actor", mx, my) self.target.forceLevelup = old_levelup + self.target.check = old_check end end, summoner_gain_exp = true, summoner = self, @@ -1058,6 +1068,7 @@ newInscription{ local damage = t.getDamage(self, t) local duration = t.getDuration(self, t) return ([[Inflicts %0.2f temporal damage. If your target survives, it will be sent %d turns into the future. + It will also lower your paradox by 25 (if you have any). Note that messing with the spacetime continuum may have unforeseen consequences.]]):format(damDesc(self, DamageType.TEMPORAL, damage), duration) end, short_info = function(self, t) diff --git a/game/modules/tome/data/talents/misc/misc.lua b/game/modules/tome/data/talents/misc/misc.lua index f8d68ff72ce97f78db2bed0823712a2e15c2fbba..c3d9f8eee9351a53e1b3866cf02e8d82dc09163d 100644 --- a/game/modules/tome/data/talents/misc/misc.lua +++ b/game/modules/tome/data/talents/misc/misc.lua @@ -48,6 +48,7 @@ newTalent{ tactical = { ATTACK = { PHYSICAL = 1 } }, no_unlearn_last = true, ignored_by_hotkeyautotalents = true, + speed = 'weapon', action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) @@ -311,7 +312,7 @@ newTalent{ -- Check for visible monsters, only see LOS actors, so telepathy wont prevent it core.fov.calc_circle(self.x, self.y, game.level.map.w, game.level.map.h, 20, function(_, x, y) return game.level.map:opaque(x, y) end, function(_, x, y) local actor = game.level.map(x, y, game.level.map.ACTOR) - if actor and actor ~= self then + if actor and actor ~= self then if actor.summoner and actor.summoner == self then seen = false else diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua index 3b4c887b0a57f349592c50eb9cc16dc01b41a973..954bb1faf659a9c1820f0a38b5a22fd6726f7aa2 100644 --- a/game/modules/tome/data/talents/misc/npcs.lua +++ b/game/modules/tome/data/talents/misc/npcs.lua @@ -1550,7 +1550,7 @@ newTalent{ direct_hit = true, reflectable = true, requires_target = true, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self)) end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self, t)) end, action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) @@ -1637,7 +1637,7 @@ newTalent{ range = 10, direct_hit = true, requires_target = true, - getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self)) * 10000 end, + getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 220, getParadoxSpellpower(self, t)) * 10000 end, action = function(self, t) local tg = {type="hit", range=self:getTalentRange(t), talent=t} local x, y = self:getTarget(tg) @@ -2569,22 +2569,22 @@ newTalent{ if target:canBe("knockback") or rng.percent(t.getKBResistPen(self, t)) then self:project({type="hit", range=tg.range}, target.x, target.y, DamageType.PHYSICAL, dam) --Direct Damage - - local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) - if tx and ty then - local ox, oy = target.x, target.y - target:move(tx, ty, true) - if config.settings.tome.smooth_move > 0 then - target:resetMoveAnim() - target:setMoveAnim(ox, oy, 8, 5) + local tx, ty = util.findFreeGrid(x, y, 5, true, {[Map.ACTOR]=true}) + if tx and ty then + local ox, oy = target.x, target.y + target:move(tx, ty, true) + if config.settings.tome.smooth_move > 0 then + target:resetMoveAnim() + target:setMoveAnim(ox, oy, 8, 5) + end + end + tg.act_exclude = {[target.uid]=true} -- Don't hit primary target with AOE + self:project(tg, target.x, target.y, DamageType.SPELLKNOCKBACK, dam/2) --AOE damage + if target:canBe("stun") then + target:setEffect(target.EFF_STUNNED, 4, {apply_power=self:combatMindpower()}) + else + game.logSeen(target, "%s resists the stun!", target.name:capitalize()) end - end - self:project(tg, target.x, target.y, DamageType.SPELLKNOCKBACK, dam/2) --AOE damage - if target:canBe("stun") then - target:setEffect(target.EFF_STUNNED, 4, {apply_power=self:combatMindpower()}) - else - game.logSeen(target, "%s resists the stun!", target.name:capitalize()) - end else --If the target resists the knockback, do half damage to it. target:logCombat(self, "#YELLOW##Source# resists #Target#'s throw!") self:project({type="hit", range=tg.range}, target.x, target.y, DamageType.PHYSICAL, dam/2) diff --git a/game/modules/tome/data/talents/misc/objects.lua b/game/modules/tome/data/talents/misc/objects.lua index 2880dbac3c08683d0ed9eca752bcacb0fb5f4a5e..6af28a4a079d7ad3b218bc8ee110c4e2b1b89f1d 100644 --- a/game/modules/tome/data/talents/misc/objects.lua +++ b/game/modules/tome/data/talents/misc/objects.lua @@ -187,6 +187,7 @@ newTalent{ cooldown = function(self, t) return 8 - util.bound(self:getTalentLevelRaw(t), 1, 5) end, + speed = 'shield', points = 5, hard_cap = 5, range = 1, @@ -239,6 +240,8 @@ newTalent{ bt[DamageType.TEMPORAL] = true end + bt.all = nil + local n = 0 for t, _ in pairs(bt) do n = n + 1 end @@ -248,9 +251,9 @@ newTalent{ e_string = DamageType.dam_def[next(bt)].name else local list = table.keys(bt) - for i = 1, #list do + for i = 1, #list do if DamageType.dam_def[list[i]] then list[i] = DamageType.dam_def[list[i]].name - end + end end e_string = table.concat(list, ", ") end return bt, e_string @@ -510,14 +513,17 @@ newTalent{ equilibrium = 20, cooldown = 50, range = 10, + fixed_cooldown = true, tactical = { BUFF = 2 }, action = function(self, t) local nb = 3 local tids = {} for tid, _ in pairs(self.talents_cd) do local tt = self:getTalentFromId(tid) - if tt.type[1]:find("^wild%-gift/") or tt.type[1]:find("psionic/") or tt.type[1]:find("cursed/") then - tids[#tids+1] = tid + if not tt.fixed_cooldown then + if tt.type[1]:find("^wild%-gift/") or tt.type[1]:find("psionic/") or tt.type[1]:find("cursed/") then + tids[#tids+1] = tid + end end end for i = 1, nb do diff --git a/game/modules/tome/data/talents/misc/races.lua b/game/modules/tome/data/talents/misc/races.lua index 13670e4ae82d24dd7a7e0770cdf0f064da766273..862bdfdd34567bbebf5e7be4723f78525e785d14 100644 --- a/game/modules/tome/data/talents/misc/races.lua +++ b/game/modules/tome/data/talents/misc/races.lua @@ -211,6 +211,7 @@ newTalent{ require = racial_req4, points = 5, no_energy = true, + fixed_cooldown = true, cooldown = function(self, t) return math.ceil(self:combatTalentLimit(t, 20, 47, 35)) end, -- Limit to >20 getEffectGood = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end, getEffectBad = function(self, t) return math.floor(self:combatTalentScale(t, 2.9, 10.01, "log")) end, @@ -253,7 +254,7 @@ newTalent{ local tids = {} for tid, lev in pairs(self.talents) do local t = self:getTalentFromId(tid) - if t and self.talents_cd[tid] then tids[#tids+1] = t end + if t and self.talents_cd[tid] and not t.fixed_cooldown then tids[#tids+1] = t end end while #tids > 0 do local tt = rng.tableRemove(tids) @@ -267,7 +268,7 @@ newTalent{ end, info = function(self, t) return ([[The world grows old as you stand through the ages. To you, time is different. - Reduces the time remaining on detrimental effects by %d, cooling down talents by %d, and increases the time remaining on beneficial effects by %d (up to 2 times the current duration).]]): + Reduces the time remaining on detrimental effects by %d, most cooling down talents by %d, and increases the time remaining on beneficial effects by %d (up to 2 times the current duration).]]): format(t.getEffectBad(self, t), t.getEffectGood(self, t), t.getEffectGood(self, t)) end, } diff --git a/game/modules/tome/data/talents/psionic/absorption.lua b/game/modules/tome/data/talents/psionic/absorption.lua index b4feaee50f4440aa122e7091924aa05cd9223099..05a7c4254905dd9a60a293535764944247b56655 100644 --- a/game/modules/tome/data/talents/psionic/absorption.lua +++ b/game/modules/tome/data/talents/psionic/absorption.lua @@ -70,8 +70,7 @@ end local function shieldSpike(self, t, p) local val = 0 for i = 0, 2 do val = val + (p.last_absorbs.values[i] or 0) end - - self:setEffect(self.EFF_PSI_DAMAGE_SHIELD, 5, {power=val*2}) + if val > 0 then self:setEffect(self.EFF_PSI_DAMAGE_SHIELD, 5, {power=val*2}) end end local function shieldOverlay(self, t, p) @@ -218,9 +217,10 @@ newTalent{ local s_str = getShieldStrength(self, t) local absorb = 100*getEfficiency(self,t) return ([[Surround yourself with a shield that will absorb %d%% of any physical/acid/nature/temporal attack, up to a maximum of %d damage per attack. - Every time your shield absorbs damage, you convert some of the attack into energy, gaining two points of Psi, plus an additional point for every %0.1f points of damage absorbed, up to a maximum %0.1f points each turn. - At talent level 3, when you de-activate the shield twice the absorbed damage in the last 3 turns is released as a full psionic shield (absorbing all damage). - The maximum amount of damage your shield can absorb and the efficiency of the psi gain scale with your mindpower.]]): + Every time your shield absorbs damage, you convert some of the attack into energy, gaining one point of Psi, plus an additional point for every %0.1f points of damage absorbed, up to a maximum %0.1f points each turn. + At talent level 3, when you de-activate the shield twice the absorbed damage (if any) in the last 3 turns is released as a full psionic shield (absorbing all damage). + The maximum amount of damage your shield can absorb and the efficiency of the psi gain scale with your mindpower. + You can only have two of these shields active at once.]]): format(absorb, s_str, shieldMastery(self, t), maxPsiAbsorb(self,t)) end, } @@ -360,9 +360,10 @@ newTalent{ local s_str = getShieldStrength(self, t) local absorb = 100*getEfficiency(self,t) return ([[Surround yourself with a shield that will absorb %d%% of any fire/cold/light/arcane attack, up to a maximum of %d damage per attack. - Every time your shield absorbs damage, you convert some of the attack into energy, gaining two points of Psi, plus an additional point for every %0.1f points of damage absorbed, up to a maximum %0.1f points each turn. - At talent level 3, when you de-activate the shield twice the absorbed damage in the last 3 turns is released as a full psionic shield (absorbing all damage). - The maximum amount of damage your shield can absorb and the efficiency of the psi gain scale with your mindpower.]]): + Every time your shield absorbs damage, you convert some of the attack into energy, gaining one point of Psi, plus an additional point for every %0.1f points of damage absorbed, up to a maximum %0.1f points each turn. + At talent level 3, when you de-activate the shield twice the absorbed damage (if any) in the last 3 turns is released as a full psionic shield (absorbing all damage). + The maximum amount of damage your shield can absorb and the efficiency of the psi gain scale with your mindpower. + You can only have two of these shields active at once.]]): format(absorb, s_str, shieldMastery(self, t), maxPsiAbsorb(self,t)) end, } @@ -502,9 +503,10 @@ newTalent{ local s_str = getShieldStrength(self, t) local absorb = 100*getEfficiency(self,t) return ([[Surround yourself with a shield that will absorb %d%% of any lightning/blight/darkness/mind attack, up to a maximum of %d damage per attack. - Every time your shield absorbs damage, you convert some of the attack into energy, gaining two points of Psi, plus an additional point for every %0.1f points of damage absorbed, up to a maximum %0.1f points each turn. - At talent level 3, when you de-activate the shield twice the absorbed damage in the last 3 turns is released as a full psionic shield (absorbing all damage). - The maximum amount of damage your shield can absorb and the efficiency of the psi gain scale with your mindpower.]]): + Every time your shield absorbs damage, you convert some of the attack into energy, gaining one point of Psi, plus an additional point for every %0.1f points of damage absorbed, up to a maximum %0.1f points each turn. + At talent level 3, when you de-activate the shield twice the absorbed damage (if any) in the last 3 turns is released as a full psionic shield (absorbing all damage). + The maximum amount of damage your shield can absorb and the efficiency of the psi gain scale with your mindpower. + You can only have two of these shields active at once.]]): format(absorb, s_str, shieldMastery(self, t), maxPsiAbsorb(self,t)) end, } diff --git a/game/modules/tome/data/talents/psionic/augmented-mobility.lua b/game/modules/tome/data/talents/psionic/augmented-mobility.lua index b178442f8442af7bc88bbbbbe44c4ebd19125008..23ee3bab0680ae634cfe0db430f9f29330703841 100644 --- a/game/modules/tome/data/talents/psionic/augmented-mobility.lua +++ b/game/modules/tome/data/talents/psionic/augmented-mobility.lua @@ -87,7 +87,7 @@ newTalent{ psi = 10, points = 5, tactical = { CLOSEIN = 2 }, - range = function(self, t) return self:combatTalentLimit(t, 10, 3, 7) end, -- Limit base range to 10 + range = function(self, t) return math.floor(self:combatTalentLimit(t, 10, 3, 7)) end, -- Limit base range to 10 action = function(self, t) local tg = {type="bolt", range=self:getTalentRange(t)} local x, y = self:getTarget(tg) diff --git a/game/modules/tome/data/talents/psionic/charged-mastery.lua b/game/modules/tome/data/talents/psionic/charged-mastery.lua index 7ed2c43b2b0d2292528d5ffd2024efa84da37259..edcb287a1310d3f4217e741291ce13b02be0a259 100644 --- a/game/modules/tome/data/talents/psionic/charged-mastery.lua +++ b/game/modules/tome/data/talents/psionic/charged-mastery.lua @@ -43,12 +43,12 @@ newTalent{ info = function(self, t) return ([[For %d turns your electrokinesis transcends your normal limits, increasing your Lightning damage by %d%% and your Lightning resistance penetration by %d%%. In addition: - The cooldowns of Charged Shield, Charged Leech, Charged Aura and Brainstorm are reset. - Charged Aura will either increase in radius to 2, or apply its damage bonus to all of your weapons, whichever is applicable. + The cooldowns of Charged Shield, Charged Leech, Charged Aura, Charged Strike and Brainstorm are reset. + Charged Aura effects will have their radius increased by 1. Your Charged Shield will have 100%% absorption efficiency and will absorb twice the normal amount of damage. Brainstorm will also inflict blindness. Charge Leech will also inflict confusion (%d%% effect). - Charged Strike will have its secondary lightning/blind burst chain to up to 3 targets in a radius of 3. + Charged Strike will have its secondary lightning burst chain to up to 3 targets in a radius of 3. The damage bonus and resistance penetration scale with your Mindpower. Only one Transcendent talent may be in effect at a time.]]):format(t.getDuration(self, t), t.getPower(self, t), t.getPenetration(self, t), t.getConfuse(self, t)) end, @@ -72,7 +72,7 @@ newTalent{ info = function(self, t) return ([[Detect the mental activity of creatures in a radius of %d for %d turns. This reveals their location and boosts your defense by %d. - The duration, defense, and radius scale with your Mindpower.]]):format(t.radius(self, t), t.getDuration(self, t), t.getDefense(self, t)) + The defense scales with you Mindpower and the range with your Willpower.]]):format(t.radius(self, t), t.getDuration(self, t), t.getDefense(self, t)) end, } @@ -83,19 +83,19 @@ newTalent{ points = 5, random_ego = "attack", psi = 32, - cooldown = 13, + cooldown = 23, tactical = { ATTACKAREA = { LIGHTNING = 2 } }, range = 8, - radius = function(self,t) return self:combatTalentScale(t, 2, 5) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 2, 4)) end, direct_hit = true, requires_target = true, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t)} end, getSlow = function(self, t) return self:combatLimit(self:combatTalentMindDamage(t, 5, 50), 50, 4, 4, 34, 34) end, -- Limit < 50% - getDamage = function(self, t) return self:combatTalentMindDamage(t, 20, 130) end, + getDamage = function(self, t) return self:combatTalentMindDamage(t, 20, 100) end, getWeaponDamage = function(self, t) return self:combatTalentMindDamage(t, 10, 50) end, - getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 9)) end, + getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 6)) end, action = function(self, t) local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) @@ -130,6 +130,7 @@ newTalent{ require = psi_cun_high4, points = 5, mode = "sustained", + no_sustain_autoreset = true, sustain_psi = 30, cooldown = 60, tactical = { BUFF = 10}, diff --git a/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua b/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua index 23c9c1c230eed978b76ec4756888b29755d218e0..436a9adcee1448135d30eba44b990b56e1960912 100644 --- a/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua +++ b/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua @@ -100,67 +100,68 @@ newTalent{ fat_red = function(self, t) return math.floor(self:combatTalentMindDamage(t, 2, 10)) end, - action = function(self, t) - local d d = self:showInventory("Reshape which weapon or armor?", self:getInven("INVEN"), - function(o) - return not o.quest and (o.type == "weapon" and o.subtype ~= "mindstar") or (o.type == "armor" and (o.slot == "BODY" or o.slot == "OFFHAND" )) and not o.fully_reshaped --Exclude fully reshaped? - end - , function(o, item) - if o.combat then - local atk_boost = t.boost(self, t) - local dam_boost = atk_boost - if (o.old_atk or 0) < atk_boost or (o.old_dam or 0) < dam_boost then - if not o.been_reshaped then - o.orig_atk = (o.combat.atk or 0) - o.orig_dam = (o.combat.dam or 0) - elseif o.been_reshaped == true then --Update items affected by older versions of this talent - o.name = o.name:gsub("reshaped ", "", 1) - o.orig_atk = o.combat.atk - (o.old_atk or 0) - o.orig_dam = o.combat.dam - (o.old_dam or 0) - end - o.combat.atk = o.orig_atk + atk_boost - o.combat.dam = o.orig_dam + dam_boost - o.old_atk = atk_boost - o.old_dam = dam_boost - game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true}) - o.special = true - o.been_reshaped = "reshaped("..tostring(atk_boost)..","..tostring(dam_boost)..") " - d.used_talent = true - else - game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true}) + reshape = function(self, t, o, in_dialog) + if o.combat then + local atk_boost = t.boost(self, t) + local dam_boost = atk_boost + if (o.old_atk or 0) < atk_boost or (o.old_dam or 0) < dam_boost then + if not o.been_reshaped then + o.orig_atk = (o.combat.atk or 0) + o.orig_dam = (o.combat.dam or 0) + elseif o.been_reshaped == true then --Update items affected by older versions of this talent + o.name = o.name:gsub("reshaped ", "", 1) + o.orig_atk = o.combat.atk - (o.old_atk or 0) + o.orig_dam = o.combat.dam - (o.old_dam or 0) end + o.combat.atk = o.orig_atk + atk_boost + o.combat.dam = o.orig_dam + dam_boost + o.old_atk = atk_boost + o.old_dam = dam_boost + game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true}) + o.special = true + o.been_reshaped = "reshaped("..tostring(atk_boost)..","..tostring(dam_boost)..") " + if in_dialog then self:talentDialogReturn(true) end else - local armour = t.arm_boost(self, t) - local fat = t.fat_red(self, t) - if (o.old_fat or 0) < fat or o.wielder.combat_armor < (o.orig_arm or 0) + armour then - o.wielder = o.wielder or {} - if not o.been_reshaped then - o.orig_arm = (o.wielder.combat_armor or 0) - o.orig_fat = (o.wielder.fatigue or 0) - end - o.wielder.combat_armor = o.orig_arm + game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true}) + end + else + local armour = t.arm_boost(self, t) + local fat = t.fat_red(self, t) + if (o.old_fat or 0) < fat or o.wielder.combat_armor < (o.orig_arm or 0) + armour then + o.wielder = o.wielder or {} + if not o.been_reshaped then + o.orig_arm = (o.wielder.combat_armor or 0) + o.orig_fat = (o.wielder.fatigue or 0) + end + o.wielder.combat_armor = o.orig_arm + o.wielder.fatigue = o.orig_fat + o.wielder.combat_armor = (o.wielder.combat_armor or 0) + armour + o.wielder.fatigue = (o.wielder.fatigue or 0) - fat + if o.wielder.fatigue < 0 and not (o.orig_fat < 0) then + o.wielder.fatigue = 0 + elseif o.wielder.fatigue < 0 and o.orig_fat < 0 then o.wielder.fatigue = o.orig_fat - o.wielder.combat_armor = (o.wielder.combat_armor or 0) + armour - o.wielder.fatigue = (o.wielder.fatigue or 0) - fat - if o.wielder.fatigue < 0 and not (o.orig_fat < 0) then - o.wielder.fatigue = 0 - elseif o.wielder.fatigue < 0 and o.orig_fat < 0 then - o.wielder.fatigue = o.orig_fat - end - o.old_fat = fat - game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true}) - o.special = true - if o.orig_name then o.name = o.orig_name end --Fix name for items affected by older versions of this talent - o.been_reshaped = "reshaped["..tostring(armour)..","..tostring(o.wielder.fatigue-o.orig_fat).."%] " - d.used_talent = true - else - game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true}) end + o.old_fat = fat + game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true}) + o.special = true + if o.orig_name then o.name = o.orig_name end --Fix name for items affected by older versions of this talent + o.been_reshaped = "reshaped["..tostring(armour)..","..tostring(o.wielder.fatigue-o.orig_fat).."%] " + if in_dialog then self:talentDialogReturn(true) end + else + game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true}) end - end) - local co = coroutine.running() - d.unload = function(self) coroutine.resume(co, self.used_talent) end - if not coroutine.yield() then return nil end + end + end, + action = function(self, t) + local ret = self:talentDialog(self:showInventory("Reshape which weapon or armor?", self:getInven("INVEN"), + function(o) + return not o.quest and (o.type == "weapon" and o.subtype ~= "mindstar") or (o.type == "armor" and (o.slot == "BODY" or o.slot == "OFFHAND" )) and not o.fully_reshaped --Exclude fully reshaped? + end + , function(o, item) + t.reshape(self, t, o, true) + end)) + if not ret then return nil end return true end, info = function(self, t) @@ -169,7 +170,8 @@ newTalent{ local fat = t.fat_red(self, t) return ([[Manipulate forces on the molecular level to realign, rebalance, and hone a weapon, set of body armor, or a shield. (Mindstars resist being adjusted because they are already in an ideal natural state.) This permanently increases the Accuracy and damage of any weapon by %d or increases the armour rating of any piece of Armour by %d, while reducing its fatigue rating by %d. - The effects increase with your Mindpower and can only be applied (or reapplied) once to any item.]]): + The effects increase with your Mindpower and multiple uses on an item only increase the effect if your skill has improved. + These bonusses are automatically updated on equipped items when you level up.]]): format(weapon_boost, arm, fat) end, } @@ -186,18 +188,15 @@ newTalent{ return self:combatTalentMindDamage(t, 10, 40) end, action = function(self, t) - local d d = self:showInventory("Use which gem?", self:getInven("INVEN"), function(gem) return gem.type == "gem" and gem.material_level and not gem.unique end, function(gem, gem_item) + local ret = self:talentDialog(self:showInventory("Use which gem?", self:getInven("INVEN"), function(gem) return gem.type == "gem" and gem.material_level and not gem.unique end, function(gem, gem_item) self:removeObject(self:getInven("INVEN"), gem_item) local amt = t.energy_per_turn(self, t) local dur = 3 + 2*(gem.material_level or 0) self:setEffect(self.EFF_PSI_REGEN, dur, {power=amt}) - self.changed = true - d.used_talent = true - self:setEffect(self.EFF_CRYSTAL_BUFF, dur, {name=gem.name, effects=gem.wielder}) - end) - local co = coroutine.running() - d.unload = function(self) coroutine.resume(co, self.used_talent) end - if not coroutine.yield() then return nil end + self:setEffect(self.EFF_CRYSTAL_BUFF, dur, {name=gem.name, gem=gem.define_as, effects=gem.wielder}) + self:talentDialogReturn(true) + end)) + if not ret then return nil end return true end, info = function(self, t) @@ -232,7 +231,7 @@ newTalent{ local inc = t.bonus(self,t) return ([[By carefully synchronizing your mind to the resonant frequencies of your psionic focus, you strengthen its effects. For conventional weapons, this increases the percentage of your willpower and cunning that is used in place of strength and dexterity, from 60%% to %d%%. - For mindstars, this increases the amount of Psi you can store by +%d. + For mindstars, this increases the chance to pull enemies to you by +%d%%. For gems, this increases the bonus stats by %d.]]): format(60+inc, inc, math.ceil(inc/5)) end, diff --git a/game/modules/tome/data/talents/psionic/focus.lua b/game/modules/tome/data/talents/psionic/focus.lua index 81f39083f4521c86601d24c3d506bb96acce4a98..952213b5bea27c93ec16f1d7af43418828a521b4 100644 --- a/game/modules/tome/data/talents/psionic/focus.lua +++ b/game/modules/tome/data/talents/psionic/focus.lua @@ -58,7 +58,7 @@ newTalent{ end, info = function(self, t) local dam = t.getDamage(self, t) - return ([[Focus energies into a beam to lash enemies with physical force, doing %d Physical damage and knocking them off balance for 2 turns (-15%% global speed). + return ([[Focus energies into a beam to lash all creatures in a line with physical force, doing %d Physical damage and knocking them off balance for 2 turns (-15%% global speed). The damage will scale with your Mindpower.]]): format(damDesc(self, DamageType.PHYSICAL, dam)) end, @@ -66,7 +66,7 @@ newTalent{ newTalent{ name = "Pyrokinesis", - type = {"psionic/focus", 2}, + type = {"psionic/focus", 1}, require = psi_wil_req2, points = 5, random_ego = "attack", @@ -74,7 +74,7 @@ newTalent{ psi = 20, tactical = { ATTACK = { FIRE = 2 } }, range = 0, - radius = function(self,t) return self:combatTalentScale(t, 4, 6) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 4, 6)) end, getDamage = function (self, t) return self:combatTalentMindDamage(t, 20, 450) end, @@ -96,19 +96,19 @@ newTalent{ local radius = self:getTalentRadius(t) local dam = t.getDamage(self, t) return ([[Telekinetically energize the matter of all foes within %d squares at the molecular level, setting them ablaze. This does %0.1f fire damage over six turns.]]): - format(radius, damDesc(self, DamageType.FIREBURN, dam)) + format(radius, damDesc(self, DamageType.FIRE, dam)) end, } newTalent{ name = "Brain Storm", - type = {"psionic/focus", 3}, + type = {"psionic/focus", 1}, points = 5, require = psi_wil_req3, psi = 15, cooldown = 10, - range = function(self,t) return self:combatTalentScale(t, 3, 5) end, - radius = function(self,t) return self:combatTalentScale(t, 2, 3) end, + range = function(self,t) return math.floor(self:combatTalentScale(t, 3, 5)) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 2, 3)) end, tactical = { DISABLE = 2, ATTACKAREA = { LIGHTNING = 2 } }, getDamage = function(self, t) return self:combatTalentMindDamage(t, 20, 290) end, action = function(self, t) diff --git a/game/modules/tome/data/talents/psionic/kinetic-mastery.lua b/game/modules/tome/data/talents/psionic/kinetic-mastery.lua index 476326916076950970c1e18d2f7ab5a3eb5b70e4..11d1c5efdd4ab3372b0babfe8c492538aee562b6 100644 --- a/game/modules/tome/data/talents/psionic/kinetic-mastery.lua +++ b/game/modules/tome/data/talents/psionic/kinetic-mastery.lua @@ -42,8 +42,8 @@ newTalent{ info = function(self, t) return ([[For %d turns your telekinesis transcends your normal limits, increasing your Physical damage by %d%% and you Physical resistance penetration by %d%%. In addition: - The cooldowns of Kinetic Shield, Kinetic Leech, Kinetic Aura and Mindlash are reset. - Kinetic Aura will either increase in radius to 2, or apply its damage bonus to all of your weapons, whichever is applicable. + The cooldowns of Kinetic Shield, Kinetic Leech, Kinetic Aura, Kinetic Strike and Mindlash are reset. + Kinetic Aura effects will have their radius increased by 1. Your Kinetic Shield will have 100%% absorption efficiency and will absorb twice the normal amount of damage. Mindlash will also inflict stun. Kinetic Leech will put enemies to sleep. @@ -53,7 +53,6 @@ newTalent{ end, } - newTalent{ name = "Kinetic Surge", image = "talents/telekinetic_throw.png", type = {"psionic/kinetic-mastery", 2}, @@ -63,7 +62,7 @@ newTalent{ cooldown = 15, psi = 20, tactical = { CLOSEIN = 2, ATTACK = { PHYSICAL = 2 }, ESCAPE = 2 }, - range = function(self, t) return self:combatTalentLimit(t, 10, 6, 9) end, + range = function(self, t) return math.floor(self:combatTalentLimit(t, 10, 6, 9)) end, getDamage = function (self, t) return math.floor(self:combatTalentMindDamage(t, 20, 180)) end, @@ -71,16 +70,17 @@ newTalent{ requires_target = true, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=2, selffire=false, talent=t} end, action = function(self, t) - local tg = {type="hit", range=1, nowarning=true } + local tg = {type="hit", range=1, nowarning=true, nolock=true } local x, y, target = self:getTarget(tg) if not x or not y or not target then return nil end if core.fov.distance(self.x, self.y, x, y) > 1 then return nil end - local dam = self:mindCrit(t.getDamage(self, t)) - if self:reactionToward(target) < 0 then + if target ~= self then local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end + + local dam = self:mindCrit(t.getDamage(self, t)) if target:canBe("knockback") or rng.percent(t.getKBResistPen(self, t)) then self:project({type="hit", range=tg.range}, target.x, target.y, DamageType.PHYSICAL, dam) --Direct Damage @@ -94,7 +94,8 @@ newTalent{ target:setMoveAnim(ox, oy, 8, 5) end end - self:project(tg, target.x, target.y, DamageType.SPELLKNOCKBACK, dam/2) --AOE damage + tg.act_exclude = {[target.uid]=true} -- Don't hit primary target with AOE + self:project(tg, target.x, target.y, DamageType.MINDKNOCKBACK, dam/2) --AOE damage if target:canBe("stun") then target:setEffect(target.EFF_STUNNED, math.floor(self:getTalentRange(t) / 2), {apply_power=self:combatMindpower()}) else @@ -109,7 +110,9 @@ newTalent{ local x, y = self:getTarget(tg) if not x or not y then return nil end if core.fov.distance(self.x, self.y, x, y) > tg.range then return nil end - + + local dam = self:mindCrit(t.getDamage(self, t)) + for i = 1, math.floor(self:getTalentRange(t) / 2) do self:project(tg, x, y, DamageType.DIG, 1) end @@ -117,7 +120,6 @@ newTalent{ local _ _, x, y = self:canProject(tg, x, y) game.level.map:particleEmitter(self.x, self.y, tg.radius, "flamebeam", {tx=x-self.x, ty=y-self.y}) game:playSoundNear(self, "talents/lightning") - local block_actor = function(_, bx, by) return game.level.map:checkEntity(bx, by, engine.Map.TERRAIN, "block_move", self) end local l = self:lineFOV(x, y, block_actor) local lx, ly, is_corner_blocked = l:step() @@ -127,10 +129,9 @@ newTalent{ tx, ty = lx, ly lx, ly, is_corner_blocked = l:step() end - --self:move(tx, ty, true) local fx, fy = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true}) - if not fx then + if fx then self:move(fx, fy, true) end return true @@ -149,7 +150,7 @@ newTalent{ When used on yourself, you will launch in a straight line, knocking enemies flying and doing %0.1f Physical damage to each. You can break through %d walls while doing this. - The damage improves with your Mindpower and the range increases with both Mindpower and Strength.]]): + The damage improves with your Mindpower and the range increases with Mindpower.]]): format(range, dam, math.floor(range/2), dam/2, t.getKBResistPen(self, t), dam, math.floor(range/2)) end, } @@ -163,22 +164,24 @@ newTalent{ mode = "sustained", no_sustain_autoreset = true, sustain_psi = 25, cooldown = 10, - range = function(self, t) return math.floor(self:combatTalentScale(t, 3, 5, "log")) end, + range = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8, "log")) end, radius = 10, target = function(self, t) return {type="hit", range=self:getTalentRange(t), selffire=false, talent=t} end, - getEvasion = function(self, t) return self:combatTalentLimit(t, 100, 17, 45), self:getTalentLevel(t) >= 4 and 2 or 1 end, -- Limit chance <100% + getEvasion = function(self, t) return self:combatTalentLimit(t, 90, 15, 40), self:getTalentLevel(t) >= 4 and 2 or 1 end, -- Limit chance <90% activate = function(self, t) local chance, spread = t.getEvasion(self, t) return { chance = self:addTemporaryValue("projectile_evasion", chance), + slow = self:addTemporaryValue("slow_projectiles", chance), spread = self:addTemporaryValue("projectile_evasion_spread", spread), } end, deactivate = function(self, t, p) self:removeTemporaryValue("projectile_evasion", p.chance) self:removeTemporaryValue("projectile_evasion_spread", p.spread) + self:removeTemporaryValue("slow_projectiles", p.slow) if self:attr("save_cleanup") then return true end local tg = self:getTalentTarget(t) @@ -206,10 +209,10 @@ newTalent{ info = function(self, t) local chance, spread = t.getEvasion(self, t) return ([[You learn to devote a portion of your attention to mentally swatting, grabbing, or otherwise deflecting incoming projectiles. - All projectiles targeting you have a %d%% chance to instead target another spot within radius %d. + All projectiles targeting you have a %d%% chance to instead target another spot within radius %d and move %d%% slower. If you choose, you can use your mind to grab all projectiles within radius 10 of you and hurl them toward any location within range %d of you, but this will break your concentration. To do this, deactivate this sustained talent.]]): - format(chance, spread, self:getTalentRange(t)) + format(chance, spread, chance, self:getTalentRange(t)) end, } @@ -237,11 +240,14 @@ newTalent{ local tg = self:getTalentTarget(t) local x, y = self:getTarget(tg) if not x or not y then return nil end - self:project(tg, x, y, DamageType.IMPLOSION, {dur=dur, dam=dam}) - local target = game.level.map(x, y, Map.ACTOR) - if target then - target:setEffect(self.EFF_PSIONIC_BIND, dur, {power=1, apply_power=self:combatMindpower()}) - end + + self:project(tg, x, y, function(px, py) + DamageType:get(DamageType.IMPLOSION).projector(self, px, py, DamageType.IMPLOSION, {dur=dur, dam=dam}) + local act = game.level.map(px, py, Map.ACTOR) + if not act then return end + act:setEffect(self.EFF_PSIONIC_BIND, dur, {power=1, apply_power=self:combatMindpower()}) + end) + return true end, info = function(self, t) diff --git a/game/modules/tome/data/talents/psionic/other.lua b/game/modules/tome/data/talents/psionic/other.lua index a2f11a9dd9a82ae586254b3302fba07ea23b071a..17e6880d9afa44860486b4ae69ba3abdcb8be3fb 100644 --- a/game/modules/tome/data/talents/psionic/other.lua +++ b/game/modules/tome/data/talents/psionic/other.lua @@ -28,7 +28,7 @@ newTalent{ no_npc_use = true, action = function(self, t) local inven = self:getInven("INVEN") - local d d = self:showInventory("Telekinetically grasp which item?", inven, function(o) + local ret = self:talentDialog(self:showInventory("Telekinetically grasp which item?", inven, function(o) return (o.type == "weapon" or o.type == "gem") and o.subtype ~= "sling" end, function(o, item) local pf = self:getInven("PSIONIC_FOCUS") @@ -71,11 +71,9 @@ newTalent{ game.logSeen(self, "%s wears: %s.", self.name:capitalize(), o:getName{do_color=true}) self:sortInven() - d.used_talent = true - end) - local co = coroutine.running() - d.unload = function(self) coroutine.resume(co, self.used_talent) end - if not coroutine.yield() then return nil end + self:talentDialogReturn(true) + end)) + if not ret then return nil end return true end, info = function(self, t) @@ -193,26 +191,18 @@ newTalent{ end end) if #list <= 0 then return end - - local elem = { - black = {DamageType.ACID, "acid"}, - blue = {DamageType.LIGHTNING, "lightning_explosion"}, - green = {DamageType.NATURE, "slime"}, - red = {DamageType.FIRE, "flame"}, - violet = {DamageType.ARCANE, "manathrust"}, - white = {DamageType.COLD, "freeze"}, - yellow = {DamageType.LIGHT, "light"}, - } - local bolt = elem[gem.subtype] - + + local color = gem.color_attributes or {} + local bolt = {color.damage_type or 'MIND', color.particle or 'light'} + table.sort(list, "dist") local a = list[1].a - self:project({type="ball", range=6, radius=0, selffire=false, talent=t}, a.x, a.y, bolt[1], self:hasEffect(self.EFF_PSIFRENZY).damage, {type=bolt[2]}) - + self:project({type="ball", range=6, radius=0, selffire=false, talent=t}, a.x, a.y, bolt[1], self:mindCrit(self:hasEffect(self.EFF_PSIFRENZY).damage), {type=bolt[2]}) + end return end - + if not p.mindstar_grab then return end if not rng.percent(p.mindstar_grab.chance) then return end @@ -274,8 +264,8 @@ newTalent{ info = function(self, t) local base = [[Allows you to wield a physical melee weapon, a mindstar or a gem telekinetically, gaining a special effect for each. A gem will provide +3 bonus to all primary stats per tier of the gem. - A mindstar will randomly try to telekinetically grab a far away foe (5% chance and range 2 for a tier 1 mindstar, +1 range and +5% chance for each tier above 1) and pull it into melee range. - A physical melee weapon will act as a semi independant entity, attacking foes nearby each turn while also replacing Strength and Dexterity with Willpower and Cunning for accuracy and damage calculations (for all melee weapons). + A mindstar will randomly try to telekinetically grab a far away foe (10% chance and range 2 for a tier 1 mindstar, +1 range and +5% chance for each tier above 1) and pull it into melee range. + A physical melee weapon will act as a semi independant entity, attacking foes nearby each turn while also replacing Strength and Dexterity with Willpower and Cunning for accuracy and damage calculations. This stat usage modification will also apply to conventionally wielded weapons. ]] @@ -290,7 +280,7 @@ newTalent{ local speed = 1 if o.type == "gem" then local ml = o.material_level or 1 - base = base..([[The telekinetically-wielded gem grants you +%d stats.]]):format(ml * 4) + base = base..([[The telekinetically-wielded gem grants you +%d stats.]]):format(ml * 3) elseif o.subtype == "mindstar" then local ml = o.material_level or 1 base = base..([[The telekinetically-wielded mindstar has a %d%% chance to grab a foe up to %d range away.]]):format((ml + 1) * 5, ml + 2) diff --git a/game/modules/tome/data/talents/psionic/projection.lua b/game/modules/tome/data/talents/psionic/projection.lua index 1c2039f6cbbf4fbcc68cdbf69c6ec66a5d930b31..093cc9e2de96b9f016e9048c99419520611dc56c 100644 --- a/game/modules/tome/data/talents/psionic/projection.lua +++ b/game/modules/tome/data/talents/psionic/projection.lua @@ -25,7 +25,7 @@ local function aura_spike_strength(self, t) end local function aura_mastery(self, t) - return 9 + self:getTalentLevel(t) + return 0.5 --9 + self:getTalentLevel(t) * 2 end local function aura_range(self, t) @@ -129,7 +129,7 @@ newTalent{ self:project(tg, self.x, self.y, function(tx, ty) local act = game.level.map(tx, ty, engine.Map.ACTOR) if act then - self:incPsi(-dam/mast) + self:incPsi(-mast) self:breakStepUp() end DamageType:get(DamageType.PHYSICAL).projector(self, tx, ty, DamageType.PHYSICAL, dam) @@ -137,7 +137,15 @@ newTalent{ end, do_combat = function(self, t, target) -- called by _M:attackTargetWith in mod.class.interface.Combat.lua local k_dam = t.getAuraStrength(self, t) - DamageType:get(DamageType.PHYSICAL).projector(self, target.x, target.y, DamageType.PHYSICAL, k_dam) + if self:hasEffect(self.EFF_TRANSCENDENT_TELEKINESIS) then + local tg = {type="ball", range=10, radius=1, selffire=false, friendlyfire=false} + self:project(tg, target.x, target.y, function(tx, ty) + DamageType:get(DamageType.PHYSICAL).projector(self, tx, ty, DamageType.PHYSICAL, k_dam) + end) + else + DamageType:get(DamageType.PHYSICAL).projector(self, target.x, target.y, DamageType.PHYSICAL, k_dam) + end + self:incPsi(-aura_mastery(self, t)) end, activate = function(self, t) self.energy.value = self.energy.value + game.energy_to_act * self:combatMindSpeed() @@ -172,13 +180,13 @@ newTalent{ local mast = aura_mastery(self, t) local spikecost = t.getSpikeCost(self, t) return ([[Fills the air around you with reactive currents of force. - If you have a gem or mindstar in your psionically wielded slot, this will do %0.1f Physical damage to all who approach. - All damage done by the aura will drain one point of energy per %0.1f points of damage dealt. - If you have a conventional weapon in your psionically wielded slot, this will add %0.1f Physical damage to its hits. + If you have a gem or mindstar in your psionically wielded slot, this will do %0.1f Physical damage to all who approach, costing %0.1f energy per creature. + If you have a conventional weapon in your psionically wielded slot, this will add %0.1f Physical damage to all your weapon hits, costing %0.1f energy per hit. When deactivated, if you have at least %d energy, a massive spike of kinetic energy is released as a range %d beam, smashing targets for up to %d physical damage and sending them flying. #{bold}#Activating the aura takes no time but de-activating it does.#{normal}# - To turn off an aura without spiking it, deactivate it and target yourself. The damage will improve with your Mindpower.]]): - format(damDesc(self, DamageType.PHYSICAL, dam), mast, damDesc(self, DamageType.PHYSICAL, dam), spikecost, t.getSpikedRange(self, t), + To turn off an aura without spiking it, deactivate it and target yourself. The damage will improve with your Mindpower. + You can only have two of these auras active at once.]]): + format(damDesc(self, DamageType.PHYSICAL, dam), mast, damDesc(self, DamageType.PHYSICAL, dam), mast, spikecost, t.getSpikedRange(self, t), damDesc(self, DamageType.PHYSICAL, spikedam)) end, } @@ -253,7 +261,15 @@ newTalent{ end, do_combat = function(self, t, target) -- called by _M:attackTargetWith in mod.class.interface.Combat.lua local t_dam = t.getAuraStrength(self, t) - DamageType:get(DamageType.FIRE).projector(self, target.x, target.y, DamageType.FIRE, t_dam) + if self:hasEffect(self.EFF_TRANSCENDENT_PYROKINESIS) then + local tg = {type="ball", range=10, radius=1, selffire=false, friendlyfire=false} + self:project(tg, target.x, target.y, function(tx, ty) + DamageType:get(DamageType.FIRE).projector(self, tx, ty, DamageType.FIRE, t_dam) + end) + else + DamageType:get(DamageType.FIRE).projector(self, target.x, target.y, DamageType.FIRE, t_dam) + end + self:incPsi(-aura_mastery(self, t)) end, activate = function(self, t) self.energy.value = self.energy.value + game.energy_to_act * self:combatMindSpeed() @@ -289,13 +305,13 @@ newTalent{ local mast = aura_mastery(self, t) local spikecost = t.getSpikeCost(self, t) return ([[Fills the air around you with reactive currents of furnace-like heat. - If you have a gem or mindstar in your psionically wielded slot, this will do %0.1f Fire damage to all who approach. - All damage done by the aura will drain one point of energy per %0.1f points of damage dealt. - If you have a conventional weapon in your psionically wielded slot, this will add %0.1f Fire damage to its hits. + If you have a gem or mindstar in your psionically wielded slot, this will do %0.1f Fire damage to all who approach, costing %0.1f energy per creature. + If you have a conventional weapon in your psionically wielded slot, this will add %0.1f Fire damage to all your weapon hits, costing %0.1f energy per hit. When deactivated, if you have at least %d energy, a massive spike of thermal energy is released as a conical blast (radius %d) of superheated air. Anybody caught in it will suffer up to %d fire damage over several turns. #{bold}#Activating the aura takes no time but de-activating it does.#{normal}# - To turn off an aura without spiking it, deactivate it and target yourself. The damage will improve with your Mindpower.]]): - format(damDesc(self, DamageType.FIRE, dam), mast, damDesc(self, DamageType.FIRE, dam), spikecost, rad, + To turn off an aura without spiking it, deactivate it and target yourself. The damage will improve with your Mindpower. + You can only have two of these auras active at once.]]): + format(damDesc(self, DamageType.FIRE, dam), mast, damDesc(self, DamageType.FIRE, dam), mast, spikecost, rad, damDesc(self, DamageType.FIRE, spikedam)) end, } @@ -356,7 +372,7 @@ newTalent{ return aura_spike_strength(self, t) end, getNumSpikeTargets = function(self, t) - return 1 + math.floor(0.5*self:getTalentLevel(t)) + return 3 + math.floor(0.5*self:getTalentLevel(t)) end, do_chargedaura = function(self, t) local mast = aura_mastery(self, t) @@ -373,7 +389,15 @@ newTalent{ end, do_combat = function(self, t, target) -- called by _M:attackTargetWith in mod.class.interface.Combat.lua local c_dam = t.getAuraStrength(self, t) - DamageType:get(DamageType.LIGHTNING).projector(self, target.x, target.y, DamageType.LIGHTNING, c_dam) + if self:hasEffect(self.EFF_TRANSCENDENT_ELECTROKINESIS) then + local tg = {type="ball", range=10, radius=1, selffire=false, friendlyfire=false} + self:project(tg, target.x, target.y, function(tx, ty) + DamageType:get(DamageType.LIGHTNING).projector(self, tx, ty, DamageType.LIGHTNING, c_dam) + end) + else + DamageType:get(DamageType.LIGHTNING).projector(self, target.x, target.y, DamageType.LIGHTNING, c_dam) + end + self:incPsi(-aura_mastery(self, t)) end, activate = function(self, t) game:playSoundNear(self, "talents/thunderstorm") @@ -449,13 +473,13 @@ newTalent{ local spikecost = t.getSpikeCost(self, t) local nb = t.getNumSpikeTargets(self, t) return ([[Fills the air around you with crackling energy. - If you have a gem or mindstar in your psionically wielded slot, this will do %0.1f Lightning damage to all who approach. - All damage done by the aura will drain one point of energy per %0.1f points of damage dealt. - If you have a conventional weapon in your psionically wielded slot, this will add %0.1f Lightning damage to its hits. + If you have a gem or mindstar in your psionically wielded slot, this will do %0.1f Lightning damage to all who approach, costing %0.1f energy per creature. + If you have a conventional weapon in your psionically wielded slot, this will add %0.1f Lightning damage to all your weapon hits, costing %0.1f energy per hit. When deactivated, if you have at least %d energy, a massive spike of electrical energy jumps between up to %d nearby targets, doing up to %0.1f Lightning damage to each with a 50%% chance of dazing them. #{bold}#Activating the aura takes no time but de-activating it does.#{normal}# - To turn off an aura without spiking it, deactivate it and target yourself.]]): - format(damDesc(self, DamageType.LIGHTNING, dam), mast, damDesc(self, DamageType.LIGHTNING, dam), spikecost, nb, damDesc(self, DamageType.LIGHTNING, spikedam)) + To turn off an aura without spiking it, deactivate it and target yourself. + You can only have two of these auras active at once.]]): + format(damDesc(self, DamageType.LIGHTNING, dam), mast, damDesc(self, DamageType.LIGHTNING, dam), mast, spikecost, nb, damDesc(self, DamageType.LIGHTNING, spikedam)) end, } @@ -483,7 +507,8 @@ newTalent{ local dur = t.duration(self,t) return ([[Overcharge your psionic focus with energy for %d turns, producing a different effect depending on what it is. A telekinetically wielded weapon enters a frenzy, striking up to %d times every turn, also increases the radius by %d. - A mindstar or a gem will fire an energy bolt at a random enemy in range 6, each turn for %0.1f damage. The type is determined by the colour of the gem or mindstar base damage. Damage scales with Mindpower. The mindstar will stop its normal attacks.]]): + A mindstar will attempt to pull in all enemies within its normal range. + A gem will fire an energy bolt at a random enemy in range 6, each turn for %0.1f damage. The type is determined by the colour of the gem. Damage scales with Mindpower.]]): format(dur, targets, targets, t.getDamage(self,t)) end, } diff --git a/game/modules/tome/data/talents/psionic/psi-fighting.lua b/game/modules/tome/data/talents/psionic/psi-fighting.lua index 12e0b4e981387f0e62d9fe934c6877c35a5adf11..19d9a0d159337838227e7b845703c1a275a9441b 100644 --- a/game/modules/tome/data/talents/psionic/psi-fighting.lua +++ b/game/modules/tome/data/talents/psionic/psi-fighting.lua @@ -74,7 +74,7 @@ newTalent{ sustain_psi = 10, no_energy = true, tactical = { BUFF = 2 }, - getMult = function(self, t) return self:combatTalentScale(t, 0.07, 0.25) end, + getMult = function(self, t) return self:combatTalentScale(t, 0.1, 0.3) end, activate = function(self, t) local str_power = t.getMult(self, t)*self:getWil() local dex_power = t.getMult(self, t)*self:getCun() @@ -106,7 +106,7 @@ newTalent{ require = psi_cun_req3, points = 5, cooldown = 10, - psi = 15, + psi = 10, no_energy = true, tactical = { BUFF = 2 }, getWeaponDamage = function(self, t) return self:combatTalentWeaponDamage(t, 0.75, 1.1) end, @@ -119,7 +119,7 @@ newTalent{ return ([[Assume a defensive mental state. For one turn, you will fully block the next melee attack used against you with your telekinetically-wielded weapon and then strike the attacker with it for %d%% weapon damage. At raw talent level 3 you will also disarm the attacker for 3 turns. - At raw talent level 5 you will be able to reflexively block up to one attack per turn with a %d%% chance, based on your cunning. Each trigger requires and uses 15 Psi. + At raw talent level 5 you will be able to reflexively block up to one attack per turn with a %d%% chance, based on your cunning. Each trigger requires and uses 10 Psi. This requires a telekinetically-wielded weapon.]]): format(100 * t.getWeaponDamage(self, t), t.getChance(self, t)) end, @@ -142,7 +142,7 @@ newTalent{ action = function(self, t) local weapon = self:getInven(self.INVEN_PSIONIC_FOCUS) and self:getInven(self.INVEN_PSIONIC_FOCUS)[1] if type(weapon) == "boolean" then weapon = nil end - if not weapon or self:attr("disarmed")then + if not weapon or not weapon.combat or self:attr("disarmed") then game.logPlayer(self, "You cannot do that without a weapon in your telekinetic slot.") return nil end @@ -182,11 +182,7 @@ newTalent{ return ([[Focus your will into a powerful thrust of your telekinetically-wielded weapon to impale your target and then viciously rip it free. This deals %d%% weapon damage and then causes the victim to bleed for %0.1f Physical damage over four turns. At level 3 the thrust is so powerful that it has %d%% chance to shatter a temporary damage shield if one exists. - Your Willpower and Cunning are used instead of Strength and Dexterity to determine Accuracy and damage. The bleeding damage increases with your Mindpower.]]): format(100 * t.getWeaponDamage(self, t), damDesc(self, DamageType.PHYSICAL, t.getDamage(self,t)), t.getShatter(self, t)) end, -} - - - +} \ No newline at end of file diff --git a/game/modules/tome/data/talents/psionic/thermal-mastery.lua b/game/modules/tome/data/talents/psionic/thermal-mastery.lua index 9a16dd093cc5627fedbbc4b779e5a09ddccac8ed..a46cd447bb681773d2b6d5c9fc17b9d83fd013e4 100644 --- a/game/modules/tome/data/talents/psionic/thermal-mastery.lua +++ b/game/modules/tome/data/talents/psionic/thermal-mastery.lua @@ -43,8 +43,8 @@ newTalent{ info = function(self, t) return ([[For %d turns your pyrokinesis transcends your normal limits, increasing your Fire and Cold damage by %d%% and your Fire and Cold resistance penetration by %d%%. In addition: - The cooldowns of Thermal Shield, Thermal Leech, Thermal Aura and Pyrokinesis are reset. - Thermal Aura will either increase in radius to 2, or apply its damage bonus to all of your weapons, whichever is applicable. + The cooldowns of Thermal Shield, Thermal Leech, Thermal Aura, Thermal Strike and Pyrokinesis are reset. + Thermal Aura effects will have their radius increased by 1. Your Thermal Shield will have 100%% absorption efficiency and will absorb twice the normal amount of damage. Pyrokinesis will inflict Flameshock. Thermal Leech will reduce enemy damage by %d%%. @@ -63,9 +63,9 @@ newTalent{ cooldown = 8, psi = 20, tactical = { ATTACK = { COLD = 3} }, - range = function(self,t) return self:combatTalentScale(t, 4, 6) end, + range = function(self,t) return math.floor(self:combatTalentScale(t, 4, 6)) end, getDamage = function (self, t) - return self:combatTalentMindDamage(t, 12, 340) + return self:combatTalentMindDamage(t, 12, 300) end, requires_target = true, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=0, selffire=false, talent=t} end, @@ -77,8 +77,12 @@ newTalent{ local target = game.level.map(x, y, Map.ACTOR) if not target then return end - self:project(tg, x, y, DamageType.COLD, self:mindCrit(rng.avg(0.8*dam, dam)), {type="mindsear"}) - target:setEffect(target.EFF_BRAINLOCKED, 4, {apply_power=self:combatMindpower()}) + self:project(tg, x, y, function(px, py) + DamageType:get(DamageType.COLD).projector(self, px, py, DamageType.COLD, self:mindCrit(rng.avg(0.8*dam, dam))) + local act = game.level.map(px, py, Map.ACTOR) + if not act then return end + act:setEffect(target.EFF_BRAINLOCKED, 4, {apply_power=self:combatMindpower()}) + end, {type="mindsear"}) return true end, @@ -101,11 +105,11 @@ newTalent{ psi = 35, tactical = { DISABLE = 4 }, range = 6, - radius = function(self,t) return self:combatTalentScale(t, 2, 4) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 2, 4)) end, getDuration = function (self, t) return math.floor(self:combatTalentMindDamage(t, 4, 8)) end, - getDamage = function(self, t) return self:combatTalentMindDamage(t, 10, 60) end, + getDamage = function(self, t) return self:combatTalentMindDamage(t, 10, 80) end, getArmor = function(self, t) return self:combatTalentMindDamage(t, 10, 20) end, requires_target = true, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t, friendlyfire=false} end, @@ -116,7 +120,7 @@ newTalent{ local dur = t.getDuration(self, t) local dam = t.getDamage(self, t) local armor = t.getArmor(self, t) - self:project(tg, self.x, self.y, function(tx, ty) + self:project(tg, x, y, function(tx, ty) local act = game.level.map(tx, ty, engine.Map.ACTOR) if act then local cold = DamageType:get("COLD").projector(self, tx, ty, DamageType.COLD, dam) @@ -153,8 +157,8 @@ newTalent{ points = 5, psi = 0, cooldown = 10, - range = function(self,t) return self:combatTalentScale(t, 4, 6) end, - radius = function(self,t) return self:combatTalentScale(t, 2, 4) end, + range = function(self,t) return math.floor(self:combatTalentScale(t, 4, 6)) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 2, 4)) end, tactical = { ATTACKAREA = { FIRE = 3, COLD = 2 }, PSI = 2 }, getDamage = function(self, t) return self:combatTalentMindDamage(t, 50, 150) end, action = function(self, t) @@ -164,7 +168,7 @@ newTalent{ local dam=self:mindCrit(t.getDamage(self, t)) local dam1 = dam * (self:getMaxPsi() - self:getPsi()) / self:getMaxPsi() - local dam2 = dam * self:getPsi() / self:getMaxPsi() + local dam2 = dam * self:getPsi() / self:getMaxPsi() * 2 self:project(tg, x, y, DamageType.COLD, dam1) self:project(tg, x, y, DamageType.FIRE, dam2) diff --git a/game/modules/tome/data/talents/psionic/thought-forms.lua b/game/modules/tome/data/talents/psionic/thought-forms.lua index 4aecc4f4fba6ac49018c12fcfb55ed36a2b97648..65729284206a5afffa6ab1a3dcc1af25c930a9ae 100644 --- a/game/modules/tome/data/talents/psionic/thought-forms.lua +++ b/game/modules/tome/data/talents/psionic/thought-forms.lua @@ -473,7 +473,7 @@ newTalent{ end, info = function(self, t) local stat = t.getStatBonus(self, t) - return ([[Forge a defender wielding a sword and shield from your thoughts. The solider learns Armor Rraining, Weapon Mastery, Combat Accuracy, Shield Pummel, and Shield Wall as it levels up, and has +%d Strength, +%d Dexterity, and +%d Constitution. + return ([[Forge a defender wielding a sword and shield from your thoughts. The solider learns Armor Training, Weapon Mastery, Combat Accuracy, Shield Pummel, and Shield Wall as it levels up, and has +%d Strength, +%d Dexterity, and +%d Constitution. Activating this talent will put all other thought-forms on cooldown. The stat bonuses will improve with your Mindpower.]]):format(stat/2, stat/2, stat) end, diff --git a/game/modules/tome/data/talents/psionic/voracity.lua b/game/modules/tome/data/talents/psionic/voracity.lua index dd766be58ac83c765f75b10c1f867103eae5f75e..4fc19b97a913ffe682669d386fc948a58359ebed 100644 --- a/game/modules/tome/data/talents/psionic/voracity.lua +++ b/game/modules/tome/data/talents/psionic/voracity.lua @@ -31,7 +31,7 @@ newTalent{ direct_hit = true, requires_target = true, range = 0, - radius = function(self,t) return self:combatTalentScale(t, 1, 4) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 1, 4)) end, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, @@ -79,7 +79,7 @@ newTalent{ return ([[You draw kinetic energy from your surroundings to replenish your Psi. This will slow all targets within radius %d by %d%% (max %d%%) for four turns, draining %0.1f (max %0.1f) stamina from each. You replenish %d (max %d) Psi from the first target, with each additional target restoring 20%% less than the one before it. - The strength of these effects increases as your Psi depletes.]]) + The strength of these effects increases as your Psi depletes and with your Mindpower.]]) :format(range, t.getSlow(self, t)*100, t.getSlow(self, t, 0)*100, t.getDam(self, t), t.getDam(self, t, 0), t.getLeech(self, t), t.getLeech(self, t, 0)) end, } @@ -97,7 +97,7 @@ newTalent{ direct_hit = true, requires_target = true, range = 0, - radius = function(self,t) return self:combatTalentScale(t, 1, 4) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 1, 4)) end, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, @@ -139,7 +139,7 @@ newTalent{ return ([[You draw thermal energy from your surroundings to replenish your Psi. This will freeze all targets within radius %d for %d (max %d) turns, and deal %0.1f (max %0.1f) Cold damage. You replenish %d (max %d) Psi from the first target, with each additional target restoring 20%% less than the one before it. - The damage and the strength of these effects increases as your Psi depletes.]]) + The damage and the strength of these effects increases as your Psi depletes and with your Mindpower.]]) :format(range, t.getDur(self, t), t.getDur(self, t, 0), damDesc(self, DamageType.COLD, t.getDam(self, t)), damDesc(self, DamageType.COLD, t.getDam(self, t, 0)), t.getLeech(self, t), t.getLeech(self, t, 0)) end, } @@ -157,7 +157,7 @@ newTalent{ direct_hit = true, requires_target = true, range = 0, - radius = function(self,t) return self:combatTalentScale(t, 1, 4) end, + radius = function(self,t) return math.floor(self:combatTalentScale(t, 1, 4)) end, target = function(self, t) return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), selffire=false, talent=t} end, @@ -208,7 +208,7 @@ newTalent{ return ([[You draw electical potential energy from your surroundings to replenish your Psi. This deals %0.1f (max %0.1f) Lightning damage to all targets around you within radius %d, and has a %d%% (max %d%%) chance to daze them for 3 turns. You replenish %d (max %d) Psi from the first target, with each additional target restoring 20%% less than the one before it. - The strength of these effects increases as your Psi depletes.]]) + The strength of these effects increases as your Psi depletes and with your Mindpower.]]) :format(t.getDam(self, t), t.getDam(self, t, 0), range, t.getDaze(self, t), t.getDaze(self, t, 0), t.getLeech(self, t), t.getLeech(self, t, 0)) end, } @@ -223,12 +223,12 @@ newTalent{ passives = function(self, t, p) local recover = t.getPsiRecover(self, t) self:talentTemporaryValue(p, "max_psi", recover*5) - self:talentTemporaryValue(p, "psi_per_kill", recover) - self:talentTemporaryValue(p, "psi_on_crit", recover*0.5) + self:talentTemporaryValue(p, "psi_per_kill", recover*0.5) + self:talentTemporaryValue(p, "psi_on_crit", recover) end, info = function(self, t) local recover = t.getPsiRecover(self, t) - return ([[Increases your maximum energy by %d. You also gain %0.1f Psi for each kill and %0.1f Psi for each mind critical.]]):format(5 * recover, recover, 0.5*recover) + return ([[Increases your maximum energy by %d. You also gain %0.1f Psi for each kill and %0.1f Psi for each mind critical.]]):format(5 * recover, 0.5*recover, recover) end, } diff --git a/game/modules/tome/data/talents/spells/acid-alchemy.lua b/game/modules/tome/data/talents/spells/acid-alchemy.lua index 1ca07f4f5810cd8eff16e0609768d061d2ed9da2..29266225d55d4207cb1bfb5fced2d52eaa83f600 100644 --- a/game/modules/tome/data/talents/spells/acid-alchemy.lua +++ b/game/modules/tome/data/talents/spells/acid-alchemy.lua @@ -28,8 +28,8 @@ newTalent{ cooldown = 30, tactical = { BUFF = 2 }, getIncrease = function(self, t) return self:combatTalentScale(t, 0.05, 0.25) * 100 end, + sustain_slots = 'alchemy_infusion', activate = function(self, t) - cancelAlchemyInfusions(self) game:playSoundNear(self, "talents/arcane") local ret = {} self:talentTemporaryValue(ret, "inc_damage", {[DamageType.ACID] = t.getIncrease(self, t)}) diff --git a/game/modules/tome/data/talents/spells/conveyance.lua b/game/modules/tome/data/talents/spells/conveyance.lua index 48cbfcf7f1a0b2f9e2ebc95ff35f85e7d9e115a7..f74be57fb6768eb6d856c5eba3f2a78be0849cd5 100644 --- a/game/modules/tome/data/talents/spells/conveyance.lua +++ b/game/modules/tome/data/talents/spells/conveyance.lua @@ -33,7 +33,7 @@ newTalent{ action = function(self, t) local target = self if self:getTalentLevel(t) >= 4 then - game.logPlayer(self, "Selects a target to teleport...") + game.logPlayer(self, "Select a target to teleport...") local tg = {default_target=self, type="hit", nowarning=true, range=10, first_target="friend"} local tx, ty = self:getTarget(tg) if tx then @@ -58,7 +58,7 @@ newTalent{ local rad = t.getRange(self, t) local radius = t.getRadius(self, t) if self:getTalentLevel(t) >= 5 or game.zone.force_controlled_teleport then - game.logPlayer(self, "Selects a teleport location...") + game.logPlayer(self, "Select a teleport location...") local tg = {type="ball", nolock=true, pass_terrain=true, nowarning=true, range=rad, radius=radius, requires_knowledge=false} x, y = self:getTarget(tg) if not x then return nil end @@ -113,7 +113,7 @@ newTalent{ local target = self if self:getTalentLevel(t) >= 4 then - game.logPlayer(self, "Selects a target to teleport...") + game.logPlayer(self, "Select a target to teleport...") local tg = {default_target=self, type="hit", nowarning=true, range=10, first_target="friend"} local tx, ty = self:getTarget(tg) if tx then @@ -138,7 +138,7 @@ newTalent{ local x, y = self.x, self.y local newpos if self:getTalentLevel(t) >= 5 then - game.logPlayer(self, "Selects a teleport location...") + game.logPlayer(self, "Select a teleport location...") local tg = {type="ball", nolock=true, pass_terrain=true, nowarning=true, range=t.getRange(self, t), radius=t.getRadius(self, t), requires_knowledge=false} x, y = self:getTarget(tg) if not x then return nil end diff --git a/game/modules/tome/data/talents/spells/energy-alchemy.lua b/game/modules/tome/data/talents/spells/energy-alchemy.lua index 5651a8c0ed1ebd5f7782011856e9e6174d9ecdf3..095aa2c10b04bd1bcca14200d541cfa174d9f1a8 100644 --- a/game/modules/tome/data/talents/spells/energy-alchemy.lua +++ b/game/modules/tome/data/talents/spells/energy-alchemy.lua @@ -28,8 +28,8 @@ newTalent{ cooldown = 30, tactical = { BUFF = 2 }, getIncrease = function(self, t) return self:combatTalentScale(t, 0.05, 0.25) * 100 end, + sustain_slots = 'alchemy_infusion', activate = function(self, t) - cancelAlchemyInfusions(self) game:playSoundNear(self, "talents/arcane") local ret = {} self:talentTemporaryValue(ret, "inc_damage", {[DamageType.LIGHTNING] = t.getIncrease(self, t)}) @@ -187,9 +187,15 @@ newTalent{ local ret = {name = self.name:capitalize().."'s "..t.name} self:talentTemporaryValue(ret, "movement_speed", t.getSpeed(self, t)) ret.last_life = self.life + + if core.shader.active(4) then + ret.particle = self:addParticles(Particles.new("shader_ring_rotating", 1, {z=5, rotation=0, radius=1.4, img="alchie_lightning"}, {type="lightningshield", time_factor = 4000, ellipsoidalFactor = {1.7, 1.4}})) + end + return ret end, deactivate = function(self, t, p) + self:removeParticles(p.particle) return true end, info = function(self, t) diff --git a/game/modules/tome/data/talents/spells/fire-alchemy.lua b/game/modules/tome/data/talents/spells/fire-alchemy.lua index 28bf3d29a5371b5bc3f908bb45a6e0d8d45a756b..068154f2f88433cd114f9f13e571640e12b3d13f 100644 --- a/game/modules/tome/data/talents/spells/fire-alchemy.lua +++ b/game/modules/tome/data/talents/spells/fire-alchemy.lua @@ -28,8 +28,8 @@ newTalent{ cooldown = 30, tactical = { BUFF = 2 }, getIncrease = function(self, t) return self:combatTalentScale(t, 0.05, 0.25) * 100 end, + sustain_slots = 'alchemy_infusion', activate = function(self, t) - cancelAlchemyInfusions(self) game:playSoundNear(self, "talents/arcane") local ret = {} self:talentTemporaryValue(ret, "inc_damage", {[DamageType.FIRE] = t.getIncrease(self, t)}) @@ -95,7 +95,7 @@ newTalent{ self:project(tg, x, y, function(px, py) local target = game.level.map(px, py, Map.ACTOR) if target and not target:hasEffect(target.EFF_BURNING) and self:reactionToward(target) < 0 then - target:setEffect(target.EFF_BURNING, heat.dur + math.ceil(t.getDuration(self, t)/3), {src=self, power=heat.power}) + target:setEffect(target.EFF_BURNING, heat.dur + math.ceil(t.getDuration(self, t)/3), {src=self, power=heat.power}) end end) end @@ -221,7 +221,7 @@ newTalent{ local insightdam = t.getFireDamageInSight(self, t) local res = t.getResistance(self, t) local manadrain = t.getManaDrain(self, t) - return ([[Turn your body into pure flame, increasing your fire resistance by %d%%, burning any creatures attacking you for %0.2f fire damage, and projecting %d random slow-moving fire bolts per turns at targets in sight, doing %0.2f fire damage with each bolt. + return ([[Turn your body into pure flame, increasing your fire resistance by %d%%, burning any creatures attacking you for %0.2f fire damage, and projecting %d random slow-moving fire bolts per turn at targets in sight, doing %0.2f fire damage with each bolt. The projectiles safely go through your friends without harming them. This powerful spell drains %0.2f mana while active. The damage and resistance will increase with your Spellpower.]]): diff --git a/game/modules/tome/data/talents/spells/fire.lua b/game/modules/tome/data/talents/spells/fire.lua index 173fd4f84b22f7ba71b108200516505cc9cad5c1..381b38f9aab33637513ed5bddd18b2d55143ed1e 100644 --- a/game/modules/tome/data/talents/spells/fire.lua +++ b/game/modules/tome/data/talents/spells/fire.lua @@ -28,10 +28,10 @@ newTalent{ tactical = { ATTACK = { FIRE = 2 } }, range = 10, reflectable = true, - proj_speed = 20, + proj_speed = 1, requires_target = true, target = function(self, t) - local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="bolt_fire", trail="firetrail"}} + local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="bolt_acid"}} if self:getTalentLevel(t) >= 5 then tg.type = "beam" end return tg end, diff --git a/game/modules/tome/data/talents/spells/frost-alchemy.lua b/game/modules/tome/data/talents/spells/frost-alchemy.lua index 5bdecd6eacf91bc1e435e0c406681170fb85dd24..460ed2efc57a4d6e7924f9f911e0fae53ede8e4b 100644 --- a/game/modules/tome/data/talents/spells/frost-alchemy.lua +++ b/game/modules/tome/data/talents/spells/frost-alchemy.lua @@ -28,8 +28,8 @@ newTalent{ cooldown = 30, tactical = { BUFF = 2 }, getIncrease = function(self, t) return self:combatTalentScale(t, 0.05, 0.25) * 100 end, + sustain_slots = 'alchemy_infusion', activate = function(self, t) - cancelAlchemyInfusions(self) game:playSoundNear(self, "talents/arcane") local ret = {} self:talentTemporaryValue(ret, "inc_damage", {[DamageType.COLD] = t.getIncrease(self, t)}) diff --git a/game/modules/tome/data/talents/spells/golemancy.lua b/game/modules/tome/data/talents/spells/golemancy.lua index 20ba93e482ec10c0d3ce367f8e7ff4cb28110780..888d421eb7f82dd3282aa48ae68bbdcba1d506dc 100644 --- a/game/modules/tome/data/talents/spells/golemancy.lua +++ b/game/modules/tome/data/talents/spells/golemancy.lua @@ -42,7 +42,7 @@ local function makeGolem(self) combat = { dam=10, atk=10, apr=0, dammod={str=1} }, - body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, BODY=1, GEM=2 }, + body = { INVEN = 1000, QS_MAINHAND = 1, QS_OFFHAND = 1, MAINHAND = 1, OFFHAND = 1, BODY=1, GEM={max = 2, stack_limit = 1} }, canWearObjectCustom = function(self, o) if o.type ~= "gem" then return end if not self.summoner then return "Golem has no master" end @@ -80,6 +80,7 @@ local function makeGolem(self) ["golem/arcane"] = 0.3, }, forbid_nature = 1, + power_source = {arcane = true}, inscription_restrictions = { ["inscriptions/runes"] = true, }, resolvers.inscription("RUNE:_SHIELDING", {cooldown=14, dur=5, power=100}), diff --git a/game/modules/tome/data/talents/spells/meta.lua b/game/modules/tome/data/talents/spells/meta.lua index 47f147f7d2e4d105190d8ad315f442d8358cff3d..1d8f700ae8cdc8a70d13d538a4003d767ab8bfef 100644 --- a/game/modules/tome/data/talents/spells/meta.lua +++ b/game/modules/tome/data/talents/spells/meta.lua @@ -119,7 +119,7 @@ newTalent{ local chance = t.getChance(self, t) return ([[You learn to finely craft and tune your offensive spells. You try to carve a hole in spells that affect an area to avoid damaging yourself. The chance of success is %d%%. - In addition, you hone your damaging spells to spellshock their targets. This talent gives a bonus of %d to Spellpower solely for the purposes of overcoming the target's Spell Save. Spellshocked targets suffer a temporary 20%% penalty to damage resistances.]]): + In addition, you hone your damaging spells to spellshock their targets. Whenever you deal damage with a spell you attempt to spellshock them with %d more Spellpower than normal. Spellshocked targets suffer a temporary 20%% penalty to damage resistances.]]): format(chance, self:combatTalentSpellDamage(t, 10, 320) / 4) end, } @@ -158,6 +158,7 @@ newTalent{ points = 5, mana = 70, cooldown = 50, + fixed_cooldown = true, tactical = { BUFF = 2 }, getTalentCount = function(self, t) return math.floor(self:combatTalentScale(t, 2, 7, "log")) end, getMaxLevel = function(self, t) return self:getTalentLevel(t) end, @@ -165,8 +166,10 @@ newTalent{ local tids = {} for tid, _ in pairs(self.talents_cd) do local tt = self:getTalentFromId(tid) - if tt.type[2] <= t.getMaxLevel(self, t) and tt.is_spell then - tids[#tids+1] = tid + if not tt.fixed_cooldown then + if tt.type[2] <= t.getMaxLevel(self, t) and tt.is_spell then + tids[#tids+1] = tid + end end end for i = 1, t.getTalentCount(self, t) do @@ -181,7 +184,7 @@ newTalent{ info = function(self, t) local talentcount = t.getTalentCount(self, t) local maxlevel = t.getMaxLevel(self, t) - return ([[Your mastery of the arcane flows allow you to reset the cooldown of %d of your spells of tier %d or less.]]): + return ([[Your mastery of the arcane flows allow you to reset the cooldown of %d of most of your spells of tier %d or less.]]): format(talentcount, maxlevel) end, } diff --git a/game/modules/tome/data/talents/spells/spells.lua b/game/modules/tome/data/talents/spells/spells.lua index dfacde99af45d9d111c9b8d0474254259d237ecb..440a267f24b8a69598d6497840831c05889b5a92 100644 --- a/game/modules/tome/data/talents/spells/spells.lua +++ b/game/modules/tome/data/talents/spells/spells.lua @@ -239,16 +239,6 @@ function necroEssenceDead(self, checkonly) end ------------------------------------------- -function cancelAlchemyInfusions(self) - local chants = {self.T_FIRE_INFUSION, self.T_FROST_INFUSION, self.T_ACID_INFUSION, self.T_LIGHTNING_INFUSION} - for i, t in ipairs(chants) do - if self:isTalentActive(t) then - self:forceUseTalent(t, {ignore_energy=true}) - end - end -end - - load("/data/talents/spells/arcane.lua") load("/data/talents/spells/aether.lua") load("/data/talents/spells/fire.lua") diff --git a/game/modules/tome/data/talents/spells/stone-alchemy.lua b/game/modules/tome/data/talents/spells/stone-alchemy.lua index 4027814059bd6f419a2ecdf9f39b2b3fa4c35bd3..6efc1b42cffe8b94248fa20ae07033a6eb72e299 100644 --- a/game/modules/tome/data/talents/spells/stone-alchemy.lua +++ b/game/modules/tome/data/talents/spells/stone-alchemy.lua @@ -40,7 +40,7 @@ newTalent{ return gem end, action = function(self, t) - local d d = self:showEquipInven("Use which gem?", function(o) return not o.unique and o.type == "gem" and not o.__tagged end, function(o, inven, item) + local ret = self:talentDialog(self:showEquipInven("Use which gem?", function(o) return not o.unique and o.type == "gem" and not o.__tagged end, function(o, inven, item) if not o then return end local gem = t.make_gem(self, t, o.define_as) if not gem then return end @@ -48,12 +48,10 @@ newTalent{ self:removeObject(inven, item) game.logPlayer(self, "You create: %s", gem:getName{do_color=true, do_count=true}) self:sortInven() - d.used_talent = true + self:talentDialogReturn(true) return true - end) - local co = coroutine.running() - d.unload = function(self) coroutine.resume(co, self.used_talent) end - if not coroutine.yield() then return nil end + end)) + if not ret then return nil end game:playSoundNear(self, "talents/arcane") return true @@ -95,15 +93,12 @@ newTalent{ self:addObject(self.INVEN_INVEN, gem) game.logPlayer(self, "You extract %s from %s", gem:getName{do_color=true, do_count=true}, o:getName{do_color=true, do_count=true}) self:sortInven() - if d then d.used_talent = true end + self:talentDialogReturn(true) end return true end, action = function(self, t) - local d d = self:showEquipInven("Try to extract gems from which metallic item?", function(o) return t.filterGem(self, t, o) end, function(o, inven, item) return t.extractGem(self, t, o, inven, item, d) end) - local co = coroutine.running() - d.unload = function(self) coroutine.resume(co, self.used_talent) end - if not coroutine.yield() then return nil end + if not self:talentDialog(self:showEquipInven("Try to extract gems from which metallic item?", function(o) return t.filterGem(self, t, o) end, function(o, inven, item) return t.extractGem(self, t, o, inven, item, d) end)) then return nil end return true end, info = function(self, t) @@ -128,7 +123,7 @@ newTalent{ no_npc_use = true, no_unlearn_last = true, action = function(self, t) - local d d = self:showInventory("Use which gem?", self:getInven("INVEN"), function(gem) return gem.type == "gem" and gem.imbue_powers and gem.material_level and gem.material_level <= self:getTalentLevelRaw(t) end, function(gem, gem_item) + local ret = self:talentDialog(self:showInventory("Use which gem?", self:getInven("INVEN"), function(gem) return gem.type == "gem" and gem.imbue_powers and gem.material_level and gem.material_level <= self:getTalentLevelRaw(t) end, function(gem, gem_item) local nd = self:showInventory("Imbue which armour?", self:getInven("INVEN"), function(o) return o.type == "armor" and (o.slot == "BODY" or (self:knowTalent(self.T_CRAFTY_HANDS) and (o.slot == "HEAD" or o.slot == "BELT"))) and not o.been_imbued end, function(o, item) self:removeObject(self:getInven("INVEN"), gem_item) o.wielder = o.wielder or {} @@ -140,15 +135,13 @@ newTalent{ o.been_imbued = " <"..gem.name..">" game.logPlayer(self, "You imbue your %s with %s.", o:getName{do_colour=true, no_count=true}, gem:getName{do_colour=true, no_count=true}) o.special = true - d.used_talent = true - game:unregisterDialog(d) + self:talentDialogReturn(true) + game:unregisterDialog(self:talentDialogGet()) end) - nd.unload = function(self) game:unregisterDialog(d) end + nd.unload = function(_) game:unregisterDialog(self:talentDialogGet()) end return true - end) - local co = coroutine.running() - d.unload = function(self) coroutine.resume(co, self.used_talent) end - if not coroutine.yield() then return nil end + end)) + if not ret then return nil end return true end, info = function(self, t) diff --git a/game/modules/tome/data/talents/techniques/archery.lua b/game/modules/tome/data/talents/techniques/archery.lua index 5418ab91086c727bc32c69ab085a8ee02215fa0c..8270be721ab1dc8e942ae6820e8a1c92fbebd173 100644 --- a/game/modules/tome/data/talents/techniques/archery.lua +++ b/game/modules/tome/data/talents/techniques/archery.lua @@ -22,6 +22,7 @@ newTalent{ name = "Shoot", type = {"technique/archery-base", 1}, no_energy = "fake", + speed = 'archery', hide = true, innate = true, points = 1, @@ -112,7 +113,7 @@ newTalent{ local q = self:hasAmmo() if not q then if not silent then game.logPlayer(self, "You must have a quiver or pouch equipped.") end return false end if q.combat.shots_left >= q.combat.capacity then return false end - return true + return true end, no_unlearn_last = true, action = function(self, t) @@ -176,6 +177,7 @@ newTalent{ apr = self:combatScale(self:getTalentLevel(t) * self:getDex(10, true), 3, 0, 53, 50)} return vals end, + sustain_slots = 'archery_stance', activate = function(self, t) local weapon = self:hasArcheryWeapon() if not weapon then @@ -183,7 +185,6 @@ newTalent{ return nil end - if self:isTalentActive(self.T_RAPID_SHOT) then self:forceUseTalent(self.T_RAPID_SHOT, {ignore_energy=true}) end local vals = t.getCombatVals(self, t) return { speed = self:addTemporaryValue("combat_physspeed", vals.speed), @@ -228,6 +229,7 @@ newTalent{ } return vals end, + sustain_slots = 'archery_stance', activate = function(self, t) local weapon = self:hasArcheryWeapon() if not weapon then @@ -235,7 +237,6 @@ newTalent{ return nil end - if self:isTalentActive(self.T_AIM) then self:forceUseTalent(self.T_AIM, {ignore_energy=true}) end local vals = t.getCombatVals(self, t) return { speed = self:addTemporaryValue("combat_physspeed", vals.speed), diff --git a/game/modules/tome/data/talents/techniques/skirmisher-slings.lua b/game/modules/tome/data/talents/techniques/skirmisher-slings.lua index 66a8d30c5b9f539aae5a4ce79cd36a4d1e24c388..0ff6f95c1f2d91fc7d0622513e01b22ca4303a41 100644 --- a/game/modules/tome/data/talents/techniques/skirmisher-slings.lua +++ b/game/modules/tome/data/talents/techniques/skirmisher-slings.lua @@ -72,6 +72,11 @@ newTalent { self.talents_cd[t.id] = math.max(cooldown - 1, 0) end end, + speed = function(self, t) return self:getSpeed('archery') * 0.5 end, + display_speed = function(self, t) + return ("Double Archery (#LIGHT_GREEN#%d%%#LAST# of a turn)"): + format(self:getSpeed('archery') * 50) + end, action = function(self, t) local old_speed = self.combat_physspeed self.combat_physspeed = old_speed * 2 @@ -106,6 +111,7 @@ newTalent { require = techs_dex_req3, points = 5, no_energy = "fake", + speed = "archery", random_ego = "attack", tactical = {ATTACKALL = {weapon = 3}}, range = 0, diff --git a/game/modules/tome/data/talents/techniques/techniques.lua b/game/modules/tome/data/talents/techniques/techniques.lua index 7bb236da3ec08e4e22105c776d10210a541c4869..9f202b0cd847a00091c83a8121cce28bb2befbe6 100644 --- a/game/modules/tome/data/talents/techniques/techniques.lua +++ b/game/modules/tome/data/talents/techniques/techniques.lua @@ -22,8 +22,8 @@ newTalentType{ allow_random=true, type="technique/2hweapon-assault", name = "two newTalentType{ allow_random=true, type="technique/strength-of-the-berserker", name = "berserker's strength", description = "Fear nothing!" } newTalentType{ allow_random=true, type="technique/2hweapon-offense", name = "two-handed weapons", description = "Specialized two-handed techniques." } newTalentType{ allow_random=true, type="technique/2hweapon-cripple", name = "two-handed maiming", description = "Specialized two-handed techniques." } -newTalentType{ allow_random=true, type="technique/shield-offense", name = "shield offense", description = "Specialized weapon and shield techniques." } -newTalentType{ allow_random=true, type="technique/shield-defense", name = "shield defense", description = "Specialized weapon and shield techniques." } +newTalentType{ allow_random=true, type="technique/shield-offense", name = "shield offense", speed = "shield", description = "Specialized weapon and shield techniques." } +newTalentType{ allow_random=true, type="technique/shield-defense", name = "shield defense", speed = "shield", description = "Specialized weapon and shield techniques." } newTalentType{ allow_random=true, type="technique/dualweapon-training", name = "dual weapons", description = "Specialized dual wielding techniques." } newTalentType{ allow_random=true, type="technique/dualweapon-attack", name = "dual techniques", description = "Specialized dual wielding techniques." } newTalentType{ allow_random=true, type="technique/archery-base", name = "archery - base", description = "Ability to shoot." } diff --git a/game/modules/tome/data/talents/uber/cun.lua b/game/modules/tome/data/talents/uber/cun.lua index 5fd94ddd6e491ea2fa2c55fa1ff9ecc31ba74b94..96ece146355254fc573bd61bb4a683990275ac7d 100644 --- a/game/modules/tome/data/talents/uber/cun.lua +++ b/game/modules/tome/data/talents/uber/cun.lua @@ -222,30 +222,32 @@ uberTalent{ for tid, _ in pairs(self.talents_cd) do local t = self:getTalentFromId(tid) - if - (kind == "physical" and - ( - t.type[1]:find("^technique/") or - t.type[1]:find("^cunning/") + if not t.fixed_cooldown then + if + (kind == "physical" and + ( + t.type[1]:find("^technique/") or + t.type[1]:find("^cunning/") + ) + ) or + (kind == "spell" and + ( + t.type[1]:find("^spell/") or + t.type[1]:find("^corruption/") or + t.type[1]:find("^celestial/") or + t.type[1]:find("^chronomancy/") + ) + ) or + (kind == "mind" and + ( + t.type[1]:find("^wild%-gift/") or + t.type[1]:find("^cursed/") or + t.type[1]:find("^psionic/") + ) ) - ) or - (kind == "spell" and - ( - t.type[1]:find("^spell/") or - t.type[1]:find("^corruption/") or - t.type[1]:find("^celestial/") or - t.type[1]:find("^chronomancy/") - ) - ) or - (kind == "mind" and - ( - t.type[1]:find("^wild%-gift/") or - t.type[1]:find("^cursed/") or - t.type[1]:find("^psionic/") - ) - ) - then - tids[#tids+1] = tid + then + tids[#tids+1] = tid + end end end if #tids == 0 then return end diff --git a/game/modules/tome/data/talents/uber/mag.lua b/game/modules/tome/data/talents/uber/mag.lua index 0f1d0971c6b903d069f8c2ddff34eeb8eb8efc68..4d8ea5aa1760ddbb595e928f96091ca509342c68 100644 --- a/game/modules/tome/data/talents/uber/mag.lua +++ b/game/modules/tome/data/talents/uber/mag.lua @@ -108,8 +108,7 @@ uberTalent{ info = function(self, t) return ([[You can wrap temporal threads around you, assuming the form of a telugoroth for 10 turns. While in this form you gain pinning, bleeding, blindness and stun immunity, 30%% temporal resistance, your temporal damage bonus is set to your current highest damage bonus + 30%%, 50%% of the damage you deal becomes temporal, and you gain 20%% temporal resistance penetration. - You also are able to cast two anomalies: Anomaly Rearrange and Anomaly Temporal Storm. - Transforming to this form will increase your paradox by 400 but also grant the equivalent of +200 willpower to control paradoxes and failures. This reverts back at the end of the effect.]]) + You also are able to cast two anomalies: Anomaly Rearrange and Anomaly Temporal Storm.]]) :format() end, } @@ -182,6 +181,8 @@ uberTalent{ who:learnTalent(who.T_BONE_SHIELD,true,math.ceil(tlevel*2/3)) elseif who.name == "mucus ooze" then who:learnTalent(who.T_VIRULENT_DISEASE,true,tlevel) + elseif who.name == "temporal hound" then + who:learnTalent(who.T_ELEMENTAL_DISCORD,true,tlevel) else -- print("Error: attempting to apply talent Blighted Summoning to incorrect creature type") return false @@ -214,6 +215,7 @@ uberTalent{ - Yeek Wayists: Dark Portal - Bloated Oozes: Bone Shield (level %d) - Mucus Oozes: Virulent Disease + - Temporal Hounds: Elemental Discord Your necrotic minions and wild-summons get a bonus to Magic equal to yours. The talent levels increase with your level, and other race- or object-based summons may also be affected. ]]):format(tl,math.ceil(tl*2/3)) diff --git a/game/modules/tome/data/talents/uber/wil.lua b/game/modules/tome/data/talents/uber/wil.lua index 9b9800a8c694d5bd8264c9921362239a6a9dcc8e..e44ebbeddc1fa93f48197fe8532084e4e2fddb9c 100644 --- a/game/modules/tome/data/talents/uber/wil.lua +++ b/game/modules/tome/data/talents/uber/wil.lua @@ -23,6 +23,7 @@ uberTalent{ no_energy = true, requires_target = true, range = 10, + fixed_cooldown = true, tactical = { BUFF = 2 }, action = function(self, t) self:setEffect(self.EFF_DRACONIC_WILL, 5, {}) diff --git a/game/modules/tome/data/timed_effects/magical.lua b/game/modules/tome/data/timed_effects/magical.lua index cc9b2500735941c9e0ac1dc770db7164b1706913..bc682a16dc6bf37056328cce3a2875474606f294 100644 --- a/game/modules/tome/data/timed_effects/magical.lua +++ b/game/modules/tome/data/timed_effects/magical.lua @@ -324,7 +324,7 @@ newEffect{ newEffect{ name = "BANE_BLINDED", image = "effects/bane_blinded.png", desc = "Bane of Blindness", - long_desc = function(self, eff) return ("The target is blinded, unable to see anything and takes %0.2f darkness damage per turns."):format(eff.dam) end, + long_desc = function(self, eff) return ("The target is blinded, unable to see anything and takes %0.2f darkness damage per turn."):format(eff.dam) end, type = "magical", subtype = { bane=true, blind=true }, status = "detrimental", @@ -353,7 +353,7 @@ newEffect{ newEffect{ name = "BANE_CONFUSED", image = "effects/bane_confused.png", desc = "Bane of Confusion", - long_desc = function(self, eff) return ("The target is confused, acting randomly (chance %d%%), unable to perform complex actions and takes %0.2f darkness damage per turns."):format(eff.power, eff.dam) end, + long_desc = function(self, eff) return ("The target is confused, acting randomly (chance %d%%), unable to perform complex actions and takes %0.2f darkness damage per turn."):format(eff.power, eff.dam) end, type = "magical", subtype = { bane=true, confusion=true }, status = "detrimental", @@ -1409,7 +1409,7 @@ newEffect{ newEffect{ name = "MANAWORM", image = "effects/manaworm.png", desc = "Manaworm", - long_desc = function(self, eff) return ("The target is infected by a manaworm, draining %0.2f mana per turns and releasing it as arcane damage to the target."):format(eff.power) end, + long_desc = function(self, eff) return ("The target is infected by a manaworm, draining %0.2f mana per turn and releasing it as arcane damage to the target."):format(eff.power) end, type = "magical", subtype = { arcane=true }, status = "detrimental", @@ -1479,15 +1479,11 @@ newEffect{ newEffect{ name = "REDUX", image = "talents/redux.png", desc = "Redux", - long_desc = function(self, eff) return "The next activated chronomancy talent that the target uses will be cast twice." end, + long_desc = function(self, eff) return ("Chronomancy spells with cooldown less than %d will not go on cooldown when cast."):format(eff.max_cd) end, type = "magical", subtype = { temporal=true }, status = "beneficial", - parameters = { power=1}, - activate = function(self, eff) - end, - deactivate = function(self, eff) - end, + parameters = { max_cd=1}, } newEffect{ @@ -1533,18 +1529,18 @@ newEffect{ newEffect{ name = "HASTE", image = "talents/haste.png", desc = "Haste", - long_desc = function(self, eff) return ("Increases global action speed by %d%%."):format(eff.power * 100) end, + long_desc = function(self, eff) return ("Increases movement speed by %d%% and attack, spell, and mind speed by %d%%."):format(eff.move * 100, eff.speed * 100) end, type = "magical", subtype = { temporal=true }, status = "beneficial", - parameters = { power=0.1 }, + parameters = { move=0.1, speed=0.1 }, on_gain = function(self, err) return "#Target# speeds up.", "+Haste" end, on_lose = function(self, err) return "#Target# slows down.", "-Haste" end, - damageOnAct = function(self, eff) - DamageType:get(DamageType.MATTER).projector(eff.src or self, self.x, self.y, DamageType.MATTER, eff.src:spellCrit(eff.dam)) - end, activate = function(self, eff) - eff.glbid = self:addTemporaryValue("global_speed_add", eff.power) + self:effectTemporaryValue(eff, "movement_speed", eff.move) + self:effectTemporaryValue(eff, "combat_physspeed", eff.speed) + self:effectTemporaryValue(eff, "combat_spellspeed", eff.speed) + self:effectTemporaryValue(eff, "combat_mindspeed", eff.speed) end, deactivate = function(self, eff) self:removeTemporaryValue("global_speed_add", eff.glbid) @@ -1554,11 +1550,11 @@ newEffect{ newEffect{ name = "CEASE_TO_EXIST", image = "talents/cease_to_exist.png", desc = "Cease to Exist", - long_desc = function(self, eff) return ("The target is being removed from the timeline, reducing all resistances by %d%%."):format(eff.power) end, + long_desc = function(self, eff) return ("The target is being removed from the timeline, all its resistances have been lowered by %d%%."):format(eff.power) end, type = "magical", subtype = { temporal=true }, status = "detrimental", - parameters = { power = 1 }, + parameters = { power = 1, damage=1 }, on_gain = function(self, err) return "#Target# is being removed from the timeline.", "+Cease to Exist" end, activate = function(self, eff) eff.resists = self:addTemporaryValue("resists", { all = -eff.power}) @@ -1634,13 +1630,13 @@ newEffect{ newEffect{ name = "SPIN_FATE", image = "talents/spin_fate.png", desc = "Spin Fate", - long_desc = function(self, eff) return ("The target's defense and saves have been increased by %d and it's resist all by %d%%."):format(eff.save_bonus * eff.spin, eff.resists * eff.spin) end, + long_desc = function(self, eff) return ("The target's defense and saves have been increased by %d."):format(eff.save_bonus * eff.spin) end, display_desc = function(self, eff) return eff.spin.." Spin" end, charges = function(self, eff) return eff.spin end, type = "magical", subtype = { temporal=true }, status = "beneficial", - parameters = { save_bonus=0, spin=0, resists=0, max_spin=3}, + parameters = { save_bonus=0, spin=0, max_spin=3}, on_gain = function(self, err) return "#Target# spins fate.", "+Spin Fate" end, on_lose = function(self, err) return "#Target#'s fate is no longer being spun.", "-Spin Fate" end, on_merge = function(self, old_eff, new_eff) @@ -1649,7 +1645,6 @@ newEffect{ self:removeTemporaryValue("combat_physresist", old_eff.physid) self:removeTemporaryValue("combat_spellresist", old_eff.spellid) self:removeTemporaryValue("combat_mentalresist", old_eff.mentalid) - self:removeTemporaryValue("resists", old_eff.resid) -- add some spin old_eff.spin = math.min(old_eff.spin + 1, new_eff.max_spin) @@ -1659,7 +1654,6 @@ newEffect{ old_eff.physid = self:addTemporaryValue("combat_physresist", old_eff.save_bonus * old_eff.spin) old_eff.spellid = self:addTemporaryValue("combat_spellresist", old_eff.save_bonus * old_eff.spin) old_eff.mentalid = self:addTemporaryValue("combat_mentalresist", old_eff.save_bonus * old_eff.spin) - old_eff.resid = self:addTemporaryValue("resists", {all = old_eff.resists * old_eff.spin}) old_eff.dur = new_eff.dur @@ -1671,7 +1665,6 @@ newEffect{ eff.physid = self:addTemporaryValue("combat_physresist", eff.save_bonus * eff.spin) eff.spellid = self:addTemporaryValue("combat_spellresist", eff.save_bonus * eff.spin) eff.mentalid = self:addTemporaryValue("combat_mentalresist", eff.save_bonus * eff.spin) - eff.resid = self:addTemporaryValue("resists", {all = eff.resists *eff.spin}) eff.particle = self:addParticles(Particles.new("arcane_power", 1)) end, deactivate = function(self, eff) @@ -1679,7 +1672,6 @@ newEffect{ self:removeTemporaryValue("combat_physresist", eff.physid) self:removeTemporaryValue("combat_spellresist", eff.spellid) self:removeTemporaryValue("combat_mentalresist", eff.mentalid) - self:removeTemporaryValue("resists", eff.resid) self:removeParticles(eff.particle) end, } @@ -2311,10 +2303,8 @@ newEffect{ self:effectTemporaryValue(eff, "resists", {[DamageType.TEMPORAL] = 30}) self:effectTemporaryValue(eff, "resists_pen", {[DamageType.TEMPORAL] = 20}) self:effectTemporaryValue(eff, "talent_cd_reduction", {[self.T_ANOMALY_REARRANGE] = -4, [self.T_ANOMALY_TEMPORAL_STORM] = -4}) - self:effectTemporaryValue(eff, "paradox_reduce_anomalies", 200) self:learnTalent(self.T_ANOMALY_REARRANGE, true) self:learnTalent(self.T_ANOMALY_TEMPORAL_STORM, true) - self:incParadox(400) self.replace_display = mod.class.Actor.new{ image = "npc/elemental_temporal_telugoroth.png", @@ -2325,7 +2315,6 @@ newEffect{ game.level.map:updateMap(self.x, self.y) end, deactivate = function(self, eff) - self:incParadox(-400) self:unlearnTalent(self.T_ANOMALY_REARRANGE) self:unlearnTalent(self.T_ANOMALY_TEMPORAL_STORM) self.replace_display = nil @@ -2582,6 +2571,14 @@ newEffect{ self:project({type="cone", cone_angle=25, range=0, radius=4}, src.x, src.y, DamageType.ACID, eff.dam) game.level.map:particleEmitter(self.x, self.y, 4, "breath_acid", {radius=4, tx=src.x-self.x, ty=src.y-self.y, spread=20}) end, + activate = function(self, eff) + if core.shader.active(4) then + eff.particle = self:addParticles(Particles.new("shader_ring_rotating", 1, {z=5, rotation=0, radius=1.1, img="alchie_acid"}, {type="lightningshield"})) + end + end, + deactivate = function(self, eff) + self:removeParticles(eff.particle) + end, } newEffect{ @@ -2870,28 +2867,10 @@ newEffect{ end, } -newEffect{ - name = "TIME_DILATION", image = "talents/time_dilation.png", - desc = "Time Dilation", - long_desc = function(self, eff) return ("Reduces global action speed by %d%%."):format(eff.slow * 100) end, - type = "magical", - subtype = { temporal=true, slow=true }, - status = "detrimental", - parameters = { slow=0.1}, - on_gain = function(self, err) return "#Target# slows down.", "+Time Dilation" end, - on_lose = function(self, err) return "#Target# speeds up.", "-Time Dilation" end, - activate = function(self, eff) - eff.tmpid = self:addTemporaryValue("global_speed_add", -eff.slow) - end, - deactivate = function(self, eff) - self:removeTemporaryValue("global_speed_add", eff.tmpid) - end, -} - newEffect{ name = "DIMENSIONAL_ANCHOR", image = "talents/dimensional_anchor.png", desc = "Dimensional Anchor", - long_desc = function(self, eff) return ("The target is unable to teleport and takes %0.2f temporal and %0.2f physical damage if they try."):format(eff.slow * 100) end, + long_desc = function(self, eff) return ("The target is unable to teleport and takes %0.2f temporal and %0.2f physical damage if they try."):format(eff.damage, eff.damage) end, type = "magical", subtype = { temporal=true, slow=true }, status = "detrimental", @@ -2941,15 +2920,27 @@ newEffect{ parameters = { power=0 }, on_gain = function(self, err) return "#Target#'s lifeline has been braided.", "+Braided" end, on_lose = function(self, err) return "#Target#'s lifeline is no longer braided.", "-Braided" end, - on_timeout = function(self, eff) - local braids = 0 - if eff.braid_one and eff.braid_one:hasEffect(eff.braid_one.EFF_BRAIDED) and not eff.braid_one.dead then - braids = braids + 1 + doBraid = function(self, eff, dam) + local braid_damage = dam * eff.power/ 100 + for i = 1, #eff.targets do + local target = eff.targets[i] + if target ~= self and not target.dead then + game:delayedLogMessage(eff.src, target, "braided", "#CRIMSON##Source# damages #Target# through the Braid!") + game:delayedLogDamage(eff.src, target, braid_damage, ("#PINK#%d braided #LAST#"):format(braid_damage), false) + target:takeHit(braid_damage, eff.src) + end end - if eff.braid_two and eff.braid_two:hasEffect(eff.braid_two.EFF_BRAIDED) and not eff.braid_two.dead then - braids = braids + 1 + end, + on_timeout = function(self, eff) + local alive = false + for i = 1, #eff.targets do + local target = eff.targets[i] + if target ~=self and not target.dead then + alive = true + break + end end - if braids == 0 then + if not alive then self:removeEffect(self.EFF_BRAIDED) end end, @@ -2980,16 +2971,16 @@ newEffect{ newEffect{ name = "WEBS_OF_FATE", image = "talents/webs_of_fate.png", desc = "Webs of Fate", - long_desc = function(self, eff) return ("Moving along the webs of fate, increasing move speed by %d%% and pin immunity by %d%%."):format(eff.move*100, eff.pin*100) end, + long_desc = function(self, eff) return ("Moving along the webs of fate, increasing stun and pin immunity by %d%%."):format(eff.imm*100) end, type = "magical", subtype = { temporal=true, speed=true }, status = "beneficial", on_gain = function(self, err) return "#Target# moves along the webs of fate.", "+Fate Webs" end, on_lose = function(self, err) return "#Target# is no longer moving along the webs of fate.", "-Fate Webs" end, - parameters = { move=0.1, pin=0.1 }, + parameters = { imm=0.1 }, activate = function(self, eff) - self:effectTemporaryValue(eff, "pin_immune", eff.pin) - self:effectTemporaryValue(eff, "movement_speed", eff.move) + self:effectTemporaryValue(eff, "pin_immune", eff.imm) + self:effectTemporaryValue(eff, "stun_immune", eff.imm) end, deactivate = function(self, eff) end, @@ -2998,18 +2989,45 @@ newEffect{ newEffect{ name = "SEAL_FATE", image = "talents/seal_fate.png", desc = "Seal Fate", - long_desc = function(self, eff) return ("The target is sealing fate, increasing critical chance and critical power by %d%%."):format(eff.crit) end, - type = "mental", + long_desc = function(self, eff) return ("The target is sealing fate, increasing the duration of detrimental status effects on targets it damages by one."):format() end, + type = "magical", subtype = { focus=true }, status = "beneficial", - parameters = { crit=10 }, + parameters = { procs=1 }, on_gain = function(self, err) return "#Target# seals fate.", "+Seal Fate" end, on_lose = function(self, err) return "#Target# is no longer sealing fate.", "-Seal Fate" end, + doDamage = function(self, eff, target) + if self.turn_procs and target.tmp then + if self.turn_procs.seal_fate and self.turn_procs.seal_fate >= eff.procs then return end + local chance = 50 + local spin = self:hasEffect(self.EFF_SPIN_FATE) + if spin then + chance = chance * (1 + spin.spin/3) + end + + if rng.percent(chance) then + local effs = {} + -- Go through all spell effects + for eff_id, p in pairs(target.tmp) do + local e = target.tempeffect_def[eff_id] + if e.status == "detrimental" and e.type ~= "other" then + effs[#effs+1] = p + end + end + + if #effs > 0 then + local p = rng.table(effs) + game.logPlayer(self, "%s", p.name) + game.logPlayer(self, "%s", p.dur) + p.dur = p.dur + 1 + end + + self.turn_procs.seal_fate = (self.turn_procs.seal_fate or 0) + 1 + end + + end + end, activate = function(self, eff) - self:effectTemporaryValue(eff, "combat_physcrit", eff.crit) - self:effectTemporaryValue(eff, "combat_spellcrit", eff.crit) - self:effectTemporaryValue(eff, "combat_mindcrit", eff.crit) - self:effectTemporaryValue(eff, "combat_critical_power", eff.crit) end, } @@ -3045,6 +3063,80 @@ newEffect{ end, } +newEffect{ + name = "ENTROPY", image = "talents/entropy.png", + desc = "Entropy", + long_desc = function(self, eff) return ("The target's timed effects are ticking twice as fast and it's taking %d temporal damage per turn, per timed effect."):format(eff.power) end, + on_gain = function(self, err) return "#Target# is caught in an entropic field!", "+Entropy" end, + on_lose = function(self, err) return "#Target# is free from the entropy.", "-Entropy" end, + type = "magical", + subtype = { temporal=true }, + status = "detrimental", + parameters = {power=10}, + on_timeout = function(self, eff) + local count = 0 + local todel = {} + + -- Go through all spell effects + for eff_id, p in pairs(self.tmp) do + local e = self.tempeffect_def[eff_id] + if e.type ~= "other" and e.status == "beneficial" then + if p.dur <= 0 then + todel[#todel+1] = eff + else + if e.on_timeout then + if p.src then p.src.__project_source = p end -- intermediate projector source + if e.on_timeout(self, p) then + todel[#todel+1] = eff + end + if p.src then p.src.__project_source = nil end + end + end + count = count + 1 + p.dur = p.dur - e.decrease + end + end + + DamageType:get(DamageType.TEMPORAL).projector(eff.src, self.x, self.y, DamageType.TEMPORAL, eff.power*count) + + while #todel > 0 do + self:removeEffect(table.remove(todel)) + end + end, +} + +newEffect{ + name = "PHASE_SHIFT", image = "talents/phase_shift.png", + desc = "Phase Shift", + long_desc = function(self, eff) return ("When hit for more than 10%% of your maximum life you teleport and reappear near where you were, reducuing the damage by 50%%.") end, + type = "magical", + subtype = { arcane=true }, + status = "beneficial", + parameters = { }, + activate = function(self, eff) + self:effectTemporaryValue(eff, "phase_shift_chrono", 1) + end, +} + +newEffect{ + name = "REGRESSION", image = "talents/temporal_bolt.png", + desc = "Regression", + long_desc = function(self, eff) return ("Reduces your three highest stats by %d."):format(eff.power) end, + on_gain = function(self, err) return "#Target# has regressed.", "+Regression" end, + on_lose = function(self, err) return "#Target# has returned to its natural state.", "-Regression" end, + type = "physical", + subtype = { temporal=true }, + status = "detrimental", + parameters = { power=1}, + activate = function(self, eff) + local l = { {Stats.STAT_STR, self:getStat("str")}, {Stats.STAT_DEX, self:getStat("dex")}, {Stats.STAT_CON, self:getStat("con")}, {Stats.STAT_MAG, self:getStat("mag")}, {Stats.STAT_WIL, self:getStat("wil")}, {Stats.STAT_CUN, self:getStat("cun")}, } + table.sort(l, function(a,b) return a[2] > b[2] end) + local inc = {} + for i = 1, 3 do inc[l[i][1]] = -eff.power end + self:effectTemporaryValue(eff, "inc_stats", inc) + end, +} + newEffect{ name = "TRIM_THREADS", image = "talents/trim_threads.png", desc = "Trim Threads", @@ -3067,4 +3159,4 @@ newEffect{ on_timeout = function(self, eff) DamageType:get(DamageType.TEMPORAL).projector(eff.src, self.x, self.y, DamageType.TEMPORAL, eff.power) end, -} +} \ No newline at end of file diff --git a/game/modules/tome/data/timed_effects/mental.lua b/game/modules/tome/data/timed_effects/mental.lua index 3e7aae0debbf7ebad7e9969070e48e840e41d361..e56ed2a05352bc2d3057394433a1bc468eeaf626 100644 --- a/game/modules/tome/data/timed_effects/mental.lua +++ b/game/modules/tome/data/timed_effects/mental.lua @@ -109,7 +109,7 @@ newEffect{ ai = self.ai, } self.faction = eff.src.faction - + self.ai_state.tactic_leash = 100 self.remove_from_party_on_death = true self.no_inventory_access = true @@ -219,7 +219,7 @@ newEffect{ newEffect{ name = "GLOOM_WEAKNESS", image = "effects/gloom_weakness.png", desc = "Gloom Weakness", - long_desc = function(self, eff) return ("The gloom reduces damage the target inflicts by %d%%."):format(-eff.incDamageChange) end, + long_desc = function(self, eff) return ("The gloom reduces damage the target inflicts by %d%%."):format(eff.incDamageChange) end, type = "mental", subtype = { gloom=true }, status = "detrimental", @@ -228,7 +228,7 @@ newEffect{ on_lose = function(self, err) return "#F53CBE##Target# is no longer weakened." end, activate = function(self, eff) eff.particle = self:addParticles(Particles.new("gloom_weakness", 1)) - eff.incDamageId = self:addTemporaryValue("inc_damage", {all = eff.incDamageChange}) + eff.incDamageId = self:addTemporaryValue("inc_damage", {all = -eff.incDamageChange}) end, deactivate = function(self, eff) self:removeParticles(eff.particle) @@ -1843,7 +1843,7 @@ newEffect{ self:removeTemporaryValue("max_life", eff.life_id) self:removeTemporaryValue("life_regen", eff.life_regen_id) self:removeTemporaryValue("stamina_regen", eff.stamina_regen_id) - + self:removeTemporaryValue("life",eff.templife_id) -- remove extra hps to prevent excessive heals at high level game.logSeen(self, "%s no longer revels in blood quite so much.",self.name:capitalize()) end, @@ -2438,7 +2438,7 @@ newEffect{ eff.what = "physical, nature, acid, temporal" elseif eff.kind == "thermal" then eff.sid = self:addTemporaryValue("flat_damage_armor", { - [DamageType.FIRE] = eff.power, + [DamageType.FIRE] = eff.power, [DamageType.COLD] = eff.power, [DamageType.LIGHT] = eff.power, [DamageType.ARCANE] = eff.power, @@ -2446,7 +2446,7 @@ newEffect{ eff.what = "fire, cold, light, arcane" elseif eff.kind == "charged" then eff.sid = self:addTemporaryValue("flat_damage_armor", { - [DamageType.LIGHTNING] = eff.power, + [DamageType.LIGHTNING] = eff.power, [DamageType.BLIGHT] = eff.power, [DamageType.MIND] = eff.power, [DamageType.DARKNESS] = eff.power, @@ -2888,21 +2888,6 @@ newEffect{ end, } -newEffect{ - name = "DRACONIC_WILL", image = "talents/draconic_will.png", - desc = "Draconic Will", - long_desc = function(self, eff) return "The target is immune to all detrimental effects." end, - type = "mental", - subtype = { nature=true }, - status = "beneficial", - on_gain = function(self, err) return "#Target#'s skin hardens.", "+Draconic Will" end, - on_lose = function(self, err) return "#Target#'s skin is back to normal.", "-Draconic Will" end, - parameters = { }, - activate = function(self, eff) - self:effectTemporaryValue(eff, "negative_status_effect_immune", 1) - end, -} - newEffect{ name = "HIDDEN_RESOURCES", image = "talents/hidden_resources.png", desc = "Hidden Resources", @@ -2980,7 +2965,7 @@ newEffect{ eff.particle = self:addParticles(Particles.new("darkness_power", 1)) end, deactivate = function(self, eff) - self:removeParticles(eff.particle) + self:removeParticles(eff.particle) end, } @@ -2997,7 +2982,7 @@ newEffect{ eff.particle = self:addParticles(Particles.new("darkness_power", 1)) end, deactivate = function(self, eff) - self:removeParticles(eff.particle) + self:removeParticles(eff.particle) end, } @@ -3012,35 +2997,10 @@ newEffect{ parameters = { }, on_gain = function(self, err) return "#Target# glints with a crystaline aura", "+Crystal Resonance" end, on_lose = function(self, err) return "#Target# is no longer glinting.", "-Crystal Resonance" end, - gem_types = { - GEM_DIAMOND = function(self, eff) return {self:effectTemporaryValue(eff, "inc_stats", {[Stats.STAT_STR] = 5, [Stats.STAT_DEX] = 5, [Stats.STAT_MAG] = 5, [Stats.STAT_WIL] = 5, [Stats.STAT_CUN] = 5, [Stats.STAT_CON] = 5 }), } end, - GEM_PEARL = function(self, eff) return {self:effectTemporaryValue(eff,"resists", { all = 5}), self:effectTemporaryValue(eff,"combat_armour", 5) } end, - GEM_MOONSTONE = function(self, eff) return {self:effectTemporaryValue(eff,"combat_def", 10), self:effectTemporaryValue(eff,"combat_mentalresist", 10), self:effectTemporaryValue(eff,"combat_spellresist", 10), self:effectTemporaryValue(eff,"combat_physresist", 10), } end, - GEM_FIRE_OPAL = function(self, eff) return {self:effectTemporaryValue(eff,"inc_damage", { all = 10}), self:effectTemporaryValue(eff,"combat_physcrit", 5), self:effectTemporaryValue(eff,"combat_mindcrit", 5), self:effectTemporaryValue(eff,"combat_spellcrit", 5) } end, - GEM_BLOODSTONE = function(self, eff) return {self:effectTemporaryValue(eff,"stun_immune", 0.6) } end, - GEM_RUBY = function(self, eff) return {self:effectTemporaryValue(eff,"inc_stats", {[Stats.STAT_STR] = 4, [Stats.STAT_DEX] = 4, [Stats.STAT_MAG] = 4, [Stats.STAT_WIL] = 4, [Stats.STAT_CUN] = 4, [Stats.STAT_CON] = 4 }) } end, - GEM_AMBER = function(self, eff) return {self:effectTemporaryValue(eff,"inc_damage", { all = 8}), self:effectTemporaryValue(eff,"combat_physcrit", 4), self:effectTemporaryValue(eff,"combat_mindcrit", 4), self:effectTemporaryValue(eff,"combat_spellcrit", 4) } end, - GEM_TURQUOISE = function(self, eff) return {self:effectTemporaryValue(eff,"see_stealth", 10), self:effectTemporaryValue(eff,"see_invisible", 10) } end, - GEM_JADE = function(self, eff) return {self:effectTemporaryValue(eff,"resists", { all = 4}), self:effectTemporaryValue(eff,"combat_armour", 4) } end, - GEM_SAPPHIRE = function(self, eff) return {self:effectTemporaryValue(eff,"combat_def", 8), self:effectTemporaryValue(eff,"combat_mentalresist", 8), self:effectTemporaryValue(eff,"combat_spellresist", 8), self:effectTemporaryValue(eff,"combat_physresist", 8), } end, - GEM_QUARTZ = function(self, eff) return {self:effectTemporaryValue(eff,"stun_immune", 0.3) } end, - GEM_EMERALD = function(self, eff) return {self:effectTemporaryValue(eff,"resists", { all = 3}), self:effectTemporaryValue(eff,"combat_armour", 3) } end, - GEM_LAPIS_LAZULI = function(self, eff) return {self:effectTemporaryValue(eff,"combat_def", 6), self:effectTemporaryValue(eff,"combat_mentalresist", 6), self:effectTemporaryValue(eff,"combat_spellresist", 6), self:effectTemporaryValue(eff,"combat_physresist", 6), } end, - GEM_GARNET = function(self, eff) return {self:effectTemporaryValue(eff,"inc_damage", { all = 6}), self:effectTemporaryValue(eff,"combat_physcrit", 3), self:effectTemporaryValue(eff,"combat_mindcrit", 3), self:effectTemporaryValue(eff,"combat_spellcrit", 3) } end, - GEM_ONYX = function(self, eff) return {self:effectTemporaryValue(eff,"inc_stats", {[Stats.STAT_STR] = 3, [Stats.STAT_DEX] = 3, [Stats.STAT_MAG] = 3, [Stats.STAT_WIL] = 3, [Stats.STAT_CUN] = 3, [Stats.STAT_CON] = 3 }) } end, - GEM_AMETHYST = function(self, eff) return {self:effectTemporaryValue(eff,"inc_damage", { all = 4}), self:effectTemporaryValue(eff,"combat_physcrit", 2), self:effectTemporaryValue(eff,"combat_mindcrit", 2), self:effectTemporaryValue(eff,"combat_spellcrit", 2) } end, - GEM_OPAL = function(self, eff) return {self:effectTemporaryValue(eff,"inc_stats", {[Stats.STAT_STR] = 2, [Stats.STAT_DEX] = 2, [Stats.STAT_MAG] = 2, [Stats.STAT_WIL] = 2, [Stats.STAT_CUN] = 2, [Stats.STAT_CON] = 2 }) } end, - GEM_TOPAZ = function(self, eff) return {self:effectTemporaryValue(eff,"combat_def", 4), self:effectTemporaryValue(eff,"combat_mentalresist", 4), self:effectTemporaryValue(eff,"combat_spellresist", 4), self:effectTemporaryValue(eff,"combat_physresist", 4), } end, - GEM_AQUAMARINE = function(self, eff) return {self:effectTemporaryValue(eff,"resists", { all = 2}), self:effectTemporaryValue(eff,"combat_armour", 2) } end, - GEM_AMETRINE = function(self, eff) return {self:effectTemporaryValue(eff,"inc_damage", { all = 2}), self:effectTemporaryValue(eff,"combat_physcrit", 1), self:effectTemporaryValue(eff,"combat_mindcrit", 1), self:effectTemporaryValue(eff,"combat_spellcrit", 1) } end, - GEM_ZIRCON = function(self, eff) return {self:effectTemporaryValue(eff,"resists", { all = 1}), self:effectTemporaryValue(eff,"combat_armour", 1) } end, - GEM_SPINEL = function(self, eff) return {self:effectTemporaryValue(eff,"combat_def", 2), self:effectTemporaryValue(eff,"combat_mentalresist", 2), self:effectTemporaryValue(eff,"combat_spellresist", 2), self:effectTemporaryValue(eff,"combat_physresist", 2), } end, - GEM_CITRINE = function(self, eff) return {self:effectTemporaryValue(eff,"lite", 1), self:effectTemporaryValue(eff,"infravision", 2) } end, - GEM_AGATE = function(self, eff) return {self:effectTemporaryValue(eff,"inc_stats", {[Stats.STAT_STR] = 1, [Stats.STAT_DEX] = 1, [Stats.STAT_MAG] = 1, [Stats.STAT_WIL] = 1, [Stats.STAT_CUN] = 1, [Stats.STAT_CON] = 1 }) } end, - }, activate = function(self, eff) - local buff = self.tempeffect_def.EFF_CRYSTAL_BUFF.gem_types[eff.gem] - eff.id1 = buff(self, eff) + for a, b in pairs(eff.effects) do + self:effectTemporaryValue(eff, a, b) + end end, deactivate = function(self, eff) @@ -3145,7 +3105,7 @@ newEffect{ activate = function(self, eff) self:effectTemporaryValue(eff, "die_at", -eff.power) end, - deactivate = function(self, eff) + deactivate = function(self, eff) end, } @@ -3163,7 +3123,7 @@ newEffect{ eff.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, oversize=1.7, a=155, appear=8, speed=0, img="transcend_tele", radius=0})) self:callTalent(self.T_KINETIC_SHIELD, "adjust_shield_gfx", true) end, - deactivate = function(self, eff) + deactivate = function(self, eff) self:removeParticles(eff.particle) self:callTalent(self.T_KINETIC_SHIELD, "adjust_shield_gfx", false) end, @@ -3183,7 +3143,7 @@ newEffect{ eff.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, oversize=1.7, a=155, appear=8, speed=0, img="transcend_pyro", radius=0})) self:callTalent(self.T_THERMAL_SHIELD, "adjust_shield_gfx", true) end, - deactivate = function(self, eff) + deactivate = function(self, eff) self:removeParticles(eff.particle) self:callTalent(self.T_THERMAL_SHIELD, "adjust_shield_gfx", false) end, @@ -3203,7 +3163,7 @@ newEffect{ eff.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, oversize=1.7, a=155, appear=8, speed=0, img="transcend_electro", radius=0})) self:callTalent(self.T_CHARGED_SHIELD, "adjust_shield_gfx", true) end, - deactivate = function(self, eff) + deactivate = function(self, eff) self:removeParticles(eff.particle) self:callTalent(self.T_CHARGED_SHIELD, "adjust_shield_gfx", false) end, diff --git a/game/modules/tome/data/timed_effects/other.lua b/game/modules/tome/data/timed_effects/other.lua index e5c35cb2c8a1a87c1592ab1113c0702c0ffdcaf9..68aa850bba3cffea88b8ce58a068883863a3e62e 100644 --- a/game/modules/tome/data/timed_effects/other.lua +++ b/game/modules/tome/data/timed_effects/other.lua @@ -396,48 +396,24 @@ newEffect{ newEffect{ name = "DAMAGE_SMEARING", image = "talents/damage_smearing.png", desc = "Damage Smearing", - long_desc = function(self, eff) return ("Passes damage received in the present onto the future self."):format(eff.power) end, - type = "other", - subtype = { time=true }, - status = "beneficial", - parameters = { power=10 }, - on_gain = function(self, err) return "The fabric of time alters around #target#.", "+Damage Smearing" end, - on_lose = function(self, err) return "The fabric of time around #target# stabilizes.", "-Damage Smearing" end, - activate = function(self, eff) - eff.particle = self:addParticles(Particles.new("time_shield", 1)) - end, - deactivate = function(self, eff) - self:removeParticles(eff.particle) - end, -} - -newEffect{ - name = "SMEARED", - desc = "Smeared", - long_desc = function(self, eff) return ("Damage received in the past is returned as %0.2f temporal damage per turn."):format(eff.power) end, + long_desc = function(self, eff) return ("Damage received in the past is returned as %0.2f temporal damage per turn."):format(eff.dam) end, type = "other", subtype = { time=true }, status = "detrimental", - parameters = { power=10 }, + parameters = { dam=10 }, on_gain = function(self, err) return "#Target# is taking damage received in the past!", "+Smeared" end, on_lose = function(self, err) return "#Target# stops taking damage received in the past.", "-Smeared" end, on_merge = function(self, old_eff, new_eff) -- Merge the flames! - local olddam = old_eff.power * old_eff.dur - local newdam = new_eff.power * new_eff.dur - local dur = math.ceil((old_eff.dur + new_eff.dur) / 2) - old_eff.dur = dur - old_eff.power = (olddam + newdam) / dur - return old_eff + local olddam = old_eff.dam * old_eff.dur + local newdam = new_eff.dam * new_eff.dur + new_eff.dam = (olddam + newdam) / new_eff.dur + return new_eff end, on_timeout = function(self, eff) - DamageType:get(DamageType.TEMPORAL).projector(eff.src, self.x, self.y, DamageType.TEMPORAL, eff.power) - end, - activate = function(self, eff) - eff.particle = self:addParticles(Particles.new("time_shield", 1)) - end, - deactivate = function(self, eff) - self:removeParticles(eff.particle) + local dead, val = self:takeHit(eff.dam, self, {special_death_msg="was smeared across all space and time"}) + + game:delayedLogDamage(eff, self, val, ("%s%d %s#LAST#"):format(DamageType:get(DamageType.TEMPORAL).text_color or "#aaaaaa#", math.ceil(val), DamageType:get(DamageType.TEMPORAL).name), false) end, } @@ -2260,7 +2236,14 @@ newEffect{ self:effectTemporaryValue(eff, "status_effect_immune", 1) self.energy.value = 0 end, - deactivate = function(self, eff) + deactivate = function(self, eff) --wake up vaulted npcs in LOS + self:computeFOV(5, nil, + function(x, y, dx, dy, sqdist) + local act = game.level.map(x, y, Map.ACTOR) + if act then + act:removeEffect(act.EFF_VAULTED, true, true) + end + end, true, false, false) end, } @@ -2462,13 +2445,9 @@ newEffect{ subtype = { time=true }, status = "beneficial", parameters = { power=10}, - on_gain = function(self, err) return "#Target# retunes the fabric of spaceime.", "+Spacetime Tuning" end, + on_gain = function(self, err) return "#Target# retunes the fabric of spacetime.", "+Spacetime Tuning" end, on_timeout = function(self, eff) - if not self.resting then - self:removeEffect(self.EFF_SPACETIME_TUNING) - else - self:incParadox(eff.power) - end + self:incParadox(eff.power) end, } @@ -2479,17 +2458,17 @@ newEffect{ return ("The target has stopped time and is dealing %d%% less damage."):format(eff.power) end, charges = function(self, eff) - local energy = self.energy.value - if energy < 2000 then + local charges = math.floor(self.energy.value/1000) - 1 + if charges <= 0 then self:removeEffect(self.EFF_TIME_STOP) end - return math.floor(energy/1000) + return charges end, type = "other", subtype = {time=true}, status = "detrimental", - decrease = 0, - no_stop_enter_worlmap = true, no_stop_resting = true, + --decrease = 0, + --no_stop_enter_worlmap = true, no_stop_resting = true, parameters = {power=50}, activate = function(self, eff) self:effectTemporaryValue(eff, "generic_damage_penalty", eff.power) @@ -2509,18 +2488,8 @@ newEffect{ on_timeout = function(self, eff) -- Clone protection if not self.on_die then return end - -- Certain talents don't cooldown in the reprieve - if not self:attr("no_talents_cooldown") then - for tid, _ in pairs(self.talents_cd) do - local t = self:getTalentFromId(tid) - if t and t.fixed_cooldown then - self.talents_cd[tid] = self.talents_cd[tid] + 1 - end - end - end end, activate = function(self, eff) - end, deactivate = function(self, eff) -- Clone protection @@ -2581,4 +2550,106 @@ newEffect{ end) end, +} + +newEffect{ + name = "TEMPORAL_FUGUE", image = "talents/temporal_fugue.png", + desc = "Temporal Fugue", + long_desc = function(self, eff) return "This target is splitting all damage with its fugue clones." end, + type = "other", + subtype = { time=true }, + status = "beneficial", + parameters = { power=10 }, + decrease = 0, + callbackOnHit = function(self, eff, cb, src) + local clones = {} + -- Find our clones + for i = 1, #eff.targets do + local target = eff.targets[i] + if target ~= self and not target.dead and game.level:hasEntity(target) then + clones[#clones+1] = target + end + end + + -- Split the damage + if #clones > 0 and not src.turn_procs.temporal_fugue_damage then + self.turn_procs.temporal_fugue_damage = true + cb.value = cb.value/#clones + game:delayedLogMessage(self, nil, "fugue_damage", "#STEEL_BLUE##Source# shares damage with %s fugue clones!", string.his_her(self)) + for i = 1, #clones do + local target = clones[i] + if target ~= self then + target:takeHit(cb.value, self) + game:delayedLogDamage(src or self, self, 0, ("#STEEL_BLUE#(%d shared)#LAST#"):format(cb.value), nil) + end + end + + self.turn_procs.temporal_fugue_damage = nil + end + + -- If we're the last clone remove the effect + if #clones <= 0 then + self:removeEffect(self.EFF_TEMPORAL_FUGUE) + end + + return cb.value + end, + on_timeout = function(self, eff) + -- Temporal Fugue does not cooldown while active + if self.talents_cd[self.T_TEMPORAL_FUGUE] then + self.talents_cd[self.T_TEMPORAL_FUGUE] = self.talents_cd[self.T_TEMPORAL_FUGUE] + 1 + end + + local alive = false + for i = 1, #eff.targets do + local target = eff.targets[i] + if target ~=self and not target.dead then + alive = true + break + end + end + if not alive then + self:removeEffect(self.EFF_TEMPORAL_FUGUE) + end + end, + activate = function(self, eff) + self:effectTemporaryValue(eff, "generic_damage_penalty", 66) + end, +} + +newEffect{ + name = "DRACONIC_WILL", image = "talents/draconic_will.png", + desc = "Draconic Will", + long_desc = function(self, eff) return "The target is immune to all detrimental effects." end, + type = "other", + subtype = { nature=true }, + status = "beneficial", + on_gain = function(self, err) return "#Target#'s skin hardens.", "+Draconic Will" end, + on_lose = function(self, err) return "#Target#'s skin is back to normal.", "-Draconic Will" end, + parameters = { }, + activate = function(self, eff) + self:effectTemporaryValue(eff, "negative_status_effect_immune", 1) + end, +} + +newEffect{ + name = "PRESERVE_PATTERN", image = "talents/preserve_pattern.png", + desc = "Preserve Pattern", + long_desc = function(self, eff) return ("Damage received in the past is returned as %0.2f paradox damage per turn."):format(eff.paradox) end, + type = "other", + subtype = { time=true }, + status = "detrimental", + parameters = { paradox=10 }, + on_gain = function(self, err) return "#Target# converts damage into paradox.", "+Preserve" end, + on_lose = function(self, err) return "#Target# stops converting damage to paradox..", "-Preserve" end, + on_merge = function(self, old_eff, new_eff) + -- Merge the flames! + local oldparadox = old_eff.paradox * old_eff.dur + local newparadox = new_eff.paradox * new_eff.dur + new_eff.paradox = (oldparadox + newparadox) / new_eff.dur + return new_eff + end, + on_timeout = function(self, eff) + self:incParadox(eff.paradox) + end, } \ No newline at end of file diff --git a/game/modules/tome/data/timed_effects/physical.lua b/game/modules/tome/data/timed_effects/physical.lua index d293877a7088c8abceb3909a4e4d9d47535788b1..21efc23075a46055315518cbc0d4afbff222bdb2 100644 --- a/game/modules/tome/data/timed_effects/physical.lua +++ b/game/modules/tome/data/timed_effects/physical.lua @@ -439,7 +439,7 @@ newEffect{ for i = 1, 4 do local t = rng.tableRemove(tids) if not t then break end - self.talents_cd[t.id] = 1 -- Just set cooldown to 1 since cooldown does not decrease while stunned + self:startTalentCooldown(t.id, 1) -- Just set cooldown to 1 since cooldown does not decrease while stunned end end, on_timeout = function(self, eff) @@ -475,7 +475,7 @@ newEffect{ for i = 1, 3 do local t = rng.tableRemove(tids) if not t then break end - self.talents_cd[t.id] = 1 -- Just set cooldown to 1 since cooldown does not decrease while stunned + self:startTalentCooldown(t.id, 1) -- Just set cooldown to 1 since cooldown does not decrease while stunned end end, deactivate = function(self, eff) @@ -1025,7 +1025,7 @@ newEffect{ newEffect{ name = "BURROW", image = "talents/burrow.png", desc = "Burrow", - long_desc = function(self, eff) return "The target is able to burrow into walls." end, + long_desc = function(self, eff) return ("The target is able to burrow into walls, and additionally has %d more APR and %d%% more physical resistance penetration."):format(eff.power, eff.power / 2) end, type = "physical", subtype = { earth=true }, status = "beneficial", @@ -1033,6 +1033,8 @@ newEffect{ activate = function(self, eff) eff.pass = self:addTemporaryValue("can_pass", {pass_wall=1}) eff.dig = self:addTemporaryValue("move_project", {[DamageType.DIG]=1}) + self:effectTemporaryValue(eff, "combat_apr", eff.power) + self:effectTemporaryValue(eff, "resists_pen", {[DamageType.PHYSICAL]=eff.power / 2 }) end, deactivate = function(self, eff) self:removeTemporaryValue("can_pass", eff.pass) @@ -2051,7 +2053,9 @@ newEffect{ game:delayedLogMessage(self, src, "block_heal", "#CRIMSON##Source# heals from blocking with %s shield!", string.his_her(self)) end if eff.properties.ref and src.life then DamageType.defaultProjector(src, src.x, src.y, type, blocked, tmp, true) end + local full = false if (self:knowTalent(self.T_RIPOSTE) or amt == 0) and src.life then + full = true src:setEffect(src.EFF_COUNTERSTRIKE, (1 + dur_inc) * math.max(1, (src.global_speed or 1)), {power=eff.power, no_ct_effect=true, src=self, crit_inc=crit_inc, nb=nb}) if eff.properties.sb then if src:canBe("disarm") then @@ -2060,7 +2064,13 @@ newEffect{ game.logSeen(src, "%s resists the disarming attempt!", src.name:capitalize()) end end + if eff.properties.on_cs then + eff.properties.on_cs(self, eff, dam, type, src) + end end-- specify duration here to avoid stacking for high speed attackers + + self:fireTalentCheck("callbackOnBlock", eff, dam, type, src) + return amt end, activate = function(self, eff) @@ -2820,30 +2830,68 @@ newEffect { newEffect{ name = "CELERITY", image = "talents/celerity.png", desc = "Celerity", - long_desc = function(self, eff) return ("The target is moving is %d%% faster."):format(eff.speed * 100 * eff.stack) end, + long_desc = function(self, eff) return ("The target is moving is %d%% faster."):format(eff.speed * 100 * eff.charges) end, type = "physical", - display_desc = function(self, eff) return eff.stack.." Celerity" end, - charges = function(self, eff) return eff.stack end, + display_desc = function(self, eff) return eff.charges.." Celerity" end, + charges = function(self, eff) return eff.charges end, subtype = { speed=true, temporal=true }, status = "beneficial", - parameters = {speed=0.1, stack=1, max_stack=3}, + parameters = {speed=0.1, charges=1, max_charges=3}, on_merge = function(self, old_eff, new_eff) -- remove the old value self:removeTemporaryValue("movement_speed", old_eff.tmpid) -- add a charge - old_eff.stack = math.min(old_eff.stack + 1, new_eff.max_stack) + old_eff.charges = math.min(old_eff.charges + 1, new_eff.max_charges) -- and apply the current values - old_eff.tmpid = self:addTemporaryValue("movement_speed", old_eff.speed * old_eff.stack) + old_eff.tmpid = self:addTemporaryValue("movement_speed", old_eff.speed * old_eff.charges) old_eff.dur = new_eff.dur return old_eff end, activate = function(self, eff) - eff.tmpid = self:addTemporaryValue("movement_speed", eff.speed * eff.stack) + eff.tmpid = self:addTemporaryValue("movement_speed", eff.speed * eff.charges) end, deactivate = function(self, eff) self:removeTemporaryValue("movement_speed", eff.tmpid) end, } + +newEffect{ + name = "GRAVITY_SLOW", image = "talents/gravity_well.png", + desc = "Gravity Slow", + long_desc = function(self, eff) return ("The target is caught in a gravity well, reducing movement speed by %d%%."):format(eff.slow* 100) end, + type = "physical", + subtype = { speed=true }, + status = "detrimental", + parameters = { slow=0.15 }, + on_gain = function(self, err) return "#Target# is slowed by gravity.", "+Gravity Slow" end, + on_lose = function(self, err) return "#Target# is free from the gravity well.", "-Gravity Slow" end, + activate = function(self, eff) + eff.slowid = self:addTemporaryValue("movement_speed", -eff.slow) + end, + deactivate = function(self, eff) + self:removeTemporaryValue("movement_speed", eff.slowid) + end, +} + +newEffect{ + name = "ANTI_GRAVITY", image = "talents/gravity_locus.png", + desc = "Anti-Gravity", + long_desc = function(self, eff) return ("Target is caught in an anti-gravity field, halving its knockback resistance."):format() end, + type = "physical", + subtype = { spacetime=true }, + status = "detrimental", + on_gain = function(self, err) return nil, "+Anti-Gravity" end, + on_lose = function(self, err) return nil, "-Anti-Gravity" end, + on_merge = function(self, old_eff, new_eff) + old_eff.dur = new_eff.dur + return old_eff + end, + activate = function(self, eff) + if self:attr("knockback_immune") then + self:effectTemporaryValue(eff, "knockback_immune", -self:attr("knockback_immune") / 2) + end + end, +} \ No newline at end of file diff --git a/game/modules/tome/data/zones/eidolon-plane/zone.lua b/game/modules/tome/data/zones/eidolon-plane/zone.lua index acf6cc74b96a4cf71b59eb1611d6b8c6d9cddec7..1305eaf93914454f22e4a44435d6041802804766 100644 --- a/game/modules/tome/data/zones/eidolon-plane/zone.lua +++ b/game/modules/tome/data/zones/eidolon-plane/zone.lua @@ -31,6 +31,7 @@ return { zero_gravity = true, no_autoexplore = true, ambient_music = "Alchemist.ogg", + allow_respec = "limited", generator = { map = { class = "engine.generator.map.Empty", diff --git a/game/modules/tome/data/zones/shertul-fortress/grids.lua b/game/modules/tome/data/zones/shertul-fortress/grids.lua index 18bc2b959f56c80c2f9c8502b8a834ca2a588832..66492dbed65c8bf32dd8f3ab302d5d008a017532 100644 --- a/game/modules/tome/data/zones/shertul-fortress/grids.lua +++ b/game/modules/tome/data/zones/shertul-fortress/grids.lua @@ -25,7 +25,8 @@ local energycount = function(self) local tex, nblines, wline = nil local lastenergy = nil local shader = require("engine.Shader").default.textoutline and require("engine.Shader").default.textoutline.shad - local font = core.display.newFont("/data/font/DroidSansMono.ttf", 16) + local FontPackage = require "engine.FontPackage" + local font = FontPackage:get("mono") local UIBase = require "engine.ui.Base" local MyUI = require("engine.class").inherit(UIBase){} MyUI.ui = "metal" @@ -264,7 +265,8 @@ local dcb = function(self) local tex, nblines, wline = nil local DamageType = require "engine.DamageType" local shader = require("engine.Shader").default.textoutline and require("engine.Shader").default.textoutline.shad - local font = core.display.newFont("/data/font/DroidSansMono.ttf", 16) + local FontPackage = require "engine.FontPackage" + local font = FontPackage:get("mono") local UIBase = require "engine.ui.Base" local MyUI = require("engine.class").inherit(UIBase){} MyUI.ui = "metal" diff --git a/game/modules/tome/data/zones/tannen-tower/npcs.lua b/game/modules/tome/data/zones/tannen-tower/npcs.lua index 53196a6f9a9949f3eeeebef47cc34c3cf9c3691b..5746acf57b5915d907045b3c970eca09471282d9 100644 --- a/game/modules/tome/data/zones/tannen-tower/npcs.lua +++ b/game/modules/tome/data/zones/tannen-tower/npcs.lua @@ -56,6 +56,7 @@ newEntity{ define_as = "TANNEN", resolvers.drops{chance=100, nb=4, {tome_drops="boss"} }, resolvers.drops{chance=100, nb=1, {defined="ORB_MANY_WAYS2"} }, resolvers.drops{chance=100, nb=1, {defined="ATHAME_WEST2"} }, + resolvers.drops{chance=100, nb=1, {defined="NOTE4"} }, resists = { [DamageType.ACID] = 100, }, diff --git a/game/modules/tome/data/zones/tannen-tower/objects.lua b/game/modules/tome/data/zones/tannen-tower/objects.lua index 88e436b696625ef196c266d1dad75e5c1b66e59e..83aec8809233ad293872ea02ade99668cdcb40d9 100644 --- a/game/modules/tome/data/zones/tannen-tower/objects.lua +++ b/game/modules/tome/data/zones/tannen-tower/objects.lua @@ -103,3 +103,13 @@ If used near a portal, it could probably activate it.]], end end, } + +for i = 1, 4 do +newEntity{ base = "BASE_LORE", + define_as = "NOTE"..i, + name = "tattered paper scrap", lore="tannen-level"..i, + desc = [[A paper scrap, left by Tannen.]], + rarity = false, + encumberance = 0, +} +end diff --git a/game/modules/tome/data/zones/tannen-tower/zone.lua b/game/modules/tome/data/zones/tannen-tower/zone.lua index 2f4132675ad958742d6c088fbc4dd7be67f2a728..c1205458da31171ef4973279cc4e888ce0c578d0 100644 --- a/game/modules/tome/data/zones/tannen-tower/zone.lua +++ b/game/modules/tome/data/zones/tannen-tower/zone.lua @@ -57,6 +57,10 @@ return { end if lev == 4 then core.fov.set_actor_vision_size(0) + if not game.level.data.seen_tannen then + game.level.data.seen_tannen = true + require("engine.ui.Dialog"):simpleLongPopup("Tannen's Tower", [[As you climb up the steps, you see Tannen standing with his drolem, reading a scrap of parchment. As he reads, his eyes grow wider, and he starts sweating and pacing back and forth. When he reaches to stuff it in his pocket, he sees you and jumps back like a startled cat. "No! Not now! You have no idea what's at stake!" He retrieves a fistful of brightly-colored flasks from his robes, and his drolem's eyes glow as it springs to life, metal screeching in an impressive imitation of a roar.]], 500) + end end end, on_leave = function() diff --git a/game/modules/tome/data/zones/town-point-zero/npcs.lua b/game/modules/tome/data/zones/town-point-zero/npcs.lua index 733eac9649b09aeeea31b3df3f24677043eb5433..2884c41c470455774a4c3d4d981d6502fc0557ac 100644 --- a/game/modules/tome/data/zones/town-point-zero/npcs.lua +++ b/game/modules/tome/data/zones/town-point-zero/npcs.lua @@ -188,7 +188,7 @@ newEntity{ base = "BASE_NPC_POINT_ZERO_TOWN", define_as = "ZEMEKKYS", [Talents.T_GRAVITY_SPIKE]=5, [Talents.T_REPULSION_FIELD]=5, [Talents.T_GRAVITY_WELL]=5, - [Talents.T_ENERGY_DECOMPOSITION]=5, + [Talents.T_ENTROPY]=5, [Talents.T_ENERGY_ABSORPTION]=5, [Talents.T_REDUX]=5, [Talents.T_TEMPORAL_FUGUE]=5, diff --git a/game/modules/tome/data/zones/unhallowed-morass/npcs.lua b/game/modules/tome/data/zones/unhallowed-morass/npcs.lua index 341a568b64404f07217d94f14d6ab4c6e7666a01..822c713a853fec88f713e2068a6ede1881b2fad3 100644 --- a/game/modules/tome/data/zones/unhallowed-morass/npcs.lua +++ b/game/modules/tome/data/zones/unhallowed-morass/npcs.lua @@ -33,7 +33,6 @@ newEntity{ autolevel = "spider", ai = "dumb_talented_simple", ai_state = { ai_move="move_complex", talent_in=2, }, - global_speed_base = 1.2, stats = { str=10, dex=17, mag=3, con=7 }, combat = { dammod={dex=0.8} }, combat_armor = 1, combat_def = 1, diff --git a/game/modules/tome/dialogs/Birther.lua b/game/modules/tome/dialogs/Birther.lua index 909aa0c1a9effc09b8c99cec2f1eebf05b6835ea..1ab459eee9a7df310ace057a36427501c438862f 100644 --- a/game/modules/tome/dialogs/Birther.lua +++ b/game/modules/tome/dialogs/Birther.lua @@ -1110,8 +1110,12 @@ function _M:setTile(f, w, h, last) end end +local to_reset_cosmetic = {} function _M:applyCosmeticActor(last) - self.actor.is_redhaed = nil -- Booh this is ugly + for i, d in ipairs(to_reset_cosmetic) do + d.reset(self.actor) + end + to_reset_cosmetic = {} local list = {} for i, d in ipairs(self.cosmetic_unlocks) do @@ -1120,6 +1124,9 @@ function _M:applyCosmeticActor(last) table.sort(list, function(a,b) return a.priority < b.priority end) for i, d in ipairs(list) do d.on_actor(self.actor, self, last) + if not last and d.reset then + to_reset_cosmetic[#to_reset_cosmetic+1] = d + end end end @@ -1394,12 +1401,27 @@ function _M:selectTile() "player/ascii_player_exotic_01.png", "player/ascii_player_shopper_01.png", } + + fs.mkdir("/data/gfx/custom-tiles/") + for file in fs.iterate("/data/gfx/custom-tiles/", function(file) return file:find("%.png") end) do + list[#list+1] = "custom-tiles/"..file + end + self:triggerHook{"Birther:donatorTiles", list=list} - local remove = Button.new{text="Use default tile", width=500, fct=function() + local remove = Button.new{text="Use default tile", width=240, fct=function() game:unregisterDialog(d) self.has_custom_tile = nil self:setTile() end} + local custom = Button.new{text="Use custom-made tile", width=240, fct=function() + self:simpleLongPopup("Howto: Custom-made tiles", ([[You can use your own custom tiles if you are a donator. +For the game to use them you must simply respect a few rules: +- they must be 64x64 or 64x128 tiles +- they must be saved as PNG files +- you must place them in folder #LIGHT_BLUE#%s#WHITE# + +Once you have done so, simply restart the game and the tiles will be listed at the bottom of the list.]]):format(fs.getRealPath("/data/gfx/custom-tiles/")), 500) + end} local list = ImageList.new{width=500, height=500, tile_w=64, tile_h=64, padding=10, scrollbar=true, list=list, fct=function(item) game:unregisterDialog(d) if not self:isDonator() then @@ -1411,6 +1433,7 @@ function _M:selectTile() d:loadUI{ {left=0, top=0, ui=list}, {left=0, bottom=0, ui=remove}, + {left=250, bottom=0, ui=custom}, } d:setupUI(true, true) d.key:addBind("EXIT", function() game:unregisterDialog(d) end) diff --git a/game/modules/tome/dialogs/CharacterSheet.lua b/game/modules/tome/dialogs/CharacterSheet.lua index ef0964cb64827afa7577a9908c4c4bf9b3b78104..8f2435d16ddbe4b564eb4f72b6603431a9a44f72 100644 --- a/game/modules/tome/dialogs/CharacterSheet.lua +++ b/game/modules/tome/dialogs/CharacterSheet.lua @@ -23,10 +23,12 @@ local Dialog = require "engine.ui.Dialog" local DamageType = require "engine.DamageType" local Talents = require "engine.interface.ActorTalents" local Tab = require "engine.ui.Tab" +local Button = require "engine.ui.Button" local SurfaceZone = require "engine.ui.SurfaceZone" local Separator = require "engine.ui.Separator" local Stats = require "engine.interface.ActorStats" local Textzone = require "engine.ui.Textzone" +local FontPackage = require "engine.FontPackage" module(..., package.seeall, class.inherit(Dialog, mod.class.interface.TooltipsData)) @@ -38,13 +40,28 @@ function _M:init(actor) self.actor = actor Dialog.init(self, "Character Sheet: "..self.actor.name, math.max(game.w * 0.7, 950), 500) - self.font = core.display.newFont("/data/font/DroidSansMono.ttf", 12) + self.font = core.display.newFont(FontPackage:getFont("mono_small", "mono")) self.font_h = self.font:lineSkip() + self.talent_sorting = config.settings.tome.charsheet_talent_sorting or 1 + self.c_general = Tab.new{title="General", default=true, fct=function() end, on_change=function(s) if s then self:switchTo("general") end end} self.c_attack = Tab.new{title="Attack", default=false, fct=function() end, on_change=function(s) if s then self:switchTo("attack") end end} self.c_defence = Tab.new{title="Defense", default=false, fct=function() end, on_change=function(s) if s then self:switchTo("defence") end end} - self.c_talents = Tab.new{title="Talents", default=false, fct=function() end, on_change=function(s) if s then self:switchTo("talents") end end} + self.c_talents = Tab.new{title="Talents", default=false, fct=function() end, on_change=function(s) if s then self:switchTo("talents") end end } + self.b_talents_sorting = Button.new{text="Sort: "..({"Groups", "Name", "Type"})[self.talent_sorting], hide=true, width=100, fct=function() + self.talent_sorting = self.talent_sorting + 1 + if self.talent_sorting > 3 then self.talent_sorting = 1 end + + --Save to config + config.settings.tome.charsheet_talent_sorting = self.talent_sorting + game:saveSettings("tome.charsheet_talent_sorting", ("tome.charsheet_talent_sorting = %d\n"):format(self.talent_sorting)) + + self.b_talents_sorting.text = "Sort: "..({"Groups", "Name", "Type"})[self.talent_sorting] + self.b_talents_sorting:generate() + self:switchTo("talents") -- Force a redraw + end} + local tw, th = self.font_bold:size(self.title) @@ -102,6 +119,7 @@ Mouse: Hover over stat for info {left=15+self.c_general.w+self.c_attack.w, top=self.c_tut.h, ui=self.c_defence}, {left=15+self.c_general.w+self.c_attack.w+self.c_defence.w, top=self.c_tut.h, ui=self.c_talents}, {left=0, top=self.c_tut.h + self.c_general.h, ui=self.vs}, + {right=0, bottom=0, ui=self.b_talents_sorting}, {left=0, top=self.c_tut.h + self.c_general.h + 5 + self.vs.h, ui=self.c_desc}, } @@ -118,11 +136,14 @@ function _M:innerDisplay(x, y, nb_keyframes) end function _M:switchTo(kind) + self.b_talents_sorting.hide = true self:drawDialog(kind, _M.cs_player_dup) if kind == "general" then self.c_attack.selected = false self.c_defence.selected = false self.c_talents.selected = false elseif kind == "attack" then self.c_general.selected = false self.c_defence.selected = false self.c_talents.selected = false elseif kind == "defence" then self.c_attack.selected = false self.c_general.selected = false self.c_talents.selected = false - elseif kind == "talents" then self.c_attack.selected = false self.c_general.selected = false self.c_defence.selected = false + elseif kind == "talents" then + self.b_talents_sorting.hide = false + self.c_attack.selected = false self.c_general.selected = false self.c_defence.selected = false end self:updateKeys() end @@ -325,7 +346,8 @@ function _M:drawDialog(kind, actor_to_compare) h = h + self.font_h if player:attr("forbid_arcane") then - s:drawColorStringBlended(self.font, "#ORCHID#Zigur follower", w, h, 255, 255, 255, true) h = h + self.font_h + local follow = (player.faction == "zigur" or player:attr("zigur_follower")) and "Zigur follower" or "Antimagic devotee" + s:drawColorStringBlended(self.font, "#ORCHID#"..follow, w, h, 255, 255, 255, true) h = h + self.font_h end if player:attr("blood_life") then s:drawColorStringBlended(self.font, "#DARK_RED#Blood of Life", w, h, 255, 255, 255, true) h = h + self.font_h @@ -964,23 +986,170 @@ function _M:drawDialog(kind, actor_to_compare) h = 0 w = 0 - local list = {} - for j, t in pairs(player.talents_def) do - if player:knowTalent(t.id) and (not t.hide or t.hide ~= "always") then - local lvl = player:getTalentLevelRaw(t) - list[#list+1] = { - name = ("%s (%d)"):format(t.name, lvl), - desc = player:getTalentFullDescription(t):toString(), - } + + local function sort_talents() + + local talents = {} + + local get_group + if self.talent_sorting == 1 then + -- Get the group/display name for a given talent type + get_group = function(t, tt) + if tt.type:match("inscriptions/.*") then + return "Inscriptions" + elseif tt.type:match("uber/.*") then + return "Prodigies" + elseif tt.type:match(".*/objects") then + return "Item Talents" + end + + local cat = tt.type:gsub("/.*", ""):bookCapitalize() + return cat.."/"..(tt.name or ""):bookCapitalize() + end + elseif self.talent_sorting == 2 then + -- Alphabetically, so no groups at all. + get_group = function(t, tt) + return "Talents" + end + else + -- Sort by usage type/speed + get_group = function(t, tt) + if t.mode == "activated" then + local no_energy = util.getval(t.no_energy, player, t) + return no_energy and "Instant" or "Activated" + else + return t.mode:bookCapitalize() + end + end end + + -- Process the talents + for j, t in pairs(player.talents_def) do + if player:knowTalent(t.id) and (not t.hide or t.hide ~= "always") then + local lvl = player:getTalentLevelRaw(t) + local tt = player:getTalentTypeFrom(t.type[1]) + + local data = {type_name = tt, + talent = t, + name = ("%s (%d)"):format(t.name, lvl), + desc = player:getTalentFullDescription(t):toString(), + } + + if self.talent_sorting == 2 then + if not talents["All"] then + talents["All"] = {type_name = "All", talent_type=nil, talents={} } + end + table.insert(talents["All"]["talents"], data) + else + local group_name = get_group(t, tt) + + if not talents[group_name] then + talents[group_name] = {type_name = group_name, talent_type=tt, talents={}} + end + table.insert(talents[group_name]["talents"], data) + end + end + end + + local sort_tt + + -- Decide what sorting method do use + if self.talent_sorting == 1 then + -- Sorting of talent groups, Racial/infusions first, then alphabetical order + local sort_rank = {"race/.*", "Inscriptions", "Prodigies", "Item Talents"} -- Relies on the groups + sort_tt = function(a, b) + a, b = a["type_name"], b["type_name"] + for i, v in ipairs(sort_rank) do + local rank_a, rank_b + rank_a = a:match(v) + if not rank_a then + rank_b = b:match(v) + if rank_b then return false end + else + return true + end + end + return a < b + end + elseif self.talent_sorting == 2 then + -- Only care about alphabetically sorting + sort_tt = function(a, b) + return a.name < b.name end + else + -- instant > activated > sustained > passive > alphabetically + local sort_rank = {"Instant", "Activated", "Sustained", "Passive" } + sort_tt = function(a, b) + a, b = a["type_name"], b["type_name"] + for i, v in ipairs(sort_rank) do + local rank_a, rank_b + rank_a = a:match(v) + if not rank_a then + rank_b = b:match(v) + if rank_b then return false end + else + return true + end + end + return a < b + end + end + -- Sort the talent type stuff + local sorted = {} + if self.talent_sorting == 2 then + table.sort(talents["All"]["talents"], sort_tt) + return talents + end + for k, v in pairs(talents) do + sorted[#sorted+1] = v + end + + if self.talent_sorting == 2 then + table.sort(sorted[1], sort_tt) + return sorted + else + table.sort(sorted, sort_tt) + end + + return sorted + end - table.sort(list, function(a,b) return a.name < b.name end) + local sorted = sort_talents() - for i, t in ipairs(list) do - self:mouseTooltip(t.desc, s:drawColorStringBlended(self.font, ("#LIGHT_GREEN#%s"):format(t.name), w, h, 255, 255, 255, true)) h = h + self.font_h + -- The color used to display it + local function get_talent_color(t) + if t.mode == "activated" then + local no_energy = util.getval(t.no_energy, player, t) + if no_energy == true then + return "#00c91b##{italic}#"--return "#E56F48#" + else + return "#00c91b#" + end + elseif t.mode == "passive" then + return "#LIGHT_STEEL_BLUE#"--return "#486FE5#" + else --Sustains + return "#C49600#"--return "#E5D848#" + end + end - if h + self.font_h >= self.c_desc.h then h = 0 w = w + self.c_desc.w / 6 end + -- Now display it! + for ts, tt in pairs(sorted) do + -- Display talent type + local tt_name = tt["type_name"] + local talent_type_name = tt["talent_type"] and tt["talent_type"].description or tt_name or "" + local talents = tt["talents"] + if tt_name then + self:mouseTooltip(talent_type_name, s:drawColorStringBlended(self.font, ("#{bold}##KHAKI#%s#{normal}#"):format(tt_name), w, h, 255, 255, 255, true)) h = h + self.font_h + end + + -- Display the talents in the talent type + for _, t in ipairs(talents) do + self:mouseTooltip(t.desc, s:drawColorStringBlended(self.font, (get_talent_color(t.talent).."%s".."#{normal}#"):format(t.name), w, h, 255, 255, 255, true)) h = h + self.font_h + + if h + self.font_h >= self.c_desc.h then h = 0 w = w + self.c_desc.w / 6 end + end + -- Add extra space to seperate categories + h = h + self.font_h end end @@ -1350,3 +1519,4 @@ function _M:dump() Dialog:simplePopup("Character dump complete", "File: "..fs.getRealPath(file)) end + diff --git a/game/modules/tome/dialogs/DeathDialog.lua b/game/modules/tome/dialogs/DeathDialog.lua index 6ee772a12dff7f34e4d6d9390619d15a0a9e0d60..d6b1c6cabaf8f3f10df924158368684ab19f7ff7 100644 --- a/game/modules/tome/dialogs/DeathDialog.lua +++ b/game/modules/tome/dialogs/DeathDialog.lua @@ -263,6 +263,10 @@ function _M:use(item) self.actor:updateModdableTile() self.actor:check("on_resurrect", "lichform") game:saveGame() + elseif act == "threads" then + game:chronoRestore("see_threads_base", true) + game:onTickEnd(function()game.player:removeEffect(game.player.EFF_SEE_THREADS)end) + game:saveGame() elseif act == "easy_mode" then self:eidolonPlane() elseif act == "skeleton" then @@ -308,6 +312,11 @@ function _M:generateList() if config.settings.cheat then list[#list+1] = {name="Resurrect by cheating", action="cheat"} end if not self.actor.no_resurrect and allow_res then + if self.actor:hasEffect(self.actor.EFF_SEE_THREADS) and game._chronoworlds then + self:use{action="threads"} + self.dont_show =true + return + end if self.actor:isTalentActive(self.actor.T_LICHFORM) then self:use{action="lichform"} self.dont_show = true diff --git a/game/modules/tome/dialogs/GameOptions.lua b/game/modules/tome/dialogs/GameOptions.lua index d6268576933bb7fcfb6af5adbba2cdf3bc152d23..eb708d076decfec8371bf766f5900316bed8e435 100644 --- a/game/modules/tome/dialogs/GameOptions.lua +++ b/game/modules/tome/dialogs/GameOptions.lua @@ -25,6 +25,7 @@ local Separator = require "engine.ui.Separator" local GetQuantity = require "engine.dialogs.GetQuantity" local Tabs = require "engine.ui.Tabs" local GraphicMode = require("mod.dialogs.GraphicMode") +local FontPackage = require "engine.FontPackage" module(..., package.seeall, class.inherit(Dialog)) @@ -158,10 +159,11 @@ function _M:generateListUi() list[#list+1] = { zone=zone, name=string.toTString"#GOLD##{bold}#Font Style#WHITE##{normal}#", status=function(item) return tostring(config.settings.tome.fonts.type):capitalize() end, fct=function(item) - Dialog:listPopup("Font style", "Select font", {{name="Fantasy", type="fantasy"}, {name="Basic", type="basic"}}, 300, 200, function(sel) + local list = FontPackage:list() + Dialog:listPopup("Font style", "Select font", list, 300, 200, function(sel) if not sel or not sel.type then return end - game:saveSettings("tome.fonts", ("tome.fonts = { type = %q, size = %q }\n"):format(sel.type, config.settings.tome.fonts.size)) - config.settings.tome.fonts.type = sel.type + game:saveSettings("tome.fonts", ("tome.fonts = { type = %q, size = %q }\n"):format(sel.id, config.settings.tome.fonts.size)) + config.settings.tome.fonts.type = sel.id self.c_list:drawItem(item) end) end,} @@ -344,6 +346,15 @@ function _M:generateListUi() if game.player.updateMainShader then game.player:updateMainShader() end end,} + local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text=string.toTString"Toggles advanced weapon statistics display.#WHITE#"} + list[#list+1] = { zone=zone, name=string.toTString"#GOLD##{bold}#Advanced Weapon Statistics#WHITE##{normal}#", status=function(item) + return tostring(config.settings.tome.advanced_weapon_stats and "enabled" or "disabled") + end, fct=function(item) + config.settings.tome.advanced_weapon_stats = not config.settings.tome.advanced_weapon_stats + game:saveSettings("tome.advanced_weapon_stats", ("tome.advanced_weapon_stats = %s\n"):format(tostring(config.settings.tome.advanced_weapon_stats))) + self.c_list:drawItem(item) + end,} + self.list = list end diff --git a/game/modules/tome/dialogs/LevelupDialog.lua b/game/modules/tome/dialogs/LevelupDialog.lua index cf1eea722588cc4b95d55b768b3aec0429d293da..adec5f2efc9d1d86f5459dfc4682186a9f4b7cf7 100644 --- a/game/modules/tome/dialogs/LevelupDialog.lua +++ b/game/modules/tome/dialogs/LevelupDialog.lua @@ -28,6 +28,7 @@ local UIContainer = require "engine.ui.UIContainer" local TalentTrees = require "mod.dialogs.elements.TalentTrees" local Separator = require "engine.ui.Separator" local DamageType = require "engine.DamageType" +local FontPackage = require "engine.FontPackage" module(..., package.seeall, class.inherit(Dialog, mod.class.interface.TooltipsData)) @@ -65,7 +66,7 @@ function _M:init(actor, on_finish, on_birth) self.talent_types_learned = {} self.stats_increased = {} - self.font = core.display.newFont("/data/font/DroidSansMono.ttf", 12) + self.font = core.display.newFont(FontPackage:getFont("mono_small", "mono")) self.font_h = self.font:lineSkip() self.actor.__hidden_talent_types = self.actor.__hidden_talent_types or {} @@ -179,6 +180,24 @@ function _M:finish() if self.actor:knowTalent(tid) then self.actor:forceUseTalent(tid, {ignore_energy=true, ignore_cd=true, no_talent_fail=true, talent_reuse=true}) end end + -- Reshape autoupdate + if self.actor:knowTalent(self.actor["T_RESHAPE_WEAPON/ARMOUR"]) then + for inven_id, inven in pairs(self.actor.inven) do + for item = #inven, 1, -1 do + local o = inven[item] + if o.been_reshaped then + if o.wielded then + o = self.actor:takeoffObject(inven, item) + local t = self.actor:getTalentFromId(self.actor["T_RESHAPE_WEAPON/ARMOUR"]) + t.reshape(self.actor, t, o, false) + self.actor:addObject(inven, o) + --self.actor:wearObject(o, true, true) + end + end + end + end + end + -- Prodigies if self.on_finish_prodigies then for tid, ok in pairs(self.on_finish_prodigies) do if ok then self.actor:learnTalent(tid, true, nil, {no_unlearn=true}) end end @@ -803,9 +822,9 @@ function _M:getStatDesc(item) text:add("Mindpower: ", color, ("%0.2f"):format(diff * 0.7), dc, true) text:add("Mental save: ", color, ("%0.2f"):format(diff * 0.35), dc, true) text:add("Spell save: ", color, ("%0.2f"):format(diff * 0.35), dc, true) - if self.actor:attr("use_psi_combat") then - text:add("Accuracy: ", color, ("%0.2f"):format(diff * 0.35), dc, true) - end +-- if self.actor:attr("use_psi_combat") then +-- text:add("Accuracy: ", color, ("%0.2f"):format(diff * 0.35), dc, true) +-- end elseif stat_id == self.actor.STAT_STR then text:add("Physical power: ", color, ("%0.2f"):format(diff), dc, true) text:add("Max encumbrance: ", color, ("%0.2f"):format(diff * 1.8), dc, true) diff --git a/game/modules/tome/dialogs/MapMenu.lua b/game/modules/tome/dialogs/MapMenu.lua index 2c72cef2a150ecedc4588b1273d8c04a1ad7a762..c736ccfa349d818d7cdb8f813bd29a3aaba34481 100644 --- a/game/modules/tome/dialogs/MapMenu.lua +++ b/game/modules/tome/dialogs/MapMenu.lua @@ -99,14 +99,15 @@ function _M:use(item) d.changed = true elseif act == "debug-inventory" then local d + local actor = item.actor d = item.actor:showEquipInven(item.actor.name..": Inventory", nil, function(o, inven, item, button, event) if not o then return end - local ud = require("mod.dialogs.UseItemDialog").new(event == "button", item.actor, o, item, inven, function(_, _, _, stop) + local ud = require("mod.dialogs.UseItemDialog").new(event == "button", actor, o, item, inven, function(_, _, _, stop) d:generate() d:generateList() - if stop then self:unregisterDialog(d) end + if stop then game:unregisterDialog(d) end end) - self:registerDialog(ud) + game:registerDialog(ud) end) end end diff --git a/game/modules/tome/dialogs/PartySendItem.lua b/game/modules/tome/dialogs/PartySendItem.lua index 5bc08fe2bbafd159cb3a73d18475357c181867b3..74d89c5e7625f1b879b77c29a97703102364a1f4 100644 --- a/game/modules/tome/dialogs/PartySendItem.lua +++ b/game/modules/tome/dialogs/PartySendItem.lua @@ -50,15 +50,14 @@ function _M:on_register() end function _M:use(item) - if not item then return end + if not item or not item.actor:canAddToInven(item.actor.INVEN_INVEN) then return end game:unregisterDialog(self) - self.source:removeObject(self.inven, self.item, true) self.source:sortInven(self.inven) self.o.__transmo = nil - item.actor:addObject(item.actor.INVEN_INVEN, self.o) - item.actor:sortInven(item.actor.INVEN_INVEN) + item.actor:addObject(item.actor.INVEN_INVEN, self.o, true) -- force full stack transfer game.log("You give %s to %s.", self.o:getName{do_color=true}, item.actor.name) + item.actor:sortInven(item.actor.INVEN_INVEN) self.on_end() end @@ -67,7 +66,7 @@ function _M:generateList() for i, act in ipairs(game.party.m_list) do if not act.no_inventory_access and act ~= game.player then - list[#list+1] = {name=act.name, actor=act} + list[#list+1] = {name=act.name..(act:canAddToInven(act.INVEN_INVEN) and "" or " #YELLOW#[NO ROOM]#LAST#"), actor=act} end end diff --git a/game/modules/tome/dialogs/ShowEquipInven.lua b/game/modules/tome/dialogs/ShowEquipInven.lua index 3452c734703ef4ccb91019f0e837b670c94cf443..9e0e57d68760fa30a1ff7618359c705fffad3e31 100644 --- a/game/modules/tome/dialogs/ShowEquipInven.lua +++ b/game/modules/tome/dialogs/ShowEquipInven.lua @@ -52,19 +52,34 @@ function _M:init(title, equip_actor, filter, action, on_select, inven_actor) end end - self.c_doll = EquipDoll.new{actor=equip_actor, drag_enable=true, filter=filter, + self.c_doll = EquipDoll.new{ + subobject=equip_actor:attr("can_tinker") and "getTinker" or nil, + subobject_restrict_slots=equip_actor.tinker_restrict_slots, + actor=equip_actor, drag_enable=true, filter=filter, fct = function(item, button, event) self:use(item, button, event) end, on_select = function(ui, inven, item, o) if ui.ui.last_display_x then self:select{last_display_x=ui.ui.last_display_x+ui.ui.w, last_display_y=ui.ui.last_display_y, object=o} end end, actorWear = function(ui, wear_inven, wear_item, wear_o) - if ui:getItem() then - local bi = self.equip_actor:getInven(ui.inven) - local ws = self.equip_actor:getInven(wear_o:wornInven()) - local os = self.equip_actor:getObjectOffslot(wear_o) - if bi and ((ws and ws.id == bi.id) or (os and self.equip_actor:getInven(os).id == bi.id)) then - self.equip_actor:doTakeoff(ui.inven, ui.item, ui:getItem(), true, self.inven_actor) + if wear_o.is_tinker then + local base_inven, base_item = ui.inven, ui.item + -- Find appropriate slot + if not base_inven or not base_item then + base_inven, base_item = self:findTinkerSpot(wear_o) end + if base_inven and base_item then + local base_o = base_inven[base_item] + self.equip_actor:doWearTinker(wear_inven, wear_item, wear_o, base_inven, base_item, base_o, true) + end + else + if ui:getItem() then + local bi = self.equip_actor:getInven(ui.inven) + local ws = self.equip_actor:getInven(wear_o:wornInven()) + local os = self.equip_actor:getObjectOffslot(wear_o) + if bi and ((ws and ws.id == bi.id) or (os and self.equip_actor:getInven(os).id == bi.id)) then + self.equip_actor:doTakeoff(ui.inven, ui.item, ui:getItem(), true, self.inven_actor) + end + end + self.equip_actor:doWear(wear_inven, wear_item, wear_o, self.inven_actor) end - self.equip_actor:doWear(wear_inven, wear_item, wear_o, self.inven_actor) self.c_inven:generateList() end } @@ -224,7 +239,7 @@ function _M:use(item, button, event) end function _M:unload() - for inven_id = 1, #self.inven_actor.inven_def do if self.inven_actor.inven[inven_id] then for item, o in ipairs(self.inven_actor.inven[inven_id]) do o.__new_pickup = nil end end end + for inven_id = 1, #self.inven_actor.inven_def do if self.inven_actor.inven[inven_id] then for item, o in ipairs(self.inven_actor.inven[inven_id]) do o:forAllStack(function(so) so.__new_pickup = nil end) end end end end function _M:updateTitle(title) @@ -261,7 +276,12 @@ end function _M:onDragTakeoff() local drag = game.mouse.dragged.payload - if drag.kind == "inventory" and drag.inven and self.equip_actor:getInven(drag.inven) and self.equip_actor:getInven(drag.inven).worn then + + if drag.kind == "inventory" and drag.inven and self.equip_actor:getInven(drag.inven) and self.equip_actor:getInven(drag.inven).worn and drag.object.is_tinker then + self.equip_actor:doTakeoffTinker(self.equip_actor:getInven(drag.inven)[drag.item_idx], drag.object) + self.c_inven:generateList() + game.mouse:usedDrag() + elseif drag.kind == "inventory" and drag.inven and self.equip_actor:getInven(drag.inven) and self.equip_actor:getInven(drag.inven).worn then self.equip_actor:doTakeoff(drag.inven, drag.item_idx, drag.object, nil, self.inven_actor) self.c_inven:generateList() game.mouse:usedDrag() @@ -280,3 +300,7 @@ end function _M:generateList() self.c_inven:generateList() end + +function _M:findTinkerSpot(tinker) + return self.equip_actor:findTinkerSpot(tinker) +end diff --git a/game/modules/tome/dialogs/UberTalent.lua b/game/modules/tome/dialogs/UberTalent.lua index 7b7707d5cd2630382e2041a9a1c8f87904d6d1ce..c46bf415d7b7e79db37bb9e4baa228afe72cbec1 100644 --- a/game/modules/tome/dialogs/UberTalent.lua +++ b/game/modules/tome/dialogs/UberTalent.lua @@ -27,6 +27,7 @@ local TextzoneList = require "engine.ui.TextzoneList" local TalentGrid = require "mod.dialogs.elements.TalentGrid" local Separator = require "engine.ui.Separator" local DamageType = require "engine.DamageType" +local FontPackage = require "engine.FontPackage" module(..., package.seeall, class.inherit(Dialog, mod.class.interface.TooltipsData)) @@ -34,7 +35,7 @@ function _M:init(actor, levelup_end_prodigies) self.actor = actor self.levelup_end_prodigies = levelup_end_prodigies - self.font = core.display.newFont("/data/font/DroidSansMono.ttf", 12) + self.font = core.display.newFont(FontPackage:getFont("mono_small", "mono")) self.font_h = self.font:lineSkip() self.actor_dup = actor:clone() diff --git a/game/modules/tome/dialogs/UseItemDialog.lua b/game/modules/tome/dialogs/UseItemDialog.lua index 0cb9bce8223d4bc9596c36aac32c51c7cc484c90..6ca4d5a36022ef7a4b25777ccb63e8fa9a256c42 100644 --- a/game/modules/tome/dialogs/UseItemDialog.lua +++ b/game/modules/tome/dialogs/UseItemDialog.lua @@ -88,6 +88,29 @@ function _M:use(item) elseif act == "takeoff" then self.actor:doTakeoff(self.inven, self.item, self.object, nil, self.dst_actor) self.onuse(self.inven, self.item, self.object, false) + elseif act == "tinker-remove" then + self.actor:doTakeoffTinker(self.inven[self.item], self.object) + self.actor:sortInven() + self.onuse(self.inven, self.item, self.object, false) + elseif act == "tinker-add" then + local list = {} + for inven_idx, inven in pairs(self.actor.inven) do if inven.worn then + for item_idx, o in ipairs(inven) do + if o:canAttachTinker(self.object, true) then list[#list+1] = {name=o:getName{do_color=true}, inven=inven, item=item_idx, o=o} end + end + end end + local doit = function(w) + if not w then return end + self.actor:doWearTinker(self.inven, self.item, self.object, w.inven, w.item, w.o, true) + self.actor:sortInven() + self.onuse(self.inven, self.item, self.object, false) + end + if #list == 1 then doit(list[1]) + elseif #list == 0 then + self:simplePopup("Attach to item", "You do not have any equipped items that it can be attached to.") + else + self:listPopup("Attach to item", "Select which item to attach it to:", list, 300, 400, doit) + end elseif act == "transfer" then game:registerDialog(PartySendItem.new(self.actor, self.object, self.inven, self.item, function() self.onuse(self.inven, self.item, self.object, false) @@ -136,6 +159,8 @@ function _M:generateList() if not self.dst_actor and not self.object.__transmo and not self.no_use_allowed then if self.object:canUseObject() then list[#list+1] = {name="Use", action="use"} end end if self.inven == self.actor.INVEN_INVEN and self.object:wornInven() and self.actor:getInven(self.object:wornInven()) then list[#list+1] = {name="Wield/Wear", action="wear"} end if not self.object.__transmo then if self.inven ~= self.actor.INVEN_INVEN and self.object:wornInven() then list[#list+1] = {name="Take off", action="takeoff"} end end + if not self.object.__transmo then if self.inven ~= self.actor.INVEN_INVEN and self.object.is_tinker and self.object.tinkered then list[#list+1] = {name="Detach from item", action="tinker-remove"} end end + if not self.object.__transmo then if self.inven == self.actor.INVEN_INVEN and self.object.is_tinker and not self.object.tinkered then list[#list+1] = {name="Attach to item", action="tinker-add"} end end if not self.dst_actor and not self.object.__tagged and self.inven == self.actor.INVEN_INVEN then list[#list+1] = {name="Drop", action="drop"} end if not self.dst_actor and self.inven == self.actor.INVEN_INVEN and game.party:countInventoryAble() >= 2 then list[#list+1] = {name="Transfer to party", action="transfer"} end if not self.dst_actor and not self.object.__tagged and self.inven == self.actor.INVEN_INVEN and transmo_chest and self.actor:transmoFilter(self.object) then list[#list+1] = {name=self.actor:transmoGetWord():capitalize().." now", action="transmo"} end diff --git a/game/modules/tome/dialogs/debug/DebugMain.lua b/game/modules/tome/dialogs/debug/DebugMain.lua index 56dd41847e3108a52db34ce5ada47d734ae415c2..d96aff3eed75122d9eda6d3f5da801ab8a5fe8f1 100644 --- a/game/modules/tome/dialogs/debug/DebugMain.lua +++ b/game/modules/tome/dialogs/debug/DebugMain.lua @@ -79,6 +79,8 @@ function _M:use(item) game.state.birth.ignore_prodigies_special_reqs = true game.player.inc_damage.all = 500 game.player:incStat("str", 100) game.player:incStat("dex", 100) game.player:incStat("mag", 100) game.player:incStat("wil", 100) game.player:incStat("cun", 100) game.player:incStat("con", 100) + elseif act == "weakdamage" then + game.player.inc_damage.all = -90 elseif act == "all_arts" then for i, e in ipairs(game.zone.object_list) do if e.unique and e.define_as ~= "VOICE_SARUMAN" and e.define_as ~= "ORB_MANY_WAYS_DEMON" then @@ -164,6 +166,7 @@ function _M:generateList() list[#list+1] = {name="Remove all creatures", action="remove-all"} list[#list+1] = {name="Semi-Godmode", action="semigodmode"} list[#list+1] = {name="Give all ingredients", action="all-ingredients"} + list[#list+1] = {name="Weakdamage", action="weakdamage"} self:triggerHook{"DebugMain:generate", menu=list} local chars = {} diff --git a/game/modules/tome/dialogs/talents/ChronomancyContingency.lua b/game/modules/tome/dialogs/talents/ChronomancyContingency.lua index ccc9e4a0cd96fb3e99a4d12412071f205c904b14..cbe55e7965e5553388ae2a8f5e0fd5701e2a7aac 100644 --- a/game/modules/tome/dialogs/talents/ChronomancyContingency.lua +++ b/game/modules/tome/dialogs/talents/ChronomancyContingency.lua @@ -43,7 +43,7 @@ function _M:init(actor) Dialog.init(self, "Contingency", game.w * 0.6, game.h * 0.8) self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[ -You may select a spell for Contingency to automatically use when your life falls below 25%. Only active spells that do not require a target may be chosen. +You may select a spell for Contingency to automatically use when your life falls below a percentage. Only active spells that do not require a target may be chosen. ]]} self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true} @@ -83,7 +83,7 @@ end function _M:use(item) if not item or not item.talent then return end - self.contingecy_talent = item.talent + self.actor:talentDialogReturn(item.talent) game:unregisterDialog(self) end @@ -104,7 +104,7 @@ function _M:generateList() -- Generate lists of all talents by category for j, t in pairs(self.actor.talents_def) do - if self.actor:knowTalent(t.id) and t.mode == "activated" and t.is_spell and not t.requires_target and not t.hide then + if self.actor:knowTalent(t.id) and t.mode == "activated" and t.is_spell and not t.requires_target and not t.hide and not t.fixed_cooldown then local nodes = talents local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"} diff --git a/game/modules/tome/dialogs/talents/ChronomancyEmpower.lua b/game/modules/tome/dialogs/talents/ChronomancyEmpower.lua new file mode 100644 index 0000000000000000000000000000000000000000..6ce95321905c62e1bc7409b3bc665e00f78beef0 --- /dev/null +++ b/game/modules/tome/dialogs/talents/ChronomancyEmpower.lua @@ -0,0 +1,133 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 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 + +require "engine.class" +local Dialog = require "engine.ui.Dialog" +local TreeList = require "engine.ui.TreeList" +local ListColumns = require "engine.ui.ListColumns" +local Textzone = require "engine.ui.Textzone" +local TextzoneList = require "engine.ui.TextzoneList" +local Separator = require "engine.ui.Separator" + +module(..., package.seeall, class.inherit(Dialog)) +-- Could use better icons when available +--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"}) +--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"}) + +-- generate talent status separately to enable quicker refresh of Dialog +local function TalentStatus(who,t) + local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} + + return tostring(status) +end + +function _M:init(actor) + self.actor = actor + actor.hotkey = actor.hotkey or {} + Dialog.init(self, "Empower", game.w * 0.6, game.h * 0.8) + + self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[ +You may select a chronomancy spell to Empower, increasing your effective spellpower when casting that spell. +]]} + self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true} + + self:generateList() + + local cols = { + {name="", width={40,"fixed"}, display_prop="char"}, + {name="Talent", width=80, display_prop="name"}, + } + self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end} + self.c_list.cur_col = 2 + + self:loadUI{ + {left=0, top=0, ui=self.c_list}, + {right=0, top=self.c_tut.h + 20, ui=self.c_desc}, + {right=0, top=0, ui=self.c_tut}, + {hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}, + } + self:setFocus(self.c_list) + self:setupUI() + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + +function _M:on_register() + game:onTickEnd(function() self.key:unicodeInput(true) end) +end + +function _M:select(item) + if item then + self.c_desc:switchItem(item, item.desc) + self.cur_item = item + end +end + +function _M:use(item) + if not item or not item.talent then return end + self.actor:talentDialogReturn(item.talent) + game:unregisterDialog(self) +end + +-- Display the player tile +function _M:innerDisplay(x, y, nb_keyframes) + if self.cur_item and self.cur_item.entity then + self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64) + end +end + +function _M:generateList() + -- Makes up the list + local list = {} + local letter = 1 + + local talents = {} + local chars = {} + + -- Generate lists of all talents by category + for j, t in pairs(self.actor.talents_def) do + if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and t.mode ~= "passive" then + local nodes = talents + local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"} + + -- Pregenerate icon with the Tiles instance that allows images + if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end + + nodes[#nodes+1] = { + name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(), + cname=t.name, + status=status, + entity=t.display_entity, + talent=t.id, + desc=self.actor:getTalentFullDescription(t), + color=function() return {0xFF, 0xFF, 0xFF} end + } + end + end + table.sort(talents, function(a,b) return a.cname < b.cname end) + for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end + + list = { + { char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Empower.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true }, + chars = chars, + } + self.list = list +end diff --git a/game/modules/tome/dialogs/talents/ChronomancyExtension.lua b/game/modules/tome/dialogs/talents/ChronomancyExtension.lua new file mode 100644 index 0000000000000000000000000000000000000000..aaed588ea83c6d4c6096aa69ccc0bbb5b4f19bd9 --- /dev/null +++ b/game/modules/tome/dialogs/talents/ChronomancyExtension.lua @@ -0,0 +1,133 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 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 + +require "engine.class" +local Dialog = require "engine.ui.Dialog" +local TreeList = require "engine.ui.TreeList" +local ListColumns = require "engine.ui.ListColumns" +local Textzone = require "engine.ui.Textzone" +local TextzoneList = require "engine.ui.TextzoneList" +local Separator = require "engine.ui.Separator" + +module(..., package.seeall, class.inherit(Dialog)) +-- Could use better icons when available +--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"}) +--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"}) + +-- generate talent status separately to enable quicker refresh of Dialog +local function TalentStatus(who,t) + local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} + + return tostring(status) +end + +function _M:init(actor) + self.actor = actor + actor.hotkey = actor.hotkey or {} + Dialog.init(self, "Extension", game.w * 0.6, game.h * 0.8) + + self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[ +You may select a chronomancy spell to Extend, increasing the duration of that spell. +]]} + self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true} + + self:generateList() + + local cols = { + {name="", width={40,"fixed"}, display_prop="char"}, + {name="Talent", width=80, display_prop="name"}, + } + self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end} + self.c_list.cur_col = 2 + + self:loadUI{ + {left=0, top=0, ui=self.c_list}, + {right=0, top=self.c_tut.h + 20, ui=self.c_desc}, + {right=0, top=0, ui=self.c_tut}, + {hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}, + } + self:setFocus(self.c_list) + self:setupUI() + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + +function _M:on_register() + game:onTickEnd(function() self.key:unicodeInput(true) end) +end + +function _M:select(item) + if item then + self.c_desc:switchItem(item, item.desc) + self.cur_item = item + end +end + +function _M:use(item) + if not item or not item.talent then return end + self.actor:talentDialogReturn(item.talent) + game:unregisterDialog(self) +end + +-- Display the player tile +function _M:innerDisplay(x, y, nb_keyframes) + if self.cur_item and self.cur_item.entity then + self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64) + end +end + +function _M:generateList() + -- Makes up the list + local list = {} + local letter = 1 + + local talents = {} + local chars = {} + + -- Generate lists of all talents by category + for j, t in pairs(self.actor.talents_def) do + if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and t.getDuration and t.mode ~= "passive" then + local nodes = talents + local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"} + + -- Pregenerate icon with the Tiles instance that allows images + if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end + + nodes[#nodes+1] = { + name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(), + cname=t.name, + status=status, + entity=t.display_entity, + talent=t.id, + desc=self.actor:getTalentFullDescription(t), + color=function() return {0xFF, 0xFF, 0xFF} end + } + end + end + table.sort(talents, function(a,b) return a.cname < b.cname end) + for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end + + list = { + { char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Extension.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true }, + chars = chars, + } + self.list = list +end diff --git a/game/modules/tome/dialogs/talents/ChronomancyMatrix.lua b/game/modules/tome/dialogs/talents/ChronomancyMatrix.lua new file mode 100644 index 0000000000000000000000000000000000000000..9a1d579cc5edb68d54e419068dc2735c87634446 --- /dev/null +++ b/game/modules/tome/dialogs/talents/ChronomancyMatrix.lua @@ -0,0 +1,133 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 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 + +require "engine.class" +local Dialog = require "engine.ui.Dialog" +local TreeList = require "engine.ui.TreeList" +local ListColumns = require "engine.ui.ListColumns" +local Textzone = require "engine.ui.Textzone" +local TextzoneList = require "engine.ui.TextzoneList" +local Separator = require "engine.ui.Separator" + +module(..., package.seeall, class.inherit(Dialog)) +-- Could use better icons when available +--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"}) +--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"}) + +-- generate talent status separately to enable quicker refresh of Dialog +local function TalentStatus(who,t) + local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} + + return tostring(status) +end + +function _M:init(actor) + self.actor = actor + actor.hotkey = actor.hotkey or {} + Dialog.init(self, "Matrix", game.w * 0.6, game.h * 0.8) + + self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[ +You may select a chronomancy spell to Matrix, reducing the cooldown of that spell. +]]} + self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true} + + self:generateList() + + local cols = { + {name="", width={40,"fixed"}, display_prop="char"}, + {name="Talent", width=80, display_prop="name"}, + } + self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end} + self.c_list.cur_col = 2 + + self:loadUI{ + {left=0, top=0, ui=self.c_list}, + {right=0, top=self.c_tut.h + 20, ui=self.c_desc}, + {right=0, top=0, ui=self.c_tut}, + {hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}, + } + self:setFocus(self.c_list) + self:setupUI() + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + +function _M:on_register() + game:onTickEnd(function() self.key:unicodeInput(true) end) +end + +function _M:select(item) + if item then + self.c_desc:switchItem(item, item.desc) + self.cur_item = item + end +end + +function _M:use(item) + if not item or not item.talent then return end + self.actor:talentDialogReturn(item.talent) + game:unregisterDialog(self) +end + +-- Display the player tile +function _M:innerDisplay(x, y, nb_keyframes) + if self.cur_item and self.cur_item.entity then + self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64) + end +end + +function _M:generateList() + -- Makes up the list + local list = {} + local letter = 1 + + local talents = {} + local chars = {} + + -- Generate lists of all talents by category + for j, t in pairs(self.actor.talents_def) do + if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and t.cooldown and t.mode ~= "passive" and not t.fixed_cooldown then + local nodes = talents + local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"} + + -- Pregenerate icon with the Tiles instance that allows images + if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end + + nodes[#nodes+1] = { + name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(), + cname=t.name, + status=status, + entity=t.display_entity, + talent=t.id, + desc=self.actor:getTalentFullDescription(t), + color=function() return {0xFF, 0xFF, 0xFF} end + } + end + end + table.sort(talents, function(a,b) return a.cname < b.cname end) + for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end + + list = { + { char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Matrix.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true }, + chars = chars, + } + self.list = list +end diff --git a/game/modules/tome/dialogs/talents/ChronomancyQuicken.lua b/game/modules/tome/dialogs/talents/ChronomancyQuicken.lua new file mode 100644 index 0000000000000000000000000000000000000000..a0b19f6f782ea889b5cb893d7a7a1330c565cda3 --- /dev/null +++ b/game/modules/tome/dialogs/talents/ChronomancyQuicken.lua @@ -0,0 +1,133 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009 - 2014 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 + +require "engine.class" +local Dialog = require "engine.ui.Dialog" +local TreeList = require "engine.ui.TreeList" +local ListColumns = require "engine.ui.ListColumns" +local Textzone = require "engine.ui.Textzone" +local TextzoneList = require "engine.ui.TextzoneList" +local Separator = require "engine.ui.Separator" + +module(..., package.seeall, class.inherit(Dialog)) +-- Could use better icons when available +--local confirmMark = require("engine.Entity").new({image="ui/chat-icon.png"}) +--local autoMark = require("engine.Entity").new({image = "/ui/hotkeys/mainmenu.png"}) + +-- generate talent status separately to enable quicker refresh of Dialog +local function TalentStatus(who,t) + local status = tstring{{"color", "LIGHT_GREEN"}, "Active"} + + return tostring(status) +end + +function _M:init(actor) + self.actor = actor + actor.hotkey = actor.hotkey or {} + Dialog.init(self, "Quicken", game.w * 0.6, game.h * 0.8) + + self.c_tut = Textzone.new{width=math.floor(self.iw / 2 - 10), height=1, auto_height=true, no_color_bleed=true, text=[[ +You may select a chronomancy spell to Quicken, reducing the time it takes you to cast that spell. +]]} + self.c_desc = TextzoneList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - self.c_tut.h - 20, scrollbar=true, no_color_bleed=true} + + self:generateList() + + local cols = { + {name="", width={40,"fixed"}, display_prop="char"}, + {name="Talent", width=80, display_prop="name"}, + } + self.c_list = TreeList.new{width=math.floor(self.iw / 2 - 10), height=self.ih - 10, all_clicks=true, scrollbar=true, columns=cols, tree=self.list, fct=function(item, sel, button) self:use(item, button) end, select=function(item, sel) self:select(item) end} + self.c_list.cur_col = 2 + + self:loadUI{ + {left=0, top=0, ui=self.c_list}, + {right=0, top=self.c_tut.h + 20, ui=self.c_desc}, + {right=0, top=0, ui=self.c_tut}, + {hcenter=0, top=5, ui=Separator.new{dir="horizontal", size=self.ih - 10}}, + } + self:setFocus(self.c_list) + self:setupUI() + + self.key:addBinds{ + EXIT = function() game:unregisterDialog(self) end, + } +end + +function _M:on_register() + game:onTickEnd(function() self.key:unicodeInput(true) end) +end + +function _M:select(item) + if item then + self.c_desc:switchItem(item, item.desc) + self.cur_item = item + end +end + +function _M:use(item) + if not item or not item.talent then return end + self.actor:talentDialogReturn(item.talent) + game:unregisterDialog(self) +end + +-- Display the player tile +function _M:innerDisplay(x, y, nb_keyframes) + if self.cur_item and self.cur_item.entity then + self.cur_item.entity:toScreen(game.uiset.hotkeys_display_icons.tiles, x + self.iw - 64, y + self.iy + self.c_tut.h - 32 + 10, 64, 64) + end +end + +function _M:generateList() + -- Makes up the list + local list = {} + local letter = 1 + + local talents = {} + local chars = {} + + -- Generate lists of all talents by category + for j, t in pairs(self.actor.talents_def) do + if self.actor:knowTalent(t.id) and t.type[1]:find("^chronomancy/") and not t.type[1]:find("^chronomancy/spellbinding") and not t.hide and not t.no_energy and t.mode ~= "passive" then + local nodes = talents + local status = tstring{{"color", "LIGHT_GREEN"}, "Talents"} + + -- Pregenerate icon with the Tiles instance that allows images + if t.display_entity then t.display_entity:getMapObjects(game.uiset.hotkeys_display_icons.tiles, {}, 1) end + + nodes[#nodes+1] = { + name=((t.display_entity and t.display_entity:getDisplayString() or "")..t.name):toTString(), + cname=t.name, + status=status, + entity=t.display_entity, + talent=t.id, + desc=self.actor:getTalentFullDescription(t), + color=function() return {0xFF, 0xFF, 0xFF} end + } + end + end + table.sort(talents, function(a,b) return a.cname < b.cname end) + for i, node in ipairs(talents) do node.char = self:makeKeyChar(letter) chars[node.char] = node letter = letter + 1 end + + list = { + { char='', name=('#{bold}#Choose a talent#{normal}#'):toTString(), status='', hotkey='', desc="All talents that can be used with Quicken.", color=function() return colors.simple(colors.LIGHT_GREEN) end, nodes=talents, shown=true }, + chars = chars, + } + self.list = list +end diff --git a/game/modules/tome/init.lua b/game/modules/tome/init.lua index 8da6af5355cb7a11711b8cb06ea297825200fddb..dc144e880f6da34de934d006f1060662cdcccb74 100644 --- a/game/modules/tome/init.lua +++ b/game/modules/tome/init.lua @@ -22,8 +22,8 @@ long_name = "Tales of Maj'Eyal: Age of Ascendancy" short_name = "tome" author = { "DarkGod", "darkgod@te4.org" } homepage = "http://te4.org/" -version = {1,2,3} -engine = {1,2,3,"te4"} +version = {1,2,4} +engine = {1,2,4,"te4"} description = [[ Welcome to Maj'Eyal. @@ -66,6 +66,9 @@ if not config.settings.censor_boot then background_name = {"tome","tome2","tome3 else background_name = {"tome3"} end +font_package_id = function() config.settings.tome = config.settings.tome or {} if not config.settings.tome.fonts then config.settings.tome.fonts = {type="fantasy", size="normal"} end return config.settings.tome.fonts.type end +font_package_size = function() config.settings.tome = config.settings.tome or {} if not config.settings.tome.fonts then config.settings.tome.fonts = {type="fantasy", size="normal"} end return config.settings.tome.fonts.size end + load_tips = { {image="/data/gfx/shockbolt/npc/humanoid_human_linaniil_supreme_archmage.png", img_y_off=-50, text=[[Though magic is still shunned in Maj'Eyal, rumours abound of secret havens of mages.]]}, {image="/data/gfx/shockbolt/npc/humanoid_orc_orc_elite_berserker.png", text=[[The Rush talent lets you close in on an enemy quickly and daze them, disabling them whilst you hack down their friends.]]}, diff --git a/game/modules/tome/load.lua b/game/modules/tome/load.lua index 9e79676c20041846a485ea2ffade00c9208608b9..a5e3eebac78c5abf7fbcc71e629a6706bebe9768 100644 --- a/game/modules/tome/load.lua +++ b/game/modules/tome/load.lua @@ -28,6 +28,7 @@ Entity.ascii_outline = {x=2, y=2, r=0, g=0, b=0, a=0.8} local UIBase = require "engine.ui.Base" local Map = require "engine.Map" local Level = require "engine.Level" +local FontPackage = require "engine.FontPackage" -- Init settings config.settings.tome = config.settings.tome or {} @@ -80,26 +81,13 @@ UIBase.ui = config.settings.tome.ui_theme2 UIBase:setTextShadow(0.6) -- Dialogs fonts -if config.settings.tome.fonts.type == "fantasy" then - local size = ({normal=16, small=12, big=18})[config.settings.tome.fonts.size] - UIBase.font = core.display.newFont("/data/font/DroidSans.ttf", size) - UIBase.font_bold = core.display.newFont("/data/font/DroidSans.ttf", size) - UIBase.font_mono = core.display.newFont("/data/font/DroidSansMono.ttf", size) - UIBase.font_bold:setStyle("bold") - UIBase.font_h = UIBase.font:lineSkip() - UIBase.font_bold_h = UIBase.font_bold:lineSkip() - UIBase.font_mono_w = UIBase.font_mono:size(" ") - UIBase.font_mono_h = UIBase.font_mono:lineSkip()+2 -else - local size = ({normal=12, small=10, big=14})[config.settings.tome.fonts.size] - UIBase.font = core.display.newFont("/data/font/Vera.ttf", size) - UIBase.font_mono = core.display.newFont("/data/font/VeraMono.ttf", size) - UIBase.font_bold = core.display.newFont("/data/font/VeraBd.ttf", size) - UIBase.font_h = UIBase.font:lineSkip() - UIBase.font_mono_w = UIBase.font_mono:size(" ") - UIBase.font_mono_h = UIBase.font_mono:lineSkip() - UIBase.font_bold_h = UIBase.font_bold:lineSkip() -end +UIBase.font = FontPackage:get("default") +UIBase.font_bold = FontPackage:get("bold") +UIBase.font_mono = FontPackage:get("mono") +UIBase.font_h = UIBase.font:lineSkip() +UIBase.font_bold_h = UIBase.font_bold:lineSkip() +UIBase.font_mono_w = UIBase.font_mono:size(" ") +UIBase.font_mono_h = UIBase.font_mono:lineSkip()+2 local Savefile = require "engine.Savefile" local KeyBind = require "engine.KeyBind" @@ -176,27 +164,27 @@ ActorInventory:defineInventory("HANDS", "On hands", true, "Various gloves can be ActorInventory:defineInventory("FEET", "On feet", true, "Sandals or boots can be worn on your feet.", nil, {equipdoll_back="ui/equipdoll/boots_inv.png"}) ActorInventory:defineInventory("TOOL", "Tool", true, "This is your readied tool, always available immediately.", nil, {equipdoll_back="ui/equipdoll/tool_inv.png"}) ActorInventory:defineInventory("QUIVER", "Quiver", true, "Your readied ammo.", nil, {equipdoll_back="ui/equipdoll/ammo_inv.png"}) -ActorInventory:defineInventory("GEM", "Socketed Gems", true, "Socketed gems.", nil, {equipdoll_back="ui/equipdoll/gem_inv.png"}) +ActorInventory:defineInventory("GEM", "Socketed Gems", true, "Gems worn in/on the body, providing their worn bonuses.", nil, {equipdoll_back="ui/equipdoll/gem_inv.png", stack_limit = 1}) ActorInventory:defineInventory("QS_MAINHAND", "Second weapon set: In main hand", false, "Weapon Set 2: Most weapons are wielded in the main hand. Press 'x' to switch weapon sets.", true) ActorInventory:defineInventory("QS_OFFHAND", "Second weapon set: In off hand", false, "Weapon Set 2: You can use shields or a second weapon in your off-hand, if you have the talents for it. Press 'x' to switch weapon sets.", true) ActorInventory:defineInventory("QS_PSIONIC_FOCUS", "Second weapon set: psionic focus", false, "Weapon Set 2: Object held in your telekinetic grasp. It can be a weapon or some other item to provide a benefit to your psionic powers. Press 'x' to switch weapon sets.", true) ActorInventory:defineInventory("QS_QUIVER", "Second weapon set: Quiver", false, "Weapon Set 2: Your readied ammo.", true) ActorInventory.equipdolls = { default = { w=48, h=48, itemframe="ui/equipdoll/itemframe48.png", itemframe_sel="ui/equipdoll/itemframe-sel48.png", ix=3, iy=3, iw=42, ih=42, doll_x=116, doll_y=168+64, list={ - PSIONIC_FOCUS = {{weight=1, x=48, y=48}}, - MAINHAND = {{weight=2, x=48, y=120}}, - OFFHAND = {{weight=3, x=48, y=192}}, - BODY = {{weight=4, x=48, y=264}}, - QUIVER = {{weight=5, x=48, y=336}}, - FINGER = {{weight=6, x=48, y=408}, {weight=7, x=120, y=408, text="bottom"}}, - LITE = {{weight=8, x=192, y=408}}, - TOOL = {{weight=9, x=264, y=408, text="bottom"}}, + PSIONIC_FOCUS = {{weight=1, x=48, y=48, subshift="left"}}, + MAINHAND = {{weight=2, x=48, y=120, subshift="left"}}, + OFFHAND = {{weight=3, x=48, y=192, subshift="left"}}, + BODY = {{weight=4, x=48, y=264, subshift="left"}}, + QUIVER = {{weight=5, x=48, y=336, subshift="left"}}, + FINGER = {{weight=6, x=48, y=408, subshift="bottom"}, {weight=7, x=120, y=408, text="bottom", subshift="bottom"}}, + LITE = {{weight=8, x=192, y=408, subshift="bottom"}}, + TOOL = {{weight=9, x=264, y=408, subshift="bottom", text="bottom"}}, FEET = {{weight=10, x=264, y=336}}, BELT = {{weight=11, x=264, y=264}}, HANDS = {{weight=12, x=264, y=192}}, CLOAK = {{weight=13, x=264, y=120}}, - NECK = {{weight=14, x=192, y=48, text="topright"}}, - HEAD = {{weight=15, x=120, y=48, text="topleft"}}, + NECK = {{weight=14, x=192, y=48, subshift="bottom", text="topright"}}, + HEAD = {{weight=15, x=120, y=48, subshift="bottom", text="topleft"}}, }}, alchemist_golem = { w=48, h=48, itemframe="ui/equipdoll/itemframe48.png", itemframe_sel="ui/equipdoll/itemframe-sel48.png", ix=3, iy=3, iw=42, ih=42, doll_x=116, doll_y=168+64, list={ MAINHAND = {{weight=1, x=48, y=120}}, diff --git a/game/modules/tome/resolvers.lua b/game/modules/tome/resolvers.lua index f69a5353ed5d71579cec0002e6c4ee5174706d15..3aebd4a3f70229b9d4fbd910b6baa9887f376f4c 100644 --- a/game/modules/tome/resolvers.lua +++ b/game/modules/tome/resolvers.lua @@ -32,24 +32,33 @@ end --- Actually resolve the equipment creation function resolvers.calc.equip(t, e) -- print("Equipment resolver for", e.name) - -- Iterate of object requests, try to create them and equip them + -- Iterate over object requests, try to create them and equip them for i, filter in ipairs(t[1]) do --- print("Equipment resolver", e.name, filter.type, filter.subtype, filter.defined) +-- print("Equipment resolver", e.name, filter.type, filter.subtype, filter.defined, filter.random_art_replace) local o - if not filter.defined then - o = game.zone:makeEntity(game.level, "object", filter, nil, true) - else - local forced - o, forced = game.zone:makeEntityByName(game.level, "object", filter.defined, filter.random_art_replace and true or false) - -- If we forced the generation this means it was already found - if forced then --- print("Serving unique "..o.name.." but forcing replacement drop") - filter.random_art_replace.chance = 100 + local tries = 0 + repeat + local ok = true + tries = tries + 1 + if not filter.defined then + o = game.zone:makeEntity(game.level, "object", filter, nil, true) + else + local forced + o, forced = game.zone:makeEntityByName(game.level, "object", filter.defined, filter.random_art_replace and true or false) + -- If we forced the generation this means it was already found + if forced then +-- print("Serving unique "..o.name.." but forcing replacement drop") + filter.random_art_replace.chance = 100 + end end - end + if o and o.power_source and (o.power_source.antimagic and e:attr("has_arcane_knowledge") or o.power_source.arcane and e:attr("forbid_arcane")) then -- check antimagic restrictions +-- if o and not filter.no_power_restrictions and not game.state:checkPowers(e, o) then -- Check power restrictions + ok = false + print(" Equipment resolver for ", e.name ," -- incompatible equipment ", o.name, "retrying", tries, "forbid ps:", filter.forbid_power_source and table.concat(table.keys(filter.forbid_power_source, ",")), "vs ps", o.power_source and table.concat(table.keys(o.power_source), ",")) + end + until ok or tries > 4 if o then --- print("Zone made us an equipment according to filter!", o:getName()) - +--- print("Zone made us an equipment according to filter!", o:getName()) -- curse (done here to ensure object attributes get applied correctly) if e:knowTalent(e.T_DEFILING_TOUCH) then local t = e:getTalentFromId(e.T_DEFILING_TOUCH) @@ -88,6 +97,60 @@ function resolvers.calc.equip(t, e) return nil end + +--- Resolves equipment creation for an actor +function resolvers.attachtinker(t) + return {__resolver="attachtinker", __resolve_last=true, t} +end +function resolvers.attachtinkerbirth(t) + for i, filter in ipairs(t) do + filter.ignore_material_restriction = true + end + return {__resolver="attachtinker", __resolve_last=true, t} +end +--- Actually resolve the equipment creation +function resolvers.calc.attachtinker(t, e) + print("Tinker resolver for", e.name) + -- Iterate of object requests, try to create them and equip them + for i, filter in ipairs(t[1]) do + print("Tinker resolver", e.name, filter.type, filter.subtype, filter.defined) + local o + if not filter.defined then + o = game.zone:makeEntity(game.level, "object", filter, nil, true) + else + local forced + o, forced = game.zone:makeEntityByName(game.level, "object", filter.defined, filter.random_art_replace and true or false) + -- If we forced the generation this means it was already found + if forced then +-- print("Serving unique "..o.name.." but forcing replacement drop") + filter.random_art_replace.chance = 100 + end + end + if o then + print("Zone made us an Tinker according to filter!", o:getName()) + -- Auto alloc some stats to be able to wear it + if filter.autoreq and rawget(o, "require") and rawget(o, "require").stat then +-- print("Autorequire stats") + for s, v in pairs(rawget(o, "require").stat) do + if e:getStat(s) < v then + e.unused_stats = e.unused_stats - (v - e:getStat(s)) + e:incStat(s, v - e:getStat(s)) + end + end + end + + local base_inven, base_item = e:findTinkerSpot(o) + if base_inven and base_item then + local base_o = base_inven[base_item] + e:doWearTinker(nil, nil, o, base_inven, base_item, base_o, true) + if t[1].id then o:identify(t[1].id) end + end + end + end + -- Delete the origin field + return nil +end + --- Resolves inventory creation for an actor function resolvers.inventory(t) return {__resolver="inventory", __resolve_last=true, t} @@ -616,6 +679,146 @@ function resolvers.calc.tactic(t, e) return {} end +--- Resolve tactical ai weights based on talents known +-- mostly to make sure randbosses have sensible ai_tactic tables +-- this tends to make npc's slightly more aggressive/defensive depending on their talents +-- @param method = function to be applied to generating the ai_tactic table <not implemented> +-- @param tactic_total = total tactical weights desired <10> +-- @param weight_power = smoothing factor to balance out weights <0.5> +-- applied with "on_added_to_level" +function resolvers.talented_ai_tactic(method, tactic_total, weight_power) + local method = method or "simple_recursive" + return {__resolver="talented_ai_tactic", method, tactic_total, weight_power, __resolve_last=true, + } +end + +-- Extra recursive methods not handled yet +function resolvers.calc.talented_ai_tactic(t, e) + local old_on_added_to_level = e.on_added_to_level + e.__ai_compute = t + e.on_added_to_level = function(e, level, x, y) + local t = e.__ai_compute + if old_on_added_to_level then old_on_added_to_level(e, level, x, y) end + print(" # talented_ai_tactic resolver function for", e.name, "level=", e.level, e.uid) + local tactic_total = t[2] or t.tactic_total or 10 --want tactic weights to total 10 + local weight_power = t[3] or t.weight_power or 0.5 --smooth out tactical weights + local tacs_offense = {attack=1, attackarea=1} + local tacs_close = {closein=1, go_melee=1} + local tacs_defense = {escape=1, defend=1, heal=1, protect=1, disable = 1} +-- local tac_types = {type="melee",type = "ranged", type="tank", type="survivor"} + local tactic, tactical = {}, {total = 0} +-- local count = {talents = 0, atk_count = 0, atk_value = 0, total_range = 0, +-- atk_melee = 0, melee_value = 0, range_value = 0, atk_range = 0, +-- escape = 0, close = 0, def_count = 0, def_value = 0, disable=0} + local do_count, counted, count_talent, val + local tac_count = #table.keys(tacs_offense) + #table.keys(tacs_close) + #table.keys(tacs_defense) + local count = {tal_count = 0, atk_count = 0, total_range = 0, + atk_melee = 0, melee_value = 0, range_value = 0, atk_range = 0, + escape = 0, close = 0, tac_count = tac_count} + -- go through all talents, adding up all the tactical weights from the tactical tables + local tal + local function get_weight(wt) + local val = 0 + if type(wt) == "function" then + wt = wt(e, tal, e) -- try to target self for effectiveness + end + if type(wt) == "number" then return wt + elseif type(wt) == "table" then + for _, n in pairs(wt) do + val = math.max(val, get_weight(n)) + end + if val == 0 then val = 2 end + end + return tonumber(val) or 0 + end + + for tid, tl in pairs(e.talents) do + tal = e:getTalentFromId(tid) + local range, radius = e:getTalentRange(tal), e:getTalentRadius(tal) +-- if range > 0 then range = range + radius*2/3 end + count_talent = false, false + if tal and tal.tactical then + print(" #- tactical table for talent", tal.name, "range", range, "radius", radius) +-- table.print(tal.tactical) + do_count = false + for tt, wt in pairs(tal.tactical) do + val = get_weight(wt, e) + print(" --- ", tt, "wt=", val) + tactical[tt] = (tactical[tt] or 0) + val -- sum up all the input weights + if tacs_offense[tt] then + do_count = true + count.atk_count = count.atk_count + 1 + val = val * tacs_offense[tt] +-- count.atk_value = count.atk_value + val + if range >= 2 or radius > 2 then + count.atk_range = count.atk_range + 1 + count.range_value = count.range_value + val + else + count.atk_melee = count.atk_melee + 1 + count.melee_value = count.melee_value + val + end + count.total_range = count.total_range + range + radius*2/3 + end + if tacs_defense[tt] then + do_count = true + if tt == "escape" then count.escape = count.escape + 1 end + if tt == "disable" then -- for range average only + count.atk_count = count.atk_count + 1 + count.total_range = count.total_range + range + radius*2/3 + end + end + if tacs_close[tt] then + do_count = true + count.close = count.close + 1 + end + if do_count then -- sum up only relevant weights + count_talent = true +-- tactical.total = tactical.total + val + tactic[tt] = (tactic[tt] or 0) + val + end + end + if count_talent then + count.tal_count = count.tal_count + 1 +-- table.print(count, "--") + end + end + end + + -- normalize weights + count.avg_attack_range = count.total_range/count.atk_count + local norm_total = 0 + for tt, wt in pairs(tactic) do + local ave_weight = (tactic[tt]+count.tal_count)/count.tal_count + local ave_xweight = ave_weight^weight_power - 1 + if ave_xweight > 1/tac_count then + tactic[tt] = ave_weight + norm_total = norm_total + ave_weight + else + tactic[tt] = nil -- defaults to a weight of 1 in the tactical ai + end + end + for tt, _ in pairs(tactic) do + tactic[tt] = tactic[tt]*tactic_total/norm_total + if tactic[tt] < 1 then tactic[tt] = nil end -- defaults to a weight of 1 in the tactical ai + end + + -- NPC's with predominantly ranged attacks will want to stay at range. + if count.atk_range + count.escape > count.atk_melee + count.close and count.range_value /(count.melee_value + 1) > 1.5 then + tactic.safe_range = math.max(2, math.ceil(count.avg_attack_range/2)) + end + + tactic.tactical_sum=tactical + tactic.count = count + tactic.level = e.level + tactic.type = "computed" +print(" ### ai_tactic table:") +for tac, wt in pairs(tactic) do print(" ##", tac, wt) end + e.ai_tactic = tactic + e.__ai_compute = nil + return tactic + end +end + --- Racial Talents resolver local racials = { diff --git a/game/profile-thread/Client.lua b/game/profile-thread/Client.lua index f34b03ea15b6eea946398b14eb0a1fffc6eac811..271e98310dd165282ef4539007a15d2eead31945 100644 --- a/game/profile-thread/Client.lua +++ b/game/profile-thread/Client.lua @@ -36,7 +36,7 @@ end function _M:connected() if self.sock then return true end self.sock = socket.connect("profiles.te4.org", mport) - if not self.sock then return false end + if not self.sock then self:disconnect() return false end -- self.sock:settimeout(10) print("[PROFILE] Thread connected to profiles.te4.org") self:login() @@ -63,12 +63,10 @@ end function _M:disconnect() cprofile.pushEvent("e='Disconnected'") - if self.psock then - self.psock:close() - self.psock = nil - end - self.sock:close() + if self.psock then self.psock:close() end + if self.sock then self.sock:close() end self.sock = nil + self.psock = nil self.auth = nil core.game.sleep(5000) -- Wait 5 secs end @@ -267,9 +265,12 @@ function _M:orderLogin(o) self.user_login = o.l self.user_pass = o.p + if not self.sock then cprofile.pushEvent("e='Disconnected'") return end + -- Already logged? if self.auth and self.auth.login == o.l then print("[PROFILE] reusing login", self.auth.name) + if self.sock then cprofile.pushEvent("e='Connected'") end cprofile.pushEvent(string.format("e='Auth' ok=%q", table.serialize(self.auth))) self.chat:forwardFriends() else @@ -282,9 +283,12 @@ function _M:orderSteamLogin(o) self.steam_token_name = o.name if o.email and #o.email > 1 then self.steam_token_email = o.email end + if not self.sock then cprofile.pushEvent("e='Disconnected'") return end + -- Already logged? if self.auth then print("[PROFILE] reusing login", self.auth.name) + if self.sock then cprofile.pushEvent("e='Connected'") end cprofile.pushEvent(string.format("e='Auth' ok=%q", table.serialize(self.auth))) else self:login() @@ -405,6 +409,30 @@ function _M:orderCheckBatchHash(o) end end +function _M:orderCheckAddonUpdates(o) + if not self.sock then cprofile.pushEvent("e='CheckAddonUpdates' ok=false not_connected=true") end + local data = zlib.compress(table.serialize(o.list)) + self:command("ADDN SHOULD_UPDATE", #data) + if self:read("200") then + self.sock:send(data) + if self:read("200") then + local _, _, size = self.last_line:find("^([0-9]+)") + size = tonumber(size) + local list = {} + if size and size > 1 then + local body = self:receive(size) + if body then body = zlib.decompress(body) end + if body then body = body:unserialize() end + if body then list = body end + end + + cprofile.pushEvent(("e='CheckAddonUpdates' ok=%q"):format(table.serialize(list))) + return + end + end + cprofile.pushEvent("e='CheckAddonUpdates' ok=false") +end + function _M:orderRegisterNewCharacter(o) self:command("CHAR", "NEW", o.module) if self:read("200") then diff --git a/game/thirdparty/licenses/Font-Salsa-OFL.txt b/game/thirdparty/licenses/Font-Salsa-OFL.txt new file mode 100644 index 0000000000000000000000000000000000000000..194e360eca4d0ad865c68483ca9d7dc7068c66b2 --- /dev/null +++ b/game/thirdparty/licenses/Font-Salsa-OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2011 by John Vargas Beltrán (john.vargasbeltran@gmail.com), +with Reserved Font Name Salsa. +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/ideas/factions.ods b/ideas/factions.ods index 5e3f211385812455e3fb91df4c433ab6d891a865..562b0e6d07e2a730ca98e02286282c518d9c4b7b 100644 Binary files a/ideas/factions.ods and b/ideas/factions.ods differ diff --git a/ideas/todo b/ideas/todo index 2035c039b6c1e326c60c39d9836f83ed7a9c7caa..2fea1cb902fbfbdbb146979aec9187f7d21a67df 100644 --- a/ideas/todo +++ b/ideas/todo @@ -17,6 +17,8 @@ ziguranth that mobbed him and will burn him when the player arrives. she can join them or rescue him. later he is back in derth and a potential love interrest. There must be a way of getting his rune off to use it +* boss spell: CORRUPETD GROUND => move to destack + * porting to gl es and mobiles. suslik canhelp . * additive blending, different blending options diff --git a/src/map.c b/src/map.c index f7e2dfef73e670a50edca1fa46a7984e37c2a0ed..b89ca451abc058dd828211002dc0d1131d9f32c5 100644 --- a/src/map.c +++ b/src/map.c @@ -93,6 +93,7 @@ static int map_object_new(lua_State *L) obj->mm_b = -1; obj->valid = TRUE; + obj->world_x = obj->world_y = 0; obj->dx = luaL_checknumber(L, 6); obj->dy = luaL_checknumber(L, 7); obj->dw = luaL_checknumber(L, 8); @@ -352,22 +353,11 @@ static int map_object_get_move_anim(lua_State *L) return 2; } -static int map_object_get_move_anim_raw(lua_State *L) +static int map_object_get_world_pos(lua_State *L) { map_object *obj = (map_object*)auxiliar_checkclass(L, "core{mapobj}", 1); - map_type *map = (map_type*)auxiliar_checkclass(L, "core{map}", 2); - - float offx = 0; - float offy = 0; - - if (obj->oldrawdx != obj->cur_x) offx = obj->cur_x - obj->oldrawdx; - obj->oldrawdx = obj->cur_x; - - if (obj->oldrawdy != obj->cur_y) offy = obj->cur_y - obj->oldrawdy; - obj->oldrawdy = obj->cur_y; - - lua_pushnumber(L, (obj->animdx + obj->cur_x + offx) * map->tile_w); - lua_pushnumber(L, (obj->animdy + obj->cur_y + offy) * map->tile_h); + lua_pushnumber(L, obj->world_x); + lua_pushnumber(L, obj->world_y); return 2; } @@ -1379,8 +1369,8 @@ void do_quad(lua_State *L, const map_object *m, const map_object *dm, const map_ } } -inline void display_map_quad(lua_State *L, GLuint *cur_tex, int *vert_idx, int *col_idx, map_type *map, int dx, int dy, float dz, map_object *m, int i, int j, float a, float seen, int nb_keyframes, bool always_show) ALWAYS_INLINE; -void display_map_quad(lua_State *L, GLuint *cur_tex, int *vert_idx, int *col_idx, map_type *map, int dx, int dy, float dz, map_object *m, int i, int j, float a, float seen, int nb_keyframes, bool always_show) +inline void display_map_quad(lua_State *L, GLuint *cur_tex, int *vert_idx, int *col_idx, map_type *map, int scrollx, int scrolly, int bdx, int bdy, float dz, map_object *m, int i, int j, float a, float seen, int nb_keyframes, bool always_show) ALWAYS_INLINE; +void display_map_quad(lua_State *L, GLuint *cur_tex, int *vert_idx, int *col_idx, map_type *map, int scrollx, int scrolly, int bdx, int bdy, float dz, map_object *m, int i, int j, float a, float seen, int nb_keyframes, bool always_show) { map_object *dm; float r, g, b; @@ -1390,6 +1380,8 @@ void display_map_quad(lua_State *L, GLuint *cur_tex, int *vert_idx, int *col_idx bool up_important = FALSE; float anim; int anim_step; + int dx = scrollx + bdx; + int dy = scrolly + bdy; /******************************************************** ** Select the color to use @@ -1570,6 +1562,8 @@ void display_map_quad(lua_State *L, GLuint *cur_tex, int *vert_idx, int *col_idx } anim = (float)anim_step / dm->anim_max; } + dm->world_x = bdx + (dm->dx + animdx) * map->tile_w; + dm->world_y = bdy + (dm->dy + animdy) * map->tile_h; do_quad(L, m, dm, map, vertices, texcoords, colors, vert_idx, col_idx, @@ -1686,8 +1680,8 @@ static int map_to_screen(lua_State *L) { for (i = mini; i < maxi; i++) { - int dx = x + i * map->tile_w; - int dy = y + j * map->tile_h + (i & map->is_hex) * map->tile_h / 2; + int dx = i * map->tile_w; + int dy = j * map->tile_h + (i & map->is_hex) * map->tile_h / 2; map_object *mo = map->grids[i][j][z]; if (!mo) continue; @@ -1695,11 +1689,11 @@ static int map_to_screen(lua_State *L) { if (map->grids_seens[j*map->w+i]) { - display_map_quad(L, &cur_tex, &vert_idx, &col_idx, map, dx, dy, z, mo, i, j, 1, map->grids_seens[j*map->w+i], nb_keyframes, always_show); + display_map_quad(L, &cur_tex, &vert_idx, &col_idx, map, x, y, dx, dy, z, mo, i, j, 1, map->grids_seens[j*map->w+i], nb_keyframes, always_show); } else { - display_map_quad(L, &cur_tex, &vert_idx, &col_idx, map, dx, dy, z, mo, i, j, 1, 0, nb_keyframes, always_show); + display_map_quad(L, &cur_tex, &vert_idx, &col_idx, map, x, y, dx, dy, z, mo, i, j, 1, 0, nb_keyframes, always_show); } } } @@ -1945,7 +1939,7 @@ static const struct luaL_Reg map_object_reg[] = {"resetMoveAnim", map_object_reset_move_anim}, {"setMoveAnim", map_object_set_move_anim}, {"getMoveAnim", map_object_get_move_anim}, - {"getMoveAnimRaw", map_object_get_move_anim_raw}, + {"getWorldPos", map_object_get_world_pos}, {"setAnim", map_object_set_anim}, {NULL, NULL}, }; diff --git a/src/map.h b/src/map.h index ee8c64799bb0f1b49f7ccce9e1c9aea645333ffb..0f6b9317c0f7948718d45d8447c6f4a23372d887 100644 --- a/src/map.h +++ b/src/map.h @@ -33,7 +33,7 @@ struct s_map_object { shader_type *shader; int shader_ref; int cur_x, cur_y; - float dx, dy, scale; + float dx, dy, scale, world_x, world_y; float animdx, animdy; float oldrawdx, oldrawdy; float dw, dh; diff --git a/src/particles.c b/src/particles.c index 786f267d688e1b0f1b62cf32e67bf840914963af..8e52ca2b4d8af81ba116eef1d5e0566ccf076bbe 100644 --- a/src/particles.c +++ b/src/particles.c @@ -180,21 +180,24 @@ static int particles_set_sub(lua_State *L) static void do_shift(particles_type *ps, float sx, float sy) { SDL_mutexP(ps->lock); - int w; - for (w = 0; w < ps->nb; w++) - { - particle_type *p = &ps->particles[w]; - - if (p->life > 0) + if (ps->alive) { + int w; + for (w = 0; w < ps->nb; w++) { - p->x += sx; - p->ox += sx; - p->y += sy; - p->oy += sy; + particle_type *p = &ps->particles[w]; + if (!p) break; + + if (p->life > 0) + { + p->x += sx; + p->ox += sx; + p->y += sy; + p->oy += sy; + } } - } - ps->recompile = TRUE; + ps->recompile = TRUE; + } SDL_mutexV(ps->lock);