Factorio: content update

Energy Link:
  * Transfer and Storage increased by 10X
  * Cost of building increased by roughly 10X
  * This is a way to address their effect on performance, as users now need a tenth of them to get the same throughput, it also differentiates them from Accumulators
5 new Traps:
  * Teleport Trap
  * Grenade Trap
  * Cluster Grenade Trap
  * Artillery Trap
  * Atomic Rocket Trap
When max science is lower than min science, the two are now swapped.
Max Evolution Trap count was changed from 25 -> 10.
New option: Ingredients Offset
  * When creating random recipes, use this many more or less ingredients in the new recipe.
This commit is contained in:
Fabian Dill 2023-03-15 17:03:33 +01:00 committed by Fabian Dill
parent 5d6184f1fd
commit d825576f12
7 changed files with 292 additions and 82 deletions

View File

@ -136,6 +136,7 @@ def generate_mod(world: "Factorio", output_directory: str):
"goal": multiworld.goal[player].value, "goal": multiworld.goal[player].value,
"energy_link": multiworld.energy_link[player].value, "energy_link": multiworld.energy_link[player].value,
"useless_technologies": useless_technologies, "useless_technologies": useless_technologies,
"chunk_shuffle": multiworld.chunk_shuffle[player].value if hasattr(multiworld, "chunk_shuffle") else 0,
} }
for factorio_option in Options.factorio_options: for factorio_option in Options.factorio_options:

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import typing import typing
import datetime
from Options import Choice, OptionDict, OptionSet, ItemDict, Option, DefaultOnToggle, Range, DeathLink, Toggle from Options import Choice, OptionDict, OptionSet, ItemDict, Option, DefaultOnToggle, Range, DeathLink, Toggle
from schema import Schema, Optional, And, Or from schema import Schema, Optional, And, Or
@ -197,6 +198,14 @@ class RecipeIngredients(Choice):
option_science_pack = 1 option_science_pack = 1
class RecipeIngredientsOffset(Range):
"""When randomizing ingredients, remove or add this many "slots" of items.
For example, at -1 a randomized Automation Science Pack will only require 1 ingredient, instead of 2."""
display_name = "Randomized Recipe Ingredients Offset"
range_start = -1
range_end = 5
class FactorioStartItems(ItemDict): class FactorioStartItems(ItemDict):
"""Mapping of Factorio internal item-name to amount granted on start.""" """Mapping of Factorio internal item-name to amount granted on start."""
display_name = "Starting Items" display_name = "Starting Items"
@ -223,9 +232,36 @@ class AttackTrapCount(TrapCount):
display_name = "Attack Traps" display_name = "Attack Traps"
class TeleportTrapCount(TrapCount):
"""Trap items that when received trigger a random teleport."""
display_name = "Teleport Traps"
class GrenadeTrapCount(TrapCount):
"""Trap items that when received trigger a grenade explosion on each player."""
display_name = "Grenade Traps"
class ClusterGrenadeTrapCount(TrapCount):
"""Trap items that when received trigger a cluster grenade explosion on each player."""
display_name = "Cluster Grenade Traps"
class ArtilleryTrapCount(TrapCount):
"""Trap items that when received trigger an artillery shell on each player."""
display_name = "Artillery Traps"
class AtomicRocketTrapCount(TrapCount):
"""Trap items that when received trigger an atomic rocket explosion on each player.
Warning: there is no warning. The launch is instantaneous."""
display_name = "Atomic Rocket Traps"
class EvolutionTrapCount(TrapCount): class EvolutionTrapCount(TrapCount):
"""Trap items that when received increase the enemy evolution.""" """Trap items that when received increase the enemy evolution."""
display_name = "Evolution Traps" display_name = "Evolution Traps"
range_end = 10
class EvolutionTrapIncrease(Range): class EvolutionTrapIncrease(Range):
@ -404,12 +440,31 @@ factorio_options: typing.Dict[str, type(Option)] = {
"free_sample_whitelist": FactorioFreeSampleWhitelist, "free_sample_whitelist": FactorioFreeSampleWhitelist,
"recipe_time": RecipeTime, "recipe_time": RecipeTime,
"recipe_ingredients": RecipeIngredients, "recipe_ingredients": RecipeIngredients,
"recipe_ingredients_offset": RecipeIngredientsOffset,
"imported_blueprints": ImportedBlueprint, "imported_blueprints": ImportedBlueprint,
"world_gen": FactorioWorldGen, "world_gen": FactorioWorldGen,
"progressive": Progressive, "progressive": Progressive,
"evolution_traps": EvolutionTrapCount, "teleport_traps": TeleportTrapCount,
"grenade_traps": GrenadeTrapCount,
"cluster_grenade_traps": ClusterGrenadeTrapCount,
"artillery_traps": ArtilleryTrapCount,
"atomic_rocket_traps": AtomicRocketTrapCount,
"attack_traps": AttackTrapCount, "attack_traps": AttackTrapCount,
"evolution_traps": EvolutionTrapCount,
"evolution_trap_increase": EvolutionTrapIncrease, "evolution_trap_increase": EvolutionTrapIncrease,
"death_link": DeathLink, "death_link": DeathLink,
"energy_link": EnergyLink "energy_link": EnergyLink,
} }
# spoilers below. If you spoil it for yourself, please at least don't spoil it for anyone else.
if datetime.datetime.today().month == 4:
class ChunkShuffle(Toggle):
"""Entrance Randomizer."""
display_name = "Chunk Shuffle"
if datetime.datetime.today().day > 1:
ChunkShuffle.__doc__ += """
2023 April Fool's option. Shuffles chunk border transitions."""
factorio_options["chunk_shuffle"] = ChunkShuffle

