Skip to content

Commit ec3a350

Browse files
committed
Implement core RPG components: Campaign, Character, Combat, Inventory, NPC, Quest, and Spell Managers
1 parent 0c388f2 commit ec3a350

8 files changed

Lines changed: 278 additions & 0 deletions

File tree

engine/rpg/campaign_manager.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
Campaign Manager for DnD-style RPG
3+
"""
4+
class Campaign:
5+
def __init__(self, title, description, scenes=None, npcs=None, quests=None):
6+
self.title = title
7+
self.description = description
8+
self.scenes = scenes or []
9+
self.npcs = npcs or []
10+
self.quests = quests or []
11+
12+
def add_scene(self, scene):
13+
self.scenes.append(scene)
14+
15+
def add_npc(self, npc):
16+
self.npcs.append(npc)
17+
18+
def add_quest(self, quest):
19+
self.quests.append(quest)
20+
21+
def __repr__(self):
22+
return f"<Campaign {self.title} ({len(self.scenes)} scenes)>"
23+
24+
class CampaignManager:
25+
def __init__(self):
26+
self.campaigns = []
27+
28+
def create_campaign(self, title, description, **kwargs):
29+
campaign = Campaign(title, description, **kwargs)
30+
self.campaigns.append(campaign)
31+
return campaign
32+
33+
def get_campaign(self, title):
34+
for campaign in self.campaigns:
35+
if campaign.title == title:
36+
return campaign
37+
return None
38+
39+
def list_campaigns(self):
40+
return self.campaigns

engine/rpg/character_manager.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
DnD-style Character Manager for PtiCalin Game Engine
3+
"""
4+
5+
class Character:
6+
def __init__(self, name, race, char_class, level=1, stats=None, inventory=None, spells=None, quests=None):
7+
self.name = name
8+
self.race = race
9+
self.char_class = char_class
10+
self.level = level
11+
self.stats = stats or {
12+
'STR': 10, 'DEX': 10, 'CON': 10, 'INT': 10, 'WIS': 10, 'CHA': 10
13+
}
14+
self.inventory = inventory or []
15+
self.spells = spells or []
16+
self.quests = quests or []
17+
self.hp = self.max_hp()
18+
19+
def max_hp(self):
20+
# Example: 10 + CON modifier per level
21+
return 10 + (self.stats['CON'] - 10) // 2 * self.level
22+
23+
def add_item(self, item):
24+
self.inventory.append(item)
25+
26+
def add_spell(self, spell):
27+
self.spells.append(spell)
28+
29+
def add_quest(self, quest):
30+
self.quests.append(quest)
31+
32+
def __repr__(self):
33+
return f"<Character {self.name} ({self.char_class} Lv{self.level})>"
34+
35+
class CharacterManager:
36+
def __init__(self):
37+
self.characters = []
38+
39+
def create_character(self, name, race, char_class, **kwargs):
40+
char = Character(name, race, char_class, **kwargs)
41+
self.characters.append(char)
42+
return char
43+
44+
def get_character(self, name):
45+
for char in self.characters:
46+
if char.name == name:
47+
return char
48+
return None
49+
50+
def list_characters(self):
51+
return self.characters

engine/rpg/combat_engine.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
Simple DnD-style Combat Engine
3+
"""
4+
from .character_manager import Character
5+
from .dice_roller import roll_dice
6+
7+
class CombatEngine:
8+
def __init__(self, party, enemies):
9+
self.party = party # list of Character
10+
self.enemies = enemies # list of Character
11+
self.turn_order = self.determine_initiative()
12+
13+
def determine_initiative(self):
14+
# Sort by DEX stat, descending
15+
all_combatants = self.party + self.enemies
16+
return sorted(all_combatants, key=lambda c: c.stats['DEX'], reverse=True)
17+
18+
def attack(self, attacker, defender):
19+
# Roll d20 + STR for melee, DEX for ranged
20+
attack_roll, _, _ = roll_dice('1d20')
21+
attack_mod = attacker.stats['STR'] if attacker.char_class in ['Fighter', 'Barbarian'] else attacker.stats['DEX']
22+
total_attack = attack_roll + (attack_mod - 10) // 2
23+
# Simple AC check
24+
if total_attack >= defender.stats.get('AC', 10):
25+
damage, _, _ = roll_dice('1d8')
26+
defender.hp -= damage
27+
return True, damage
28+
return False, 0

engine/rpg/dice_roller.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""
2+
Dice Roller for DnD-style RPG
3+
"""
4+
import random
5+
6+
def roll_dice(dice_str):
7+
"""
8+
Roll dice in NdM format, e.g. '2d6+1'
9+
Returns total and breakdown.
10+
"""
11+
import re
12+
match = re.match(r"(\d+)d(\d+)([+-]\d+)?", dice_str)
13+
if not match:
14+
raise ValueError("Invalid dice format")
15+
n, m, mod = match.groups()
16+
n, m = int(n), int(m)
17+
mod = int(mod) if mod else 0
18+
rolls = [random.randint(1, m) for _ in range(n)]
19+
total = sum(rolls) + mod
20+
return total, rolls, mod

