Skip to content
Snippets Groups Projects
Commit df5c1441 authored by DarkGod's avatar DarkGod
Browse files

Merge branch 'NPCTargetPassingFixes' into 'master'

Npc target passing fixes

Hostile NPC's will not randomly agro other NPC's at long ranges.
NPC's will alert each other to the player more freely (and at greater range based on game Difficulty), so that they don't ignore the player when their allies are killed right next to them.
Player summons will not target Cold Flame Traps when they are damaged.
parents 8e01ac32 823dfdd3
No related branches found
No related tags found
No related merge requests found
......@@ -147,12 +147,12 @@ function _M:setTarget(target, last_seen)
if last_seen then
self.ai_state.target_last_seen = last_seen
else
local target_pos = target and self.fov and self.fov.actors and self.fov.actors[self.ai_target.actor] or {x=self.x, y=self.y}
local target_pos = target and (self.fov and self.fov.actors and self.fov.actors[target] or {x=target.x, y=target.y}) or {x=self.x, y=self.y} --No FOV: aiSeeTargetPos will assume new target position is ~3 turns old by default (AI_LOCATION_GUESS_ERROR)
self.ai_state.target_last_seen=table.merge(self.ai_state.target_last_seen or {}, {x=target_pos.x, y=target_pos.y, turn=game.turn}) -- Merge to keep obfuscation data
end
end
_M.AI_SEEN_CACHE_DELAY = 10 * 100
_M.AI_LOCATION_GUESS_ERROR = 3 -- Start position guess errors at ~3 grids
--- Returns the seen coords of the target
-- This will usually return the exact coords, but if the target is only partially visible (or not at all)
......@@ -167,12 +167,12 @@ function _M:aiSeeTargetPos(target)
local spread = 0
-- Guess Cache turn to update position guess (so it's consistent during a turn)
-- Start at -1000 to make sure ti gets run the first time.
LSeen.GCache_turn = LSeen.GCache_turn or game.turn - self.AI_SEEN_CACHE_DELAY
-- Set last cache turn before game turn to make sure it gets run the first time.
LSeen.GCache_turn = LSeen.GCache_turn or game.turn - self.AI_LOCATION_GUESS_ERROR * game.energy_to_act/game.energy_per_tick
-- Guess Cache known turn for spread calculation (self.ai_state.target_last_seen.turn
-- can't be used because it's needed by FOV code)
LSeen.GCknown_turn = LSeen.GCknown_turn or game.turn - self.AI_SEEN_CACHE_DELAY
LSeen.GCknown_turn = LSeen.GCknown_turn or game.turn - self.AI_LOCATION_GUESS_ERROR * game.energy_to_act/game.energy_per_tick
-- Check if target is currently seen
local see, chance = self:canSee(target)
......@@ -188,6 +188,10 @@ function _M:aiSeeTargetPos(target)
if LSeen.GCache_x then -- update guess with new random position. Could use util.findFreeGrid here at cost of speed
tx = math.floor(LSeen.GCache_x + (tx-LSeen.GCache_x)/2)
ty = math.floor(LSeen.GCache_y + (ty-LSeen.GCache_y)/2)
if not target:canMove(tx, ty, true) then -- find a reasonable spot if target can't be at that position
local nx, ny = util.findFreeGrid(tx, ty, spread, false)
if nx then tx, ty = nx, ny end
end
end
LSeen.GCache_x, LSeen.GCache_y = tx, ty
LSeen.GCache_turn = game.turn
......
......@@ -220,7 +220,7 @@ function _M:seen_by(who)
if self:hasEffect(self.EFF_VAULTED) and who and game.party:hasMember(who) then self:removeEffect(self.EFF_VAULTED, true, true) end
-- Check if we can pass target
if self.dont_pass_target then return end
if self.dont_pass_target then return end -- This means that ghosts can alert other NPC's but not vice versa ;)
if not who.ai_target then return end
if not who.ai_target.actor then return end
if not who.ai_target.actor.x then return end
......@@ -246,14 +246,20 @@ function _M:seen_by(who)
end
return
end
-- Only trust the ally if they can actually see the target
if not who:canSee(who.ai_target.actor) then return end
if who.ai_state and who.ai_state.target_last_seen and type(who.ai_state.target_last_seen) == "table" then
-- Don't believe allies if they saw the target far, far away
if who.ai_state.target_last_seen.x and who.ai_state.target_last_seen.y and core.fov.distance(self.x, self.y, who.ai_state.target_last_seen.x, who.ai_state.target_last_seen.y) > self.sight then return end
-- Don't believe allies if they think the target is too far (away based on distance to ally plus ally to hostile estimate (1.3 * sight range, usually))
local tx, ty = who:aiSeeTargetPos(who.ai_target.actor)
local distallyhostile = core.fov.distance(who.x, who.y, tx, ty) or 100
local range_factor = 1.2 + (tonumber(game.difficulty) or 1)/20 -- NPC's pass targets more freely at higher difficulties
if distallyhostile + core.fov.distance(self.x, self.y, who.x, who.y) > math.min(10, math.max(self.sight, self.infravision or 0, self.heightened_senses or 0, self.sense_radius or 0))*range_factor then return end
-- Don't believe allies if they saw the target over 10 turns ago
if (game.turn - (who.ai_state.target_last_seen.turn or game.turn)) / (game.energy_to_act / game.energy_per_tick) > 10 then return end
end
-- And only trust the ally if they can actually see the target
if not who:canSee(who.ai_target.actor) then return end
self:setTarget(who.ai_target.actor, who.ai_state.target_last_seen)
print("[TARGET] Passing target", self.name, "from", who.name, "to", who.ai_target.actor.name)
......
......@@ -255,6 +255,7 @@ newEntity{ base = "TRAP_COMPLEX",
type = "trap", subtype = "arcane",
combatSpellpower = function(self) return self.dam end,
getTarget = function(self) return self.x, self.y end,
targetable = false,
dam = self.dam,
x = x, y = y,
faction = self.faction,
......
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