Skip to content
Snippets Groups Projects
Commit 5c00bce6 authored by dg's avatar dg
Browse files

mouse-right-click will now make the player either:

- run to the clicked spot if a path can be found
- run in the direct if no path is found
- move one turn in the direction if hostiles are present or if the clicked spot is adjacent

summary: can be played with the mouse


git-svn-id: http://svn.net-core.org/repos/t-engine4@593 51575b47-30f0-44d4-a5cc-537603b46e54
parent 8b441612
No related branches found
No related tags found
No related merge requests found
......@@ -45,12 +45,13 @@ end
function _M:createPath(came_from, cur)
if not came_from[cur] then return end
local path = {}
local rpath, path = {}, {}
while came_from[cur] do
local x, y = self:toDouble(cur)
path[#path+1] = {x=x,y=y}
rpath[#rpath+1] = {x=x,y=y}
cur = came_from[cur]
end
for i = #rpath, 1, -1 do path[#path+1] = rpath[i] end
return path
end
......@@ -59,10 +60,9 @@ end
-- @param sy the start coord
-- @param tx the end coord
-- @param ty the end coord
-- @param use_has_seen if true the astar wont consider non-has_seen grids
-- @return either nil if no path or a list of nodes in the form { {x=...,y=...}, {x=...,y=...}, ..., {x=tx,y=ty}}
function _M:calc(sx, sy, tx, ty)
if self.actor.changed then
function _M:calc(sx, sy, tx, ty, use_has_seen)
local w, h = self.map.w, self.map.h
local start = self:toSingle(sx, sy)
local stop = self:toSingle(tx, ty)
......@@ -75,7 +75,7 @@ function _M:calc(sx, sy, tx, ty)
local checkPos = function(node, nx, ny)
local nnode = self:toSingle(nx, ny)
if not closed[nnode] and self.map:isBound(nx, ny) and not self.map:checkEntity(nx, ny, Map.TERRAIN, "block_move", self.actor) then
if not closed[nnode] and self.map:isBound(nx, ny) and ((use_has_seen and not self.map.has_seens(nx, ny)) or not self.map:checkEntity(nx, ny, Map.TERRAIN, "block_move", self.actor, nil, true)) then
local tent_g_score = g_score[node] + 1 -- we can adjust hre for difficult passable terrain
local tent_is_better = false
if not open[nnode] then open[nnode] = true; tent_is_better = true
......
-- 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 Map = require "engine.Map"
--- Computes a direct line path from start to end
module(..., package.seeall, class.make)
--- Initializes DirectPath for a map and an actor
function _M:init(map, actor)
self.map = map
self.actor = actor
end
--- Compute path from sx/sy to tx/ty
-- @param sx the start coord
-- @param sy the start coord
-- @param tx the end coord
-- @param ty the end coord
-- @param use_has_seen if true the astar wont consider non-has_seen grids
-- @return either nil if no path or a list of nodes in the form { {x=...,y=...}, {x=...,y=...}, ..., {x=tx,y=ty}}
function _M:calc(sx, sy, tx, ty, use_has_seen)
local path = {}
local l = line.new(sx, sy, tx, ty)
local nx, ny = l()
while nx and ny do
if (not use_has_seen or self.map.has_seens(nx, ny)) and self.map:isBound(nx, ny) and not self.map:checkEntity(nx, ny, Map.TERRAIN, "block_move", self.actor, nil, true) then
path[#path+1] = {x=nx, y=ny}
else
break
end
nx, ny = l()
end
return path
end
......@@ -107,6 +107,7 @@ function _M:init(w, h)
self.map = {}
self.lites = {}
self.seens = {}
self.has_seens = {}
self.remembers = {}
self.effects = {}
for i = 0, w * h - 1 do self.map[i] = {} end
......@@ -143,10 +144,18 @@ function _M:loaded()
if v ~= nil then
t[x + y * self.w] = v
self._map:setSeen(x, y, v)
if v then self.has_seen[x + y * self.w] = true end
self.changed = true
end
return t[x + y * self.w]
end
local maphasseen = function(t, x, y, v)
if x < 0 or y < 0 or x >= self.w or y >= self.h then return end
if v ~= nil then
t[x + y * self.w] = v
end
return t[x + y * self.w]
end
local mapremember = function(t, x, y, v)
if x < 0 or y < 0 or x >= self.w or y >= self.h then return end
if v ~= nil then
......@@ -169,6 +178,7 @@ function _M:loaded()
getmetatable(self).__call = _M.call
setmetatable(self.lites, {__call = maplite})
setmetatable(self.seens, {__call = mapseen})
setmetatable(self.has_seens, {__call = maphasseen})
setmetatable(self.remembers, {__call = mapremember})
self.surface = core.display.newSurface(self.viewport.width, self.viewport.height)
......@@ -345,6 +355,7 @@ function _M:apply(x, y)
if x < 0 or x >= self.w or y < 0 or y >= self.h then return end
if self.lites[x + y * self.w] then
self.seens[x + y * self.w] = true
self.has_seens[x + y * self.w] = true
self._map:setSeen(x, y, true)
self.remembers[x + y * self.w] = true
self._map:setRemember(x, y, true)
......@@ -360,6 +371,7 @@ function _M:applyLite(x, y)
self._map:setRemember(x, y, true)
end
self.seens[x + y * self.w] = true
self.has_seens[x + y * self.w] = true
self._map:setSeen(x, y, true)
end
......
......@@ -83,15 +83,25 @@ function _M:runInit(dir)
self:runStep()
end
--- Initializes running to a specific position
-- This does not use the normal running algorithm but instead an A*
function _M:runTo(x, y)
local block_left, block_right = false, false
--- Initializes running to a specific position using the given path
-- This does not use the normal running algorithm
function _M:runFollow(path)
local found = false
local runpath = {}
-- Find ourself along the path
for i, c in ipairs(path) do
if found then runpath[#runpath+1] = c
elseif c.x == self.x and c.y == self.y then found = true end
end
if #runpath == 0 then
game.logPlayer(self, "Invalid running path.")
return
end
self.running = {
to = {x=x, y=y},
block_left = block_left,
block_right = block_right,
path = runpath,
cnt = 1,
dialog = Dialog:simplePopup("Running...", "You are running, press any key to stop.", function()
self:runStop()
......@@ -117,11 +127,21 @@ function _M:runStep()
self:runStop(msg)
return false
else
if isEdge(self, self.running.dir) then self:runStop()
else self:moveDir(self.running.dir) end
local oldx, oldy = self.x, self.y
if self.running.path then
if not self.running.path[self.running.cnt] then self:runStop()
else self:move(self.running.path[self.running.cnt].x, self.running.path[self.running.cnt].y) end
else
if isEdge(self, self.running.dir) then self:runStop()
else self:moveDir(self.running.dir) end
end
-- Did not move ? no use in running
if self.x == oldx and self.y == oldy then self:runStop() end
if not self.running then return false end
self.running.cnt = self.running.cnt + 1
if self.running.newdir then
self.running.dir = self.running.newdir
self.running.newdir = nil
......@@ -145,28 +165,30 @@ end
-- 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
return true
if not self.running.path then
-- 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
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
return true
end
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
return true
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
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
......@@ -186,18 +208,24 @@ 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)
if not self.running.path then
-- 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)
elseif self.running.path[self.running.cnt] then
-- Ahead
local x, y = self.running.path[self.running.cnt].x, self.running.path[self.running.cnt].y
fct(x, y)
end
end
......@@ -248,6 +248,7 @@ function _M:magicMap(radius, x, y)
for i = x - radius, x + radius do for j = y - radius, y + radius do
if game.level.map:isBound(i, j) and core.fov.distance(x, y, i, j) < radius then
game.level.map.remembers(i, j, true)
game.level.map.has_seens(i, j, true)
end
end end
end
......
......@@ -670,12 +670,7 @@ function _M:setupMouse()
-- Target stuff
if button == "right" then
-- DEBUG
if config.settings.tome.cheat then
game.player:move(tmx, tmy, true)
else
-- Move along the projected A* path
end
self.player:mouseMove(tmx, tmy)
-- Move map around
elseif button == "left" and xrel and yrel then
......@@ -699,9 +694,13 @@ function _M:setupMouse()
game.level.map._map:setScroll(game.level.map.mx, game.level.map.my)
elseif button == "none" then
if self.key.status[self.key._LSHIFT] and (self.test_x ~= tmx or self.test_y ~= tmy) then
local Astar = require"engine.Astar"
local a = Astar.new(self.level.map, self.player)
local path = a:calc(self.player.x, self.player.y, tmx, tmy)
local path = a:calc(self.player.x, self.player.y, tmx, tmy, true)
-- No Astar path ? jsut be dumb and try direct line
if not path then
local d= DirectPath.new(self.level.map, self.player)
path = d:calc(self.player.x, self.player.y, tmx, tmy, true)
end
self.test_x = tmx
self.text_y = tmy
self.test_path = path
......
......@@ -27,12 +27,12 @@ function _M:init(t, no_default)
engine.Grid.init(self, t, no_default)
end
function _M:block_move(x, y, e, act)
function _M:block_move(x, y, e, act, couldpass)
-- Open doors
if self.door_opened and act then
game.level.map(x, y, engine.Map.TERRAIN, game.zone.grid_list.DOOR_OPEN)
return true
elseif self.door_opened then
elseif self.door_opened and not couldpass then
return true
end
......
......@@ -29,6 +29,8 @@ 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/>
......@@ -82,8 +84,6 @@ function _M:init(t, no_default)
end
function _M:move(x, y, force)
-- x, y = self:tryPlayerSlide(x, y, force)
local moved = mod.class.Actor.move(self, x, y, force)
if moved then
game.level.map:moveViewSurround(self.x, self.y, 8, 8)
......@@ -431,6 +431,42 @@ 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 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
end
------ Quest Events
function _M:on_quest_grant(quest)
......
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