name: "Main Area" panels { name: "COURTYARD" path: "Panels/Entrances/panel_1" clue: "courtyard" answer: "plaza" symbols: SUN } panels { name: "ASTOUNDING" path: "Panels/Entrances/panel_2" clue: "astounding" answer: "impressive" symbols: PYRAMID } panels { name: "INFLEXIBLE" path: "Panels/Entrances/panel_3" clue: "inflexible" answer: "unyielding" symbols: SUN } panels { name: "AMONG" path: "Panels/Entrances/panel_4" clue: "among" answer: "between" symbols: SUN } panels { name: "AFFABLE" path: "Panels/Entrances/panel_7" clue: "affable" answer: "charismatic" symbols: SUN } ports { name: "UNYIELDING" path: "Components/Warps/worldport" orientation: "west" } ports { name: "IMPRESSIVE" path: "Components/Warps/worldport2" orientation: "west" } ports { name: "BETWEEN" path: "Components/Warps/worldport3" orientation: "west" } oup label='branches'> Unnamed repository; edit this file 'description' to name the repository.
summary refs log tree commit diff stats
path: root/utils/pickle_static_data.py
blob: 92bcb7a859ea69a97d8eeb3041a7cc1e16578ced (plain) (blame)
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
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
name: "North Castle Area"
panel_display_name: "North Area"
panels {
  name: "A"
  path: "Panels/Castle Entrance/castle_north_helper"
  clue: "a"
  answer: "the"
  symbols: EXAMPLE
}
panels {
  name: "SUMMER"
  path: "Panels/Castle Entrance/castle_north_helper2"
  clue: "summer"
  answer: "winter"
  symbols: EXAMPLE
}
panels {
  name: "PLACE"
  path: "Panels/Castle Entrance/castle_north_helper3"
  clue: "place"
  answer: "palace"
  symbols: SPARKLES
}
panels {
  name: "A SUMMER PLACE"
  path: "Panels/Castle Entrance/castle_north"
  clue: ""
  answer: "castle"
  symbols: EXAMPLE
  symbols: LINGO
  required_door { name: "Castle North Hider" }
}
paintings {
  name: "RELIC2"
  path: "Components/Paintings/Group1/relic2"
}
id='n266' href='#n266'>266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
from typing import Dict, List, Set, Optional

import os
import sys

sys.path.append(os.path.join("worlds", "lingo"))
sys.path.append(".")
sys.path.append("..")
from datatypes import Door, DoorType, EntranceType, Painting, Panel, PanelDoor, Progression, Room, RoomAndDoor,\
    RoomAndPanel, RoomAndPanelDoor, RoomEntrance

import hashlib
import pickle
import sys
import Utils


ALL_ROOMS: List[Room] = []
DOORS_BY_ROOM: Dict[str, Dict[str, Door]] = {}
PANELS_BY_ROOM: Dict[str, Dict[str, Panel]] = {}
PANEL_DOORS_BY_ROOM: Dict[str, Dict[str, PanelDoor]] = {}
PAINTINGS: Dict[str, Painting] = {}

PROGRESSIVE_ITEMS: Set[str] = set()
PROGRESSIVE_DOORS_BY_ROOM: Dict[str, Dict[str, Progression]] = {}
PROGRESSIVE_PANELS_BY_ROOM: Dict[str, Dict[str, Progression]] = {}

PAINTING_ENTRANCES: int = 0
PAINTING_EXIT_ROOMS: Set[str] = set()
PAINTING_EXITS: int = 0
REQUIRED_PAINTING_ROOMS: List[str] = []
REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS: List[str] = []

SUNWARP_ENTRANCES: List[str] = ["", "", "", "", "", ""]
SUNWARP_EXITS: List[str] = ["", "", "", "", "", ""]

SPECIAL_ITEM_IDS: Dict[str, int] = {}
PANEL_LOCATION_IDS: Dict[str, Dict[str, int]] = {}
DOOR_LOCATION_IDS: Dict[str, Dict[str, int]] = {}
DOOR_ITEM_IDS: Dict[str, Dict[str, int]] = {}
DOOR_GROUP_ITEM_IDS: Dict[str, int] = {}
PANEL_DOOR_ITEM_IDS: Dict[str, Dict[str, int]] = {}
PANEL_GROUP_ITEM_IDS: Dict[str, int] = {}
PROGRESSIVE_ITEM_IDS: Dict[str, int] = {}

