EverQuest II Loot System
This package implements a comprehensive loot generation and management system for the EverQuest II server emulator, converted from the original C++ implementation.
Overview
The loot system handles:
- Loot Generation: Probability-based item and coin generation from configurable loot tables
- Treasure Chests: Physical chest spawns with tier-appropriate appearances
- Global Loot: Level, race, and zone-based loot assignments
- Loot Distribution: Group loot methods and player rights management
- Database Integration: Persistent loot table and assignment storage
- Client Communication: Version-specific packet building for loot windows
Architecture
Core Components
LootDatabase (database.go
)
- Manages all database operations for loot tables, drops, and assignments
- Caches loot data in memory for performance
- Supports real-time updates and reloading
LootManager (manager.go
)
- Central coordinator for loot generation and chest management
- Implements probability-based loot algorithms
- Handles treasure chest lifecycle and cleanup
ChestService (chest.go
)
- Manages treasure chest interactions (view, loot, disarm, lockpick)
- Validates player permissions and positioning
- Integrates with player and zone services
LootPacketBuilder (packets.go
)
- Builds client-specific packets for loot communication
- Supports multiple client versions (v1 through v60114+)
- Handles loot window updates and interaction responses
LootSystem (integration.go
)
- High-level integration layer combining all components
- Provides simplified API for common operations
- Manages system lifecycle and configuration
Data Structures
LootTable
type LootTable struct {
ID int32 // Unique table identifier
Name string // Descriptive name
MinCoin int32 // Minimum coin drop
MaxCoin int32 // Maximum coin drop
MaxLootItems int16 // Maximum items per loot
LootDropProbability float32 // Chance for loot to drop (0-100%)
CoinProbability float32 // Chance for coins to drop (0-100%)
Drops []*LootDrop // Individual item drops
}
LootDrop
type LootDrop struct {
LootTableID int32 // Parent table ID
ItemID int32 // Item to drop
ItemCharges int16 // Item stack size/charges
EquipItem bool // Auto-equip item
Probability float32 // Drop chance (0-100%)
NoDropQuestCompletedID int32 // Required quest completion
}
TreasureChest
type TreasureChest struct {
ID int32 // Unique chest ID
SpawnID int32 // Source spawn ID
ZoneID int32 // Zone location
X, Y, Z float32 // Position coordinates
Heading float32 // Orientation
AppearanceID int32 // Visual appearance
LootResult *LootResult // Contained items and coins
Created time.Time // Creation timestamp
LootRights []uint32 // Players with access rights
IsDisarmable bool // Can be disarmed
IsLocked bool // Requires lockpicking
}
Database Schema
Core Tables
loottable
CREATE TABLE loottable (
id INTEGER PRIMARY KEY,
name TEXT,
mincoin INTEGER DEFAULT 0,
maxcoin INTEGER DEFAULT 0,
maxlootitems INTEGER DEFAULT 6,
lootdrop_probability REAL DEFAULT 100.0,
coin_probability REAL DEFAULT 50.0
);
lootdrop
CREATE TABLE lootdrop (
loot_table_id INTEGER,
item_id INTEGER,
item_charges INTEGER DEFAULT 1,
equip_item INTEGER DEFAULT 0,
probability REAL DEFAULT 100.0,
no_drop_quest_completed_id INTEGER DEFAULT 0
);
spawn_loot
CREATE TABLE spawn_loot (
spawn_id INTEGER,
loottable_id INTEGER
);
loot_global
CREATE TABLE loot_global (
type TEXT, -- 'level', 'race', or 'zone'
loot_table INTEGER, -- Target loot table ID
value1 INTEGER, -- Min level, race ID, or zone ID
value2 INTEGER, -- Max level (for level type)
value3 INTEGER, -- Loot tier
value4 INTEGER -- Reserved
);
Usage Examples
Basic Setup
// Create loot system
config := &LootSystemConfig{
DatabaseConnection: db,
ItemMasterList: itemMasterList,
PlayerService: playerService,
ZoneService: zoneService,
ClientService: clientService,
ItemPacketBuilder: itemPacketBuilder,
StartCleanupTimer: true,
}
lootSystem, err := NewLootSystem(config)
if err != nil {
log.Fatal(err)
}
Generate Loot and Create Chest
// Create loot context
context := &LootContext{
PlayerLevel: 25,
PlayerRace: 1,
ZoneID: 100,
KillerID: playerID,
GroupMembers: []uint32{playerID},
CompletedQuests: playerQuests,
LootMethod: GroupLootMethodFreeForAll,
}
// Generate loot and create chest
chest, err := lootSystem.GenerateAndCreateChest(
spawnID, zoneID, x, y, z, heading, context)
if err != nil {
log.Printf("Failed to create loot chest: %v", err)
}
Handle Player Loot Interaction
// Player opens chest
err := lootSystem.ShowChestToPlayer(chestID, playerID)
// Player loots specific item
err = lootSystem.HandlePlayerLootInteraction(
chestID, playerID, ChestInteractionLoot, itemUniqueID)
// Player loots everything
err = lootSystem.HandlePlayerLootInteraction(
chestID, playerID, ChestInteractionLootAll, 0)
Create Loot Tables
// Create a simple loot table
items := []QuickLootItem{
{ItemID: 1001, Charges: 1, Probability: 100.0, AutoEquip: false},
{ItemID: 1002, Charges: 5, Probability: 50.0, AutoEquip: false},
{ItemID: 1003, Charges: 1, Probability: 25.0, AutoEquip: true},
}
err := lootSystem.CreateQuickLootTable(
tableID, "Orc Warrior Loot", items, 10, 50, 3)
// Assign to spawns
spawnIDs := []int32{2001, 2002, 2003}
err = lootSystem.AssignLootToSpawns(tableID, spawnIDs)
Chest Appearances
Chest appearance is automatically selected based on the highest tier item:
Tier Range | Appearance | Chest Type |
---|---|---|
1-2 (Common-Uncommon) | 4034 | Small Chest |
3-4 (Treasured-Rare) | 5864 | Treasure Chest |
5-6 (Legendary-Fabled) | 5865 | Ornate Chest |
7+ (Mythical+) | 4015 | Exquisite Chest |
Loot Generation Algorithm
- Table Selection: Get loot tables assigned to spawn + applicable global tables
- Drop Probability: Roll against
lootdrop_probability
to determine if loot drops - Coin Generation: If
coin_probability
succeeds, generate random coins between min/max - Item Processing: For each loot drop:
- Check quest requirements
- Roll against item probability
- Generate item instance with specified charges
- Stop when
maxlootitems
reached
- Chest Creation: If items qualify (tier >= Common), create treasure chest
Global Loot System
Global loot provides automatic loot assignment based on:
Level-Based Loot
err := lootSystem.CreateGlobalLevelLoot(10, 20, tableID, LootTierCommon)
Race-Based Loot
INSERT INTO loot_global (type, loot_table, value1, value2)
VALUES ('race', 100, 1, 0); -- Human racial loot
Zone-Based Loot
INSERT INTO loot_global (type, loot_table, value1, value2)
VALUES ('zone', 200, 150, 0); -- Zone 150 specific loot
Group Loot Methods
The system supports various loot distribution methods:
- Free For All: Anyone can loot anything
- Round Robin: Items distributed in turn order
- Master Looter: Designated player distributes loot
- Need/Greed: Players roll need or greed for items
- Lotto: Random distribution for high-tier items
Statistics and Monitoring
// Get comprehensive statistics
stats, err := lootSystem.GetSystemStatistics()
// Check loot generation stats
genStats := lootSystem.Manager.GetStatistics()
fmt.Printf("Total loots: %d, Average items per loot: %.2f",
genStats.TotalLoots, genStats.AverageItemsPerLoot)
Validation and Debugging
// Validate all items in loot tables exist
errors := lootSystem.ValidateItemsInLootTables()
for _, err := range errors {
fmt.Printf("Validation error: %s\n", err.Description)
}
// Preview potential loot without generating
preview, err := lootSystem.GetLootPreview(spawnID, context)
fmt.Printf("Possible items: %d, Coin range: %d-%d",
len(preview.PossibleItems), preview.MinCoins, preview.MaxCoins)
Performance Considerations
- Memory Caching: All loot tables are cached in memory for fast access
- Prepared Statements: Database queries use prepared statements for efficiency
- Concurrent Safety: All operations are thread-safe with proper mutex usage
- Cleanup Timers: Automatic cleanup of expired chests prevents memory leaks
- Batch Operations: Support for bulk loot table and spawn assignments
Client Version Compatibility
The packet building system supports multiple EverQuest II client versions:
- Version 1: Basic loot display (oldest clients)
- Version 373: Added item type and icon support
- Version 546: Enhanced item appearance data
- Version 1193: Heirloom and no-trade flags
- Version 60114: Full modern feature set with adornments
Migration from C++
This Go implementation maintains full compatibility with the original C++ EQ2EMu loot system:
- Database Schema: Identical table structure and data
- Loot Algorithms: Same probability calculations and item selection
- Chest Logic: Equivalent chest appearance and interaction rules
- Global Loot: Compatible global loot table processing
- Packet Format: Maintains client protocol compatibility
Testing
Comprehensive test suite covers:
- Loot generation algorithms
- Database operations
- Chest interactions
- Packet building
- Statistics tracking
- Performance benchmarks
Run tests with:
go test ./internal/items/loot/...
Configuration
Key configuration constants in constants.go
:
DefaultMaxLootItems
: Maximum items per loot (default: 6)ChestDespawnTime
: Empty chest despawn time (5 minutes)ChestCleanupTime
: Force cleanup time (10 minutes)LootTierCommon
: Minimum tier for chest creation
Error Handling
The system provides detailed error reporting for:
- Missing loot tables or items
- Invalid player permissions
- Database connection issues
- Packet building failures
- Chest interaction violations
All errors are logged with appropriate prefixes ([LOOT]
, [CHEST]
, [LOOT-DB]
) for easy debugging.