View File

@ -35,6 +35,11 @@ class FactorioItem(Item):
all_items = tech_table.copy() all_items = tech_table.copy()
all_items["Attack Trap"] = factorio_base_id - 1 all_items["Attack Trap"] = factorio_base_id - 1
all_items["Evolution Trap"] = factorio_base_id - 2 all_items["Evolution Trap"] = factorio_base_id - 2
all_items["Teleport Trap"] = factorio_base_id - 3
all_items["Grenade Trap"] = factorio_base_id - 4
all_items["Cluster Grenade Trap"] = factorio_base_id - 5
all_items["Artillery Trap"] = factorio_base_id - 6
all_items["Atomic Rocket Trap"] = factorio_base_id - 7
class Factorio(World): class Factorio(World):
@ -43,7 +48,7 @@ class Factorio(World):
Nauvis, an inhospitable world filled with dangerous creatures called biters. Build a factory, Nauvis, an inhospitable world filled with dangerous creatures called biters. Build a factory,
research new technologies, and become more efficient in your quest to build a rocket and return home. research new technologies, and become more efficient in your quest to build a rocket and return home.
""" """
game: str = "Factorio" game = "Factorio"
special_nodes = {"automation", "logistics", "rocket-silo"} special_nodes = {"automation", "logistics", "rocket-silo"}
custom_recipes: typing.Dict[str, Recipe] custom_recipes: typing.Dict[str, Recipe]
location_pool: typing.List[FactorioScienceLocation] location_pool: typing.List[FactorioScienceLocation]
@ -52,12 +57,11 @@ class Factorio(World):
web = FactorioWeb() web = FactorioWeb()
item_name_to_id = all_items item_name_to_id = all_items
# TODO: remove base_tech_table ~ 0.3.7 location_name_to_id = location_table
location_name_to_id = {**base_tech_table, **location_table}
item_name_groups = { item_name_groups = {
"Progressive": set(progressive_tech_table.keys()), "Progressive": set(progressive_tech_table.keys()),
} }
data_version = 6 data_version = 7
required_client_version = (0, 3, 6) required_client_version = (0, 3, 6)
ordered_science_packs: typing.List[str] = MaxSciencePack.get_ordered_science_packs() ordered_science_packs: typing.List[str] = MaxSciencePack.get_ordered_science_packs()
@ -73,8 +77,10 @@ class Factorio(World):
generate_output = generate_mod generate_output = generate_mod
def generate_early(self) -> None: def generate_early(self) -> None:
self.multiworld.max_tech_cost[self.player] = max(self.multiworld.max_tech_cost[self.player], # if max < min, then swap max and min
self.multiworld.min_tech_cost[self.player]) if self.multiworld.max_tech_cost[self.player] < self.multiworld.min_tech_cost[self.player]:
self.multiworld.min_tech_cost[self.player].value, self.multiworld.max_tech_cost[self.player].value = \
self.multiworld.max_tech_cost[self.player].value, self.multiworld.min_tech_cost[self.player].value
self.tech_mix = self.multiworld.tech_cost_mix[self.player] self.tech_mix = self.multiworld.tech_cost_mix[self.player]
self.skip_silo = self.multiworld.silo[self.player].value == Silo.option_spawn self.skip_silo = self.multiworld.silo[self.player].value == Silo.option_spawn
@ -87,14 +93,25 @@ class Factorio(World):
nauvis = Region("Nauvis", player, self.multiworld) nauvis = Region("Nauvis", player, self.multiworld)
location_count = len(base_tech_table) - len(useless_technologies) - self.skip_silo + \ location_count = len(base_tech_table) - len(useless_technologies) - self.skip_silo + \
self.multiworld.evolution_traps[player].value + self.multiworld.attack_traps[player].value self.multiworld.evolution_traps[player] + \
self.multiworld.attack_traps[player] + \
self.multiworld.teleport_traps[player] + \
self.multiworld.grenade_traps[player] + \
self.multiworld.cluster_grenade_traps[player] + \
self.multiworld.atomic_rocket_traps[player] + \
self.multiworld.artillery_traps[player]
location_pool = [] location_pool = []
for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()): for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
location_pool.extend(location_pools[pack]) location_pool.extend(location_pools[pack])
try:
location_names = self.multiworld.random.sample(location_pool, location_count) location_names = self.multiworld.random.sample(location_pool, location_count)
except ValueError as e:
# should be "ValueError: Sample larger than population or is negative"
raise Exception("Too many traps for too few locations. Either decrease the trap count, "
f"or increase the location count (higher max science pack). (Player {self.player})") from e
self.locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis) self.locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis)
for loc_name in location_names] for loc_name in location_names]
distribution: TechCostDistribution = self.multiworld.tech_cost_distribution[self.player] distribution: TechCostDistribution = self.multiworld.tech_cost_distribution[self.player]
@ -132,6 +149,14 @@ class Factorio(World):
crash.connect(nauvis) crash.connect(nauvis)
self.multiworld.regions += [menu, nauvis] self.multiworld.regions += [menu, nauvis]
def create_items(self) -> None:
player = self.player
traps = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery", "Atomic Rocket")
for trap_name in traps:
self.multiworld.itempool.extend(self.create_item(f"{trap_name} Trap") for _ in
range(getattr(self.multiworld,
f"{trap_name.lower().replace(' ', '_')}_traps")[player]))
def set_rules(self): def set_rules(self):
world = self.multiworld world = self.multiworld
player = self.player player = self.player
@ -184,10 +209,6 @@ class Factorio(World):
player = self.player player = self.player
want_progressives = collections.defaultdict(lambda: self.multiworld.progressive[player]. want_progressives = collections.defaultdict(lambda: self.multiworld.progressive[player].
want_progressives(self.multiworld.random)) want_progressives(self.multiworld.random))
self.multiworld.itempool.extend(self.create_item("Evolution Trap") for _ in
range(self.multiworld.evolution_traps[player].value))
self.multiworld.itempool.extend(self.create_item("Attack Trap") for _ in
range(self.multiworld.attack_traps[player].value))
cost_sorted_locations = sorted(self.locations, key=lambda location: location.name) cost_sorted_locations = sorted(self.locations, key=lambda location: location.name)
special_index = {"automation": 0, special_index = {"automation": 0,
@ -265,10 +286,11 @@ class Factorio(World):
2: "chemistry"} 2: "chemistry"}
return categories.get(liquids, category) return categories.get(liquids, category)
def make_quick_recipe(self, original: Recipe, pool: list, allow_liquids: int = 2) -> Recipe: def make_quick_recipe(self, original: Recipe, pool: list, allow_liquids: int = 2,
ingredients_offset: int = 0) -> Recipe:
new_ingredients = {} new_ingredients = {}
liquids_used = 0 liquids_used = 0
for _ in original.ingredients: for _ in range(len(original.ingredients) + ingredients_offset):
new_ingredient = pool.pop() new_ingredient = pool.pop()
if new_ingredient in fluids: if new_ingredient in fluids:
while liquids_used == allow_liquids and new_ingredient in fluids: while liquids_used == allow_liquids and new_ingredient in fluids:
@ -282,7 +304,7 @@ class Factorio(World):
original.products, original.energy) original.products, original.energy)
def make_balanced_recipe(self, original: Recipe, pool: typing.Set[str], factor: float = 1, def make_balanced_recipe(self, original: Recipe, pool: typing.Set[str], factor: float = 1,
allow_liquids: int = 2) -> Recipe: allow_liquids: int = 2, ingredients_offset: int = 0) -> Recipe:
"""Generate a recipe from pool with time and cost similar to original * factor""" """Generate a recipe from pool with time and cost similar to original * factor"""
new_ingredients = {} new_ingredients = {}
# have to first sort for determinism, while filtering out non-stacking items # have to first sort for determinism, while filtering out non-stacking items
@ -291,7 +313,7 @@ class Factorio(World):
self.multiworld.random.shuffle(pool) self.multiworld.random.shuffle(pool)
target_raw = int(sum((count for ingredient, count in original.base_cost.items())) * factor) target_raw = int(sum((count for ingredient, count in original.base_cost.items())) * factor)
target_energy = original.total_energy * factor target_energy = original.total_energy * factor
target_num_ingredients = len(original.ingredients) target_num_ingredients = len(original.ingredients) + ingredients_offset
remaining_raw = target_raw remaining_raw = target_raw
remaining_energy = target_energy remaining_energy = target_energy
remaining_num_ingredients = target_num_ingredients remaining_num_ingredients = target_num_ingredients
@ -382,12 +404,13 @@ class Factorio(World):
return custom_technologies return custom_technologies
def set_custom_recipes(self): def set_custom_recipes(self):
ingredients_offset = self.multiworld.recipe_ingredients_offset[self.player]
original_rocket_part = recipes["rocket-part"] original_rocket_part = recipes["rocket-part"]
science_pack_pools = get_science_pack_pools() science_pack_pools = get_science_pack_pools()
valid_pool = sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_max_pack()] & valid_ingredients) valid_pool = sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_max_pack()] & valid_ingredients)
self.multiworld.random.shuffle(valid_pool) self.multiworld.random.shuffle(valid_pool)
self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category, self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category,
{valid_pool[x]: 10 for x in range(3)}, {valid_pool[x]: 10 for x in range(3 + ingredients_offset)},
original_rocket_part.products, original_rocket_part.products,
original_rocket_part.energy)} original_rocket_part.energy)}
@ -397,7 +420,8 @@ class Factorio(World):
valid_pool += sorted(science_pack_pools[pack]) valid_pool += sorted(science_pack_pools[pack])
self.multiworld.random.shuffle(valid_pool) self.multiworld.random.shuffle(valid_pool)
if pack in recipes: # skips over space science pack if pack in recipes: # skips over space science pack
new_recipe = self.make_quick_recipe(recipes[pack], valid_pool) new_recipe = self.make_quick_recipe(recipes[pack], valid_pool, ingredients_offset=
ingredients_offset)
self.custom_recipes[pack] = new_recipe self.custom_recipes[pack] = new_recipe
if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe \ if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe \
@ -407,21 +431,27 @@ class Factorio(World):
valid_pool |= science_pack_pools[pack] valid_pool |= science_pack_pools[pack]
if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe: if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe:
new_recipe = self.make_balanced_recipe(recipes["rocket-silo"], valid_pool, new_recipe = self.make_balanced_recipe(
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7) recipes["rocket-silo"], valid_pool,
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7,
ingredients_offset=ingredients_offset)
self.custom_recipes["rocket-silo"] = new_recipe self.custom_recipes["rocket-silo"] = new_recipe
if self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe: if self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe:
new_recipe = self.make_balanced_recipe(recipes["satellite"], valid_pool, new_recipe = self.make_balanced_recipe(
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7) recipes["satellite"], valid_pool,
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7,
ingredients_offset=ingredients_offset)
self.custom_recipes["satellite"] = new_recipe self.custom_recipes["satellite"] = new_recipe
bridge = "ap-energy-bridge" bridge = "ap-energy-bridge"
new_recipe = self.make_quick_recipe( new_recipe = self.make_quick_recipe(
Recipe(bridge, "crafting", {"replace_1": 1, "replace_2": 1, "replace_3": 1}, Recipe(bridge, "crafting", {"replace_1": 1, "replace_2": 1, "replace_3": 1,
"replace_4": 1, "replace_5": 1, "replace_6": 1},
{bridge: 1}, 10), {bridge: 1}, 10),
sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_ordered_science_packs()[0]])) sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_ordered_science_packs()[0]]),
ingredients_offset=ingredients_offset)
for ingredient_name in new_recipe.ingredients: for ingredient_name in new_recipe.ingredients:
new_recipe.ingredients[ingredient_name] = self.multiworld.random.randint(10, 100) new_recipe.ingredients[ingredient_name] = self.multiworld.random.randint(50, 500)
self.custom_recipes[bridge] = new_recipe self.custom_recipes[bridge] = new_recipe
needed_recipes = self.multiworld.max_science_pack[self.player].get_allowed_packs() | {"rocket-part"} needed_recipes = self.multiworld.max_science_pack[self.player].get_allowed_packs() | {"rocket-part"}
@ -452,7 +482,7 @@ class Factorio(World):
tech_table[name], self.player) tech_table[name], self.player)
item = FactorioItem(name, item = FactorioItem(name,
ItemClassification.trap if "Trap" in name else ItemClassification.filler, ItemClassification.trap if name.endswith("Trap") else ItemClassification.filler,
all_items[name], self.player) all_items[name], self.player)
return item return item

