Generate treasure hoards
This commit is contained in:
parent
541991d404
commit
97782ab212
7 changed files with 132 additions and 9 deletions
|
|
@ -2,7 +2,8 @@ import random
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import map.quest_map
|
import map.quest_map
|
||||||
from map.quest_map import QuestMap, MapEncounter, EncounterType as ET, QuestPath as QP
|
import map.treasure_hoard_generator
|
||||||
|
from map.quest_map import QuestMap, MapEncounter, BattleEncounter, EncounterType as ET, QuestPath as QP
|
||||||
|
|
||||||
|
|
||||||
# 27 cards (21 are drawn)
|
# 27 cards (21 are drawn)
|
||||||
|
|
@ -26,9 +27,10 @@ def generate_encounter_inaccessible() -> MapEncounter:
|
||||||
return MapEncounter(ET.INACCESSIBLE)
|
return MapEncounter(ET.INACCESSIBLE)
|
||||||
|
|
||||||
|
|
||||||
def generate_encounter_battle(battle_type: ET):
|
def generate_encounter_battle(battle_type: ET, level: int):
|
||||||
assert battle_type in map.quest_map.ENCOUNTER_TYPES_BATTLE
|
assert battle_type in map.quest_map.ENCOUNTER_TYPES_BATTLE
|
||||||
return MapEncounter(battle_type)
|
hoard = map.treasure_hoard_generator.generate_hoard(battle_type, level)
|
||||||
|
return BattleEncounter(battle_type, hoard)
|
||||||
|
|
||||||
|
|
||||||
def generate_encounter_town(town_type: ET):
|
def generate_encounter_town(town_type: ET):
|
||||||
|
|
@ -36,7 +38,7 @@ def generate_encounter_town(town_type: ET):
|
||||||
return MapEncounter(town_type)
|
return MapEncounter(town_type)
|
||||||
|
|
||||||
|
|
||||||
def generate_map() -> QuestMap:
|
def generate_map(level: int) -> QuestMap:
|
||||||
quest_map = QuestMap()
|
quest_map = QuestMap()
|
||||||
|
|
||||||
# Generate encounters
|
# Generate encounters
|
||||||
|
|
@ -46,13 +48,13 @@ def generate_map() -> QuestMap:
|
||||||
row: List[MapEncounter] = []
|
row: List[MapEncounter] = []
|
||||||
if r == 10:
|
if r == 10:
|
||||||
row.append(generate_encounter_inaccessible())
|
row.append(generate_encounter_inaccessible())
|
||||||
row.append(generate_encounter_battle(ET.BATTLE_BOSS))
|
row.append(generate_encounter_battle(ET.BATTLE_BOSS, level))
|
||||||
row.append(generate_encounter_inaccessible())
|
row.append(generate_encounter_inaccessible())
|
||||||
elif r in (1, 4, 7):
|
elif r in (1, 4, 7):
|
||||||
elite_column = random.randrange(4) # 75% chance of elite
|
elite_column = random.randrange(4) # 75% chance of elite
|
||||||
for c in range(3):
|
for c in range(3):
|
||||||
battle_type = ET.BATTLE_ELITE if c == elite_column else ET.BATTLE_REGULAR
|
battle_type = ET.BATTLE_ELITE if c == elite_column else ET.BATTLE_REGULAR
|
||||||
row.append(generate_encounter_battle(battle_type))
|
row.append(generate_encounter_battle(battle_type, level))
|
||||||
else:
|
else:
|
||||||
for c in range(3):
|
for c in range(3):
|
||||||
town_type = next(town_deck_it)
|
town_type = next(town_deck_it)
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,12 @@ class RenderedElement:
|
||||||
h: int
|
h: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
class RenderedEncounter(RenderedElement):
|
class RenderedEncounter(RenderedElement):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
class RenderedPath(RenderedElement):
|
class RenderedPath(RenderedElement):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import enum
|
||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from map.treasure_hoard import TreasureHoard
|
||||||
|
|
||||||
|
|
||||||
class EncounterType(enum.Enum):
|
class EncounterType(enum.Enum):
|
||||||
INACCESSIBLE = 'inaccessible'
|
INACCESSIBLE = 'inaccessible'
|
||||||
|
|
@ -60,6 +62,15 @@ class MapEncounter:
|
||||||
return str(self.type)
|
return str(self.type)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class BattleEncounter(MapEncounter):
|
||||||
|
treasure_hoard: TreasureHoard
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return f'{self.type} [{self.treasure_hoard.description}]'
|
||||||
|
|
||||||
|
|
||||||
class QuestMap:
|
class QuestMap:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# List of rows of encounters, each row has 3 encounters.
|
# List of rows of encounters, each row has 3 encounters.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<head></head>
|
<head></head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Map</h1>
|
<h1>Map of {{level_ordinal}} quest</h1>
|
||||||
|
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
{% for e in map.encounters %}
|
{% for e in map.encounters %}
|
||||||
|
|
|
||||||
38
questhelper/map/treasure_hoard.py
Normal file
38
questhelper/map/treasure_hoard.py
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import dataclasses
|
||||||
|
import enum
|
||||||
|
|
||||||
|
|
||||||
|
class TreasureType(enum.Enum):
|
||||||
|
GOLD = 'gold'
|
||||||
|
INK = 'ink'
|
||||||
|
COG = 'cog'
|
||||||
|
DEED = 'deed'
|
||||||
|
GEMS = 'bag of gems'
|
||||||
|
ART_OBJECT = 'art object'
|
||||||
|
CONSUMABLE = 'consumable'
|
||||||
|
LIFE = 'life'
|
||||||
|
BLESSING = 'blessing'
|
||||||
|
NOBILITY_TITLE = 'nobility title'
|
||||||
|
RELIC = 'relic'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
|
class Treasure:
|
||||||
|
amount: int
|
||||||
|
treasure_type: TreasureType
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
word = str(self.treasure_type.value)
|
||||||
|
word = word.capitalize()
|
||||||
|
|
||||||
|
return f'{self.amount} {word}'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
|
class TreasureHoard:
|
||||||
|
revealed: Treasure
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return f'{self.revealed.description} + ?'
|
||||||
65
questhelper/map/treasure_hoard_generator.py
Normal file
65
questhelper/map/treasure_hoard_generator.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import random
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
from map.quest_map import EncounterType
|
||||||
|
from map.treasure_hoard import TreasureHoard, Treasure, TreasureType as TT
|
||||||
|
|
||||||
|
|
||||||
|
class HoardTable:
|
||||||
|
def __init__(self, input: List[Tuple[int, int, TT]]):
|
||||||
|
self.input = input
|
||||||
|
|
||||||
|
def roll(self) -> Treasure:
|
||||||
|
dice_total = random.randint(1, 6) + random.randint(1, 6)
|
||||||
|
for top_of_band, amount, treasure_type in self.input:
|
||||||
|
if dice_total <= top_of_band:
|
||||||
|
return Treasure(amount, treasure_type)
|
||||||
|
|
||||||
|
|
||||||
|
_TABLES = {
|
||||||
|
1: {
|
||||||
|
EncounterType.BATTLE_REGULAR: HoardTable([
|
||||||
|
(2, 200, TT.GOLD),
|
||||||
|
(4, 2, TT.COG),
|
||||||
|
(5, 1, TT.CONSUMABLE),
|
||||||
|
(6, 1, TT.DEED),
|
||||||
|
(8, 50, TT.GOLD),
|
||||||
|
(9, 1, TT.GEMS),
|
||||||
|
(11, 1, TT.ART_OBJECT),
|
||||||
|
(12, 1, TT.LIFE),
|
||||||
|
]),
|
||||||
|
EncounterType.BATTLE_ELITE: HoardTable([
|
||||||
|
(2, 300, TT.GOLD),
|
||||||
|
(4, 4, TT.COG),
|
||||||
|
(5, 2, TT.CONSUMABLE),
|
||||||
|
(6, 2, TT.DEED),
|
||||||
|
(8, 100, TT.GOLD),
|
||||||
|
(9, 2, TT.GEMS),
|
||||||
|
(10, 2, TT.ART_OBJECT),
|
||||||
|
(11, 1, TT.NOBILITY_TITLE),
|
||||||
|
(12, 1, TT.LIFE),
|
||||||
|
]),
|
||||||
|
EncounterType.BATTLE_BOSS: HoardTable([
|
||||||
|
(2, 400, TT.GOLD),
|
||||||
|
(4, 6, TT.COG),
|
||||||
|
(5, 3, TT.CONSUMABLE),
|
||||||
|
(6, 3, TT.DEED),
|
||||||
|
(8, 150, TT.GOLD),
|
||||||
|
(9, 3, TT.GEMS),
|
||||||
|
(11, 1, TT.NOBILITY_TITLE),
|
||||||
|
(12, 1, TT.RELIC),
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_hoard(battle_type: EncounterType, level: int):
|
||||||
|
return TreasureHoard(_roll_treasure(battle_type, level))
|
||||||
|
|
||||||
|
|
||||||
|
def _roll_treasure(battle_type: EncounterType, level: int):
|
||||||
|
# TODO: Handle more quest levels
|
||||||
|
assert level == 1
|
||||||
|
|
||||||
|
table: HoardTable = _TABLES[level][battle_type]
|
||||||
|
return table.roll()
|
||||||
|
|
@ -7,10 +7,15 @@ from map.map_renderer import RenderedMap
|
||||||
from map.quest_map import QuestMap
|
from map.quest_map import QuestMap
|
||||||
|
|
||||||
|
|
||||||
|
def _ordinal(level):
|
||||||
|
return {1: '1st', 2: '2nd', 3: '3rd', 4: '4th', 5: '5th'}[level]
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
quest_map: QuestMap = map_generator.generate_map()
|
level = 1
|
||||||
|
quest_map: QuestMap = map_generator.generate_map(level)
|
||||||
rendered_map: RenderedMap = map_renderer.render_map(quest_map)
|
rendered_map: RenderedMap = map_renderer.render_map(quest_map)
|
||||||
|
|
||||||
template = loader.get_template('map/index.html')
|
template = loader.get_template('map/index.html')
|
||||||
context = {'map': rendered_map}
|
context = {'level_ordinal': _ordinal(level), 'map': rendered_map}
|
||||||
return HttpResponse(template.render(context, request))
|
return HttpResponse(template.render(context, request))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue