Factorio: add silo 'spawn' option

This commit is contained in:
black-sliver 2021-07-25 22:12:03 +02:00 committed by Fabian Dill
parent 08beb5fbe6
commit db0604f585
6 changed files with 177 additions and 10 deletions

View File

@ -97,6 +97,7 @@ Factorio:
silo:
vanilla: 1
randomize_recipe: 0
spawn: 0 # spawn silo near player spawn point
free_samples:
none: 1
single_craft: 0

View File

@ -45,9 +45,10 @@ class TechCost(Choice):
class Silo(Choice):
"""Ingredients to craft rocket silo."""
"""Ingredients to craft rocket silo or auto-place if set to spawn."""
option_vanilla = 0
option_randomize_recipe = 1
option_spawn = 2
default = 0

View File

@ -278,8 +278,9 @@ for ingredient_name in all_ingredient_names:
@functools.lru_cache(10)
def get_rocket_requirements(silo_recipe: Recipe, part_recipe: Recipe) -> Set[str]:
techs = set()
for ingredient in silo_recipe.ingredients:
techs |= recursively_get_unlocking_technologies(ingredient)
if silo_recipe:
for ingredient in silo_recipe.ingredients:
techs |= recursively_get_unlocking_technologies(ingredient)
for ingredient in part_recipe.ingredients:
techs |= recursively_get_unlocking_technologies(ingredient)
return {tech.name for tech in techs}

View File

@ -7,7 +7,7 @@ from .Technologies import base_tech_table, recipe_sources, base_technology_table
get_science_pack_pools, Recipe, recipes, technology_table, tech_table
from .Shapes import get_shapes
from .Mod import generate_mod
from .Options import factorio_options
from .Options import factorio_options, Silo
import logging
@ -26,7 +26,10 @@ class Factorio(World):
location_name_to_id = base_tech_table
def generate_basic(self):
skip_silo = self.world.silo[self.player].value == Silo.option_spawn
for tech_name in base_tech_table:
if skip_silo and tech_name == "rocket-silo":
continue
if self.world.progressive:
item_name = tech_to_progressive_lookup.get(tech_name, tech_name)
else:
@ -49,7 +52,10 @@ class Factorio(World):
menu.exits.append(crash)
nauvis = Region("Nauvis", None, "Nauvis", player, self.world)
skip_silo = self.world.silo[self.player].value == Silo.option_spawn
for tech_name, tech_id in base_tech_table.items():
if skip_silo and tech_name == "rocket-silo":
continue
tech = Location(player, tech_name, tech_id, nauvis)
nauvis.locations.append(tech)
tech.game = "Factorio"
@ -92,7 +98,10 @@ class Factorio(World):
location.access_rule = lambda state, ingredient=ingredient: \
all(state.has(technology.name, player) for technology in required_technologies[ingredient])
skip_silo = self.world.silo[self.player].value == Silo.option_spawn
for tech_name, technology in self.custom_technologies.items():
if skip_silo and tech_name == "rocket-silo":
continue
location = world.get_location(tech_name, player)
Rules.set_rule(location, technology.build_rule(player))
prequisites = shapes.get(tech_name)
@ -101,7 +110,8 @@ class Factorio(World):
Rules.add_rule(location, lambda state,
locations=locations: all(state.can_reach(loc) for loc in locations))
silo_recipe = self.custom_recipes["rocket-silo"] \
silo_recipe = None if self.world.silo[self.player].value == Silo.option_spawn \
else self.custom_recipes["rocket-silo"] \
if "rocket-silo" in self.custom_recipes \
else next(iter(all_product_sources.get("rocket-silo")))
part_recipe = self.custom_recipes["rocket-part"]
@ -230,7 +240,7 @@ class Factorio(World):
new_recipe.recursive_unlocking_technologies}
self.custom_recipes[pack] = new_recipe
if self.world.silo[self.player]:
if self.world.silo[self.player].value == Silo.option_randomize_recipe:
valid_pool = []
for pack in self.world.max_science_pack[self.player].get_allowed_packs():
valid_pool += sorted(science_pack_pools[pack])

View File

