ghost-logic commited on
Commit
73726cb
·
verified ·
1 Parent(s): 758e190

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +330 -11
app.py CHANGED
@@ -85,12 +85,25 @@ class Campaign:
85
  class NPC:
86
  name: str
87
  race: str
 
88
  occupation: str
89
  personality: str
90
  secret: str
91
  voice_description: str
92
  relationship_to_party: str
93
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  # ===== AI AGENT CLASSES =====
95
  class DungeonMasterAgent:
96
  """AI agent that acts as a Dungeon Master"""
@@ -176,19 +189,22 @@ class NPCAgent:
176
  self.personality = "Versatile character actor with deep understanding of motivations"
177
  self.specializations = ["Character creation", "Dialogue", "Motivations", "Voice acting"]
178
 
179
- def generate_npc(self, context: str, role: str, importance: str) -> Dict:
180
  """Generate a detailed NPC"""
181
  try:
182
  from openai import OpenAI
183
  client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
184
 
 
 
185
  prompt = f"""Create a detailed NPC for:
186
  Context: {context}
187
  Role: {role}
188
  Importance: {importance}
 
189
 
190
  Generate:
191
- 1. Name and basic demographics
192
  2. Personality traits (3-4 key traits)
193
  3. Background and motivation
194
  4. Speech patterns/accent description
@@ -201,7 +217,7 @@ class NPCAgent:
201
 
202
  response = client.chat.completions.create(
203
  model="gpt-4",
204
- messages=[{"role": "system", "content": "You are an expert at creating memorable, three-dimensional NPCs for D&D campaigns."},
205
  {"role": "user", "content": prompt}],
206
  max_tokens=400,
207
  temperature=0.8
@@ -211,7 +227,7 @@ class NPCAgent:
211
 
212
  except Exception as e:
213
  logger.error(f"NPC generation failed: {e}")
214
- return {"success": False, "error": str(e), "content": f"Mock NPC: {role} character for {context}"}
215
 
216
  def roleplay_npc(self, npc_description: str, player_input: str, context: str) -> Dict:
217
  """Roleplay as an NPC in response to player actions"""
@@ -289,6 +305,90 @@ class WorldBuilderAgent:
289
  logger.error(f"Location generation failed: {e}")
290
  return {"success": False, "error": str(e), "content": f"Mock Location: {theme} {location_type} for {purpose}"}
291
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  class LootMasterAgent:
293
  """AI agent specialized in creating balanced loot and magic items"""
294
 
@@ -588,6 +688,7 @@ def create_main_interface():
588
  dm_agent = DungeonMasterAgent()
589
  npc_agent = NPCAgent()
590
  world_agent = WorldBuilderAgent()
 
591
  loot_agent = LootMasterAgent()
592
  character_creator = CharacterCreator()
593
 
@@ -621,6 +722,7 @@ def create_main_interface():
621
  - 🗺️ World Builder Agent
622
  - 💰 Loot Master & Magic Item Designer
623
  - 🐉 Enhanced Character Creator
 
624
  """)
625
 
626
  with gr.Tabs():
@@ -738,6 +840,10 @@ def create_main_interface():
738
  choices=["Minor", "Moderate", "Major", "Recurring"],
739
  label="Importance Level", value="Moderate"
740
  )
 
 
 
 
741
 
742
  create_npc_btn = gr.Button("🎭 Generate NPC", variant="primary")
743
 
@@ -785,6 +891,57 @@ def create_main_interface():
785
 
786
  location_output = gr.Textbox(label="Location Details", lines=12, elem_classes=["output-box"])
787
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
  # ===== LOOT MASTER TAB =====
789
  with gr.TabItem("💰 Loot Master"):
790
  gr.Markdown("## 💎 Treasure & Magic Item Generator", elem_classes=["agent-card"])
@@ -1052,14 +1209,16 @@ def create_main_interface():
1052
  return result.get("content", "Error generating session")
1053
 
1054
  # NPC Events
1055
- def create_npc(context, role, importance):
1056
- result = npc_agent.generate_npc(context, role, importance)
1057
  return result.get("content", "Error creating NPC")
1058
 
