 65df153947
			
		
	
	65df153947
	
	
	
		
			
			* Overhauls control.lua template
- Adds buffering of received free-samples items.
  - Players with a full inventory will receive new items as space becomes
    available.
  - Players who do not yet exist in the world will receive all free samples
    upon joining.
- When receiving new technologies, announce the technology BEFORE marking
  it as researched so that it appears before its free samples in the log.
- If receiving a half-stack of free samples, use math.ceil on the division
  in case a mod uses an odd number as a stack size... or a stack size of 1
- Handle free_samples logic in the Lua side rather than as part of a Jinja
  template.  This makes this hopefully more adaptable to a future setup
  where all the rando information is shipped as startup settings.
* Apparently, I'm supposed to give myself credit.  Or something.
		
	
		
			
				
	
	
		
			215 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| require "lib"
 | |
| require "util"
 | |
| 
 | |
| FREE_SAMPLES = {{ free_samples }}
 | |
| --SUPPRESS_INVENTORY_EVENTS = false
 | |
| 
 | |
| -- Initialize force data, either from it being created or already being part of the game when the mod was added.
 | |
| function on_force_created(event)
 | |
|     game.forces[event.force].research_queue_enabled = true
 | |
|     local data = {}
 | |
|     if FREE_SAMPLES ~= 0 then
 | |
|         data['earned_samples'] = {
 | |
|             ["burner-mining-drill"] = 19,
 | |
|             ["stone-furnace"] = 19
 | |
|         }
 | |
|     end
 | |
|     global.forcedata[event.force] = data
 | |
| end
 | |
| script.on_event(defines.events.on_force_created, on_force_created)
 | |
| 
 | |
| -- Destroy force data.  This doesn't appear to be currently possible with the Factorio API, but here for completeness.
 | |
| function on_force_destroyed(event)
 | |
|     global.forcedata[event.force] = nil
 | |
| end
 | |
| 
 | |
| -- Initialize player data, either from them joining the game or them already being part of the game when the mod was
 | |
