Initial Commit
This commit is contained in:
commit
d25706163c
34 changed files with 1035 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.venv
|
||||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
10
.idea/dnd_dm_toolkit.iml
generated
Normal file
10
.idea/dnd_dm_toolkit.iml
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.13 (dnd_dm_toolkit)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.13 (dnd_dm_toolkit)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (dnd_dm_toolkit)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/dnd_dm_toolkit.iml" filepath="$PROJECT_DIR$/.idea/dnd_dm_toolkit.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
0
README.md
Normal file
0
README.md
Normal file
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
0
src/dnd_dm_toolkit/__init__.py
Normal file
0
src/dnd_dm_toolkit/__init__.py
Normal file
0
src/dnd_dm_toolkit/core/__init__.py
Normal file
0
src/dnd_dm_toolkit/core/__init__.py
Normal file
5
src/dnd_dm_toolkit/core/constants.py
Normal file
5
src/dnd_dm_toolkit/core/constants.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
from .sentinel import _AllStatsSentinel
|
||||||
|
|
||||||
|
ALL_STATS = _AllStatsSentinel()
|
||||||
|
|
||||||
|
MAXIMUM_STAT_VALUE = 30
|
||||||
0
src/dnd_dm_toolkit/core/enums/__init__.py
Normal file
0
src/dnd_dm_toolkit/core/enums/__init__.py
Normal file
244
src/dnd_dm_toolkit/core/enums/game.py
Normal file
244
src/dnd_dm_toolkit/core/enums/game.py
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
from enum import Enum, auto
|
||||||
|
|
||||||
|
class Ability(Enum):
|
||||||
|
"""
|
||||||
|
The six core ability scores in Dungeons & Dragons, which represent a character's
|
||||||
|
innate physical and mental capabilities. These abilities influence skill checks,
|
||||||
|
attack rolls, saving throws, and other mechanics.
|
||||||
|
|
||||||
|
- STRENGTH: Physical power, affects melee attacks and lifting/carrying capacity.
|
||||||
|
- DEXTERITY: Agility and reflexes, affects ranged attacks, AC, and initiative.
|
||||||
|
- CONSTITUTION: Endurance and health, affects hit points and resistance to fatigue/poison.
|
||||||
|
- INTELLIGENCE: Reasoning and memory, affects knowledge-based skills and spellcasting for some classes.
|
||||||
|
- WISDOM: Perception and insight, affects awareness, intuition, and some spellcasting.
|
||||||
|
- CHARISMA: Force of personality, affects social interactions and some spellcasting.
|
||||||
|
"""
|
||||||
|
STRENGTH = auto()
|
||||||
|
DEXTERITY = auto()
|
||||||
|
CONSTITUTION = auto()
|
||||||
|
INTELLIGENCE = auto()
|
||||||
|
WISDOM = auto()
|
||||||
|
CHARISMA = auto()
|
||||||
|
|
||||||
|
class Skill(Enum):
|
||||||
|
"""
|
||||||
|
List of character skills, each tied to a specific ability score.
|
||||||
|
Skills determine how well a character performs certain tasks and checks in the game.
|
||||||
|
|
||||||
|
- ACROBATICS (DEX): Balance, agility, and tumbling.
|
||||||
|
- ANIMAL_HANDLING (WIS): Calming or controlling animals.
|
||||||
|
- ARCANA (INT): Knowledge of magic, spells, and magical traditions.
|
||||||
|
- ATHLETICS (STR): Climbing, swimming, jumping, and other physical feats.
|
||||||
|
- DECEPTION (CHA): Lying, misleading, or bluffing.
|
||||||
|
- HISTORY (INT): Knowledge of historical events and civilizations.
|
||||||
|
- INSIGHT (WIS): Detecting motives, emotions, or hidden truths.
|
||||||
|
- INTIMIDATION (CHA): Coercing or threatening others.
|
||||||
|
- INVESTIGATION (INT): Searching for clues, analyzing evidence.
|
||||||
|
- MEDICINE (WIS): Treating wounds, diagnosing illness.
|
||||||
|
- NATURE (INT): Knowledge of plants, animals, terrain, and weather.
|
||||||
|
- PERCEPTION (WIS): Spotting, hearing, or noticing hidden things.
|
||||||
|
- PERFORMANCE (CHA): Acting, singing, dancing, or entertaining.
|
||||||
|
- PERSUASION (CHA): Convincing others through charm or negotiation.
|
||||||
|
- RELIGION (INT): Knowledge of deities, rituals, and sacred traditions.
|
||||||
|
- SLEIGHT_OF_HAND (DEX): Pickpocketing, trickery with hands.
|
||||||
|
- STEALTH (DEX): Moving silently or hiding.
|
||||||
|
- SURVIVAL (WIS): Tracking, foraging, and surviving in the wild.
|
||||||
|
"""
|
||||||
|
ACROBATICS = auto()
|
||||||
|
ANIMAL_HANDLING = auto()
|
||||||
|
ARCANA = auto()
|
||||||
|
ATHLETICS = auto()
|
||||||
|
DECEPTION = auto()
|
||||||
|
HISTORY = auto()
|
||||||
|
INSIGHT = auto()
|
||||||
|
INTIMIDATION = auto()
|
||||||
|
INVESTIGATION = auto()
|
||||||
|
MEDICINE = auto()
|
||||||
|
NATURE = auto()
|
||||||
|
PERCEPTION = auto()
|
||||||
|
PERFORMANCE = auto()
|
||||||
|
PERSUASION = auto()
|
||||||
|
RELIGION = auto()
|
||||||
|
SLEIGHT_OF_HAND = auto()
|
||||||
|
STEALTH = auto()
|
||||||
|
SURVIVAL = auto()
|
||||||
|
|
||||||
|
class CheckDifficulty(Enum):
|
||||||
|
"""
|
||||||
|
Difficulty class (DC) levels for ability and skill checks.
|
||||||
|
These represent the target number a player must meet or exceed on a d20 roll
|
||||||
|
to succeed at a task.
|
||||||
|
|
||||||
|
- VERY_EASY: DC 5
|
||||||
|
- EASY: DC 10
|
||||||
|
- MEDIUM: DC 15
|
||||||
|
- HARD: DC 20
|
||||||
|
- VERY_HARD: DC 25
|
||||||
|
- NEARLY_IMPOSSIBLE: DC 30
|
||||||
|
"""
|
||||||
|
VERY_EASY = 5
|
||||||
|
EASY = 10
|
||||||
|
MEDIUM = 15
|
||||||
|
HARD = 20
|
||||||
|
VERY_HARD = 25
|
||||||
|
NEARLY_IMPOSSIBLE = 30
|
||||||
|
|
||||||
|
class TravelPace(Enum):
|
||||||
|
"""
|
||||||
|
Standard travel paces for overland movement, given in feet per hour, feet per day,
|
||||||
|
and maximum hours per day. Adjusted for character movement and terrain.
|
||||||
|
|
||||||
|
- FAST: Covers more distance but reduces perception and may cause exhaustion.
|
||||||
|
- NORMAL: Default pace for most travel.
|
||||||
|
- SLOW: Moves cautiously, allowing better perception and reduced fatigue.
|
||||||
|
"""
|
||||||
|
FAST = (400, 4000, 30000) # (feet per minute, feet per hour, feet per day)
|
||||||
|
NORMAL = (300, 3000, 24000)
|
||||||
|
SLOW = (200, 2000, 18000)
|
||||||
|
|
||||||
|
class CreatureSize(Enum):
|
||||||
|
"""
|
||||||
|
Represents creature sizes and their occupied space on the battle grid.
|
||||||
|
|
||||||
|
Each value is a tuple of (side length in feet, grid cells).
|
||||||
|
|
||||||
|
- TINY: 2.5 ft, 0 cells (½ × ½ grid)
|
||||||
|
- SMALL: 5 ft, 1 cell (1 × 1 grid)
|
||||||
|
- MEDIUM: 5 ft, 1 cell (1 × 1 grid)
|
||||||
|
- LARGE: 10 ft, 2 cells (2 × 2 grid)
|
||||||
|
- HUGE: 15 ft, 3 cells (3 × 3 grid)
|
||||||
|
- GARGANTUAN: 20 ft, 4 cells (4 × 4 grid)
|
||||||
|
"""
|
||||||
|
TINY = (2.5, 0.25)
|
||||||
|
SMALL = (5, 1)
|
||||||
|
MEDIUM = (5, 1)
|
||||||
|
LARGE = (10, 2)
|
||||||
|
HUGE = (15, 3)
|
||||||
|
GARGANTUAN = (20, 4)
|
||||||
|
|
||||||
|
class CoverType(Enum):
|
||||||
|
"""
|
||||||
|
Types of cover in combat, affecting how hard it is to hit a creature.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
NO_COVER: Creature is fully exposed, no protection.
|
||||||
|
HALF: Creature has partial cover, some protection from attacks.
|
||||||
|
THREE_QUARTERS: Creature is mostly behind cover, harder to hit.
|
||||||
|
TOTAL: Creature is completely behind cover, nearly immune to attacks.
|
||||||
|
"""
|
||||||
|
NO_COVER = None
|
||||||
|
HALF = auto()
|
||||||
|
THREE_QUARTERS = auto()
|
||||||
|
TOTAL = auto()
|
||||||
|
|
||||||
|
class AlignmentLC(Enum):
|
||||||
|
"""
|
||||||
|
Law–Chaos alignment axis.
|
||||||
|
|
||||||
|
This axis describes a character’s attitude toward order, tradition,
|
||||||
|
authority, and structure versus freedom, individuality, and spontaneity.
|
||||||
|
|
||||||
|
- LAWFUL: Values order, rules, hierarchy, and tradition.
|
||||||
|
- VERITABLE: Neutral position on the Law–Chaos axis. The character does not
|
||||||
|
strongly favor either structure or anarchy. The term is intentionally
|
||||||
|
chosen to emphasize balance and internal consistency rather than indifference.
|
||||||
|
- CHAOTIC: Values personal freedom, adaptability, and rejection of imposed order.
|
||||||
|
"""
|
||||||
|
LAWFUL = auto()
|
||||||
|
VERITABLE = auto()
|
||||||
|
CHAOTIC = auto()
|
||||||
|
|
||||||
|
class AlignmentGE(Enum):
|
||||||
|
"""
|
||||||
|
Good–Evil alignment axis.
|
||||||
|
|
||||||
|
This axis represents a character’s moral outlook and concern for the
|
||||||
|
well-being of others.
|
||||||
|
|
||||||
|
- GOOD: Acts with altruism, compassion, and respect for life.
|
||||||
|
- NEUTRAL: Lacks strong moral commitment toward good or evil.
|
||||||
|
- EVIL: Acts with cruelty, selfishness, or disregard for others.
|
||||||
|
"""
|
||||||
|
GOOD = auto()
|
||||||
|
NEUTRAL = auto()
|
||||||
|
EVIL = auto()
|
||||||
|
|
||||||
|
class LanguageScript(Enum):
|
||||||
|
"""Writing systems used by languages."""
|
||||||
|
NONE = auto() # Language hasn't writing system
|
||||||
|
COMMON = auto()
|
||||||
|
DWARVISH = auto()
|
||||||
|
ELVISH = auto()
|
||||||
|
INFERNAL = auto()
|
||||||
|
DRACONIC = auto()
|
||||||
|
CELESTIAL = auto()
|
||||||
|
|
||||||
|
class LanguageRarity(Enum):
|
||||||
|
"""How common a language is."""
|
||||||
|
STANDARD = auto()
|
||||||
|
EXOTIC = auto()
|
||||||
|
SECRET = auto()
|
||||||
|
|
||||||
|
class CreatureType(Enum):
|
||||||
|
HUMANOID = auto()
|
||||||
|
|
||||||
|
class FeatureSource(Enum):
|
||||||
|
RACE = auto()
|
||||||
|
CLASS = auto()
|
||||||
|
SPELL = auto()
|
||||||
|
ITEM = auto()
|
||||||
|
|
||||||
|
class LightCoverage(Enum):
|
||||||
|
FULL = auto()
|
||||||
|
DIM = auto()
|
||||||
|
DARK = auto()
|
||||||
|
|
||||||
|
class EventType(Enum):
|
||||||
|
"""All possible game events that can be hooked."""
|
||||||
|
|
||||||
|
# Attack Events
|
||||||
|
BEFORE_ATTACK = auto()
|
||||||
|
AFTER_ATTACK = auto()
|
||||||
|
ATTACK_HIT = auto()
|
||||||
|
ATTACK_MISS = auto()
|
||||||
|
CRITICAL_HIT = auto()
|
||||||
|
|
||||||
|
# Damage Events
|
||||||
|
BEFORE_DAMAGE = auto()
|
||||||
|
AFTER_DAMAGE = auto()
|
||||||
|
DAMAGE_TAKEN = auto()
|
||||||
|
DAMAGE_DEALT = auto()
|
||||||
|
|
||||||
|
# Saving Throw Events
|
||||||
|
BEFORE_SAVING_THROW = auto()
|
||||||
|
AFTER_SAVING_THROW = auto()
|
||||||
|
SAVING_THROW_SUCCESS = auto()
|
||||||
|
SAVING_THROW_FAILURE = auto()
|
||||||
|
|
||||||
|
# Ability Check Events
|
||||||
|
BEFORE_ABILITY_CHECK = auto()
|
||||||
|
AFTER_ABILITY_CHECK = auto()
|
||||||
|
|
||||||
|
# Skill Check Events
|
||||||
|
BEFORE_SKILL_CHECK = auto()
|
||||||
|
AFTER_SKILL_CHECK = auto()
|
||||||
|
|
||||||
|
# Movement Events
|
||||||
|
BEFORE_MOVE = auto()
|
||||||
|
AFTER_MOVE = auto()
|
||||||
|
|
||||||
|
# HP Events
|
||||||
|
HP_GAINED = auto()
|
||||||
|
HP_LOST = auto()
|
||||||
|
HP_ZERO = auto()
|
||||||
|
|
||||||
|
# Turn Events
|
||||||
|
TURN_START = auto()
|
||||||
|
TURN_END = auto()
|
||||||
|
|
||||||
|
# Rest Events
|
||||||
|
SHORT_REST = auto()
|
||||||
|
LONG_REST = auto()
|
||||||
|
|
||||||
|
# Level Events
|
||||||
|
LEVEL_UP = auto()
|
||||||
0
src/dnd_dm_toolkit/core/event_bus.py
Normal file
0
src/dnd_dm_toolkit/core/event_bus.py
Normal file
0
src/dnd_dm_toolkit/core/hooks.py
Normal file
0
src/dnd_dm_toolkit/core/hooks.py
Normal file
17
src/dnd_dm_toolkit/core/sentinel.py
Normal file
17
src/dnd_dm_toolkit/core/sentinel.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
class _AllStatsSentinel:
|
||||||
|
"""Sentinel для обозначения 'любые навыки'"""
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super().__new__(cls)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "ALL_STATS"
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return isinstance(other, _AllStatsSentinel)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash("ALL_STATS")
|
||||||
0
src/dnd_dm_toolkit/core/stuctures/__init__.py
Normal file
0
src/dnd_dm_toolkit/core/stuctures/__init__.py
Normal file
204
src/dnd_dm_toolkit/core/stuctures/character.py
Normal file
204
src/dnd_dm_toolkit/core/stuctures/character.py
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
import warnings
|
||||||
|
from typing import List, Callable, Protocol, Any
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from ..enums.game import (
|
||||||
|
Ability,
|
||||||
|
Skill,
|
||||||
|
AlignmentLC,
|
||||||
|
AlignmentGE,
|
||||||
|
CreatureSize,
|
||||||
|
LanguageScript,
|
||||||
|
LanguageRarity,
|
||||||
|
CreatureType
|
||||||
|
)
|
||||||
|
from ..sentinel import _AllStatsSentinel
|
||||||
|
from ..constants import MAXIMUM_STAT_VALUE
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Attributes:
|
||||||
|
"""
|
||||||
|
Represents the six core ability scores of a character.
|
||||||
|
|
||||||
|
Ability scores define a character’s physical and mental capabilities
|
||||||
|
and serve as the foundation for most game mechanics, including skill
|
||||||
|
checks, saving throws, attacks, and spellcasting.
|
||||||
|
|
||||||
|
Each value is typically in the range 1–30.
|
||||||
|
"""
|
||||||
|
strength: int
|
||||||
|
dexterity: int
|
||||||
|
constitution: int
|
||||||
|
intelligence: int
|
||||||
|
wisdom: int
|
||||||
|
charisma: int
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
"""Validate class definition."""
|
||||||
|
for name, value in self.__dict__.items():
|
||||||
|
if getattr(self, name) > MAXIMUM_STAT_VALUE:
|
||||||
|
warnings.warn("Current rules configuration does not support "
|
||||||
|
f"creating character with stat's higher that {MAXIMUM_STAT_VALUE}, "
|
||||||
|
f"{name} attribute set to {MAXIMUM_STAT_VALUE}",
|
||||||
|
UserWarning)
|
||||||
|
setattr(self, name, MAXIMUM_STAT_VALUE)
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CharacterClass:
|
||||||
|
"""
|
||||||
|
Describes a character’s class.
|
||||||
|
|
||||||
|
A character class defines the character’s role, core mechanics, and
|
||||||
|
progression framework, including hit dice, proficiencies, and
|
||||||
|
recommended abilities.
|
||||||
|
|
||||||
|
This object is intended to be immutable and reusable, allowing both
|
||||||
|
SRD and homebrew classes to be defined as constants.
|
||||||
|
"""
|
||||||
|
name: str
|
||||||
|
hit_die: int
|
||||||
|
description: str
|
||||||
|
skill_choices: int
|
||||||
|
saving_throws: List[Ability]
|
||||||
|
abilities: List[Ability] | _AllStatsSentinel | None = None
|
||||||
|
skills: List[Skill] | _AllStatsSentinel | None = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
"""Validate class definition."""
|
||||||
|
if self.skills is None:
|
||||||
|
raise ValueError(f"{self.name}: 'skills' must be specified explicitly")
|
||||||
|
|
||||||
|
if self.abilities is None:
|
||||||
|
object.__setattr__(self, 'abilities', [])
|
||||||
|
|
||||||
|
if self.hit_die not in {6, 8, 10, 12}:
|
||||||
|
raise ValueError(
|
||||||
|
f"{self.name}: hit_die must be 6, 8, 10, or 12 (got {self.hit_die})"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.skill_choices < 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"{self.name}: skill_choices cannot be negative"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.skill_choices > 10:
|
||||||
|
warnings.warn(
|
||||||
|
f"{self.name}: skill_choices={self.skill_choices} is unusually high. "
|
||||||
|
f"Are you sure this is intended?",
|
||||||
|
UserWarning
|
||||||
|
)
|
||||||
|
|
||||||
|
if not isinstance(self.skills, _AllStatsSentinel):
|
||||||
|
if len(self.skills) < self.skill_choices:
|
||||||
|
raise ValueError(
|
||||||
|
f"{self.name}: skill_choices={self.skill_choices} but only "
|
||||||
|
f"{len(self.skills)} skills available"
|
||||||
|
)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Alignment:
|
||||||
|
"""
|
||||||
|
Represents a character’s alignment using two independent axes:
|
||||||
|
Law–Chaos and Good–Evil.
|
||||||
|
|
||||||
|
This structure follows the standard Dungeons & Dragons alignment model
|
||||||
|
as defined in the SRD, without extensions or intermediate states.
|
||||||
|
"""
|
||||||
|
lc_axis: AlignmentLC
|
||||||
|
ge_axis: AlignmentGE
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ActionDefinition:
|
||||||
|
name: str
|
||||||
|
related_abilities: list[Ability]
|
||||||
|
related_skills: list[Skill]
|
||||||
|
description: str
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CharacterItem:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CharacterToolKits(CharacterItem):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Featureable(Protocol):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Feature:
|
||||||
|
"""Abstract mechanic/feature"""
|
||||||
|
name: str
|
||||||
|
source: Featureable
|
||||||
|
description: str
|
||||||
|
stackable: bool
|
||||||
|
apply: Callable[[Featureable], None] | None = None # If None = Feature not mechanical
|
||||||
|
condition: Callable[[Featureable], bool] | None = None # If None = Feature always works
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if not self.apply:
|
||||||
|
if self.condition:
|
||||||
|
warnings.warn("Non-mechanical features cannot have the condition "
|
||||||
|
"`self.condition` field rewritten to `None`",
|
||||||
|
UserWarning)
|
||||||
|
self.condition = None
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Background:
|
||||||
|
"""
|
||||||
|
Represents a character background.
|
||||||
|
|
||||||
|
A background describes a character’s life before adventuring and
|
||||||
|
provides narrative context, recommended abilities, proficiencies,
|
||||||
|
and special features.
|
||||||
|
|
||||||
|
The behavioral logic of a background (such as granting proficiencies
|
||||||
|
or feats) is intended to be applied externally via a callable.
|
||||||
|
"""
|
||||||
|
name: str
|
||||||
|
recommended_abilities: List[Ability]
|
||||||
|
background_specs: List[Feature]
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Language:
|
||||||
|
"""
|
||||||
|
Represents a language in the game world.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: Language name (e.g., "Common", "Elvish").
|
||||||
|
script: Writing system used, or None for spoken-only languages.
|
||||||
|
rarity: How common the language is (standard/exotic/secret).
|
||||||
|
description: Brief description of the language.
|
||||||
|
"""
|
||||||
|
name: str
|
||||||
|
script: LanguageScript
|
||||||
|
rarity: LanguageRarity
|
||||||
|
description: str = ""
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Race:
|
||||||
|
"""
|
||||||
|
Defines a playable race with its inherent traits and abilities.
|
||||||
|
|
||||||
|
A race represents a character's species and provides ability score
|
||||||
|
increases, size, speed, languages, and special features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name: Race name (e.g., "Hill Dwarf", "High Elf").
|
||||||
|
ability_score_increases: Mapping of abilities to their bonuses.
|
||||||
|
size: The race's size category.
|
||||||
|
speed: Base walking speed in feet.
|
||||||
|
languages: Languages the race knows automatically.
|
||||||
|
features: List of racial features (darkvision, resistances, etc.).
|
||||||
|
age_description: Optional description of the race's lifespan.
|
||||||
|
alignment_tendency: Optional typical alignment for the race.
|
||||||
|
"""
|
||||||
|
name: str
|
||||||
|
ability_score_increases: dict[Ability, int]
|
||||||
|
size: CreatureSize
|
||||||
|
speed: int
|
||||||
|
type: CreatureType
|
||||||
|
languages: list[Language]
|
||||||
|
features: list[Feature]
|
||||||
|
age_description: str = ""
|
||||||
|
alignment_tendency: str = ""
|
||||||
|
extra_language_choices: int = 0
|
||||||
72
src/dnd_dm_toolkit/core/stuctures/event.py
Normal file
72
src/dnd_dm_toolkit/core/stuctures/event.py
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
from typing import Dict, Any
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from ..enums.game import (
|
||||||
|
EventType,
|
||||||
|
Ability,
|
||||||
|
Skill
|
||||||
|
)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GameEvent:
|
||||||
|
"""
|
||||||
|
Base class for all game events.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
event_type: The type of event.
|
||||||
|
source: Entity that triggered the event (e.g., Character, Monster).
|
||||||
|
target: Entity affected by the event (optional).
|
||||||
|
data: Additional event-specific data.
|
||||||
|
cancelled: If True, the event is cancelled and won't proceed.
|
||||||
|
"""
|
||||||
|
event_type: EventType
|
||||||
|
source: Any
|
||||||
|
target: Any | None = None
|
||||||
|
data: Dict[str, Any] | None = None
|
||||||
|
cancelled: bool = False
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
"""Cancel this event."""
|
||||||
|
self.cancelled = True
|
||||||
|
|
||||||
|
# Specific Event Types
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AttackEvent(GameEvent):
|
||||||
|
"""Event for attacks."""
|
||||||
|
attacker: Any = None
|
||||||
|
defender: Any = None
|
||||||
|
weapon: Any | None = None
|
||||||
|
attack_roll: int | None = None
|
||||||
|
is_hit: bool = False
|
||||||
|
is_critical: bool = False
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DamageEvent(GameEvent):
|
||||||
|
"""Event for damage."""
|
||||||
|
amount: int = 0
|
||||||
|
damage_type: str = "untyped"
|
||||||
|
is_critical: bool = False
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SavingThrowEvent(GameEvent):
|
||||||
|
"""Event for saving throws."""
|
||||||
|
ability: Ability = None
|
||||||
|
dc: int = 10
|
||||||
|
roll_result: int | None = None
|
||||||
|
is_success: bool = False
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AbilityCheckEvent(GameEvent):
|
||||||
|
"""Event for ability checks."""
|
||||||
|
ability: Ability = None
|
||||||
|
dc: int = 10
|
||||||
|
roll_result: int | None = None
|
||||||
|
is_success: bool = False
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SkillCheckEvent(GameEvent):
|
||||||
|
"""Event for skill checks."""
|
||||||
|
skill: Skill = None
|
||||||
|
dc: int = 10
|
||||||
|
roll_result: int | None = None
|
||||||
|
is_success: bool = False
|
||||||
0
src/dnd_dm_toolkit/data/__init__.py
Normal file
0
src/dnd_dm_toolkit/data/__init__.py
Normal file
45
src/dnd_dm_toolkit/data/stats.py
Normal file
45
src/dnd_dm_toolkit/data/stats.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
from ..core.enums.game import Ability, Skill
|
||||||
|
|
||||||
|
skill_to_ability = {
|
||||||
|
Skill.ACROBATICS: Ability.DEXTERITY,
|
||||||
|
Skill.ANIMAL_HANDLING: Ability.WISDOM,
|
||||||
|
Skill.ARCANA: Ability.INTELLIGENCE,
|
||||||
|
Skill.ATHLETICS: Ability.STRENGTH,
|
||||||
|
Skill.DECEPTION: Ability.CHARISMA,
|
||||||
|
Skill.HISTORY: Ability.INTELLIGENCE,
|
||||||
|
Skill.INSIGHT: Ability.WISDOM,
|
||||||
|
Skill.INTIMIDATION: Ability.CHARISMA,
|
||||||
|
Skill.INVESTIGATION: Ability.INTELLIGENCE,
|
||||||
|
Skill.MEDICINE: Ability.WISDOM,
|
||||||
|
Skill.NATURE: Ability.INTELLIGENCE,
|
||||||
|
Skill.PERCEPTION: Ability.WISDOM,
|
||||||
|
Skill.PERFORMANCE: Ability.CHARISMA,
|
||||||
|
Skill.PERSUASION: Ability.CHARISMA,
|
||||||
|
Skill.RELIGION: Ability.INTELLIGENCE,
|
||||||
|
Skill.SLEIGHT_OF_HAND: Ability.DEXTERITY,
|
||||||
|
Skill.STEALTH: Ability.DEXTERITY,
|
||||||
|
Skill.SURVIVAL: Ability.WISDOM,
|
||||||
|
}
|
||||||
|
|
||||||
|
proficiency_bonus_by_level = {
|
||||||
|
1: 2,
|
||||||
|
2: 2,
|
||||||
|
3: 2,
|
||||||
|
4: 2,
|
||||||
|
5: 3,
|
||||||
|
6: 3,
|
||||||
|
7: 3,
|
||||||
|
8: 3,
|
||||||
|
9: 4,
|
||||||
|
10: 4,
|
||||||
|
11: 4,
|
||||||
|
12: 4,
|
||||||
|
13: 5,
|
||||||
|
14: 5,
|
||||||
|
15: 5,
|
||||||
|
16: 5,
|
||||||
|
17: 6,
|
||||||
|
18: 6,
|
||||||
|
19: 6,
|
||||||
|
20: 6
|
||||||
|
}
|
||||||
0
src/dnd_dm_toolkit/entities/__init__.py
Normal file
0
src/dnd_dm_toolkit/entities/__init__.py
Normal file
152
src/dnd_dm_toolkit/entities/classes.py
Normal file
152
src/dnd_dm_toolkit/entities/classes.py
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
from ..core.constants import ALL_STATS
|
||||||
|
from ..core.enums.game import Ability, Skill
|
||||||
|
from ..core.stuctures.character import CharacterClass
|
||||||
|
|
||||||
|
BARBARIAN = CharacterClass(
|
||||||
|
name="Barbarian",
|
||||||
|
hit_die=12,
|
||||||
|
description="Savage warriors of unbridled fury.",
|
||||||
|
saving_throws=[Ability.STRENGTH, Ability.CONSTITUTION],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ANIMAL_HANDLING, Skill.ATHLETICS, Skill.INTIMIDATION,
|
||||||
|
Skill.NATURE, Skill.PERCEPTION, Skill.SURVIVAL
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
BARD = CharacterClass(
|
||||||
|
name="Bard",
|
||||||
|
hit_die=8,
|
||||||
|
description="Inspirational performers whose music weaves magic.",
|
||||||
|
saving_throws=[Ability.DEXTERITY, Ability.CHARISMA],
|
||||||
|
skill_choices=3,
|
||||||
|
skills=ALL_STATS
|
||||||
|
)
|
||||||
|
|
||||||
|
CLERIC = CharacterClass(
|
||||||
|
name="Cleric",
|
||||||
|
hit_die=8,
|
||||||
|
description="Divine agents wielding holy power to heal or harm.",
|
||||||
|
saving_throws=[Ability.WISDOM, Ability.CHARISMA],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.HISTORY, Skill.INSIGHT, Skill.MEDICINE,
|
||||||
|
Skill.PERSUASION, Skill.RELIGION
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
DRUID = CharacterClass(
|
||||||
|
name="Druid",
|
||||||
|
hit_die=8,
|
||||||
|
description="Nature speakers who command elemental forces.",
|
||||||
|
saving_throws=[Ability.INTELLIGENCE, Ability.WISDOM],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ARCANA, Skill.INSIGHT, Skill.MEDICINE,
|
||||||
|
Skill.NATURE, Skill.PERCEPTION, Skill.RELIGION,
|
||||||
|
Skill.SURVIVAL
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
FIGHTER = CharacterClass(
|
||||||
|
name="Fighter",
|
||||||
|
hit_die=10,
|
||||||
|
description="Versatile warriors trained in weapons and armor.",
|
||||||
|
saving_throws=[Ability.STRENGTH, Ability.CONSTITUTION],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ACROBATICS, Skill.ANIMAL_HANDLING, Skill.ATHLETICS,
|
||||||
|
Skill.HISTORY, Skill.INSIGHT, Skill.INTIMIDATION,
|
||||||
|
Skill.PERCEPTION, Skill.SURVIVAL
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
MONK = CharacterClass(
|
||||||
|
name="Monk",
|
||||||
|
hit_die=8,
|
||||||
|
description="Masters of martial arts and mystic ki.",
|
||||||
|
saving_throws=[Ability.STRENGTH, Ability.DEXTERITY],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ACROBATICS, Skill.ATHLETICS, Skill.HISTORY,
|
||||||
|
Skill.INSIGHT, Skill.RELIGION, Skill.STEALTH
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
PALADIN = CharacterClass(
|
||||||
|
name="Paladin",
|
||||||
|
hit_die=10,
|
||||||
|
description="Holy knights who smite foes and protect allies.",
|
||||||
|
saving_throws=[Ability.WISDOM, Ability.CHARISMA],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ATHLETICS, Skill.INSIGHT, Skill.INTIMIDATION,
|
||||||
|
Skill.MEDICINE, Skill.PERSUASION, Skill.RELIGION
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ranger
|
||||||
|
RANGER = CharacterClass(
|
||||||
|
name="Ranger",
|
||||||
|
hit_die=10,
|
||||||
|
description="Skilled hunters and wilderness warriors.",
|
||||||
|
saving_throws=[Ability.STRENGTH, Ability.DEXTERITY],
|
||||||
|
skill_choices=3,
|
||||||
|
skills=[
|
||||||
|
Skill.ANIMAL_HANDLING, Skill.ATHLETICS, Skill.INSIGHT,
|
||||||
|
Skill.INVESTIGATION, Skill.NATURE, Skill.PERCEPTION,
|
||||||
|
Skill.STEALTH, Skill.SURVIVAL
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
ROGUE = CharacterClass(
|
||||||
|
name="Rogue",
|
||||||
|
hit_die=8,
|
||||||
|
description="Stealthy opportunists and skill experts.",
|
||||||
|
saving_throws=[Ability.DEXTERITY, Ability.INTELLIGENCE],
|
||||||
|
skill_choices=4,
|
||||||
|
skills=[
|
||||||
|
Skill.ACROBATICS, Skill.ATHLETICS, Skill.DECEPTION,
|
||||||
|
Skill.INSIGHT, Skill.INTIMIDATION, Skill.INVESTIGATION,
|
||||||
|
Skill.PERCEPTION, Skill.PERFORMANCE,
|
||||||
|
Skill.PERSUASION, Skill.SLEIGHT_OF_HAND,
|
||||||
|
Skill.STEALTH, Skill.SURVIVAL
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
SORCERER = CharacterClass(
|
||||||
|
name="Sorcerer",
|
||||||
|
hit_die=6,
|
||||||
|
description="Innate spellcasters fueled by raw power.",
|
||||||
|
saving_throws=[Ability.CONSTITUTION, Ability.CHARISMA],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ARCANA, Skill.DECEPTION, Skill.INSIGHT,
|
||||||
|
Skill.INTIMIDATION, Skill.PERSUASION, Skill.RELIGION
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
WARLOCK = CharacterClass(
|
||||||
|
name="Warlock",
|
||||||
|
hit_die=8,
|
||||||
|
description="Pact-bound wielders of forbidden power.",
|
||||||
|
saving_throws=[Ability.WISDOM, Ability.CHARISMA],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ARCANA, Skill.DECEPTION, Skill.HISTORY,
|
||||||
|
Skill.INSIGHT, Skill.INTIMIDATION, Skill.INVESTIGATION,
|
||||||
|
Skill.PERCEPTION, Skill.RELIGION
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
WIZARD = CharacterClass(
|
||||||
|
name="Wizard",
|
||||||
|
hit_die=6,
|
||||||
|
description="Scholars of the arcane arts.",
|
||||||
|
saving_throws=[Ability.INTELLIGENCE, Ability.WISDOM],
|
||||||
|
skill_choices=2,
|
||||||
|
skills=[
|
||||||
|
Skill.ARCANA, Skill.HISTORY, Skill.INSIGHT,
|
||||||
|
Skill.INVESTIGATION, Skill.MEDICINE, Skill.RELIGION
|
||||||
|
],
|
||||||
|
)
|
||||||
0
src/dnd_dm_toolkit/entities/featues/__init__.py
Normal file
0
src/dnd_dm_toolkit/entities/featues/__init__.py
Normal file
4
src/dnd_dm_toolkit/entities/featues/func.py
Normal file
4
src/dnd_dm_toolkit/entities/featues/func.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
from ...core.stuctures.character import Featureable
|
||||||
|
|
||||||
|
def darkvision(entity: Featureable):
|
||||||
|
pass
|
||||||
6
src/dnd_dm_toolkit/entities/featues/objects.py
Normal file
6
src/dnd_dm_toolkit/entities/featues/objects.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from ...core.enums.game import FeatureSource
|
||||||
|
from ...core.stuctures.character import Feature
|
||||||
|
|
||||||
|
DARKVISION = Feature(
|
||||||
|
|
||||||
|
)
|
||||||
160
src/dnd_dm_toolkit/entities/languages.py
Normal file
160
src/dnd_dm_toolkit/entities/languages.py
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
from ..core.stuctures.character import (
|
||||||
|
LanguageScript,
|
||||||
|
LanguageRarity,
|
||||||
|
Language
|
||||||
|
)
|
||||||
|
|
||||||
|
# === STANDARD LANGUAGES ===
|
||||||
|
|
||||||
|
COMMON = Language(
|
||||||
|
name="Common",
|
||||||
|
script=LanguageScript.COMMON,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="The trade language spoken across most civilized lands."
|
||||||
|
)
|
||||||
|
|
||||||
|
COMMON_SIGN_LANG = Language(
|
||||||
|
name="Common",
|
||||||
|
script=LanguageScript.NONE,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="The trade sign language spoken across most civilized lands."
|
||||||
|
)
|
||||||
|
|
||||||
|
DRACONIC = Language(
|
||||||
|
name="Draconic",
|
||||||
|
script=LanguageScript.DRACONIC,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="One of the oldest languages, often used in magic studies."
|
||||||
|
)
|
||||||
|
|
||||||
|
DWARVISH = Language(
|
||||||
|
name="Dwarvish",
|
||||||
|
script=LanguageScript.DWARVISH,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="Full of hard consonants and guttural sounds."
|
||||||
|
)
|
||||||
|
|
||||||
|
ELVISH = Language(
|
||||||
|
name="Elvish",
|
||||||
|
script=LanguageScript.ELVISH,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="Fluid, with subtle intonations and intricate grammar."
|
||||||
|
)
|
||||||
|
|
||||||
|
GIANT = Language(
|
||||||
|
name="Giant",
|
||||||
|
script=LanguageScript.DWARVISH,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="The language of giants and their kin."
|
||||||
|
)
|
||||||
|
|
||||||
|
GNOMISH = Language(
|
||||||
|
name="Gnomish",
|
||||||
|
script=LanguageScript.DWARVISH,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="Renowned for technical treatises and catalogs of knowledge."
|
||||||
|
)
|
||||||
|
|
||||||
|
GOBLIN = Language(
|
||||||
|
name="Goblin",
|
||||||
|
script=LanguageScript.DWARVISH,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="Harsh and strident, spoken by goblins, hobgoblins, and bugbears."
|
||||||
|
)
|
||||||
|
|
||||||
|
HALFLING = Language(
|
||||||
|
name="Halfling",
|
||||||
|
script=LanguageScript.COMMON,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="Not often written; halflings have a strong oral tradition."
|
||||||
|
)
|
||||||
|
|
||||||
|
ORC = Language(
|
||||||
|
name="Orc",
|
||||||
|
script=LanguageScript.DWARVISH,
|
||||||
|
rarity=LanguageRarity.STANDARD,
|
||||||
|
description="Harsh and grating, with hard consonants and no script of its own."
|
||||||
|
)
|
||||||
|
|
||||||
|
# === EXOTIC LANGUAGES ===
|
||||||
|
|
||||||
|
ABYSSAL = Language(
|
||||||
|
name="Abyssal",
|
||||||
|
script=LanguageScript.INFERNAL,
|
||||||
|
rarity=LanguageRarity.EXOTIC,
|
||||||
|
description="The language of demons and the Abyss."
|
||||||
|
)
|
||||||
|
|
||||||
|
CELESTIAL = Language(
|
||||||
|
name="Celestial",
|
||||||
|
script=LanguageScript.CELESTIAL,
|
||||||
|
rarity=LanguageRarity.EXOTIC,
|
||||||
|
description="The tongue of angels and other good outsiders."
|
||||||
|
)
|
||||||
|
|
||||||
|
DEEP_SPEECH = Language(
|
||||||
|
name="Deep Speech",
|
||||||
|
script=LanguageScript.NONE,
|
||||||
|
rarity=LanguageRarity.EXOTIC,
|
||||||
|
description="An alien language spoken by aberrations from the Far Realm."
|
||||||
|
)
|
||||||
|
|
||||||
|
INFERNAL = Language(
|
||||||
|
name="Infernal",
|
||||||
|
script=LanguageScript.INFERNAL,
|
||||||
|
rarity=LanguageRarity.EXOTIC,
|
||||||
|
description="The language of devils and the Nine Hells."
|
||||||
|
)
|
||||||
|
|
||||||
|
PRIMORDIAL = Language(
|
||||||
|
name="Primordial",
|
||||||
|
script=LanguageScript.DWARVISH,
|
||||||
|
rarity=LanguageRarity.EXOTIC,
|
||||||
|
description="The language of elementals, with dialects: Aquan, Auran, Ignan, Terran."
|
||||||
|
)
|
||||||
|
|
||||||
|
SYLVAN = Language(
|
||||||
|
name="Sylvan",
|
||||||
|
script=LanguageScript.ELVISH,
|
||||||
|
rarity=LanguageRarity.EXOTIC,
|
||||||
|
description="The language of the Feywild and its inhabitants."
|
||||||
|
)
|
||||||
|
|
||||||
|
UNDERCOMMON = Language(
|
||||||
|
name="Undercommon",
|
||||||
|
script=LanguageScript.ELVISH,
|
||||||
|
rarity=LanguageRarity.EXOTIC,
|
||||||
|
description="The trade language of the Underdark."
|
||||||
|
)
|
||||||
|
|
||||||
|
# === SECRET LANGUAGES ===
|
||||||
|
|
||||||
|
DRUIDIC = Language(
|
||||||
|
name="Druidic",
|
||||||
|
script=LanguageScript.NONE,
|
||||||
|
rarity=LanguageRarity.SECRET,
|
||||||
|
description="Secret language of druids. Druids are forbidden to teach it to non-druids."
|
||||||
|
)
|
||||||
|
|
||||||
|
THIEVES_CANT = Language(
|
||||||
|
name="Thieves' Cant",
|
||||||
|
script=LanguageScript.NONE,
|
||||||
|
rarity=LanguageRarity.SECRET,
|
||||||
|
description="A secret mix of slang and coded messages used by criminals."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convenience: all languages in one place
|
||||||
|
ALL_LANGUAGES = [
|
||||||
|
COMMON, DWARVISH, ELVISH, GIANT, GNOMISH, GOBLIN, HALFLING, ORC,
|
||||||
|
ABYSSAL, CELESTIAL, DRACONIC, DEEP_SPEECH, INFERNAL, PRIMORDIAL,
|
||||||
|
SYLVAN, UNDERCOMMON, DRUIDIC, THIEVES_CANT
|
||||||
|
]
|
||||||
|
|
||||||
|
STANDARD_LANGUAGES = [
|
||||||
|
COMMON, DRACONIC, DWARVISH, ELVISH, GIANT, GNOMISH, GOBLIN, HALFLING, ORC
|
||||||
|
]
|
||||||
|
|
||||||
|
EXOTIC_LANGUAGES = [
|
||||||
|
ABYSSAL, CELESTIAL, DEEP_SPEECH, INFERNAL, PRIMORDIAL,
|
||||||
|
SYLVAN, UNDERCOMMON
|
||||||
|
]
|
||||||
0
src/dnd_dm_toolkit/entities/races.py
Normal file
0
src/dnd_dm_toolkit/entities/races.py
Normal file
0
src/dnd_dm_toolkit/rules/__init__.py
Normal file
0
src/dnd_dm_toolkit/rules/__init__.py
Normal file
37
src/dnd_dm_toolkit/rules/core.py
Normal file
37
src/dnd_dm_toolkit/rules/core.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from typing import List
|
||||||
|
from ..core.stuctures.character import CharacterClass, Race, Attributes, Background, Language, Featureable
|
||||||
|
|
||||||
|
class Character(Featureable):
|
||||||
|
def __init__(self,
|
||||||
|
race: Race,
|
||||||
|
dnd_class: CharacterClass,
|
||||||
|
attrs: Attributes,
|
||||||
|
background: Background,
|
||||||
|
languages: List[Language]
|
||||||
|
):
|
||||||
|
self._char_race = race
|
||||||
|
self._char_cls = dnd_class
|
||||||
|
self._char_attrs = attrs
|
||||||
|
self._char_background = background
|
||||||
|
self._char_languages = languages
|
||||||
|
|
||||||
|
# Special Fields
|
||||||
|
self._char_features = list()
|
||||||
|
self._current_level = 0
|
||||||
|
self._char_available_languages = list()
|
||||||
|
|
||||||
|
self._init_languages()
|
||||||
|
self._init_features()
|
||||||
|
self.level_up()
|
||||||
|
|
||||||
|
def _init_languages(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _init_features(self):
|
||||||
|
all_features = self._char_race.features + self._char_background.background_specs
|
||||||
|
|
||||||
|
for feature in all_features:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def level_up(self):
|
||||||
|
pass
|
||||||
18
src/dnd_dm_toolkit/scrapper.sh
Executable file
18
src/dnd_dm_toolkit/scrapper.sh
Executable file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
OUTPUT="all_code.txt"
|
||||||
|
ROOT="."
|
||||||
|
|
||||||
|
> "$OUTPUT"
|
||||||
|
|
||||||
|
find "$ROOT" \
|
||||||
|
-type d -name "__pycache__" -prune -o \
|
||||||
|
-type f -name "*.py" ! -name "*.pyc" \
|
||||||
|
-print | sort | while read -r file; do
|
||||||
|
echo "########################################" >> "$OUTPUT"
|
||||||
|
echo "# FILE: $file" >> "$OUTPUT"
|
||||||
|
echo "########################################" >> "$OUTPUT"
|
||||||
|
echo >> "$OUTPUT"
|
||||||
|
cat "$file" >> "$OUTPUT"
|
||||||
|
echo -e "\n\n" >> "$OUTPUT"
|
||||||
|
done
|
||||||
0
src/dnd_dm_toolkit/utils/__init__.py
Normal file
0
src/dnd_dm_toolkit/utils/__init__.py
Normal file
30
src/dnd_dm_toolkit/utils/languages.py
Normal file
30
src/dnd_dm_toolkit/utils/languages.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
from ..core.enums.game import LanguageRarity
|
||||||
|
from ..core.stuctures.character import Language, Race
|
||||||
|
|
||||||
|
def get_available_languages(
|
||||||
|
race: Race,
|
||||||
|
exclude_secret: bool = True
|
||||||
|
) -> list[Language]:
|
||||||
|
"""
|
||||||
|
Returns languages available for a character to learn.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
race: The character's race.
|
||||||
|
exclude_secret: If True, excludes secret languages (Druidic, Thieves' Cant).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of learnable languages.
|
||||||
|
"""
|
||||||
|
from ..entities.languages import ALL_LANGUAGES
|
||||||
|
|
||||||
|
# Already known languages
|
||||||
|
known = set(race.languages)
|
||||||
|
|
||||||
|
# Available languages
|
||||||
|
available = [
|
||||||
|
lang for lang in ALL_LANGUAGES
|
||||||
|
if lang not in known
|
||||||
|
and (not exclude_secret or lang.rarity != LanguageRarity.SECRET)
|
||||||
|
]
|
||||||
|
|
||||||
|
return available
|
||||||
Loading…
Add table
Add a link
Reference in a new issue