diff --git a/game/engine/Actor.lua b/game/engine/Actor.lua index 9d58fd9030087384b3cb512d83c37c31cbc10b13..1a1c6a86210491585f739fb3cee94ecb13db04d6 100644 --- a/game/engine/Actor.lua +++ b/game/engine/Actor.lua @@ -27,6 +27,15 @@ function _M:act() return true end +--- Gets the actor target +-- Does nothing, AI redefines it so should a "Player" class +function _M:getTarget() +end +--- Sets the actor target +-- Does nothing, AI redefines it so should a "Player" class +function _M:setTarget(target) +end + --- Moves an actor on the map -- *WARNING*: changing x and y properties manualy is *WRONG* and will blow up in your face. Use this method. Always. -- @param map the map to move onto diff --git a/game/engine/Entity.lua b/game/engine/Entity.lua index 109d2fa68593a5666a529db7fecf2df3e6377f2f..c2e0418d5383380a3116e60c76b55474eae549f2 100644 --- a/game/engine/Entity.lua +++ b/game/engine/Entity.lua @@ -41,18 +41,19 @@ function _M:init(t, no_default) self[k] = ee end + if self.color then + self.color_r = self.color.r + self.color_g = self.color.g + self.color_b = self.color.b + self.color_br = self.color.br + self.color_bg = self.color.bg + self.color_bb = self.color.bb + self.color = nil + end + if not no_default then self.image = self.image or nil self.display = self.display or '.' - if self.color then - self.color_r = self.color.r - self.color_g = self.color.g - self.color_b = self.color.b - self.color_br = self.color.br - self.color_bg = self.color.bg - self.color_bb = self.color.bb - self.color = nil - end self.color_r = self.color_r or 0 self.color_g = self.color_g or 0 self.color_b = self.color_b or 0 diff --git a/game/engine/GameEnergyBased.lua b/game/engine/GameEnergyBased.lua index fdb3563ccc05935d38504fa471a78584fd21fbe5..2a7a3034ec0ca65387a23ddadf5b793cb4f3db5c 100644 --- a/game/engine/GameEnergyBased.lua +++ b/game/engine/GameEnergyBased.lua @@ -11,8 +11,18 @@ module(..., package.seeall, class.inherit(engine.Game)) -- @energy_to_act how much energy does an entity need to act -- @energy_per_tick how much energy does an entity recieves per game tick. This is multiplied by the entity energy.mod property function _M:init(keyhandler, energy_to_act, energy_per_tick) - self.energy_to_act, self.energy_per_tick = energy_to_act, energy_per_tick + self.energy_to_act, self.energy_per_tick = energy_to_act or 1000, energy_per_tick or 100 engine.Game.init(self, keyhandler) + + self.entities = {} + self:loaded() +end + +function _M:loaded() + engine.Game.loaded(self) + + -- Setup the entities repository as a weak value table, when the entities are no more used anywhere else they disappear from there too + setmetatable(self.entities, {__mode="v"}) end --- Gives energy and act if needed @@ -25,14 +35,44 @@ function _M:tick() local arr = self.level.e_array for i = 1, #arr do e = arr[i] - if e then + if e and e.act then if e.energy and e.energy.value < self.energy_to_act then e.energy.value = (e.energy.value or 0) + self.energy_per_tick * (e.energy.mod or 1) end - if e.energy.value >= self.energy_to_act and e.act then + if e.energy.value >= self.energy_to_act then e:act(self) end end end end + + local arr = self.entities + for i, e in pairs(arr) do + e = arr[i] + if e and e.act then + if e.energy and e.energy.value < self.energy_to_act then + e.energy.value = (e.energy.value or 0) + self.energy_per_tick * (e.energy.mod or 1) + end + if e.energy.value >= self.energy_to_act then + e:act(self) + end + end + end +end + +--- Adds an entity to the game +-- This differs from Level:addEntity in that it's not specific to actors and the entities are not bound to +-- the current level. Also they are stored in a *WEAK* table, so this wont hold them from garbage +-- collecting if they are not +function _M:addEntity(e) + if not e.canAct or not e:canAct() then return end + if self.entities[e.uid] then error("Entity "..e.uid.." already present in the game") end + self.entities[e.uid] = e +end + +--- Removes an entity from the game +function _M:removeEntity(e) + if not e.canAct or not e:canAct() then return end + if not self.entities[e.uid] then error("Entity "..e.uid.." not present in the game") end + self.entities[e.uid] = nil end diff --git a/game/engine/Level.lua b/game/engine/Level.lua index d631223a8dd4427b9db15cbf544434f529775bec..63730141f9d91fe7fe4cdf30e8bf7518408e5044 100644 --- a/game/engine/Level.lua +++ b/game/engine/Level.lua @@ -26,6 +26,7 @@ function _M:addEntity(e) if self.entities[e.uid] then error("Entity "..e.uid.." already present on the level") end self.entities[e.uid] = e table.insert(self.e_array, e) + game:addEntity(e) end --- Removes an entity from the level @@ -38,6 +39,7 @@ function _M:removeEntity(e) break end end + game:removeEntity(e) -- Tells it to delete itself if needed if e.deleteFromMap then e:deleteFromMap(self.map) end end diff --git a/game/engine/Object.lua b/game/engine/Object.lua index 9bfb78f8ad7ea605baf8e0da6e94dcc3305b420c..4bbedc857f037aa24f3156da6316c2be386cc9e1 100644 --- a/game/engine/Object.lua +++ b/game/engine/Object.lua @@ -5,9 +5,37 @@ module(..., package.seeall, class.inherit(Entity)) function _M:init(t, no_default) t = t or {} + + self.energy = t.energy or { value=0, mod=1 } + self.energy.value = self.energy.value or 0 + self.energy.mod = self.energy.mod or 0 + Entity.init(self, t, no_default) end +--- Resolves the object +-- This will call the entities resolver and then add to the game entities list +function _M:resolve(t) + engine.Entity.resolve(self, t) + + if not t then + -- Auto add all objects to the game, if they can act + game:addEntity(self) + end +end + +--- Can this object act at all +-- Most object will want to anwser false, only recharging and stuff needs them +function _M:canAct() + return false +end + +--- Do something when its your turn +-- For objects this mostly is to recharge them +-- By default, does nothing at all +function _M:act() +end + --- Gets the full name of the object function _M:getName() return self.name @@ -24,3 +52,15 @@ function _M:wornInven() local invens = require "engine.interface.ActorInventory" return invens["INVEN_"..self.slot] end + +--- Do we have enough energy +function _M:enoughEnergy(val) + val = val or game.energy_to_act + return self.energy.value >= val +end + +--- Use some energy +function _M:useEnergy(val) + val = val or game.energy_to_act + self.energy.value = self.energy.value - val +end diff --git a/game/engine/generator/actor/Random.lua b/game/engine/generator/actor/Random.lua index f9d2e2b10751b71d940fae230afe2153632cbad6..9f11140c6a7c17f5c970f21cd620a2a316f36bea 100644 --- a/game/engine/generator/actor/Random.lua +++ b/game/engine/generator/actor/Random.lua @@ -41,7 +41,7 @@ function _M:generate() end end --- if self.level.level <= self.zone.max_level then return end + if self.level.level <= self.zone.max_level then return end if self.guardian then local m = self.zone:makeEntityByName(self.level, "actor", self.guardian) diff --git a/game/engine/interface/ActorAI.lua b/game/engine/interface/ActorAI.lua index bfcea37f31dc417ddd385f52f5d86a5d491f1975..61bdc4eb121af210095cce7b89c94e42cbeb5aae 100644 --- a/game/engine/interface/ActorAI.lua +++ b/game/engine/interface/ActorAI.lua @@ -98,3 +98,8 @@ end function _M:getTarget(typ) return self.ai_target.actor.x, self.ai_target.actor.y, self.ai_target.actor end + +--- Sets the current target +function _M:setTarget(target) + self.ai_target.actor = target +end diff --git a/game/engine/interface/ObjectActivable.lua b/game/engine/interface/ObjectActivable.lua new file mode 100644 index 0000000000000000000000000000000000000000..855bbe9816e0219d78f4a51f03b6dcd6098960ca --- /dev/null +++ b/game/engine/interface/ObjectActivable.lua @@ -0,0 +1,44 @@ +require "engine.class" + +--- Handles activable objects, much more simple than actor's resource +module(..., package.seeall, class.make) + +function _M:init(t) + if t.max_power then + self.power = t.max_power + self.max_power = t.max_power + self.power_regen = t.power_regen or 0 + end +end + +--- Regen resources, shout be called in your actor's act() method +function _M:regenPower() + self.power = util.bound(self.power + self.power_regen, 0, self.max_power) +end + +function _M:canUse() + if self.use_simple or self.use_power then + return true + end +end + +function _M:getUseDesc() + if self.use_power then + return ("It can be used to %s, costing %d power out of %d/%d."):format(self.use_power.name, self.use_power.power, self.power, self.max_power) + elseif self.use_simple then + return ("It can be used to %s."):format(self.use_simple.name) + end +end + +function _M:useObject(who) + if self.use_power then + if self.power >= self.use_power.power then + local ret, no_power = self.use_power.use(self, who) + if not no_power then self.power = self.power - self.use_power.power end + return ret + end + elseif self.use_simple then + local ret = self.use_simple.use(self, who) + return ret + end +end diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index eeb97b2882b8bac3e15c489461ee05abb5f1f41f..8a869acda22561cddee635b7b71aed32240dc3d8 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -190,11 +190,6 @@ function _M:attack(target) self:bumpInto(target) end ---- Tries to get a target from the user -function _M:getTarget() - return self.target.x, self.target.y -end - --- Called before a talent is used -- Check the actor can cast it -- @param ab the talent (not the id, the table) diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 585b27f3f62b98f0a1b27d4f4af4786fc43d9523..86357131cebce27a502d4d28cc67de24b991938b 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -93,7 +93,7 @@ function _M:loaded() end function _M:save() - return class.save(self, {w=true, h=true, zone=true, player=true, level=true, + return class.save(self, {w=true, h=true, zone=true, player=true, level=true, entities=true, energy_to_act=true, energy_per_tick=true, turn=true, paused=true, save_name=true, }, true) end @@ -345,6 +345,18 @@ function _M:setupCommands() end) end, + -- Use item + _u = function() + self.player:showInventory(nil, self.player:getInven(self.player.INVEN_INVEN), nil, function(o, item) + local ret = o:use(self.player) + if ret and ret == "destroy" then + self.player:removeObject(self.player:getInven(self.player.INVEN_INVEN), item) + game.log("You have no more "..o:getName()) + end + end) + end, + + [{"_g","shift"}] = function() local none = true if self.player.unused_stats > 0 then diff --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua index fa16ba7c7801199ab4dc1dd6b9fd95bd6321f99c..09d9e74568c0d0192ea67b9b13f4e826ad6c6486 100644 --- a/game/modules/tome/class/Object.lua +++ b/game/modules/tome/class/Object.lua @@ -1,14 +1,45 @@ require "engine.class" require "engine.Object" +require "engine.interface.ObjectActivable" local Stats = require("engine.interface.ActorStats") -module(..., package.seeall, class.inherit(engine.Object)) +module(..., package.seeall, class.inherit(engine.Object, engine.interface.ObjectActivable)) function _M:init(t, no_default) engine.Object.init(self, t, no_default) + engine.interface.ObjectActivable.init(self, t) end +--- Can this object act at all +-- Most object will want to anwser false, only recharging and stuff needs them +function _M:canAct() + if self.power_regen then return true end + return false +end + +--- Do something when its your turn +-- For objects this mostly is to recharge them +-- By default, does nothing at all +function _M:act() + self:regenPower() + self:useEnergy() +end + +--- Use the object (quaff, read, ...) +function _M:use(who, typ) + local types = {} + if self:canUse() then types[#types+1] = "use" end + + if not typ and #types == 1 then typ = types[1] end + + if typ == "use" then + who:useEnergy() + return self:useObject(who) + end +end + +--- Returns a tooltip for the object function _M:tooltip() return self:getDesc() end @@ -46,5 +77,8 @@ function _M:getDesc() if w.lite then desc[#desc+1] = ("Light radius %d"):format(w.lite) end + local use_desc = self:getUseDesc() + if use_desc then desc[#desc+1] = use_desc end + return table.concat(desc, "\n") end diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index e0750caddb319c8201979cd7ff700668c48bd8eb..501934a713cb8b9a12e5897913238a23470cd48a 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -117,6 +117,13 @@ function _M:getTarget(typ) return game.target.target.x, game.target.target.y, game.level.map(game.target.target.x, game.target.target.y, Map.ACTOR) end +--- Sets the current target +function _M:setTarget(target) + game.target.target.entity = target + game.target.target.x = target.x + game.target.target.y = target.y +end + --- Quick way to check if the player can see the target function _M:canSee(entity) if entity.x and entity.y and game.level.map.seens(entity.x, entity.y) then return true end diff --git a/game/modules/tome/data/general/npcs/skeleton.lua b/game/modules/tome/data/general/npcs/skeleton.lua index 60f2d3e8c395b3076824a788c498509a9ad05588..30aede7a9f39072968ab1e4c07a450a2c4552222 100644 --- a/game/modules/tome/data/general/npcs/skeleton.lua +++ b/game/modules/tome/data/general/npcs/skeleton.lua @@ -9,7 +9,7 @@ newEntity{ body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 }, equipment = resolvers.equip{ {type="weapon", subtype="longsword"} }, - drops = resolvers.drops{chance=20, nb=1, {} }, + drops = resolvers.drops{chance=200, nb=10, {} }, autolevel = "warrior", ai = "dumb_talented_simple", ai_state = { talent_in=3, }, diff --git a/game/modules/tome/data/general/objects/objects.lua b/game/modules/tome/data/general/objects/objects.lua index 15187c6776560bf70f83a2e2bd8f1ac299f6f61e..b853cad39986e3860ad28db0e8e850a22d9b8af7 100644 --- a/game/modules/tome/data/general/objects/objects.lua +++ b/game/modules/tome/data/general/objects/objects.lua @@ -1,3 +1,4 @@ +load("/data/general/objects/potions.lua") load("/data/general/objects/staves.lua") load("/data/general/objects/swords.lua") load("/data/general/objects/shields.lua") diff --git a/game/modules/tome/data/general/objects/potions.lua b/game/modules/tome/data/general/objects/potions.lua new file mode 100644 index 0000000000000000000000000000000000000000..e04960379a43baff7c074b74507a4f5686822110 --- /dev/null +++ b/game/modules/tome/data/general/objects/potions.lua @@ -0,0 +1,117 @@ +newEntity{ + define_as = "BASE_POTION", + type = "potion", subtype="potion", + display = "!", color=colors.WHITE, + encumber = 0.2, + desc = [[Magical potions can have wildly different effects, from healing to killing you, beware! Most of them function better with a high Magic score]], +} + +------------------------------------------------------- +-- Healing & Curing +------------------------------------------------------- +newEntity{ base = "BASE_POTION", + name = "potion of lesser healing", + color = colors.LIGHT_RED, + level_range = {1, 20}, + rarity = 3, + + use_simple = { name="heal some life", use = function(self, who) + who:heal(40 + who:getMag()) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} + +newEntity{ base = "BASE_POTION", + name = "potion of healing", + color = colors.LIGHT_RED, + level_range = {15, 35}, + rarity = 4, + + use_simple = { name="heal a good part of your life", use = function(self, who) + who:heal(140 + who:getMag()) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} + +newEntity{ base = "BASE_POTION", + name = "potion of greater healing", + color = colors.LIGHT_RED, + level_range = {30, 50}, + rarity = 5, + + use_simple = { name="heal a lot", use = function(self, who) + who:heal(240 + who:getMag()) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} + +newEntity{ base = "BASE_POTION", + name = "potion of full healing", + color = colors.LIGHT_RED, + level_range = {45, 50}, + rarity = 14, + + use_simple = { name="fully heal", use = function(self, who) + who:heal(1000000) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} + +------------------------------------------------------- +-- Mana +------------------------------------------------------- +newEntity{ base = "BASE_POTION", + name = "potion of lesser mana", + color = colors.LIGHT_BLUE, + level_range = {1, 20}, + rarity = 3, + + use_simple = { name="restore some mana", use = function(self, who) + who:incMana(40 + who:getMag()) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} + +newEntity{ base = "BASE_POTION", + name = "potion of mana", + color = colors.LIGHT_BLUE, + level_range = {15, 35}, + rarity = 4, + + use_simple = { name="restore a good part of your mana", use = function(self, who) + who:incMana(140 + who:getMag()) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} + +newEntity{ base = "BASE_POTION", + name = "potion of greater mana", + color = colors.LIGHT_BLUE, + level_range = {30, 50}, + rarity = 5, + + use_simple = { name="restore a lot of mana", use = function(self, who) + who:incMana(240 + who:getMag()) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} + +newEntity{ base = "BASE_POTION", + name = "potion of full mana", + color = colors.LIGHT_BLUE, + level_range = {45, 50}, + rarity = 14, + + use_simple = { name="fully restore mana", use = function(self, who) + who:incMana(1000000) + game.logSeen(who, "%s quaffs a %s!", who.name:capitalize(), self:getName()) + return "destroy" + end} +} diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index 04c91407e812694f6a9bdda29234984fb0074309..5430eb8d0194d3d006dd50ec21b0e8514dcb1c65 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -34,7 +34,7 @@ newEntity{ desc = [[A small crystal phial, with the light of Earendil's Star contained inside. Its light is imperishable, and near it darkness cannot endure.]], wielder = { - lite = 3, + lite = 4, }, } @@ -63,12 +63,33 @@ newEntity{ level_range = {1, 10}, display = '"', color=colors.VIOLET, encumber = 1, - rarity = 100, + rarity = 1, desc = [[A crystal clear stone hangs on the chain. It displays images of your surroundings, but somehow they seem closer.]], - use = function(self, o) - game.logSeen(self, "%s uses the Shifting Amulet and blinks away!", self.name:capitalize()) - self:teleportRandom(self.x, self.y, 10) - self:useEnergy() - end, + max_power = 15, power_regen = 1, + use_power = { name = "blink away", power = 5, + use = function(self, who) + game.logSeen(who, "%s uses the Shifting Amulet and blinks away!", who.name:capitalize()) + who:teleportRandom(who.x, who.y, 10) + end + }, +} + +newEntity{ + unique = true, + type = "potion", subtype="potion", + name = "Ever Refilling Potion of Healing", + level_range = {35, 40}, + display = '!', color=colors.VIOLET, + encumber = 0.4, + rarity = 80, + desc = [[Bottle containing healing magic. But the more you drink from it, the more it refills!]], + + max_power = 40, power_regen = 1, + use_power = { name = "blink away", power = 30, + use = function(self, who) + who:heal(150 + who:getMag()) + game.logSeen("%s quaffs an %s!", who.name:capitalize(), self:getName()) + end + }, } diff --git a/ideas/spells.ods b/ideas/spells.ods index 991558f5e8c5886bcdc9daa6f3d6876aa016f51e..16ab542ab4a4cb524afc045aaad8154bc87433f2 100644 Binary files a/ideas/spells.ods and b/ideas/spells.ods differ