eq2go/internal/items/loot/README.md

11 KiB

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

  1. Table Selection: Get loot tables assigned to spawn + applicable global tables
  2. Drop Probability: Roll against lootdrop_probability to determine if loot drops
  3. Coin Generation: If coin_probability succeeds, generate random coins between min/max
  4. Item Processing: For each loot drop:
    • Check quest requirements
    • Roll against item probability
    • Generate item instance with specified charges
    • Stop when maxlootitems reached
  5. 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.