Spaces:
Sleeping
Sleeping
File size: 10,097 Bytes
71b378e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
"""
Campaign and session management data models
"""
from datetime import datetime
from typing import Optional, List, Dict, Any
from enum import Enum
from pydantic import BaseModel, Field
class CampaignTheme(str, Enum):
"""Campaign themes"""
HIGH_FANTASY = "High Fantasy"
DARK_FANTASY = "Dark Fantasy"
URBAN_FANTASY = "Urban Fantasy"
POLITICAL_INTRIGUE = "Political Intrigue"
HORROR = "Horror"
EXPLORATION = "Exploration"
DUNGEON_CRAWL = "Dungeon Crawl"
CUSTOM = "Custom"
class EventType(str, Enum):
"""Types of campaign events"""
COMBAT = "Combat"
SOCIAL = "Social"
EXPLORATION = "Exploration"
DISCOVERY = "Discovery"
PLOT_DEVELOPMENT = "Plot Development"
CHARACTER_MOMENT = "Character Moment"
NPC_INTERACTION = "NPC Interaction"
QUEST_UPDATE = "Quest Update"
class CampaignEvent(BaseModel):
"""Individual campaign event/memory"""
id: Optional[str] = Field(default=None, description="Event ID")
campaign_id: str = Field(description="Campaign this event belongs to")
session_number: int = Field(ge=1, description="Session number")
event_type: EventType = Field(description="Type of event")
title: str = Field(min_length=1, max_length=200, description="Event title")
description: str = Field(description="Full event description")
# Participants
characters_involved: List[str] = Field(default_factory=list, description="Character IDs")
npcs_involved: List[str] = Field(default_factory=list, description="NPC IDs")
locations: List[str] = Field(default_factory=list, description="Location names")
# Context
consequences: List[str] = Field(default_factory=list, description="Event consequences")
items_gained: List[str] = Field(default_factory=list, description="Items acquired")
items_lost: List[str] = Field(default_factory=list, description="Items lost")
experience_awarded: int = Field(ge=0, default=0, description="XP awarded")
# Metadata
timestamp: datetime = Field(default_factory=datetime.now)
importance: int = Field(ge=1, le=5, default=3, description="Event importance (1-5)")
tags: List[str] = Field(default_factory=list, description="Event tags")
# GM notes
gm_notes: str = Field(default="", description="Private GM notes")
player_visible: bool = Field(default=True, description="Visible to players")
def to_markdown(self) -> str:
"""Convert to markdown"""
return f"""## {self.title}
**Type:** {self.event_type.value} | **Importance:** {'⭐' * self.importance}
**Session:** {self.session_number} | **Date:** {self.timestamp.strftime('%Y-%m-%d')}
{self.description}
**Participants:** {', '.join(self.characters_involved)}
**NPCs:** {', '.join(self.npcs_involved)}
**Locations:** {', '.join(self.locations)}
**Consequences:**
{chr(10).join(f"- {c}" for c in self.consequences)}
"""
class CampaignMemory(BaseModel):
"""Searchable campaign memory/context"""
campaign_id: str = Field(description="Campaign ID")
# Full context for AI
full_context: str = Field(default="", description="Complete campaign context")
# Key information
major_npcs: Dict[str, str] = Field(default_factory=dict, description="NPC name -> description")
key_locations: Dict[str, str] = Field(default_factory=dict, description="Location -> description")
active_quests: List[str] = Field(default_factory=list, description="Current quests")
completed_quests: List[str] = Field(default_factory=list, description="Finished quests")
# Story beats
important_events: List[str] = Field(default_factory=list, description="Key story moments")
unresolved_threads: List[str] = Field(default_factory=list, description="Plot threads")
secrets_discovered: List[str] = Field(default_factory=list, description="Revealed secrets")
secrets_hidden: List[str] = Field(default_factory=list, description="Hidden secrets")
# Relationships
faction_standings: Dict[str, int] = Field(default_factory=dict, description="Faction name -> standing (-100 to 100)")
npc_relationships: Dict[str, str] = Field(default_factory=dict, description="NPC -> relationship status")
# Metadata
last_updated: datetime = Field(default_factory=datetime.now)
total_events: int = Field(ge=0, default=0)
def add_event_to_memory(self, event: CampaignEvent):
"""Update memory with new event"""
self.total_events += 1
# Add NPCs
for npc in event.npcs_involved:
if npc not in self.major_npcs:
self.major_npcs[npc] = "Recently introduced"
# Add locations
for location in event.locations:
if location not in self.key_locations:
self.key_locations[location] = "Visited"
# Add to important events if high importance
if event.importance >= 4:
self.important_events.append(event.title)
self.last_updated = datetime.now()
class Campaign(BaseModel):
"""Complete D&D campaign"""
# Core identity
id: Optional[str] = Field(default=None, description="Campaign ID")
name: str = Field(min_length=1, max_length=100, description="Campaign name")
theme: CampaignTheme = Field(description="Campaign theme")
# Setting
setting: str = Field(description="Campaign setting description")
world_name: str = Field(default="", description="World/realm name")
starting_location: str = Field(default="", description="Starting location")
# Campaign info
summary: str = Field(description="Campaign summary/hook")
current_arc: str = Field(default="", description="Current story arc")
level_range: str = Field(default="1-5", description="Expected level range")
# Story elements
main_conflict: str = Field(description="Central conflict")
key_factions: List[str] = Field(default_factory=list, description="Important factions")
major_villains: List[str] = Field(default_factory=list, description="Main antagonists")
central_mysteries: List[str] = Field(default_factory=list, description="Ongoing mysteries")
# Characters
character_ids: List[str] = Field(default_factory=list, description="Player character IDs")
party_size: int = Field(ge=1, le=10, default=4, description="Expected party size")
# Session tracking
current_session: int = Field(ge=1, default=1, description="Current session number")
total_sessions: int = Field(ge=0, default=0, description="Total sessions played")
# Memory
memory: CampaignMemory = Field(default=None, description="Campaign memory")
# Metadata
game_master: str = Field(default="", description="GM name")
created_at: datetime = Field(default_factory=datetime.now)
updated_at: datetime = Field(default_factory=datetime.now)
last_session_date: Optional[datetime] = Field(default=None)
# Settings
is_active: bool = Field(default=True, description="Campaign active?")
homebrew_rules: List[str] = Field(default_factory=list, description="Custom rules")
notes: str = Field(default="", description="GM notes")
def __init__(self, **data):
super().__init__(**data)
if self.memory is None:
self.memory = CampaignMemory(campaign_id=self.id or "")
def add_character(self, character_id: str):
"""Add character to campaign"""
if character_id not in self.character_ids:
self.character_ids.append(character_id)
self.updated_at = datetime.now()
def remove_character(self, character_id: str):
"""Remove character from campaign"""
if character_id in self.character_ids:
self.character_ids.remove(character_id)
self.updated_at = datetime.now()
def start_new_session(self):
"""Start a new session"""
self.current_session += 1
self.total_sessions += 1
self.last_session_date = datetime.now()
self.updated_at = datetime.now()
def add_event(self, event: CampaignEvent):
"""Add event to campaign memory"""
self.memory.add_event_to_memory(event)
self.updated_at = datetime.now()
def to_context_string(self) -> str:
"""Generate context string for AI"""
return f"""Campaign: {self.name}
Theme: {self.theme.value}
Setting: {self.setting}
Main Conflict: {self.main_conflict}
Current Arc: {self.current_arc}
Session: {self.current_session}
Key Factions: {', '.join(self.key_factions)}
Major Villains: {', '.join(self.major_villains)}
Active Quests: {', '.join(self.memory.active_quests)}
Important Events: {', '.join(self.memory.important_events[-5:])} # Last 5 events
Party Size: {self.party_size}
Level Range: {self.level_range}
"""
def to_markdown(self) -> str:
"""Generate markdown campaign summary"""
return f"""# {self.name}
**{self.theme.value} Campaign**
## Setting
{self.setting}
**World:** {self.world_name}
**Starting Location:** {self.starting_location}
## Campaign Summary
{self.summary}
## Main Conflict
{self.main_conflict}
## Current Story Arc
{self.current_arc}
## Key Elements
**Factions:** {', '.join(self.key_factions)}
**Villains:** {', '.join(self.major_villains)}
**Mysteries:** {', '.join(self.central_mysteries)}
## Party Information
**Party Size:** {self.party_size}
**Level Range:** {self.level_range}
**Current Session:** {self.current_session}
**Total Sessions:** {self.total_sessions}
## Campaign Status
**Active:** {'Yes' if self.is_active else 'No'}
**Last Session:** {self.last_session_date.strftime('%Y-%m-%d') if self.last_session_date else 'Not started'}
## GM Notes
{self.notes}
"""
class Config:
json_schema_extra = {
"example": {
"name": "The Shattered Crown",
"theme": "High Fantasy",
"setting": "A kingdom torn by civil war",
"summary": "Adventurers must unite the realm",
"main_conflict": "Succession crisis threatens the kingdom",
"party_size": 4,
"level_range": "1-10"
}
}
|