Skip to content

Commit

Permalink
implement split translator
Browse files Browse the repository at this point in the history
  • Loading branch information
Ixrec committed Sep 23, 2024
1 parent 3e0b09b commit b05f4d3
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 157 deletions.
8 changes: 7 additions & 1 deletion worlds/outer_wilds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,13 @@ def set_rules(self) -> None:
self.multiworld.completion_condition[self.player] = lambda state: state.has(goal_item, self.player)

def fill_slot_data(self):
slot_data = self.options.as_dict("goal", "death_link", "logsanity", "spawn", "enable_eote_dlc", "dlc_only")
slot_data = self.options.as_dict(
"death_link", # a client/mod feature
"goal", "spawn", # affects tons of stuff, but also a client/mod faeture
"logsanity", "enable_eote_dlc", "dlc_only", # changes AP locations, needed by in-game tracker
"split_translator" # changes AP items, and how client/mod implements Translator
)
# more client/mod features, these are only in the apworld because we want them fixed per-slot/at gen time
slot_data["eotu_coordinates"] = self.eotu_coordinates
slot_data["db_layout"] = self.db_layout
slot_data["planet_order"] = self.planet_order
Expand Down
26 changes: 24 additions & 2 deletions worlds/outer_wilds/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class OuterWildsItemData(NamedTuple):
code: Optional[int] = None
type: ItemClassification = ItemClassification.filler
category: Optional[str] = None
split_translator: Optional[bool] = None


pickled_data = pkgutil.get_data(__name__, "shared_static_logic/static_logic.pickle")
Expand All @@ -38,6 +39,7 @@ class OuterWildsItemData(NamedTuple):
code=(items_data_entry["code"] if "code" in items_data_entry else None),
type=item_types_map[items_data_entry["type"]],
category=(items_data_entry["category"] if "category" in items_data_entry else None),
split_translator=(items_data_entry["split_translator"] if "split_translator" in items_data_entry else None),
)

all_non_event_items_table = {name: data.code for name, data in item_data_table.items() if data.code is not None}
Expand Down Expand Up @@ -80,6 +82,12 @@ class OuterWildsItemData(NamedTuple):
},
"Tools": {
"Translator",
"Translator (Hourglass Twins)",
"Translator (Timber Hearth)",
"Translator (Brittle Hollow)",
"Translator (Giant's Deep)",
"Translator (Dark Bramble)",
"Translator (Other)",
"Signalscope",
"Scout",
"Ghost Matter Wavelength",
Expand Down Expand Up @@ -181,6 +189,8 @@ def create_items(world: "OuterWildsWorld") -> None:
instances = 1
if name in repeated_prog_useful_items:
instances = repeated_prog_useful_items[name]
if item.split_translator is not None and item.split_translator != options.split_translator:
instances = 0
for _ in range(0, instances):
prog_and_useful_items.append(create_item(player, name))

Expand Down Expand Up @@ -230,14 +240,26 @@ def create_items(world: "OuterWildsWorld") -> None:
multiworld.itempool += itempool

if options.early_key_item:
relevant_translator = "Translator"
if options.split_translator:
if options.spawn == Spawn.option_hourglass_twins:
relevant_translator = "Translator (Hourglass Twins)"
if options.spawn == Spawn.option_timber_hearth:
relevant_translator = "Translator (Timber Hearth)"
if options.spawn == Spawn.option_brittle_hollow:
relevant_translator = "Translator (Brittle Hollow)"
if options.spawn == Spawn.option_giants_deep:
relevant_translator = "Translator (Giant's Deep)"
# ignore stranger spawn since it won't offer a Translator at all

key_item = None
if options.early_key_item == EarlyKeyItem.option_any:
if options.spawn == Spawn.option_stranger:
key_item = random.choice(["Launch Codes", "Stranger Light Modulator"])
else:
key_item = random.choice(["Translator", "Nomai Warp Codes", "Launch Codes"])
key_item = random.choice([relevant_translator, "Nomai Warp Codes", "Launch Codes"])
elif options.early_key_item == EarlyKeyItem.option_translator:
key_item = "Translator"
key_item = relevant_translator
elif options.early_key_item == EarlyKeyItem.option_nomai_warp_codes:
key_item = "Nomai Warp Codes"
elif options.early_key_item == EarlyKeyItem.option_launch_codes:
Expand Down
16 changes: 10 additions & 6 deletions worlds/outer_wilds/locations_and_regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ def create_regions(world: "OuterWildsWorld") -> None:
for region_name in region_data_table.keys():
mw.regions.append(Region(region_name, p, mw))

split_translator = options.split_translator

# add locations and connections to each region
for region_name, region_data in region_data_table.items():
region = mw.get_region(region_name, p)
Expand All @@ -131,7 +133,7 @@ def create_regions(world: "OuterWildsWorld") -> None:
for connection in exit_connections:
to = connection["to"]
requires = connection["requires"]
rule = None if len(requires) == 0 else lambda state, r=requires: eval_rule(state, p, r)
rule = None if len(requires) == 0 else lambda state, r=requires, st=split_translator: eval_rule(state, p, r, st)
entrance = region.connect(mw.get_region(to, p), None, rule)
indirect_regions = regions_referenced_by_rule(requires)
for indirect_region in indirect_regions:
Expand All @@ -141,7 +143,7 @@ def create_regions(world: "OuterWildsWorld") -> None:
for ld in locations_data:
if ld["name"] in locations_to_create and len(ld["requires"]) > 0:
set_rule(mw.get_location(ld["name"], p),
lambda state, r=ld["requires"]: eval_rule(state, p, r))
lambda state, r=ld["requires"], st=split_translator: eval_rule(state, p, r, st))

# add dynamic logic, i.e. connections based on player options
menu = mw.get_region("Menu", p)
Expand Down Expand Up @@ -211,11 +213,11 @@ def rule(state: CollectionState) -> bool:

# In particular: this eval_rule() function is the main piece of code which will have to
# be implemented in both languages, so it's important we keep the implementations in sync
def eval_rule(state: CollectionState, p: int, rule: [Any]) -> bool:
return all(eval_criterion(state, p, criterion) for criterion in rule)
def eval_rule(state: CollectionState, p: int, rule: [Any], split_translator: bool) -> bool:
return all(eval_criterion(state, p, criterion, split_translator) for criterion in rule)


def eval_criterion(state: CollectionState, p: int, criterion: Any) -> bool:
def eval_criterion(state: CollectionState, p: int, criterion: Any, split_translator: bool) -> bool:
# all valid criteria are dicts
if isinstance(criterion, dict):
# we're only using JSON objects / Python dicts here as discriminated unions,
Expand All @@ -227,9 +229,11 @@ def eval_criterion(state: CollectionState, p: int, criterion: Any) -> bool:
# { "item": "..." } and { "anyOf": [ ... ] } and { "location": "foo" } and { "region": "bar" }
# mean exactly what they sound like, and those are the only kinds of criteria.
if key == "item" and isinstance(value, str):
if not split_translator and value.startswith("Translator ("):
return state.has("Translator", p)
return state.has(value, p)
elif key == "anyOf" and isinstance(value, list):
return any(eval_criterion(state, p, sub_criterion) for sub_criterion in value)
return any(eval_criterion(state, p, sub_criterion, split_translator) for sub_criterion in value)
elif key == "location" and isinstance(value, str):
return state.can_reach(value, "Location", p)
elif key == "region" and isinstance(value, str):
Expand Down
9 changes: 9 additions & 0 deletions worlds/outer_wilds/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ class DLCOnly(Toggle):
display_name = "DLC Only"


class SplitTranslator(Toggle):
"""
Splits the "Translator" item into 6 items: 5 for the main planets and their satellites, plus a
"Translator (Other)" for smaller parts of the vanilla system and systems added by story mods.
"""
display_name = "Split Translator"


@dataclass
class OuterWildsGameOptions(PerGameCommonOptions):
start_inventory_from_pool: StartInventoryPool
Expand All @@ -200,6 +208,7 @@ class OuterWildsGameOptions(PerGameCommonOptions):
death_link: DeathLink
logsanity: Logsanity
shuffle_spacesuit: ShuffleSpacesuit
split_translator: SplitTranslator


def get_creation_settings(options: OuterWildsGameOptions) -> Set[str]:
Expand Down
8 changes: 7 additions & 1 deletion worlds/outer_wilds/shared_static_logic/items.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
{ "code": 1734473681, "name": "Launch Codes", "type": "progression" },

{ "code": 1734473732, "name": "Spacesuit", "type": "progression" },
{ "category": "base", "code": 1734473682, "name": "Translator", "type": "progression" },
{ "category": "base", "split_translator": false, "code": 1734473682, "name": "Translator", "type": "progression" },
{ "category": "base", "split_translator": true, "code": 1734473746, "name": "Translator (Hourglass Twins)", "type": "progression" },
{ "category": "base", "split_translator": true, "code": 1734473747, "name": "Translator (Timber Hearth)", "type": "progression" },
{ "category": "base", "split_translator": true, "code": 1734473748, "name": "Translator (Brittle Hollow)", "type": "progression" },
{ "category": "base", "split_translator": true, "code": 1734473749, "name": "Translator (Giant's Deep)", "type": "progression" },
{ "category": "base", "split_translator": true, "code": 1734473750, "name": "Translator (Dark Bramble)", "type": "progression" },
{ "category": "base", "split_translator": true, "code": 1734473751, "name": "Translator (Other)", "type": "progression" },
{ "category": "base", "code": 1734473683, "name": "Signalscope", "type": "progression" },
{ "code": 1734473684, "name": "Scout", "type": "progression" },
{ "code": 1734473685, "name": "Ghost Matter Wavelength", "type": "progression" },
Expand Down
Loading

0 comments on commit b05f4d3

Please sign in to comment.