1059
  def generate_npc_portrait(npc_data):
1060
  if not npc_data:
1061
- return "Create an NPC first"
1062
- prompt = "Fantasy portrait of a D&D NPC character, detailed face, professional RPG art"
 
 
1063
  return generate_image(prompt)
1064
 
1065
  def npc_roleplay_response(npc_data, player_input, context):
@@ -1077,6 +1236,23 @@ def create_main_interface():
1077
  prompt = f"{theme} {loc_type} fantasy location, detailed environment art, D&D setting"
1078
  return generate_image(prompt)
1079
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1080
  # Loot Events
1081
  def create_loot_table(level, encounter, rarity):
1082
  result = loot_agent.generate_loot_table(level, encounter, rarity)
@@ -1189,13 +1365,13 @@ def create_main_interface():
1189
  # NPC events
1190
  create_npc_btn.click(
1191
  create_npc,
1192
- inputs=[npc_context, npc_role, npc_importance],
1193
  outputs=[npc_output]
1194
  )
1195
 
1196
  npc_portrait_btn.click(
1197
  generate_npc_portrait,
1198
- inputs=[current_npc_data],
1199
  outputs=[npc_portrait]
1200
  )
1201
 
@@ -1218,6 +1394,31 @@ def create_main_interface():
1218
  outputs=[location_image]
1219
  )
1220
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1221
  # Loot events
