diff --git a/game/engine/Map.lua b/game/engine/Map.lua index f28e3713418c99a668ab0291f0bbc56bf6856d78..c8207b806a02ab9343a506e67e1b2ec11c34fb1e 100644 --- a/game/engine/Map.lua +++ b/game/engine/Map.lua @@ -89,9 +89,6 @@ end --- Serialization function _M:save() return class.save(self, { - _fov_esp = true, - _fov_lite = true, - _fov = true, _map = true, surface = true, particle = true, @@ -139,9 +136,6 @@ function _M:loaded() setmetatable(self.remembers, {__call = mapremember}) self.surface = core.display.newSurface(self.viewport.width, self.viewport.height) - self._fov = core.fov.new(_M.opaque, _M.apply, self) - self._fov_lite = core.fov.new(_M.opaque, _M.applyLite, self) - self._fov_esp = core.fov.new(_M.opaqueESP, _M.applyESP, self) self.changed = true self.finished = true @@ -170,51 +164,14 @@ end -- to the map. Cyclic references! BAD BAD BAD !<br/> -- The closing should be handled automatically by the Zone class so no bother for authors function _M:close() - self._fov = false - self._fov_lite = false - self._fov_esp = false -end - ---- Runs the FOV algorithm on the map --- @param x source point of the ligth --- @param y source point of the ligth --- @param d radius of the light -function _M:fov(x, y, d) - -- Reset seen grids - if self.clean_fov then - self.clean_fov = false - for i = 0, self.w * self.h - 1 do self.seens[i] = nil end - self._map:cleanSeen(); - end - self._fov(x, y, d) -end - ---- Runs the FOV algorithm on the map, ligthing grids to allow rememberance --- @param x source point of the ligth --- @param y source point of the ligth --- @param d radius of the light -function _M:fovLite(x, y, d) - -- Reset seen grids - if self.clean_fov then - self.clean_fov = false - for i = 0, self.w * self.h - 1 do self.seens[i] = nil end - self._map:cleanSeen(); - end - self._fov_lite(x, y, d) -end - ---- Runs the FOV algorithm on the map, finding ESP(Extra Sensorial Power) targets --- @param x source point of the ligth --- @param y source point of the ligth --- @param d radius of the light -function _M:fovESP(x, y, d) - -- Reset seen grids - if self.clean_fov then - self.clean_fov = false - for i = 0, self.w * self.h - 1 do self.seens[i] = nil end - self._map:cleanSeen(); - end - self._fov_esp(x, y, d) +end + +--- Cleans the FOV infos (seens table) +function _M:cleanFOV() + if not self.clean_fov then return end + self.clean_fov = false + for i = 0, self.w * self.h - 1 do self.seens[i] = nil end + self._map:cleanSeen() end function _M:updateMap(x, y) diff --git a/game/engine/Tooltip.lua b/game/engine/Tooltip.lua index 82ac0dbc42f97914c615b56708094f813f8dd3e8..5fefca31886a07afb9e2e4861ca94a82d350f6d4 100644 --- a/game/engine/Tooltip.lua +++ b/game/engine/Tooltip.lua @@ -1,5 +1,7 @@ require "engine.class" +local Map = require "engine.Map" +--- Displays a tooltip module(..., package.seeall, class.make) tiles = engine.Tiles.new(16, 16) @@ -13,6 +15,7 @@ function _M:init(fontname, fontsize, color, bgcolor) self.changed = true end +--- Set the tooltip text function _M:set(str, ...) self.text = str:format(...):splitLines(300, self.font) self.w, self.h = 0, 0 @@ -54,3 +57,28 @@ function _M:display() end return self.surface end + +--- Displays the tooltip at the given map coordinates +-- @param tmx the map coordinate to get tooltip from +-- @param tmy the map coordinate to get tooltip from +-- @param mx the screen coordinate to display at, if nil it will be computed from tmx +-- @param my the screen coordinate to display at, if nil it will be computed from tmy +function _M:displayAtMap(tmx, tmy, mx, my) + if not mx then + mx, my = game.level.map:getTileToScreen(tmx, tmy) + end + + local tt = game.level.map:checkEntity(tmx, tmy, Map.ACTOR, "tooltip") or + game.level.map:checkEntity(tmx, tmy, Map.OBJECT, "tooltip") or + game.level.map:checkEntity(tmx, tmy, Map.TRAP, "tooltip") or + game.level.map:checkEntity(tmx, tmy, Map.TERRAIN, "tooltip") + if tt and game.level.map.seens(tmx, tmy) then + self:set("%s", tt) + local t = self:display() + mx = mx - self.w + my = my - self.h + if mx < 0 then mx = 0 end + if my < 0 then my = 0 end + if t then t:toScreen(mx, my) end + end +end diff --git a/game/engine/interface/ActorFOV.lua b/game/engine/interface/ActorFOV.lua index 9547c40cd0c208ad6bdaad780a2aec8b4ff05ca8..307c080826b653495b99855898417b32bb026caf 100644 --- a/game/engine/interface/ActorFOV.lua +++ b/game/engine/interface/ActorFOV.lua @@ -19,42 +19,58 @@ end --- Computes actor's FOV -- @param radius the FOV radius, defaults to 20 -- @param block the property to look for FOV blocking, defaults to "block_sight" +-- @param apply an apply function that will be called on each seen grids, defaults to nil -- @param force set to true to force a regeneration even if we did not move -function _M:computeFOV(radius, block, force) +-- @param no_store do not store FOV informations +function _M:computeFOV(radius, block, apply, force, no_store) -- If we did not move, do not update if not force and self.fov_last_x == self.x and self.fov_last_y == self.y and self.fov_computed then return end radius = radius or 20 block = block or "block_sight" - local fov = {actors={}, actors_dist={}} - setmetatable(fov.actors, {__mode='k'}) - setmetatable(fov.actors_dist, {__mode='v'}) + -- Simple FOV compute no storage + if no_store and apply then + local map = game.level.map + core.fov.calc_circle(self.x, self.y, radius, function(_, x, y) + if map:checkAllEntities(x, y, block, self) then return true end + end, function(_, x, y, dx, dy) + apply(x, y, dx, dy) + end, self) - local map = game.level.map - core.fov.calc_circle(self.x, self.y, radius, function(_, x, y) - if map:checkAllEntities(x, y, block, self) then return true end - end, function(_, x, y, dx, dy) - -- Note actors - local a = map(x, y, Map.ACTOR) - if a and a ~= self and not a.dead then - local t = {x=x,y=y, dx=dx, dy=dy, sqdist=dx*dx+dy*dy} - fov.actors[a] = t - fov.actors_dist[#fov.actors_dist+1] = a - a:updateFOV(self, t.sqdist) - end - end, self) + -- FOV + storage + elseif not no_store then + local fov = {actors={}, actors_dist={}} + setmetatable(fov.actors, {__mode='k'}) + setmetatable(fov.actors_dist, {__mode='v'}) - -- Sort actors by distance (squared but we do not care) - table.sort(fov.actors_dist, function(a, b) return fov.actors[a].sqdist < fov.actors[b].sqdist end) - for i = 1, #fov.actors_dist do fov.actors_dist[i].i = i end --- print("Computed FOV for", self.uid, self.name, ":: seen ", #fov.actors_dist, "actors closeby") + local map = game.level.map + core.fov.calc_circle(self.x, self.y, radius, function(_, x, y) + if map:checkAllEntities(x, y, block, self) then return true end + end, function(_, x, y, dx, dy) + if apply then apply(x, y, dx, dy) end - self.fov = fov - self.fov_last_x = self.x - self.fov_last_y = self.y - self.fov_last_turn = game.turn - self.fov_last_change = game.turn - self.fov_computed = true + -- Note actors + local a = map(x, y, Map.ACTOR) + if a and a ~= self and not a.dead then + local t = {x=x,y=y, dx=dx, dy=dy, sqdist=dx*dx+dy*dy} + fov.actors[a] = t + fov.actors_dist[#fov.actors_dist+1] = a + a:updateFOV(self, t.sqdist) + end + end, self) + + -- Sort actors by distance (squared but we do not care) + table.sort(fov.actors_dist, function(a, b) return fov.actors[a].sqdist < fov.actors[b].sqdist end) + for i = 1, #fov.actors_dist do fov.actors_dist[i].i = i end +-- print("Computed FOV for", self.uid, self.name, ":: seen ", #fov.actors_dist, "actors closeby") + + self.fov = fov + self.fov_last_x = self.x + self.fov_last_y = self.y + self.fov_last_turn = game.turn + self.fov_last_change = game.turn + self.fov_computed = true + end end --- Update our fov to include the given actor at the given dist diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 463d04adadaab84ff084ac0277445307dd9f7ab4..9aa46860247e79e112db5b5ab3437d60cd411a9b 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -10,6 +10,7 @@ require "engine.interface.ActorTalents" require "engine.interface.ActorResource" require "engine.interface.ActorQuest" require "engine.interface.BloodyDeath" +require "engine.interface.ActorFOV" require "mod.class.interface.Combat" local Map = require "engine.Map" @@ -25,6 +26,7 @@ module(..., package.seeall, class.inherit( engine.interface.ActorResource, engine.interface.ActorQuest, engine.interface.BloodyDeath, + engine.interface.ActorFOV, mod.class.interface.Combat )) @@ -89,6 +91,7 @@ function _M:init(t, no_default) engine.interface.ActorTalents.init(self, t) engine.interface.ActorResource.init(self, t) engine.interface.ActorLevel.init(self, t) + engine.interface.ActorFOV.init(self, t) end function _M:act() diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index d8e15a8c33a77813edbbba046d9a107114e8a7e6..3ec6a511a08c2fd5ffd24dec0c3fb3ed04d20f3a 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -245,31 +245,32 @@ function _M:display() if self.level and self.level.map and self.level.map.finished then -- Display the map and compute FOV for the player if needed if self.level.map.changed then - self.level.map:fovESP(self.player.x, self.player.y, self.player.esp.range or 10) - self.level.map:fov(self.player.x, self.player.y, 20) - if self.player.lite > 0 then self.level.map:fovLite(self.player.x, self.player.y, self.player.lite) end + -- Compute FOV, if needed + self.level.map:cleanFOV() + self.player:computeFOV(self.player.esp.range or 10, "block_esp", function(x, y) self.level.map:applyESP(x, y) end, true, true) + self.player:computeFOV(self.player.sight or 20, "block_sight", function(x, y) self.level.map:apply(x, y) end, true) + if self.player.lite > 0 then self.player:computeFOV(self.player.life, "block_sight", function(x, y) self.level.map:applyLite(x, y) end, true, true) end -- -- Handle Sense spell -- if self.player:attr("detect_range") then - core.fov.calc_circle(self.player.x, self.player.y, self.player:attr("detect_range"), function(map, lx, ly) - if game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_sense") then return true end - end, function(map, lx, ly) + self.player:computeFOV(self.player:attr("detect_range"), "block_sense", function(x, y) local ok = false - if game.player:attr("detect_actor") and game.level.map(lx, ly, game.level.map.ACTOR) then ok = true end - if game.player:attr("detect_object") and game.level.map(lx, ly, game.level.map.OBJECT) then ok = true end - if game.player:attr("detect_trap") and game.level.map(lx, ly, game.level.map.TRAP) then - game.level.map(lx, ly, game.level.map.TRAP):setKnown(self.player, true) + if self.player:attr("detect_actor") and self.level.map(x, y, self.level.map.ACTOR) then ok = true end + if self.player:attr("detect_object") and self.level.map(x, y, self.level.map.OBJECT) then ok = true end + if self.player:attr("detect_trap") and self.level.map(x, y, self.level.map.TRAP) then + self.level.map(x, y, self.level.map.TRAP):setKnown(self.player, true) ok = true end if ok then - game.level.map.seens(lx, ly, true) + self.level.map.seens(x, y, true) end - end, self) + end, true, true) end end + self.level.map:display() -- Display the targetting system if active @@ -278,19 +279,9 @@ function _M:display() -- Display a tooltip if available local mx, my = core.mouse.get() local tmx, tmy = self.level.map:getMouseTile(mx, my) - local tt = self.level.map:checkEntity(tmx, tmy, Map.ACTOR, "tooltip") or - self.level.map:checkEntity(tmx, tmy, Map.OBJECT, "tooltip") or - self.level.map:checkEntity(tmx, tmy, Map.TRAP, "tooltip") or - self.level.map:checkEntity(tmx, tmy, Map.TERRAIN, "tooltip") - if tt and self.level.map.seens(tmx, tmy) then - self.tooltip:set("%s", tt) - local t = self.tooltip:display() - mx = mx - self.tooltip.w - my = my - self.tooltip.h - if mx < 0 then mx = 0 end - if my < 0 then my = 0 end - if t then t:toScreen(mx, my) end - end + self.tooltip:displayAtMap(tmx, tmy, mx, my) + + -- Move target around if self.old_tmx ~= tmx or self.old_tmy ~= tmy then self.target.target.x, self.target.target.y = tmx, tmy end diff --git a/game/modules/tome/class/NPC.lua b/game/modules/tome/class/NPC.lua index 84c6118a4ee9a33c58707f8f42b9c4019dcdc11e..739e4336b5be13c3676f2f037a853eeeaa7d0e79 100644 --- a/game/modules/tome/class/NPC.lua +++ b/game/modules/tome/class/NPC.lua @@ -1,27 +1,25 @@ require "engine.class" local ActorAI = require "engine.interface.ActorAI" -local ActorFOV = require "engine.interface.ActorFOV" require "mod.class.Actor" -module(..., package.seeall, class.inherit(mod.class.Actor, engine.interface.ActorAI, engine.interface.ActorFOV)) +module(..., package.seeall, class.inherit(mod.class.Actor, engine.interface.ActorAI)) function _M:init(t, no_default) mod.class.Actor.init(self, t, no_default) ActorAI.init(self, t) - ActorFOV.init(self, t) end function _M:act() -- Do basic actor stuff if not mod.class.Actor.act(self) then return end + -- Compute FOV, if needed + self:computeFOV(self.sight or 20) + -- Let the AI think .... beware of Shub ! -- If AI did nothing, use energy anyway self:doAI() if not self.energy.used then self:useEnergy() end - - -- Compute FOV, if needed - self:computeFOV(self.sight or 20) end --- Called by ActorLife interface diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index c6639a8753bcea3eba7f7f9203592c3c82a7d95e..6adcbfb0a0df6ae148c48f09f3066eae2d480b63 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -4,7 +4,6 @@ require "engine.interface.PlayerRest" require "engine.interface.PlayerRun" require "engine.interface.PlayerHotkeys" require "engine.interface.PlayerSlide" -local ActorFOV = require "engine.interface.ActorFOV" local Map = require "engine.Map" local Dialog = require "engine.Dialog" local ActorTalents = require "engine.interface.ActorTalents" @@ -20,8 +19,7 @@ module(..., package.seeall, class.inherit( engine.interface.PlayerRest, engine.interface.PlayerRun, engine.interface.PlayerHotkeys, - engine.interface.PlayerSlide, - engine.interface.ActorFOV + engine.interface.PlayerSlide )) function _M:init(t, no_default) @@ -40,7 +38,6 @@ function _M:init(t, no_default) } mod.class.Actor.init(self, t, no_default) engine.interface.PlayerHotkeys.init(self, t) - ActorFOV.init(self, t) self.player = true self.type = "humanoid" self.subtype = "player" @@ -92,9 +89,6 @@ end function _M:act() if not mod.class.Actor.act(self) then return end - -- Compute FOV, if needed - self:computeFOV(self.sight or 20) - -- Clean log flasher game.flash:empty()