Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various small additions #59

Merged
merged 12 commits into from
Jul 11, 2024
7 changes: 4 additions & 3 deletions Dolphin scripts/Entrance Randomizer/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@
from lib.constants import * # noqa: F403
from lib.constants import __version__
from lib.entrance_rando import (
highjack_transition,
highjack_transition_rando,
set_transitions_map,
starting_area,
temp_disabled_exits,
transitions_map,
)
from lib.graph_creation import create_graphml
Expand All @@ -32,6 +31,8 @@
draw_text,
dump_spoiler_logs,
follow_pointer_path,
highjack_transition,
highjack_transition_rando,
prevent_item_softlock,
prevent_transition_softlocks,
reset_draw_text_index,
Expand All @@ -49,7 +50,7 @@

# Dump spoiler logs and graph
dump_spoiler_logs(starting_area_name, transitions_map, seed_string)
create_graphml(transitions_map, seed_string, starting_area)
create_graphml(transitions_map, temp_disabled_exits, seed_string, starting_area)


async def main_loop():
Expand Down
4 changes: 2 additions & 2 deletions Dolphin scripts/Entrance Randomizer/lib/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ class LevelCRC(IntEnum):
TWIN_OUTPOSTS = 0xE6B9138A
TWIN_OUTPOSTS_UNDERWATER = 0xDE524DA6
UNDERGROUND_DAM = 0x9D6149E1
VALLEY_OF_THE_SPIRITS = 0x08E3C641
VALLEY_OF_SPIRITS = 0x08E3C641
VIRACOCHA_MONOLITHS = 0x6F498BBD
VIRACOCHA_MONOLITHS_CUTSCENE = 0xE8362F5F
WHACK_A_TUCO = 0x0A1F2526
Expand All @@ -249,7 +249,7 @@ class LevelCRC(IntEnum):
SOFTLOCKABLE_ENTRANCES = {
int(LevelCRC.FLOODED_COURTYARD): 8, # From st claire: 7
int(LevelCRC.EYES_OF_DOOM): 9,
int(LevelCRC.VALLEY_OF_THE_SPIRITS): 8,
int(LevelCRC.VALLEY_OF_SPIRITS): 8,
int(LevelCRC.COPACANTI_LAKE): 8,
}
"""Entrances that can softlock by infinitely running into a door.
Expand Down
120 changes: 31 additions & 89 deletions Dolphin scripts/Entrance Randomizer/lib/entrance_rando.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import CONFIGS
from lib.constants import * # noqa: F403
from lib.transition_infos import Area
from lib.utils import follow_pointer_path, state


class Transition(NamedTuple):
Expand All @@ -28,6 +27,17 @@ class Choice(IntEnum):
LevelCRC.PENGUIN_TEMPLE,
)

one_way_exits = (
wossnameGitHub marked this conversation as resolved.
Show resolved Hide resolved
# the White Valley geyser
Transition(LevelCRC.WHITE_VALLEY, LevelCRC.MOUNTAIN_SLED_RUN),
# the Apu Illapu Shrine geyser
Transition(LevelCRC.APU_ILLAPU_SHRINE, LevelCRC.WHITE_VALLEY),
# the Apu Illapu Shrine one-way door
Transition(LevelCRC.MOUNTAIN_SLED_RUN, LevelCRC.APU_ILLAPU_SHRINE),
# the Jungle Canyon waterfall
Transition(LevelCRC.CAVERN_LAKE, LevelCRC.JUNGLE_CANYON),
)

_possible_starting_areas = [
area for area in ALL_TRANSITION_AREAS
# Remove unwanted starting areas from the list of possibilities
Expand Down Expand Up @@ -59,33 +69,7 @@ class Choice(IntEnum):
}
]

# Call RNG even if this is unused to not impact randomization of other things for the same seed
starting_area = random.choice(_possible_starting_areas)
if CONFIGS.STARTING_AREA is not None:
starting_area = CONFIGS.STARTING_AREA

transitions_map: dict[tuple[int, int], Transition] = {}
"""```python
{
(og_from_id, og_to_id): (og_from_id, og_to_id)
}
```"""

__connections_left: dict[int, int] = {}
"""Used in randomization process to track per Area how many exits aren't connected yet."""

one_way_exits = (
# the White Valley geyser
Transition(LevelCRC.WHITE_VALLEY, LevelCRC.MOUNTAIN_SLED_RUN),
# the Apu Illapu Shrine geyser
Transition(LevelCRC.APU_ILLAPU_SHRINE, LevelCRC.WHITE_VALLEY),
# the Apu Illapu Shrine one-way door
Transition(LevelCRC.MOUNTAIN_SLED_RUN, LevelCRC.APU_ILLAPU_SHRINE),
# the Jungle Canyon waterfall
Transition(LevelCRC.CAVERN_LAKE, LevelCRC.JUNGLE_CANYON),
)

