plugin dev guide
Guide for plugin dev guide
Legends of Hastinapur - Plugin Development Guide
Version: 1.0
Last Updated: 2026-01-05
Plugin API Version: 1.0.0
Last Updated: 2026-01-05
Plugin API Version: 1.0.0
Table of Contents
Introduction
The Legends of Hastinapur plugin system allows you to create gameplay enhancements using Lua scripts. Plugins can track player statistics, display custom UI, respond to game events, and (in development mode) automate testing scenarios.
What Can Plugins Do?
Production Mode (Release Builds):
- Read player stats (position, health, skills)
- Query world state (nearby entities, NPCs)
- Display UI overlays (tables, progress bars, charts)
- Subscribe to game events (combat, skills, quests)
- Store persistent configuration
Development Mode (Debug Builds):
- All production features PLUS:
- Teleport player
- Spawn entities
- Modify inventory
- Set skill levels
- Automated testing capabilities
Plugin Modes
The plugin system operates in two distinct modes for security and safety:
Production Mode
When: Release builds (
Purpose: Safe, read-only gameplay enhancements
Available APIs: player., world., ui., events., config.*
cargo build --release)Purpose: Safe, read-only gameplay enhancements
Available APIs: player., world., ui., events., config.*
Development Mode
When: Debug builds (
Purpose: Testing, automation, rapid development
Available APIs: All production APIs + automation.*
cargo build)Purpose: Testing, automation, rapid development
Available APIs: All production APIs + automation.*
Mode Detection:
-- Plugins automatically receive the correct API surface
-- No code changes needed between modesGetting Started
1. Create Plugin Directory
mkdir -p plugins/my_plugin
cd plugins/my_plugin2. Create plugin.toml Manifest
[plugin]
name = "My Plugin"
version = "1.0.0"
author = "Your Name"
description = "A helpful gameplay enhancement"
[plugin.main]
script = "main.lua"3. Create main.lua Script
-- Plugin lifecycle hooks
function on_load()
ui.show_notification("My Plugin loaded!")
end
function on_tick(delta)
-- Called every frame
-- delta = time since last frame in seconds
end
function on_unload()
-- Cleanup before plugin unloads
end4. Test Your Plugin
cd ../../loh-game
cargo run
# Plugin will auto-load from plugins/ directoryPlugin API Reference
Player API
Access player state and statistics.
-- Get player position
local pos = player.position()
-- Returns: {x, y, z}
print("Player at", pos[1], pos[2], pos[3])
-- Get health
local hp = player.health()
local max_hp = player.max_health()
-- Get username
local name = player.username()
-- Get skill levels
local skills = player.skill_level
for skill_name, level in pairs(skills) do
print(skill_name, level)
end
-- Example: Fishing=15, Smithing=10, etc.World API
Query game world state.
-- Get nearby entities
local entities = world.get_nearby_entities()
for id, entity in pairs(entities) do
-- entity has: name, position, type
print(entity.name, "at", entity.position)
end
-- Get nearby NPCs
local npcs = world.get_nearby_npcs()
for id, npc in pairs(npcs) do
print(npc.name, npc.examine)
endUI API
Display custom UI overlays.
-- Show notification
ui.show_notification("Hello, World!")
-- Draw data table
local headers = {"Stat", "Value"}
local rows = {
{"Health", tostring(player.health())},
{"Position", "100, 50, 25"}
}
ui.draw_table(headers, rows)
-- Draw progress bar
ui.draw_progress_bar("XP Progress", 750, 1000)
-- Draw line chart
local data_points = {
{0, 100}, -- {x, y}
{1, 150},
{2, 175}
}
ui.draw_line_chart("DPS Over Time", data_points)Events API
Subscribe to game events.
-- Listen for combat events
events.on_combat(function(event)
local event_type = event.event_type -- "Attack", "Hit", "Death"
local attacker = event.attacker_id
local target = event.target_id
local damage = event.damage or 0
if event_type == "Hit" and attacker == "player" then
ui.show_notification("Dealt " .. damage .. " damage!")
end
end)
-- Listen for skill events
events.on_skill(function(event)
local skill = event.skill -- "Fishing", "Smithing", etc.
local xp_gained = event.xp_gained
local level_after = event.level_after
if xp_gained > 0 then
ui.show_notification("+" .. xp_gained .. " " .. skill .. " XP")
end
end)Quest API
Access quest data.
-- Get active quests
local active = quests.get_active()
for quest_id, progress in pairs(active) do
print("Quest:", quest_id, "Stage:", progress.stage_id)
end
-- Get completed quests
local completed = quests.get_completed()
-- Get quest definition
local quest_def = quests.get_definition("tutorial_quest")
if quest_def then
print(quest_def.name) -- Quest title
print(quest_def.description) -- Quest description
-- quest_def.steps = array of objective descriptions
endConfig API
Store persistent plugin data.
-- Save configuration
config.save("my_setting", "value")
config.save("total_kills", tostring(100))
-- Load configuration
local setting = config.load("my_setting")
local kills = tonumber(config.load("total_kills")) or 0
-- Config persists between game sessionsAutomation API (Development Mode Only)
⚠️ WARNING: Only available in debug builds. Will error in production.
-- Teleport player
automation.teleport(100.0, 50.0, 25.0)
-- Spawn entity
automation.spawn_entity("goblin", 100.0, 50.0, 30.0)
-- Modify inventory
automation.modify_inventory("bronze_sword", 1) -- Add 1 bronze sword
automation.modify_inventory("coins", -100) -- Remove 100 coins
-- Set skill level
automation.set_skill_level("Fishing", 50)Security Model
Sandbox Restrictions
Lua plugins run in a sandboxed environment with restricted capabilities:
Disabled Lua Functions:
os.*- No OS accessio.*- No file I/Oloadfile,dofile- No arbitrary code loadingrequire- No module loadingdebug.*- No debug manipulation
Allowed:
- String manipulation
- Table operations
- Math functions
- Basic Lua logic
- Plugin API functions
Mode Enforcement
- Compile-time: Automation APIs only compile in debug builds
- Runtime: Additional permission checks prevent production access
- API gating:
PluginModeenum enforces capability separation
Best Practices
Performance
- Cache API calls:
-- Bad: Calls API every frame function on_tick(delta) local pos = player.position() ui.draw_table(headers, {{pos[1], pos[2], pos[3]}}) end -- Good: Only update when changed local last_pos = nil function on_tick(delta) local pos = player.position() if pos ~= last_pos then ui.draw_table(headers, {{pos[1], pos[2], pos[3]}}) last_pos = pos end end - Limit UI commands: Don't draw excessive UI elements every frame
- Use events over polling: Subscribe to events instead of checking state in
on_tick
Error Handling
function on_tick(delta)
local success, error = pcall(function()
-- Your code here
local pos = player.position()
end)
if not success then
print("Error:", error)
end
endData Persistence
-- Save state on unload
function on_unload()
config.save("session_xp", tostring(total_xp))
config.save("start_time", tostring(os.time()))
end
-- Load state on startup
function on_load()
total_xp = tonumber(config.load("session_xp")) or 0
start_time = tonumber(config.load("start_time")) or os.time()
endExamples
See the
loh-plugins/ directory for complete examples:Basic Examples
- xp_tracker.lua - Track total XP gains with notifications
- player_info.lua - Display player stats in real-time
- nearby_entities.lua - Show nearby NPCs and entities
Advanced Examples
- skill_calculator.lua - Calculate XP/hour and time-to-level
- combat_stats.lua - Track DPS, hits landed, damage taken
- loot_tracker.lua - Monitor loot drops and GP/hour
- quest_helper.lua - Display active quest objectives
- farming_timer.lua - Track farming patch grow times
- idle_notifier.lua - Alert when player inactive
Example: Simple XP Tracker
local total_xp = 0
function on_load()
total_xp = tonumber(config.load("total_xp")) or 0
ui.show_notification("XP Tracker loaded! Total: " .. total_xp)
events.on_skill(function(event)
if event.xp_gained > 0 then
total_xp = total_xp + event.xp_gained
ui.show_notification("+" .. event.xp_gained .. " XP!")
end
end)
end
function on_tick(delta)
local headers = {"Total XP"}
local rows = {{tostring(total_xp)}}
ui.draw_table(headers, rows)
end
function on_unload()
config.save("total_xp", tostring(total_xp))
endTroubleshooting
Plugin Doesn't Load
- Check
plugin.tomlis valid TOML format - Verify
main.luaexists and has no syntax errors - Check game console for error messages
- Ensure plugin directory is in
plugins/
API Function Not Found
- Verify you're calling the correct API (check spelling)
- Ensure you're using current API (not old deprecated functions)
- Development-only APIs require debug build
Performance Issues
- Reduce UI draw calls (cache and update only when changed)
- Use events instead of polling in
on_tick - Profile with
print()statements to find bottlenecks
Migration from Old API
If you have plugins using the old API format (
api.ui.draw_text()), update them to use the current API:Old API (Deprecated):
api.ui.draw_text(100, 100, "Hello", {1, 1, 1, 1})
api.ui.draw_rect(10, 10, 200, 50, {0, 0, 0, 0.5})
api.events.on("ExperienceGained", callback)New API (Current):
ui.show_notification("Hello")
ui.draw_table(headers, rows)
events.on_skill(callback)Benefits of new API:
- Higher level abstractions
- No coordinate management needed
- Type-safe event subscriptions
- Easier to use and maintain
Support
- Examples:
/loh-plugins/directory - Issues: File bug reports on project repository
- Community: Share plugins and get help
Happy Plugin Development! 🎮