Try it out. Just set some Units' SightDistance to 0 and try a test game with one AI player.
Code: Select all
function gadget:GetInfo()
return {
name = "Alternative LOS Handler",
desc = "Replaces Spring's LOS system with a cell-based handler.",
author = "Argh",
date = "December 14th, 2008",
license = "Public Domain, or the least-restrictive rights of your country of residence",
layer = 1,
enabled = true,
}
end
if (gadgetHandler:IsSyncedCode()) then
--------------------------------------------------------------------------------
-- SYNCED
--------------------------------------------------------------------------------
---------------------------------------------------------------------------------VARIABLE DECLARATIONS
local LOSTable = {}
local LOSList = {}
local LOS_SectorSize = 1024
local i,j,count = 0,0,0
local teamTable0 = {}
local teamTable1 = {}
local unitID
local MaxSizeX = Game.mapSizeX
local MaxSizeZ = Game.mapSizeZ
local startX = 0 - LOS_SectorSize
local startZ = 0 - LOS_SectorSize
local finishX = MaxSizeX + LOS_SectorSize
local finishZ = MaxSizeZ + LOS_SectorSize
local gaiaTeam = Spring.GetGaiaTeamID()
local unitTable
function gadget:Initialize()
end
function unitCreated(unitID, unitDefID, teamID)
Spring.SetUnitLosState(unitID, teamID, { los = true})
Spring.SetUnitLosMask(unitID, teamID, { los = true})
end
function gadget:GameFrame(f)
if f % 16 < 0.1 then
for _,unitID in ipairs (Spring.GetAllUnits()) do
local lengthTable = Spring.GetTeamList()
local length = # lengthTable
for i = -1,length,1 do
--Spring.Echo(i)
if Spring.GetTeamInfo(i) ~= nil then
local team = Spring.GetUnitTeam(unitID)
if team ~= gaiaTeam and team ~= i then
Spring.SetUnitLosMask(unitID, i, { los =false})
Spring.SetUnitLosState(unitID, i, { los = false})
end
if team == gaiaTeam or team == i then
Spring.SetUnitLosMask(unitID, i, { los = false})
Spring.SetUnitLosState(unitID, i, { los = true})
Spring.SetUnitLosMask(unitID, i, { los = true})
end
end
end
end
for squareX = startX,finishX,LOS_SectorSize do
for squareZ = startX,finishX,LOS_SectorSize do
count = count + 1
unitTable = Spring.GetUnitsInRectangle(squareX,squareZ,squareX + LOS_SectorSize, squareZ + LOS_SectorSize)
if unitTable[1] ~= nil then
for _,unitID in ipairs (unitTable) do
local team = Spring.GetUnitTeam(unitID)
if team == 0 then
table.insert(teamTable0,{squarex = squareX - 1, squarez = squareZ - 1}) -- UPPER LEFT
table.insert(teamTable0,{squarex = squareX, squarez = squareZ - 1}) -- UPPER CENTER
table.insert(teamTable0,{squarex = squareX + 1, squarez = squareZ - 1}) -- UPPER RIGHT
table.insert(teamTable0,{squarex = squareX - 1, squarez = squareZ}) -- CENTER LEFT
table.insert(teamTable0,{squarex = squareX, squarez = squareZ}) -- CENTER
table.insert(teamTable0,{squarex = squareX + 1, squarez = squareZ}) -- CENTER RIGHT
table.insert(teamTable0,{squarex = squareX - 1, squarez = squareZ + 1}) -- LOWER LEFT
table.insert(teamTable0,{squarex = squareX, squarez = squareZ + 1}) -- LOWER CENTER
table.insert(teamTable0,{squarex = squareX + 1, squarez = squareZ + 1}) -- LOWER RIGHT
end
if team == 1 then
table.insert(teamTable1,{squarex = squareX - 1, squarez = squareZ - 1}) -- UPPER LEFT
table.insert(teamTable1,{squarex = squareX, squarez = squareZ - 1}) -- UPPER CENTER
table.insert(teamTable1,{squarex = squareX + 1, squarez = squareZ - 1}) -- UPPER RIGHT
table.insert(teamTable1,{squarex = squareX - 1, squarez = squareZ}) -- CENTER LEFT
table.insert(teamTable1,{squarex = squareX, squarez = squareZ}) -- CENTER
table.insert(teamTable1,{squarex = squareX + 1, squarez = squareZ}) -- CENTER RIGHT
table.insert(teamTable1,{squarex = squareX - 1, squarez = squareZ + 1}) -- LOWER LEFT
table.insert(teamTable1,{squarex = squareX, squarez = squareZ + 1}) -- LOWER CENTER
table.insert(teamTable1,{squarex = squareX + 1, squarez = squareZ + 1}) -- LOWER RIGHT
end
end
end
end
end
if # teamTable0 ~= nil then
for a,b in ipairs (teamTable0) do
if b.squarex ~= nil then
--Spring.Echo("found")
local sightTable = Spring.GetUnitsInRectangle(b.squarex,b.squarez, b.squarex + LOS_SectorSize, b.squarez + LOS_SectorSize)
for _,unitID in ipairs (sightTable) do
local team = Spring.GetUnitTeam(unitID)
if Spring.GetUnitIsCloaked(unitID) == false and team ~= 0 and team ~= gaiaTeam then
Spring.SetUnitLosState(unitID, 0, { los = true})
Spring.SetUnitLosMask(unitID, 0, { los = true})
end
end
end
table.remove(teamTable0,a)
end
end
if # teamTable1 ~= nil then
for a,b in ipairs (teamTable1) do
if b.squarex ~= nil then
--Spring.Echo("found")
local sightTable = Spring.GetUnitsInRectangle(b.squarex,b.squarez, b.squarex + LOS_SectorSize, b.squarez + LOS_SectorSize)
for _,unitID in ipairs (sightTable) do
local team = Spring.GetUnitTeam(unitID)
if Spring.GetUnitIsCloaked(unitID) == false and team ~= 1 and team ~= gaiaTeam then
Spring.SetUnitLosState(unitID, 1, { los = true})
Spring.SetUnitLosMask(unitID, 1, { los = true})
end
end
end
table.remove(teamTable1,a)
end
end
end
end
-------------------------------------------------------------------------------------------END SYNC
end
Then it's searching for any units on a given Team inhabiting that sector. If it finds any, it stores that information in a table, and then "populates" the surrounding sectors.
If a given Team returns a list of valid sectors, then a search is performed, on a per-sector basis, searching for Units and changing their state. If a given Unit is not currently visible, then they become visible.
All of that works. Gaia stuff remains visible. It's fast, and not frame-bound like the current LOS system. It uses a cell-based system, instead a unit-bound system- what counts is not unit positions, or terrain, but whether a given cell is inhabited.
To make it really worth using, though, a lot of suck needs to get removed, and I'm posting this up in case anybody is interested in this topic and would like to help make it run better.
1. I've eliminated overlapping searches, which are obviously very costly, but now I have to do a bunch of smaller searches, which is not efficient, especially in Lua. I'd like to know if there's a way to convert the grid into a smaller number of efficient search zones, to reduce the number of searches as much as possible.
2. There are all kinds of glitchy behaviors that occur if I set the LOS Mask / State like this. CEGs become invisible, because AirLOS expects LOS entries in the LOS Handler, and if there aren't any, it can't get the job done.
However, the fact that I can see a few CEGs almost makes it look like there's some minimal presence in the LOS Handler, which doesn't make any sense at all.
I'm not sure how to deal with this. We can't modify the state of CEGs, get their existence, turn their visibility on / off, etc., so I'm kinda stuck on that one at the moment.
Lastly, the current LOSHandler keeps trying to change the LOS status of Units, unless the Mask is set. I'd like a way to turn the LOSHandler completely off, or just restrict it to CEGs, it's very annoying to have to set the masks to keep my Units from blinking out constantly.
3. There's the obvious accuracy discrepancy between zones based on their size- i.e., the farther the Unit gets from the center of a zone, the more obvious it is that the LOS is not bound to the Unit at all. Until I have a fast method of consolidating searches into the lowest-possible number of areas, it's not very practical to think about making the zones smaller to avoid this.
All of these errors / issues aside, this is a really different approach to LOS for this engine, and one that I think is worth exploring further, as it's already quite fast and ignores terrain. If anybody has some practical ideas to address these issues, your opinion is welcome, or you can take the code as-is.