1222
  generate_loot_btn.click(
1223
  create_loot_table,
@@ -1242,6 +1443,124 @@ def create_main_interface():
1242
  gen_encounter_btn.click(generate_random_encounter, inputs=[encounter_level, encounter_difficulty], outputs=[random_encounter])
1243
  gen_hook_btn.click(generate_plot_hook, inputs=[hook_theme], outputs=[plot_hook])
1244
  gen_weather_btn.click(generate_weather, inputs=[climate], outputs=[weather])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1245
 
1246
  return demo
1247
 
@@ -1270,4 +1589,4 @@ if __name__ == "__main__":
1270
  except Exception as e:
1271
  logger.error(f"❌ Launch failed: {e}")
1272
  # Fallback launch for HF Spaces
1273
- demo.launch()
 
85
  class NPC:
86
  name: str
87
  race: str
88
+ gender: str
89
  occupation: str
90
  personality: str
91
  secret: str
92
  voice_description: str
93
  relationship_to_party: str
94
 
95
+ @dataclass
96
+ class Deity:
97
+ name: str
98
+ domain: str
99
+ alignment: Alignment
100
+ gender: str
101
+ description: str
102
+ holy_symbol: str
103
+ followers: str
104
+ tenets: List[str]
105
+ clergy: str
106
+
107
  # ===== AI AGENT CLASSES =====
108
  class DungeonMasterAgent:
109
  """AI agent that acts as a Dungeon Master"""
 
189
  self.personality = "Versatile character actor with deep understanding of motivations"
190
  self.specializations = ["Character creation", "Dialogue", "Motivations", "Voice acting"]
191
 
192
+ def generate_npc(self, context: str, role: str, importance: str, gender: str = "") -> Dict:
193
  """Generate a detailed NPC"""
194
  try:
195
  from openai import OpenAI
196
  client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
197
 
198
+ gender_text = f"Gender: {gender}" if gender else "Gender: Choose appropriate gender"
199
+
200
  prompt = f"""Create a detailed NPC for:
201
  Context: {context}
202
  Role: {role}
203
  Importance: {importance}
204
+ {gender_text}
205
 
206
  Generate:
207
+ 1. Name and basic demographics (including gender)
208
  2. Personality traits (3-4 key traits)
209
  3. Background and motivation
210
  4. Speech patterns/accent description
 
217
 
218
  response = client.chat.completions.create(
219
  model="gpt-4",
220
+ messages=[{"role": "system", "content": "You are an expert at creating memorable, three-dimensional NPCs for D&D campaigns with diverse and inclusive representation."},
221
  {"role": "user", "content": prompt}],
222
  max_tokens=400,
223
  temperature=0.8
 
227
 
228
  except Exception as e:
229
  logger.error(f"NPC generation failed: {e}")
230
+ return {"success": False, "error": str(e), "content": f"Mock NPC: {gender if gender else 'Character'} {role} for {context}"}
231
 
232
  def roleplay_npc(self, npc_description: str, player_input: str, context: str) -> Dict:
233
  """Roleplay as an NPC in response to player actions"""
 
305
  logger.error(f"Location generation failed: {e}")
306
  return {"success": False, "error": str(e), "content": f"Mock Location: {theme} {location_type} for {purpose}"}
307
 
308
+ class DeityAgent:
309
+ """AI agent specialized in creating deities and pantheons"""
310
+
311
+ def __init__(self):
312
+ self.personality = "Divine architect of mythological systems"
313
+ self.specializations = ["Mythology", "Religion", "Divine domains", "Pantheon design"]
314
+
315
+ def generate_deity(self, domain: str, alignment: str, gender: str, pantheon_theme: str) -> Dict:
316
+ """Generate a detailed deity"""
317
+ try:
318
+ from openai import OpenAI
319
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
320
+
321
+ prompt = f"""Create a detailed deity for D&D:
322
+ Domain: {domain}
323
+ Alignment: {alignment}
324
+ Gender: {gender}
325
+ Pantheon Theme: {pantheon_theme}
326
+
327
+ Generate:
328
+ 1. Name and titles
329
+ 2. Physical description and manifestation
330
+ 3. Personality and temperament
331
+ 4. Core domains and portfolio
332
+ 5. Holy symbol and sacred items
333
+ 6. Typical followers and clergy
334
+ 7. 3-4 key tenets or commandments
335
+ 8. Relationships with other deities
336
+ 9. Myths or legends about them
337
+ 10. How they interact with mortals
338
+
339
+ Make them feel divine and mythologically rich!"""
340
+
341
+ response = client.chat.completions.create(
342
+ model="gpt-4",
343
+ messages=[{"role": "system", "content": "You are an expert mythologist and world-builder creating rich, detailed deities for fantasy settings."},
344
+ {"role": "user", "content": prompt}],
345
+ max_tokens=600,
346
+ temperature=0.8
347
+ )
348
+
349
+ return {"success": True, "content": response.choices[0].message.content}
350
+
351
+ except Exception as e:
352
+ logger.error(f"Deity generation failed: {e}")
353
+ return {"success": False, "error": str(e), "content": f"Mock Deity: {gender} deity of {domain} with {alignment} alignment"}
354
+
355
+ def generate_pantheon(self, theme: str, size: str, culture: str) -> Dict:
356
+ """Generate an entire pantheon"""
357
+ try:
358
+ from openai import OpenAI
359
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
360
+
361
+ prompt = f"""Create a complete pantheon for D&D:
362
+ Theme: {theme}
363
+ Size: {size}
364
+ Cultural Influence: {culture}
365
+
366
+ Generate:
367
+ 1. Pantheon name and overview
368
+ 2. Creation myth
369
+ 3. 5-8 major deities with names, domains, and relationships
370
+ 4. 3-5 lesser deities or demigods
371
+ 5. Pantheon hierarchy and conflicts
372
+ 6. How mortals worship them
373
+ 7. Religious festivals and holy days
374
+ 8. Sacred sites and temples
375
+
376
+ Make it cohesive and interconnected!"""
377
+
378
+ response = client.chat.completions.create(
379
+ model="gpt-4",
380
+ messages=[{"role": "system", "content": "You are creating interconnected pantheons with rich mythological depth."},
381
+ {"role": "user", "content": prompt}],
382
+ max_tokens=800,
383
+ temperature=0.7
384
+ )
385
+
386
+ return {"success": True, "content": response.choices[0].message.content}
387
+
388
+ except Exception as e:
389
+ logger.error(f"Pantheon generation failed: {e}")
390
+ return {"success": False, "error": str(e), "content": f"Mock Pantheon: {size} {theme} pantheon inspired by {culture}"}
391
+
392
  class LootMasterAgent:
393
  """AI agent specialized in creating balanced loot and magic items"""
394
 
 
688
  dm_agent = DungeonMasterAgent()
689
  npc_agent = NPCAgent()
690
  world_agent = WorldBuilderAgent()
691
+ deity_agent = DeityAgent()
692
  loot_agent = LootMasterAgent()
693
  character_creator = CharacterCreator()
694
 
 
722
  - 🗺️ World Builder Agent
723
  - 💰 Loot Master & Magic Item Designer
724
  - 🐉 Enhanced Character Creator
725
+ - ⚡ Deity & Pantheon Generator
726
  """)
