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

only some actors can open doors

path string cache for speedy A*


git-svn-id: http://svn.net-core.org/repos/t-engine4@647 51575b47-30f0-44d4-a5cc-537603b46e54
parent 48c54153
No related branches found
No related tags found
No related merge requests found
Showing
with 159 additions and 20 deletions
......@@ -104,6 +104,12 @@ function _M:canMove(x, y, terrain_only)
end
end
--- Get the "path string" for this actor
-- See Map:addPathString() for more info
function _M:getPathString()
return ""
end
--- Teleports randomly to a passable grid
-- @param x the coord of the teleporatation
-- @param y the coord of the teleporatation
......@@ -133,17 +139,28 @@ end
--- Knock back the actor
function _M:knockback(srcx, srcy, dist)
print("[KNOCKBACK] from", srcx, srcy, "over", dist)
local l = line.new(srcx, srcy, self.x, self.y, true)
local lx, ly = l(true)
local ox, oy = lx, ly
dist = dist - 1
print("[KNOCKBACK] try", lx, ly, dist)
while game.level.map:isBound(lx, ly) and not game.level.map:checkAllEntities(lx, ly, "block_move", self) and dist > 0 do
dist = dist - 1
ox, oy = lx, ly
lx, ly = l(true)
print("[KNOCKBACK] try", lx, ly, dist, "::", game.level.map:checkAllEntities(lx, ly, "block_move", self))
end
if game.level.map:isBound(lx, ly) and not game.level.map:checkAllEntities(lx, ly, "block_move", self) then
print("[KNOCKBACK] ok knocked to", lx, ly, "::", game.level.map:checkAllEntities(lx, ly, "block_move", self))
self:move(lx, ly, true)
elseif game.level.map:isBound(ox, oy) and not game.level.map:checkAllEntities(ox, oy, "block_move", self) then
print("[KNOCKBACK] failsafe knocked to", ox, oy, "::", game.level.map:checkAllEntities(ox, oy, "block_move", self))
self:move(ox, oy, true)
end
end
......
......@@ -73,20 +73,42 @@ function _M:calc(sx, sy, tx, ty, use_has_seen)
local f_score = {[start] = self:heuristic(sx, sy, tx, ty)}
local came_from = {}
local checkPos = function(node, nx, ny)
local nnode = self:toSingle(nx, ny)
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
elseif tent_g_score < g_score[nnode] then tent_is_better = true
local cache = self.map._fovcache.path_caches[self.actor:getPathString()]
local checkPos
if cache then
checkPos = function(node, nx, ny)
local nnode = self:toSingle(nx, ny)
if not closed[nnode] and self.map:isBound(nx, ny) and ((use_has_seen and not self.map.has_seens(nx, ny)) or not cache:get(nx, ny)) 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
elseif tent_g_score < g_score[nnode] then tent_is_better = true
end
if tent_is_better then
came_from[nnode] = node
g_score[nnode] = tent_g_score
h_score[nnode] = self:heuristic(tx, ty, nx, ny)
f_score[nnode] = g_score[nnode] + h_score[nnode]
end
end
end
else
checkPos = function(node, nx, ny)
local nnode = self:toSingle(nx, ny)
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
elseif tent_g_score < g_score[nnode] then tent_is_better = true
end
if tent_is_better then
came_from[nnode] = node
g_score[nnode] = tent_g_score
h_score[nnode] = self:heuristic(tx, ty, nx, ny)
f_score[nnode] = g_score[nnode] + h_score[nnode]
if tent_is_better then
came_from[nnode] = node
g_score[nnode] = tent_g_score
h_score[nnode] = self:heuristic(tx, ty, nx, ny)
f_score[nnode] = g_score[nnode] + h_score[nnode]
end
end
end
end
......
......@@ -165,6 +165,7 @@ function _M:init(w, h)
self.has_seens = {}
self.remembers = {}
self.effects = {}
self.path_strings = {}
for i = 0, w * h - 1 do self.map[i] = {} end
self:loaded()
......@@ -200,7 +201,23 @@ function _M:makeCMap()
block_sight = core.fov.newCache(self.w, self.h),
block_esp = core.fov.newCache(self.w, self.h),
block_sense = core.fov.newCache(self.w, self.h),
path_caches = {},
}
for i, ps in ipairs(self.path_strings) do
self._fovcache.path_caches[ps] = core.fov.newCache(self.w, self.h)
end
end
--- Adds a "path string" to the map
-- "Path strings" are strings defining what terrain an actor can cross. Their format is left to the module to decide (by overloading Actor:getPathString() )<br/>
-- They are totally optional as they re only used to compute A* paths adn the likes and even then the algorithms still work without them, only slower<br/>
-- If you use them the block_move function of your Grid class must be able to handle either an actor or a "path string" as their third argument
function _M:addPathString(ps)
for i, eps in ipairs(self.path_strings) do
if eps == ps then return end
end
self.path_strings[#self.path_strings+1] = ps
if self._fovcache then self._fovcache.path_caches[ps] = core.fov.newCache(self.w, self.h) end
end
function _M:loaded()
......@@ -291,7 +308,7 @@ function _M:cleanFOV()
end
--- Updates the map on the given spot
-- This updates many things, from the C map object, the FOC caches, the minimap if it exists, ...
-- This updates many things, from the C map object, the FOV caches, the minimap if it exists, ...
function _M:updateMap(x, y)
local g = self(x, y, TERRAIN)
local o = self(x, y, OBJECT)
......@@ -354,6 +371,12 @@ function _M:updateMap(x, y)
else self._fovcache.block_esp:set(x, y, false) end
if self:checkAllEntities(x, y, "block_sense", self.actor_player) then self._fovcache.block_sense:set(x, y, true)
else self._fovcache.block_sense:set(x, y, false) end
-- Update path caches from path strings
for i = 1, #self.path_strings do
local ps = self.path_strings[i]
self._fovcache.path_caches[ps]:set(x, y, self:checkEntity(x, y, "block_move", ps, false, true))
end
end
--- Sets/gets a value from the map
......
......@@ -161,7 +161,7 @@ function _M:preUseTalent(ab, silent)
return false
end
else
if ab.power and self:getMana() < ab.power * (100 + self.fatigue) / 100 then
if ab.power and self:getPower() < ab.power then
game.logPlayer(self, "You do not have enough power to cast %s.", ab.name)
return false
end
......@@ -223,7 +223,7 @@ function _M:getTalentFullDescription(t)
else d[#d+1] = "#6fff83#Use mode: #00FF00#Activable"
end
if t.power or t.sustain_power then d[#d+1] = "#6fff83#Mana cost: #7fffd4#"..(t.power or t.sustain_power) end
if t.power or t.sustain_power then d[#d+1] = "#6fff83#Power cost: #7fffd4#"..(t.power or t.sustain_power) end
if self:getTalentRange(t) > 1 then d[#d+1] = "#6fff83#Range: #FFFFFF#"..self:getTalentRange(t)
else d[#d+1] = "#6fff83#Range: #FFFFFF#melee/personal"
end
......
......@@ -36,7 +36,10 @@ newBirthDescriptor{
name = "Destroyer",
desc =
{
"Boom!",
"Crashhhhh!",
},
talents = {
[ActorTalents.T_KICK]=1,
},
}
......@@ -47,4 +50,7 @@ newBirthDescriptor{
{
"Zshhhhhhhh!",
},
talents = {
[ActorTalents.T_ACID_SPRAY]=1,
},
}
......@@ -25,10 +25,36 @@ newTalent{
points = 1,
cooldown = 6,
power = 2,
range = 1,
action = function(self, t)
local tg = {type="hit", range=self:getTalentRange(t)}
local x, y, target = self:getTarget(tg)
if not x or not y or not target then return nil end
if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end
target:knockback(self.x, self.y, 2 + self:getDex())
return true
end,
info = function(self, t)
return "Kick!"
end,
}
newTalent{
name = "Acid Spray",
type = {"role/combat", 1},
points = 1,
cooldown = 6,
power = 2,
range = 6,
action = function(self, t)
local tg = {type="ball", range=self:getTalentRange(t), radius=1, talent=t}
local x, y = self:getTarget(tg)
if not x or not y then return nil end
self:project(tg, x, y, DamageType.ACID, 1 + self:getDex(), {type="acid"})
return true
end,
info = function(self, t)
return "Zshhhhhhhhh!"
end,
}
......@@ -205,6 +205,18 @@ function _M:move(x, y, force)
return moved
end
--- Get the "path string" for this actor
-- See Map:addPathString() for more info
function _M:getPathString()
local ps = self.open_door and "open_door=true;can_pass={" or "can_pass={"
for what, check in pairs(self.can_pass) do
ps = ps .. what.."="..check..","
end
ps = ps.."}"
print("[PATH STRING] for", self.name, " :=: ", ps)
return ps
end
--- Blink through walls
function _M:probabilityTravel(x, y, dist)
local dirx, diry = x - self.x, y - self.y
......
......@@ -301,7 +301,14 @@ function _M:changeLevel(lev, zone)
self:stopMusic()
end
-- Update the minimap
self:setupMiniMap()
-- Tell the map to use path strings to speed up path calculations
for uid, e in pairs(self.level.entities) do
self.level.map:addPathString(e:getPathString())
end
self.level.map:redisplay()
end
function _M:getPlayer()
......
......@@ -28,16 +28,22 @@ function _M:init(t, no_default)
end
function _M:block_move(x, y, e, act, couldpass)
-- Path strings
if not e then e = {}
elseif type(e) == "string" then
e = loadstring(e)()
end
-- Open doors
if self.door_opened and act then
game.level.map(x, y, engine.Map.TERRAIN, game.zone.grid_list.DOOR_OPEN)
if self.door_opened and e.open_door and act then
game.level.map(x, y, engine.Map.TERRAIN, game.zone.grid_list[self.door_opened])
return true
elseif self.door_opened and not couldpass then
elseif self.door_opened and e.open_door and not couldpass then
return true
end
-- Pass walls
if e and self.can_pass and e.can_pass then
if self.can_pass and e.can_pass then
for what, check in pairs(e.can_pass) do
if self.can_pass[what] and self.can_pass[what] <= check then return false end
end
......
......@@ -63,6 +63,7 @@ function _M:init(t, no_default)
t.color_b=t.color_b or 230
t.player = true
t.open_door = true
t.type = t.type or "humanoid"
t.subtype = t.subtype or "player"
t.faction = t.faction or "players"
......
......@@ -33,6 +33,7 @@ newEntity{
combat_armor = 1, combat_def = 1,
combat = { dam=5, atk=15, apr=7, dammod={str=0.6} },
max_life = resolvers.rngavg(100,120),
open_door = true,
rank = 2,
size_category = 3,
can_breath={water=1},
......
......@@ -37,6 +37,8 @@ newEntity{
resolvers.tmasteries{ ["technique/other"]=1, },
open_door = true,
blind_immune = 1,
see_invisible = 2,
undead = 1,
......
......@@ -36,6 +36,8 @@ newEntity{
rank = 2,
size_category = 4,
open_door = true,
autolevel = "warrior",
ai = "dumb_talented_simple", ai_state = { talent_in=5, },
energy = { mod=1.2 },
......
......@@ -34,6 +34,8 @@ newEntity{
rank = 2,
size_category = 3,
open_door = true,
resolvers.tmasteries{ ["technique/2hweapon"]=1, },
blind_immune = 1,
......
......@@ -34,6 +34,8 @@ newEntity{
rank = 2,
size_category = 3,
open_door = true,
autolevel = "warrior",
ai = "dumb_talented_simple", ai_state = { talent_in=3, },
energy = { mod=1 },
......
......@@ -39,6 +39,8 @@ newEntity{
resolvers.tmasteries{ ["technique/other"]=0.3, ["technique/2hweapon-offense"]=0.3, ["technique/2hweapon-cripple"]=0.3 },
open_door = true,
blind_immune = 1,
fear_immune = 1,
see_invisible = 2,
......
......@@ -39,6 +39,8 @@ newEntity{
rank = 2,
size_category = 3,
open_door = true,
autolevel = "rogue",
ai = "dumb_talented_simple", ai_state = { talent_in=5, },
energy = { mod=1 },
......
......@@ -41,6 +41,8 @@ newEntity{
energy = { mod=1 },
stats = { str=20, dex=8, mag=6, con=16 },
open_door = true,
resolvers.tmasteries{ ["technique/other"]=0.3 },
resists = { [DamageType.FIRE] = -50 },
......
......@@ -55,6 +55,8 @@ newEntity{
size_category = 3,
rank = 2,
open_door = true,
resolvers.tmasteries{ ["technique/other"]=0.5, ["spell/phantasm"]=0.8, },
resists = { [DamageType.COLD] = 80, [DamageType.NATURE] = 80, [DamageType.LIGHT] = -50, },
......
......@@ -45,6 +45,8 @@ newEntity{
rank = 2,
size_category = 3,
open_door = true,
resolvers.tmasteries{ ["technique/other"]=0.3, ["spell/air"]=0.3, ["spell/fire"]=0.3 },
resists = { [DamageType.COLD] = 80, [DamageType.FIRE] = 20, [DamageType.LIGHTNING] = 40, [DamageType.PHYSICAL] = 35, [DamageType.LIGHT] = -50, },
......
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