feat(api): add luck (LUK) stat to character system

Add new Luck stat to the character stats system with class-specific values:
- Assassin: 12 (highest - critical specialists)
- Luminary: 11 (divine favor)
- Wildstrider/Lorekeeper: 10 (average)
- Arcanist/Oathkeeper: 9 (modest)
- Vanguard: 8 (default - relies on strength)
- Necromancer: 7 (lowest - dark arts cost)

Changes:
- Add luck field to Stats dataclass with default of 8
- Add LUCK to StatType enum
- Update all 8 class YAML files with luck values
- Display LUK in character panel (play page) and detail page
- Update DATA_MODELS.md documentation

Backward compatible: existing characters without luck default to 8.
This commit is contained in:
2025-11-26 12:27:18 -06:00
parent d789b5df65
commit 30c3b800e6
13 changed files with 62 additions and 27 deletions

View File

@@ -8,7 +8,7 @@ description: >
excel in devastating spell damage, capable of incinerating groups of foes or freezing excel in devastating spell damage, capable of incinerating groups of foes or freezing
enemies in place. Choose your element: embrace the flames or command the frost. enemies in place. Choose your element: embrace the flames or command the frost.
# Base stats (total: 65) # Base stats (total: 65 + luck)
base_stats: base_stats:
strength: 8 # Low physical power strength: 8 # Low physical power
dexterity: 10 # Average agility dexterity: 10 # Average agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 15 # Exceptional magical power intelligence: 15 # Exceptional magical power
wisdom: 12 # Above average perception wisdom: 12 # Above average perception
charisma: 11 # Above average social charisma: 11 # Above average social
luck: 9 # Slight chaos magic boost
starting_equipment: starting_equipment:
- worn_staff - worn_staff

View File

@@ -8,7 +8,7 @@ description: >
capable of becoming an elusive phantom or a master of critical strikes. Choose your path: embrace capable of becoming an elusive phantom or a master of critical strikes. Choose your path: embrace
the shadows or perfect the killing blow. the shadows or perfect the killing blow.
# Base stats (total: 65) # Base stats (total: 65 + luck)
base_stats: base_stats:
strength: 11 # Above average physical power strength: 11 # Above average physical power
dexterity: 15 # Exceptional agility dexterity: 15 # Exceptional agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 9 # Below average magic intelligence: 9 # Below average magic
wisdom: 10 # Average perception wisdom: 10 # Average perception
charisma: 10 # Average social charisma: 10 # Average social
luck: 12 # High luck for crits and precision
starting_equipment: starting_equipment:
- rusty_dagger - rusty_dagger

View File

@@ -8,7 +8,7 @@ description: >
excel in supporting allies and controlling enemies through clever magic and mental manipulation. excel in supporting allies and controlling enemies through clever magic and mental manipulation.
Choose your art: weave arcane power or bend reality itself. Choose your art: weave arcane power or bend reality itself.
# Base stats (total: 67) # Base stats (total: 67 + luck)
base_stats: base_stats:
strength: 8 # Low physical power strength: 8 # Low physical power
dexterity: 11 # Above average agility dexterity: 11 # Above average agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 13 # Above average magical power intelligence: 13 # Above average magical power
wisdom: 11 # Above average perception wisdom: 11 # Above average perception
charisma: 14 # High social/performance charisma: 14 # High social/performance
luck: 10 # Knowledge is its own luck
starting_equipment: starting_equipment:
- tome - tome

View File

@@ -8,7 +8,7 @@ description: >
capable of becoming a guardian angel for their allies or a righteous crusader smiting evil. capable of becoming a guardian angel for their allies or a righteous crusader smiting evil.
Choose your calling: protect the innocent or judge the wicked. Choose your calling: protect the innocent or judge the wicked.
# Base stats (total: 68) # Base stats (total: 68 + luck)
base_stats: base_stats:
strength: 9 # Below average physical power strength: 9 # Below average physical power
dexterity: 9 # Below average agility dexterity: 9 # Below average agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 12 # Above average magical power intelligence: 12 # Above average magical power
wisdom: 14 # High perception/divine power wisdom: 14 # High perception/divine power
charisma: 13 # Above average social charisma: 13 # Above average social
luck: 11 # Divine favor grants fortune
starting_equipment: starting_equipment:
- rusty_mace - rusty_mace