# This doesn't need to be stored in the datafile.
PANEL_DOOR_BY_PANEL_BY_ROOM: Dict[str, Dict[str, str]] = {}


def hash_file(path):
    md5 = hashlib.md5()
    
    with open(path, 'rb') as f:
        content = f.read()
        content = content.replace(b'\r\n', b'\n')
        md5.update(content)
    
    return md5.hexdigest()


def load_static_data(ll1_path, ids_path):
    global PAINTING_EXITS, SPECIAL_ITEM_IDS, PANEL_LOCATION_IDS, DOOR_LOCATION_IDS, DOOR_ITEM_IDS, \
        DOOR_GROUP_ITEM_IDS, PROGRESSIVE_ITEM_IDS, PANEL_DOOR_ITEM_IDS, PANEL_GROUP_ITEM_IDS

    # Load in all item and location IDs. These are broken up into groups based on the type of item/location.
    with open(ids_path, "r") as file:
        config = Utils.parse_yaml(file)

        if "special_items" in config:
            for item_name, item_id in config["special_items"].items():
                SPECIAL_ITEM_IDS[item_name] = item_id

        if "panels" in config:
            for room_name in config["panels"].keys():
                PANEL_LOCATION_IDS[room_name] = {}

                for panel_name, location_id in config["panels"][room_name].items():
                    PANEL_LOCATION_IDS[room_name][panel_name] = location_id

        if "doors" in config:
            for room_name in config["doors"].keys():
                DOOR_LOCATION_IDS[room_name] = {}
                DOOR_ITEM_IDS[room_name] = {}

                for door_name, door_data in config["doors"][room_name].items():
                    if "location" in door_data:
                        DOOR_LOCATION_IDS[room_name][door_name] = door_data["location"]

                    if "item" in door_data:
                        DOOR_ITEM_IDS[room_name][door_name] = door_data["item"]

        if "door_groups" in config:
            for item_name, item_id in config["door_groups"].items():
                DOOR_GROUP_ITEM_IDS[item_name] = item_id

        if "panel_doors" in config:
            for room_name, panel_doors in config["panel_doors"].items():
                PANEL_DOOR_ITEM_IDS[room_name] = {}

                for panel_door, item_id in panel_doors.items():
                    PANEL_DOOR_ITEM_IDS[room_name][panel_door] = item_id

        if "panel_groups" in config:
            for item_name, item_id in config["panel_groups"].items():
                PANEL_GROUP_ITEM_IDS[item_name] = item_id

        if "progression" in config:
            for item_name, item_id in config["progression"].items():
                PROGRESSIVE_ITEM_IDS[item_name] = item_id

    # Process the main world file.
    with open(ll1_path, "r") as file:
        config = Utils.parse_yaml(file)

        for room_name, room_data in config.items():
            process_room(room_name, room_data)

        PAINTING_EXITS = len(PAINTING_EXIT_ROOMS)


def process_single_entrance(source_room: str, room_name: str, door_obj) -> RoomEntrance:
    global PAINTING_ENTRANCES, PAINTING_EXIT_ROOMS

    entrance_type = EntranceType.NORMAL
    if "painting" in door_obj and door_obj["painting"]:
        entrance_type = EntranceType.PAINTING
    elif "sunwarp" in door_obj and door_obj["sunwarp"]:
        entrance_type = EntranceType.SUNWARP
    elif "warp" in door_obj and door_obj["warp"]:
        entrance_type = EntranceType.WARP
    elif source_room == "Crossroads" and room_name == "Roof":
        entrance_type = EntranceType.CROSSROADS_ROOF_ACCESS

    if "painting" in door_obj and door_obj["painting"]:
        PAINTING_EXIT_ROOMS.add(room_name)
        PAINTING_ENTRANCES += 1

    if "door" in door_obj:
        return RoomEntrance(source_room, RoomAndDoor(
            door_obj["room"] if "room" in door_obj else None,
            door_obj["door"]
        ), entrance_type)
    else:
        return RoomEntrance(source_room, None, entrance_type)


