Modding Quick Start¶
This guide will get you creating mods for Yendoria in just a few minutes. If you’re new to modding or want a comprehensive tutorial, see the Modding Tutorial.
Installation & Setup¶
Ensure Yendoria is Working:
poetry install poetry run python -m yendoria
Create Your Mod Directory:
mkdir my_mods cd my_mods
Test the Event System:
poetry run python examples/mods/event_system_demo.py
Your First Mod in 5 Minutes¶
Let’s create a simple mod that adds flavor text to the game.
Step 1: Create the Mod File
Create atmosphere_mod.py
:
from yendoria.modding import EventBus, EventType, GameEvent
import random
class AtmosphereMod:
"""Adds atmospheric flavor text to the game."""
def __init__(self, event_bus: EventBus):
self.event_bus = event_bus
self.event_bus.subscribe(EventType.LEVEL_GENERATE, self.on_level_generate)
self.event_bus.subscribe(EventType.COMBAT_START, self.on_combat_start)
self.event_bus.subscribe(EventType.ENTITY_DEATH, self.on_entity_death)
def on_level_generate(self, event: GameEvent) -> None:
"""Add atmospheric descriptions to new levels."""
atmospheres = [
"🌫️ A damp mist clings to the dungeon walls...",
"🔥 The air shimmers with heat from unknown sources.",
"❄️ A bone-chilling cold permeates this level.",
"🌟 Strange lights flicker in the darkness.",
"💀 The stench of death hangs heavy here."
]
print(random.choice(atmospheres))
def on_combat_start(self, event: GameEvent) -> None:
"""Add dramatic combat descriptions."""
combat_lines = [
"⚔️ Steel rings against claw!",
"🛡️ Battle is joined in the depths!",
"⚡ Lightning-quick strikes fill the air!",
"🔥 The fury of combat erupts!"
]
print(random.choice(combat_lines))
def on_entity_death(self, event: GameEvent) -> None:
"""Add dramatic death descriptions."""
entity = event.data.get("entity")
if entity and not hasattr(entity, "is_player"):
death_lines = [
f"💀 {entity.name} falls with a final, echoing cry.",
f"⚰️ {entity.name} collapses into the shadows.",
f"👻 {entity.name}'s spirit departs this realm.",
f"🌪️ {entity.name} is vanquished in a swirl of dust."
]
print(random.choice(death_lines))
Step 2: Test Your Mod
# test_atmosphere.py
if __name__ == "__main__":
from yendoria.modding import EventBus, EventType
from atmosphere_mod import AtmosphereMod
# Create event bus and mod
event_bus = EventBus()
mod = AtmosphereMod(event_bus)
# Test level generation
print("🧪 Testing level generation...")
event_bus.emit_simple(EventType.LEVEL_GENERATE, {"rooms": []})
# Test combat
print("\\n🧪 Testing combat...")
event_bus.emit_simple(EventType.COMBAT_START, {
"attacker": type('Player', (), {})(),
"defender": type('Orc', (), {})()
})
# Test death
print("\\n🧪 Testing death...")
event_bus.emit_simple(EventType.ENTITY_DEATH, {
"entity": type('Orc', (), {"name": "Fierce Orc"})()
})
print("\\n✅ Atmosphere mod test complete!")
Run your test:
python test_atmosphere.py
Step 3: Integrate Into Game
To use your mod in the actual game, you would need to integrate it into the game engine. This is currently a manual process (automatic mod loading is planned for Phase 2):
# In the game's main initialization code:
from atmosphere_mod import AtmosphereMod
# After creating the engine:
atmosphere_mod = AtmosphereMod(engine.event_bus)
Common Mod Patterns¶
Statistics Tracking¶
class StatsTracker:
def __init__(self, event_bus: EventBus):
self.kills = 0
self.steps = 0
event_bus.subscribe(EventType.ENTITY_DEATH, self.count_kills)
event_bus.subscribe(EventType.ENTITY_MOVE, self.count_steps)
def count_kills(self, event: GameEvent) -> None:
killer = event.data.get("killer")
if hasattr(killer, "is_player") and killer.is_player:
self.kills += 1
def count_steps(self, event: GameEvent) -> None:
if event.data.get("is_player", False):
self.steps += 1
Gameplay Modification¶
class LuckSystem:
def __init__(self, event_bus: EventBus):
self.luck = 0
event_bus.subscribe(EventType.COMBAT_START, self.apply_luck)
def apply_luck(self, event: GameEvent) -> None:
attacker = event.data.get("attacker")
if hasattr(attacker, "is_player") and attacker.is_player:
if self.luck < -50:
# Very unlucky - sometimes avoid combat
if random.random() < 0.1:
event.cancel()
print("🍀 You stumble and avoid the fight!")
Event Cancellation¶
def peaceful_mode(event: GameEvent) -> None:
"""Cancel all combat events for peaceful gameplay."""
event.cancel()
print("☮️ Combat avoided - peaceful mode active!")
# Register the handler
event_bus.subscribe(EventType.COMBAT_START, peaceful_mode)
Available Events Reference¶
Entity Events:
- ENTITY_SPAWN
- When entities are created
- ENTITY_MOVE
- When entities move
- ENTITY_DEATH
- When entities die
Combat Events:
- COMBAT_START
- Combat begins (cancellable)
- COMBAT_HIT
- Successful attacks
Game Flow Events:
- TURN_START
- Beginning of each turn
- TURN_END
- End of each turn
World Events:
- LEVEL_GENERATE
- New level created
For complete event details, see Modding System.
Next Steps¶
Learn More: Read the Modding Tutorial for comprehensive examples
Advanced Features: Check the Modding Roadmap for upcoming capabilities
Technical Details: See Modding System for complete API documentation
Examples: Study
examples/mods/event_system_demo.py
for practical patterns
Troubleshooting¶
- Mod not responding to events?
Check that you’re subscribing to the correct event type
Verify the event is actually being emitted during gameplay
Ensure your handler function signature is correct:
(self, event: GameEvent) -> None
- Type errors?
Remember event data is
dict[str, Any]
- always validate typesUse
event.data.get("key")
instead ofevent.data["key"]
Check the event documentation for expected data fields
- Need help?
Review the comprehensive examples in the tutorial
Check the modding architecture documentation
Examine the core event system implementation