disabled_exits = (
temp_disabled_exits = [
# Mouth of Inti has 2 connections with Altar of Huitaca, which causes problems,
# basically it's very easy to get softlocked by the spider web when entering Altar of Huitaca
# So for now just don't randomize it. That way runs don't just end out of nowhere
Expand All @@ -97,6 +81,10 @@ class Choice(IntEnum):
# So for now just don't randomize it. That way we won't have to worry about that yet
(LevelCRC.TWIN_OUTPOSTS, LevelCRC.TWIN_OUTPOSTS_UNDERWATER),
(LevelCRC.TWIN_OUTPOSTS_UNDERWATER, LevelCRC.TWIN_OUTPOSTS),
]

disabled_exits = (
*temp_disabled_exits,
# The 3 Spirit Fights are not randomized,
# because that will cause issues with the transformation cutscene trigger.
# Plus it wouldn't really improve anything, given that the Temples are randomized anyway.
Expand Down Expand Up @@ -143,9 +131,24 @@ class Choice(IntEnum):
(LevelCRC.BETA_VOLCANO, LevelCRC.PLANE_COCKPIT),
)

# Call RNG even if this is unused to not impact randomization of other things for the same seed
starting_area = random.choice(_possible_starting_areas)
if CONFIGS.STARTING_AREA is not None:
starting_area = CONFIGS.STARTING_AREA

TRANSITION_INFOS_DICT_RANDO = TRANSITION_INFOS_DICT.copy()
ALL_POSSIBLE_TRANSITIONS_RANDO = ALL_POSSIBLE_TRANSITIONS

transitions_map: dict[tuple[int, int], Transition] = {}
"""```python
{
(og_from_id, og_to_id): (og_from_id, og_to_id)
}
```"""

__connections_left: dict[int, int] = {}
"""Used in randomization process to track per Area how many exits aren't connected yet."""


def initialize_connections_left():
for area in TRANSITION_INFOS_DICT.values():
Expand Down Expand Up @@ -177,67 +180,6 @@ def remove_disabled_exits():
]


def highjack_transition(
from_: int | None,
to: int | None,
redirect: int,
):
if from_ is None:
from_ = state.current_area_old
if to is None:
to = state.current_area_new

# Early return. Detect the start of a transition
if state.current_area_old == state.current_area_new:
return False

if from_ == state.current_area_old and to == state.current_area_new:
print(
"highjack_transition |",
f"From: {hex(state.current_area_old)},",
f"To: {hex(state.current_area_new)}.",
f"Redirecting to: {hex(redirect)}",
)
memory.write_u32(ADDRESSES.current_area, redirect)
return True
return False


def highjack_transition_rando():
# Early return, faster check. Detect the start of a transition
if state.current_area_old == state.current_area_new:
return False

redirect = transitions_map.get((state.current_area_old, state.current_area_new))
if not redirect:
return False

# Apply Altar of Ages logic to St. Claire's Excavation Camp
if redirect.to in {LevelCRC.ST_CLAIRE_DAY, LevelCRC.ST_CLAIRE_NIGHT}:
redirect = Transition(
redirect.from_,
to=LevelCRC.ST_CLAIRE_NIGHT if state.visited_altar_of_ages else LevelCRC.ST_CLAIRE_DAY,
)

# Check if you're visiting a Temple for the first time, if so go directly to Spirit Fight
if redirect.to in temples:
spirit = TRANSITION_INFOS_DICT[redirect.to].exits[1].area_id
if not state.visited_spirits[spirit]:
redirect = Transition(from_=redirect.to, to=spirit)

print(
"highjack_transition_rando |",
f"From: {hex(state.current_area_old)},",
f"To: {hex(state.current_area_new)}.",
f"Redirecting to: {hex(redirect.to)}",
f"({hex(redirect.from_)} entrance)\n",
)
memory.write_u32(follow_pointer_path(ADDRESSES.prev_area), redirect.from_)
memory.write_u32(ADDRESSES.current_area, redirect.to)
state.current_area_new = redirect[1]
return redirect


def link_two_levels(first: Area, second: Area):
__connections_left[first.area_id] -= 1
__connections_left[second.area_id] -= 1
Expand Down
40 changes: 22 additions & 18 deletions Dolphin scripts/Entrance Randomizer/lib/graph_creation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from collections.abc import Mapping
from collections.abc import Mapping, Sequence
from pathlib import Path

