357 lines
11 KiB
Markdown
357 lines
11 KiB
Markdown
# 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
|
|
```go
|
|
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
|
|
```go
|
|
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
|
|
```go
|
|
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
|
|
```sql
|
|
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
|
|
```sql
|
|
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
|
|
```sql
|
|
CREATE TABLE spawn_loot (
|
|
spawn_id INTEGER,
|
|
loottable_id INTEGER
|
|
);
|
|
```
|
|
|
|
#### loot_global
|
|
```sql
|
|
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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
```go
|
|
err := lootSystem.CreateGlobalLevelLoot(10, 20, tableID, LootTierCommon)
|
|
```
|
|
|
|
### Race-Based Loot
|
|
```sql
|
|
INSERT INTO loot_global (type, loot_table, value1, value2)
|
|
VALUES ('race', 100, 1, 0); -- Human racial loot
|
|
```
|
|
|
|
### Zone-Based Loot
|
|
```sql
|
|
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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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:
|
|
```bash
|
|
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. |