Can I detect if a player (not a radar) revealed a chunk?

Place to get help with not working mods / modding interface.
Post Reply
Solinya
Long Handed Inserter
Long Handed Inserter
Posts: 79
Joined: Sun Mar 17, 2019 10:39 pm
Contact:

Can I detect if a player (not a radar) revealed a chunk?

Post by Solinya »

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.
Last edited by Solinya on Fri Dec 06, 2019 6:24 am, edited 1 time in total.

User avatar
steinio
Smart Inserter
Smart Inserter
Posts: 2633
Joined: Sat Mar 12, 2016 4:19 pm
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by steinio »

Maybe create an api request for returning the entity on chunk charted which revealed it.

Could be also handsome for detecting artillery revealed chunks.
Image

Transport Belt Repair Man

View unread Posts

Bilka
Factorio Staff
Factorio Staff
Posts: 3139
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by Bilka »

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.

Solinya
Long Handed Inserter
Long Handed Inserter
Posts: 79
Joined: Sun Mar 17, 2019 10:39 pm
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by Solinya »

Bilka wrote:
Fri Dec 06, 2019 5:47 am
on_sector_scanned might do the trick.
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?

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by DaveMcW »

Solinya wrote:
Fri Dec 06, 2019 4:53 am
I'm worried that the memory and search time cost of the charted chunk table could cause problems on big maps.

Code: Select all

global.charted_chunks = {
 "X1Y1" = true,
 "X2Y1" = true,
 "X3Y1" = true,
}
This has O(1) lookup time, you don't need to worry about search time. Memory cost is low too. You could save even more memory by converting the string to an integer (y * 65536 + x).

User avatar
Klonan
Factorio Staff
Factorio Staff
Posts: 5150
Joined: Sun Jan 11, 2015 2:09 pm
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by Klonan »

Solinya wrote:
Fri Dec 06, 2019 4:53 am
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.
For things like this I would just use a 2d map,
like global.chunks[chunk_x][chunk_y] = true

Solinya
Long Handed Inserter
Long Handed Inserter
Posts: 79
Joined: Sun Mar 17, 2019 10:39 pm
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by Solinya »

That would be a lot smarter. For some reason I was thinking I needed the lua equivalent of a hashset.

Honktown
Smart Inserter
Smart Inserter
Posts: 1026
Joined: Thu Oct 03, 2019 7:10 am
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by Honktown »

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.
I have mods! I guess!
Link

Solinya
Long Handed Inserter
Long Handed Inserter
Posts: 79
Joined: Sun Mar 17, 2019 10:39 pm
Contact:

Re: Can I detect if a player (not a radar) revealed a chunk?

Post by Solinya »

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.

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
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).

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 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.

Post Reply

Return to “Modding help”