def process_entrance(source_room, doors, room_obj):
    # If the value of an entrance is just True, that means that the entrance is always accessible.
    if doors is True:
        room_obj.entrances.append(RoomEntrance(source_room, None, EntranceType.NORMAL))
    elif isinstance(doors, dict):
        # If the value of an entrance is a dictionary, that means the entrance requires a door to be accessible, is a
        # painting-based entrance, or both.
        room_obj.entrances.append(process_single_entrance(source_room, room_obj.name, doors))
    else:
        # If the value of an entrance is a list, then there are multiple possible doors that can give access to the
        # entrance. If there are multiple connections with the same door (or lack of door) that differ only by entrance
        # type, coalesce them into one entrance.
        entrances: Dict[Optional[RoomAndDoor], EntranceType] = {}
        for door in doors:
            entrance = process_single_entrance(source_room, room_obj.name, door)
            entrances[entrance.door] = entrances.get(entrance.door, EntranceType(0)) | entrance.type

        for door, entrance_type in entrances.items():
            room_obj.entrances.append(RoomEntrance(source_room, door, entrance_type))


def process_panel_door(room_name, panel_door_name, panel_door_data):
    global PANEL_DOORS_BY_ROOM, PANEL_DOOR_BY_PANEL_BY_ROOM

    panels: List[RoomAndPanel] = list()
    for panel in panel_door_data["panels"]:
        if isinstance(panel, dict):
            panels.append(RoomAndPanel(panel["room"], panel["panel"]))
        else:
            panels.append(RoomAndPanel(room_name, panel))

    for panel in panels:
        PANEL_DOOR_BY_PANEL_BY_ROOM.setdefault(panel.room, {})[panel.panel] = RoomAndPanelDoor(room_name,
                                                                                               panel_door_name)

    if "item_name" in panel_door_data:
        item_name = panel_door_data["item_name"]
    else:
        panel_per_room = dict()
        for panel in panels:
            panel_room_name = room_name if panel.room is None else panel.room
            panel_per_room.setdefault(panel_room_name, []).append(panel.panel)

        room_strs = list()
        for door_room_str, door_panels_str in panel_per_room.items():
            room_strs.append(door_room_str + " - " + ", ".join(door_panels_str))

        if len(panels) == 1:
            item_name = f"{room_strs[0]} (Panel)"
        else:
            item_name = " and ".join(room_strs) + " (Panels)"

    if "panel_group" in panel_door_data:
        panel_group = panel_door_data["panel_group"]
    else:
        panel_group = None

    panel_door_obj = PanelDoor(item_name, panel_group)
    PANEL_DOORS_BY_ROOM[room_name][panel_door_name] = panel_door_obj


def process_panel(room_name, panel_name, panel_data):
    global PANELS_BY_ROOM

    # required_room can either be a single room or a list of rooms.
    if "required_room" in panel_data:
        if isinstance(panel_data["required_room"], list):
            required_rooms = panel_data["required_room"]
        else:
            required_rooms = [panel_data["required_room"]]
    else:
        required_rooms = []

    # required_door can either be a single door or a list of doors. For convenience, the room key for each door does not
    # need to be specified if the door is in this room.
    required_doors = list()
    if "required_door" in panel_data:
        if isinstance(panel_data["required_door"], dict):
            door = panel_data["required_door"]
            required_doors.append(RoomAndDoor(
                door["room"] if "room" in door else None,
                door["door"]
            ))
        else:
            for door in panel_data["required_door"]:
                required_doors.append(RoomAndDoor(
                    door["room"] if "room" in door else None,
                    door["door"]
                ))

    # required_panel can either be a single panel or a list of panels. For convenience, the room key for each panel does
    # not need to be specified if the panel is in this room.
    required_panels = list()
    if "required_panel" in panel_data:
        if isinstance(panel_data["required_panel"], dict):
            other_panel = panel_data["required_panel"]
            required_panels.append(RoomAndPanel(
                other_panel["room"] if "room" in other_panel else None,
                other_panel["panel"]
            ))
        else:
            for other_panel in panel_data["required_panel"]:
                required_panels.append(RoomAndPanel(
                    other_panel["room"] if "room" in other_panel else None,
                    other_panel["panel"]
                ))

    # colors can either be a single color or a list of colors.
    if "colors" in panel_data:
        if isinstance(panel_data["colors"], list):
            colors = panel_data["colors"]
        else:
            colors = [panel_data["colors"]]
    else:
        colors = []

    if "check" in panel_data:
        check = panel_data["check"]
    else:
        check = False

    if "event" in panel_data:
        event = panel_data["event"]
    else:
        event = False

    if "achievement" in panel_data:
        achievement = True
    else:
        achievement = False

    if "exclude_reduce" in panel_data:
        exclude_reduce = panel_data["exclude_reduce"]
    else:
        exclude_reduce = False

    if "non_counting" in panel_data:
        non_counting = panel_data["non_counting"]
    else:
        non_counting = False

    if room_name in PANEL_DOOR_BY_PANEL_BY_ROOM and panel_name in PANEL_DOOR_BY_PANEL_BY_ROOM[room_name]:
        panel_door = PANEL_DOOR_BY_PANEL_BY_ROOM[room_name][panel_name]
    else:
        panel_door = None

    if "location_name" in panel_data:
        location_name = panel_data["location_name"]
    else:
        location_name = None

    panel_obj = Panel(required_rooms, required_doors, required_panels, colors, check, event, exclude_reduce,
                      achievement, non_counting, panel_door, location_name)
    PANELS_BY_ROOM[room_name][panel_name] = panel_obj


