I'm looking at ways to incentivize leaving the base and exploring the world. For my idea to work, I'd need to know when a character is revealing portions of the map, rather than a radar dish. If I detect a player revealed a chunk, I'll give them a small reward.
It looks like the main event I'd have to use is on_chunk_charted, but that only gets me the surface, chunk, and force. The event description also says it fires when a chunk is re-charted, which isn't going to be something I want.
I think I've devised a workaround, but asking here in case there's a better/simpler solution.
To filter out re-charts, I'll need to keep a table of all the chunks that have been charted on that surface. For a chunk identifier, I'm thinking of serializing the chunk position (e.g. "X5Y3"). When a chunk is revealed, I check if it's already in the table, and if so, ignore the event. If it's not in the table, I add it to the table and then try to guess if a radar or a player charted it. (There's also an on_sector_scanned event, but it doesn't seem to add anything over what on_chunk_charted has.)
To filter out radar charts, I'll check the charted chunk's position and compare it to each player's character position (/32 to convert to chunk position). If a player is <= 2 chunks in the X or Y away (character sight radius is 2 chunks, right? any way to modify that?), I assume that player revealed it. Otherwise, I assume it was the radar. This isn't perfect as a radar might chart a chunk a player is moving towards, but I think crediting the player in these rare cases is acceptable.
I'll also need to exclude the chunks charted around the player at the start of the game, because those are freebies.
Is this plan feasible? I'm worried that the memory and search time cost of the charted chunk table could cause problems on big maps. My personal games haven't gone far beyond 100k chunks, but I've seen some screenshots of players with 1.7M chunks charted, and I have no idea how many chunks mods like Space Exploration will use.
Can I detect if a player (not a radar) revealed a chunk?
Can I detect if a player (not a radar) revealed a chunk?
Last edited by Solinya on Fri Dec 06, 2019 6:24 am, edited 1 time in total.
Re: Can I detect if a player (not a radar) revealed a chunk?
Maybe create an api request for returning the entity on chunk charted which revealed it.
Could be also handsome for detecting artillery revealed chunks.
Could be also handsome for detecting artillery revealed chunks.
Re: Can I detect if a player (not a radar) revealed a chunk?
on_sector_scanned might do the trick.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.
Re: Can I detect if a player (not a radar) revealed a chunk?
This event only fires for radar. I'm trying to find chunks charted by non-radar characters.
Booted up the game. Each time a radar scans a chunk it fires on_sector_scanned. Each time a radar scans a new chunk it fires on_sector_scanned and on_chunk_charted (in that order).
If I walk my character into the black unrevealed part of the map, it just fires on_chunk_charted. The event appears to fire right as I cross a chunk boundary, so my character's position will be within 2 chunks of the charted position. It seems like I'll still need to do the position calculation to determine if it was the player or the radar charting, since the charting entity is not part of the event, only the force.
I also feared on_chunk_charted would fire when I re-revealed a previously visible chunk (clearing the fog of war), but that's not the case. Unfortunately, I still need to guard against recharting since some factorio updates re-chart all chunks in the save game (like the landfill map color change). Pretty sure the map color changes in 0.18 will do the same.
So it looks like my prototyped algorithm will work, but it would be nice if I could find a way to avoid caching and looking up all the already-charted chunks. Is there another part of the API that would help with this?
Re: Can I detect if a player (not a radar) revealed a chunk?
Code: Select all
global.charted_chunks = {
"X1Y1" = true,
"X2Y1" = true,
"X3Y1" = true,
}
Re: Can I detect if a player (not a radar) revealed a chunk?
For things like this I would just use a 2d map,Solinya wrote: ↑Fri Dec 06, 2019 4:53 amTo filter out re-charts, I'll need to keep a table of all the chunks that have been charted on that surface. For a chunk identifier, I'm thinking of serializing the chunk position (e.g. "X5Y3"). When a chunk is revealed, I check if it's already in the table, and if so, ignore the event.
like global.chunks[chunk_x][chunk_y] = true
Re: Can I detect if a player (not a radar) revealed a chunk?
That would be a lot smarter. For some reason I was thinking I needed the lua equivalent of a hashset.
Re: Can I detect if a player (not a radar) revealed a chunk?
When a chunk is charted you can iterate over the players and see if they're at "charting distance". That would determine if a player charted the chunk vs a radar.
A local variable to the mod can be used to store how many times the event fired, and only give out rewards on the next tick based on last-tick's chunks charted and player info. Based on distance, only so many chunks can be charted by the player at a time. You'd have to know how large a chunk is and how far the player can see. If the player could only see one chunk away, then the most they could reveal in one tick is five chunks, by exactly leaving a corner at its very tip. I think the normal view reveals a max of 7 or 9 chunks at a time? Not sure.
Edit: A way to manage it, is every time the event fires, increment the times the event fired and store the chunk and current player positions. If the count is higher than the current players can see, set it to -1 and empty the chunk table and player table (only record and increment if the count is >= 0). On every tick, if the count is 0, do nothing. If it's -1, we know last chunk we saw to many, so set it back to 0. If it's positive, go over the players and hand out rewards to those who were at viewing distance to the chunks.
A local variable to the mod can be used to store how many times the event fired, and only give out rewards on the next tick based on last-tick's chunks charted and player info. Based on distance, only so many chunks can be charted by the player at a time. You'd have to know how large a chunk is and how far the player can see. If the player could only see one chunk away, then the most they could reveal in one tick is five chunks, by exactly leaving a corner at its very tip. I think the normal view reveals a max of 7 or 9 chunks at a time? Not sure.
Edit: A way to manage it, is every time the event fires, increment the times the event fired and store the chunk and current player positions. If the count is higher than the current players can see, set it to -1 and empty the chunk table and player table (only record and increment if the count is >= 0). On every tick, if the count is 0, do nothing. If it's -1, we know last chunk we saw to many, so set it back to 0. If it's positive, go over the players and hand out rewards to those who were at viewing distance to the chunks.
I have mods! I guess!
Link
Link
Re: Can I detect if a player (not a radar) revealed a chunk?
I got this working for multiple surfaces, but only a single player force. (My mod doesn't support multiple forces in general yet, so this would be OK for now.). Sharing my solution out in case someone searching for this later wants a starting point.
The trickier part was coming up with a save game migration to detect all chunks charted before my mod was applied. This seems to work and cover multiple surfaces (but again, not multiple forces).
I think multi-force support wouldn't be too bad. Would just need a fourth dimension to the charted chunk table and detecting all the player forces on migration. I just need to make other modifications to my mod before I can even consider supporting custom forces (and figuring out which ones are custom players vs custom enemy forces).
I also need to consider surface deletion and clearing out the charted cache for that surface when that happens, but I think I know how to do that.
Code: Select all
function onChunkCharted(event)
if event.force.name ~= 'player' then
return
end
local x = event.position.x
local y = event.position.y
-- Detect if this is a new surface or row
if global.chartedChunks[event.surface_index] == nil then
global.chartedChunks[event.surface_index] = {}
end
if global.chartedChunks[event.surface_index][x] == nil then
global.chartedChunks[event.surface_index][x] = {}
end
-- Check if the chunk has already been charted.
if global.chartedChunks[event.surface_index][x][y] then
return
else
global.chartedChunks[event.surface_index][x][y] = true
end
-- Exclude the starting chunks (approx 15 x 15 area).
if (x >= -7 and x <= 7 and y >= -7 and y <= 7) then
return
end
-- Reward any players in range 3 (players have vision of 2 chunks + allow 1 grace chunk for if multiple players are exploring together)
for _, player in pairs(game.players) do
local player_chunk_x = player.position.x / 32
local player_chunk_y = player.position.y / 32
if x - player_chunk_x >= -3 and x - player_chunk_x <= 3 and y - player_chunk_y >= -3 and y - player_chunk_y <= 3 then
-- Reward code omitted for brevity
end
end
end
Code: Select all
local force = game.forces["player"]
for _, surface in pairs(game.surfaces) do
local surfaceIndex = surface.index
global.chartedChunks[surfaceIndex] = {}
for chunk in surface.get_chunks() do
if global.chartedChunks[surfaceIndex][chunk.x] == nil then
global.chartedChunks[surfaceIndex][chunk.x] = {}
end
if force.is_chunk_charted(surface, chunk) then
global.chartedChunks[surfaceIndex][chunk.x][chunk.y] = true
end
end
end
I also need to consider surface deletion and clearing out the charted cache for that surface when that happens, but I think I know how to do that.