View File

@@ -8,7 +8,7 @@ description: >
excel in draining enemies over time or overwhelming foes with undead minions. excel in draining enemies over time or overwhelming foes with undead minions.
Choose your dark art: curse your enemies or raise an army of the dead. Choose your dark art: curse your enemies or raise an army of the dead.
# Base stats (total: 65) # Base stats (total: 65 + luck)
base_stats: base_stats:
strength: 8 # Low physical power strength: 8 # Low physical power
dexterity: 10 # Average agility dexterity: 10 # Average agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 14 # High magical power intelligence: 14 # High magical power
wisdom: 11 # Above average perception wisdom: 11 # Above average perception
charisma: 12 # Above average social (commands undead) charisma: 12 # Above average social (commands undead)
luck: 7 # Dark arts come with a cost
starting_equipment: starting_equipment:
- bone_wand - bone_wand

View File

@@ -8,7 +8,7 @@ description: >
capable of becoming an unyielding shield for their allies or a beacon of healing light. capable of becoming an unyielding shield for their allies or a beacon of healing light.
Choose your oath: defend the weak or redeem the fallen. Choose your oath: defend the weak or redeem the fallen.
# Base stats (total: 67) # Base stats (total: 67 + luck)
base_stats: base_stats:
strength: 12 # Above average physical power strength: 12 # Above average physical power
dexterity: 9 # Below average agility dexterity: 9 # Below average agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 10 # Average magic intelligence: 10 # Average magic
wisdom: 12 # Above average perception wisdom: 12 # Above average perception
charisma: 11 # Above average social charisma: 11 # Above average social
luck: 9 # Honorable, modest fortune
starting_equipment: starting_equipment:
- rusty_sword - rusty_sword

View File

@@ -8,7 +8,7 @@ description: >
capable of becoming an unbreakable shield for their allies or a relentless damage dealer. capable of becoming an unbreakable shield for their allies or a relentless damage dealer.
Choose your path: become a stalwart defender or a devastating weapon master. Choose your path: become a stalwart defender or a devastating weapon master.
# Base stats (total: 65, average: 10.83) # Base stats (total: 65 + luck)
base_stats: base_stats:
strength: 14 # High physical power strength: 14 # High physical power
dexterity: 10 # Average agility dexterity: 10 # Average agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 8 # Low magic intelligence: 8 # Low magic
wisdom: 10 # Average perception wisdom: 10 # Average perception
charisma: 9 # Below average social charisma: 9 # Below average social
luck: 8 # Low luck, relies on strength
# Starting equipment (minimal) # Starting equipment (minimal)
starting_equipment: starting_equipment:

View File

@@ -8,7 +8,7 @@ description: >
can become elite marksmen with unmatched accuracy or beast masters commanding powerful can become elite marksmen with unmatched accuracy or beast masters commanding powerful
animal companions. Choose your path: perfect your aim or unleash the wild. animal companions. Choose your path: perfect your aim or unleash the wild.
# Base stats (total: 66) # Base stats (total: 66 + luck)
base_stats: base_stats:
strength: 10 # Average physical power strength: 10 # Average physical power
dexterity: 14 # High agility dexterity: 14 # High agility
@@ -16,6 +16,7 @@ base_stats:
intelligence: 9 # Below average magic intelligence: 9 # Below average magic
wisdom: 13 # Above average perception wisdom: 13 # Above average perception
charisma: 9 # Below average social charisma: 9 # Below average social
luck: 10 # Average luck, self-reliant
starting_equipment: starting_equipment:
- rusty_bow - rusty_bow

View File

@@ -49,6 +49,7 @@ class StatType(Enum):
INTELLIGENCE = "intelligence" # Magical power INTELLIGENCE = "intelligence" # Magical power
WISDOM = "wisdom" # Perception and insight WISDOM = "wisdom" # Perception and insight
CHARISMA = "charisma" # Social influence CHARISMA = "charisma" # Social influence
LUCK = "luck" # Fortune and fate
class AbilityType(Enum): class AbilityType(Enum):

View File

