mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-21 15:03:18 -07:00
Compare commits
16 Commits
api-refere
...
use_self.o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2524ddc075 | ||
|
|
d545b78803 | ||
|
|
88b1c94eb2 | ||
|
|
7742d5d804 | ||
|
|
d3e148dcc6 | ||
|
|
b5fccde913 | ||
|
|
55e9b0687a | ||
|
|
79e1bf351e | ||
|
|
fcfea9d9aa | ||
|
|
cfc5508f06 | ||
|
|
62cb5f1fc2 | ||
|
|
7e70b16656 | ||
|
|
7b486b3380 | ||
|
|
09cac0a685 | ||
|
|
12c583533d | ||
|
|
c5af28a649 |
5
Main.py
5
Main.py
@@ -167,10 +167,11 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
|||||||
|
|
||||||
# remove starting inventory from pool items.
|
# remove starting inventory from pool items.
|
||||||
# Because some worlds don't actually create items during create_items this has to be as late as possible.
|
# Because some worlds don't actually create items during create_items this has to be as late as possible.
|
||||||
if any(world.start_inventory_from_pool[player].value for player in world.player_ids):
|
if any(getattr(world.worlds[player].options, "start_inventory_from_pool", StartInventoryPool({})).value for player in world.player_ids):
|
||||||
new_items: List[Item] = []
|
new_items: List[Item] = []
|
||||||
depletion_pool: Dict[int, Dict[str, int]] = {
|
depletion_pool: Dict[int, Dict[str, int]] = {
|
||||||
player: world.start_inventory_from_pool[player].value.copy() for player in world.player_ids}
|
player: getattr(world.worlds[player].options, "start_inventory_from_pool", StartInventoryPool({})).value.copy()
|
||||||
|
for player in world.player_ids}
|
||||||
for player, items in depletion_pool.items():
|
for player, items in depletion_pool.items():
|
||||||
player_world: AutoWorld.World = world.worlds[player]
|
player_world: AutoWorld.World = world.worlds[player]
|
||||||
for count in items.values():
|
for count in items.values():
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from Options import Choice, Option, DefaultOnToggle, DeathLink, Range, Toggle
|
from Options import Choice, Option, DefaultOnToggle, DeathLink, Range, Toggle, PerGameCommonOptions
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
class FreeincarnateMax(Range):
|
class FreeincarnateMax(Range):
|
||||||
@@ -224,21 +226,20 @@ class StartCastle(Choice):
|
|||||||
default = option_yellow
|
default = option_yellow
|
||||||
|
|
||||||
|
|
||||||
adventure_option_definitions: Dict[str, type(Option)] = {
|
@dataclass
|
||||||
"dragon_slay_check": DragonSlayCheck,
|
class AdventureOptions(PerGameCommonOptions):
|
||||||
"death_link": DeathLink,
|
dragon_slay_check: DragonSlayCheck
|
||||||
"bat_logic": BatLogic,
|
death_link: DeathLink
|
||||||
"freeincarnate_max": FreeincarnateMax,
|
bat_logic: BatLogic
|
||||||
"dragon_rando_type": DragonRandoType,
|
freeincarnate_max: FreeincarnateMax
|
||||||
"connector_multi_slot": ConnectorMultiSlot,
|
dragon_rando_type: DragonRandoType
|
||||||
"yorgle_speed": YorgleStartingSpeed,
|
connector_multi_slot: ConnectorMultiSlot
|
||||||
"yorgle_min_speed": YorgleMinimumSpeed,
|
yorgle_speed: YorgleStartingSpeed
|
||||||
"grundle_speed": GrundleStartingSpeed,
|
yorgle_min_speed: YorgleMinimumSpeed
|
||||||
"grundle_min_speed": GrundleMinimumSpeed,
|
grundle_speed: GrundleStartingSpeed
|
||||||
"rhindle_speed": RhindleStartingSpeed,
|
grundle_min_speed: GrundleMinimumSpeed
|
||||||
"rhindle_min_speed": RhindleMinimumSpeed,
|
rhindle_speed: RhindleStartingSpeed
|
||||||
"difficulty_switch_a": DifficultySwitchA,
|
rhindle_min_speed: RhindleMinimumSpeed
|
||||||
"difficulty_switch_b": DifficultySwitchB,
|
difficulty_switch_a: DifficultySwitchA
|
||||||
"start_castle": StartCastle,
|
difficulty_switch_b: DifficultySwitchB
|
||||||
|
start_castle: StartCastle
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from BaseClasses import MultiWorld, Region, Entrance, LocationProgressType
|
from BaseClasses import MultiWorld, Region, Entrance, LocationProgressType
|
||||||
from .Locations import location_table, LocationData, AdventureLocation, dragon_room_to_region
|
from .Locations import location_table, LocationData, AdventureLocation, dragon_room_to_region
|
||||||
|
from Options import PerGameCommonOptions
|
||||||
|
|
||||||
|
|
||||||
def connect(world: MultiWorld, player: int, source: str, target: str, rule: callable = lambda state: True,
|
def connect(world: MultiWorld, player: int, source: str, target: str, rule: callable = lambda state: True,
|
||||||
@@ -24,7 +25,7 @@ def connect(world: MultiWorld, player: int, source: str, target: str, rule: call
|
|||||||
connect(world, player, target, source, rule, True)
|
connect(world, player, target, source, rule, True)
|
||||||
|
|
||||||
|
|
||||||
def create_regions(multiworld: MultiWorld, player: int, dragon_rooms: []) -> None:
|
def create_regions(options: PerGameCommonOptions, multiworld: MultiWorld, player: int, dragon_rooms: []) -> None:
|
||||||
for name, locdata in location_table.items():
|
for name, locdata in location_table.items():
|
||||||
locdata.get_position(multiworld.random)
|
locdata.get_position(multiworld.random)
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ def create_regions(multiworld: MultiWorld, player: int, dragon_rooms: []) -> Non
|
|||||||
credits_room_far_side.exits.append(Entrance(player, "CreditsFromFarSide", credits_room_far_side))
|
credits_room_far_side.exits.append(Entrance(player, "CreditsFromFarSide", credits_room_far_side))
|
||||||
multiworld.regions.append(credits_room_far_side)
|
multiworld.regions.append(credits_room_far_side)
|
||||||
|
|
||||||
dragon_slay_check = multiworld.dragon_slay_check[player].value
|
dragon_slay_check = options.dragon_slay_check.value
|
||||||
priority_locations = determine_priority_locations(multiworld, dragon_slay_check)
|
priority_locations = determine_priority_locations(multiworld, dragon_slay_check)
|
||||||
|
|
||||||
for name, location_data in location_table.items():
|
for name, location_data in location_table.items():
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ from BaseClasses import LocationProgressType
|
|||||||
|
|
||||||
def set_rules(self) -> None:
|
def set_rules(self) -> None:
|
||||||
world = self.multiworld
|
world = self.multiworld
|
||||||
use_bat_logic = world.bat_logic[self.player].value == BatLogic.option_use_logic
|
options = self.options
|
||||||
|
use_bat_logic = options.bat_logic.value == BatLogic.option_use_logic
|
||||||
|
|
||||||
set_rule(world.get_entrance("YellowCastlePort", self.player),
|
set_rule(world.get_entrance("YellowCastlePort", self.player),
|
||||||
lambda state: state.has("Yellow Key", self.player))
|
lambda state: state.has("Yellow Key", self.player))
|
||||||
@@ -28,7 +29,7 @@ def set_rules(self) -> None:
|
|||||||
lambda state: state.has("Bridge", self.player) or
|
lambda state: state.has("Bridge", self.player) or
|
||||||
state.has("Magnet", self.player))
|
state.has("Magnet", self.player))
|
||||||
|
|
||||||
dragon_slay_check = world.dragon_slay_check[self.player].value
|
dragon_slay_check = options.dragon_slay_check.value
|
||||||
if dragon_slay_check:
|
if dragon_slay_check:
|
||||||
if self.difficulty_switch_b == DifficultySwitchB.option_hard_with_unlock_item:
|
if self.difficulty_switch_b == DifficultySwitchB.option_hard_with_unlock_item:
|
||||||
set_rule(world.get_location("Slay Yorgle", self.player),
|
set_rule(world.get_location("Slay Yorgle", self.player),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from Options import AssembleOptions
|
|||||||
from worlds.AutoWorld import WebWorld, World
|
from worlds.AutoWorld import WebWorld, World
|
||||||
from Fill import fill_restrictive
|
from Fill import fill_restrictive
|
||||||
from worlds.generic.Rules import add_rule, set_rule
|
from worlds.generic.Rules import add_rule, set_rule
|
||||||
from .Options import adventure_option_definitions, DragonRandoType, DifficultySwitchA, DifficultySwitchB
|
from .Options import AdventureOptions, DragonRandoType, DifficultySwitchA, DifficultySwitchB
|
||||||
from .Rom import get_base_rom_bytes, get_base_rom_path, AdventureDeltaPatch, apply_basepatch, \
|
from .Rom import get_base_rom_bytes, get_base_rom_path, AdventureDeltaPatch, apply_basepatch, \
|
||||||
AdventureAutoCollectLocation
|
AdventureAutoCollectLocation
|
||||||
from .Items import item_table, ItemData, nothing_item_id, event_table, AdventureItem, standard_item_max
|
from .Items import item_table, ItemData, nothing_item_id, event_table, AdventureItem, standard_item_max
|
||||||
@@ -109,7 +109,8 @@ class AdventureWorld(World):
|
|||||||
game: ClassVar[str] = "Adventure"
|
game: ClassVar[str] = "Adventure"
|
||||||
web: ClassVar[WebWorld] = AdventureWeb()
|
web: ClassVar[WebWorld] = AdventureWeb()
|
||||||
|
|
||||||
option_definitions: ClassVar[Dict[str, AssembleOptions]] = adventure_option_definitions
|
options = AdventureOptions
|
||||||
|
options_dataclass = AdventureOptions
|
||||||
settings: ClassVar[AdventureSettings]
|
settings: ClassVar[AdventureSettings]
|
||||||
item_name_to_id: ClassVar[Dict[str, int]] = {name: data.id for name, data in item_table.items()}
|
item_name_to_id: ClassVar[Dict[str, int]] = {name: data.id for name, data in item_table.items()}
|
||||||
location_name_to_id: ClassVar[Dict[str, int]] = {name: data.location_id for name, data in location_table.items()}
|
location_name_to_id: ClassVar[Dict[str, int]] = {name: data.location_id for name, data in location_table.items()}
|
||||||
@@ -150,18 +151,18 @@ class AdventureWorld(World):
|
|||||||
bytearray(f"ADVENTURE{__version__.replace('.', '')[:3]}_{self.player}_{self.multiworld.seed}", "utf8")[:21]
|
bytearray(f"ADVENTURE{__version__.replace('.', '')[:3]}_{self.player}_{self.multiworld.seed}", "utf8")[:21]
|
||||||
self.rom_name.extend([0] * (21 - len(self.rom_name)))
|
self.rom_name.extend([0] * (21 - len(self.rom_name)))
|
||||||
|
|
||||||
self.dragon_rando_type = self.multiworld.dragon_rando_type[self.player].value
|
self.dragon_rando_type = self.options.dragon_rando_type.value
|
||||||
self.dragon_slay_check = self.multiworld.dragon_slay_check[self.player].value
|
self.dragon_slay_check = self.options.dragon_slay_check.value
|
||||||
self.connector_multi_slot = self.multiworld.connector_multi_slot[self.player].value
|
self.connector_multi_slot = self.options.connector_multi_slot.value
|
||||||
self.yorgle_speed = self.multiworld.yorgle_speed[self.player].value
|
self.yorgle_speed = self.options.yorgle_speed.value
|
||||||
self.yorgle_min_speed = self.multiworld.yorgle_min_speed[self.player].value
|
self.yorgle_min_speed = self.options.yorgle_min_speed.value
|
||||||
self.grundle_speed = self.multiworld.grundle_speed[self.player].value
|
self.grundle_speed = self.options.grundle_speed.value
|
||||||
self.grundle_min_speed = self.multiworld.grundle_min_speed[self.player].value
|
self.grundle_min_speed = self.options.grundle_min_speed.value
|
||||||
self.rhindle_speed = self.multiworld.rhindle_speed[self.player].value
|
self.rhindle_speed = self.options.rhindle_speed.value
|
||||||
self.rhindle_min_speed = self.multiworld.rhindle_min_speed[self.player].value
|
self.rhindle_min_speed = self.options.rhindle_min_speed.value
|
||||||
self.difficulty_switch_a = self.multiworld.difficulty_switch_a[self.player].value
|
self.difficulty_switch_a = self.options.difficulty_switch_a.value
|
||||||
self.difficulty_switch_b = self.multiworld.difficulty_switch_b[self.player].value
|
self.difficulty_switch_b = self.options.difficulty_switch_b.value
|
||||||
self.start_castle = self.multiworld.start_castle[self.player].value
|
self.start_castle = self.options.start_castle.value
|
||||||
self.created_items = 0
|
self.created_items = 0
|
||||||
|
|
||||||
if self.dragon_slay_check == 0:
|
if self.dragon_slay_check == 0:
|
||||||
@@ -228,7 +229,7 @@ class AdventureWorld(World):
|
|||||||
extra_filler_count = num_locations - self.created_items
|
extra_filler_count = num_locations - self.created_items
|
||||||
|
|
||||||
# traps would probably go here, if enabled
|
# traps would probably go here, if enabled
|
||||||
freeincarnate_max = self.multiworld.freeincarnate_max[self.player].value
|
freeincarnate_max = self.options.freeincarnate_max.value
|
||||||
actual_freeincarnates = min(extra_filler_count, freeincarnate_max)
|
actual_freeincarnates = min(extra_filler_count, freeincarnate_max)
|
||||||
self.multiworld.itempool += [self.create_item("Freeincarnate") for _ in range(actual_freeincarnates)]
|
self.multiworld.itempool += [self.create_item("Freeincarnate") for _ in range(actual_freeincarnates)]
|
||||||
self.created_items += actual_freeincarnates
|
self.created_items += actual_freeincarnates
|
||||||
@@ -248,7 +249,7 @@ class AdventureWorld(World):
|
|||||||
self.created_items += 1
|
self.created_items += 1
|
||||||
|
|
||||||
def create_regions(self) -> None:
|
def create_regions(self) -> None:
|
||||||
create_regions(self.multiworld, self.player, self.dragon_rooms)
|
create_regions(self.options, self.multiworld, self.player, self.dragon_rooms)
|
||||||
|
|
||||||
set_rules = set_rules
|
set_rules = set_rules
|
||||||
|
|
||||||
@@ -355,7 +356,7 @@ class AdventureWorld(World):
|
|||||||
auto_collect_locations: [AdventureAutoCollectLocation] = []
|
auto_collect_locations: [AdventureAutoCollectLocation] = []
|
||||||
local_item_to_location: {int, int} = {}
|
local_item_to_location: {int, int} = {}
|
||||||
bat_no_touch_locs: [LocationData] = []
|
bat_no_touch_locs: [LocationData] = []
|
||||||
bat_logic: int = self.multiworld.bat_logic[self.player].value
|
bat_logic: int = self.options.bat_logic.value
|
||||||
try:
|
try:
|
||||||
rom_deltas: { int, int } = {}
|
rom_deltas: { int, int } = {}
|
||||||
self.place_dragons(rom_deltas)
|
self.place_dragons(rom_deltas)
|
||||||
@@ -411,7 +412,7 @@ class AdventureWorld(World):
|
|||||||
item_position_data_start = get_item_position_data_start(unplaced_item.table_index)
|
item_position_data_start = get_item_position_data_start(unplaced_item.table_index)
|
||||||
rom_deltas[item_position_data_start] = 0xff
|
rom_deltas[item_position_data_start] = 0xff
|
||||||
|
|
||||||
if self.multiworld.connector_multi_slot[self.player].value:
|
if self.options.connector_multi_slot.value:
|
||||||
rom_deltas[connector_port_offset] = (self.player & 0xff)
|
rom_deltas[connector_port_offset] = (self.player & 0xff)
|
||||||
else:
|
else:
|
||||||
rom_deltas[connector_port_offset] = 0
|
rom_deltas[connector_port_offset] = 0
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from Options import Choice, Toggle, DefaultOnToggle, DeathLink, StartInventoryPool
|
from Options import Choice, Toggle, DefaultOnToggle, DeathLink, StartInventoryPool, PerGameCommonOptions
|
||||||
|
from dataclasses import dataclass
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
@@ -163,26 +164,26 @@ class BlasphemousDeathLink(DeathLink):
|
|||||||
Note that Guilt Fragments will not appear when killed by Death Link."""
|
Note that Guilt Fragments will not appear when killed by Death Link."""
|
||||||
|
|
||||||
|
|
||||||
blasphemous_options = {
|
@dataclass
|
||||||
"prie_dieu_warp": PrieDieuWarp,
|
class BlasphemousOptions(PerGameCommonOptions):
|
||||||
"skip_cutscenes": SkipCutscenes,
|
prie_dieu_warp: PrieDieuWarp
|
||||||
"corpse_hints": CorpseHints,
|
skip_cutscenes: SkipCutscenes
|
||||||
"difficulty": Difficulty,
|
corpse_hints: CorpseHints
|
||||||
"penitence": Penitence,
|
difficulty: Difficulty
|
||||||
"starting_location": StartingLocation,
|
penitence: Penitence
|
||||||
"ending": Ending,
|
starting_location: StartingLocation
|
||||||
"skip_long_quests": SkipLongQuests,
|
ending: Ending
|
||||||
"thorn_shuffle" : ThornShuffle,
|
skip_long_quests: SkipLongQuests
|
||||||
"dash_shuffle": DashShuffle,
|
thorn_shuffle : ThornShuffle
|
||||||
"wall_climb_shuffle": WallClimbShuffle,
|
dash_shuffle: DashShuffle
|
||||||
"reliquary_shuffle": ReliquaryShuffle,
|
wall_climb_shuffle: WallClimbShuffle
|
||||||
"boots_of_pleading": CustomItem1,
|
reliquary_shuffle: ReliquaryShuffle
|
||||||
"purified_hand": CustomItem2,
|
boots_of_pleading: CustomItem1
|
||||||
"start_wheel": StartWheel,
|
purified_hand: CustomItem2
|
||||||
"skill_randomizer": SkillRando,
|
start_wheel: StartWheel
|
||||||
"enemy_randomizer": EnemyRando,
|
skill_randomizer: SkillRando
|
||||||
"enemy_groups": EnemyGroups,
|
enemy_randomizer: EnemyRando
|
||||||
"enemy_scaling": EnemyScaling,
|
enemy_groups: EnemyGroups
|
||||||
"death_link": BlasphemousDeathLink,
|
enemy_scaling: EnemyScaling
|
||||||
"start_inventory": StartInventoryPool
|
death_link: BlasphemousDeathLink
|
||||||
}
|
start_inventory: StartInventoryPool
|
||||||
@@ -497,8 +497,9 @@ def chalice_rooms(state: CollectionState, player: int, number: int) -> bool:
|
|||||||
def rules(blasphemousworld):
|
def rules(blasphemousworld):
|
||||||
world = blasphemousworld.multiworld
|
world = blasphemousworld.multiworld
|
||||||
player = blasphemousworld.player
|
player = blasphemousworld.player
|
||||||
logic = world.difficulty[player].value
|
options = blasphemousworld.options
|
||||||
enemy = world.enemy_randomizer[player].value
|
logic = options.difficulty.value
|
||||||
|
enemy = options.enemy_randomizer.value
|
||||||
|
|
||||||
|
|
||||||
# D01Z01S01 (The Holy Line)
|
# D01Z01S01 (The Holy Line)
|
||||||
@@ -2488,7 +2489,7 @@ def rules(blasphemousworld):
|
|||||||
|
|
||||||
# D04Z02S01 (Mother of Mothers)
|
# D04Z02S01 (Mother of Mothers)
|
||||||
# Items
|
# Items
|
||||||
if world.purified_hand[player]:
|
if options.purified_hand:
|
||||||
set_rule(world.get_location("MoM: Western room ledge", player),
|
set_rule(world.get_location("MoM: Western room ledge", player),
|
||||||
lambda state: (
|
lambda state: (
|
||||||
state.has("D04Z02S01[N]", player)
|
state.has("D04Z02S01[N]", player)
|
||||||
@@ -4093,7 +4094,7 @@ def rules(blasphemousworld):
|
|||||||
|
|
||||||
# D17Z01S04 (Brotherhood of the Silent Sorrow)
|
# D17Z01S04 (Brotherhood of the Silent Sorrow)
|
||||||
# Items
|
# Items
|
||||||
if world.boots_of_pleading[player]:
|
if options.boots_of_pleading:
|
||||||
set_rule(world.get_location("BotSS: 2nd meeting with Redento", player),
|
set_rule(world.get_location("BotSS: 2nd meeting with Redento", player),
|
||||||
lambda state: redento(state, blasphemousworld, player, 2))
|
lambda state: redento(state, blasphemousworld, player, 2))
|
||||||
# Doors
|
# Doors
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from .Locations import location_table
|
|||||||
from .Rooms import room_table, door_table
|
from .Rooms import room_table, door_table
|
||||||
from .Rules import rules
|
from .Rules import rules
|
||||||
from worlds.generic.Rules import set_rule, add_rule
|
from worlds.generic.Rules import set_rule, add_rule
|
||||||
from .Options import blasphemous_options
|
from .Options import BlasphemousOptions
|
||||||
from .Vanilla import unrandomized_dict, junk_locations, thorn_set, skill_dict
|
from .Vanilla import unrandomized_dict, junk_locations, thorn_set, skill_dict
|
||||||
|
|
||||||
|
|
||||||
@@ -39,7 +39,8 @@ class BlasphemousWorld(World):
|
|||||||
location_name_to_game_id = {loc["name"]: loc["game_id"] for loc in location_table}
|
location_name_to_game_id = {loc["name"]: loc["game_id"] for loc in location_table}
|
||||||
|
|
||||||
item_name_groups = group_table
|
item_name_groups = group_table
|
||||||
option_definitions = blasphemous_options
|
options = BlasphemousOptions
|
||||||
|
options_dataclass = BlasphemousOptions
|
||||||
|
|
||||||
required_client_version = (0, 4, 2)
|
required_client_version = (0, 4, 2)
|
||||||
|
|
||||||
@@ -73,60 +74,61 @@ class BlasphemousWorld(World):
|
|||||||
|
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
|
options = self.options
|
||||||
world = self.multiworld
|
world = self.multiworld
|
||||||
player = self.player
|
player = self.player
|
||||||
|
|
||||||
if not world.starting_location[player].randomized:
|
if not options.starting_location.randomized:
|
||||||
if world.starting_location[player].value == 6 and world.difficulty[player].value < 2:
|
if options.starting_location.value == 6 and options.difficulty.value < 2:
|
||||||
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {world.starting_location[player]}"
|
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {options.starting_location}"
|
||||||
" cannot be chosen if Difficulty is lower than Hard.")
|
" cannot be chosen if Difficulty is lower than Hard.")
|
||||||
|
|
||||||
if (world.starting_location[player].value == 0 or world.starting_location[player].value == 6) \
|
if (options.starting_location.value == 0 or options.starting_location.value == 6) \
|
||||||
and world.dash_shuffle[player]:
|
and options.dash_shuffle:
|
||||||
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {world.starting_location[player]}"
|
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {options.starting_location}"
|
||||||
" cannot be chosen if Shuffle Dash is enabled.")
|
" cannot be chosen if Shuffle Dash is enabled.")
|
||||||
|
|
||||||
if world.starting_location[player].value == 3 and world.wall_climb_shuffle[player]:
|
if options.starting_location.value == 3 and options.wall_climb_shuffle:
|
||||||
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {world.starting_location[player]}"
|
raise Exception(f"[Blasphemous - '{world.get_player_name(player)}'] {options.starting_location}"
|
||||||
" cannot be chosen if Shuffle Wall Climb is enabled.")
|
" cannot be chosen if Shuffle Wall Climb is enabled.")
|
||||||
else:
|
else:
|
||||||
locations: List[int] = [ 0, 1, 2, 3, 4, 5, 6 ]
|
locations: List[int] = [ 0, 1, 2, 3, 4, 5, 6 ]
|
||||||
invalid: bool = False
|
invalid: bool = False
|
||||||
|
|
||||||
if world.difficulty[player].value < 2:
|
if options.difficulty.value < 2:
|
||||||
locations.remove(6)
|
locations.remove(6)
|
||||||
|
|
||||||
if world.dash_shuffle[player]:
|
if options.dash_shuffle:
|
||||||
locations.remove(0)
|
locations.remove(0)
|
||||||
if 6 in locations:
|
if 6 in locations:
|
||||||
locations.remove(6)
|
locations.remove(6)
|
||||||
|
|
||||||
if world.wall_climb_shuffle[player]:
|
if options.wall_climb_shuffle:
|
||||||
locations.remove(3)
|
locations.remove(3)
|
||||||
|
|
||||||
if world.starting_location[player].value == 6 and world.difficulty[player].value < 2:
|
if options.starting_location.value == 6 and options.difficulty.value < 2:
|
||||||
invalid = True
|
invalid = True
|
||||||
|
|
||||||
if (world.starting_location[player].value == 0 or world.starting_location[player].value == 6) \
|
if (options.starting_location.value == 0 or options.starting_location.value == 6) \
|
||||||
and world.dash_shuffle[player]:
|
and options.dash_shuffle:
|
||||||
invalid = True
|
invalid = True
|
||||||
|
|
||||||
if world.starting_location[player].value == 3 and world.wall_climb_shuffle[player]:
|
if options.starting_location.value == 3 and options.wall_climb_shuffle:
|
||||||
invalid = True
|
invalid = True
|
||||||
|
|
||||||
if invalid:
|
if invalid:
|
||||||
world.starting_location[player].value = world.random.choice(locations)
|
options.starting_location.value = world.random.choice(locations)
|
||||||
|
|
||||||
|
|
||||||
if not world.dash_shuffle[player]:
|
if not options.dash_shuffle:
|
||||||
world.push_precollected(self.create_item("Dash Ability"))
|
world.push_precollected(self.create_item("Dash Ability"))
|
||||||
|
|
||||||
if not world.wall_climb_shuffle[player]:
|
if not options.wall_climb_shuffle:
|
||||||
world.push_precollected(self.create_item("Wall Climb Ability"))
|
world.push_precollected(self.create_item("Wall Climb Ability"))
|
||||||
|
|
||||||
if world.skip_long_quests[player]:
|
if options.skip_long_quests:
|
||||||
for loc in junk_locations:
|
for loc in junk_locations:
|
||||||
world.exclude_locations[player].value.add(loc)
|
options.exclude_locations.value.add(loc)
|
||||||
|
|
||||||
start_rooms: Dict[int, str] = {
|
start_rooms: Dict[int, str] = {
|
||||||
0: "D17Z01S01",
|
0: "D17Z01S01",
|
||||||
@@ -138,12 +140,12 @@ class BlasphemousWorld(World):
|
|||||||
6: "D20Z02S09"
|
6: "D20Z02S09"
|
||||||
}
|
}
|
||||||
|
|
||||||
self.start_room = start_rooms[world.starting_location[player].value]
|
self.start_room = start_rooms[options.starting_location.value]
|
||||||
|
|
||||||
|
|
||||||
def create_items(self):
|
def create_items(self):
|
||||||
|
options = self.options
|
||||||
world = self.multiworld
|
world = self.multiworld
|
||||||
player = self.player
|
|
||||||
|
|
||||||
removed: int = 0
|
removed: int = 0
|
||||||
to_remove: List[str] = [
|
to_remove: List[str] = [
|
||||||
@@ -157,46 +159,46 @@ class BlasphemousWorld(World):
|
|||||||
skipped_items = []
|
skipped_items = []
|
||||||
junk: int = 0
|
junk: int = 0
|
||||||
|
|
||||||
for item, count in world.start_inventory[player].value.items():
|
for item, count in options.start_inventory.value.items():
|
||||||
for _ in range(count):
|
for _ in range(count):
|
||||||
skipped_items.append(item)
|
skipped_items.append(item)
|
||||||
junk += 1
|
junk += 1
|
||||||
|
|
||||||
skipped_items.extend(unrandomized_dict.values())
|
skipped_items.extend(unrandomized_dict.values())
|
||||||
|
|
||||||
if world.thorn_shuffle[player] == 2:
|
if options.thorn_shuffle == 2:
|
||||||
for i in range(8):
|
for i in range(8):
|
||||||
skipped_items.append("Thorn Upgrade")
|
skipped_items.append("Thorn Upgrade")
|
||||||
|
|
||||||
if world.dash_shuffle[player]:
|
if options.dash_shuffle:
|
||||||
skipped_items.append(to_remove[removed])
|
skipped_items.append(to_remove[removed])
|
||||||
removed += 1
|
removed += 1
|
||||||
elif not world.dash_shuffle[player]:
|
elif not options.dash_shuffle:
|
||||||
skipped_items.append("Dash Ability")
|
skipped_items.append("Dash Ability")
|
||||||
|
|
||||||
if world.wall_climb_shuffle[player]:
|
if options.wall_climb_shuffle:
|
||||||
skipped_items.append(to_remove[removed])
|
skipped_items.append(to_remove[removed])
|
||||||
removed += 1
|
removed += 1
|
||||||
elif not world.wall_climb_shuffle[player]:
|
elif not options.wall_climb_shuffle:
|
||||||
skipped_items.append("Wall Climb Ability")
|
skipped_items.append("Wall Climb Ability")
|
||||||
|
|
||||||
if not world.reliquary_shuffle[player]:
|
if not options.reliquary_shuffle:
|
||||||
skipped_items.extend(reliquary_set)
|
skipped_items.extend(reliquary_set)
|
||||||
elif world.reliquary_shuffle[player]:
|
elif options.reliquary_shuffle:
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
skipped_items.append(to_remove[removed])
|
skipped_items.append(to_remove[removed])
|
||||||
removed += 1
|
removed += 1
|
||||||
|
|
||||||
if not world.boots_of_pleading[player]:
|
if not options.boots_of_pleading:
|
||||||
skipped_items.append("Boots of Pleading")
|
skipped_items.append("Boots of Pleading")
|
||||||
|
|
||||||
if not world.purified_hand[player]:
|
if not options.purified_hand:
|
||||||
skipped_items.append("Purified Hand of the Nun")
|
skipped_items.append("Purified Hand of the Nun")
|
||||||
|
|
||||||
if world.start_wheel[player]:
|
if options.start_wheel:
|
||||||
skipped_items.append("The Young Mason's Wheel")
|
skipped_items.append("The Young Mason's Wheel")
|
||||||
|
|
||||||
if not world.skill_randomizer[player]:
|
if not options.skill_randomizer:
|
||||||
skipped_items.extend(skill_dict.values())
|
skipped_items.extend(skill_dict.values())
|
||||||
|
|
||||||
counter = Counter(skipped_items)
|
counter = Counter(skipped_items)
|
||||||
@@ -219,23 +221,24 @@ class BlasphemousWorld(World):
|
|||||||
|
|
||||||
|
|
||||||
def pre_fill(self):
|
def pre_fill(self):
|
||||||
|
options = self.options
|
||||||
world = self.multiworld
|
world = self.multiworld
|
||||||
player = self.player
|
player = self.player
|
||||||
|
|
||||||
self.place_items_from_dict(unrandomized_dict)
|
self.place_items_from_dict(unrandomized_dict)
|
||||||
|
|
||||||
if world.thorn_shuffle[player] == 2:
|
if options.thorn_shuffle == 2:
|
||||||
self.place_items_from_set(thorn_set, "Thorn Upgrade")
|
self.place_items_from_set(thorn_set, "Thorn Upgrade")
|
||||||
|
|
||||||
if world.start_wheel[player]:
|
if options.start_wheel:
|
||||||
world.get_location("Beginning gift", player)\
|
world.get_location("Beginning gift", player)\
|
||||||
.place_locked_item(self.create_item("The Young Mason's Wheel"))
|
.place_locked_item(self.create_item("The Young Mason's Wheel"))
|
||||||
|
|
||||||
if not world.skill_randomizer[player]:
|
if not options.skill_randomizer:
|
||||||
self.place_items_from_dict(skill_dict)
|
self.place_items_from_dict(skill_dict)
|
||||||
|
|
||||||
if world.thorn_shuffle[player] == 1:
|
if options.thorn_shuffle == 1:
|
||||||
world.local_items[player].value.add("Thorn Upgrade")
|
options.local_items.value.add("Thorn Upgrade")
|
||||||
|
|
||||||
|
|
||||||
def place_items_from_set(self, location_set: Set[str], name: str):
|
def place_items_from_set(self, location_set: Set[str], name: str):
|
||||||
@@ -251,6 +254,7 @@ class BlasphemousWorld(World):
|
|||||||
|
|
||||||
|
|
||||||
def create_regions(self) -> None:
|
def create_regions(self) -> None:
|
||||||
|
options = self.options
|
||||||
player = self.player
|
player = self.player
|
||||||
world = self.multiworld
|
world = self.multiworld
|
||||||
|
|
||||||
@@ -282,9 +286,9 @@ class BlasphemousWorld(World):
|
|||||||
})
|
})
|
||||||
|
|
||||||
for index, loc in enumerate(location_table):
|
for index, loc in enumerate(location_table):
|
||||||
if not world.boots_of_pleading[player] and loc["name"] == "BotSS: 2nd meeting with Redento":
|
if not options.boots_of_pleading and loc["name"] == "BotSS: 2nd meeting with Redento":
|
||||||
continue
|
continue
|
||||||
if not world.purified_hand[player] and loc["name"] == "MoM: Western room ledge":
|
if not options.purified_hand and loc["name"] == "MoM: Western room ledge":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
region: Region = world.get_region(loc["room"], player)
|
region: Region = world.get_region(loc["room"], player)
|
||||||
@@ -310,9 +314,9 @@ class BlasphemousWorld(World):
|
|||||||
victory.place_locked_item(self.create_event("Victory"))
|
victory.place_locked_item(self.create_event("Victory"))
|
||||||
world.get_region("D07Z01S03", player).locations.append(victory)
|
world.get_region("D07Z01S03", player).locations.append(victory)
|
||||||
|
|
||||||
if world.ending[self.player].value == 1:
|
if options.ending.value == 1:
|
||||||
set_rule(victory, lambda state: state.has("Thorn Upgrade", player, 8))
|
set_rule(victory, lambda state: state.has("Thorn Upgrade", player, 8))
|
||||||
elif world.ending[self.player].value == 2:
|
elif options.ending.value == 2:
|
||||||
set_rule(victory, lambda state: state.has("Thorn Upgrade", player, 8) and
|
set_rule(victory, lambda state: state.has("Thorn Upgrade", player, 8) and
|
||||||
state.has("Holy Wound of Abnegation", player))
|
state.has("Holy Wound of Abnegation", player))
|
||||||
|
|
||||||
@@ -332,11 +336,12 @@ class BlasphemousWorld(World):
|
|||||||
locations = []
|
locations = []
|
||||||
doors: Dict[str, str] = {}
|
doors: Dict[str, str] = {}
|
||||||
|
|
||||||
|
options = self.options
|
||||||
world = self.multiworld
|
world = self.multiworld
|
||||||
player = self.player
|
player = self.player
|
||||||
thorns: bool = True
|
thorns: bool = True
|
||||||
|
|
||||||
if world.thorn_shuffle[player].value == 2:
|
if options.thorn_shuffle.value == 2:
|
||||||
thorns = False
|
thorns = False
|
||||||
|
|
||||||
for loc in world.get_filled_locations(player):
|
for loc in world.get_filled_locations(player):
|
||||||
@@ -354,28 +359,28 @@ class BlasphemousWorld(World):
|
|||||||
locations.append(data)
|
locations.append(data)
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"LogicDifficulty": world.difficulty[player].value,
|
"LogicDifficulty": options.difficulty.value,
|
||||||
"StartingLocation": world.starting_location[player].value,
|
"StartingLocation": options.starting_location.value,
|
||||||
"VersionCreated": "AP",
|
"VersionCreated": "AP",
|
||||||
|
|
||||||
"UnlockTeleportation": bool(world.prie_dieu_warp[player].value),
|
"UnlockTeleportation": bool(options.prie_dieu_warp.value),
|
||||||
"AllowHints": bool(world.corpse_hints[player].value),
|
"AllowHints": bool(options.corpse_hints.value),
|
||||||
"AllowPenitence": bool(world.penitence[player].value),
|
"AllowPenitence": bool(options.penitence.value),
|
||||||
|
|
||||||
"ShuffleReliquaries": bool(world.reliquary_shuffle[player].value),
|
"ShuffleReliquaries": bool(options.reliquary_shuffle.value),
|
||||||
"ShuffleBootsOfPleading": bool(world.boots_of_pleading[player].value),
|
"ShuffleBootsOfPleading": bool(options.boots_of_pleading.value),
|
||||||
"ShufflePurifiedHand": bool(world.purified_hand[player].value),
|
"ShufflePurifiedHand": bool(options.purified_hand.value),
|
||||||
"ShuffleDash": bool(world.dash_shuffle[player].value),
|
"ShuffleDash": bool(options.dash_shuffle.value),
|
||||||
"ShuffleWallClimb": bool(world.wall_climb_shuffle[player].value),
|
"ShuffleWallClimb": bool(options.wall_climb_shuffle.value),
|
||||||
|
|
||||||
"ShuffleSwordSkills": bool(world.skill_randomizer[player].value),
|
"ShuffleSwordSkills": bool(options.skill_randomizer.value),
|
||||||
"ShuffleThorns": thorns,
|
"ShuffleThorns": thorns,
|
||||||
"JunkLongQuests": bool(world.skip_long_quests[player].value),
|
"JunkLongQuests": bool(options.skip_long_quests.value),
|
||||||
"StartWithWheel": bool(world.start_wheel[player].value),
|
"StartWithWheel": bool(options.start_wheel.value),
|
||||||
|
|
||||||
"EnemyShuffleType": world.enemy_randomizer[player].value,
|
"EnemyShuffleType": options.enemy_randomizer.value,
|
||||||
"MaintainClass": bool(world.enemy_groups[player].value),
|
"MaintainClass": bool(options.enemy_groups.value),
|
||||||
"AreaScaling": bool(world.enemy_scaling[player].value),
|
"AreaScaling": bool(options.enemy_scaling.value),
|
||||||
|
|
||||||
"BossShuffleType": 0,
|
"BossShuffleType": 0,
|
||||||
"DoorShuffleType": 0
|
"DoorShuffleType": 0
|
||||||
@@ -385,8 +390,8 @@ class BlasphemousWorld(World):
|
|||||||
"locations": locations,
|
"locations": locations,
|
||||||
"doors": doors,
|
"doors": doors,
|
||||||
"cfg": config,
|
"cfg": config,
|
||||||
"ending": world.ending[self.player].value,
|
"ending": options.ending.value,
|
||||||
"death_link": bool(world.death_link[self.player].value)
|
"death_link": bool(options.death_link.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return slot_data
|
return slot_data
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
from Options import Option, Range
|
from Options import Option, Range, PerGameCommonOptions
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
class TaskAdvances(Range):
|
class TaskAdvances(Range):
|
||||||
@@ -69,12 +70,12 @@ class KillerTrapWeight(Range):
|
|||||||
default = 0
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
bumpstik_options: typing.Dict[str, type(Option)] = {
|
@dataclass
|
||||||
"task_advances": TaskAdvances,
|
class BumpStikOptions(PerGameCommonOptions):
|
||||||
"turners": Turners,
|
task_advances: TaskAdvances
|
||||||
"paint_cans": PaintCans,
|
turners: Turners
|
||||||
"trap_count": Traps,
|
paint_cans: PaintCans
|
||||||
"rainbow_trap_weight": RainbowTrapWeight,
|
trap_count: Traps
|
||||||
"spinner_trap_weight": SpinnerTrapWeight,
|
rainbow_trap_weight: RainbowTrapWeight
|
||||||
"killer_trap_weight": KillerTrapWeight
|
spinner_trap_weight: SpinnerTrapWeight
|
||||||
}
|
killer_trap_weight: KillerTrapWeight
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ class BumpStikWorld(World):
|
|||||||
|
|
||||||
required_client_version = (0, 3, 8)
|
required_client_version = (0, 3, 8)
|
||||||
|
|
||||||
option_definitions = bumpstik_options
|
options = BumpStikOptions
|
||||||
|
options_dataclass = BumpStikOptions
|
||||||
|
|
||||||
def __init__(self, world: MultiWorld, player: int):
|
def __init__(self, world: MultiWorld, player: int):
|
||||||
super(BumpStikWorld, self).__init__(world, player)
|
super(BumpStikWorld, self).__init__(world, player)
|
||||||
@@ -86,13 +87,13 @@ class BumpStikWorld(World):
|
|||||||
return "Nothing"
|
return "Nothing"
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
self.task_advances = self.multiworld.task_advances[self.player].value
|
self.task_advances = self.options.task_advances.value
|
||||||
self.turners = self.multiworld.turners[self.player].value
|
self.turners = self.options.turners.value
|
||||||
self.paint_cans = self.multiworld.paint_cans[self.player].value
|
self.paint_cans = self.options.paint_cans.value
|
||||||
self.traps = self.multiworld.trap_count[self.player].value
|
self.traps = self.options.trap_count.value
|
||||||
self.rainbow_trap_weight = self.multiworld.rainbow_trap_weight[self.player].value
|
self.rainbow_trap_weight = self.options.rainbow_trap_weight.value
|
||||||
self.spinner_trap_weight = self.multiworld.spinner_trap_weight[self.player].value
|
self.spinner_trap_weight = self.options.spinner_trap_weight.value
|
||||||
self.killer_trap_weight = self.multiworld.killer_trap_weight[self.player].value
|
self.killer_trap_weight = self.options.killer_trap_weight.value
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
create_regions(self.multiworld, self.player)
|
create_regions(self.multiworld, self.player)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import typing
|
import typing
|
||||||
from Options import Option
|
from Options import Option, PerGameCommonOptions
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
checksfinder_options: typing.Dict[str, type(Option)] = {
|
class ChecksFinderOptions(PerGameCommonOptions):
|
||||||
}
|
pass
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification
|
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification
|
||||||
from .Items import ChecksFinderItem, item_table, required_items
|
from .Items import ChecksFinderItem, item_table, required_items
|
||||||
from .Locations import ChecksFinderAdvancement, advancement_table, exclusion_table
|
from .Locations import ChecksFinderAdvancement, advancement_table, exclusion_table
|
||||||
from .Options import checksfinder_options
|
from .Options import ChecksFinderOptions
|
||||||
from .Rules import set_rules, set_completion_rules
|
from .Rules import set_rules, set_completion_rules
|
||||||
from ..AutoWorld import World, WebWorld
|
from ..AutoWorld import World, WebWorld
|
||||||
|
from dataclasses import fields
|
||||||
|
|
||||||
client_version = 7
|
client_version = 7
|
||||||
|
|
||||||
@@ -26,7 +27,8 @@ class ChecksFinderWorld(World):
|
|||||||
with the mines! You win when you get all your items and beat the board!
|
with the mines! You win when you get all your items and beat the board!
|
||||||
"""
|
"""
|
||||||
game: str = "ChecksFinder"
|
game: str = "ChecksFinder"
|
||||||
option_definitions = checksfinder_options
|
options = ChecksFinderOptions
|
||||||
|
options_dataclass = ChecksFinderOptions
|
||||||
topology_present = True
|
topology_present = True
|
||||||
web = ChecksFinderWeb()
|
web = ChecksFinderWeb()
|
||||||
|
|
||||||
@@ -79,8 +81,8 @@ class ChecksFinderWorld(World):
|
|||||||
|
|
||||||
def fill_slot_data(self):
|
def fill_slot_data(self):
|
||||||
slot_data = self._get_checksfinder_data()
|
slot_data = self._get_checksfinder_data()
|
||||||
for option_name in checksfinder_options:
|
for option_name in [field.name for field in fields(ChecksFinderOptions)]:
|
||||||
option = getattr(self.multiworld, option_name)[self.player]
|
option = getattr(self.options, option_name)
|
||||||
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
|
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
|
||||||
slot_data[option_name] = int(option.value)
|
slot_data[option_name] = int(option.value)
|
||||||
return slot_data
|
return slot_data
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from typing import Callable, Dict, NamedTuple, Optional
|
from typing import Callable, Dict, NamedTuple, Optional
|
||||||
|
|
||||||
from BaseClasses import Item, ItemClassification, MultiWorld
|
from BaseClasses import Item, ItemClassification
|
||||||
|
from .Options import CliqueOptions
|
||||||
|
|
||||||
|
|
||||||
class CliqueItem(Item):
|
class CliqueItem(Item):
|
||||||
@@ -10,7 +11,7 @@ class CliqueItem(Item):
|
|||||||
class CliqueItemData(NamedTuple):
|
class CliqueItemData(NamedTuple):
|
||||||
code: Optional[int] = None
|
code: Optional[int] = None
|
||||||
type: ItemClassification = ItemClassification.filler
|
type: ItemClassification = ItemClassification.filler
|
||||||
can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True
|
can_create: Callable[[CliqueOptions], bool] = lambda options: True
|
||||||
|
|
||||||
|
|
||||||
item_data_table: Dict[str, CliqueItemData] = {
|
item_data_table: Dict[str, CliqueItemData] = {
|
||||||
@@ -21,11 +22,11 @@ item_data_table: Dict[str, CliqueItemData] = {
|
|||||||
"Button Activation": CliqueItemData(
|
"Button Activation": CliqueItemData(
|
||||||
code=69696968,
|
code=69696968,
|
||||||
type=ItemClassification.progression,
|
type=ItemClassification.progression,
|
||||||
can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]),
|
can_create=lambda options: bool(getattr(options, "hard_mode")),
|
||||||
),
|
),
|
||||||
"A Cool Filler Item (No Satisfaction Guaranteed)": CliqueItemData(
|
"A Cool Filler Item (No Satisfaction Guaranteed)": CliqueItemData(
|
||||||
code=69696967,
|
code=69696967,
|
||||||
can_create=lambda multiworld, player: False # Only created from `get_filler_item_name`.
|
can_create=lambda options: False # Only created from `get_filler_item_name`.
|
||||||
),
|
),
|
||||||
"The Urge to Push": CliqueItemData(
|
"The Urge to Push": CliqueItemData(
|
||||||
type=ItemClassification.progression,
|
type=ItemClassification.progression,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from typing import Callable, Dict, NamedTuple, Optional
|
from typing import Callable, Dict, NamedTuple, Optional
|
||||||
|
|
||||||
from BaseClasses import Location, MultiWorld
|
from BaseClasses import Location
|
||||||
|
from .Options import CliqueOptions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CliqueLocation(Location):
|
class CliqueLocation(Location):
|
||||||
@@ -10,7 +12,7 @@ class CliqueLocation(Location):
|
|||||||
class CliqueLocationData(NamedTuple):
|
class CliqueLocationData(NamedTuple):
|
||||||
region: str
|
region: str
|
||||||
address: Optional[int] = None
|
address: Optional[int] = None
|
||||||
can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True
|
can_create: Callable[[CliqueOptions], bool] = lambda options: True
|
||||||
locked_item: Optional[str] = None
|
locked_item: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ location_data_table: Dict[str, CliqueLocationData] = {
|
|||||||
"The Item on the Desk": CliqueLocationData(
|
"The Item on the Desk": CliqueLocationData(
|
||||||
region="The Button Realm",
|
region="The Button Realm",
|
||||||
address=69696968,
|
address=69696968,
|
||||||
can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]),
|
can_create=lambda options: bool(getattr(options, "hard_mode")),
|
||||||
),
|
),
|
||||||
"In the Player's Mind": CliqueLocationData(
|
"In the Player's Mind": CliqueLocationData(
|
||||||
region="The Button Realm",
|
region="The Button Realm",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from Options import Choice, Option, Toggle
|
from Options import Choice, Option, Toggle, PerGameCommonOptions
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
class HardMode(Toggle):
|
class HardMode(Toggle):
|
||||||
@@ -25,10 +26,12 @@ class ButtonColor(Choice):
|
|||||||
option_black = 11
|
option_black = 11
|
||||||
|
|
||||||
|
|
||||||
clique_options: Dict[str, type(Option)] = {
|
|
||||||
"color": ButtonColor,
|
@dataclass
|
||||||
"hard_mode": HardMode,
|
class CliqueOptions(PerGameCommonOptions):
|
||||||
|
color: ButtonColor
|
||||||
|
hard_mode: HardMode
|
||||||
|
|
||||||
# DeathLink is always on. Always.
|
# DeathLink is always on. Always.
|
||||||
# "death_link": DeathLink,
|
# death_link: DeathLink
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from BaseClasses import CollectionState, MultiWorld
|
from BaseClasses import CollectionState
|
||||||
|
from .Options import CliqueOptions
|
||||||
|
|
||||||
|
|
||||||
def get_button_rule(multiworld: MultiWorld, player: int) -> Callable[[CollectionState], bool]:
|
def get_button_rule(options: CliqueOptions, player: int) -> Callable[[CollectionState], bool]:
|
||||||
if getattr(multiworld, "hard_mode")[player]:
|
if getattr(options, "hard_mode"):
|
||||||
return lambda state: state.has("Button Activation", player)
|
return lambda state: state.has("Button Activation", player)
|
||||||
|
|
||||||
return lambda state: True
|
return lambda state: True
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from BaseClasses import Region, Tutorial
|
|||||||
from worlds.AutoWorld import WebWorld, World
|
from worlds.AutoWorld import WebWorld, World
|
||||||
from .Items import CliqueItem, item_data_table, item_table
|
from .Items import CliqueItem, item_data_table, item_table
|
||||||
from .Locations import CliqueLocation, location_data_table, location_table, locked_locations
|
from .Locations import CliqueLocation, location_data_table, location_table, locked_locations
|
||||||
from .Options import clique_options
|
from .Options import CliqueOptions
|
||||||
from .Regions import region_data_table
|
from .Regions import region_data_table
|
||||||
from .Rules import get_button_rule
|
from .Rules import get_button_rule
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@ class CliqueWorld(World):
|
|||||||
game = "Clique"
|
game = "Clique"
|
||||||
data_version = 3
|
data_version = 3
|
||||||
web = CliqueWebWorld()
|
web = CliqueWebWorld()
|
||||||
option_definitions = clique_options
|
options = CliqueOptions
|
||||||
|
options_dataclass = CliqueOptions
|
||||||
location_name_to_id = location_table
|
location_name_to_id = location_table
|
||||||
item_name_to_id = item_table
|
item_name_to_id = item_table
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ class CliqueWorld(World):
|
|||||||
def create_items(self) -> None:
|
def create_items(self) -> None:
|
||||||
item_pool: List[CliqueItem] = []
|
item_pool: List[CliqueItem] = []
|
||||||
for name, item in item_data_table.items():
|
for name, item in item_data_table.items():
|
||||||
if item.code and item.can_create(self.multiworld, self.player):
|
if item.code and item.can_create(self.options):
|
||||||
item_pool.append(self.create_item(name))
|
item_pool.append(self.create_item(name))
|
||||||
|
|
||||||
self.multiworld.itempool += item_pool
|
self.multiworld.itempool += item_pool
|
||||||
@@ -55,27 +56,27 @@ class CliqueWorld(World):
|
|||||||
region = self.multiworld.get_region(region_name, self.player)
|
region = self.multiworld.get_region(region_name, self.player)
|
||||||
region.add_locations({
|
region.add_locations({
|
||||||
location_name: location_data.address for location_name, location_data in location_data_table.items()
|
location_name: location_data.address for location_name, location_data in location_data_table.items()
|
||||||
if location_data.region == region_name and location_data.can_create(self.multiworld, self.player)
|
if location_data.region == region_name and location_data.can_create(self.options)
|
||||||
}, CliqueLocation)
|
}, CliqueLocation)
|
||||||
region.add_exits(region_data_table[region_name].connecting_regions)
|
region.add_exits(region_data_table[region_name].connecting_regions)
|
||||||
|
|
||||||
# Place locked locations.
|
# Place locked locations.
|
||||||
for location_name, location_data in locked_locations.items():
|
for location_name, location_data in locked_locations.items():
|
||||||
# Ignore locations we never created.
|
# Ignore locations we never created.
|
||||||
if not location_data.can_create(self.multiworld, self.player):
|
if not location_data.can_create(self.options):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
locked_item = self.create_item(location_data_table[location_name].locked_item)
|
locked_item = self.create_item(location_data_table[location_name].locked_item)
|
||||||
self.multiworld.get_location(location_name, self.player).place_locked_item(locked_item)
|
self.multiworld.get_location(location_name, self.player).place_locked_item(locked_item)
|
||||||
|
|
||||||
# Set priority location for the Big Red Button!
|
# Set priority location for the Big Red Button!
|
||||||
self.multiworld.priority_locations[self.player].value.add("The Big Red Button")
|
self.options.priority_locations.value.add("The Big Red Button")
|
||||||
|
|
||||||
def get_filler_item_name(self) -> str:
|
def get_filler_item_name(self) -> str:
|
||||||
return "A Cool Filler Item (No Satisfaction Guaranteed)"
|
return "A Cool Filler Item (No Satisfaction Guaranteed)"
|
||||||
|
|
||||||
def set_rules(self) -> None:
|
def set_rules(self) -> None:
|
||||||
button_rule = get_button_rule(self.multiworld, self.player)
|
button_rule = get_button_rule(self.options, self.player)
|
||||||
self.multiworld.get_location("The Big Red Button", self.player).access_rule = button_rule
|
self.multiworld.get_location("The Big Red Button", self.player).access_rule = button_rule
|
||||||
self.multiworld.get_location("In the Player's Mind", self.player).access_rule = button_rule
|
self.multiworld.get_location("In the Player's Mind", self.player).access_rule = button_rule
|
||||||
|
|
||||||
@@ -88,5 +89,5 @@ class CliqueWorld(World):
|
|||||||
|
|
||||||
def fill_slot_data(self):
|
def fill_slot_data(self):
|
||||||
return {
|
return {
|
||||||
"color": getattr(self.multiworld, "color")[self.player].current_key
|
"color": getattr(self.options, "color").current_key
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
from Options import Toggle, DefaultOnToggle, Option, Range, Choice, ItemDict, DeathLink
|
from Options import Toggle, DefaultOnToggle, Option, Range, Choice, ItemDict, DeathLink, PerGameCommonOptions
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
class RandomizeWeaponLocations(DefaultOnToggle):
|
class RandomizeWeaponLocations(DefaultOnToggle):
|
||||||
@@ -200,36 +201,36 @@ class EnableDLCOption(Toggle):
|
|||||||
display_name = "Enable DLC"
|
display_name = "Enable DLC"
|
||||||
|
|
||||||
|
|
||||||
dark_souls_options: typing.Dict[str, Option] = {
|
@dataclass
|
||||||
"enable_weapon_locations": RandomizeWeaponLocations,
|
class DarkSouls3Options(PerGameCommonOptions):
|
||||||
"enable_shield_locations": RandomizeShieldLocations,
|
enable_weapon_locations: RandomizeWeaponLocations
|
||||||
"enable_armor_locations": RandomizeArmorLocations,
|
enable_shield_locations: RandomizeShieldLocations
|
||||||
"enable_ring_locations": RandomizeRingLocations,
|
enable_armor_locations: RandomizeArmorLocations
|
||||||
"enable_spell_locations": RandomizeSpellLocations,
|
enable_ring_locations: RandomizeRingLocations
|
||||||
"enable_key_locations": RandomizeKeyLocations,
|
enable_spell_locations: RandomizeSpellLocations
|
||||||
"enable_boss_locations": RandomizeBossSoulLocations,
|
enable_key_locations: RandomizeKeyLocations
|
||||||
"enable_npc_locations": RandomizeNPCLocations,
|
enable_boss_locations: RandomizeBossSoulLocations
|
||||||
"enable_misc_locations": RandomizeMiscLocations,
|
enable_npc_locations: RandomizeNPCLocations
|
||||||
"enable_health_upgrade_locations": RandomizeHealthLocations,
|
enable_misc_locations: RandomizeMiscLocations
|
||||||
"enable_progressive_locations": RandomizeProgressiveLocationsOption,
|
enable_health_upgrade_locations: RandomizeHealthLocations
|
||||||
"pool_type": PoolTypeOption,
|
enable_progressive_locations: RandomizeProgressiveLocationsOption
|
||||||
"guaranteed_items": GuaranteedItemsOption,
|
pool_type: PoolTypeOption
|
||||||
"auto_equip": AutoEquipOption,
|
guaranteed_items: GuaranteedItemsOption
|
||||||
"lock_equip": LockEquipOption,
|
auto_equip: AutoEquipOption
|
||||||
"no_weapon_requirements": NoWeaponRequirementsOption,
|
lock_equip: LockEquipOption
|
||||||
"randomize_infusion": RandomizeInfusionOption,
|
no_weapon_requirements: NoWeaponRequirementsOption
|
||||||
"randomize_infusion_percentage": RandomizeInfusionPercentageOption,
|
randomize_infusion: RandomizeInfusionOption
|
||||||
"randomize_weapon_level": RandomizeWeaponLevelOption,
|
randomize_infusion_percentage: RandomizeInfusionPercentageOption
|
||||||
"randomize_weapon_level_percentage": RandomizeWeaponLevelPercentageOption,
|
randomize_weapon_level: RandomizeWeaponLevelOption
|
||||||
"min_levels_in_5": MinLevelsIn5WeaponPoolOption,
|
randomize_weapon_level_percentage: RandomizeWeaponLevelPercentageOption
|
||||||
"max_levels_in_5": MaxLevelsIn5WeaponPoolOption,
|
min_levels_in_5: MinLevelsIn5WeaponPoolOption
|
||||||
"min_levels_in_10": MinLevelsIn10WeaponPoolOption,
|
max_levels_in_5: MaxLevelsIn5WeaponPoolOption
|
||||||
"max_levels_in_10": MaxLevelsIn10WeaponPoolOption,
|
min_levels_in_10: MinLevelsIn10WeaponPoolOption
|
||||||
"early_banner": EarlySmallLothricBanner,
|
max_levels_in_10: MaxLevelsIn10WeaponPoolOption
|
||||||
"late_basin_of_vows": LateBasinOfVowsOption,
|
early_banner: EarlySmallLothricBanner
|
||||||
"late_dlc": LateDLCOption,
|
late_basin_of_vows: LateBasinOfVowsOption
|
||||||
"no_spell_requirements": NoSpellRequirementsOption,
|
late_dlc: LateDLCOption
|
||||||
"no_equip_load": NoEquipLoadOption,
|
no_spell_requirements: NoSpellRequirementsOption
|
||||||
"death_link": DeathLink,
|
no_equip_load: NoEquipLoadOption
|
||||||
"enable_dlc": EnableDLCOption,
|
death_link: DeathLink
|
||||||
}
|
enable_dlc: EnableDLCOption
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from worlds.generic.Rules import set_rule, add_rule, add_item_rule
|
|||||||
|
|
||||||
from .Items import DarkSouls3Item, DS3ItemCategory, item_dictionary, key_item_names, item_descriptions
|
from .Items import DarkSouls3Item, DS3ItemCategory, item_dictionary, key_item_names, item_descriptions
|
||||||
from .Locations import DarkSouls3Location, DS3LocationCategory, location_tables, location_dictionary
|
from .Locations import DarkSouls3Location, DS3LocationCategory, location_tables, location_dictionary
|
||||||
from .Options import RandomizeWeaponLevelOption, PoolTypeOption, EarlySmallLothricBanner, dark_souls_options
|
from .Options import RandomizeWeaponLevelOption, PoolTypeOption, EarlySmallLothricBanner, DarkSouls3Options
|
||||||
|
|
||||||
|
|
||||||
class DarkSouls3Web(WebWorld):
|
class DarkSouls3Web(WebWorld):
|
||||||
@@ -43,7 +43,8 @@ class DarkSouls3World(World):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
game: str = "Dark Souls III"
|
game: str = "Dark Souls III"
|
||||||
option_definitions = dark_souls_options
|
options = DarkSouls3Options
|
||||||
|
options_dataclass = DarkSouls3Options
|
||||||
topology_present: bool = True
|
topology_present: bool = True
|
||||||
web = DarkSouls3Web()
|
web = DarkSouls3Web()
|
||||||
data_version = 8
|
data_version = 8
|
||||||
@@ -72,47 +73,47 @@ class DarkSouls3World(World):
|
|||||||
|
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
if self.multiworld.enable_weapon_locations[self.player] == Toggle.option_true:
|
if self.options.enable_weapon_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.WEAPON)
|
self.enabled_location_categories.add(DS3LocationCategory.WEAPON)
|
||||||
if self.multiworld.enable_shield_locations[self.player] == Toggle.option_true:
|
if self.options.enable_shield_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.SHIELD)
|
self.enabled_location_categories.add(DS3LocationCategory.SHIELD)
|
||||||
if self.multiworld.enable_armor_locations[self.player] == Toggle.option_true:
|
if self.options.enable_armor_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.ARMOR)
|
self.enabled_location_categories.add(DS3LocationCategory.ARMOR)
|
||||||
if self.multiworld.enable_ring_locations[self.player] == Toggle.option_true:
|
if self.options.enable_ring_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.RING)
|
self.enabled_location_categories.add(DS3LocationCategory.RING)
|
||||||
if self.multiworld.enable_spell_locations[self.player] == Toggle.option_true:
|
if self.options.enable_spell_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.SPELL)
|
self.enabled_location_categories.add(DS3LocationCategory.SPELL)
|
||||||
if self.multiworld.enable_npc_locations[self.player] == Toggle.option_true:
|
if self.options.enable_npc_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.NPC)
|
self.enabled_location_categories.add(DS3LocationCategory.NPC)
|
||||||
if self.multiworld.enable_key_locations[self.player] == Toggle.option_true:
|
if self.options.enable_key_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.KEY)
|
self.enabled_location_categories.add(DS3LocationCategory.KEY)
|
||||||
if self.multiworld.early_banner[self.player] == EarlySmallLothricBanner.option_early_global:
|
if self.options.early_banner == EarlySmallLothricBanner.option_early_global:
|
||||||
self.multiworld.early_items[self.player]['Small Lothric Banner'] = 1
|
self.options.early_items['Small Lothric Banner'] = 1
|
||||||
elif self.multiworld.early_banner[self.player] == EarlySmallLothricBanner.option_early_local:
|
elif self.options.early_banner == EarlySmallLothricBanner.option_early_local:
|
||||||
self.multiworld.local_early_items[self.player]['Small Lothric Banner'] = 1
|
self.options.local_early_items['Small Lothric Banner'] = 1
|
||||||
if self.multiworld.enable_boss_locations[self.player] == Toggle.option_true:
|
if self.options.enable_boss_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.BOSS)
|
self.enabled_location_categories.add(DS3LocationCategory.BOSS)
|
||||||
if self.multiworld.enable_misc_locations[self.player] == Toggle.option_true:
|
if self.options.enable_misc_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.MISC)
|
self.enabled_location_categories.add(DS3LocationCategory.MISC)
|
||||||
if self.multiworld.enable_health_upgrade_locations[self.player] == Toggle.option_true:
|
if self.options.enable_health_upgrade_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.HEALTH)
|
self.enabled_location_categories.add(DS3LocationCategory.HEALTH)
|
||||||
if self.multiworld.enable_progressive_locations[self.player] == Toggle.option_true:
|
if self.options.enable_progressive_locations == Toggle.option_true:
|
||||||
self.enabled_location_categories.add(DS3LocationCategory.PROGRESSIVE_ITEM)
|
self.enabled_location_categories.add(DS3LocationCategory.PROGRESSIVE_ITEM)
|
||||||
|
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
progressive_location_table = []
|
progressive_location_table = []
|
||||||
if self.multiworld.enable_progressive_locations[self.player]:
|
if self.options.enable_progressive_locations:
|
||||||
progressive_location_table = [] + \
|
progressive_location_table = [] + \
|
||||||
location_tables["Progressive Items 1"] + \
|
location_tables["Progressive Items 1"] + \
|
||||||
location_tables["Progressive Items 2"] + \
|
location_tables["Progressive Items 2"] + \
|
||||||
location_tables["Progressive Items 3"] + \
|
location_tables["Progressive Items 3"] + \
|
||||||
location_tables["Progressive Items 4"]
|
location_tables["Progressive Items 4"]
|
||||||
|
|
||||||
if self.multiworld.enable_dlc[self.player].value:
|
if self.options.enable_dlc.value:
|
||||||
progressive_location_table += location_tables["Progressive Items DLC"]
|
progressive_location_table += location_tables["Progressive Items DLC"]
|
||||||
|
|
||||||
if self.multiworld.enable_health_upgrade_locations[self.player]:
|
if self.options.enable_health_upgrade_locations:
|
||||||
progressive_location_table += location_tables["Progressive Items Health"]
|
progressive_location_table += location_tables["Progressive Items Health"]
|
||||||
|
|
||||||
# Create Vanilla Regions
|
# Create Vanilla Regions
|
||||||
@@ -146,7 +147,7 @@ class DarkSouls3World(World):
|
|||||||
regions["Consumed King's Garden"].locations.append(potd_location)
|
regions["Consumed King's Garden"].locations.append(potd_location)
|
||||||
|
|
||||||
# Create DLC Regions
|
# Create DLC Regions
|
||||||
if self.multiworld.enable_dlc[self.player]:
|
if self.options.enable_dlc:
|
||||||
regions.update({region_name: self.create_region(region_name, location_tables[region_name]) for region_name in [
|
regions.update({region_name: self.create_region(region_name, location_tables[region_name]) for region_name in [
|
||||||
"Painted World of Ariandel 1",
|
"Painted World of Ariandel 1",
|
||||||
"Painted World of Ariandel 2",
|
"Painted World of Ariandel 2",
|
||||||
@@ -192,7 +193,7 @@ class DarkSouls3World(World):
|
|||||||
create_connection("Consumed King's Garden", "Untended Graves")
|
create_connection("Consumed King's Garden", "Untended Graves")
|
||||||
|
|
||||||
# Connect DLC Regions
|
# Connect DLC Regions
|
||||||
if self.multiworld.enable_dlc[self.player]:
|
if self.options.enable_dlc:
|
||||||
create_connection("Cathedral of the Deep", "Painted World of Ariandel 1")
|
create_connection("Cathedral of the Deep", "Painted World of Ariandel 1")
|
||||||
create_connection("Painted World of Ariandel 1", "Painted World of Ariandel 2")
|
create_connection("Painted World of Ariandel 1", "Painted World of Ariandel 2")
|
||||||
create_connection("Painted World of Ariandel 2", "Dreg Heap")
|
create_connection("Painted World of Ariandel 2", "Dreg Heap")
|
||||||
@@ -240,7 +241,7 @@ class DarkSouls3World(World):
|
|||||||
|
|
||||||
|
|
||||||
def create_items(self):
|
def create_items(self):
|
||||||
dlc_enabled = self.multiworld.enable_dlc[self.player] == Toggle.option_true
|
dlc_enabled = self.options.enable_dlc == Toggle.option_true
|
||||||
|
|
||||||
itempool_by_category = {category: [] for category in self.enabled_location_categories}
|
itempool_by_category = {category: [] for category in self.enabled_location_categories}
|
||||||
|
|
||||||
@@ -254,7 +255,7 @@ class DarkSouls3World(World):
|
|||||||
itempool_by_category[location.category].append(location.default_item_name)
|
itempool_by_category[location.category].append(location.default_item_name)
|
||||||
|
|
||||||
# Replace each item category with a random sample of items of those types
|
# Replace each item category with a random sample of items of those types
|
||||||
if self.multiworld.pool_type[self.player] == PoolTypeOption.option_various:
|
if self.options.pool_type == PoolTypeOption.option_various:
|
||||||
def create_random_replacement_list(item_categories: Set[DS3ItemCategory], num_items: int):
|
def create_random_replacement_list(item_categories: Set[DS3ItemCategory], num_items: int):
|
||||||
candidates = [
|
candidates = [
|
||||||
item.name for item
|
item.name for item
|
||||||
@@ -300,7 +301,7 @@ class DarkSouls3World(World):
|
|||||||
# A list of items we can replace
|
# A list of items we can replace
|
||||||
removable_items = [item for item in itempool if item.classification != ItemClassification.progression]
|
removable_items = [item for item in itempool if item.classification != ItemClassification.progression]
|
||||||
|
|
||||||
guaranteed_items = self.multiworld.guaranteed_items[self.player].value
|
guaranteed_items = self.options.guaranteed_items.value
|
||||||
for item_name in guaranteed_items:
|
for item_name in guaranteed_items:
|
||||||
# Break early just in case nothing is removable (if user is trying to guarantee more
|
# Break early just in case nothing is removable (if user is trying to guarantee more
|
||||||
# items than the pool can hold, for example)
|
# items than the pool can hold, for example)
|
||||||
@@ -384,22 +385,22 @@ class DarkSouls3World(World):
|
|||||||
state.has("Cinders of a Lord - Aldrich", self.player) and
|
state.has("Cinders of a Lord - Aldrich", self.player) and
|
||||||
state.has("Cinders of a Lord - Lothric Prince", self.player))
|
state.has("Cinders of a Lord - Lothric Prince", self.player))
|
||||||
|
|
||||||
if self.multiworld.late_basin_of_vows[self.player] == Toggle.option_true:
|
if self.options.late_basin_of_vows == Toggle.option_true:
|
||||||
add_rule(self.multiworld.get_entrance("Go To Lothric Castle", self.player),
|
add_rule(self.multiworld.get_entrance("Go To Lothric Castle", self.player),
|
||||||
lambda state: state.has("Small Lothric Banner", self.player))
|
lambda state: state.has("Small Lothric Banner", self.player))
|
||||||
|
|
||||||
# DLC Access Rules Below
|
# DLC Access Rules Below
|
||||||
if self.multiworld.enable_dlc[self.player]:
|
if self.options.enable_dlc:
|
||||||
set_rule(self.multiworld.get_entrance("Go To Ringed City", self.player),
|
set_rule(self.multiworld.get_entrance("Go To Ringed City", self.player),
|
||||||
lambda state: state.has("Small Envoy Banner", self.player))
|
lambda state: state.has("Small Envoy Banner", self.player))
|
||||||
|
|
||||||
# If key items are randomized, must have contraption key to enter second half of Ashes DLC
|
# If key items are randomized, must have contraption key to enter second half of Ashes DLC
|
||||||
# If key items are not randomized, Contraption Key is guaranteed to be accessible before it is needed
|
# If key items are not randomized, Contraption Key is guaranteed to be accessible before it is needed
|
||||||
if self.multiworld.enable_key_locations[self.player] == Toggle.option_true:
|
if self.options.enable_key_locations == Toggle.option_true:
|
||||||
add_rule(self.multiworld.get_entrance("Go To Painted World of Ariandel 2", self.player),
|
add_rule(self.multiworld.get_entrance("Go To Painted World of Ariandel 2", self.player),
|
||||||
lambda state: state.has("Contraption Key", self.player))
|
lambda state: state.has("Contraption Key", self.player))
|
||||||
|
|
||||||
if self.multiworld.late_dlc[self.player] == Toggle.option_true:
|
if self.options.late_dlc == Toggle.option_true:
|
||||||
add_rule(self.multiworld.get_entrance("Go To Painted World of Ariandel 1", self.player),
|
add_rule(self.multiworld.get_entrance("Go To Painted World of Ariandel 1", self.player),
|
||||||
lambda state: state.has("Small Doll", self.player))
|
lambda state: state.has("Small Doll", self.player))
|
||||||
|
|
||||||
@@ -407,7 +408,7 @@ class DarkSouls3World(World):
|
|||||||
set_rule(self.multiworld.get_location("PC: Cinders of a Lord - Yhorm the Giant", self.player),
|
set_rule(self.multiworld.get_location("PC: Cinders of a Lord - Yhorm the Giant", self.player),
|
||||||
lambda state: state.has("Storm Ruler", self.player))
|
lambda state: state.has("Storm Ruler", self.player))
|
||||||
|
|
||||||
if self.multiworld.enable_ring_locations[self.player] == Toggle.option_true:
|
if self.options.enable_ring_locations == Toggle.option_true:
|
||||||
set_rule(self.multiworld.get_location("ID: Bellowing Dragoncrest Ring", self.player),
|
set_rule(self.multiworld.get_location("ID: Bellowing Dragoncrest Ring", self.player),
|
||||||
lambda state: state.has("Jailbreaker's Key", self.player))
|
lambda state: state.has("Jailbreaker's Key", self.player))
|
||||||
set_rule(self.multiworld.get_location("ID: Covetous Gold Serpent Ring", self.player),
|
set_rule(self.multiworld.get_location("ID: Covetous Gold Serpent Ring", self.player),
|
||||||
@@ -415,7 +416,7 @@ class DarkSouls3World(World):
|
|||||||
set_rule(self.multiworld.get_location("UG: Hornet Ring", self.player),
|
set_rule(self.multiworld.get_location("UG: Hornet Ring", self.player),
|
||||||
lambda state: state.has("Small Lothric Banner", self.player))
|
lambda state: state.has("Small Lothric Banner", self.player))
|
||||||
|
|
||||||
if self.multiworld.enable_npc_locations[self.player] == Toggle.option_true:
|
if self.options.enable_npc_locations == Toggle.option_true:
|
||||||
set_rule(self.multiworld.get_location("HWL: Greirat's Ashes", self.player),
|
set_rule(self.multiworld.get_location("HWL: Greirat's Ashes", self.player),
|
||||||
lambda state: state.has("Cell Key", self.player))
|
lambda state: state.has("Cell Key", self.player))
|
||||||
set_rule(self.multiworld.get_location("HWL: Blue Tearstone Ring", self.player),
|
set_rule(self.multiworld.get_location("HWL: Blue Tearstone Ring", self.player),
|
||||||
@@ -431,11 +432,11 @@ class DarkSouls3World(World):
|
|||||||
set_rule(self.multiworld.get_location("ID: Karla's Trousers", self.player),
|
set_rule(self.multiworld.get_location("ID: Karla's Trousers", self.player),
|
||||||
lambda state: state.has("Jailer's Key Ring", self.player))
|
lambda state: state.has("Jailer's Key Ring", self.player))
|
||||||
|
|
||||||
if self.multiworld.enable_misc_locations[self.player] == Toggle.option_true:
|
if self.options.enable_misc_locations == Toggle.option_true:
|
||||||
set_rule(self.multiworld.get_location("ID: Prisoner Chief's Ashes", self.player),
|
set_rule(self.multiworld.get_location("ID: Prisoner Chief's Ashes", self.player),
|
||||||
lambda state: state.has("Jailer's Key Ring", self.player))
|
lambda state: state.has("Jailer's Key Ring", self.player))
|
||||||
|
|
||||||
if self.multiworld.enable_boss_locations[self.player] == Toggle.option_true:
|
if self.options.enable_boss_locations == Toggle.option_true:
|
||||||
set_rule(self.multiworld.get_location("PC: Soul of Yhorm the Giant", self.player),
|
set_rule(self.multiworld.get_location("PC: Soul of Yhorm the Giant", self.player),
|
||||||
lambda state: state.has("Storm Ruler", self.player))
|
lambda state: state.has("Storm Ruler", self.player))
|
||||||
set_rule(self.multiworld.get_location("HWL: Soul of the Dancer", self.player),
|
set_rule(self.multiworld.get_location("HWL: Soul of the Dancer", self.player),
|
||||||
@@ -443,7 +444,7 @@ class DarkSouls3World(World):
|
|||||||
|
|
||||||
# Lump Soul of the Dancer in with LC for locations that should not be reachable
|
# Lump Soul of the Dancer in with LC for locations that should not be reachable
|
||||||
# before having access to US. (Prevents requiring getting Basin to fight Dancer to get SLB to go to US)
|
# before having access to US. (Prevents requiring getting Basin to fight Dancer to get SLB to go to US)
|
||||||
if self.multiworld.late_basin_of_vows[self.player] == Toggle.option_true:
|
if self.options.late_basin_of_vows == Toggle.option_true:
|
||||||
add_rule(self.multiworld.get_location("HWL: Soul of the Dancer", self.player),
|
add_rule(self.multiworld.get_location("HWL: Soul of the Dancer", self.player),
|
||||||
lambda state: state.has("Small Lothric Banner", self.player))
|
lambda state: state.has("Small Lothric Banner", self.player))
|
||||||
|
|
||||||
@@ -453,10 +454,10 @@ class DarkSouls3World(World):
|
|||||||
|
|
||||||
set_rule(self.multiworld.get_location("LC: Grand Archives Key", self.player), gotthard_corpse_rule)
|
set_rule(self.multiworld.get_location("LC: Grand Archives Key", self.player), gotthard_corpse_rule)
|
||||||
|
|
||||||
if self.multiworld.enable_weapon_locations[self.player] == Toggle.option_true:
|
if self.options.enable_weapon_locations == Toggle.option_true:
|
||||||
set_rule(self.multiworld.get_location("LC: Gotthard Twinswords", self.player), gotthard_corpse_rule)
|
set_rule(self.multiworld.get_location("LC: Gotthard Twinswords", self.player), gotthard_corpse_rule)
|
||||||
|
|
||||||
self.multiworld.completion_condition[self.player] = lambda state: \
|
self.options.completion_condition = lambda state: \
|
||||||
state.has("Cinders of a Lord - Abyss Watcher", self.player) and \
|
state.has("Cinders of a Lord - Abyss Watcher", self.player) and \
|
||||||
state.has("Cinders of a Lord - Yhorm the Giant", self.player) and \
|
state.has("Cinders of a Lord - Yhorm the Giant", self.player) and \
|
||||||
state.has("Cinders of a Lord - Aldrich", self.player) and \
|
state.has("Cinders of a Lord - Aldrich", self.player) and \
|
||||||
@@ -470,13 +471,13 @@ class DarkSouls3World(World):
|
|||||||
name_to_ds3_code = {item.name: item.ds3_code for item in item_dictionary.values()}
|
name_to_ds3_code = {item.name: item.ds3_code for item in item_dictionary.values()}
|
||||||
|
|
||||||
# Randomize some weapon upgrades
|
# Randomize some weapon upgrades
|
||||||
if self.multiworld.randomize_weapon_level[self.player] != RandomizeWeaponLevelOption.option_none:
|
if self.options.randomize_weapon_level != RandomizeWeaponLevelOption.option_none:
|
||||||
# if the user made an error and set a min higher than the max we default to the max
|
# if the user made an error and set a min higher than the max we default to the max
|
||||||
max_5 = self.multiworld.max_levels_in_5[self.player]
|
max_5 = self.options.max_levels_in_5
|
||||||
min_5 = min(self.multiworld.min_levels_in_5[self.player], max_5)
|
min_5 = min(self.options.min_levels_in_5, max_5)
|
||||||
max_10 = self.multiworld.max_levels_in_10[self.player]
|
max_10 = self.options.max_levels_in_10
|
||||||
min_10 = min(self.multiworld.min_levels_in_10[self.player], max_10)
|
min_10 = min(self.options.min_levels_in_10, max_10)
|
||||||
weapon_level_percentage = self.multiworld.randomize_weapon_level_percentage[self.player]
|
weapon_level_percentage = self.options.randomize_weapon_level_percentage
|
||||||
|
|
||||||
for item in item_dictionary.values():
|
for item in item_dictionary.values():
|
||||||
if self.multiworld.per_slot_randoms[self.player].randint(0, 99) < weapon_level_percentage:
|
if self.multiworld.per_slot_randoms[self.player].randint(0, 99) < weapon_level_percentage:
|
||||||
@@ -486,8 +487,8 @@ class DarkSouls3World(World):
|
|||||||
name_to_ds3_code[item.name] += self.multiworld.per_slot_randoms[self.player].randint(min_10, max_10)
|
name_to_ds3_code[item.name] += self.multiworld.per_slot_randoms[self.player].randint(min_10, max_10)
|
||||||
|
|
||||||
# Randomize some weapon infusions
|
# Randomize some weapon infusions
|
||||||
if self.multiworld.randomize_infusion[self.player] == Toggle.option_true:
|
if self.options.randomize_infusion == Toggle.option_true:
|
||||||
infusion_percentage = self.multiworld.randomize_infusion_percentage[self.player]
|
infusion_percentage = self.options.randomize_infusion_percentage
|
||||||
for item in item_dictionary.values():
|
for item in item_dictionary.values():
|
||||||
if item.category in {DS3ItemCategory.WEAPON_UPGRADE_10_INFUSIBLE, DS3ItemCategory.SHIELD_INFUSIBLE}:
|
if item.category in {DS3ItemCategory.WEAPON_UPGRADE_10_INFUSIBLE, DS3ItemCategory.SHIELD_INFUSIBLE}:
|
||||||
if self.multiworld.per_slot_randoms[self.player].randint(0, 99) < infusion_percentage:
|
if self.multiworld.per_slot_randoms[self.player].randint(0, 99) < infusion_percentage:
|
||||||
@@ -518,22 +519,22 @@ class DarkSouls3World(World):
|
|||||||
|
|
||||||
slot_data = {
|
slot_data = {
|
||||||
"options": {
|
"options": {
|
||||||
"enable_weapon_locations": self.multiworld.enable_weapon_locations[self.player].value,
|
"enable_weapon_locations": self.options.enable_weapon_locations.value,
|
||||||
"enable_shield_locations": self.multiworld.enable_shield_locations[self.player].value,
|
"enable_shield_locations": self.options.enable_shield_locations.value,
|
||||||
"enable_armor_locations": self.multiworld.enable_armor_locations[self.player].value,
|
"enable_armor_locations": self.options.enable_armor_locations.value,
|
||||||
"enable_ring_locations": self.multiworld.enable_ring_locations[self.player].value,
|
"enable_ring_locations": self.options.enable_ring_locations.value,
|
||||||
"enable_spell_locations": self.multiworld.enable_spell_locations[self.player].value,
|
"enable_spell_locations": self.options.enable_spell_locations.value,
|
||||||
"enable_key_locations": self.multiworld.enable_key_locations[self.player].value,
|
"enable_key_locations": self.options.enable_key_locations.value,
|
||||||
"enable_boss_locations": self.multiworld.enable_boss_locations[self.player].value,
|
"enable_boss_locations": self.options.enable_boss_locations.value,
|
||||||
"enable_npc_locations": self.multiworld.enable_npc_locations[self.player].value,
|
"enable_npc_locations": self.options.enable_npc_locations.value,
|
||||||
"enable_misc_locations": self.multiworld.enable_misc_locations[self.player].value,
|
"enable_misc_locations": self.options.enable_misc_locations.value,
|
||||||
"auto_equip": self.multiworld.auto_equip[self.player].value,
|
"auto_equip": self.options.auto_equip.value,
|
||||||
"lock_equip": self.multiworld.lock_equip[self.player].value,
|
"lock_equip": self.options.lock_equip.value,
|
||||||
"no_weapon_requirements": self.multiworld.no_weapon_requirements[self.player].value,
|
"no_weapon_requirements": self.options.no_weapon_requirements.value,
|
||||||
"death_link": self.multiworld.death_link[self.player].value,
|
"death_link": self.options.death_link.value,
|
||||||
"no_spell_requirements": self.multiworld.no_spell_requirements[self.player].value,
|
"no_spell_requirements": self.options.no_spell_requirements.value,
|
||||||
"no_equip_load": self.multiworld.no_equip_load[self.player].value,
|
"no_equip_load": self.options.no_equip_load.value,
|
||||||
"enable_dlc": self.multiworld.enable_dlc[self.player].value
|
"enable_dlc": self.options.enable_dlc.value
|
||||||
},
|
},
|
||||||
"seed": self.multiworld.seed_name, # to verify the server's multiworld
|
"seed": self.multiworld.seed_name, # to verify the server's multiworld
|
||||||
"slot": self.multiworld.player_name[self.player], # to connect to server
|
"slot": self.multiworld.player_name[self.player], # to connect to server
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import shutil
|
|||||||
import threading
|
import threading
|
||||||
import zipfile
|
import zipfile
|
||||||
from typing import Optional, TYPE_CHECKING, Any, List, Callable, Tuple, Union
|
from typing import Optional, TYPE_CHECKING, Any, List, Callable, Tuple, Union
|
||||||
|
from dataclasses import fields
|
||||||
|
import datetime
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
@@ -88,6 +90,7 @@ class FactorioModFile(worlds.Files.APContainer):
|
|||||||
def generate_mod(world: "Factorio", output_directory: str):
|
def generate_mod(world: "Factorio", output_directory: str):
|
||||||
player = world.player
|
player = world.player
|
||||||
multiworld = world.multiworld
|
multiworld = world.multiworld
|
||||||
|
options = world.options
|
||||||
global data_final_template, locale_template, control_template, data_template, settings_template
|
global data_final_template, locale_template, control_template, data_template, settings_template
|
||||||
with template_load_lock:
|
with template_load_lock:
|
||||||
if not data_final_template:
|
if not data_final_template:
|
||||||
@@ -129,40 +132,40 @@ def generate_mod(world: "Factorio", output_directory: str):
|
|||||||
"base_tech_table": base_tech_table,
|
"base_tech_table": base_tech_table,
|
||||||
"tech_to_progressive_lookup": tech_to_progressive_lookup,
|
"tech_to_progressive_lookup": tech_to_progressive_lookup,
|
||||||
"mod_name": mod_name,
|
"mod_name": mod_name,
|
||||||
"allowed_science_packs": multiworld.max_science_pack[player].get_allowed_packs(),
|
"allowed_science_packs": options.max_science_pack.get_allowed_packs(),
|
||||||
"custom_technologies": multiworld.worlds[player].custom_technologies,
|
"custom_technologies": multiworld.worlds[player].custom_technologies,
|
||||||
"tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites,
|
"tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites,
|
||||||
"slot_name": multiworld.player_name[player], "seed_name": multiworld.seed_name,
|
"slot_name": multiworld.player_name[player], "seed_name": multiworld.seed_name,
|
||||||
"slot_player": player,
|
"slot_player": player,
|
||||||
"starting_items": multiworld.starting_items[player], "recipes": recipes,
|
"starting_items": options.starting_items, "recipes": recipes,
|
||||||
"random": random, "flop_random": flop_random,
|
"random": random, "flop_random": flop_random,
|
||||||
"recipe_time_scale": recipe_time_scales.get(multiworld.recipe_time[player].value, None),
|
"recipe_time_scale": recipe_time_scales.get(options.recipe_time.value, None),
|
||||||
"recipe_time_range": recipe_time_ranges.get(multiworld.recipe_time[player].value, None),
|
"recipe_time_range": recipe_time_ranges.get(options.recipe_time.value, None),
|
||||||
"free_sample_blacklist": {item: 1 for item in free_sample_exclusions},
|
"free_sample_blacklist": {item: 1 for item in free_sample_exclusions},
|
||||||
"progressive_technology_table": {tech.name: tech.progressive for tech in
|
"progressive_technology_table": {tech.name: tech.progressive for tech in
|
||||||
progressive_technology_table.values()},
|
progressive_technology_table.values()},
|
||||||
"custom_recipes": world.custom_recipes,
|
"custom_recipes": world.custom_recipes,
|
||||||
"max_science_pack": multiworld.max_science_pack[player].value,
|
"max_science_pack": options.max_science_pack.value,
|
||||||
"liquids": fluids,
|
"liquids": fluids,
|
||||||
"goal": multiworld.goal[player].value,
|
"goal": options.goal.value,
|
||||||
"energy_link": multiworld.energy_link[player].value,
|
"energy_link": options.energy_link.value,
|
||||||
"useless_technologies": useless_technologies,
|
"useless_technologies": useless_technologies,
|
||||||
"chunk_shuffle": multiworld.chunk_shuffle[player].value if hasattr(multiworld, "chunk_shuffle") else 0,
|
"chunk_shuffle": options.chunk_shuffle.value if datetime.datetime.today().month == 4 else 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
for factorio_option in Options.factorio_options:
|
for factorio_option in [field.name for field in fields(Options.FactorioOptions)]:
|
||||||
if factorio_option in ["free_sample_blacklist", "free_sample_whitelist"]:
|
if factorio_option in ["free_sample_blacklist", "free_sample_whitelist"]:
|
||||||
continue
|
continue
|
||||||
template_data[factorio_option] = getattr(multiworld, factorio_option)[player].value
|
template_data[factorio_option] = getattr(options, factorio_option).value
|
||||||
|
|
||||||
if getattr(multiworld, "silo")[player].value == Options.Silo.option_randomize_recipe:
|
if getattr(options, "silo").value == Options.Silo.option_randomize_recipe:
|
||||||
template_data["free_sample_blacklist"]["rocket-silo"] = 1
|
template_data["free_sample_blacklist"]["rocket-silo"] = 1
|
||||||
|
|
||||||
if getattr(multiworld, "satellite")[player].value == Options.Satellite.option_randomize_recipe:
|
if getattr(options, "satellite").value == Options.Satellite.option_randomize_recipe:
|
||||||
template_data["free_sample_blacklist"]["satellite"] = 1
|
template_data["free_sample_blacklist"]["satellite"] = 1
|
||||||
|
|
||||||
template_data["free_sample_blacklist"].update({item: 1 for item in multiworld.free_sample_blacklist[player].value})
|
template_data["free_sample_blacklist"].update({item: 1 for item in options.free_sample_blacklist.value})
|
||||||
template_data["free_sample_blacklist"].update({item: 0 for item in multiworld.free_sample_whitelist[player].value})
|
template_data["free_sample_blacklist"].update({item: 0 for item in options.free_sample_whitelist.value})
|
||||||
|
|
||||||
zf_path = os.path.join(output_directory, versioned_mod_name + ".zip")
|
zf_path = os.path.join(output_directory, versioned_mod_name + ".zip")
|
||||||
mod = FactorioModFile(zf_path, player=player, player_name=multiworld.player_name[player])
|
mod = FactorioModFile(zf_path, player=player, player_name=multiworld.player_name[player])
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import typing
|
import typing
|
||||||
import datetime
|
|
||||||
|
|
||||||
from Options import Choice, OptionDict, OptionSet, Option, DefaultOnToggle, Range, DeathLink, Toggle, \
|
from Options import Choice, OptionDict, OptionSet, Option, DefaultOnToggle, Range, DeathLink, Toggle, \
|
||||||
StartInventoryPool
|
StartInventoryPool, PerGameCommonOptions
|
||||||
from schema import Schema, Optional, And, Or
|
from schema import Schema, Optional, And, Or
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
# schema helpers
|
# schema helpers
|
||||||
FloatRange = lambda low, high: And(Or(int, float), lambda f: low <= f <= high)
|
FloatRange = lambda low, high: And(Or(int, float), lambda f: low <= f <= high)
|
||||||
@@ -422,50 +422,44 @@ class EnergyLink(Toggle):
|
|||||||
display_name = "EnergyLink"
|
display_name = "EnergyLink"
|
||||||
|
|
||||||
|
|
||||||
factorio_options: typing.Dict[str, type(Option)] = {
|
class ChunkShuffle(Toggle):
|
||||||
"max_science_pack": MaxSciencePack,
|
"""Entrance Randomizer.
|
||||||
"goal": Goal,
|
2023 April Fool's option. Shuffles chunk border transitions.
|
||||||
"tech_tree_layout": TechTreeLayout,
|
Only valid during the Month of April. Forced off otherwise."""
|
||||||
"min_tech_cost": MinTechCost,
|
|
||||||
"max_tech_cost": MaxTechCost,
|
|
||||||
"tech_cost_distribution": TechCostDistribution,
|
|
||||||
"tech_cost_mix": TechCostMix,
|
|
||||||
"ramping_tech_costs": RampingTechCosts,
|
|
||||||
"silo": Silo,
|
|
||||||
"satellite": Satellite,
|
|
||||||
"free_samples": FreeSamples,
|
|
||||||
"tech_tree_information": TechTreeInformation,
|
|
||||||
"starting_items": FactorioStartItems,
|
|
||||||
"free_sample_blacklist": FactorioFreeSampleBlacklist,
|
|
||||||
"free_sample_whitelist": FactorioFreeSampleWhitelist,
|
|
||||||
"recipe_time": RecipeTime,
|
|
||||||
"recipe_ingredients": RecipeIngredients,
|
|
||||||
"recipe_ingredients_offset": RecipeIngredientsOffset,
|
|
||||||
"imported_blueprints": ImportedBlueprint,
|
|
||||||
"world_gen": FactorioWorldGen,
|
|
||||||
"progressive": Progressive,
|
|
||||||
"teleport_traps": TeleportTrapCount,
|
|
||||||
"grenade_traps": GrenadeTrapCount,
|
|
||||||
"cluster_grenade_traps": ClusterGrenadeTrapCount,
|
|
||||||
"artillery_traps": ArtilleryTrapCount,
|
|
||||||
"atomic_rocket_traps": AtomicRocketTrapCount,
|
|
||||||
"attack_traps": AttackTrapCount,
|
|
||||||
"evolution_traps": EvolutionTrapCount,
|
|
||||||
"evolution_trap_increase": EvolutionTrapIncrease,
|
|
||||||
"death_link": DeathLink,
|
|
||||||
"energy_link": EnergyLink,
|
|
||||||
"start_inventory_from_pool": StartInventoryPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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:
|
@dataclass
|
||||||
ChunkShuffle.__doc__ += """
|
class FactorioOptions(PerGameCommonOptions):
|
||||||
2023 April Fool's option. Shuffles chunk border transitions."""
|
max_science_pack: MaxSciencePack
|
||||||
factorio_options["chunk_shuffle"] = ChunkShuffle
|
goal: Goal
|
||||||
|
tech_tree_layout: TechTreeLayout
|
||||||
|
min_tech_cost: MinTechCost
|
||||||
|
max_tech_cost: MaxTechCost
|
||||||
|
tech_cost_distribution: TechCostDistribution
|
||||||
|
tech_cost_mix: TechCostMix
|
||||||
|
ramping_tech_costs: RampingTechCosts
|
||||||
|
silo: Silo
|
||||||
|
satellite: Satellite
|
||||||
|
free_samples: FreeSamples
|
||||||
|
tech_tree_information: TechTreeInformation
|
||||||
|
starting_items: FactorioStartItems
|
||||||
|
free_sample_blacklist: FactorioFreeSampleBlacklist
|
||||||
|
free_sample_whitelist: FactorioFreeSampleWhitelist
|
||||||
|
recipe_time: RecipeTime
|
||||||
|
recipe_ingredients: RecipeIngredients
|
||||||
|
recipe_ingredients_offset: RecipeIngredientsOffset
|
||||||
|
imported_blueprints: ImportedBlueprint
|
||||||
|
world_gen: FactorioWorldGen
|
||||||
|
progressive: Progressive
|
||||||
|
teleport_traps: TeleportTrapCount
|
||||||
|
grenade_traps: GrenadeTrapCount
|
||||||
|
cluster_grenade_traps: ClusterGrenadeTrapCount
|
||||||
|
artillery_traps: ArtilleryTrapCount
|
||||||
|
atomic_rocket_traps: AtomicRocketTrapCount
|
||||||
|
attack_traps: AttackTrapCount
|
||||||
|
evolution_traps: EvolutionTrapCount
|
||||||
|
evolution_trap_increase: EvolutionTrapIncrease
|
||||||
|
death_link: DeathLink
|
||||||
|
energy_link: EnergyLink
|
||||||
|
start_inventory_from_pool: StartInventoryPool
|
||||||
|
chunk_shuffle: ChunkShuffle
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ def _sorter(location: "FactorioScienceLocation"):
|
|||||||
|
|
||||||
|
|
||||||
def get_shapes(factorio_world: "Factorio") -> Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]]:
|
def get_shapes(factorio_world: "Factorio") -> Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]]:
|
||||||
|
options = factorio_world.options
|
||||||
world = factorio_world.multiworld
|
world = factorio_world.multiworld
|
||||||
player = factorio_world.player
|
|
||||||
prerequisites: Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]] = {}
|
prerequisites: Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]] = {}
|
||||||
layout = world.tech_tree_layout[player].value
|
layout = options.tech_tree_layout.value
|
||||||
locations: List["FactorioScienceLocation"] = sorted(factorio_world.science_locations, key=lambda loc: loc.name)
|
locations: List["FactorioScienceLocation"] = sorted(factorio_world.science_locations, key=lambda loc: loc.name)
|
||||||
world.random.shuffle(locations)
|
world.random.shuffle(locations)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from worlds.LauncherComponents import Component, components, Type, launch_subpro
|
|||||||
from worlds.generic import Rules
|
from worlds.generic import Rules
|
||||||
from .Locations import location_pools, location_table
|
from .Locations import location_pools, location_table
|
||||||
from .Mod import generate_mod
|
from .Mod import generate_mod
|
||||||
from .Options import factorio_options, MaxSciencePack, Silo, Satellite, TechTreeInformation, Goal, TechCostDistribution
|
from .Options import FactorioOptions, MaxSciencePack, Silo, Satellite, TechTreeInformation, Goal, TechCostDistribution, TechCostMix
|
||||||
from .Shapes import get_shapes
|
from .Shapes import get_shapes
|
||||||
from .Technologies import base_tech_table, recipe_sources, base_technology_table, \
|
from .Technologies import base_tech_table, recipe_sources, base_technology_table, \
|
||||||
all_ingredient_names, all_product_sources, required_technologies, get_rocket_requirements, \
|
all_ingredient_names, all_product_sources, required_technologies, get_rocket_requirements, \
|
||||||
@@ -88,6 +88,9 @@ class Factorio(World):
|
|||||||
location_pool: typing.List[FactorioScienceLocation]
|
location_pool: typing.List[FactorioScienceLocation]
|
||||||
advancement_technologies: typing.Set[str]
|
advancement_technologies: typing.Set[str]
|
||||||
|
|
||||||
|
options = FactorioOptions
|
||||||
|
options_dataclass = FactorioOptions
|
||||||
|
|
||||||
web = FactorioWeb()
|
web = FactorioWeb()
|
||||||
|
|
||||||
item_name_to_id = all_items
|
item_name_to_id = all_items
|
||||||
@@ -117,11 +120,11 @@ class Factorio(World):
|
|||||||
|
|
||||||
def generate_early(self) -> None:
|
def generate_early(self) -> None:
|
||||||
# if max < min, then swap max and min
|
# if max < min, then swap max and min
|
||||||
if self.multiworld.max_tech_cost[self.player] < self.multiworld.min_tech_cost[self.player]:
|
if self.options.max_tech_cost < self.options.min_tech_cost:
|
||||||
self.multiworld.min_tech_cost[self.player].value, self.multiworld.max_tech_cost[self.player].value = \
|
self.options.min_tech_cost.value, self.options.max_tech_cost.value = \
|
||||||
self.multiworld.max_tech_cost[self.player].value, self.multiworld.min_tech_cost[self.player].value
|
self.options.max_tech_cost.value, self.options.min_tech_cost.value
|
||||||
self.tech_mix = self.multiworld.tech_cost_mix[self.player]
|
self.tech_mix = self.options.tech_cost_mix
|
||||||
self.skip_silo = self.multiworld.silo[self.player].value == Silo.option_spawn
|
self.skip_silo = self.options.silo.value == Silo.option_spawn
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
player = self.player
|
player = self.player
|
||||||
@@ -132,17 +135,17 @@ 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] + \
|
self.options.evolution_traps + \
|
||||||
self.multiworld.attack_traps[player] + \
|
self.options.attack_traps + \
|
||||||
self.multiworld.teleport_traps[player] + \
|
self.options.teleport_traps + \
|
||||||
self.multiworld.grenade_traps[player] + \
|
self.options.grenade_traps + \
|
||||||
self.multiworld.cluster_grenade_traps[player] + \
|
self.options.cluster_grenade_traps + \
|
||||||
self.multiworld.atomic_rocket_traps[player] + \
|
self.options.atomic_rocket_traps + \
|
||||||
self.multiworld.artillery_traps[player]
|
self.options.artillery_traps
|
||||||
|
|
||||||
location_pool = []
|
location_pool = []
|
||||||
|
|
||||||
for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
|
for pack in sorted(self.options.max_science_pack.get_allowed_packs()):
|
||||||
location_pool.extend(location_pools[pack])
|
location_pool.extend(location_pools[pack])
|
||||||
try:
|
try:
|
||||||
location_names = self.multiworld.random.sample(location_pool, location_count)
|
location_names = self.multiworld.random.sample(location_pool, location_count)
|
||||||
@@ -151,11 +154,11 @@ class Factorio(World):
|
|||||||
raise Exception("Too many traps for too few locations. Either decrease the trap count, "
|
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
|
f"or increase the location count (higher max science pack). (Player {self.player})") from e
|
||||||
|
|
||||||
self.science_locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis)
|
self.science_locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis, self.options.tech_cost_mix)
|
||||||
for loc_name in location_names]
|
for loc_name in location_names]
|
||||||
distribution: TechCostDistribution = self.multiworld.tech_cost_distribution[self.player]
|
distribution: TechCostDistribution = self.options.tech_cost_distribution
|
||||||
min_cost = self.multiworld.min_tech_cost[self.player]
|
min_cost = self.options.min_tech_cost
|
||||||
max_cost = self.multiworld.max_tech_cost[self.player]
|
max_cost = self.options.max_tech_cost
|
||||||
if distribution == distribution.option_even:
|
if distribution == distribution.option_even:
|
||||||
rand_values = (random.randint(min_cost, max_cost) for _ in self.science_locations)
|
rand_values = (random.randint(min_cost, max_cost) for _ in self.science_locations)
|
||||||
else:
|
else:
|
||||||
@@ -164,7 +167,7 @@ class Factorio(World):
|
|||||||
distribution.option_high: max_cost}[distribution.value]
|
distribution.option_high: max_cost}[distribution.value]
|
||||||
rand_values = (random.triangular(min_cost, max_cost, mode) for _ in self.science_locations)
|
rand_values = (random.triangular(min_cost, max_cost, mode) for _ in self.science_locations)
|
||||||
rand_values = sorted(rand_values)
|
rand_values = sorted(rand_values)
|
||||||
if self.multiworld.ramping_tech_costs[self.player]:
|
if self.options.ramping_tech_costs:
|
||||||
def sorter(loc: FactorioScienceLocation):
|
def sorter(loc: FactorioScienceLocation):
|
||||||
return loc.complexity, loc.rel_cost
|
return loc.complexity, loc.rel_cost
|
||||||
else:
|
else:
|
||||||
@@ -179,7 +182,7 @@ class Factorio(World):
|
|||||||
event = FactorioItem("Victory", ItemClassification.progression, None, player)
|
event = FactorioItem("Victory", ItemClassification.progression, None, player)
|
||||||
location.place_locked_item(event)
|
location.place_locked_item(event)
|
||||||
|
|
||||||
for ingredient in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
|
for ingredient in sorted(self.options.max_science_pack.get_allowed_packs()):
|
||||||
location = FactorioLocation(player, f"Automate {ingredient}", None, nauvis)
|
location = FactorioLocation(player, f"Automate {ingredient}", None, nauvis)
|
||||||
nauvis.locations.append(location)
|
nauvis.locations.append(location)
|
||||||
event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player)
|
event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player)
|
||||||
@@ -195,10 +198,10 @@ class Factorio(World):
|
|||||||
traps = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery", "Atomic Rocket")
|
traps = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery", "Atomic Rocket")
|
||||||
for trap_name in traps:
|
for trap_name in traps:
|
||||||
self.multiworld.itempool.extend(self.create_item(f"{trap_name} Trap") for _ in
|
self.multiworld.itempool.extend(self.create_item(f"{trap_name} Trap") for _ in
|
||||||
range(getattr(self.multiworld,
|
range(getattr(self.options,
|
||||||
f"{trap_name.lower().replace(' ', '_')}_traps")[player]))
|
f"{trap_name.lower().replace(' ', '_')}_traps")))
|
||||||
|
|
||||||
want_progressives = collections.defaultdict(lambda: self.multiworld.progressive[player].
|
want_progressives = collections.defaultdict(lambda: self.options.progressive.
|
||||||
want_progressives(self.multiworld.random))
|
want_progressives(self.multiworld.random))
|
||||||
|
|
||||||
cost_sorted_locations = sorted(self.science_locations, key=lambda location: location.name)
|
cost_sorted_locations = sorted(self.science_locations, key=lambda location: location.name)
|
||||||
@@ -206,7 +209,7 @@ class Factorio(World):
|
|||||||
"logistics": 1,
|
"logistics": 1,
|
||||||
"rocket-silo": -1}
|
"rocket-silo": -1}
|
||||||
loc: FactorioScienceLocation
|
loc: FactorioScienceLocation
|
||||||
if self.multiworld.tech_tree_information[player] == TechTreeInformation.option_full:
|
if self.options.tech_tree_information == TechTreeInformation.option_full:
|
||||||
# mark all locations as pre-hinted
|
# mark all locations as pre-hinted
|
||||||
for loc in self.science_locations:
|
for loc in self.science_locations:
|
||||||
loc.revealed = True
|
loc.revealed = True
|
||||||
@@ -237,10 +240,10 @@ class Factorio(World):
|
|||||||
player = self.player
|
player = self.player
|
||||||
shapes = get_shapes(self)
|
shapes = get_shapes(self)
|
||||||
|
|
||||||
for ingredient in self.multiworld.max_science_pack[self.player].get_allowed_packs():
|
for ingredient in self.options.max_science_pack.get_allowed_packs():
|
||||||
location = world.get_location(f"Automate {ingredient}", player)
|
location = world.get_location(f"Automate {ingredient}", player)
|
||||||
|
|
||||||
if self.multiworld.recipe_ingredients[self.player]:
|
if self.options.recipe_ingredients:
|
||||||
custom_recipe = self.custom_recipes[ingredient]
|
custom_recipe = self.custom_recipes[ingredient]
|
||||||
|
|
||||||
location.access_rule = lambda state, ingredient=ingredient, custom_recipe=custom_recipe: \
|
location.access_rule = lambda state, ingredient=ingredient, custom_recipe=custom_recipe: \
|
||||||
@@ -261,16 +264,16 @@ class Factorio(World):
|
|||||||
prerequisites: all(state.can_reach(loc) for loc in locations))
|
prerequisites: all(state.can_reach(loc) for loc in locations))
|
||||||
|
|
||||||
silo_recipe = None
|
silo_recipe = None
|
||||||
if self.multiworld.silo[self.player] == Silo.option_spawn:
|
if self.options.silo == Silo.option_spawn:
|
||||||
silo_recipe = self.custom_recipes["rocket-silo"] if "rocket-silo" in self.custom_recipes \
|
silo_recipe = self.custom_recipes["rocket-silo"] if "rocket-silo" in self.custom_recipes \
|
||||||
else next(iter(all_product_sources.get("rocket-silo")))
|
else next(iter(all_product_sources.get("rocket-silo")))
|
||||||
part_recipe = self.custom_recipes["rocket-part"]
|
part_recipe = self.custom_recipes["rocket-part"]
|
||||||
satellite_recipe = None
|
satellite_recipe = None
|
||||||
if self.multiworld.goal[self.player] == Goal.option_satellite:
|
if self.options.goal == Goal.option_satellite:
|
||||||
satellite_recipe = self.custom_recipes["satellite"] if "satellite" in self.custom_recipes \
|
satellite_recipe = self.custom_recipes["satellite"] if "satellite" in self.custom_recipes \
|
||||||
else next(iter(all_product_sources.get("satellite")))
|
else next(iter(all_product_sources.get("satellite")))
|
||||||
victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe)
|
victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe)
|
||||||
if self.multiworld.silo[self.player] != Silo.option_spawn:
|
if self.options.silo != Silo.option_spawn:
|
||||||
victory_tech_names.add("rocket-silo")
|
victory_tech_names.add("rocket-silo")
|
||||||
world.get_location("Rocket Launch", player).access_rule = lambda state: all(state.has(technology, player)
|
world.get_location("Rocket Launch", player).access_rule = lambda state: all(state.has(technology, player)
|
||||||
for technology in
|
for technology in
|
||||||
@@ -279,12 +282,12 @@ class Factorio(World):
|
|||||||
world.completion_condition[player] = lambda state: state.has('Victory', player)
|
world.completion_condition[player] = lambda state: state.has('Victory', player)
|
||||||
|
|
||||||
def generate_basic(self):
|
def generate_basic(self):
|
||||||
map_basic_settings = self.multiworld.world_gen[self.player].value["basic"]
|
map_basic_settings = self.options.world_gen.value["basic"]
|
||||||
if map_basic_settings.get("seed", None) is None: # allow seed 0
|
if map_basic_settings.get("seed", None) is None: # allow seed 0
|
||||||
# 32 bit uint
|
# 32 bit uint
|
||||||
map_basic_settings["seed"] = self.multiworld.per_slot_randoms[self.player].randint(0, 2 ** 32 - 1)
|
map_basic_settings["seed"] = self.multiworld.per_slot_randoms[self.player].randint(0, 2 ** 32 - 1)
|
||||||
|
|
||||||
start_location_hints: typing.Set[str] = self.multiworld.start_location_hints[self.player].value
|
start_location_hints: typing.Set[str] = self.options.start_location_hints.value
|
||||||
|
|
||||||
for loc in self.science_locations:
|
for loc in self.science_locations:
|
||||||
# show start_location_hints ingame
|
# show start_location_hints ingame
|
||||||
@@ -308,8 +311,6 @@ class Factorio(World):
|
|||||||
|
|
||||||
return super(Factorio, self).collect_item(state, item, remove)
|
return super(Factorio, self).collect_item(state, item, remove)
|
||||||
|
|
||||||
option_definitions = factorio_options
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def stage_write_spoiler(cls, world, spoiler_handle):
|
def stage_write_spoiler(cls, world, spoiler_handle):
|
||||||
factorio_players = world.get_game_players(cls.game)
|
factorio_players = world.get_game_players(cls.game)
|
||||||
@@ -437,25 +438,25 @@ class Factorio(World):
|
|||||||
|
|
||||||
def set_custom_technologies(self):
|
def set_custom_technologies(self):
|
||||||
custom_technologies = {}
|
custom_technologies = {}
|
||||||
allowed_packs = self.multiworld.max_science_pack[self.player].get_allowed_packs()
|
allowed_packs = self.options.max_science_pack.get_allowed_packs()
|
||||||
for technology_name, technology in base_technology_table.items():
|
for technology_name, technology in base_technology_table.items():
|
||||||
custom_technologies[technology_name] = technology.get_custom(self.multiworld, allowed_packs, self.player)
|
custom_technologies[technology_name] = technology.get_custom(self.multiworld, allowed_packs, self.player)
|
||||||
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]
|
ingredients_offset = self.options.recipe_ingredients_offset
|
||||||
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.options.max_science_pack.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 + ingredients_offset)},
|
{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)}
|
||||||
|
|
||||||
if self.multiworld.recipe_ingredients[self.player]:
|
if self.options.recipe_ingredients:
|
||||||
valid_pool = []
|
valid_pool = []
|
||||||
for pack in self.multiworld.max_science_pack[self.player].get_ordered_science_packs():
|
for pack in self.options.max_science_pack.get_ordered_science_packs():
|
||||||
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
|
||||||
@@ -463,23 +464,23 @@ class Factorio(World):
|
|||||||
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.options.silo.value == Silo.option_randomize_recipe \
|
||||||
or self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe:
|
or self.options.satellite.value == Satellite.option_randomize_recipe:
|
||||||
valid_pool = set()
|
valid_pool = set()
|
||||||
for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
|
for pack in sorted(self.options.max_science_pack.get_allowed_packs()):
|
||||||
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.options.silo.value == Silo.option_randomize_recipe:
|
||||||
new_recipe = self.make_balanced_recipe(
|
new_recipe = self.make_balanced_recipe(
|
||||||
recipes["rocket-silo"], valid_pool,
|
recipes["rocket-silo"], valid_pool,
|
||||||
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7,
|
factor=(self.options.max_science_pack.value + 1) / 7,
|
||||||
ingredients_offset=ingredients_offset)
|
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.options.satellite.value == Satellite.option_randomize_recipe:
|
||||||
new_recipe = self.make_balanced_recipe(
|
new_recipe = self.make_balanced_recipe(
|
||||||
recipes["satellite"], valid_pool,
|
recipes["satellite"], valid_pool,
|
||||||
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7,
|
factor=(self.options.max_science_pack.value + 1) / 7,
|
||||||
ingredients_offset=ingredients_offset)
|
ingredients_offset=ingredients_offset)
|
||||||
self.custom_recipes["satellite"] = new_recipe
|
self.custom_recipes["satellite"] = new_recipe
|
||||||
bridge = "ap-energy-bridge"
|
bridge = "ap-energy-bridge"
|
||||||
@@ -487,16 +488,16 @@ class Factorio(World):
|
|||||||
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},
|
"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.options.max_science_pack.get_ordered_science_packs()[0]]),
|
||||||
ingredients_offset=ingredients_offset)
|
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(50, 500)
|
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.options.max_science_pack.get_allowed_packs() | {"rocket-part"}
|
||||||
if self.multiworld.silo[self.player] != Silo.option_spawn:
|
if self.options.silo != Silo.option_spawn:
|
||||||
needed_recipes |= {"rocket-silo"}
|
needed_recipes |= {"rocket-silo"}
|
||||||
if self.multiworld.goal[self.player].value == Goal.option_satellite:
|
if self.options.goal.value == Goal.option_satellite:
|
||||||
needed_recipes |= {"satellite"}
|
needed_recipes |= {"satellite"}
|
||||||
|
|
||||||
for recipe in needed_recipes:
|
for recipe in needed_recipes:
|
||||||
@@ -538,7 +539,7 @@ class FactorioScienceLocation(FactorioLocation):
|
|||||||
ingredients: typing.Dict[str, int]
|
ingredients: typing.Dict[str, int]
|
||||||
count: int = 0
|
count: int = 0
|
||||||
|
|
||||||
def __init__(self, player: int, name: str, address: int, parent: Region):
|
def __init__(self, player: int, name: str, address: int, parent: Region, tech_cost_mix: TechCostMix):
|
||||||
super(FactorioScienceLocation, self).__init__(player, name, address, parent)
|
super(FactorioScienceLocation, self).__init__(player, name, address, parent)
|
||||||
# "AP-{Complexity}-{Cost}"
|
# "AP-{Complexity}-{Cost}"
|
||||||
self.complexity = int(self.name[3]) - 1
|
self.complexity = int(self.name[3]) - 1
|
||||||
@@ -546,7 +547,7 @@ class FactorioScienceLocation(FactorioLocation):
|
|||||||
|
|
||||||
self.ingredients = {Factorio.ordered_science_packs[self.complexity]: 1}
|
self.ingredients = {Factorio.ordered_science_packs[self.complexity]: 1}
|
||||||
for complexity in range(self.complexity):
|
for complexity in range(self.complexity):
|
||||||
if parent.multiworld.tech_cost_mix[self.player] > parent.multiworld.random.randint(0, 99):
|
if tech_cost_mix > parent.multiworld.random.randint(0, 99):
|
||||||
self.ingredients[Factorio.ordered_science_packs[complexity]] = 1
|
self.ingredients[Factorio.ordered_science_packs[complexity]] = 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ else:
|
|||||||
|
|
||||||
def locality_needed(world: MultiWorld) -> bool:
|
def locality_needed(world: MultiWorld) -> bool:
|
||||||
for player in world.player_ids:
|
for player in world.player_ids:
|
||||||
if world.local_items[player].value:
|
if world.worlds[player].options.local_items.value:
|
||||||
return True
|
return True
|
||||||
if world.non_local_items[player].value:
|
if world.worlds[player].options.non_local_items.value:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Group
|
# Group
|
||||||
@@ -40,12 +40,12 @@ def locality_rules(world: MultiWorld):
|
|||||||
forbid_data[sender][receiver].update(items)
|
forbid_data[sender][receiver].update(items)
|
||||||
|
|
||||||
for receiving_player in world.player_ids:
|
for receiving_player in world.player_ids:
|
||||||
local_items: typing.Set[str] = world.local_items[receiving_player].value
|
local_items: typing.Set[str] = world.worlds[receiving_player].options.local_items.value
|
||||||
if local_items:
|
if local_items:
|
||||||
for sending_player in world.player_ids:
|
for sending_player in world.player_ids:
|
||||||
if receiving_player != sending_player:
|
if receiving_player != sending_player:
|
||||||
forbid(sending_player, receiving_player, local_items)
|
forbid(sending_player, receiving_player, local_items)
|
||||||
non_local_items: typing.Set[str] = world.non_local_items[receiving_player].value
|
non_local_items: typing.Set[str] = world.worlds[receiving_player].options.non_local_items.value
|
||||||
if non_local_items:
|
if non_local_items:
|
||||||
forbid(receiving_player, receiving_player, non_local_items)
|
forbid(receiving_player, receiving_player, non_local_items)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user