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

Merge branch 'master' of git.net-core.org:darkgod/t-engine4

parents f8d1b377 8e3fba36
No related branches found
No related tags found
No related merge requests found
......@@ -123,7 +123,7 @@ function _M:doAI()
local target_pos = self.ai_target.actor and self.fov and self.fov.actors and self.fov.actors[self.ai_target.actor]
if target_pos then
local tx, ty = self:aiSeeTargetPos(self.ai_target.actor)
self.ai_state.target_last_seen = {x=tx, y=ty, turn=self.fov_last_turn}
self.ai_state.target_last_seen=table.merge(self.ai_state.target_last_seen or {}, {x=tx, y=ty, turn=self.fov_last_turn}) -- Merge to keep obfuscation data
end
return self:runAI(self.ai)
......@@ -148,39 +148,43 @@ function _M:setTarget(target, last_seen)
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}
self.ai_state.target_last_seen = {x=target_pos.x, y=target_pos.y, turn=game.turn}
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
--- 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)
-- it will return estimates, to throw the AI a bit off
-- it will return estimates, to throw the AI a bit off (up to 10 tiles error)
-- @param target the target we are tracking
-- @return x, y coords to move/cast to
function _M:aiSeeTargetPos(target)
if not target then return self.x, self.y end
local tx, ty = target.x, target.y
local LSeen = self.ai_state.target_last_seen
if type(LSeen) ~= "table" then return tx, ty end
local spread = 0
LSeen.GCache_turn = LSeen.GCache_turn or game.turn -- Guess Cache turn to update position guess (so it's consistent during a turn)
LSeen.GCknown_turn = LSeen.GCknown_turn or game.turn -- 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)
-- Adding some type-safety checks, but this isn't fixing the source of the errors
if target == self.ai_target.actor and self.ai_state.target_last_seen and type(self.ai_state.target_last_seen) == "table" and self.ai_state.target_last_seen.x and not self:hasLOS(self.ai_state.target_last_seen.x, self.ai_state.target_last_seen.y) then
tx, ty = self.ai_state.target_last_seen.x, self.ai_state.target_last_seen.y
spread = spread + math.floor((game.turn - (self.ai_state.target_last_seen.turn or game.turn)) / (game.energy_to_act / game.energy_per_tick))
end
-- Check if target is currently seen
local see, chance = self:canSee(target)
-- Compute the maximum spread if we need to obfuscate
local spread = see and 0 or math.floor((100 - chance) / 10)
-- We don't know the exact position, so we obfuscate
if spread > 0 then
tx = tx + rng.range(0, spread * 2) - spread
ty = ty + rng.range(0, spread * 2) - spread
return util.bound(tx, 0, game.level.map.w - 1), util.bound(ty, 0, game.level.map.h - 1)
-- Directly seeing it, no spread at all
if see and self:hasLOS(target.x, target.y) then -- canSee doesn't check LOS
LSeen.GCache_x, LSeen.GCache_y = nil, nil
LSeen.GCknown_turn = game.turn
LSeen.GCache_turn = game.turn
else
return util.bound(tx, 0, game.level.map.w - 1), util.bound(ty, 0, game.level.map.h - 1)
if target == self.ai_target.actor and (LSeen.GCache_turn or 0) + 10 <= game.turn and LSeen.x then
spread = spread + math.min(10, math.floor((game.turn - (LSeen.GCknown_turn or game.turn)) / (game.energy_to_act / game.energy_per_tick))) -- Limit spread to 10 tiles
tx, ty = util.bound(tx + rng.range(-spread, spread), 0, game.level.map.w - 1), util.bound(ty + rng.range(-spread, spread), 0, game.level.map.h - 1)
-- Inertial average with last guess: can specify another method here to make the targeting position less random
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)
end
LSeen.GCache_x, LSeen.GCache_y = tx, ty
LSeen.GCache_turn = game.turn
end
if LSeen.GCache_x then return LSeen.GCache_x, LSeen.GCache_y end
end
end
return tx, ty -- Fall through to correct coords
end
\ No newline at end of file
......@@ -355,6 +355,7 @@ function _M:generateRandart(data)
-- Add ego properties
-----------------------------------------------------------
local nb_egos = data.egos or 3
local gr_egos = data.greater_egos_bias or math.floor(nb_egos*2/3) -- 2/3 greater egos by default
if o.egos and nb_egos > 0 then
local legos = {}
local been_greater = 0
......@@ -365,7 +366,7 @@ function _M:generateRandart(data)
local egos = rng.table(legos)
local list = {}
local filter = nil
if rng.percent(lev) and been_greater < 2 then been_greater = been_greater + 1 filter = function(e) return e.greater_ego end end
if rng.percent(100*lev/(lev+50)) and been_greater < gr_egos then been_greater = been_greater + 1 filter = function(e) return e.greater_ego end end --RE Phase out (but don't eliminate) lesser egos with level
for z = 1, #egos do list[#list+1] = egos[z].e end
local ef = self:egoFilter(game.zone, game.level, "object", "randartego", o, {special=filter, forbid_power_source=data.forbid_power_source, power_source=data.power_source}, list, {})
......@@ -1266,6 +1267,32 @@ function _M:entityFilterPost(zone, level, type, e, filter)
end
if data.user_post then data.user_post(b, data) end
end,
post = function(b, data)
if data.level <= 20 then
b.inc_damage = b.inc_damage or {}
b.inc_damage.all = (b.inc_damage.all or 0) - 40 * (20 - data.level + 1) / 20
end
-- Drop
for i = 1, data.nb_rares do -- generate rares as weak (1 ego) randarts
local fil = {lev=lev, egos=1, greater_egos_bias = 0, forbid_power_source=b.not_power_source,
base_filter = {no_tome_drops=true, ego_filter={keep_egos=true, ego_chance=-1000},
special=function(e)
return (not e.unique and e.randart_able) and (not e.material_level or e.material_level >= 1) and true or false
end}
}
local o = game.state:generateRandart(fil,nil, true)
if o then
-- print("[entityFilterPost]: Generated random object for", tostring(b.name)) --RE
o.unique, o.randart, o.rare = nil, nil, true
b:addObject(b.INVEN_INVEN, o)
game.zone:addEntity(game.level, o, "object")
else
print("[entityFilterPost]: Failed to generate random object for", tostring(b.name))
end
end
if data.user_post then data.user_post(b, data) end
end,
}
e = self:createRandomBoss(e, table.merge(base, filter.random_elite, true))
end
......@@ -1273,6 +1300,8 @@ function _M:entityFilterPost(zone, level, type, e, filter)
if filter.random_object and not e.unique and e.randart_able then
local data = _G.type(filter.random_object) == "table" and filter.random_object or {}
local lev = math.max(1, game.zone:level_adjust_level(game.level, game.zone, "object"))
print("[entityFilterPost]: Generating obsolete random_object")
print(debug.traceback())
e = game.state:generateRandart{
lev = lev,
egos = 0,
......
......@@ -409,14 +409,28 @@ function _M:tooltip(x, y, seen_by)
local str = mod.class.Actor.tooltip(self, x, y, seen_by)
if not str then return end
local killed = game:getPlayer(true).all_kills and (game:getPlayer(true).all_kills[self.name] or 0) or 0
local target = self.ai_target.actor
str:add(
true,
("Killed by you: %s"):format(killed), true,
"Target: ", self.ai_target.actor and self.ai_target.actor.name or "none"
"Target: ", target and target.name or "none"
)
if config.settings.cheat then str:add(true, "UID: "..self.uid, true, self.image) end
-- Give hints to stealthed/invisible players about where the NPC is looking (if they have LOS)
if target == game.player and (game.player:attr("stealth") or game.player:attr("invisible")) and game.player:hasLOS(self.x, self.y) then
local tx, ty = self:aiSeeTargetPos(self.ai_target.actor)
local dx, dy = tx - self.ai_target.actor.x, ty - self.ai_target.actor.y
local offset = engine.Map:compassDirection(dx, dy)
if offset then
str:add(" looking " ..offset)
if config.settings.cheat then str:add((" (%+d, %+d)"):format(dx, dy)) end
else
str:add(" looking at you.")
end
end
if config.settings.cheat then
str:add(true, "UID: "..self.uid, true, self.image)
end
return str
end
......
......@@ -154,6 +154,14 @@ function _M:onEnterLevel(zone, level)
end
end
for i, eff_id in ipairs(effs) do self:removeEffect(eff_id) end
-- Clear existing player created effects on the map
for i, eff in ipairs(level.map.effects) do
if eff.src and eff.src == game.player then
eff.duration = 0
eff.grids = {}
end
end
end
function _M:onEnterLevelEnd(zone, level)
......
......@@ -648,6 +648,7 @@ newTalent{
if not x or not y then return nil end
local _ _, x, y = self:canProject(tg, x, y)
if game.level.map(x, y, Map.TRAP) then game.logPlayer(self, "You somehow fail to set the trap.") return nil end
if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then game.logPlayer(self, "You somehow fail to set the trap.") return nil end
local dam = t.getDamage(self, t)
-- Need to pass the actor in to the triggered function for the apply_power to work correctly
......
......@@ -358,7 +358,7 @@ newTalent{
local x, y = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true})
if not x then
game.logPlayer(self, "Not enough space to summon!")
return
if i == 1 then return else break end
end
local NPC = require "mod.class.NPC"
......
......@@ -62,6 +62,7 @@ newTalent{
if not x or not y then return nil end
local _ _, x, y = self:canProject(tg, x, y)
if game.level.map(x, y, Map.TRAP) then game.logPlayer(self, "You somehow fail to set the aether beam.") return nil end
if game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") then game.logPlayer(self, "You somehow fail to set the aether beam.") return nil end
local t = basetrap(self, t, x, y, 44, {
type = "aether", name = "aether beam", color=colors.VIOLET, image = "trap/trap_glyph_explosion_01_64.png",
......
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