@@ -21,6 +21,7 @@ class Stats:
intelligence: Magical power, affects spell damage and MP intelligence: Magical power, affects spell damage and MP
wisdom: Perception and insight, affects magical resistance wisdom: Perception and insight, affects magical resistance
charisma: Social influence, affects NPC interactions charisma: Social influence, affects NPC interactions
luck: Fortune and fate, affects critical hits, loot, and random outcomes
Computed Properties: Computed Properties:
hit_points: Maximum HP = 10 + (constitution × 2) hit_points: Maximum HP = 10 + (constitution × 2)
@@ -35,6 +36,7 @@ class Stats:
intelligence: int = 10 intelligence: int = 10
wisdom: int = 10 wisdom: int = 10
charisma: int = 10 charisma: int = 10
luck: int = 8
@property @property
def hit_points(self) -> int: def hit_points(self) -> int:
@@ -111,6 +113,7 @@ class Stats:
intelligence=data.get("intelligence", 10), intelligence=data.get("intelligence", 10),
wisdom=data.get("wisdom", 10), wisdom=data.get("wisdom", 10),
charisma=data.get("charisma", 10), charisma=data.get("charisma", 10),
luck=data.get("luck", 8),
) )
def copy(self) -> 'Stats': def copy(self) -> 'Stats':
@@ -127,6 +130,7 @@ class Stats:
intelligence=self.intelligence, intelligence=self.intelligence,
wisdom=self.wisdom, wisdom=self.wisdom,
charisma=self.charisma, charisma=self.charisma,
luck=self.luck,
) )
def __repr__(self) -> str: def __repr__(self) -> str:
@@ -134,7 +138,7 @@ class Stats:
return ( return (
f"Stats(STR={self.strength}, DEX={self.dexterity}, " f"Stats(STR={self.strength}, DEX={self.dexterity}, "
f"CON={self.constitution}, INT={self.intelligence}, " f"CON={self.constitution}, INT={self.intelligence}, "
f"WIS={self.wisdom}, CHA={self.charisma}, " f"WIS={self.wisdom}, CHA={self.charisma}, LUK={self.luck}, "
f"HP={self.hit_points}, MP={self.mana_points}, " f"HP={self.hit_points}, MP={self.mana_points}, "
f"DEF={self.defense}, RES={self.resistance})" f"DEF={self.defense}, RES={self.resistance})"
) )

View File

