| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  | require "lib" | 
					
						
							| 
									
										
										
										
											2021-04-17 21:06:53 -07:00
										 |  |  | 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) | 
					
						
							| 
									
										
										
										
											2021-04-13 12:35:42 +02:00
										 |  |  |     local player = game.players[event.player_index] | 
					
						
							| 
									
										
										
										
											2021-04-17 21:06:53 -07:00
										 |  |  |     -- 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 | 
					
						
							| 
									
										
										
										
											2021-04-13 11:14:05 +02:00
										 |  |  | end) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | -- for testing | 
					
						
							| 
									
										
										
										
											2021-04-08 19:53:24 +02:00
										 |  |  | script.on_event(defines.events.on_tick, function(event) | 
					
						
							|  |  |  |     if event.tick%600 == 0 then | 
					
						
							|  |  |  |         dumpTech(game.forces["player"]) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | -- hook into researches done | 
					
						
							|  |  |  | script.on_event(defines.events.on_research_finished, function(event) | 
					
						
							| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  |     local technology = event.research | 
					
						
							|  |  |  |     dumpTech(technology.force) | 
					
						
							| 
									
										
										
										
											2021-04-17 21:06:53 -07:00
										 |  |  |     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) | 
					
						
							| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  |                         end | 
					
						
							|  |  |  |                     end | 
					
						
							| 
									
										
										
										
											2021-04-17 21:06:53 -07:00
										 |  |  |                     add_samples(technology.force, name, count) | 
					
						
							| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  |                 end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 01:55:53 +02:00
										 |  |  | function dumpTech(force) | 
					
						
							| 
									
										
										
										
											2021-04-13 12:35:42 +02:00
										 |  |  |     local research_done = {} | 
					
						
							|  |  |  |     local data_collection = {["research_done"] = research_done} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     for tech_name, tech in pairs(force.technologies) do | 
					
						
							| 
									
										
										
										
											2021-04-14 17:51:11 +02:00
										 |  |  |         if tech.researched and string.find(tech_name, "ap%-") == 1 then | 
					
						
							| 
									
										
										
										
											2021-04-14 04:14:37 +02:00
										 |  |  |             research_done[tech_name] = tech.researched | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2021-04-14 17:51:11 +02:00
										 |  |  |     game.write_file("ap_bridge.json", game.table_to_json(data_collection), false, 0) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     -- 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) | 
					
						
							| 
									
										
										
										
											2021-04-09 00:33:32 +02:00
										 |  |  |     dumpTech(game.players[call.player_index].force) | 
					
						
							|  |  |  |     game.print("Wrote bridge file.") | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | end) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | commands.add_command("ap-get-technology", "Grant a technology, used by the Archipelago Client.", function(call) | 
					
						
							|  |  |  |     local force = game.forces["player"] | 
					
						
							| 
									
										
										
										
											2021-04-13 11:14:05 +02:00
										 |  |  |     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" | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     local tech = force.technologies[tech_name] | 
					
						
							|  |  |  |     if tech ~= nil then | 
					
						
							|  |  |  |         if tech.researched ~= true then | 
					
						
							| 
									
										
										
										
											2021-04-14 18:38:06 +02:00
										 |  |  |             game.print({"", "Received [technology=" .. tech.name .. "] from ", source}) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |             game.play_sound({path="utility/research_completed"}) | 
					
						
							| 
									
										
										
										
											2021-04-17 21:06:53 -07:00
										 |  |  |             tech.researched = true | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         end | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         game.print("Unknown Technology " .. tech_name) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end) |