Last active
January 25, 2026 22:14
-
-
Save greg76/163b7f1e73b47695c08453f73d74abf5 to your computer and use it in GitHub Desktop.
Simple example for AD&D classes implemented in Python using abstract classes and properties
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from abc import ABC, abstractmethod | |
| from dataclasses import asdict, dataclass | |
| class Character(ABC): | |
| @dataclass(slots=True, kw_only=True) | |
| class Stats: | |
| STR: int | |
| DEX: int | |
| CON: int | |
| INT: int | |
| WIS: int | |
| CHA: int | |
| def __str__(self) -> str: | |
| return " ".join(f"{k}:{v}" for k, v in asdict(self).items()) | |
| @property | |
| @abstractmethod | |
| def STANDARD_ARRAY(self) -> Stats: | |
| """ | |
| Returns the standard array for a specific class. | |
| Also adds +2/+1 for the primary abilities for higher initial bonuses. | |
| Each character class must implement their own standard array. | |
| """ | |
| return Character.Stats(STR=15 + 1, DEX=14 + 2, CON=13, INT=12, WIS=10, CHA=8) | |
| @property | |
| @abstractmethod | |
| def HIT_DIE(self) -> int: | |
| """ | |
| Specifies how many sided dice is used for calcualting the hit points for the class. | |
| Each character class must implement and specify its hit die. | |
| """ | |
| return 8 | |
| def __init__(self, level=None) -> None: | |
| super().__init__() | |
| self.stats = self.STANDARD_ARRAY | |
| self.level = level or 1 | |
| self.set_max_hp() | |
| self.hp = self.max_hp | |
| def set_max_hp(self) -> None: | |
| mod = self.get_modifier(self.stats.CON) | |
| self.max_hp = self.HIT_DIE + mod | |
| if self.level > 1: | |
| self.max_hp += round((self.level - 1) * ((1 + self.HIT_DIE) / 2 + mod)) | |
| def get_modifier(self, score: int) -> int: | |
| return (score - 10) // 2 | |
| def __str__(self) -> str: | |
| """Provides a standard way of representing the characters stats in a human friendly way.""" | |
| description = f""" | |
| Level {self.level} {self.__class__.__name__.lower()} | |
| HP: {self.hp}/{self.max_hp} {self.stats} | |
| """.strip() | |
| return "\n".join(line.strip() for line in description.splitlines()) | |
| class Paladin(Character): | |
| @property | |
| def HIT_DIE(self) -> int: | |
| return 10 | |
| @property | |
| def STANDARD_ARRAY(self) -> Character.Stats: | |
| return Character.Stats(STR=15 + 1, DEX=8, CON=13, INT=10, WIS=12, CHA=14 + 2) | |
| class Ranger(Character): | |
| @property | |
| def HIT_DIE(self) -> int: | |
| return 10 | |
| @property | |
| def STANDARD_ARRAY(self) -> Character.Stats: | |
| return Character.Stats(STR=8, DEX=15 + 1, CON=13, INT=12, WIS=14 + 2, CHA=10) | |
| class Bard(Character): | |
| @property | |
| def HIT_DIE(self) -> int: | |
| return 8 | |
| @property | |
| def STANDARD_ARRAY(self) -> Character.Stats: | |
| return Character.Stats(STR=8, DEX=14 + 2, CON=13, INT=10, WIS=12, CHA=15 + 1) | |
| def main() -> None: | |
| party = [Paladin(level=10), Bard(level=11), Ranger(level=9)] | |
| for member in party: | |
| print(member) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment