"""Schematy: nazwy kolumn .DTA i znaczenie indeksów w .ARR. Kolumny .DTA nie są zapisane w pliku — pochodzą z definicji STRUCT w Arcade.cnv (linie 1455-1489). Znaczenia pól ARR z MAINMENU.class (DEFSETS, SAVEGAME). """ from __future__ import annotations from dataclasses import dataclass INT = "INTEGER" STR = "STRING" @dataclass(frozen=True) class Column: name: str type: str note: str = "" #: SOBJECT — model bazy obiektów sceny (.DTA). SOBJECT: tuple[Column, ...] = ( Column("NAME", STR, "ścieżka do .ANN/.IMG/.SEK względem katalogu sceny"), Column("IDNAME", STR, "identyfikator logiczny, używany przez skrypty sceny"), Column("TYPE", INT, "rola wiersza — patrz OBJECT_TYPES"), Column("SPARAM0", STR), Column("SPARAM1", STR), Column("SPARAM2", STR), Column("IPARAM0", INT, "dla TYPE==2: 1 = widoczny, 0 = zabrany"), Column("IPARAM1", INT), Column("IPARAM2", INT), ) #: SITEM — model ekwipunku (ITEMS.DTA). SITEM: tuple[Column, ...] = ( Column("GUID", INT, "indeks slotu 0..9, równy numerowi wiersza"), Column("NAME", STR, "identyfikator przedmiotu, NULL gdy pusto"), Column("PARENT", INT, "indeks wiersza w .DTA lokacji, z której podniesiono (-1 = brak)"), Column("BASE", STR, "lokacja pochodzenia"), Column("EMPTY", INT, "MYLĄCA NAZWA: 1 = slot ZAJĘTY, 0 = wolny"), ) DTA_SCHEMAS: dict[str, tuple[Column, ...]] = { "scene": SOBJECT, "items": SITEM, } #: Kolumna TYPE w SOBJECT — dekoder z Arcade.cnv:__LOAD_DB__. OBJECT_TYPES: dict[int, str] = { 101: "tło statyczne (.IMG)", 102: "maska ścieżek chodzenia (.SEK)", 103: "aktywacja przejścia: WPATH^SETACTIVE(IPARAM0, IPARAM1, IPARAM2)", 104: "Reksio; IPARAM0 = prędkość maks.", 105: "punkt startowy, zestaw 0; IDNAME = scena skąd, IPARAM0/1 = X/Y", 106: "punkt docelowy, zestaw 0; IPARAM0/1 = X/Y", 107: "tło animowane; IPARAM0/1 = szerokość/wysokość", 108: "muzyka lokacji; IDNAME = plik .WAV", 140: "Kret", 145: "punkt startowy, zestaw 1", 146: "punkt docelowy, zestaw 1", } def describe_object_type(value: int) -> str: if value in OBJECT_TYPES: return OBJECT_TYPES[value] if value < 100: extra = " — IPARAM0 to flaga widoczności" if value == 2 else "" return f"obiekt sceny (ANNOBJECT), podtyp {value}{extra}" return f"nieznany typ {value}" #: GAME.ARR — 6 elementów, wszystkie zapisane jako STRING. GAMESETS: tuple[Column, ...] = ( Column("G_SARCADEOBJECTS", STR, "bieżąca lokacja"), Column("G_SLASTOBJECTS", STR, "poprzednia lokacja — wyznacza punkt wejścia"), Column("G_SDIALOGCHARACTER", STR, "rozmówca"), Column("G_SDIALOGRETURN", STR, "dokąd wrócić po rozmowie"), Column("G_ITKTPM", INT, "identyfikator \"Tego, Który Tak Potwornie Mąci\""), Column("G_IKRETACTIVE", INT, "czy Kret towarzyszy Reksiowi (0/1)"), ) #: Stan fabryczny GAME0.ARR z DEFSETS (indeks 4 jest losowany 0..7). GAMESETS_DEFAULT: list[str] = ["INTRO_1", "NULL", "BUREKTOR", "NULL", "0", "0"] #: SPELLS.ARR — 17 intów. Stan fabryczny z NEWGAME. SPELLS_DEFAULT: list[int] = [0] * 12 + [64] + [0] * 4 #: Próg doświadczenia na jeden poziom (MAINMENU.class: LEVELUP, BFITMP57/59/61). EXP_PER_LEVEL = 150 #: Ile klatek animacji klepsydry — klatka = exp // 10 + 1 (BFITMP55). EXP_PER_HOURGLASS_FRAME = 10 #: Indeksy 0..11 to flagi „czar znany" (0/1). Sześć czarów w wersji zwykłej (0-5) #: i obronnej (6-11, sufiks _OBR); czar N ma obronę N+6. Nazwy z uchwytów klikania #: kul w gabinetach — Guldryk.cnv, Gabinetsnejka.cnv, Wmwnetrze.cnv. SPELL_NAMES: tuple[str, ...] = ( "Sen", "Zamiana w żabę", "Plaga much", "Kula budyniu", "Koła i wiry", "Ciemność", "Obrona przed Snem", "Obrona przed Zamianą w żabę", "Obrona przed Plagą much", "Obrona przed Kulą budyniu", "Obrona przed Kołami i wirami", "Obrona przed Ciemnością", ) #: Indeksy 12..16 — liczniki i flagi stanu klepsydry. SPELLS_NOTES: dict[int, str] = { 12: "startuje na 64, SUBAT(12,3) przy każdym awansie — nikt tego nie czyta", 13: f"DOŚWIADCZENIE, 0..{EXP_PER_LEVEL - 1}; po przekroczeniu progu reszta przechodzi dalej", 14: "licznik awansów (ile razy klepsydra się przesypała)", 15: "1 → klepsydra/różdżka widoczna w menu (SHOWEXP, M_WAND)", 16: "1 → klepsydra miga, można nauczyć się czaru", } #: Indeks doświadczenia w SPELLS.ARR. EXP_INDEX = 13 LEVEL_INDEX = 14 HOURGLASS_VISIBLE_INDEX = 15 HOURGLASS_BLINKING_INDEX = 16 def describe_spell_index(index: int) -> str: if index < len(SPELL_NAMES): return f"{SPELL_NAMES[index]} (1 = znany)" return SPELLS_NOTES.get(index, "nieznane")