Initial Commit

This commit is contained in:
helldh 2026-01-13 09:10:49 +03:00
commit d25706163c
34 changed files with 1035 additions and 0 deletions

0
src/__init__.py Normal file
View file

View file

View file

View file

@ -0,0 +1,5 @@
from .sentinel import _AllStatsSentinel
ALL_STATS = _AllStatsSentinel()
MAXIMUM_STAT_VALUE = 30

View 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):
"""
LawChaos alignment axis.
This axis describes a characters attitude toward order, tradition,
authority, and structure versus freedom, individuality, and spontaneity.
- LAWFUL: Values order, rules, hierarchy, and tradition.
- VERITABLE: Neutral position on the LawChaos 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):
"""
GoodEvil alignment axis.
This axis represents a characters 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()

View file

View file

View 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")

View 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 characters 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 130.
"""
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 characters class.
A character class defines the characters 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 characters alignment using two independent axes:
LawChaos and GoodEvil.
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 characters 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

View 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

View file

View 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
}

View file

View 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
],
)

View file

@ -0,0 +1,4 @@
from ...core.stuctures.character import Featureable
def darkvision(entity: Featureable):
pass

View file

@ -0,0 +1,6 @@
from ...core.enums.game import FeatureSource
from ...core.stuctures.character import Feature
DARKVISION = Feature(
)

View 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
]

View file

View file

View 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
View 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

View file

View 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