from lib.constants import * # noqa: F403
Expand Down Expand Up @@ -63,25 +63,25 @@
output_text += (
f'<node positionX="{counter_x * 100 + counter_y * 20}" '
+ f'positionY="{counter_x * 50 + counter_y * 50}" '
+ f'id="{int(area_id)}" mainText="{area_name}" '
+ f'id="{int(area_id)}" mainText="{area_name}"'
)
if area_id == starting_area:
output_text += (
'ownStyles = "{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
' ownStyles="{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
+ STARTING_AREA_COLOR
+ '&quot;}}" '
+ '&quot;}}"'
)
elif area_id in UPGRADE_AREAS:
output_text += (
'ownStyles = "{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
' ownStyles="{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
+ UPGRADE_AREAS_COLOR
+ '&quot;}}" '
+ '&quot;}}"'
)
elif area_id in IMPORTANT_STORY_TRIGGER_AREAS:
output_text += (
'ownStyles = "{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
' ownStyles="{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
+ IMPORTANT_STORY_TRIGGER_AREAS_COLOR
+ '&quot;}}" '
+ '&quot;}}"'
)
output_text += "></node>\n"
row_length = 10
Expand Down Expand Up @@ -109,32 +109,36 @@
output_text += (
f'<edge source="{TRANSITION_INFOS_DICT[pairing[0]].area_id}" '
+ f'target="{TRANSITION_INFOS_DICT[pairing[1]].area_id}" isDirect="false" '
+ f'id="{counter}" ></edge>\n'
+ f'id="{counter}"></edge>\n'
)
counter += 1
for pairing in connections_one_way:
output_text += (
f'<edge source="{TRANSITION_INFOS_DICT[pairing[0]].area_id}" '
+ f'target="{TRANSITION_INFOS_DICT[pairing[1]].area_id}" isDirect="true" '
+ f'id="{counter}" ></edge>\n'
+ f'id="{counter}"></edge>\n'
)
counter += 1
return output_text


def create_graphml(
transitions_map: Mapping[tuple[int, int], tuple[int, int]],
temp_disabled_exits: Sequence[tuple[int, int]],
seed_string: SeedType,
starting_area: int,
):
graphml_text = f"""\
<?xml version="1.0" encoding="UTF-8"?>
<graphml>
<graph id="Graph" uidGraph="1" uidEdge="1">
{create_vertices(transitions_map, starting_area)}
{create_edges(transitions_map)}
</graph>
</graphml>"""
all_transitions: Mapping[tuple[int, int], tuple[int, int]] = transitions_map.copy()

Check failure on line 131 in Dolphin scripts/Entrance Randomizer/lib/graph_creation.py

View workflow job for this annotation

GitHub Actions / Run pyright

Type of "all_transitions" is partially unknown   Type of "all_transitions" is "Unknown | Mapping[tuple[int, int], tuple[int, int]]" (reportUnknownVariableType)

Check failure on line 131 in Dolphin scripts/Entrance Randomizer/lib/graph_creation.py

View workflow job for this annotation

GitHub Actions / Run pyright

Type of "copy" is unknown (reportUnknownMemberType)

Check failure on line 131 in Dolphin scripts/Entrance Randomizer/lib/graph_creation.py

View workflow job for this annotation

GitHub Actions / Run pyright

Cannot access attribute "copy" for class "Mapping[tuple[int, int], tuple[int, int]]"   Attribute "copy" is unknown (reportAttributeAccessIssue)
wossnameGitHub marked this conversation as resolved.
Show resolved Hide resolved
for item in temp_disabled_exits:
all_transitions[item] = item

Check failure on line 133 in Dolphin scripts/Entrance Randomizer/lib/graph_creation.py

View workflow job for this annotation

GitHub Actions / Run pyright

"__setitem__" method not defined on type "Mapping[tuple[int, int], tuple[int, int]]" (reportIndexIssue)

graphml_text = (
'<?xml version="1.0" encoding="UTF-8"?>'
+ '<graphml><graph id="Graph" uidGraph="1" uidEdge="1">\n'
+ create_vertices(all_transitions, starting_area)

Check failure on line 138 in Dolphin scripts/Entrance Randomizer/lib/graph_creation.py

View workflow job for this annotation

GitHub Actions / Run pyright

Argument type is partially unknown   Argument corresponds to parameter "transitions_map" in function "create_vertices"   Argument type is "Unknown | Mapping[tuple[int, int], tuple[int, int]]" (reportUnknownArgumentType)
+ create_edges(all_transitions)

Check failure on line 139 in Dolphin scripts/Entrance Randomizer/lib/graph_creation.py

View workflow job for this annotation

GitHub Actions / Run pyright

Argument type is partially unknown   Argument corresponds to parameter "transitions_map" in function "create_edges"   Argument type is "Unknown | Mapping[tuple[int, int], tuple[int, int]]" (reportUnknownArgumentType)
+ "</graph></graphml>"
)

# TODO (Avasam): Get actual user folder based whether Dolphin Emulator is in AppData/Roaming
# and if the current installation is portable.
Expand Down
Loading
Loading