cubequest/questhelper/map/map_renderer.py
henriquenakashima d46d05490e Show treasures
2021-03-28 19:50:01 -04:00

175 lines
5.2 KiB
Python

import dataclasses
from map.quest_map import MapEncounter, QuestMap, EncounterType, QuestPath, BattleEncounter
from typing import List, Optional
from map.treasure_hoard import TreasureType, Treasure
_ENCOUNTER_ICONS = {
EncounterType.BATTLE_REGULAR: 'noun_Battle_3647629_cropped.png',
EncounterType.BATTLE_ELITE: 'noun_Shield_3806325_cropped.png',
EncounterType.BATTLE_BOSS: 'noun_dragon_3820621_cropped.png',
EncounterType.TOWN_TAVERN: 'noun_Beer_3054928_cropped.png',
EncounterType.TOWN_DOCKS: 'noun_Caravel_2421659_cropped.png',
EncounterType.TOWN_MARKET: 'noun_Store_786819_cropped.png',
EncounterType.TOWN_LIBRARY: 'noun_Library_3820590_cropped.png',
EncounterType.TOWN_WORKSHOP: 'noun_Carving Tools_693475_cropped.png',
EncounterType.TOWN_TOWN_HALL: 'noun_brussels town hall_3284349_cropped.png',
EncounterType.TOWN_TEMPLE: 'noun_temple_2382601_cropped.png',
EncounterType.TOWN_REST: 'noun_Tent_3814999_cropped.png',
EncounterType.TOWN_ROAD: 'noun_Hiking Trail_2686687_cropped.png',
}
_PATH_ICONS = {
QuestPath.FORWARD: 'arrow_down.png',
QuestPath.FORWARD_LEFT: 'arrow_down_left.png',
QuestPath.FORWARD_RIGHT: 'arrow_down_right.png',
}
_TREASURE_ICONS = {
TreasureType.GOLD: 'noun_coin_48892_cropped.png',
TreasureType.INK: 'noun_Ink_2509717_cropped.png',
TreasureType.COG: 'noun_cogs_442166_cropped.png',
TreasureType.DEED: 'noun_deed_3309438_cropped.png',
TreasureType.ART_OBJECT: 'noun_Art_3410337_cropped.png',
TreasureType.GEMS: 'noun_Diamond_3820185_cropped.png',
TreasureType.CONSUMABLE: 'noun_potion_34882_cropped.png',
TreasureType.NOBILITY_TITLE: 'noun_Crown_1936588_cropped.png',
TreasureType.LIFE: 'noun_life_3553107_cropped.png',
TreasureType.RELIC: 'noun_Ancient Egyptian religion_248077_cropped.png',
}
@dataclasses.dataclass(frozen=True)
class RenderedElement:
description: str
src: str
x: int
y: int
w: int
h: int
secondary: Optional['SecondaryRenderedElement'] = None
@dataclasses.dataclass(frozen=True)
class SecondaryRenderedElement(RenderedElement):
text: str = None
text_x: int = None
text_y: int = None
@dataclasses.dataclass(frozen=True)
class RenderedEncounter(RenderedElement):
pass
@dataclasses.dataclass(frozen=True)
class RenderedPath(RenderedElement):
pass
@dataclasses.dataclass(frozen=True)
class RenderedMap:
encounters: List[RenderedEncounter]
paths: List[RenderedPath]
def render_map(quest_map: QuestMap) -> RenderedMap:
rendered_encounters = []
row: List[MapEncounter]
for r, row in enumerate(quest_map.encounters):
for c, encounter in enumerate(row):
rendered_encounter = _render_encounter(r, c, encounter)
if rendered_encounter is not None:
rendered_encounters.append(rendered_encounter)
rendered_paths = []
row: List[QuestPath]
for r, row in enumerate(quest_map.paths):
for c, path in enumerate(row):
rendered_path = _render_path(r, c, path)
if rendered_path is not None:
rendered_paths.append(rendered_path)
return RenderedMap(rendered_encounters, rendered_paths)
_LEFT = 20
_TOP = 20
_ENCOUNTER_W = 40
_ENCOUNTER_H = 40
_ARROW_W = 40
_ARROW_H = 20
_PADDING_W = 15
_PADDING_V = 10
_ENCOUNTER_COLUMN_W = _ENCOUNTER_W + _PADDING_W
_PATH_COLUMN_W = _ARROW_W + _PADDING_W
_COLUMN_W = _ENCOUNTER_COLUMN_W + _PATH_COLUMN_W
_ROW_H = _ENCOUNTER_H + _PADDING_V + _ARROW_H + _PADDING_V
_SECONDARY_W = 24
_SECONDARY_H = 24
_SECONDARY_PADDING = 6
def _render_encounter(r: int, c: int, encounter: MapEncounter) -> Optional[RenderedEncounter]:
if encounter.type == EncounterType.INACCESSIBLE:
return None
icon = f'map/{_ENCOUNTER_ICONS[encounter.type]}'
x = _LEFT + _COLUMN_W * c
y = _TOP + _ROW_H * r
w = _ENCOUNTER_W
h = _ENCOUNTER_H
if isinstance(encounter, BattleEncounter):
x -= (_SECONDARY_PADDING + _SECONDARY_W) / 2
secondary = _render_battle_encounter_secondary_info(encounter, x, y, w, h)
else:
secondary = None
return RenderedEncounter(
description=encounter.description,
src=icon,
x=x,
y=y,
w=w,
h=h,
secondary=secondary
)
def _render_path(r: int, c: int, path: QuestPath) -> Optional[RenderedPath]:
if path == QuestPath.NO_PATH:
return None
icon = f'map/{_PATH_ICONS[path]}'
x = _LEFT + _ENCOUNTER_COLUMN_W * ((c + 1) // 2) + _PATH_COLUMN_W * (c // 2)
y = _TOP + (_ENCOUNTER_H + _PADDING_V) + _ROW_H * r
w = _ARROW_W
h = _ARROW_H
return RenderedPath(
description=path.description,
src=icon,
x=x,
y=y,
w=w,
h=h,
)
def _render_battle_encounter_secondary_info(encounter: BattleEncounter, x, y, w, h) -> Optional[RenderedElement]:
treasure: Treasure = encounter.treasure_hoard.revealed
return SecondaryRenderedElement(
description=encounter.treasure_hoard.description,
src = f'map/{_TREASURE_ICONS[treasure.treasure_type]}',
x=x + w + _SECONDARY_PADDING,
y=y,
w=_SECONDARY_W,
h=_SECONDARY_H,
secondary=None,
text=treasure.amount,
text_x=x + w + _SECONDARY_PADDING,
text_y=y + _SECONDARY_H
)