| -- added.`
 | |
| function on_player_created(event)
 | |
|     local player = game.players[event.player_index]
 | |
|     -- FIXME: This (probably) fires before any other mod has a chance to change the player's force
 | |
|     -- For now, they will (probably) always be on the 'player' force when this event fires.
 | |
|     local data = {}
 | |
|     if FREE_SAMPLES ~= 0 then
 | |
|         data['pending_samples'] = table.deepcopy(global.forcedata[player.force.name]['earned_samples'])
 | |
|     end
 | |
|     global.playerdata[player.index] = data
 | |
|     update_player(player.index)  -- Attempt to send pending free samples, if relevant.
 | |
| end
 | |
| script.on_event(defines.events.on_player_created, on_player_created)
 | |
| 
 | |
| function on_player_removed(event)
 | |
|     global.playerdata[event.player_index] = nil
 | |
| end
 | |
| script.on_event(defines.events.on_player_removed, on_player_removed)
 | |
| 
 | |
| -- Updates a player, attempting to send them any pending samples (if relevant)
 | |
| function update_player(index)
 | |
|     if FREE_SAMPLES == 0 then  -- This is effectively a noop
 | |
|         return
 | |
|     end
 | |
|     local player = game.players[index]
 | |
|     if not player or not player.valid then     -- Do nothing if we reference an invalid player somehow
 | |
|         return
 | |
|     end
 | |
|     local character = player.character or player.cutscene_character
 | |
|     if not character or not character.valid then
 | |
|         return
 | |
|     end
 | |
|     local data = global.playerdata[index]
 | |
|     local samples = data['pending_samples']
 | |
|     local sent
 | |
|     --player.print(serpent.block(data['pending_samples']))
 | |
|     local stack = {}
 | |
|     --SUPPRESS_INVENTORY_EVENTS = true
 | |
|     for name, count in pairs(samples) do
 | |
|         stack.name = name
 | |
|         stack.count = count
 | |
|         if character.can_insert(stack) then
 | |
|             sent = character.insert(stack)
 | |
|         else
 | |
|             sent = 0
 | |
|         end
 | |
|         if sent > 0 then
 | |
|             player.print("Received " .. sent .. "x [item=" .. name .. "]")
 | |
|             data.suppress_full_inventory_message = false
 | |
|         end
 | |
|         if sent ~= count then               -- Couldn't full send.
 | |
|             if not data.suppress_full_inventory_message then
 | |
|                 player.print("Additional items will be sent when inventory space is available.", {r=1, g=1, b=0.25})
 | |
|             end
 | |
|             data.suppress_full_inventory_message = true -- Avoid spamming them with repeated full inventory messages.
 | |
|             samples[name] = count - sent    -- Buffer the remaining items
 | |
|             break                           -- Stop trying to send other things
 | |
|         else
 | |
|             samples[name] = nil             -- Remove from the list
 | |
|         end
 | |
|     end
 | |
|     --SUPPRESS_INVENTORY_EVENTS = false
 | |
| end
 | |
| 
 | |
| -- Update players upon them connecting, since updates while they're offline are suppressed.
 | |
| script.on_event(defines.events.on_player_joined_game, function(event) update_player(event.player_index) end)
 | |
| 
 | |
| function update_player_event(event)
 | |
|     --if not SUPPRESS_INVENTORY_EVENTS then
 | |
|     update_player(event.player_index)
 | |
|     --end
 | |
| end
 | |
| 
 | |
| if FREE_SAMPLES then
 | |
|     script.on_event(defines.events.on_player_main_inventory_changed, update_player_event)
 | |
| end
 | |
| 
 | |
| function add_samples(force, name, count)
 | |
|     local function add_to_table(t)
 | |
|         t[name] = (t[name] or 0) + count
 | |
|     end
 | |
|     -- Add to global table of earned samples for future new players
 | |
|     add_to_table(global.forcedata[force.name]['earned_samples'])
 | |
|     -- Add to existing players
 | |
|     for _, player in pairs(force.players) do
 | |
|         add_to_table(global.playerdata[player.index]['pending_samples'])
 | |
|         update_player(player.index)
 | |
|     end
 | |
| end
 | |
| 
 | |
| script.on_init(function()
 | |
|     global.forcedata = {}
 | |
|     global.playerdata = {}
 | |
|     -- Fire dummy events for all currently existing forces.
 | |
|     local e = {}
 | |
|     for name, _ in pairs(game.forces) do
 | |
|         e.force = name
 | |
|         on_force_created(e)
 | |
|     end
 | |
|     e.force = nil
 | |
| 
 | |
|     -- Fire dummy events for all currently existing players.
 | |
|     for index, _ in pairs(game.players) do
 | |
|         e.player_index = index
 | |
|         on_player_created(e)
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- for testing
 | |
| script.on_event(defines.events.on_tick, function(event)
 | |
|     if event.tick%600 == 0 then
 | |
|         dumpTech(game.forces["player"])
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- hook into researches done
 | |
| script.on_event(defines.events.on_research_finished, function(event)
 | |
|     local technology = event.research
 | |
|     dumpTech(technology.force)
 | |
|     if FREE_SAMPLES == 0 then
 | |
|         return  -- Nothing else to do
 | |
|     end
 | |
|     if not technology.effects then
 | |
|         return  -- No technology effects, so nothing to do.
 | |
|     end
 | |
|     for _, effect in pairs(technology.effects) do
 | |
|         if effect.type == "unlock-recipe" then
 | |
|             local recipe = game.recipe_prototypes[effect.recipe]
 | |
|             for _, result in pairs(recipe.products) do
 | |
|                 if result.type == "item" and result.amount then
 | |
|                     local name = result.name
 | |
|                     local count
 | |
|                     if FREE_SAMPLES == 1 then
 | |
|                         count = result.amount
 | |
|                     else
 | |
|                         count = get_any_stack_size(result.name)
 | |
|                         if FREE_SAMPLES == 2 then
 | |
|                             count = math.ceil(count / 2)
 | |
|                         end
 | |
|                     end
 | |
|                     add_samples(technology.force, name, count)
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|     end
 | |
| end)
 | |
| 
 | |
| function dumpTech(force)
 | |
|     local research_done = {}
 | |
|     local data_collection = {["research_done"] = research_done}
 | |
| 
 | |
|     for tech_name, tech in pairs(force.technologies) do
 | |
|         if tech.researched and string.find(tech_name, "ap%-") == 1 then
 | |
|             research_done[tech_name] = tech.researched
 | |
|         end
 | |
|     end
 | |
|     game.write_file("ap_bridge.json", game.table_to_json(data_collection), false, 0)
 | |
|     -- game.write_file("research_done.json", game.table_to_json(data_collection), false, 0)
 | |
|     -- game.print("Sent progress to Archipelago.")
 | |
| end
 | |
| 
 | |
| 
 | |
| 
 | |
| -- add / commands
 | |
| 
 | |
| commands.add_command("ap-sync", "Run manual Research Sync with Archipelago.", function(call)
 | |
|     dumpTech(game.players[call.player_index].force)
 | |
|     game.print("Wrote bridge file.")
 | |
| end)
 | |
| 
 | |
| commands.add_command("ap-get-technology", "Grant a technology, used by the Archipelago Client.", function(call)
 | |
|     local force = game.forces["player"]
 | |
|     chunks = {}
 | |
|     for substring in call.parameter:gmatch("%S+") do -- split on " "
 | |
|         table.insert(chunks, substring)
 | |
|     end
 | |
|     local tech_name = chunks[1]
 | |
|     local source = chunks[2] or "Archipelago"
 | |
|     local tech = force.technologies[tech_name]
 | |
|     if tech ~= nil then
 | |
|         if tech.researched ~= true then
 | |
|             game.print({"", "Received [technology=" .. tech.name .. "] from ", source})
 | |
|             game.play_sound({path="utility/research_completed"})
 | |
|             tech.researched = true
 | |
|         end
 | |
|     else
 | |
|         game.print("Unknown Technology " .. tech_name)
 | |
|     end
 | |
| end) |