def process_door(room_name, door_name, door_data):
    global DOORS_BY_ROOM

    # The item name associated with a door can be explicitly specified in the configuration. If it is not, it is
    # generated from the room and door name.
    if "item_name" in door_data:
        item_name = door_data["item_name"]
    else:
        item_name = f"{room_name} - {door_name}"

    if "skip_location" in door_data:
        skip_location = door_data["skip_location"]
    else:
        skip_location = False

    if "skip_item" in door_data:
        skip_item = door_data["skip_item"]
    else:
        skip_item = False

    if "event" in door_data:
        event = door_data["event"]
    else:
        event = False

    if "include_reduce" in door_data:
        include_reduce = door_data["include_reduce"]
    else:
        include_reduce = False

    if "door_group" in door_data:
        door_group = door_data["door_group"]
    else:
        door_group = None

    if "item_group" in door_data:
        item_group = door_data["item_group"]
    else:
        item_group = None

    # panels is a list of panels. Each panel can either be a simple string (the name of a panel in the current room) or
    # a dictionary specifying a panel in a different room.
    if "panels" in door_data:
        panels = list()
        for panel in door_data["panels"]:
            if isinstance(panel, dict):
                panels.append(RoomAndPanel(panel["room"], panel["panel"]))
            else:
                panels.append(RoomAndPanel(None, panel))
    else:
        skip_location = True
        panels = []

    # The location name associated with a door can be explicitly specified in the configuration. If it is not, then the
    # name is generated using a combination of all of the panels that would ordinarily open the door. This can get quite
    # messy if there are a lot of panels, especially if panels from multiple rooms are involved, so in these cases it
    # would be better to specify a name.
    if "location_name" in door_data:
        location_name = door_data["location_name"]
    elif skip_location is False:
        panel_per_room = dict()
        for panel in panels:
            panel_room_name = room_name if panel.room is None else panel.room
            panel_per_room.setdefault(panel_room_name, []).append(panel.panel)

        room_strs = list()
        for door_room_str, door_panels_str in panel_per_room.items():
            room_strs.append(door_room_str + " - " + ", ".join(door_panels_str))

        location_name = " and ".join(room_strs)
    else:
        location_name = None

    # The id field can be a single item, or a list of door IDs, in the event that the item for this logical door should
    # open more than one actual in-game door.
    has_doors = "id" in door_data

    # The painting_id field can be a single item, or a list of painting IDs, in the event that the item for this logical
    # door should move more than one actual in-game painting.
    if "painting_id" in door_data:
        if isinstance(door_data["painting_id"], list):
            painting_ids = door_data["painting_id"]
        else:
            painting_ids = [door_data["painting_id"]]
    else:
        painting_ids = []

    door_type = DoorType.NORMAL
    if room_name == "Sunwarps":
        door_type = DoorType.SUNWARP
    elif room_name == "Pilgrim Antechamber" and door_name == "Sun Painting":
        door_type = DoorType.SUN_PAINTING

    door_obj = Door(door_name, item_name, location_name, panels, skip_location, skip_item, has_doors,
                    painting_ids, event, door_group, include_reduce, door_type, item_group)

    DOORS_BY_ROOM[room_name][door_name] = door_obj


def process_painting(room_name, painting_data):
    global PAINTINGS, REQUIRED_PAINTING_ROOMS, REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS

    # Read in information about this painting and store it in an object.
    painting_id = painting_data["id"]

    if "disable" in painting_data:
        disable_painting = painting_data["disable"]
    else:
        disable_painting = False

    if "required" in painting_data:
        required_painting = painting_data["required"]
        if required_painting:
            REQUIRED_PAINTING_ROOMS.append(room_name)
    else:
        required_painting = False

    if "required_when_no_doors" in painting_data:
        rwnd = painting_data["required_when_no_doors"]
        if rwnd:
            REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS.append(room_name)
    else:
        rwnd = False

    if "exit_only" in painting_data:
        exit_only = painting_data["exit_only"]
    else:
        exit_only = False

    if "enter_only" in painting_data:
        enter_only = painting_data["enter_only"]
    else:
        enter_only = False

    if "req_blocked" in painting_data:
        req_blocked = painting_data["req_blocked"]
    else:
        req_blocked = False

    if "req_blocked_when_no_doors" in painting_data:
        req_blocked_when_no_doors = painting_data["req_blocked_when_no_doors"]
    else:
        req_blocked_when_no_doors = False

    required_door = None
    if "required_door" in painting_data:
        door = painting_data["required_door"]
        required_door = RoomAndDoor(
            door["room"] if "room" in door else room_name,
            door["door"]
        )

    painting_obj = Painting(painting_id, room_name, enter_only, exit_only,
                            required_painting, rwnd, required_door, disable_painting, req_blocked,
                            req_blocked_when_no_doors)
    PAINTINGS[painting_id] = painting_obj


def process_sunwarp(room_name, sunwarp_data):
    global SUNWARP_ENTRANCES, SUNWARP_EXITS

    if sunwarp_data["direction"] == "enter":
        SUNWARP_ENTRANCES[sunwarp_data["dots"] - 1] = room_name
    else:
        SUNWARP_EXITS[sunwarp_data["dots"] - 1] = room_name


def process_progressive_door(room_name, progression_name, progression_doors):
    global PROGRESSIVE_ITEMS, PROGRESSIVE_DOORS_BY_ROOM

    # Progressive items are configured as a list of doors.
    PROGRESSIVE_ITEMS.add(progression_name)

    progression_index = 1
    for door in progression_doors:
        if isinstance(door, Dict):
            door_room = door["room"]
            door_door = door["door"]
        else:
            door_room = room_name
            door_door = door

        room_progressions = PROGRESSIVE_DOORS_BY_ROOM.setdefault(door_room, {})
        room_progressions[door_door] = Progression(progression_name, progression_index)
        progression_index += 1


def process_progressive_panel(room_name, progression_name, progression_panel_doors):
    global PROGRESSIVE_ITEMS, PROGRESSIVE_PANELS_BY_ROOM

    # Progressive items are configured as a list of panel doors.
    PROGRESSIVE_ITEMS.add(progression_name)

    progression_index = 1
    for panel_door in progression_panel_doors:
        if isinstance(panel_door, Dict):
            panel_door_room = panel_door["room"]
            panel_door_door = panel_door["panel_door"]
        else:
            panel_door_room = room_name
            panel_door_door = panel_door

        room_progressions = PROGRESSIVE_PANELS_BY_ROOM.setdefault(panel_door_room, {})
        room_progressions[panel_door_door] = Progression(progression_name, progression_index)
        progression_index += 1


def process_room(room_name, room_data):
    global ALL_ROOMS

    room_obj = Room(room_name, [])

    if "entrances" in room_data:
        for source_room, doors in room_data["entrances"].items():
            process_entrance(source_room, doors, room_obj)

    if "panel_doors" in room_data:
        PANEL_DOORS_BY_ROOM[room_name] = dict()

        for panel_door_name, panel_door_data in room_data["panel_doors"].items():
            process_panel_door(room_name, panel_door_name, panel_door_data)

    if "panels" in room_data:
        PANELS_BY_ROOM[room_name] = dict()

        for panel_name, panel_data in room_data["panels"].items():
            process_panel(room_name, panel_name, panel_data)

    if "doors" in room_data:
        DOORS_BY_ROOM[room_name] = dict()

        for door_name, door_data in room_data["doors"].items():
            process_door(room_name, door_name, door_data)

    if "paintings" in room_data:
        for painting_data in room_data["paintings"]:
            process_painting(room_name, painting_data)

    if "sunwarps" in room_data:
        for sunwarp_data in room_data["sunwarps"]:
            process_sunwarp(room_name, sunwarp_data)

    if "progression" in room_data:
        for progression_name, pdata in room_data["progression"].items():
            if "doors" in pdata:
                process_progressive_door(room_name, progression_name, pdata["doors"])
            if "panel_doors" in pdata:
                process_progressive_panel(room_name, progression_name, pdata["panel_doors"])

    ALL_ROOMS.append(room_obj)


if __name__ == '__main__':
    if len(sys.argv) == 1:
        ll1_path = os.path.join("worlds", "lingo", "data", "LL1.yaml")
        ids_path = os.path.join("worlds", "lingo", "data", "ids.yaml")
        output_path = os.path.join("worlds", "lingo", "data", "generated.dat")
    elif len(sys.argv) != 4:
        print("")
        print("Usage: python worlds/lingo/utils/pickle_static_data.py [args]")
        print("Arguments:")
        print(" - Path to LL1.yaml")
        print(" - Path to ids.yaml")
        print(" - Path to output file")
        
        exit()
    else:
        ll1_path = sys.argv[1]
        ids_path = sys.argv[2]
        output_path = sys.argv[3]
        
    load_static_data(ll1_path, ids_path)
    
    hashes = {
        "LL1.yaml": hash_file(ll1_path),
        "ids.yaml": hash_file(ids_path),
    }
    
    pickdata = {
        "HASHES": hashes,
        "PAINTINGS": PAINTINGS,
        "ALL_ROOMS": ALL_ROOMS,
        "DOORS_BY_ROOM": DOORS_BY_ROOM,
        "PANELS_BY_ROOM": PANELS_BY_ROOM,
        "PANEL_DOORS_BY_ROOM": PANEL_DOORS_BY_ROOM,
        "PROGRESSIVE_ITEMS": PROGRESSIVE_ITEMS,
        "PROGRESSIVE_DOORS_BY_ROOM": PROGRESSIVE_DOORS_BY_ROOM,
        "PROGRESSIVE_PANELS_BY_ROOM": PROGRESSIVE_PANELS_BY_ROOM,
        "PAINTING_ENTRANCES": PAINTING_ENTRANCES,
        "PAINTING_EXIT_ROOMS": PAINTING_EXIT_ROOMS,
        "PAINTING_EXITS": PAINTING_EXITS,
        "REQUIRED_PAINTING_ROOMS": REQUIRED_PAINTING_ROOMS,
        "REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS": REQUIRED_PAINTING_WHEN_NO_DOORS_ROOMS,
        "SUNWARP_ENTRANCES": SUNWARP_ENTRANCES,
        "SUNWARP_EXITS": SUNWARP_EXITS,
        "SPECIAL_ITEM_IDS": SPECIAL_ITEM_IDS,
        "PANEL_LOCATION_IDS": PANEL_LOCATION_IDS,
        "DOOR_LOCATION_IDS": DOOR_LOCATION_IDS,
        "DOOR_ITEM_IDS": DOOR_ITEM_IDS,
        "DOOR_GROUP_ITEM_IDS": DOOR_GROUP_ITEM_IDS,
        "PANEL_DOOR_ITEM_IDS": PANEL_DOOR_ITEM_IDS,
        "PANEL_GROUP_ITEM_IDS": PANEL_GROUP_ITEM_IDS,
        "PROGRESSIVE_ITEM_IDS": PROGRESSIVE_ITEM_IDS,
    }
    
    with open(output_path, "wb") as file:
        pickle.dump(pickdata, file)