engine/rpg/inventory_helper.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Inventory Helper for DnD-style RPG
3+
"""
4+
class Item:
5+
def __init__(self, name, item_type, description, properties=None):
6+
self.name = name
7+
self.item_type = item_type # e.g. weapon, armor, potion
8+
self.description = description
9+
self.properties = properties or {}
10+
11+
def __repr__(self):
12+
return f"<Item {self.name} ({self.item_type})>"
13+
14+
class InventoryHelper:
15+
def __init__(self):
16+
self.items = []
17+
18+
def add_item(self, name, item_type, description, properties=None):
19+
item = Item(name, item_type, description, properties)
20+
self.items.append(item)
21+
return item
22+
23+
def get_item(self, name):
24+
for item in self.items:
25+
if item.name == name:
26+
return item
27+
return None
28+
29+
def list_items(self, item_type=None):
30+
if item_type:
31+
return [i for i in self.items if i.item_type == item_type]
32+
return self.items

engine/rpg/npc_helper.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
NPC Helper for DnD-style RPG
3+
"""
4+
class NPC:
5+
def __init__(self, name, traits=None, personality=None, dialogue=None):
6+
self.name = name
7+
self.traits = traits or {}
8+
self.personality = personality or "Neutral"
9+
self.dialogue = dialogue or []
10+
11+
def add_dialogue(self, line):
12+
self.dialogue.append(line)
13+
14+
def __repr__(self):
15+
return f"<NPC {self.name} ({self.personality})>"
16+
17+
class NPCHelper:
18+
def __init__(self):
19+
self.npcs = []
20+
21+
def add_npc(self, name, traits=None, personality=None, dialogue=None):
22+
npc = NPC(name, traits, personality, dialogue)
23+
self.npcs.append(npc)
24+
return npc
25+
26+
def get_npc(self, name):
27+
for npc in self.npcs:
28+
if npc.name == name:
29+
return npc
30+
return None
31+
32+
def list_npcs(self):
33+
return self.npcs

engine/rpg/quest_manager.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
Quest Manager for DnD-style RPG
3+
"""
4+
class Quest:
5+
def __init__(self, title, description, status='active', rewards=None):
6+
self.title = title
7+
self.description = description
8+
self.status = status # active, completed, failed
9+
self.rewards = rewards or []
10+
11+
def complete(self):
12+
self.status = 'completed'
13+
14+
def fail(self):
15+
self.status = 'failed'
16+
17+
def __repr__(self):
18+
return f"<Quest {self.title} ({self.status})>"
19+
20+
class QuestManager:
21+
def __init__(self):
22+
self.quests = []
23+
24+
def add_quest(self, title, description, **kwargs):
25+
quest = Quest(title, description, **kwargs)
26+
self.quests.append(quest)
27+
return quest
28+
29+
def get_quest(self, title):
30+
for quest in self.quests:
31+
if quest.title == title:
32+
return quest
33+
return None
34+
35+
def list_quests(self, status=None):
36+
if status:
37+
return [q for q in self.quests if q.status == status]
38+
return self.quests

engine/rpg/spell_manager.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Spell Manager for DnD-style RPG
3+
"""
4+
class Spell:
5+
def __init__(self, name, level, school, description, effect=None):
6+
self.name = name
7+
self.level = level
8+
self.school = school
9+
self.description = description
10+
self.effect = effect or {}
11+
12+
def __repr__(self):
13+
return f"<Spell {self.name} (Lv{self.level}, {self.school})>"
14+
15+
class SpellManager:
16+
def __init__(self):
17+
self.spells = []
18+
19+
def add_spell(self, name, level, school, description, effect=None):
20+
spell = Spell(name, level, school, description, effect)
21+
self.spells.append(spell)
22+
return spell
23+
24+
def get_spell(self, name):
25+
for spell in self.spells:
26+
if spell.name == name:
27+
return spell
28+
return None
29+
30+
def list_spells(self, school=None, level=None):
31+
result = self.spells
32+
if school:
33+
result = [s for s in result if s.school == school]
34+
if level:
35+
result = [s for s in result if s.level == level]
36+
return result

0 commit comments

Comments
 (0)