View File

@ -1,7 +1,8 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2021 Berserker55 and Dewiniaid Copyright (c) 2023 Berserker55
Copyright (c) 2021 Dewiniaid
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,32 +1,3 @@
function filter_ingredients(ingredients, ingredient_filter)
local new_ingredient_list = {}
for _, ingredient_table in pairs(ingredients) do
if ingredient_filter[ingredient_table[1]] then -- name of ingredient_table
table.insert(new_ingredient_list, ingredient_table)
end
end
return new_ingredient_list
end
function add_ingredients(ingredients, added_ingredients)
local new_ingredient_list = table.deepcopy(ingredients)
for new_ingredient, count in pairs(added_ingredients) do
local found = false
for _, old_ingredient in pairs(ingredients) do
if old_ingredient[1] == new_ingredient then
found = true
break
end
end
if not found then
table.insert(new_ingredient_list, {new_ingredient, count})
end
end
return new_ingredient_list
end
function get_any_stack_size(name) function get_any_stack_size(name)
local item = game.item_prototypes[name] local item = game.item_prototypes[name]
if item ~= nil then if item ~= nil then
@ -51,3 +22,18 @@ function split(s, sep)
return fields return fields
end end
function random_offset_position(position, offset)
return {x=position.x+math.random(-offset, offset), y=position.y+math.random(-1024, 1024)}
end
function fire_entity_at_players(entity_name, speed)
for _, player in ipairs(game.forces["player"].players) do
current_character = player.character
if current_character ~= nil then
current_character.surface.create_entity{name=entity_name,
position=random_offset_position(current_character.position, 128),
target=current_character, speed=speed}
end
end
end