727
 
728
  with gr.Tabs():
 
840
  choices=["Minor", "Moderate", "Major", "Recurring"],
841
  label="Importance Level", value="Moderate"
842
  )
843
+ npc_gender = gr.Dropdown(
844
+ choices=["", "Male", "Female", "Non-binary", "Transgender Male", "Transgender Female", "Genderfluid", "Agender", "Other"],
845
+ label="Gender (Optional)", value=""
846
+ )
847
 
848
  create_npc_btn = gr.Button("🎭 Generate NPC", variant="primary")
849
 
 
891
 
892
  location_output = gr.Textbox(label="Location Details", lines=12, elem_classes=["output-box"])
893
 
894
+ # ===== DEITY CREATOR TAB =====
895
+ with gr.TabItem("⚡ Deity Creator"):
896
+ gr.Markdown("## ⚡ Divine Pantheon Generator", elem_classes=["agent-card"])
897
+
898
+ with gr.Tabs():
899
+ with gr.TabItem("🌟 Single Deity"):
900
+ with gr.Row():
901
+ with gr.Column():
902
+ deity_domain = gr.Dropdown(
903
+ choices=["War", "Death", "Life", "Knowledge", "Nature", "Tempest", "Trickery", "Light", "Forge", "Grave", "Order", "Peace", "Twilight"],
904
+ label="Primary Domain", value="Life"
905
+ )
906
+ deity_alignment = gr.Dropdown(
907
+ choices=[alignment.value for alignment in Alignment],
908
+ label="Alignment", value="Neutral Good"
909
+ )
910
+ deity_gender = gr.Dropdown(
911
+ choices=["Male", "Female", "Non-binary", "Genderfluid", "Agender", "Beyond Gender", "Dual-Gendered", "Other"],
912
+ label="Gender", value="Female"
913
+ )
914
+ pantheon_theme = gr.Textbox(label="Pantheon Theme", placeholder="Greek, Norse, Egyptian, Original, etc.")
915
+
916
+ generate_deity_btn = gr.Button("⚡ Generate Deity", variant="primary")
917
+ deity_art_btn = gr.Button("🎨 Generate Deity Art")
918
+
919
+ with gr.Column():
920
+ deity_image = gr.Image(label="Deity Visual", height=300)
921
+
922
+ deity_output = gr.Textbox(label="Deity Details", lines=15, elem_classes=["output-box"])
923
+
924
+ with gr.TabItem("🏛️ Full Pantheon"):
925
+ with gr.Row():
926
+ with gr.Column():
927
+ pantheon_theme_full = gr.Dropdown(
928
+ choices=["Greek-inspired", "Norse-inspired", "Egyptian-inspired", "Celtic-inspired", "Hindu-inspired", "Mesopotamian-inspired", "Original Fantasy", "Dark Pantheon", "Nature Pantheon", "Elemental Pantheon"],
929
+ label="Pantheon Style", value="Original Fantasy"
930
+ )
931
+ pantheon_size = gr.Dropdown(
932
+ choices=["Small (5-8 deities)", "Medium (9-12 deities)", "Large (13-16 deities)", "Epic (17+ deities)"],
933
+ label="Pantheon Size", value="Medium (9-12 deities)"
934
+ )
935
+ cultural_influence = gr.Textbox(label="Cultural Influence", placeholder="What mortal culture do they influence?")
936
+
937
+ generate_pantheon_btn = gr.Button("🏛️ Generate Pantheon", variant="primary")
938
+
939
+ with gr.Column():
940
+ pantheon_visual_btn = gr.Button("🖼️ Generate Pantheon Art")
941
+ pantheon_image = gr.Image(label="Pantheon Visual", height=300)
942
+
943
+ pantheon_output = gr.Textbox(label="Pantheon Details", lines=20, elem_classes=["output-box"])
944
+
945
  # ===== LOOT MASTER TAB =====
