diff --git a/game/engine/Map.lua b/game/engine/Map.lua index 804b93ae31e19740ddf7f83e3a242790c34ffd32..d7e45507d32511f4081ad4922536929eb506e10a 100644 --- a/game/engine/Map.lua +++ b/game/engine/Map.lua @@ -376,10 +376,12 @@ end -- @param dir the numpad direction of the effect, 5 for a ball effect -- @param overlay a simple display entity to draw upon the map -- @param update_fct optional function that will be called each time the effect is updated with the effect itself as parameter. Use it to change radius, move around .... -function _M:addEffect(src, x, y, duration, damtype, dam, radius, dir, angle, overlay, update_fct) +function _M:addEffect(src, x, y, duration, damtype, dam, radius, dir, angle, overlay, update_fct, friendlyfire) + if friendlyfire == nil then friendlyfire = true end + print(friendlyfire) table.insert(self.effects, { src=src, x=x, y=y, duration=duration, damtype=damtype, dam=dam, radius=radius, dir=dir, angle=angle, overlay=overlay, - update_fct=update_fct, + update_fct=update_fct, friendlyfire=friendlyfire }) self.changed = true end @@ -429,7 +431,9 @@ function _M:processEffects() -- Now display each grids for lx, ys in pairs(grids) do for ly, _ in pairs(ys) do - DamageType:get(e.damtype).projector(e.src, lx, ly, e.damtype, e.dam) + if e.friendlyfire or (lx ~= e.src.x and ly ~= e.src.y) then + DamageType:get(e.damtype).projector(e.src, lx, ly, e.damtype, e.dam) + end end end diff --git a/game/engine/class.lua b/game/engine/class.lua index e7f10e3166a476c51a83e7bcebe0f119834eb1a2..08a164cbda642e932c3f4d610739aa8a999a23a9 100644 --- a/game/engine/class.lua +++ b/game/engine/class.lua @@ -98,7 +98,7 @@ local function basicSerialize(o) if type(o) == "number" or type(o) == "boolean" then return tostring(o) elseif type(o) == "function" then - return string.format("%q", string.dump(o)) + return string.format("loadstring(%q)", string.dump(o)) else -- assume it is a string return string.format("%q", o) end @@ -157,6 +157,7 @@ local function deserialize(string) local f, err = loadstring(string) if err then print("error deserializing", string, err) end setfenv(f, { + loadstring = loadstring, loadObject = function(n) -- print("wants to load",n) return engine.Savefile.current_save:loadReal(n) diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 389188e70325ffeb7209dac5f9eb6d6163869c98..85fb1ff4df26b6e0f1ef49dabc40bd8e52bc32df 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -37,6 +37,10 @@ function _M:init(t) self.combat_spellspeed = 0 self.combat_spellcrit = 0 self.combat_spellpower = 0 + + self.combat_physresist = 0 + self.combat_spellresist = 0 + self.fatigue = 0 -- Default melee barehanded damage @@ -67,6 +71,9 @@ function _M:act() -- Compute timed effects self:timedEffects() + -- Still enough energy to act ? + if self.energy.value < game.energy_to_act then return false end + return true end diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 9f3a82184bac9396bc103701335dad8a24e887b1..fa86718b34894c1b67ae356ecd62c1eeca897b8a 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -72,11 +72,7 @@ end function _M:newGame() self.zone = Zone.new("ancient_ruins") - self.player = Player.new{ - name=self.player_name, max_life=25, max_mana=25, max_stamina=25, display='@', color_r=230, color_g=230, color_b=230, - unused_stats = 6, unused_talents = 3, image="player.png", - move_others=true, - } + self.player = Player.new{name=self.player_name} local birth = Birther.new(self.player, {"base", "race", "subrace", "sex", "class", "subclass" }, function() self:changeLevel(1) diff --git a/game/modules/tome/class/NPC.lua b/game/modules/tome/class/NPC.lua index 2187211f92ba08b0afa25b30c812f43aa282a6bb..7a1372162c5c04c73c52e975b0230f38b3ed7de8 100644 --- a/game/modules/tome/class/NPC.lua +++ b/game/modules/tome/class/NPC.lua @@ -11,7 +11,7 @@ end function _M:act() -- Do basic actor stuff - mod.class.Actor.act(self) + if not mod.class.Actor.act(self) then return end -- Let the AI think .... beware of Shub ! self:doAI() diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index 72cd7bdfc6c4b4461fbd22af63b8a52039728776..eb02f03215c209327ecea76937021c72f142aeee 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -24,10 +24,23 @@ function _M:init(t) self.player = true self.faction = "players" + self.display='@' + self.color_r=230 + self.color_g=230 + self.color_b=230 + self.image="player.png" + -- Default regen - self.mana_regen = self.mana_regen or 1 - self.stamina_regen = self.stamina_regen or 1 - self.life_regen = self.life_regen or 0.5 + self.mana_regen = 1 + self.stamina_regen = 1 + self.life_regen = 0.5 + + self.max_life=85 + self.max_mana=85 + self.max_stamina=85 + self.unused_stats = 6 + self.unused_talents = 3 + self.move_others=true self.descriptor = {} self.hotkey = {} @@ -47,7 +60,7 @@ function _M:move(x, y, force) end function _M:act() - mod.class.Actor.act(self) + if not mod.class.Actor.act(self) then return end game.paused = true end diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index d4d108e9e9467f112f4fb5cb8185cf742c0a00cd..c677dd25e0e33e1d6102c429fa1312e6acf27c5a 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -72,6 +72,23 @@ function _M:attackTarget(target) end end +--- Computes a logarithmic chance to hit, opposing chance to hit to chance to miss +-- This will be used for melee attacks, physical and spell resistance +function _M:checkHit(atk, def, min, max, facotr) +print("checkHit", atk, def) + if atk == 0 then atk = 1 end + local hit = nil + factor = factor or 5 + if atk > def then + hit = math.log10(1 + 5 * (atk - def) / atk) * 100 + 50 + else + hit = -math.log10(1 + 5 * (def - atk) / atk) * 100 + 50 + end + hit = util.bound(hit, min or 5, max or 95) +print("=> chance to hit", hit) + return rng.percent(hit) +end + --- Attacks with one weapon function _M:attackTargetWith(target, weapon) local damtype = DamageType.PHYSICAL @@ -79,19 +96,9 @@ function _M:attackTargetWith(target, weapon) -- Does the blow connect? yes .. complex :/ local atk, def = self:combatAttack(weapon), target:combatDefense() local dam, apr, armor = self:combatDamage(weapon), self:combatAPR(weapon), target:combatArmor() - print(atk, def, "::", dam, apr, armor) - if afk == 0 then atk = 1 end - local hit = nil - if atk > def then - hit = math.log10(1 + 5 * (atk - def) / atk) * 100 + 50 - else - hit = -math.log10(1 + 5 * (def - atk) / atk) * 100 + 50 - end - hit = util.bound(hit, 5, 95) - print("hit: ", hit, "from", atk, def) -- If hit is over 0 it connects, if it is 0 we still have 50% chance - if rng.percent(hit) then + if self:checkHit(atk, def) then local dam = dam - math.max(0, armor - apr) dam = self:physicalCrit(dam, weapon) DamageType:get(damtype).projector(self, target.x, target.y, damtype, dam) @@ -181,3 +188,13 @@ function _M:spellCrit(dam) end return dam end + +--- Computes physical resistance +function _M:combatPhysicalResist() + return self.combat_physresist + (self:getCon() + self:getStr()) * 0.5 +end + +--- Computes spell resistance +function _M:combatSpellResist() + return self.combat_spellresist + (self:getMag() + self:getWil()) * 0.5 +end diff --git a/game/modules/tome/data/birth/classes.lua b/game/modules/tome/data/birth/classes.lua index 57c8b1b9a12c276b1a391fbfd1caf2ebc43665cb..bb5da8c77bd525df9140c04a0471ccae212a2582 100644 --- a/game/modules/tome/data/birth/classes.lua +++ b/game/modules/tome/data/birth/classes.lua @@ -59,7 +59,7 @@ newBirthDescriptor{ ["spell/arcane"]=true, ["spell/fire"]=true, ["spell/earth"]=true, - ["spell/cold"]=true, + ["spell/water"]=true, ["spell/air"]=true, ["spell/mind"]=true, ["spell/temporal"]=true, diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua index 66d9aee8de45c2569d4ff1e076e6dff5887645ea..f4cebca9b1cba8d2a3e0c9bf83d23295a51f36cf 100644 --- a/game/modules/tome/data/damage_types.lua +++ b/game/modules/tome/data/damage_types.lua @@ -49,7 +49,7 @@ newDamageType{ local target = game.level.map(x, y, Map.ACTOR) if target then -- Set on fire! - target:setEffect(target.EFF_BURNING, 3, {src=src, power=dam / 2 / 3}) + target:setEffect(target.EFF_BURNING, 3, {src=src, power=dam / 6}) end end, } @@ -57,3 +57,22 @@ newDamageType{ newDamageType{ name = "netherflame", type = "NETHERFLAME", } + +newDamageType{ + name = "freeze", type = "FREEZE", + projector = function(src, x, y, type, dam) + local target = game.level.map(x, y, Map.ACTOR) + if target then + -- Freeze it, if we pass the test + local sx, sy = game.level.map:getTileToScreen(x, y) + if target:checkHit(src:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) then + target:setEffect(target.EFF_FROZEN, dam, {src=src}) + + game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, "Frozen!", {0,255,155}) + else + game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, "Resist!", {0,255,155}) + game.logSeen(target, "%s resists!", target.name:capitalize()) + end + end + end, +} diff --git a/game/modules/tome/data/talents/spells/water.lua b/game/modules/tome/data/talents/spells/water.lua index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a2b6540f57027f3271cb9d374e66fae8e15b33dc 100644 --- a/game/modules/tome/data/talents/spells/water.lua +++ b/game/modules/tome/data/talents/spells/water.lua @@ -0,0 +1,56 @@ +newTalent{ + name = "Freeze", + type = {"spell/water", 1}, + mana = 14, + cooldown = 3, + tactical = { + ATTACK = 10, + }, + action = function(self) + local t = {type="hit", range=20} + local x, y = self:getTarget(t) + if not x or not y then return nil end + self:project(t, x, y, DamageType.COLD, self:spellCrit(7 + self:combatSpellpower(0.7))) + self:project(t, x, y, DamageType.FREEZE, 2) + return true + end, + require = { stat = { mag=14 }, }, + info = function(self) + return ([[Condenses ambiant water on a target, freezing it for a short while. + The damage will increase with the Magic stat]]):format(7 + self:combatSpellpower(0.7)) + end, +} + +newTalent{ + name = "Ice Storm", + type = {"spell/water",2}, + mana = 45, + cooldown = 30, + tactical = { + ATTACKAREA = 20, + }, + action = function(self) + local duration = 5 + self:combatSpellpower(0.25) + local radius = 3 + local dam = 12 + self:combatSpellpower(0.20) + -- Add a lasting map effect + game.level.map:addEffect(self, + self.x, self.y, duration, + DamageType.COLD, dam, + radius, + 5, nil, + engine.Entity.new{alpha=100, display='', color_br=30, color_bg=60, color_bb=200}, + function(e) + e.x = e.src.x + e.y = e.src.y + end, + false + ) + return true + end, + require = { stat = { mag=16 }, }, + info = function(self) + return ([[A furious ice storm rages around the caster doing %0.2f cold damage in a radius of 3 each turns for %d turns. + The damage and duration will increase with the Magic stat]]):format(12 + self:combatSpellpower(0.20), 5 + self:combatSpellpower(0.25)) + end, +} diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua index d2bcd39add0721946f285be3d4538465d4c67a48..2af1bb3d0b543ec398baaf52d43f0fa336f4bc6e 100644 --- a/game/modules/tome/data/timed_effects.lua +++ b/game/modules/tome/data/timed_effects.lua @@ -55,3 +55,53 @@ newEffect{ DamageType:get(DamageType.FIRE).projector(eff.src, self.x, self.y, DamageType.FIRE, eff.power) end, } + +newEffect{ + name = "FROZEN", + desc = "Frozen", + type = "magical", + status = "detrimental", + parameters = {}, + on_gain = function(self, err) return "#Target# is frozen!" end, + on_lose = function(self, err) return "#Target# warms up.", "-Frozen" end, + activate = function(self, eff) + -- Change color + eff.old_r = self.color_r + eff.old_g = self.color_g + eff.old_b = self.color_b + self.color_r = 0 + self.color_g = 255 + self.color_b = 155 + game.level.map:updateMap(self.x, self.y) + + -- Frozen, cannot act + self.energy.value = 0 + end, + on_timeout = function(self, eff) + -- Frozen, cannot act + self.energy.value = 0 + end, + deactivate = function(self, eff) + self.color_r = eff.old_r + self.color_g = eff.old_g + self.color_b = eff.old_b + end, +} + +newEffect{ + name = "STUNNED", + desc = "STUN", + type = "physical", + status = "detrimental", + parameters = {}, + on_gain = function(self, err) return "#Target# is stunned!", "+Stunned" end, + on_lose = function(self, err) return "#Target# is not stunned anymore.", "-Stunned" end, + activate = function(self, eff) + -- Frozen, cannot act + self.energy.value = 0 + end, + on_timeout = function(self, eff) + -- Frozen, cannot act + self.energy.value = 0 + end, +} diff --git a/ideas/spells.ods b/ideas/spells.ods index 3585db60e4592842bed131312433b5eaa82f8a04..991558f5e8c5886bcdc9daa6f3d6876aa016f51e 100644 Binary files a/ideas/spells.ods and b/ideas/spells.ods differ