View File

@ -11,7 +11,7 @@ TRAP_EVO_FACTOR = {{ evolution_trap_increase }} / 100
MAX_SCIENCE_PACK = {{ max_science_pack }} MAX_SCIENCE_PACK = {{ max_science_pack }}
GOAL = {{ goal }} GOAL = {{ goal }}
ARCHIPELAGO_DEATH_LINK_SETTING = "archipelago-death-link-{{ slot_player }}-{{ seed_name }}" ARCHIPELAGO_DEATH_LINK_SETTING = "archipelago-death-link-{{ slot_player }}-{{ seed_name }}"
ENERGY_INCREMENT = {{ energy_link * 1000000 }} ENERGY_INCREMENT = {{ energy_link * 10000000 }}
ENERGY_LINK_EFFICIENCY = 0.75 ENERGY_LINK_EFFICIENCY = 0.75
if settings.global[ARCHIPELAGO_DEATH_LINK_SETTING].value then if settings.global[ARCHIPELAGO_DEATH_LINK_SETTING].value then
@ -22,6 +22,119 @@ end
CURRENTLY_DEATH_LOCK = 0 CURRENTLY_DEATH_LOCK = 0
{% if chunk_shuffle %}
LAST_POSITIONS = {}
GENERATOR = nil
NORTH = 1
EAST = 2
SOUTH = 3
WEST = 4
ER_COLOR = {1, 1, 1, 0.2}
ER_SEED = {{ random.randint(4294967295, 2*4294967295)}}
CURRENTLY_MOVING = false
ER_FRAMES = {}
CHUNK_OFFSET = {
[NORTH] = {0, 1},
[EAST] = {1, 0},
[SOUTH] = {0, -1},
[WEST] = {-1, 0}
}
function on_player_changed_position(event)
if CURRENTLY_MOVING == true then
return
end
local player_id = event.player_index
local player = game.get_player(player_id)
local character = player.character -- can be nil, such as spectators
if character == nil then
return
end
local last_position = LAST_POSITIONS[player_id]
if last_position == nil then
LAST_POSITIONS[player_id] = character.position
return
end
last_x_chunk = math.floor(last_position.x / 32)
current_x_chunk = math.floor(character.position.x / 32)
last_y_chunk = math.floor(last_position.y / 32)
current_y_chunk = math.floor(character.position.y / 32)
if (ER_FRAMES[player_id] ~= nil and rendering.is_valid(ER_FRAMES[player_id])) then
rendering.destroy(ER_FRAMES[player_id])
end
ER_FRAMES[player_id] = rendering.draw_rectangle{
color=ER_COLOR, width=1, filled=false, left_top = {current_x_chunk*32, current_y_chunk*32},
right_bottom={current_x_chunk*32+32, current_y_chunk*32+32}, players={player}, time_to_live=60,
draw_on_ground= true, only_in_alt_mode = true, surface=character.surface}
if current_x_chunk == last_x_chunk and current_y_chunk == last_y_chunk then -- nothing needs doing
return
end
if ((last_position.x - character.position.x) ^ 2 + (last_position.y - character.position.y) ^ 2) > 4000 then
-- distance too high, death or other teleport took place
LAST_POSITIONS[player_id] = character.position
return
end
-- we'll need a deterministic random state
if GENERATOR == nil or not GENERATOR.valid then
GENERATOR = game.create_random_generator()
end
-- sufficiently random pattern
GENERATOR.re_seed((ER_SEED + (last_x_chunk * 1730000000) + (last_y_chunk * 97000)) % 4294967295)
-- we now need all 4 exit directions deterministically shuffled to the 4 outgoing directions.
local exit_table = {
[1] = 1,
[2] = 2,
[3] = 3,
[4] = 4
}
exit_table = fisher_yates_shuffle(exit_table)
if current_x_chunk > last_x_chunk then -- going right/east
outbound_direction = EAST
elseif current_x_chunk < last_x_chunk then -- going left/west
outbound_direction = WEST
end
if current_y_chunk > last_y_chunk then -- going down/south
outbound_direction = SOUTH
elseif current_y_chunk < last_y_chunk then -- going up/north
outbound_direction = NORTH
end
local target_direction = exit_table[outbound_direction]
local target_position = {(CHUNK_OFFSET[target_direction][1] + last_x_chunk) * 32 + 16,
(CHUNK_OFFSET[target_direction][2] + last_y_chunk) * 32 + 16}
target_position = character.surface.find_non_colliding_position(character.prototype.name,
target_position, 32, 0.5)
if target_position ~= nil then
rendering.draw_circle{color = ER_COLOR, radius = 1, filled = true,
target = {character.position.x, character.position.y}, surface = character.surface,
time_to_live = 300, draw_on_ground = true}
rendering.draw_line{color = ER_COLOR, width = 3, gap_length = 0.5, dash_length = 0.5,
from = {character.position.x, character.position.y}, to = target_position,
surface = character.surface,
time_to_live = 300, draw_on_ground = true}
CURRENTLY_MOVING = true -- prevent recursive event
character.teleport(target_position)
CURRENTLY_MOVING = false
end
LAST_POSITIONS[player_id] = character.position
end
function fisher_yates_shuffle(tbl)
for i = #tbl, 2, -1 do
local j = GENERATOR(i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
return tbl
end
script.on_event(defines.events.on_player_changed_position, on_player_changed_position)
{% endif %}
function on_check_energy_link(event) function on_check_energy_link(event)
--- assuming 1 MJ increment and 5MJ battery: --- assuming 1 MJ increment and 5MJ battery:
--- first 2 MJ request fill, last 2 MJ push energy, middle 1 MJ does nothing --- first 2 MJ request fill, last 2 MJ push energy, middle 1 MJ does nothing
@ -510,6 +623,37 @@ commands.add_command("ap-print", "Used by the Archipelago client to print messag
game.print(call.parameter) game.print(call.parameter)
end) end)
TRAP_TABLE = {
["Attack Trap"] = function ()
game.surfaces["nauvis"].build_enemy_base(game.forces["player"].get_spawn_position(game.get_surface(1)), 25)
end,
["Evolution Trap"] = function ()
game.forces["enemy"].evolution_factor = game.forces["enemy"].evolution_factor + (TRAP_EVO_FACTOR * (1 - game.forces["enemy"].evolution_factor))
game.print({"", "New evolution factor:", game.forces["enemy"].evolution_factor})
end,
["Teleport Trap"] = function ()
for _, player in ipairs(game.forces["player"].players) do
current_character = player.character
if current_character ~= nil then
current_character.teleport(current_character.surface.find_non_colliding_position(
current_character.prototype.name, random_offset_position(current_character.position, 1024), 0, 1))
end
end
end,
["Grenade Trap"] = function ()
fire_entity_at_players("grenade", 0.1)
end,
["Cluster Grenade Trap"] = function ()
fire_entity_at_players("cluster-grenade", 0.1)
end,
["Artillery Trap"] = function ()
fire_entity_at_players("artillery-projectile", 1)
end,
["Atomic Rocket Trap"] = function ()
fire_entity_at_players("atomic-rocket", 0.1)
end,
}
commands.add_command("ap-get-technology", "Grant a technology, used by the Archipelago Client.", function(call) commands.add_command("ap-get-technology", "Grant a technology, used by the Archipelago Client.", function(call)
if global.index_sync == nil then if global.index_sync == nil then
global.index_sync = {} global.index_sync = {}
@ -552,18 +696,11 @@ commands.add_command("ap-get-technology", "Grant a technology, used by the Archi
tech.researched = true tech.researched = true
end end
end end
elseif item_name == "Attack Trap" then elseif TRAP_TABLE[item_name] ~= nil then
if global.index_sync[index] == nil then -- not yet received trap
game.print({"", "Received Attack Trap from ", source})
global.index_sync[index] = item_name
local spawn_position = force.get_spawn_position(game.get_surface(1))
game.surfaces["nauvis"].build_enemy_base(spawn_position, 25)
end
elseif item_name == "Evolution Trap" then
if global.index_sync[index] == nil then -- not yet received trap if global.index_sync[index] == nil then -- not yet received trap
global.index_sync[index] = item_name global.index_sync[index] = item_name
game.forces["enemy"].evolution_factor = game.forces["enemy"].evolution_factor + (TRAP_EVO_FACTOR * (1 - game.forces["enemy"].evolution_factor)) game.print({"", "Received ", item_name, " from ", source})
game.print({"", "Received Evolution Trap from ", source, ". New factor:", game.forces["enemy"].evolution_factor}) TRAP_TABLE[item_name]()
end end
else else
game.print("Unknown Item " .. item_name) game.print("Unknown Item " .. item_name)

View File

@ -14,9 +14,9 @@ local energy_bridge = table.deepcopy(data.raw["accumulator"]["accumulator"])
energy_bridge.name = "ap-energy-bridge" energy_bridge.name = "ap-energy-bridge"
energy_bridge.minable.result = "ap-energy-bridge" energy_bridge.minable.result = "ap-energy-bridge"
energy_bridge.localised_name = "Archipelago EnergyLink Bridge" energy_bridge.localised_name = "Archipelago EnergyLink Bridge"
energy_bridge.energy_source.buffer_capacity = "5MJ" energy_bridge.energy_source.buffer_capacity = "50MJ"
energy_bridge.energy_source.input_flow_limit = "1MW" energy_bridge.energy_source.input_flow_limit = "10MW"
energy_bridge.energy_source.output_flow_limit = "1MW" energy_bridge.energy_source.output_flow_limit = "10MW"
tint_icon(energy_bridge, energy_bridge_tint()) tint_icon(energy_bridge, energy_bridge_tint())
energy_bridge.picture.layers[1].tint = energy_bridge_tint() energy_bridge.picture.layers[1].tint = energy_bridge_tint()
energy_bridge.picture.layers[1].hr_version.tint = energy_bridge_tint() energy_bridge.picture.layers[1].hr_version.tint = energy_bridge_tint()