946
  with gr.TabItem("💰 Loot Master"):
947
  gr.Markdown("## 💎 Treasure & Magic Item Generator", elem_classes=["agent-card"])
 
1209
  return result.get("content", "Error generating session")
1210
 
1211
  # NPC Events
1212
+ def create_npc(context, role, importance, gender):
1213
+ result = npc_agent.generate_npc(context, role, importance, gender)
1214
  return result.get("content", "Error creating NPC")
1215
 
1216
  def generate_npc_portrait(npc_data):
1217
  if not npc_data:
1218
+ prompt = "Fantasy portrait of a D&D NPC character, detailed face, professional RPG art"
1219
+ else:
1220
+ # Extract key details from NPC description for better portraits
1221
+ prompt = f"Fantasy portrait of a D&D NPC character based on: {npc_data[:200]}..., detailed face, professional RPG art"
1222
  return generate_image(prompt)
1223
 
1224
  def npc_roleplay_response(npc_data, player_input, context):
 
1236
  prompt = f"{theme} {loc_type} fantasy location, detailed environment art, D&D setting"
1237
  return generate_image(prompt)
1238
 
1239
+ # Deity Events
1240
+ def create_deity(domain, alignment, gender, theme):
1241
+ result = deity_agent.generate_deity(domain, alignment, gender, theme)
1242
+ return result.get("content", "Error creating deity")
1243
+
1244
+ def generate_deity_art(domain, gender, theme):
1245
+ prompt = f"{gender} deity of {domain}, {theme} mythology style, divine fantasy art, glowing divine aura"
1246
+ return generate_image(prompt)
1247
+
1248
+ def create_pantheon(theme, size, culture):
1249
+ result = deity_agent.generate_pantheon(theme, size, culture)
1250
+ return result.get("content", "Error creating pantheon")
1251
+
1252
+ def generate_pantheon_art(theme, culture):
1253
+ prompt = f"{theme} pantheon temple complex, {culture} architectural style, multiple divine statues, epic fantasy art"
1254
+ return generate_image(prompt)
1255
+
1256
  # Loot Events
1257
  def create_loot_table(level, encounter, rarity):
1258
  result = loot_agent.generate_loot_table(level, encounter, rarity)
 
1365
  # NPC events
1366
  create_npc_btn.click(
1367
  create_npc,
1368
+ inputs=[npc_context, npc_role, npc_importance, npc_gender],
1369
  outputs=[npc_output]
1370
  )
1371
 
1372
  npc_portrait_btn.click(
1373
  generate_npc_portrait,
1374
+ inputs=[npc_output], # Changed from current_npc_data to npc_output
1375
  outputs=[npc_portrait]
1376
  )
1377
 
 
1394
  outputs=[location_image]
1395
  )
1396
 
1397
+ # Deity events
1398
+ generate_deity_btn.click(
1399
+ create_deity,
1400
+ inputs=[deity_domain, deity_alignment, deity_gender, pantheon_theme],
1401
+ outputs=[deity_output]
1402
+ )
1403
+
1404
+ deity_art_btn.click(
1405
+ generate_deity_art,
1406
+ inputs=[deity_domain, deity_gender, pantheon_theme],
1407
+ outputs=[deity_image]
1408
+ )
1409
+
1410
+ generate_pantheon_btn.click(
1411
+ create_pantheon,
1412
+ inputs=[pantheon_theme_full, pantheon_size, cultural_influence],
1413
+ outputs=[pantheon_output]
1414
+ )
1415
+
1416
+ pantheon_visual_btn.click(
1417
+ generate_pantheon_art,
1418
+ inputs=[pantheon_theme_full, cultural_influence],
1419
+ outputs=[pantheon_image]
1420
+ )
1421
+
1422
  # Loot events
