File size: 3,289 Bytes
2803d7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from __future__ import annotations

from collections import defaultdict

from .schema import DoorNode, Edge, NpcTrade, ReadableNode, UseEffect, WorldDefinition


def readable_clue_mapping(world: WorldDefinition) -> dict[str, str]:
    return {node.id: node.clue_id for node in world.nodes if isinstance(node, ReadableNode)}


def clue_source_mapping(world: WorldDefinition) -> dict[str, str]:
    mapping = {node.clue_id: node.id for node in world.nodes if isinstance(node, ReadableNode)}
    for node in world.nodes:
        if node.type == "npc" and node.gives_clue_id:
            mapping[node.gives_clue_id] = node.id
    return mapping


def npc_trade_mapping(world: WorldDefinition) -> dict[str, NpcTrade]:
    trades: dict[str, NpcTrade] = {}
    for node in world.nodes:
        if node.type != "npc" or node.id == world.meta.win_condition.target_npc_id:
            continue
        trades[node.id] = NpcTrade(
            required_item_id=node.requires_item_id or "",
            gives_item_id=node.gives_item_id,
            gives_clue_id=node.gives_clue_id,
        )
    return trades


def use_effect_mapping(world: WorldDefinition) -> dict[str, UseEffect]:
    effects: dict[str, UseEffect] = {}
    for node in world.nodes:
        if node.type == "readable" and node.requires_item_id:
            effects[node.id] = UseEffect(
                required_item_id=node.requires_item_id,
                clue_id=node.clue_id,
                consumes_item=node.consumes_item,
            )
        elif node.type == "fixture":
            effects[node.id] = UseEffect(
                required_item_id=node.requires_item_id,
                reveals_item_id=node.reveals_item_id,
                reveals_readable_id=node.reveals_readable_id,
                consumes_item=node.consumes_item,
            )
    return effects


def recipe_mapping(world: WorldDefinition) -> dict[frozenset[str], str]:
    return {frozenset(recipe.input_item_ids): recipe.output_item_id for recipe in world.recipes}


def produced_item_ids(world: WorldDefinition) -> set[str]:
    produced = {recipe.output_item_id for recipe in world.recipes}
    for node in world.nodes:
        if node.type == "npc" and node.gives_item_id:
            produced.add(node.gives_item_id)
        if node.type == "fixture" and node.reveals_item_id:
            produced.add(node.reveals_item_id)
    return produced


def hidden_readable_ids(world: WorldDefinition) -> set[str]:
    return {node.reveals_readable_id for node in world.nodes if node.type == "fixture" and node.reveals_readable_id}


def door_room_mapping(world: WorldDefinition) -> dict[str, frozenset[str]]:
    mapping: dict[str, set[str]] = defaultdict(set)
    for edge in world.edges:
        if edge.door_node_id:
            mapping[edge.door_node_id].add(edge.from_node_id)
            mapping[edge.door_node_id].add(edge.to_node_id)
    return {door_id: frozenset(rooms) for door_id, rooms in mapping.items()}


def edge_for_door(world: WorldDefinition, door_id: str) -> Edge | None:
    for edge in world.edges:
        if edge.door_node_id == door_id:
            return edge
    return None


def door_nodes(world: WorldDefinition) -> dict[str, DoorNode]:
    return {node.id: node for node in world.nodes if isinstance(node, DoorNode)}