@@ -50,6 +50,7 @@ All enum types are defined in `/app/models/enums.py` for type safety throughout
| `INTELLIGENCE` | Magical power | | `INTELLIGENCE` | Magical power |
| `WISDOM` | Perception and insight | | `WISDOM` | Perception and insight |
| `CHARISMA` | Social influence | | `CHARISMA` | Social influence |
| `LUCK` | Fortune and fate (affects crits, loot, random outcomes) |
### AbilityType ### AbilityType
@@ -597,14 +598,15 @@ success = service.soft_delete_message(
### Stats ### Stats
| Field | Type | Description | | Field | Type | Default | Description |
|-------|------|-------------| |-------|------|---------|-------------|
| `strength` | int | Physical power | | `strength` | int | 10 | Physical power |
| `dexterity` | int | Agility and precision | | `dexterity` | int | 10 | Agility and precision |
| `constitution` | int | Endurance and health | | `constitution` | int | 10 | Endurance and health |
| `intelligence` | int | Magical power | | `intelligence` | int | 10 | Magical power |
| `wisdom` | int | Perception and insight | | `wisdom` | int | 10 | Perception and insight |
| `charisma` | int | Social influence | | `charisma` | int | 10 | Social influence |
| `luck` | int | 8 | Fortune and fate (affects crits, loot, random outcomes) |
**Derived Properties (Computed):** **Derived Properties (Computed):**
- `hit_points` = 10 + (constitution × 2) - `hit_points` = 10 + (constitution × 2)
@@ -614,6 +616,8 @@ success = service.soft_delete_message(
**Note:** Defense and resistance are computed properties, not stored values. They are calculated on-the-fly from constitution and wisdom. **Note:** Defense and resistance are computed properties, not stored values. They are calculated on-the-fly from constitution and wisdom.
**Luck Stat:** The luck stat has a lower default (8) compared to other stats (10). Each class has a specific luck value ranging from 7 (Necromancer) to 12 (Assassin). Luck will influence critical hit chance, hit/miss calculations, base damage variance, NPC interactions, loot generation, and spell power in future implementations.
### SkillNode ### SkillNode
| Field | Type | Description | | Field | Type | Description |
@@ -662,16 +666,26 @@ success = service.soft_delete_message(
### Initial 8 Player Classes ### Initial 8 Player Classes
| Class | Theme | Skill Tree 1 | Skill Tree 2 | | Class | Theme | LUK | Skill Tree 1 | Skill Tree 2 |
|-------|-------|--------------|--------------| |-------|-------|-----|--------------|--------------|
| **Vanguard** | Tank/melee | Defensive (shields, armor, taunts) | Offensive (weapon mastery, heavy strikes) | | **Vanguard** | Tank/melee | 8 | Defensive (shields, armor, taunts) | Offensive (weapon mastery, heavy strikes) |
| **Assassin** | Stealth/critical | Assassination (critical hits, poisons) | Shadow (stealth, evasion) | | **Assassin** | Stealth/critical | 12 | Assassination (critical hits, poisons) | Shadow (stealth, evasion) |
| **Arcanist** | Elemental spells | Pyromancy (fire spells, DoT) | Cryomancy (ice spells, crowd control) | | **Arcanist** | Elemental spells | 9 | Pyromancy (fire spells, DoT) | Cryomancy (ice spells, crowd control) |
| **Luminary** | Healing/support | Holy (healing, buffs) | Divine Wrath (smite, undead damage) | | **Luminary** | Healing/support | 11 | Holy (healing, buffs) | Divine Wrath (smite, undead damage) |
| **Wildstrider** | Ranged/nature | Marksmanship (bow skills, critical shots) | Beast Master (pet companion, nature magic) | | **Wildstrider** | Ranged/nature | 10 | Marksmanship (bow skills, critical shots) | Beast Master (pet companion, nature magic) |
| **Oathkeeper** | Hybrid tank/healer | Protection (defensive auras, healing) | Retribution (holy damage, smites) | | **Oathkeeper** | Hybrid tank/healer | 9 | Protection (defensive auras, healing) | Retribution (holy damage, smites) |
| **Necromancer** | Death magic/summon | Dark Arts (curses, life drain) | Summoning (undead minions) | | **Necromancer** | Death magic/summon | 7 | Dark Arts (curses, life drain) | Summoning (undead minions) |
| **Lorekeeper** | Support/control | Performance (buffs, debuffs via music) | Trickery (illusions, charm) | | **Lorekeeper** | Support/control | 10 | Performance (buffs, debuffs via music) | Trickery (illusions, charm) |
**Class Luck Values:**
- **Assassin (12):** Highest luck - critical strike specialists benefit most from fortune
- **Luminary (11):** Divine favor grants above-average luck
- **Wildstrider (10):** Average luck - self-reliant nature
- **Lorekeeper (10):** Average luck - knowledge is their advantage
- **Arcanist (9):** Slight chaos magic influence
- **Oathkeeper (9):** Honorable path grants modest fortune
- **Vanguard (8):** Relies on strength and skill, not luck
- **Necromancer (7):** Lowest luck - dark arts exact a toll
**Extensibility:** Class system designed to easily add more classes in future updates. **Extensibility:** Class system designed to easily add more classes in future updates.

View File

@@ -81,6 +81,10 @@
<span class="stat-name">CHA</span> <span class="stat-name">CHA</span>
<span class="stat-value">{{ character.base_stats.charisma }}</span> <span class="stat-value">{{ character.base_stats.charisma }}</span>
</div> </div>
<div class="stat-item">
<span class="stat-name">LUK</span>
<span class="stat-value">{{ character.base_stats.luck }}</span>
</div>
</div> </div>
<!-- Derived Stats --> <!-- Derived Stats -->

View File

@@ -74,6 +74,10 @@ Displays character stats, resource bars, and action buttons
<div class="stat-abbr">CHA</div> <div class="stat-abbr">CHA</div>
<div class="stat-value">{{ character.stats.charisma }}</div> <div class="stat-value">{{ character.stats.charisma }}</div>
</div> </div>
<div class="stat-item">
<div class="stat-abbr">LUK</div>
<div class="stat-value">{{ character.stats.luck }}</div>
</div>
</div> </div>
</div> </div>
</div> </div>