1423
  generate_loot_btn.click(
1424
  create_loot_table,
 
1443
  gen_encounter_btn.click(generate_random_encounter, inputs=[encounter_level, encounter_difficulty], outputs=[random_encounter])
1444
  gen_hook_btn.click(generate_plot_hook, inputs=[hook_theme], outputs=[plot_hook])
1445
  gen_weather_btn.click(generate_weather, inputs=[climate], outputs=[weather])
1446
+
1447
+ # Additional missing event handlers
1448
+
1449
+ # Export character functionality
1450
+ def export_character_json(character_data):
1451
+ if not character_data:
1452
+ return None
1453
+
1454
+ try:
1455
+ # Convert character to dict for JSON export
1456
+ char_dict = {
1457
+ "name": character_data.name,
1458
+ "race": character_data.race,
1459
+ "class": character_data.character_class,
1460
+ "level": character_data.level,
1461
+ "gender": character_data.gender,
1462
+ "alignment": character_data.alignment.value,
1463
+ "abilities": character_data.abilities,
1464
+ "hit_points": character_data.hit_points,
1465
+ "skills": character_data.skills,
1466
+ "background": character_data.background,
1467
+ "backstory": character_data.backstory
1468
+ }
1469
+
1470
+ # Create temporary file
1471
+ import tempfile
1472
+ import json
1473
+
1474
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
1475
+ json.dump(char_dict, f, indent=2)
1476
+ return f.name
1477
+
1478
+ except Exception as e:
1479
+ logger.error(f"Character export failed: {e}")
1480
+ return None
1481
+
1482
+ export_btn.click(
1483
+ export_character_json,
1484
+ inputs=[character_data],
1485
+ outputs=[export_file]
1486
+ )
1487
+
1488
+ # Session notes functionality
1489
+ def save_session_notes(session_date, key_events, npc_interactions, player_actions, next_session_prep):
1490
+ try:
1491
+ import tempfile
1492
+
1493
+ notes_content = f"""# {session_date}
1494
+
1495
+ ## Key Events
1496
+ {key_events}
1497
+
1498
+ ## NPC Interactions
1499
+ {npc_interactions}
1500
+
1501
+ ## Notable Player Actions
1502
+ {player_actions}
1503
+
1504
+ ## Next Session Preparation
1505
+ {next_session_prep}
1506
+
1507
+ ---
1508
+ *Generated by D&D Campaign Manager*
1509
+ """
1510
+
1511
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
1512
+ f.write(notes_content)
1513
+ return f.name
1514
+
1515
+ except Exception as e:
1516
+ logger.error(f"Session notes save failed: {e}")
1517
+ return None
1518
+
1519
+ save_notes_btn.click(
1520
+ save_session_notes,
1521
+ inputs=[session_date, key_events, npc_interactions, player_actions, next_session_prep],
1522
+ outputs=[notes_output]
1523
+ )
1524
+
1525
+ # Initiative tracker functionality
1526
+ initiative_data = gr.State([])
1527
+
1528
+ def add_to_initiative(current_data, char_name, initiative_roll):
1529
+ if not char_name:
1530
+ return current_data
1531
+
1532
+ if current_data is None:
1533
+ current_data = []
1534
+
1535
+ # Add new character
1536
+ new_entry = [char_name, int(initiative_roll), "Full", "10", "Active"]
1537
+ current_data.append(new_entry)
1538
+
1539
+ # Sort by initiative (descending)
1540
+ current_data.sort(key=lambda x: x[1], reverse=True)
1541
+
1542
+ return current_data
1543
+
1544
+ def reset_initiative():
1545
+ return []
1546
+
1547
+ add_btn.click(
1548
+ add_to_initiative,
1549
+ inputs=[initiative_data, add_character, add_initiative],
1550
+ outputs=[initiative_data]
1551
+ )
1552
+
1553
+ # Update the dataframe when initiative_data changes
1554
+ initiative_data.change(
1555
+ lambda data: data,
1556
+ inputs=[initiative_data],
1557
+ outputs=[initiative_list]
1558
+ )
1559
+
1560
+ reset_combat_btn.click(
1561
+ reset_initiative,
1562
+ outputs=[initiative_data]
1563
+ )
1564
 
1565
  return demo
1566
 
 
1589
  except Exception as e:
1590
  logger.error(f"❌ Launch failed: {e}")
1591
  # Fallback launch for HF Spaces
1592
+ demo.launch()