Skip to content

Commit

Permalink
Add graph of connections in .graphml format
Browse files Browse the repository at this point in the history
  • Loading branch information
wossnameGitHub authored and Avasam committed Jun 8, 2024
1 parent 3383a65 commit 0808d99
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Dolphin scripts/Entrance Randomizer/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
starting_area,
transitions_map,
)
from lib.graph_creation import create_graphml
from lib.shaman_shop import patch_shaman_shop, randomize_shaman_shop
from lib.utils import (
draw_text,
Expand All @@ -47,6 +48,7 @@

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


async def main_loop():
Expand Down
136 changes: 136 additions & 0 deletions Dolphin scripts/Entrance Randomizer/lib/graph_creation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from __future__ import annotations

from collections.abc import Mapping
from pathlib import Path

from lib.constants import * # noqa: F403
from lib.constants import __version__
from lib.types_ import SeedType

STARTING_AREA_COLOR = "#ff8000" # Orange
UPGRADE_AREAS_COLOR = "#0080ff" # Blue
IMPORTANT_STORY_TRIGGER_AREAS_COLOR = "#ff0000" # Red
UPGRADE_AREAS = {
LevelCRC.PLANE_COCKPIT,
LevelCRC.BITTENBINDERS_CAMP,
LevelCRC.MOUTH_OF_INTI,
LevelCRC.FLOODED_COURTYARD,
LevelCRC.TURTLE_MONUMENT,
LevelCRC.NATIVE_VILLAGE,
LevelCRC.RENEGADE_HEADQUARTERS,
LevelCRC.CAVERN_LAKE,
LevelCRC.MOUNTAIN_SLED_RUN,
LevelCRC.MOUNTAIN_OVERLOOK,
LevelCRC.APU_ILLAPU_SHRINE,
}
IMPORTANT_STORY_TRIGGER_AREAS = {
LevelCRC.ALTAR_OF_AGES,
LevelCRC.ST_CLAIRE_DAY,
LevelCRC.ST_CLAIRE_NIGHT,
LevelCRC.GATES_OF_EL_DORADO,
}


def create_vertices(
transitions_map: Mapping[tuple[int, int], tuple[int, int]],
starting_area: int,
):
output_text = ""
area_ids_randomized = set(
chain(
*(
(original[0], redirect[1])
for original, redirect
in transitions_map.items()
),
),
)
counter_x = 0
counter_y = 0
for area_id in area_ids_randomized:
output_text += (
f'<node positionX="{counter_x * 100 + counter_y * 20}" '
+ f'positionY="{counter_x * 50 + counter_y * 50}" '
+ f'id="{area_id}" mainText="{TRANSITION_INFOS_DICT[area_id].name}" '
)
if area_id == starting_area:
output_text += (
'ownStyles = "{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
+ STARTING_AREA_COLOR
+ '&quot;}}" '
)
elif area_id in UPGRADE_AREAS:
output_text += (
'ownStyles = "{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
+ UPGRADE_AREAS_COLOR
+ '&quot;}}" '
)
elif area_id in IMPORTANT_STORY_TRIGGER_AREAS:
output_text += (
'ownStyles = "{&quot;0&quot;:{&quot;fillStyle&quot;:&quot;'
+ IMPORTANT_STORY_TRIGGER_AREAS_COLOR
+ '&quot;}}" '
)
output_text += "></node>\n"
row_length = 10
counter_x += 1
if counter_x == row_length:
counter_x = 0
counter_y += 1
return output_text


def create_edges(transitions_map: Mapping[tuple[int, int], tuple[int, int]]):
output_text = ""
connections = [(original[0], redirect[1]) for original, redirect in transitions_map.items()]
connections_two_way: list[tuple[int, int]] = []
connections_one_way: list[tuple[int, int]] = []
for pairing in connections:
if (pairing[1], pairing[0]) not in connections_two_way:
if (pairing[1], pairing[0]) in connections:
connections_two_way.append(pairing)
else:
connections_one_way.append(pairing)
counter = 1 # Can't start at 0 since that's the MAIN_MENU id
for pairing in connections_two_way:
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'
)
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'
)
counter += 1
return output_text


def create_graphml(
transitions_map: Mapping[tuple[int, int], 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>"""

# TODO (Avasam): Get actual user folder based whether Dolphin Emulator is in AppData/Roaming
# and if the current installation is portable.
dolphin_path = Path().absolute()
graphml_file = (
dolphin_path
/ "User"
/ "Logs"
/ f"RANDOMIZED_MAP_v{__version__}_{seed_string}.graphml"
)
Path.write_text(graphml_file, graphml_text)
print("Graphml file written to", graphml_file)
10 changes: 10 additions & 0 deletions Dolphin scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
6. In Dolphin, under "Scripts", click "Add new Scripts" and select `Scripts/Entrance Randomizer/__main__.py`.
7. Enjoy and watch logs for errors 🙂

### About the `.graphml` file

In order to display the generated map take these steps:

1. Go to <https://graphonline.ru/en/>
2. Under "Graph" choose "Import from file" and choose your generated `.graphml` file
3. Under "Algorithms" choose "Arrange the graph"
4. Enjoy your map! You can freely move the dots, and you can use multiple features under the "Algorithms" tab (like "Find all paths" for instance)
5. You can also save your map at any time by choosing "Graph" --> "Export to file"

### Known issues and limitations

- To generate a new seed, simply reload the script.
Expand Down

0 comments on commit 0808d99

Please sign in to comment.