diff --git a/game/engine/Actor.lua b/game/engine/Actor.lua index 5cb5fde38f05ddd99f7e328e885f9cb1bf288167..245801b2bf7fd7689afa5cd8fba95dff8ae74be9 100644 --- a/game/engine/Actor.lua +++ b/game/engine/Actor.lua @@ -62,6 +62,13 @@ function _M:move(x, y, force) return true end +--- Moves into the given direction (calls actor:move() internally) +function _M:moveDir(dir) + local dx, dy = dir_to_coord[dir][1], dir_to_coord[dir][2] + local x, y = self.x + dx, self.y + dy + return self:move(x, y) +end + --- Can the actor go there -- @param terrain_only if true checks only the terrain, otherwise checks all entities function _M:canMove(x, y, terrain_only) diff --git a/game/engine/interface/PlayerRest.lua b/game/engine/interface/PlayerRest.lua index 774828cbbfb39f0a3fedf361e51cf5db45c8b708..62223f2ecd5d6086015dca7d5a9032b688e7c711 100644 --- a/game/engine/interface/PlayerRest.lua +++ b/game/engine/interface/PlayerRest.lua @@ -40,7 +40,7 @@ end -- Rewrite this method to check for mana, life, whatever. By default we alawys return false so resting will never work -- @return true if we can continue to rest, false otherwise function _M:restCheck() - return false + return false, "player:restCheck() method not defined" end --- Stops resting diff --git a/game/engine/interface/PlayerRun.lua b/game/engine/interface/PlayerRun.lua new file mode 100644 index 0000000000000000000000000000000000000000..fc7d8ec7b205f15b2b072d70351710c2dbfda479 --- /dev/null +++ b/game/engine/interface/PlayerRun.lua @@ -0,0 +1,160 @@ +require "engine.class" +local Dialog = require "engine.Dialog" + +--- Handles player running +-- This should work for running inside tunnel, alongside walls, in open spaces.<br/> +module(..., package.seeall, class.make) + +local sides = +{ + [1] = {left=2, right=4}, + [2] = {left=3, right=1}, + [3] = {left=6, right=2}, + [4] = {left=1, right=7}, + [6] = {left=9, right=3}, + [7] = {left=4, right=8}, + [8] = {left=7, right=9}, + [9] = {left=8, right=6}, +} +local turn = +{ + [1] = {left=3, right=7}, + [2] = {left=6, right=4}, + [3] = {left=9, right=1}, + [4] = {left=2, right=8}, + [6] = {left=8, right=2}, + [7] = {left=1, right=9}, + [8] = {left=4, right=6}, + [9] = {left=7, right=3}, +} + +local function checkDir(a, dir, dist) + dist = dist or 1 + local dx, dy = dir_to_coord[dir][1], dir_to_coord[dir][2] + local x, y = a.x + dx * dist, a.y + dy * dist + return game.level.map:checkAllEntities(x, y, "block_move", a) and true or false +end + +--- Initializes running +-- We check the direction sides to know if we are in a tunnel, along a wall or in open space. +function _M:runInit(dir) + local block_left, block_right = false, false + + -- Check sides + if checkDir(self, sides[dir].left) then block_left = true end + if checkDir(self, sides[dir].right) then block_right = true end + + self.running = { + dir = dir, + block_left = block_left, + block_right = block_right, + cnt = 1, +-- dialog = Dialog:simplePopup("Resting...", "You are running, press any key to stop.", function() +-- self:runStop() +-- end), + } + + print("run", dir, block_left, block_right) + + self:runStep() +end + +--- Run a turn +-- For a turn based game you want in you player's act() something like that:<br/> +-- <pre> +-- if not self:runStep() then game.paused = true end +-- </pre><br/> +-- This will move the actor using the :move() method, this SHOULD have been redefined by the module +-- to use energy, otherwise running will be free. +-- @return true if we can continue to run, false otherwise +function _M:runStep() + if not self.running then return false end + + local ret, msg = self:runCheck() + if not ret then + self:runStop(msg) + return false + else + self:moveDir(self.running.dir) + self.running.cnt = self.running.cnt + 1 + if self.running.newdir then + self.running.dir = self.running.newdir + self.running.newdir = nil + end + if self.running.ignore_left then + self.running.ignore_left = self.running.ignore_left - 1 + if self.running.ignore_left <= 0 then self.running.ignore_left = nil end + end + if self.running.ignore_right then + self.running.ignore_right = self.running.ignore_right - 1 + if self.running.ignore_right <= 0 then self.running.ignore_right = nil end + end + + return true + end +end + +--- Can we continue running ? +-- Rewrite this method to hostiles, interresting terrain, whatever. +-- This method should be called by its submethod, it tries to detect changes in the terrain.<br/> +-- It will also try to follow tunnels when they simply change direction. +-- @return true if we can continue to run, false otherwise +function _M:runCheck() + -- Do we change run direction ? We can only choose to change for left or right, never backwards. + -- We must also be in a tunnel (both sides blocked) + if self.running.block_left and self.running.block_right then + -- Turn left + if not checkDir(self, self.running.dir) and checkDir(self, self.running.dir, 2) and not checkDir(self, sides[self.running.dir].left) and checkDir(self, sides[self.running.dir].right) then + self.running.newdir = turn[self.running.dir].left + self.running.ignore_left = 2 + print("will turn left") + return true + end + + -- Turn right + if not checkDir(self, self.running.dir) and checkDir(self, self.running.dir, 2) and checkDir(self, sides[self.running.dir].left) and not checkDir(self, sides[self.running.dir].right) then + self.running.newdir = turn[self.running.dir].right + self.running.ignore_right = 2 + print("will turn right") + return true + end + end + + if not self.running.ignore_left and self.running.block_left ~= checkDir(self, sides[self.running.dir].left) then return false, "terrain change on left side" end + if not self.running.ignore_right and self.running.block_right ~= checkDir(self, sides[self.running.dir].right) then return false, "terrain change on right side" end + if checkDir(self, self.running.dir) then return false, "terrain ahead blocks" end + + return true +end + +--- Stops running +function _M:runStop(msg) + if not self.running then return false end + +-- game:unregisterDialog(self.running.dialog) + + if msg then + game.log("Ran for %d turns (stop reason: %s).", self.running.cnt, msg) + end + + self.running = nil + return true +end + +--- Scan the run direction and sides with the given function +function _M:runScan(fct) + -- Ahead + local dx, dy = dir_to_coord[self.running.dir][1], dir_to_coord[self.running.dir][2] + local x, y = self.x + dx, self.y + dy + fct(x, y) + + -- Ahead left + local dx, dy = dir_to_coord[sides[self.running.dir].left][1], dir_to_coord[sides[self.running.dir].left][2] + local x, y = self.x + dx, self.y + dy + fct(x, y) + + -- Ahead right + local dx, dy = dir_to_coord[sides[self.running.dir].right][1], dir_to_coord[sides[self.running.dir].right][2] + local x, y = self.x + dx, self.y + dy + fct(x, y) +end diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 3fdc69a99a0eaf97f76dc22ba879905edaf4ae09..aca0896a164263eb13c99eb5fe17b672a7611b67 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -297,7 +297,7 @@ function _M:setupCommands() [{"_DOWN","shift"}] = function() self.target:freemove(2) end, [{"_KP4","shift"}] = function() self.target:freemove(4) end, [{"_KP6","shift"}] = function() self.target:freemove(6) end, - [{"_KP8","shift"}] = function() self.target:freemove(7) end, + [{"_KP8","shift"}] = function() self.target:freemove(8) end, [{"_KP2","shift"}] = function() self.target:freemove(2) end, [{"_KP1","shift"}] = function() self.target:freemove(1) end, [{"_KP3","shift"}] = function() self.target:freemove(3) end, @@ -338,6 +338,20 @@ function _M:setupCommands() _RIGHTPAREN = function() self.player:activateHotkey(11) end, _EQUALS = function() self.player:activateHotkey(12) end, + -- running + [{"_LEFT","shift"}] = function() self.player:runInit(4) end, + [{"_RIGHT","shift"}] = function() self.player:runInit(6) end, + [{"_UP","shift"}] = function() self.player:runInit(8) end, + [{"_DOWN","shift"}] = function() self.player:runInit(2) end, + [{"_KP4","shift"}] = function() self.player:runInit(4) end, + [{"_KP6","shift"}] = function() self.player:runInit(6) end, + [{"_KP8","shift"}] = function() self.player:runInit(8) end, + [{"_KP2","shift"}] = function() self.player:runInit(2) end, + [{"_KP1","shift"}] = function() self.player:runInit(1) end, + [{"_KP3","shift"}] = function() self.player:runInit(3) end, + [{"_KP7","shift"}] = function() self.player:runInit(7) end, + [{"_KP9","shift"}] = function() self.player:runInit(9) end, + -- resting [{"_r","shift"}] = function() self.player:restInit() diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index 418a56d17b7f9480d081425952522bfdaea4471b..4ad776ede7828d04611f79e73fb3a27f068f2041 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -1,12 +1,20 @@ require "engine.class" require "mod.class.Actor" require "engine.interface.PlayerRest" +require "engine.interface.PlayerRun" local Savefile = require "engine.Savefile" local Map = require "engine.Map" local Dialog = require "engine.Dialog" local ActorTalents = require "engine.interface.ActorTalents" -module(..., package.seeall, class.inherit(mod.class.Actor, engine.interface.PlayerRest)) +--- Defines the player for ToME +-- It is a normal actor, with some redefined methods to handle user interaction.<br/> +-- It is also able to run and rest. +module(..., package.seeall, class.inherit( + mod.class.Actor, + engine.interface.PlayerRest, + engine.interface.PlayerRun +)) function _M:init(t, no_default) t.body = { @@ -74,8 +82,8 @@ function _M:act() -- Clean log flasher game.flash:empty() - -- Resting ? - if not self:restStep() then + -- Resting ? Running ? Otherwise pause + if not self:restStep() and not self:runStep() then game.paused = true end end @@ -148,16 +156,20 @@ function _M:activateHotkey(id) end end ---- Can we continue resting ? --- We can rest if no hostiles are in sight, and if we need life/mana/stamina (and their regen rates allows them to fully regen) -function _M:restCheck() +local function spotHostiles(self) local seen = false -- Check for visible monsters, only see LOS actors, so telepathy wont prevent resting core.fov.calc_circle(self.x, self.y, 20, game.level.map.opaque, function(map, x, y) local actor = map(x, y, map.ACTOR) if actor and self:reactionToward(actor) < 0 and self:canSee(actor) then seen = true end end, game.level.map) - if seen then return false, "hostile spotted" end + return seen +end + +--- Can we continue resting ? +-- We can rest if no hostiles are in sight, and if we need life/mana/stamina (and their regen rates allows them to fully regen) +function _M:restCheck() + if spotHostiles(self) then return false, "hostile spotted" end -- Check ressources, make sure they CAN go up, otherwise we will never stop if self:getMana() < self:getMaxMana() and self.mana_regen > 0 then return true end @@ -166,3 +178,24 @@ function _M:restCheck() return false, "all resources and life at maximun" end + +--- Can we continue running? +-- We can run if no hostiles are in sight, and if we no interresting terrains are next to us +function _M:runCheck() + if spotHostiles(self) then return false, "hostile spotted" end + + -- Notice any noticable terrain + local noticed = false + self:runScan(function(x, y) + -- Only notice interresting terrains + local grid = game.level.map(x, y, Map.TERRAIN) + if grid and grid.notice then noticed = "interresting terrain" end + + -- Objects are always interresting + local obj = game.level.map:getObject(x, y, 1) + if obj then noticed = "object seen" end + end) + if noticed then return false, noticed end + + return engine.interface.PlayerRun.runCheck(self) +end diff --git a/game/modules/tome/data/general/grids/basic.lua b/game/modules/tome/data/general/grids/basic.lua index 9985c1c2e5515e8367d34deb4de75a444c625051..c03b3e3bad035064a6c9df2f3133cfcb2e9b733d 100644 --- a/game/modules/tome/data/general/grids/basic.lua +++ b/game/modules/tome/data/general/grids/basic.lua @@ -3,6 +3,7 @@ newEntity{ name = "exit to the wilds", display = '<', color_r=255, color_g=0, color_b=255, always_remember = true, + notice = true, change_level = 1, change_zone = "wilderness", } @@ -11,6 +12,7 @@ newEntity{ define_as = "UP", name = "previous level", display = '<', color_r=255, color_g=255, color_b=0, + notice = true, always_remember = true, change_level = -1, } @@ -19,6 +21,7 @@ newEntity{ define_as = "DOWN", name = "next level", display = '>', color_r=255, color_g=255, color_b=0, + notice = true, always_remember = true, change_level = 1, } @@ -43,6 +46,7 @@ newEntity{ define_as = "DOOR", name = "door", image = "terrain/granite_door1.png", display = '+', color_r=238, color_g=154, color_b=77, + notice = true, always_remember = true, block_sight = true, door_opened = "DOOR_OPEN", diff --git a/game/modules/tome/data/talents/physical/dualweapon.lua b/game/modules/tome/data/talents/physical/dualweapon.lua index f812a531c6ff6632f607d03c9da6d083c21ce793..1b0b85eb629a18fe4fce33ef6ed87fd91af56dc8 100644 --- a/game/modules/tome/data/talents/physical/dualweapon.lua +++ b/game/modules/tome/data/talents/physical/dualweapon.lua @@ -5,7 +5,7 @@ newTalent{ points = 5, require = { stat = { dex=14 } }, info = function(self, t) - return ([[Reduces the damage penality of the off-hand weapon to %d%%]]):format(100 / (2 - self:getTalentLevel(t) / 6)) + return ([[Increases the damage of the off-hand weapon to %d%%.]]):format(100 / (2 - self:getTalentLevel(t) / 6)) end, }