Overhauls control.lua template (#1)
* 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.
			
			
This commit is contained in:
		
							parent
							
								
									2dd6dcab20
								
							
						
					
					
						commit
						65df153947
					
				| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2021 Berserker55
 | 
			
		||||
Copyright (c) 2021 Berserker55 and Dewiniaid
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
    "name": "archipelago-client",
 | 
			
		||||
    "version": "0.0.1",
 | 
			
		||||
    "title": "Archipelago",
 | 
			
		||||
    "author": "Berserker",
 | 
			
		||||
    "author": "Berserker and Dewiniaid",
 | 
			
		||||
    "homepage": "https://archipelago.gg",
 | 
			
		||||
    "description": "Integration client for the Archipelago Randomizer",
 | 
			
		||||
    "factorio_version": "1.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,135 @@
 | 
			
		|||
require "lib"
 | 
			
		||||
script.on_event(defines.events.on_player_created, function(event)
 | 
			
		||||
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]
 | 
			
		||||
    player.force.research_queue_enabled = true
 | 
			
		||||
    {% if free_samples %}
 | 
			
		||||
    player.insert({count=19, name="burner-mining-drill"})
 | 
			
		||||
    player.insert({count=19, name="stone-furnace"})
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    -- 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
 | 
			
		||||
| 
						 | 
				
			
			@ -19,31 +143,32 @@ end)
 | 
			
		|||
script.on_event(defines.events.on_research_finished, function(event)
 | 
			
		||||
    local technology = event.research
 | 
			
		||||
    dumpTech(technology.force)
 | 
			
		||||
    {% if free_samples %}
 | 
			
		||||
    local players = technology.force.players
 | 
			
		||||
    if technology.effects then
 | 
			
		||||
        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
 | 
			
		||||
                        {% if free_samples == 1 %}
 | 
			
		||||
                        local new = {count=result.amount, name=result.name}
 | 
			
		||||
                        {% elif free_samples == 2 %}
 | 
			
		||||
                        local new = {count=get_any_stack_size(result.name) * 0.5, name=result.name}
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                        local new = {count=get_any_stack_size(result.name), name=result.name}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        for _, player in pairs(players) do
 | 
			
		||||
                            player.insert(new)
 | 
			
		||||
    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
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
function dumpTech(force)
 | 
			
		||||
| 
						 | 
				
			
			@ -80,9 +205,9 @@ commands.add_command("ap-get-technology", "Grant a technology, used by the Archi
 | 
			
		|||
    local tech = force.technologies[tech_name]
 | 
			
		||||
    if tech ~= nil then
 | 
			
		||||
        if tech.researched ~= true then
 | 
			
		||||
            tech.researched = true
 | 
			
		||||
            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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue