Skip to content
Snippets Groups Projects
Commit 1ef77503 authored by dg's avatar dg
Browse files

New engine interface PlayerMouse, this handles the default mouse actions, so...

New engine interface PlayerMouse, this handles the default mouse actions, so that modules can easily implent them. You are encouraged to switch to use it.
New engine interface GameTargeting, this handles all the complex targeting code for you. You are encouraged to switch to use it.
Updated both ToME and example module to the new interfaces


git-svn-id: http://svn.net-core.org/repos/t-engine4@958 51575b47-30f0-44d4-a5cc-537603b46e54
parent 4348edd5
No related branches found
No related tags found
No related merge requests found
-- TE4 - T-Engine 4
-- Copyright (C) 2009, 2010 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"
require "engine.KeyBind"
local Map = require "engine.Map"
local Target = require "engine.Target"
--- Handles default targeting interface & display
module(..., package.seeall, class.make)
--- Initializes targeting
function _M:init()
self.target = Target.new(Map, self.player)
self.target.target.entity = self.player
self.old_tmx, self.old_tmy = 0, 0
self.target_style = "lock"
end
--- Maintain the current target each tick
-- Make sure the target still exists
function _M:targetOnTick()
if self.target.target.entity and not self.level:hasEntity(self.target.target.entity) then self.target.target.entity = false end
end
--- Display the tooltip, if any
function _M:targetDisplayTooltip()
-- Tooltip is displayed over all else
if self.level and self.level.map and self.level.map.finished then
-- Display a tooltip if available
if self.tooltip_x then self.tooltip:displayAtMap(self.level.map:getMouseTile(self.tooltip_x , self.tooltip_y)) end
-- 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
self.old_tmx, self.old_tmy = tmx, tmy
end
end
--- Enter/leave targeting mode
-- This is the "meat" of this interface, do not expect to understand it easily, it mixes some nasty stuff
-- This require the Game to have both a "key" field (this is the default) and a "normal_key" field<br/>
-- It will switch over to a special keyhandler and then restore the "normal_key" one
function _M:targetMode(v, msg, co, typ)
local old = self.target_mode
self.target_mode = v
if not v then
Map:setViewerFaction(self.always_target and self.player.faction or nil)
if msg then self.log(type(msg) == "string" and msg or "Tactical display disabled. Press shift+'t' to enable.") end
self.level.map.changed = true
self.target:setActive(false)
if tostring(old) == "exclusive" then
local fct = function(ok)
if not ok then
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
end
self.key = self.normal_key
self.key:setCurrent()
if self.target_co then
local co = self.target_co
self.target_co = nil
local ok, err = coroutine.resume(co, self.target.target.x, self.target.target.y, self.target.target.entity)
if not ok and err then print(debug.traceback(co)) error(err) end
end
end
if self.target_warning and self.target.target.x == self.player.x and self.target.target.y == self.player.y then
Dialog:yesnoPopup("Target yourself?", "Are you sure you want to target yourself?", fct)
else
fct(true)
end
end
else
Map:setViewerFaction(self.player.faction)
if msg then self.log(type(msg) == "string" and msg or "Tactical display enabled. Press shift+'t' to disable.") end
self.level.map.changed = true
self.target:setActive(true, typ)
self.target_style = "lock"
self.target_warning = true
-- Exclusive mode means we disable the current key handler and use a specific one
-- that only allows targetting and resumes talent coroutine when done
if tostring(v) == "exclusive" then
self.target_co = co
self.key = self.targetmode_key
self.key:setCurrent()
if self.target.target.entity and self.level.map.seens(self.target.target.entity.x, self.target.target.entity.y) and self.player ~= self.target.target.entity then
else
local filter = nil
if type(typ) == "table" and typ.first_target and typ.first_target == "friend" then
filter = function(a) return self.player:reactionToward(a) >= 0 end
else
filter = function(a) return self.player:reactionToward(a) < 0 end
end
self.target:scan(5, nil, self.player.x, self.player.y, filter)
end
end
if self.target.target.x then
self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y)
end
end
end
--- This setups the default keybindings for targeting
function _M:targetSetupKey()
self.targetmode_key = engine.KeyBind.new()
self.targetmode_key:addCommands{ _SPACE=function() self:targetMode(false, false) self.tooltip_x, self.tooltip_y = nil, nil end, }
self.targetmode_key:addBinds
{
TACTICAL_DISPLAY = function()
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
ACCEPT = function()
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
EXIT = function()
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
-- Targeting movement
RUN_LEFT = function() self.target:freemove(4) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT = function() self.target:freemove(6) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_UP = function() self.target:freemove(8) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_DOWN = function() self.target:freemove(2) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_LEFT_DOWN = function() self.target:freemove(1) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT_DOWN = function() self.target:freemove(3) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_LEFT_UP = function() self.target:freemove(7) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT_UP = function() self.target:freemove(9) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT = function() if self.target_style == "lock" then self.target:scan(4) else self.target:freemove(4) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT = function() if self.target_style == "lock" then self.target:scan(6) else self.target:freemove(6) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_UP = function() if self.target_style == "lock" then self.target:scan(8) else self.target:freemove(8) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_DOWN = function() if self.target_style == "lock" then self.target:scan(2) else self.target:freemove(2) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT_DOWN = function() if self.target_style == "lock" then self.target:scan(1) else self.target:freemove(1) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT_DOWN = function() if self.target_style == "lock" then self.target:scan(3) else self.target:freemove(3) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT_UP = function() if self.target_style == "lock" then self.target:scan(7) else self.target:freemove(7) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT_UP = function() if self.target_style == "lock" then self.target:scan(9) else self.target:freemove(9) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
}
end
--- Handle mouse event for targeting
-- @return true if the event was handled
function _M:targetMouse(button, mx, my, xrel, yrel)
-- Move tooltip
self.tooltip_x, self.tooltip_y = mx, my
local tmx, tmy = self.level.map:getMouseTile(mx, my)
if self.key == self.targetmode_key then
-- Target with mouse
if button == "none" and xrel and yrel then
self.target:setSpot(tmx, tmy)
-- Cancel target
elseif button ~= "left" and not xrel and not yrel then
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
-- Accept target
elseif not xrel and not yrel then
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end
return true
end
end
--- Player requests a target
-- This method should be called by your Player:getTarget() method, it will handle everything
-- @param typ the targeting parameters
function _M:targetGetForPlayer(typ)
if self.target.forced then return unpack(self.target.forced) end
if coroutine.running() then
local msg
if type(typ) == "string" then msg, typ = typ, nil
elseif type(typ) == "table" then
if typ.default_target then self.target.target.entity = typ.default_target end
msg = typ.msg
end
self:targetMode("exclusive", msg, coroutine.running(), typ)
if typ.nolock then self.target_style = "free" end
if typ.nowarning then self.target_warning = false end
return coroutine.yield()
end
return self.target.target.x, self.target.target.y, self.target.target.entity
end
--- Player wants to set its target
-- This method should be called by your Player:setTarget() method, it will handle everything
function _M:targetSetForPlayer(target)
self.target.target.entity = target
self.target.target.x = target.x
self.target.target.y = target.y
end
-- TE4 - T-Engine 4
-- Copyright (C) 2009, 2010 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 Astar = require"engine.Astar"
local DirectPath = require"engine.DirectPath"
--- Handles player default mouse actions
-- Defines some methods to help use the mouse in an uniform way in all modules
module(..., package.seeall, class.make)
--- Runs to the clicked mouse spot
-- if no monsters in sight it will try to make an A* path, if it fails it will do a direct path<br/>
-- if there are monsters in sight it will move one stop in the direct path direction<br/>
-- this method requires to use PlayerRun interface
-- @param tmx the coords clicked
-- @param tmy the coords clicked
-- @param spotHostiles a function taking only the player as a parameter that must return true if hostiles are in sight
function _M:mouseMove(tmx, tmy, spotHostiles)
if config.settings.tome.cheat and core.key.modState("ctrl") then
game.log("[CHEAT] teleport to %dx%d", tmx, tmy)
self:move(tmx, tmy, true)
else
-- If hostiles, attack!
if (spotHostiles and spotHostiles(self)) or math.floor(core.fov.distance(self.x, self.y, tmx, tmy)) == 1 then
local l = line.new(self.x, self.y, tmx, tmy)
local nx, ny = l()
self:move(nx or self.x, ny or self.y)
return
end
local a = Astar.new(game.level.map, self)
local path = a:calc(self.x, self.y, tmx, tmy, true)
-- No Astar path ? jsut be dumb and try direct line
if not path then
local d = DirectPath.new(game.level.map, self)
path = d:calc(self.x, self.y, tmx, tmy, true)
end
if path then
-- Should we just try to move in the direction, aka: attack!
if path[1] and game.level.map:checkAllEntities(path[1].x, path[1].y, "block_move", self) then self:move(path[1].x, path[1].y) return end
-- Insert the player coords, running needs to find the player
table.insert(path, 1, {x=self.x, y=self.y})
-- Move along the projected A* path
self:runFollow(path)
end
end
end
local moving_around = false
local derivx, derivy = 0, 0
--- Handles mouse scrolling the map
-- @param map the Map to scroll
-- @param xrel the x movement velocity, gotten from a mouse event
-- @param yrel the y movement velocity, gotten from a mouse event
function _M:mouseScrollMap(map, xrel, yrel)
derivx = derivx + xrel
derivy = derivy + yrel
map.changed = true
if derivx >= map.tile_w then
map.mx = map.mx - 1
derivx = derivx - map.tile_w
elseif derivx <= -map.tile_w then
map.mx = map.mx + 1
derivx = derivx + map.tile_w
end
if derivy >= map.tile_h then
map.my = map.my - 1
derivy = derivy - map.tile_h
elseif derivy <= -map.tile_h then
map.my = map.my + 1
derivy = derivy + map.tile_h
end
map._map:setScroll(map.mx, map.my)
end
--- Handles global mouse event
-- This will handle events like this:<ul>
-- <li>Left click: player mouse movement</li>
-- <li>Shift + left click: map scroll</li>
-- <li>Any other click: pass on the click as a key event, to allow actiosnto be bound to the mouse</li>
-- </ul>
-- @param key the Key object to which to pass the event if not treated, this should be your game default key handler probably
-- @param allow_move true if this will allow player movement (you should use it to check that you are not in targetting mode)
function _M:mouseHandleDefault(key, allow_move, button, mx, my, xrel, yrel)
local tmx, tmy = game.level.map:getMouseTile(mx, my)
-- Move
if button == "left" and not core.key.modState("shift") and not moving_around and not xrel and not yrel then
if allow_move then self:mouseMove(tmx, tmy) end
-- Move map around
elseif button == "left" and xrel and yrel and core.key.modState("shift") then
self:mouseScrollMap(game.level.map, xrel, yrel)
moving_around = true
-- Zoom map
-- elseif button == "wheelup" then
-- game.level.map:setZoom(0.1, tmx, tmy)
-- elseif button == "wheeldown" then
-- game.level.map:setZoom(-0.1, tmx, tmy)
-- Pass any other buttons to the keybinder
elseif button ~= "none" and not xrel and not yrel then
key:receiveKey(button, core.key.modState("ctrl") and true or false, core.key.modState("shift") and true or false, core.key.modState("alt") and true or false, core.key.modState("meta") and true or false, nil, false, true)
end
if not xrel and not yrel then moving_around = false end
end
......@@ -19,12 +19,12 @@
require "engine.class"
require "engine.GameTurnBased"
require "engine.interface.GameTargeting"
require "engine.KeyBind"
local Savefile = require "engine.Savefile"
local DamageType = require "engine.DamageType"
local Zone = require "engine.Zone"
local Map = require "engine.Map"
local Target = require "engine.Target"
local Level = require "engine.Level"
local Birther = require "engine.Birther"
......@@ -41,7 +41,7 @@ local Tooltip = require "engine.Tooltip"
local QuitDialog = require "mod.dialogs.Quit"
module(..., package.seeall, class.inherit(engine.GameTurnBased, engine.interface.GameMusic, engine.interface.GameSound))
module(..., package.seeall, class.inherit(engine.GameTurnBased, engine.interface.GameTargeting))
function _M:init()
engine.GameTurnBased.init(self, engine.KeyBind.new(), 1000, 100)
......@@ -74,10 +74,7 @@ function _M:run()
if not self.player then self:newGame() end
-- Setup the targetting system
self.target = Target.new(Map, self.player)
self.target.target.entity = self.player
self.old_tmx, self.old_tmy = 0, 0
self.target_style = "lock"
engine.interface.GameTargeting.init(self)
-- Ok everything is good to go, activate the game in the engine!
self:setCurrent()
......@@ -180,7 +177,7 @@ end
function _M:tick()
if self.level then
if self.target.target.entity and not self.level:hasEntity(self.target.target.entity) then self.target.target.entity = false end
self:targetOnTick()
engine.GameTurnBased.tick(self)
-- Fun stuff: this can make the game realtime, although callit it in display() will make it work better
......@@ -219,112 +216,21 @@ function _M:display()
-- Display the targetting system if active
self.target:display()
-- Display a tooltip if available
if self.tooltip_x then
local mx, my = self.tooltip_x , self.tooltip_y
local tmx, tmy = self.level.map:getMouseTile(mx, my)
self.tooltip:displayAtMap(tmx, tmy, mx, my)
end
-- 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
self.old_tmx, self.old_tmy = tmx, tmy
-- And the minimap
self.level.map:minimapDisplay(self.w - 200, 20, util.bound(self.player.x - 25, 0, self.level.map.w - 50), util.bound(self.player.y - 25, 0, self.level.map.h - 50), 50, 50, 0.6)
end
engine.GameTurnBased.display(self)
end
-- Tooltip is displayed over all else
self:targetDisplayTooltip()
--- Targeting mode
-- Now before this is an hard piece of code. You probably wont need to change it much.<br/>
-- This uses a coroutine to allow a talent to request a target without interruption, yet while preserving the realtime-ness of the engine
function _M:targetMode(v, msg, co, typ)
local old = self.target_mode
self.target_mode = v
if not v then
Map:setViewerFaction(self.always_target and "players" or nil)
if msg then self.log(type(msg) == "string" and msg or "Tactical display disabled. Press shift+'t' or right mouse click to enable.") end
self.level.map.changed = true
self.target:setActive(false)
if tostring(old) == "exclusive" then
self.key = self.normal_key
self.key:setCurrent()
if self.target_co then
local co = self.target_co
self.target_co = nil
local ok, err = coroutine.resume(co, self.target.target.x, self.target.target.y, self.target.target.entity)
if not ok and err then print(debug.traceback(co)) error(err) end
end
end
else
Map:setViewerFaction("players")
if msg then self.log(type(msg) == "string" and msg or "Tactical display enabled. Press shift+'t' to disable.") end
self.level.map.changed = true
self.target:setActive(true, typ)
self.target_style = "lock"
-- Exclusive mode means we disable the current key handler and use a specific one
-- that only allows targetting and resumes talent coroutine when done
if tostring(v) == "exclusive" then
self.target_co = co
self.key = self.targetmode_key
self.key:setCurrent()
if self.target.target.entity and self.level.map.seens(self.target.target.entity.x, self.target.target.entity.y) and self.player ~= self.target.target.entity then
else
self.target:scan(5, nil, self.player.x, self.player.y)
end
end
self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y)
end
engine.GameTurnBased.display(self)
end
--- Setup the keybinds
function _M:setupCommands()
-- One key handler for targeting
self.targetmode_key = engine.KeyBind.new()
self.targetmode_key:addCommands{ _SPACE=function() self:targetMode(false, false) end, }
self.targetmode_key:addBinds
{
TACTICAL_DISPLAY = function() self:targetMode(false, false) end,
ACCEPT = function()
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
EXIT = function()
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
-- Targeting movement
RUN_LEFT = function() self.target:freemove(4) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT = function() self.target:freemove(6) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_UP = function() self.target:freemove(8) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_DOWN = function() self.target:freemove(2) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_LEFT_DOWN = function() self.target:freemove(1) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT_DOWN = function() self.target:freemove(3) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_LEFT_UP = function() self.target:freemove(7) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT_UP = function() self.target:freemove(9) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT = function() if self.target_style == "lock" then self.target:scan(4) else self.target:freemove(4) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT = function() if self.target_style == "lock" then self.target:scan(6) else self.target:freemove(6) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_UP = function() if self.target_style == "lock" then self.target:scan(8) else self.target:freemove(8) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_DOWN = function() if self.target_style == "lock" then self.target:scan(2) else self.target:freemove(2) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT_DOWN = function() if self.target_style == "lock" then self.target:scan(1) else self.target:freemove(1) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT_DOWN = function() if self.target_style == "lock" then self.target:scan(3) else self.target:freemove(3) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT_UP = function() if self.target_style == "lock" then self.target:scan(7) else self.target:freemove(7) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT_UP = function() if self.target_style == "lock" then self.target:scan(9) else self.target:freemove(9) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
}
-- Make targeting work
self.normal_key = self.key
self:targetSetupKey()
-- One key handled for normal function
self.key:addBinds
......@@ -453,72 +359,13 @@ function _M:setupCommands()
end
function _M:setupMouse(reset)
-- Those 2 locals will be "absorbed" into the mosue event handler function, this is a closure
local derivx, derivy = 0, 0
local moving_around = false
if reset then self.mouse:reset() end
self.mouse:registerZone(Map.display_x, Map.display_y, Map.viewport.width, Map.viewport.height, function(button, mx, my, xrel, yrel)
-- Move tooltip
self.tooltip_x, self.tooltip_y = mx, my
local tmx, tmy = self.level.map:getMouseTile(mx, my)
if self.key == self.targetmode_key then
-- Target with mouse
if button == "none" and xrel and yrel then
self.target:setSpot(tmx, tmy)
-- Cancel target
elseif button ~= "left" and not xrel and not yrel then
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
-- Accept target
elseif not xrel and not yrel then
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end
return
end
-- Move
if button == "left" and not core.key.modState("shift") and not moving_around and not xrel and not yrel then
if self.key == self.normal_key then self.player:mouseMove(tmx, tmy) end
-- Move map around
elseif button == "left" and xrel and yrel and core.key.modState("shift") then
derivx = derivx + xrel
derivy = derivy + yrel
game.level.map.changed = true
if derivx >= game.level.map.tile_w then
game.level.map.mx = game.level.map.mx - 1
derivx = derivx - game.level.map.tile_w
elseif derivx <= -game.level.map.tile_w then
game.level.map.mx = game.level.map.mx + 1
derivx = derivx + game.level.map.tile_w
end
if derivy >= game.level.map.tile_h then
game.level.map.my = game.level.map.my - 1
derivy = derivy - game.level.map.tile_h
elseif derivy <= -game.level.map.tile_h then
game.level.map.my = game.level.map.my + 1
derivy = derivy + game.level.map.tile_h
end
game.level.map._map:setScroll(game.level.map.mx, game.level.map.my)
moving_around = true
elseif button ~= "none" and not xrel and not yrel then
self.key:receiveKey(
button,
core.key.modState("ctrl") and true or false,
core.key.modState("shift") and true or false,
core.key.modState("alt") and true or false,
core.key.modState("meta") and true or false,
nil, false, true
)
end
-- Handle targeting
if self:targetMouse(button, mx, my, xrel, yrel) then return end
if not xrel and not yrel then moving_around = false end
-- Handle the mouse movement/scrolling
self.player:mouseHandleDefault(self.key, self.key == self.normal_key, button, mx, my, xrel, yrel)
end)
-- Scroll message log
self.mouse:registerZone(self.logdisplay.display_x, self.logdisplay.display_y, self.w, self.h, function(button)
......
......@@ -21,6 +21,7 @@ require "engine.class"
require "mod.class.Actor"
require "engine.interface.PlayerRest"
require "engine.interface.PlayerRun"
require "engine.interface.PlayerMouse"
require "engine.interface.PlayerHotkeys"
local Map = require "engine.Map"
local Dialog = require "engine.Dialog"
......@@ -36,6 +37,7 @@ module(..., package.seeall, class.inherit(
mod.class.Actor,
engine.interface.PlayerRest,
engine.interface.PlayerRun,
engine.interface.PlayerMouse,
engine.interface.PlayerHotkeys
))
......@@ -134,30 +136,13 @@ function _M:levelup()
end
--- Tries to get a target from the user
-- *WARNING* If used inside a coroutine it will yield and resume it later when a target is found.
-- This is usualy just what you want so dont think too much about it :)
function _M:getTarget(typ)
if coroutine.running() then
local msg
if type(typ) == "string" then msg, typ = typ, nil
elseif type(typ) == "table" then
if typ.default_target then game.target.target.entity = typ.default_target end
msg = typ.msg
end
game:targetMode("exclusive", msg, coroutine.running(), typ)
if typ.nolock then
game.target_style = "free"
end
return coroutine.yield()
end
return game.target.target.x, game.target.target.y, game.target.target.entity
return game:targetGetForPlayer(typ)
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
return game:targetSetForPlayer(target)
end
local function spotHostiles(self)
......@@ -201,40 +186,8 @@ function _M:runCheck()
return engine.interface.PlayerRun.runCheck(self)
end
--- Runs to the clicked mouse spot
-- if no monsters in sight it will try to make an A* path, if it fails it will do a direct path
-- if there are monsters in sight it will move one stop in the direct path direction
--- Move with the mouse
-- We just feed our spotHostile to the interface mouseMove
function _M:mouseMove(tmx, tmy)
if config.settings.tome.cheat and game.key.status[game.key._LSHIFT] and game.key.status[game.key._LCTRL] then
game.log("[CHEAT] teleport to %dx%d", tmx, tmy)
self:move(tmx, tmy, true)
else
-- If hostiles, attack!
if spotHostiles(self) or math.floor(core.fov.distance(self.x, self.y, tmx, tmy)) == 1 then
local l = line.new(self.x, self.y, tmx, tmy)
local nx, ny = l()
self:move(nx or self.x, ny or self.y)
return
end
local a = Astar.new(game.level.map, self)
local path = a:calc(self.x, self.y, tmx, tmy, true)
-- No Astar path ? jsut be dumb and try direct line
if not path then
local d = DirectPath.new(game.level.map, self)
path = d:calc(self.x, self.y, tmx, tmy, true)
end
if path then
-- Should we just try to move in the direction, aka: attack!
if path[1] and game.level.map:checkAllEntities(path[1].x, path[1].y, "block_move", self) then self:move(path[1].x, path[1].y) return end
-- Insert the player coords, running needs to find the player
table.insert(path, 1, {x=self.x, y=self.y})
-- Move along the projected A* path
self:runFollow(path)
end
end
return engine.interface.PlayerMouse.mouseMove(self, tmx, tmy, spotHostiles)
end
......@@ -21,12 +21,12 @@ require "engine.class"
require "engine.GameTurnBased"
require "engine.interface.GameMusic"
require "engine.interface.GameSound"
require "engine.interface.GameTargeting"
local KeyBind = require "engine.KeyBind"
local Savefile = require "engine.Savefile"
local DamageType = require "engine.DamageType"
local Zone = require "engine.Zone"
local Map = require "engine.Map"
local Target = require "engine.Target"
local Level = require "engine.Level"
local Birther = require "engine.Birther"
local Astar = require "engine.Astar"
......@@ -58,7 +58,7 @@ local Calendar = require "engine.Calendar"
local Dialog = require "engine.Dialog"
local QuitDialog = require "mod.dialogs.Quit"
module(..., package.seeall, class.inherit(engine.GameTurnBased, engine.interface.GameMusic, engine.interface.GameSound))
module(..., package.seeall, class.inherit(engine.GameTurnBased, engine.interface.GameMusic, engine.interface.GameSound, engine.interface.GameTargeting))
-- Difficulty settings
DIFFICULTY_EASY = 1
......@@ -107,10 +107,7 @@ function _M:run()
self.hotkeys_display.actor = self.player
self.npcs_display.actor = self.player
self.target = Target.new(Map, self.player)
self.target.target.entity = self.player
self.old_tmx, self.old_tmy = 0, 0
self.target_style = "lock"
engine.interface.GameTargeting.init(self)
-- Ok everything is good to go, activate the game in the engine!
self:setCurrent()
......@@ -220,8 +217,7 @@ function _M:setupDisplayMode()
end
if self.level then
self.level.map:recreate()
self.target = Target.new(Map, self.player)
self.target.target.entity = self.player
engine.interface.GameTargeting.init(self)
self.level.map:moveViewSurround(self.player.x, self.player.y, 8, 8)
end
self:setupMiniMap()
......@@ -368,7 +364,7 @@ end
function _M:tick()
if self.level then
if self.target.target.entity and not self.level:hasEntity(self.target.target.entity) then self.target.target.entity = false end
self:targetOnTick()
engine.GameTurnBased.tick(self)
-- Fun stuff: this can make the game realtime, although callit it in display() will make it work better
......@@ -448,126 +444,16 @@ function _M:display()
if self.player then self.player.changed = false end
-- Tooltip is displayed over all else
if self.level and self.level.map and self.level.map.finished then
-- Display a tooltip if available
if self.tooltip_x then self.tooltip:displayAtMap(self.level.map:getMouseTile(self.tooltip_x , self.tooltip_y)) end
-- 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
self.old_tmx, self.old_tmy = tmx, tmy
end
self:targetDisplayTooltip()
engine.GameTurnBased.display(self)
end
function _M:targetMode(v, msg, co, typ)
local old = self.target_mode
self.target_mode = v
if not v then
Map:setViewerFaction(self.always_target and self.player.faction or nil)
if msg then self.log(type(msg) == "string" and msg or "Tactical display disabled. Press shift+'t' or right mouse click to enable.") end
self.level.map.changed = true
self.target:setActive(false)
if tostring(old) == "exclusive" then
local fct = function(ok)
if not ok then
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
end
self.key = self.normal_key
self.key:setCurrent()
if self.target_co then
local co = self.target_co
self.target_co = nil
local ok, err = coroutine.resume(co, self.target.target.x, self.target.target.y, self.target.target.entity)
if not ok and err then print(debug.traceback(co)) error(err) end
end
end
if self.target_warning and self.target.target.x == game.player.x and self.target.target.y == game.player.y then
Dialog:yesnoPopup("Target yourself?", "Are you sure you want to target yourself?", fct)
else
fct(true)
end
end
else
Map:setViewerFaction(self.player.faction)
if msg then self.log(type(msg) == "string" and msg or "Tactical display enabled. Press shift+'t' to disable.") end
self.level.map.changed = true
self.target:setActive(true, typ)
self.target_style = "lock"
self.target_warning = true
-- Exclusive mode means we disable the current key handler and use a specific one
-- that only allows targetting and resumes talent coroutine when done
if tostring(v) == "exclusive" then
self.target_co = co
self.key = self.targetmode_key
self.key:setCurrent()
if self.target.target.entity and self.level.map.seens(self.target.target.entity.x, self.target.target.entity.y) and self.player ~= self.target.target.entity then
else
local filter = nil
if type(typ) == "table" and typ.first_target and typ.first_target == "friend" then
filter = function(a) return self.player:reactionToward(a) >= 0 end
else
filter = function(a) return self.player:reactionToward(a) < 0 end
end
self.target:scan(5, nil, self.player.x, self.player.y, filter)
end
end
if self.target.target.x then
self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y)
end
end
end
function _M:setupCommands()
self.targetmode_key = engine.KeyBind.new()
self.targetmode_key:addCommands{ _SPACE=function() self:targetMode(false, false) self.tooltip_x, self.tooltip_y = nil, nil end, }
self.targetmode_key:addBinds
{
TACTICAL_DISPLAY = function()
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
ACCEPT = function()
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
EXIT = function()
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end,
-- Targeting movement
RUN_LEFT = function() self.target:freemove(4) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT = function() self.target:freemove(6) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_UP = function() self.target:freemove(8) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_DOWN = function() self.target:freemove(2) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_LEFT_DOWN = function() self.target:freemove(1) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT_DOWN = function() self.target:freemove(3) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_LEFT_UP = function() self.target:freemove(7) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
RUN_RIGHT_UP = function() self.target:freemove(9) self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT = function() if self.target_style == "lock" then self.target:scan(4) else self.target:freemove(4) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT = function() if self.target_style == "lock" then self.target:scan(6) else self.target:freemove(6) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_UP = function() if self.target_style == "lock" then self.target:scan(8) else self.target:freemove(8) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_DOWN = function() if self.target_style == "lock" then self.target:scan(2) else self.target:freemove(2) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT_DOWN = function() if self.target_style == "lock" then self.target:scan(1) else self.target:freemove(1) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT_DOWN = function() if self.target_style == "lock" then self.target:scan(3) else self.target:freemove(3) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_LEFT_UP = function() if self.target_style == "lock" then self.target:scan(7) else self.target:freemove(7) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
MOVE_RIGHT_UP = function() if self.target_style == "lock" then self.target:scan(9) else self.target:freemove(9) end self.tooltip_x, self.tooltip_y = self.level.map:getTileToScreen(self.target.target.x, self.target.target.y) end,
}
-- Make targeting work
self.normal_key = self.key
self:targetSetupKey()
-- Activate profiler keybinds
self.key:setupProfiler()
......@@ -793,79 +679,13 @@ function _M:setupCommands()
end
function _M:setupMouse(reset)
-- Those 2 locals will be "absorbed" into the mouse event handler function, this is a closure
local derivx, derivy = 0, 0
local zoom = 1
local moving_around = false
if reset then self.mouse:reset() end
self.mouse:registerZone(Map.display_x, Map.display_y, Map.viewport.width, Map.viewport.height, function(button, mx, my, xrel, yrel)
-- Move tooltip
self.tooltip_x, self.tooltip_y = mx, my
local tmx, tmy = self.level.map:getMouseTile(mx, my)
if self.key == self.targetmode_key then
-- Target with mouse
if button == "none" and xrel and yrel then
self.target:setSpot(tmx, tmy)
-- Cancel target
elseif button ~= "left" and not xrel and not yrel then
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
-- Accept target
elseif not xrel and not yrel then
self.target.target.entity = nil
self.target.target.x = nil
self.target.target.y = nil
self:targetMode(false, false)
self.tooltip_x, self.tooltip_y = nil, nil
end
return
end
-- Move
if button == "left" and not core.key.modState("shift") and not moving_around and not xrel and not yrel then
if self.key == self.normal_key then self.player:mouseMove(tmx, tmy) end
-- Move map around
elseif button == "left" and xrel and yrel and core.key.modState("shift") then
derivx = derivx + xrel
derivy = derivy + yrel
game.level.map.changed = true
if derivx >= game.level.map.tile_w then
game.level.map.mx = game.level.map.mx - 1
derivx = derivx - game.level.map.tile_w
elseif derivx <= -game.level.map.tile_w then
game.level.map.mx = game.level.map.mx + 1
derivx = derivx + game.level.map.tile_w
end
if derivy >= game.level.map.tile_h then
game.level.map.my = game.level.map.my - 1
derivy = derivy - game.level.map.tile_h
elseif derivy <= -game.level.map.tile_h then
game.level.map.my = game.level.map.my + 1
derivy = derivy + game.level.map.tile_h
end
game.level.map._map:setScroll(game.level.map.mx, game.level.map.my)
moving_around = true
-- Zoom map
-- elseif button == "wheelup" then
-- game.level.map:setZoom(0.1, tmx, tmy)
-- elseif button == "wheeldown" then
-- game.level.map:setZoom(-0.1, tmx, tmy)
-- Pass any other buttons to the keybinder
elseif button ~= "none" and not xrel and not yrel then
self.key:receiveKey(
button,
core.key.modState("ctrl") and true or false,
core.key.modState("shift") and true or false,
core.key.modState("alt") and true or false,
core.key.modState("meta") and true or false,
nil, false, true
)
end
-- Handle targeting
if self:targetMouse(button, mx, my, xrel, yrel) then return end
if not xrel and not yrel then moving_around = false end
-- Handle the mouse movement/scrolling
self.player:mouseHandleDefault(self.key, self.key == self.normal_key, button, mx, my, xrel, yrel)
end)
-- Scroll message log
self.mouse:registerZone(self.logdisplay.display_x, self.logdisplay.display_y, self.w, self.h, function(button)
......
......@@ -23,14 +23,13 @@ require "engine.interface.PlayerRest"
require "engine.interface.PlayerRun"
require "engine.interface.PlayerHotkeys"
require "engine.interface.PlayerSlide"
require "engine.interface.PlayerMouse"
local Map = require "engine.Map"
local Dialog = require "engine.Dialog"
local ActorTalents = require "engine.interface.ActorTalents"
local LevelupStatsDialog = require "mod.dialogs.LevelupStatsDialog"
local LevelupTalentsDialog = require "mod.dialogs.LevelupTalentsDialog"
local DeathDialog = require "mod.dialogs.DeathDialog"
local Astar = require"engine.Astar"
local DirectPath = require"engine.DirectPath"
--- Defines the player for ToME
-- It is a normal actor, with some redefined methods to handle user interaction.<br/>
......@@ -40,6 +39,7 @@ module(..., package.seeall, class.inherit(
engine.interface.PlayerRest,
engine.interface.PlayerRun,
engine.interface.PlayerHotkeys,
engine.interface.PlayerMouse,
engine.interface.PlayerSlide
))
......@@ -293,30 +293,13 @@ function _M:levelup()
end
--- Tries to get a target from the user
-- *WARNING* If used inside a coroutine it will yield and resume it later when a target is found.
-- This is usualy just what you want so dont think too much about it :)
function _M:getTarget(typ)
if game.target.forced then return unpack(game.target.forced) end
if coroutine.running() then
local msg
if type(typ) == "string" then msg, typ = typ, nil
elseif type(typ) == "table" then
if typ.default_target then game.target.target.entity = typ.default_target end
msg = typ.msg
end
game:targetMode("exclusive", msg, coroutine.running(), typ)
if typ.nolock then game.target_style = "free" end
if typ.nowarning then game.target_warning = false end
return coroutine.yield()
end
return game.target.target.x, game.target.target.y, game.target.target.entity
return game:targetGetForPlayer(typ)
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
return game:targetSetForPlayer(target)
end
local function spotHostiles(self)
......@@ -384,6 +367,12 @@ function _M:runCheck()
return engine.interface.PlayerRun.runCheck(self)
end
--- Move with the mouse
-- We just feed our spotHostile to the interface mouseMove
function _M:mouseMove(tmx, tmy)
return engine.interface.PlayerMouse.mouseMove(self, tmx, tmy, spotHostiles)
end
--- Called after running a step
function _M:runMoved()
self:playerFOV()
......@@ -541,43 +530,6 @@ function _M:playerLevelup(on_finish)
end
end
--- Runs to the clicked mouse spot
-- if no monsters in sight it will try to make an A* path, if it fails it will do a direct path
-- if there are monsters in sight it will move one stop in the direct path direction
function _M:mouseMove(tmx, tmy)
if config.settings.tome.cheat and core.key.modState("ctrl") then
game.log("[CHEAT] teleport to %dx%d", tmx, tmy)
self:move(tmx, tmy, true)
else
-- If hostiles, attack!
if spotHostiles(self) or math.floor(core.fov.distance(self.x, self.y, tmx, tmy)) == 1 then
local l = line.new(self.x, self.y, tmx, tmy)
local nx, ny = l()
self:move(nx or self.x, ny or self.y)
return
end
local a = Astar.new(game.level.map, self)
local path = a:calc(self.x, self.y, tmx, tmy, true)
-- No Astar path ? jsut be dumb and try direct line
if not path then
local d = DirectPath.new(game.level.map, self)
path = d:calc(self.x, self.y, tmx, tmy, true)
end
if path then
-- Should we just try to move in the direction, aka: attack!
if path[1] and game.level.map:checkAllEntities(path[1].x, path[1].y, "block_move", self) then self:move(path[1].x, path[1].y) return end
-- Insert the player coords, running needs to find the player
table.insert(path, 1, {x=self.x, y=self.y})
-- Move along the projected A* path
self:runFollow(path)
end
end
end
--- Use a portal with the orb of many ways
function _M:useOrbPortal(portal)
if portal.change_wilderness then
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment