diff --git a/doc/src/patchouli/page/__init__.py b/doc/src/patchouli/page/__init__.py index 7216d6d0..d6eb7e16 100644 --- a/doc/src/patchouli/page/__init__.py +++ b/doc/src/patchouli/page/__init__.py @@ -1,2 +1,44 @@ +from common.deserialize import TypeHooks +from common.tagged_union import get_union_types + from .abstract import * -from .concrete import * +from .hexcasting import * +from .patchouli import * + +Page = ( + TextPage + | ImagePage + | CraftingPage + | SmeltingPage + | MultiblockPage + | EntityPage + | SpotlightPage + | LinkPage + | RelationsPage + | QuestPage + | EmptyPage + | PatternPage + | ManualPatternNosigPage + | ManualOpPatternPage + | ManualRawPatternPage + | CraftingMultiPage + | BrainsweepPage +) + + +def _raw_page_hook(data: dict[str, Any] | str) -> dict[str, Any]: + if isinstance(data, str): + # special case, thanks patchouli + return {"type": "patchouli:text", "text": data} + return data + + +def make_page_hooks(book: Book) -> TypeHooks: + """Creates type hooks for deserializing Page types.""" + + type_hooks: TypeHooks = {Page: _raw_page_hook} + + for cls_ in get_union_types(Page): + type_hooks[cls_] = cls_.make_type_hook(book) + + return type_hooks diff --git a/doc/src/patchouli/page/concrete.py b/doc/src/patchouli/page/concrete.py deleted file mode 100644 index cf755f96..00000000 --- a/doc/src/patchouli/page/concrete.py +++ /dev/null @@ -1,208 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import Any - -from common.deserialize import TypeHooks, rename -from common.formatting import FormatTree -from common.pattern import RawPatternInfo -from common.tagged_union import get_union_types -from common.types import Book, LocalizedItem, LocalizedStr -from minecraft.recipe import BrainsweepRecipe, CraftingRecipe -from minecraft.resource import Entity, ItemStack, ResourceLocation - -from .abstract import ( - BasePage, - PageWithCraftingRecipes, - PageWithPattern, - PageWithText, - PageWithTitle, -) - - -@dataclass(kw_only=True) -class TextPage(PageWithTitle, type="patchouli:text"): - text: FormatTree - - -@dataclass -class ImagePage(PageWithTitle, type="patchouli:image"): - images: list[ResourceLocation] - border: bool = False - - -@dataclass -class CraftingPage(PageWithCraftingRecipes, type="patchouli:crafting"): - recipe: CraftingRecipe - recipe2: CraftingRecipe | None = None - - @property - def recipes(self) -> list[CraftingRecipe]: - recipes = [self.recipe] - if self.recipe2: - recipes.append(self.recipe2) - return recipes - - -# TODO: this should probably inherit PageWithRecipes too -@dataclass -class SmeltingPage(PageWithTitle, type="patchouli:smelting"): - recipe: ItemStack - recipe2: ItemStack | None = None - - -@dataclass -class MultiblockPage(PageWithText, type="patchouli:multiblock"): - name: LocalizedStr - multiblock_id: ResourceLocation | None = None - # TODO: https://vazkiimods.github.io/Patchouli/docs/patchouli-basics/multiblocks/ - # this should be a dataclass, but hex doesn't have any multiblock pages so idc - multiblock: Any | None = None - enable_visualize: bool = True - - def __post_init__(self): - if self.multiblock_id is None and self.multiblock is None: - raise ValueError(f"One of multiblock_id or multiblock must be set\n{self}") - - -@dataclass -class EntityPage(PageWithText, type="patchouli:entity"): - entity: Entity - scale: float = 1 - offset: float = 0 - rotate: bool = True - default_rotation: float = -45 - name: LocalizedStr | None = None - - -@dataclass -class SpotlightPage(PageWithTitle, type="patchouli:spotlight"): - item: LocalizedItem # TODO: patchi says this is an ItemStack, so this might break - link_recipe: bool = False - - -@dataclass -class LinkPage(TextPage, type="patchouli:link"): - url: str - link_text: LocalizedStr - - -@dataclass(kw_only=True) -class RelationsPage(PageWithTitle, type="patchouli:relations"): - entries: list[ResourceLocation] - _title: LocalizedStr = field( - default=LocalizedStr("Related Chapters"), metadata=rename("title") - ) - - -@dataclass -class QuestPage(PageWithTitle, type="patchouli:quest"): - trigger: ResourceLocation | None = None - _title: LocalizedStr = field( - default=LocalizedStr("Objective"), metadata=rename("title") - ) - - -@dataclass -class EmptyPage(BasePage, type="patchouli:empty"): - draw_filler: bool = True - - -@dataclass -class PatternPage(PageWithPattern, type="hexcasting:pattern"): - patterns: list[RawPatternInfo] = field(init=False) - op_id: ResourceLocation - header: None - - def __post_init__(self): - self.patterns = [self.book.patterns[self.op_id]] - - @property - def name(self) -> LocalizedStr: - return self.i18n.localize_pattern(self.op_id) - - -@dataclass -class ManualPatternNosigPage(PageWithPattern, type="hexcasting:manual_pattern_nosig"): - header: LocalizedStr - op_id: None - input: None - output: None - - @property - def name(self) -> LocalizedStr: - return self.header - - -@dataclass -class ManualOpPatternPage(PageWithPattern, type="hexcasting:manual_pattern"): - op_id: ResourceLocation - header: None - - @property - def name(self) -> LocalizedStr: - return self.i18n.localize_pattern(self.op_id) - - -@dataclass -class ManualRawPatternPage(PageWithPattern, type="hexcasting:manual_pattern"): - op_id: None - header: LocalizedStr - - @property - def name(self) -> LocalizedStr: - return self.header - - -@dataclass -class CraftingMultiPage(PageWithCraftingRecipes, type="hexcasting:crafting_multi"): - heading: LocalizedStr # ...heading? - _recipes: list[CraftingRecipe] = field(metadata=rename("recipes")) - - @property - def recipes(self) -> list[CraftingRecipe]: - return self._recipes - - -@dataclass -class BrainsweepPage(PageWithText, type="hexcasting:brainsweep"): - recipe: BrainsweepRecipe - - -Page = ( - TextPage - | ImagePage - | CraftingPage - | SmeltingPage - | MultiblockPage - | EntityPage - | SpotlightPage - | LinkPage - | RelationsPage - | QuestPage - | EmptyPage - | PatternPage - | ManualPatternNosigPage - | ManualOpPatternPage - | ManualRawPatternPage - | CraftingMultiPage - | BrainsweepPage -) - - -def _raw_page_hook(data: dict[str, Any] | str) -> dict[str, Any]: - if isinstance(data, str): - # special case, thanks patchouli - return {"type": "patchouli:text", "text": data} - return data - - -def make_page_hooks(book: Book) -> TypeHooks: - """Creates type hooks for deserializing Page types.""" - - type_hooks: TypeHooks = {Page: _raw_page_hook} - - for cls_ in get_union_types(Page): - type_hooks[cls_] = cls_.make_type_hook(book) - - return type_hooks diff --git a/doc/src/patchouli/page/hexcasting.py b/doc/src/patchouli/page/hexcasting.py new file mode 100644 index 00000000..5e1446e9 --- /dev/null +++ b/doc/src/patchouli/page/hexcasting.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from dataclasses import dataclass, field + +from common.deserialize import rename +from common.pattern import RawPatternInfo +from common.types import LocalizedStr +from minecraft.recipe import BrainsweepRecipe, CraftingRecipe +from minecraft.resource import ResourceLocation + +from .abstract import PageWithCraftingRecipes, PageWithPattern, PageWithText + + +@dataclass +class PatternPage(PageWithPattern, type="hexcasting:pattern"): + patterns: list[RawPatternInfo] = field(init=False) + op_id: ResourceLocation + header: None + + def __post_init__(self): + self.patterns = [self.book.patterns[self.op_id]] + + @property + def name(self) -> LocalizedStr: + return self.i18n.localize_pattern(self.op_id) + + +@dataclass +class ManualPatternNosigPage(PageWithPattern, type="hexcasting:manual_pattern_nosig"): + header: LocalizedStr + op_id: None + input: None + output: None + + @property + def name(self) -> LocalizedStr: + return self.header + + +@dataclass +class ManualOpPatternPage(PageWithPattern, type="hexcasting:manual_pattern"): + op_id: ResourceLocation + header: None + + @property + def name(self) -> LocalizedStr: + return self.i18n.localize_pattern(self.op_id) + + +@dataclass +class ManualRawPatternPage(PageWithPattern, type="hexcasting:manual_pattern"): + op_id: None + header: LocalizedStr + + @property + def name(self) -> LocalizedStr: + return self.header + + +@dataclass +class CraftingMultiPage(PageWithCraftingRecipes, type="hexcasting:crafting_multi"): + heading: LocalizedStr # ...heading? + _recipes: list[CraftingRecipe] = field(metadata=rename("recipes")) + + @property + def recipes(self) -> list[CraftingRecipe]: + return self._recipes + + +@dataclass +class BrainsweepPage(PageWithText, type="hexcasting:brainsweep"): + recipe: BrainsweepRecipe diff --git a/doc/src/patchouli/page/patchouli.py b/doc/src/patchouli/page/patchouli.py new file mode 100644 index 00000000..7b52d042 --- /dev/null +++ b/doc/src/patchouli/page/patchouli.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any + +from common.deserialize import rename +from common.formatting import FormatTree +from common.types import LocalizedItem, LocalizedStr +from minecraft.recipe import CraftingRecipe +from minecraft.resource import Entity, ItemStack, ResourceLocation + +from .abstract import BasePage, PageWithCraftingRecipes, PageWithText, PageWithTitle + + +@dataclass(kw_only=True) +class TextPage(PageWithTitle, type="patchouli:text"): + text: FormatTree + + +@dataclass +class ImagePage(PageWithTitle, type="patchouli:image"): + images: list[ResourceLocation] + border: bool = False + + +@dataclass +class CraftingPage(PageWithCraftingRecipes, type="patchouli:crafting"): + recipe: CraftingRecipe + recipe2: CraftingRecipe | None = None + + @property + def recipes(self) -> list[CraftingRecipe]: + recipes = [self.recipe] + if self.recipe2: + recipes.append(self.recipe2) + return recipes + + +# TODO: this should probably inherit PageWithRecipes too +@dataclass +class SmeltingPage(PageWithTitle, type="patchouli:smelting"): + recipe: ItemStack + recipe2: ItemStack | None = None + + +@dataclass +class MultiblockPage(PageWithText, type="patchouli:multiblock"): + name: LocalizedStr + multiblock_id: ResourceLocation | None = None + # TODO: https://vazkiimods.github.io/Patchouli/docs/patchouli-basics/multiblocks/ + # this should be a dataclass, but hex doesn't have any multiblock pages so idc + multiblock: Any | None = None + enable_visualize: bool = True + + def __post_init__(self): + if self.multiblock_id is None and self.multiblock is None: + raise ValueError(f"One of multiblock_id or multiblock must be set\n{self}") + + +@dataclass +class EntityPage(PageWithText, type="patchouli:entity"): + entity: Entity + scale: float = 1 + offset: float = 0 + rotate: bool = True + default_rotation: float = -45 + name: LocalizedStr | None = None + + +@dataclass +class SpotlightPage(PageWithTitle, type="patchouli:spotlight"): + item: LocalizedItem # TODO: patchi says this is an ItemStack, so this might break + link_recipe: bool = False + + +@dataclass +class LinkPage(TextPage, type="patchouli:link"): + url: str + link_text: LocalizedStr + + +@dataclass(kw_only=True) +class RelationsPage(PageWithTitle, type="patchouli:relations"): + entries: list[ResourceLocation] + _title: LocalizedStr = field( + default=LocalizedStr("Related Chapters"), metadata=rename("title") + ) + + +@dataclass +class QuestPage(PageWithTitle, type="patchouli:quest"): + trigger: ResourceLocation | None = None + _title: LocalizedStr = field( + default=LocalizedStr("Objective"), metadata=rename("title") + ) + + +@dataclass +class EmptyPage(BasePage, type="patchouli:empty"): + draw_filler: bool = True