@ -18,19 +18,56 @@ function set_permissions()
end
{%- endif %}
function check_spawn_silo(force)
if force.players and #force.players > 0 and force.get_entity_count("rocket-silo") < 1 then
local surface = game.get_surface(1)
local spawn_position = force.get_spawn_position(surface)
spawn_entity(surface, force, "rocket-silo", spawn_position.x, spawn_position.y, 80, true)
end
end
function check_despawn_silo(force)
if not force.players or #force.players < 1 and force.get_entity_count("rocket-silo") > 0 then
local surface = game.get_surface(1)
local spawn_position = force.get_spawn_position(surface)
local x1 = spawn_position.x - 41
local x2 = spawn_position.x + 41
local y1 = spawn_position.y - 41
local y2 = spawn_position.y + 41
local silos = surface.find_entities_filtered{area = { {x1, y1}, {x2, y2} },
name = "rocket-silo",
force = force}
for i,silo in ipairs(silos) do
silo.destructible = true
silo.destroy()
end
end
end
-- 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)
--event.force appears to be LuaForce.name, not LuaForce
game.forces[event.force].research_queue_enabled = true
local force = event.force
if type(event.force) == "string" then -- should be of type LuaForce
force = game.forces[force]
end
force.research_queue_enabled = true
local data = {}
data['earned_samples'] = {{ dict_to_lua(starting_items) }}
data["victory"] = 0
global.forcedata[event.force] = data
{%- if silo == 2 %}
check_spawn_silo(force)
{%- endif %}
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)
{%- if silo == 2 %}
check_despawn_silo(event.force)
{%- endif %}
global.forcedata[event.force.name] = nil
end
@ -44,9 +81,21 @@ function on_player_created(event)
data['pending_samples'] = table.deepcopy(global.forcedata[player.force.name]['earned_samples'])
global.playerdata[player.index] = data
update_player(player.index) -- Attempt to send pending free samples, if relevant.
{%- if silo == 2 %}
check_spawn_silo(game.players[event.player_index].force)
{%- endif %}
end
script.on_event(defines.events.on_player_created, on_player_created)
-- Create/destroy silo for force if player switched force
function on_player_changed_force(event)
{%- if silo == 2 %}
check_despawn_silo(event.force)
check_spawn_silo(game.players[event.player_index].force)
{%- endif %}
end
script.on_event(defines.events.on_player_changed_force, on_player_changed_force)
function on_player_removed(event)
global.playerdata[event.player_index] = nil
end
@ -195,6 +244,98 @@ function chain_lookup(table, ...)
end
function spawn_entity(surface, force, name, x, y, radius, randomize)
local prototype = game.entity_prototypes[name]
local args = { -- For can_place_entity and place_entity
name = prototype.name,
position = {x = x, y = y},
force = force.name,
build_check_type = defines.build_check_type.blueprint_ghost,
forced = true
}
local box = prototype.selection_box
local dims = {
w = box.right_bottom.x - box.left_top.x,
h = box.right_bottom.y - box.left_top.y
}
local entity_radius = math.ceil(math.max(dims.w, dims.h) / math.sqrt(2) / 2)
local bounds = {
xmin = math.ceil(x - (radius - dims.w/2)),
xmax = math.floor(x + (radius - dims.w/2)),
ymin = math.ceil(y - (radius - dims.h/2)),
ymax = math.floor(y + (radius - dims.h/2))
}
local entity = nil
local attempts = 1000
for i = 1,attempts do -- Try multiple times
-- Find a position
if (randomize and i < attempts-3) or (not randomize and i ~= 1) then
args.position.x = math.random(bounds.xmin, bounds.xmax)
args.position.y = math.random(bounds.ymin, bounds.ymax)
elseif randomize then
args.position.x = x + (i + 3 - attempts) * dims.w
args.position.y = y + (i + 3 - attempts) * dims.h
end
-- Generate required chunks
local x1 = args.position.x + box.left_top.x
local x2 = args.position.x + box.right_bottom.x
local y1 = args.position.y + box.left_top.y
local y2 = args.position.y + box.right_bottom.y
if not surface.is_chunk_generated({x = x1, y = y1}) or
not surface.is_chunk_generated({x = x2, y = y1}) or
not surface.is_chunk_generated({x = x1, y = y2}) or
not surface.is_chunk_generated({x = x2, y = y2}) then
--player.print("Generating chunk at " .. serpent.line(args.position) .. ", radius=" .. entity_radius)
surface.request_to_generate_chunks(args.position, entity_radius)
surface.force_generate_chunk_requests()
end
-- Try to place entity
if surface.can_place_entity(args) then
-- Can hypothetically place this entity here. Destroy everything underneath it.
local collision_area = {
{
args.position.x + prototype.collision_box.left_top.x,
args.position.y + prototype.collision_box.left_top.x
},
{
args.position.x + prototype.collision_box.right_bottom.x,
args.position.y + prototype.collision_box.right_bottom.x
}
}
local entities = surface.find_entities_filtered {
area = collision_area,
collision_mask = prototype.collision_mask
}
local has_invalid_entities = false
for _, entity in pairs(entities) do
if entity.force and (entity.force.name ~= 'neutral') then
has_invalid_entities = true
end
end
if not has_invalid_entities then
for _, entity in pairs(entities) do
entity.destroy({do_cliff_correction=true, raise_destroy=true})
end
args.build_check_type = defines.build_check_type.script
args.create_build_effect_smoke = false
entity = surface.create_entity(args)
if entity then
entity.destructible = false
entity.minable = false
entity.rotatable = false
break
end
end
end
end
if entity == nil then
force.print("Failed to place " .. args.name .. " in " .. serpent.line({x = x, y = y, radius = radius}))
end
end
-- add / commands
commands.add_command("ap-sync", "Used by the Archipelago client to get progress information", function(call)
local force
@ -268,5 +409,13 @@ commands.add_command("ap-rcon-info", "Used by the Archipelago client to get info
rcon.print(game.table_to_json({["slot_name"] = SLOT_NAME, ["seed_name"] = SEED_NAME}))
end)
{% if allow_cheats -%}
commands.add_command("ap-spawn-silo", "Attempts to spawn a silo around 0,0", function(call)
spawn_entity(game.player.surface, game.player.force, "rocket-silo", 0, 0, 80, true)
end)
{% endif -%}
-- data
progressive_technologies = {{ dict_to_lua(progressive_technology_table) }}
progressive_technologies = {{ dict_to_lua(progressive_technology_table) }}

View File

@ -106,4 +106,9 @@ data:extend{new_tree_copy}
adjust_energy("{{ recipe_name }}", {{ random.triangular(*recipe_time_scale) }})
{%- endif %}
{%- endfor -%}
{% endif %}
{% endif %}
{%- if silo==2 %}
-- disable silo research for pre-placed silo
technologies["rocket-silo"].hidden = true
{%- endif %}