1
0

big changes

This commit is contained in:
Sky Johnson 2025-09-02 20:25:42 -05:00
parent 321f82c96a
commit 46642d41d0
570 changed files with 164932 additions and 39151 deletions

View File

@ -1,286 +0,0 @@
package eq2net
import (
"encoding/binary"
"fmt"
)
// AppPacket handles application-level game packets with opcode abstraction
// This layer sits above the protocol layer and handles game-specific opcodes
type AppPacket struct {
EQPacket
EmuOpcode uint16 // Emulator opcode (internal representation)
OpcodeSize uint8 // Size of opcode in bytes (1 or 2)
}
// Default opcode size for application packets
var DefaultAppOpcodeSize uint8 = 2
// NewAppPacket creates a new application packet with emulator opcode
func NewAppPacket(emuOpcode uint16, data []byte) *AppPacket {
p := &AppPacket{
EQPacket: *NewEQPacket(0, data), // Network opcode will be set during conversion
EmuOpcode: emuOpcode,
OpcodeSize: DefaultAppOpcodeSize,
}
return p
}
// NewAppPacketWithSize creates an application packet with specified opcode size
func NewAppPacketWithSize(emuOpcode uint16, data []byte, opcodeSize uint8) *AppPacket {
p := &AppPacket{
EQPacket: *NewEQPacket(0, data),
EmuOpcode: emuOpcode,
OpcodeSize: opcodeSize,
}
return p
}
// ParseAppPacket creates an application packet from raw data
// The data should NOT include the protocol header, just the app opcode + payload
func ParseAppPacket(data []byte, opcodeSize uint8) (*AppPacket, error) {
if len(data) < int(opcodeSize) {
return nil, fmt.Errorf("packet too small for opcode: need %d bytes, got %d", opcodeSize, len(data))
}
// Extract opcode
var opcode uint16
if opcodeSize == 1 {
opcode = uint16(data[0])
} else {
// Handle special encoding for 2-byte opcodes
if data[0] == 0x00 && len(data) > 2 {
// Special case: extra 0x00 prefix for opcodes with low byte = 0x00
opcode = binary.BigEndian.Uint16(data[1:3])
data = data[3:] // Skip the extra byte
} else {
opcode = binary.BigEndian.Uint16(data[0:2])
data = data[2:]
}
}
p := &AppPacket{
EQPacket: *NewEQPacket(opcode, data[opcodeSize:]),
EmuOpcode: opcode, // Initially same as network opcode
OpcodeSize: opcodeSize,
}
return p, nil
}
// SetEmuOpcode sets the emulator opcode
// This is used when converting between emulator and network opcodes
func (p *AppPacket) SetEmuOpcode(opcode uint16) {
p.EmuOpcode = opcode
}
// GetEmuOpcode returns the emulator opcode
func (p *AppPacket) GetEmuOpcode() uint16 {
return p.EmuOpcode
}
// SerializeApp serializes the application packet with proper opcode encoding
func (p *AppPacket) SerializeApp() []byte {
opcodeBytes := p.OpcodeSize
extraBytes := 0
// Handle special encoding rules for 2-byte opcodes
if p.OpcodeSize == 2 && (p.Opcode&0x00FF) == 0 {
// Opcodes with low byte = 0x00 need an extra 0x00 prefix
extraBytes = 1
}
// Create buffer
buf := make([]byte, int(opcodeBytes)+extraBytes+len(p.Buffer))
offset := 0
// Write opcode
if p.OpcodeSize == 1 {
buf[offset] = byte(p.Opcode)
offset++
} else {
if extraBytes > 0 {
// Special encoding: add 0x00 prefix
buf[offset] = 0x00
offset++
binary.BigEndian.PutUint16(buf[offset:], p.Opcode)
offset += 2
} else {
binary.BigEndian.PutUint16(buf[offset:], p.Opcode)
offset += 2
}
}
// Copy packet data
if len(p.Buffer) > 0 {
copy(buf[offset:], p.Buffer)
}
return buf
}
// Combine combines multiple application packets into a single packet
// Returns true if successful, false if size limit exceeded
func (p *AppPacket) Combine(other *AppPacket) bool {
// Create a new buffer with both packets
myData := p.SerializeApp()
otherData := other.SerializeApp()
// Check size limit (application packets can be larger than protocol)
if len(myData)+len(otherData) > 8192 { // Reasonable max size
return false
}
// Combine the data
newBuffer := make([]byte, len(myData)+len(otherData))
copy(newBuffer, myData)
copy(newBuffer[len(myData):], otherData)
// Update packet
p.Buffer = newBuffer
return true
}
// Copy creates a deep copy of the application packet
func (p *AppPacket) Copy() *AppPacket {
newPacket := &AppPacket{
EQPacket: *p.Clone(),
EmuOpcode: p.EmuOpcode,
OpcodeSize: p.OpcodeSize,
}
return newPacket
}
// SetVersion sets the protocol version for opcode conversion
func (p *AppPacket) SetVersion(version int16) {
p.Version = version
}
// ConvertToProtocolPacket wraps this application packet in a protocol packet
// This is used when sending application data over the network
func (p *AppPacket) ConvertToProtocolPacket() *ProtocolPacket {
// Serialize the application packet
appData := p.SerializeApp()
// Create protocol packet with OP_Packet opcode
proto := &ProtocolPacket{
EQPacket: *NewEQPacket(OP_Packet, appData),
}
// Copy network info
proto.SrcIP = p.SrcIP
proto.SrcPort = p.SrcPort
proto.DstIP = p.DstIP
proto.DstPort = p.DstPort
proto.Timestamp = p.Timestamp
proto.Version = p.Version
return proto
}
// AppCombine performs application-level packet combining (EQ2-specific)
// This is different from protocol-level combining
func AppCombine(packets []*AppPacket) *AppPacket {
if len(packets) == 0 {
return nil
}
if len(packets) == 1 {
return packets[0]
}
// Calculate total size needed
var totalSize int
for _, p := range packets {
data := p.SerializeApp()
if len(data) >= 255 {
totalSize += 3 + len(data) // 0xFF marker + 2-byte size + data
} else {
totalSize += 1 + len(data) // 1-byte size + data
}
}
// Build combined buffer
buffer := make([]byte, totalSize)
offset := 0
for _, p := range packets {
data := p.SerializeApp()
size := len(data)
if size >= 255 {
// Oversized packet: use 0xFF marker followed by 2-byte size
buffer[offset] = 0xFF
offset++
binary.BigEndian.PutUint16(buffer[offset:], uint16(size))
offset += 2
} else {
// Normal packet: 1-byte size
buffer[offset] = byte(size)
offset++
}
// Copy packet data
copy(buffer[offset:], data)
offset += size
}
// Create combined packet with OP_AppCombined
combined := &AppPacket{
EQPacket: *NewEQPacket(OP_AppCombined, buffer),
OpcodeSize: DefaultAppOpcodeSize,
}
return combined
}
// ExtractAppPackets extracts individual packets from an app-combined packet
func ExtractAppPackets(combined *AppPacket) ([]*AppPacket, error) {
if combined.Opcode != OP_AppCombined {
return nil, fmt.Errorf("not an app-combined packet")
}
var packets []*AppPacket
data := combined.Buffer
offset := 0
for offset < len(data) {
if offset >= len(data) {
break
}
var size int
// Read size
if data[offset] == 0xFF {
// Oversized packet
if offset+3 > len(data) {
return nil, fmt.Errorf("invalid oversized packet header")
}
offset++
size = int(binary.BigEndian.Uint16(data[offset:]))
offset += 2
} else {
// Normal packet
size = int(data[offset])
offset++
}
// Extract packet data
if offset+size > len(data) {
return nil, fmt.Errorf("packet size exceeds buffer")
}
packetData := data[offset : offset+size]
offset += size
// Parse the packet
app, err := ParseAppPacket(packetData, combined.OpcodeSize)
if err != nil {
return nil, fmt.Errorf("failed to parse app packet: %w", err)
}
packets = append(packets, app)
}
return packets, nil
}

365
game_packet.go Normal file
View File

@ -0,0 +1,365 @@
package eq2net
import (
"encoding/binary"
"fmt"
)
// GameEmuOpcode represents emulator opcodes for the game server
type GameEmuOpcode uint16
// Game server emulator opcodes (partial list - there are hundreds)
const (
GOP_Unknown GameEmuOpcode = iota
GOP_LoginReplyMsg
GOP_LoginByNumRequestMsg
GOP_WSLoginRequestMsg
GOP_ESInitMsg
GOP_ESReadyForClientsMsg
GOP_CreateZoneInstanceMsg
GOP_ZoneInstanceCreateReplyMsg
GOP_ZoneInstanceDestroyedMsg
GOP_ExpectClientAsCharacterRequest
GOP_ExpectClientAsCharacterReplyMs
GOP_ZoneInfoMsg
GOP_CreateCharacterRequestMsg
GOP_DoneLoadingZoneResourcesMsg
GOP_DoneSendingInitialEntitiesMsg
GOP_DoneLoadingEntityResourcesMsg
GOP_DoneLoadingUIResourcesMsg
GOP_PredictionUpdateMsg
GOP_RemoteCmdMsg
GOP_SetRemoteCmdsMsg
GOP_GameWorldTimeMsg
GOP_MOTDMsg
GOP_ZoneMOTDMsg
GOP_GuildRecruitingMemberInfo
GOP_GuildRecruiting
GOP_GuildRecruitingDetails
GOP_GuildRecruitingImage
GOP_AvatarCreatedMsg
GOP_AvatarDestroyedMsg
GOP_RequestCampMsg
GOP_MapRequest
GOP_CampStartedMsg
GOP_CampAbortedMsg
GOP_WhoQueryRequestMsg
GOP_WhoQueryReplyMsg
GOP_RemoveClientFromGroupMsg
GOP_GroupCreatedMsg
GOP_GroupDestroyedMsg
GOP_GroupMemberAddedMsg
GOP_GroupMemberRemovedMsg
GOP_GroupInfoRequestMsg
GOP_GroupInfoUpdateMsg
GOP_GroupRemovedFromGroupMsg
GOP_GroupLeaderChangedMsg
GOP_GroupOptionsMsg
GOP_UpdateDataMsg
GOP_UpdateSpawnMsg
GOP_UpdateCharacterSheetMsg
GOP_UpdateSkillsMsg
GOP_UpdateQuestMsg
GOP_UpdateInventoryMsg
GOP_UpdatePositionMsg
GOP_UpdateRaidMsg
GOP_UpdateTradeMsg
GOP_UpdateTargetMsg
GOP_UpdateTargetLocMsg
GOP_UpdateActorTargetMsg
GOP_UpdatePlayerMailMsg
GOP_UpdatePlayerMail
GOP_UpdateTimeMsg
GOP_MaxGameOpcode // Add more as needed
)
// GamePacket represents a game server packet
type GamePacket struct {
EQPacket // Embed base packet
GameEmuOpcode GameEmuOpcode // Game server emulator opcode
}
// NewGamePacket creates a new game server packet
func NewGamePacket(opcode GameEmuOpcode, data []byte) *GamePacket {
base := NewEQPacket(uint16(opcode), data)
base.EmuOpcode = uint16(opcode)
base.OpcodeSize = 2 // Game server uses 2-byte opcodes
p := &GamePacket{
EQPacket: *base,
GameEmuOpcode: opcode,
}
return p
}
// ParseGamePacket creates a game packet from raw data
func ParseGamePacket(data []byte) (*GamePacket, error) {
if len(data) < 2 {
return nil, fmt.Errorf("packet too small for game opcode")
}
// Extract 2-byte opcode
var opcode GameEmuOpcode
var remainingData []byte
// Handle special encoding for 2-byte opcodes
// Special case: When we have 3+ bytes and first is 0x00 and third is 0x00,
// it's the special encoding for opcodes with low byte = 0x00
if len(data) > 2 && data[0] == 0x00 && data[2] == 0x00 {
// Extra 0x00 prefix for opcodes like 0x1200
opcode = GameEmuOpcode(binary.BigEndian.Uint16(data[1:3]))
remainingData = data[3:] // Skip the extra byte
} else {
opcode = GameEmuOpcode(binary.BigEndian.Uint16(data[0:2]))
remainingData = data[2:]
}
// Create packet with remaining data
base := NewEQPacket(uint16(opcode), remainingData)
base.EmuOpcode = uint16(opcode)
base.OpcodeSize = 2
p := &GamePacket{
EQPacket: *base,
GameEmuOpcode: opcode,
}
return p, nil
}
// GetGameOpcode returns the game emulator opcode
func (p *GamePacket) GetGameOpcode() GameEmuOpcode {
return p.GameEmuOpcode
}
// SetGameOpcode sets the game emulator opcode
func (p *GamePacket) SetGameOpcode(opcode GameEmuOpcode) {
p.GameEmuOpcode = opcode
p.EmuOpcode = uint16(opcode)
p.Opcode = uint16(opcode)
}
// SerializeGame serializes the game packet with proper encoding
func (p *GamePacket) SerializeGame() []byte {
// Handle special encoding rules for 2-byte opcodes
opcodeBytes := 2
extraBytes := 0
if (uint16(p.GameEmuOpcode) & 0x00FF) == 0 {
// Opcodes with low byte = 0x00 need an extra 0x00 prefix
extraBytes = 1
}
// Create buffer
buf := make([]byte, opcodeBytes+extraBytes+len(p.Buffer))
offset := 0
// Write opcode
if extraBytes > 0 {
// Special encoding: add 0x00 prefix
buf[offset] = 0x00
offset++
binary.BigEndian.PutUint16(buf[offset:], uint16(p.GameEmuOpcode))
offset += 2
} else {
binary.BigEndian.PutUint16(buf[offset:], uint16(p.GameEmuOpcode))
offset += 2
}
// Copy packet data
if len(p.Buffer) > 0 {
copy(buf[offset:], p.Buffer)
}
return buf
}
// GetGameOpcodeName returns the string name of a game emulator opcode
func GetGameOpcodeName(opcode GameEmuOpcode) string {
switch opcode {
case GOP_Unknown:
return "GOP_Unknown"
case GOP_LoginReplyMsg:
return "GOP_LoginReplyMsg"
case GOP_LoginByNumRequestMsg:
return "GOP_LoginByNumRequestMsg"
case GOP_WSLoginRequestMsg:
return "GOP_WSLoginRequestMsg"
case GOP_ESInitMsg:
return "GOP_ESInitMsg"
case GOP_ESReadyForClientsMsg:
return "GOP_ESReadyForClientsMsg"
case GOP_CreateZoneInstanceMsg:
return "GOP_CreateZoneInstanceMsg"
case GOP_ZoneInstanceCreateReplyMsg:
return "GOP_ZoneInstanceCreateReplyMsg"
case GOP_ZoneInstanceDestroyedMsg:
return "GOP_ZoneInstanceDestroyedMsg"
case GOP_ExpectClientAsCharacterRequest:
return "GOP_ExpectClientAsCharacterRequest"
case GOP_ExpectClientAsCharacterReplyMs:
return "GOP_ExpectClientAsCharacterReplyMs"
case GOP_ZoneInfoMsg:
return "GOP_ZoneInfoMsg"
case GOP_CreateCharacterRequestMsg:
return "GOP_CreateCharacterRequestMsg"
case GOP_DoneLoadingZoneResourcesMsg:
return "GOP_DoneLoadingZoneResourcesMsg"
case GOP_DoneSendingInitialEntitiesMsg:
return "GOP_DoneSendingInitialEntitiesMsg"
case GOP_DoneLoadingEntityResourcesMsg:
return "GOP_DoneLoadingEntityResourcesMsg"
case GOP_DoneLoadingUIResourcesMsg:
return "GOP_DoneLoadingUIResourcesMsg"
case GOP_PredictionUpdateMsg:
return "GOP_PredictionUpdateMsg"
case GOP_RemoteCmdMsg:
return "GOP_RemoteCmdMsg"
case GOP_SetRemoteCmdsMsg:
return "GOP_SetRemoteCmdsMsg"
case GOP_GameWorldTimeMsg:
return "GOP_GameWorldTimeMsg"
case GOP_MOTDMsg:
return "GOP_MOTDMsg"
case GOP_ZoneMOTDMsg:
return "GOP_ZoneMOTDMsg"
case GOP_GuildRecruitingMemberInfo:
return "GOP_GuildRecruitingMemberInfo"
case GOP_GuildRecruiting:
return "GOP_GuildRecruiting"
case GOP_GuildRecruitingDetails:
return "GOP_GuildRecruitingDetails"
case GOP_GuildRecruitingImage:
return "GOP_GuildRecruitingImage"
case GOP_AvatarCreatedMsg:
return "GOP_AvatarCreatedMsg"
case GOP_AvatarDestroyedMsg:
return "GOP_AvatarDestroyedMsg"
case GOP_RequestCampMsg:
return "GOP_RequestCampMsg"
case GOP_MapRequest:
return "GOP_MapRequest"
case GOP_CampStartedMsg:
return "GOP_CampStartedMsg"
case GOP_CampAbortedMsg:
return "GOP_CampAbortedMsg"
case GOP_WhoQueryRequestMsg:
return "GOP_WhoQueryRequestMsg"
case GOP_WhoQueryReplyMsg:
return "GOP_WhoQueryReplyMsg"
case GOP_RemoveClientFromGroupMsg:
return "GOP_RemoveClientFromGroupMsg"
case GOP_GroupCreatedMsg:
return "GOP_GroupCreatedMsg"
case GOP_GroupDestroyedMsg:
return "GOP_GroupDestroyedMsg"
case GOP_GroupMemberAddedMsg:
return "GOP_GroupMemberAddedMsg"
case GOP_GroupMemberRemovedMsg:
return "GOP_GroupMemberRemovedMsg"
case GOP_GroupInfoRequestMsg:
return "GOP_GroupInfoRequestMsg"
case GOP_GroupInfoUpdateMsg:
return "GOP_GroupInfoUpdateMsg"
case GOP_GroupRemovedFromGroupMsg:
return "GOP_GroupRemovedFromGroupMsg"
case GOP_GroupLeaderChangedMsg:
return "GOP_GroupLeaderChangedMsg"
case GOP_GroupOptionsMsg:
return "GOP_GroupOptionsMsg"
case GOP_UpdateDataMsg:
return "GOP_UpdateDataMsg"
case GOP_UpdateSpawnMsg:
return "GOP_UpdateSpawnMsg"
case GOP_UpdateCharacterSheetMsg:
return "GOP_UpdateCharacterSheetMsg"
case GOP_UpdateSkillsMsg:
return "GOP_UpdateSkillsMsg"
case GOP_UpdateQuestMsg:
return "GOP_UpdateQuestMsg"
case GOP_UpdateInventoryMsg:
return "GOP_UpdateInventoryMsg"
case GOP_UpdatePositionMsg:
return "GOP_UpdatePositionMsg"
case GOP_UpdateRaidMsg:
return "GOP_UpdateRaidMsg"
case GOP_UpdateTradeMsg:
return "GOP_UpdateTradeMsg"
case GOP_UpdateTargetMsg:
return "GOP_UpdateTargetMsg"
case GOP_UpdateTargetLocMsg:
return "GOP_UpdateTargetLocMsg"
case GOP_UpdateActorTargetMsg:
return "GOP_UpdateActorTargetMsg"
case GOP_UpdatePlayerMailMsg:
return "GOP_UpdatePlayerMailMsg"
case GOP_UpdatePlayerMail:
return "GOP_UpdatePlayerMail"
case GOP_UpdateTimeMsg:
return "GOP_UpdateTimeMsg"
default:
return fmt.Sprintf("Unknown(%d)", opcode)
}
}
// ConvertToProtocolPacket wraps this game packet in a protocol packet
func (p *GamePacket) ConvertToProtocolPacket() *ProtocolPacket {
// Serialize the game packet
gameData := p.SerializeGame()
// Create protocol packet with OP_Packet opcode
proto := NewProtocolPacket(OP_Packet, gameData)
// Copy network info
proto.SrcIP = p.SrcIP
proto.SrcPort = p.SrcPort
proto.DstIP = p.DstIP
proto.DstPort = p.DstPort
proto.Timestamp = p.Timestamp
proto.Version = p.Version
return proto
}
// Copy creates a deep copy of the game packet
func (p *GamePacket) Copy() *GamePacket {
newPacket := &GamePacket{
EQPacket: *p.Clone(),
GameEmuOpcode: p.GameEmuOpcode,
}
return newPacket
}
// CombineGamePackets combines multiple game packets for efficient transmission
func CombineGamePackets(packets []*GamePacket) *ProtocolPacket {
if len(packets) == 0 {
return nil
}
if len(packets) == 1 {
return packets[0].ConvertToProtocolPacket()
}
// Build combined buffer
var buf []byte
for _, p := range packets {
data := p.SerializeGame()
size := len(data)
// Add size encoding
if size >= 255 {
// Oversized packet
buf = append(buf, 0xFF)
buf = append(buf, byte(size>>8), byte(size))
} else {
buf = append(buf, byte(size))
}
// Add packet data
buf = append(buf, data...)
}
// Create combined protocol packet
return NewProtocolPacket(OP_AppCombined, buf)
}

235
login_packet.go Normal file
View File

@ -0,0 +1,235 @@
package eq2net
import (
"fmt"
)
// LoginEmuOpcode represents emulator opcodes for the login server
type LoginEmuOpcode uint16
// Login server emulator opcodes
const (
LOP_Unknown LoginEmuOpcode = iota
LOP_LoginRequestMsg
LOP_LoginByNumRequestMsg
LOP_WSLoginRequestMsg
LOP_ESLoginRequestMsg
LOP_LoginReplyMsg
LOP_WorldListMsg
LOP_WorldStatusChangeMsg
LOP_AllWSDescRequestMsg
LOP_WSStatusReplyMsg
LOP_AllCharactersDescRequestMsg
LOP_AllCharactersDescReplyMsg
LOP_CreateCharacterRequestMsg
LOP_ReskinCharacterRequestMsg
LOP_CreateCharacterReplyMsg
LOP_WSCreateCharacterRequestMsg
LOP_WSCreateCharacterReplyMsg
LOP_DeleteCharacterRequestMsg
LOP_DeleteCharacterReplyMsg
LOP_PlayCharacterRequestMsg
LOP_PlayCharacterReplyMsg
LOP_ServerPlayCharacterRequestMsg
LOP_ServerPlayCharacterReplyMsg
LOP_KeymapLoadMsg
LOP_KeymapNoneMsg
LOP_KeymapDataMsg
LOP_KeymapSaveMsg
LOP_LSCheckAcctLockMsg
LOP_WSAcctLockStatusMsg
LOP_LsRequestClientCrashLogMsg
LOP_LsClientBaselogReplyMsg
LOP_LsClientCrashlogReplyMsg
LOP_LsClientAlertlogReplyMsg
LOP_LsClientVerifylogReplyMsg
LOP_BadLanguageFilter
LOP_WSServerLockMsg
LOP_WSServerHideMsg
LOP_LSServerLockMsg
LOP_UpdateCharacterSheetMsg
LOP_UpdateInventoryMsg
LOP_MaxLoginOpcode
)
// LoginPacket represents a login server packet
type LoginPacket struct {
EQPacket // Embed base packet
LoginEmuOpcode LoginEmuOpcode // Login server emulator opcode
}
// NewLoginPacket creates a new login server packet
func NewLoginPacket(opcode LoginEmuOpcode, data []byte) *LoginPacket {
base := NewEQPacket(uint16(opcode), data)
base.EmuOpcode = uint16(opcode)
base.OpcodeSize = 1 // Login server uses 1-byte opcodes
p := &LoginPacket{
EQPacket: *base,
LoginEmuOpcode: opcode,
}
return p
}
// ParseLoginPacket creates a login packet from raw data
func ParseLoginPacket(data []byte) (*LoginPacket, error) {
if len(data) < 1 {
return nil, fmt.Errorf("packet too small for login opcode")
}
// Extract 1-byte opcode
opcode := LoginEmuOpcode(data[0])
// Create packet with remaining data
base := NewEQPacket(uint16(opcode), data[1:])
base.EmuOpcode = uint16(opcode)
base.OpcodeSize = 1
p := &LoginPacket{
EQPacket: *base,
LoginEmuOpcode: opcode,
}
return p, nil
}
// GetLoginOpcode returns the login emulator opcode
func (p *LoginPacket) GetLoginOpcode() LoginEmuOpcode {
return p.LoginEmuOpcode
}
// SetLoginOpcode sets the login emulator opcode
func (p *LoginPacket) SetLoginOpcode(opcode LoginEmuOpcode) {
p.LoginEmuOpcode = opcode
p.EmuOpcode = uint16(opcode)
p.Opcode = uint16(opcode)
}
// SerializeLogin serializes the login packet with proper encoding
func (p *LoginPacket) SerializeLogin() []byte {
// Login packets use 1-byte opcodes
buf := make([]byte, 1+len(p.Buffer))
buf[0] = byte(p.LoginEmuOpcode)
if len(p.Buffer) > 0 {
copy(buf[1:], p.Buffer)
}
return buf
}
// GetLoginOpcodeName returns the string name of a login emulator opcode
func GetLoginOpcodeName(opcode LoginEmuOpcode) string {
switch opcode {
case LOP_Unknown:
return "LOP_Unknown"
case LOP_LoginRequestMsg:
return "LOP_LoginRequestMsg"
case LOP_LoginByNumRequestMsg:
return "LOP_LoginByNumRequestMsg"
case LOP_WSLoginRequestMsg:
return "LOP_WSLoginRequestMsg"
case LOP_ESLoginRequestMsg:
return "LOP_ESLoginRequestMsg"
case LOP_LoginReplyMsg:
return "LOP_LoginReplyMsg"
case LOP_WorldListMsg:
return "LOP_WorldListMsg"
case LOP_WorldStatusChangeMsg:
return "LOP_WorldStatusChangeMsg"
case LOP_AllWSDescRequestMsg:
return "LOP_AllWSDescRequestMsg"
case LOP_WSStatusReplyMsg:
return "LOP_WSStatusReplyMsg"
case LOP_AllCharactersDescRequestMsg:
return "LOP_AllCharactersDescRequestMsg"
case LOP_AllCharactersDescReplyMsg:
return "LOP_AllCharactersDescReplyMsg"
case LOP_CreateCharacterRequestMsg:
return "LOP_CreateCharacterRequestMsg"
case LOP_ReskinCharacterRequestMsg:
return "LOP_ReskinCharacterRequestMsg"
case LOP_CreateCharacterReplyMsg:
return "LOP_CreateCharacterReplyMsg"
case LOP_WSCreateCharacterRequestMsg:
return "LOP_WSCreateCharacterRequestMsg"
case LOP_WSCreateCharacterReplyMsg:
return "LOP_WSCreateCharacterReplyMsg"
case LOP_DeleteCharacterRequestMsg:
return "LOP_DeleteCharacterRequestMsg"
case LOP_DeleteCharacterReplyMsg:
return "LOP_DeleteCharacterReplyMsg"
case LOP_PlayCharacterRequestMsg:
return "LOP_PlayCharacterRequestMsg"
case LOP_PlayCharacterReplyMsg:
return "LOP_PlayCharacterReplyMsg"
case LOP_ServerPlayCharacterRequestMsg:
return "LOP_ServerPlayCharacterRequestMsg"
case LOP_ServerPlayCharacterReplyMsg:
return "LOP_ServerPlayCharacterReplyMsg"
case LOP_KeymapLoadMsg:
return "LOP_KeymapLoadMsg"
case LOP_KeymapNoneMsg:
return "LOP_KeymapNoneMsg"
case LOP_KeymapDataMsg:
return "LOP_KeymapDataMsg"
case LOP_KeymapSaveMsg:
return "LOP_KeymapSaveMsg"
case LOP_LSCheckAcctLockMsg:
return "LOP_LSCheckAcctLockMsg"
case LOP_WSAcctLockStatusMsg:
return "LOP_WSAcctLockStatusMsg"
case LOP_LsRequestClientCrashLogMsg:
return "LOP_LsRequestClientCrashLogMsg"
case LOP_LsClientBaselogReplyMsg:
return "LOP_LsClientBaselogReplyMsg"
case LOP_LsClientCrashlogReplyMsg:
return "LOP_LsClientCrashlogReplyMsg"
case LOP_LsClientAlertlogReplyMsg:
return "LOP_LsClientAlertlogReplyMsg"
case LOP_LsClientVerifylogReplyMsg:
return "LOP_LsClientVerifylogReplyMsg"
case LOP_BadLanguageFilter:
return "LOP_BadLanguageFilter"
case LOP_WSServerLockMsg:
return "LOP_WSServerLockMsg"
case LOP_WSServerHideMsg:
return "LOP_WSServerHideMsg"
case LOP_LSServerLockMsg:
return "LOP_LSServerLockMsg"
case LOP_UpdateCharacterSheetMsg:
return "LOP_UpdateCharacterSheetMsg"
case LOP_UpdateInventoryMsg:
return "LOP_UpdateInventoryMsg"
default:
return fmt.Sprintf("Unknown(%d)", opcode)
}
}
// ConvertToProtocolPacket wraps this login packet in a protocol packet
func (p *LoginPacket) ConvertToProtocolPacket() *ProtocolPacket {
// Serialize the login packet
loginData := p.SerializeLogin()
// Create protocol packet with OP_Packet opcode
proto := NewProtocolPacket(OP_Packet, loginData)
// Copy network info
proto.SrcIP = p.SrcIP
proto.SrcPort = p.SrcPort
proto.DstIP = p.DstIP
proto.DstPort = p.DstPort
proto.Timestamp = p.Timestamp
proto.Version = p.Version
return proto
}
// Copy creates a deep copy of the login packet
func (p *LoginPacket) Copy() *LoginPacket {
newPacket := &LoginPacket{
EQPacket: *p.Clone(),
LoginEmuOpcode: p.LoginEmuOpcode,
}
return newPacket
}

View File

@ -25,7 +25,7 @@
#include "../common/EQ2_Common_Structs.h"
#ifdef WORLD
#include "../WorldServer/SpawnLists.h"
#include "../world/SpawnLists.h"
#endif
using namespace std;

View File

@ -35,7 +35,7 @@
#include "Log.h"
#ifdef LOGIN
#include "../LoginServer/login_structs.h"
#include "../login/login_structs.h"
#endif
@ -1046,7 +1046,6 @@ void EQStream::ProcessPacket(EQProtocolPacket* p, EQProtocolPacket* lastp)
break;
}
}
}
/**
* Compress EQ2 packet data using zlib deflate compression

View File

@ -28,7 +28,7 @@
#include "EQStream.h"
#include "packet_dump.h"
#ifdef WORLD
#include "../WorldServer/client.h"
#include "../world/client.h"
#endif
using namespace std;

View File

@ -27,9 +27,9 @@
#include <errno.h>
#include <time.h>
#include <sys/stat.h>
#include "../WorldServer/World.h"
#include "../WorldServer/client.h"
#include "../WorldServer/zoneserver.h"
#include "../world/World.h"
#include "../world/client.h"
#include "../world/zoneserver.h"
extern ZoneList zone_list;

View File

@ -21,7 +21,7 @@
#define LOG_H_
#include <string.h>
#include "../WorldServer/client.h"
#include "../world/client.h"
#define LOG_BUFFER_SIZE 4096

View File

@ -26,9 +26,9 @@
#include "Log.h"
#ifdef WORLD
#include "../WorldServer/Items/Items.h"
#include "../WorldServer/Player.h"
#include "../WorldServer/World.h"
#include "../world/Items/Items.h"
#include "../world/Player.h"
#include "../world/World.h"
#endif
extern ConfigReader configReader;

View File

@ -35,11 +35,11 @@
#include "../version.h"
#ifdef WORLD
#include "../../WorldServer/WorldDatabase.h"
#include "../../world/WorldDatabase.h"
extern WorldDatabase database;
#endif
#ifdef LOGIN
#include "../../LoginServer/LoginDatabase.h"
#include "../../login/LoginDatabase.h"
extern LoginDatabase database;
#endif

View File

@ -48,13 +48,13 @@ using namespace std;
#include "packet_functions.h"
#include "emu_opcodes.h"
#ifdef WORLD
#include "../WorldServer/WorldDatabase.h"
#include "../WorldServer/Web/PeerManager.h"
#include "../world/WorldDatabase.h"
#include "../world/Web/PeerManager.h"
extern WorldDatabase database;
extern PeerManager peer_manager;
#endif
#ifdef LOGIN
#include "../LoginServer/LoginDatabase.h"
#include "../login/LoginDatabase.h"
extern LoginDatabase database;
#endif
#ifdef PARSER

View File

@ -34,8 +34,8 @@ namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
#include "../common/linked_list.h"
#include "../WorldServer/MutexList.h"
#include "../WorldServer/MutexMap.h"
#include "../world/MutexList.h"
#include "../world/MutexMap.h"
#include "../common/timer.h"
#include "../common/types.h"
#include "../common/queue.h"

View File

@ -0,0 +1,332 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Achievements.h"
#include "../../common/Log.h"
#include "../../common/ConfigReader.h"
#include <assert.h>
extern ConfigReader configReader;
extern MasterAchievementList master_achievement_list;
Achievement::Achievement() {
id = 0;
memset(title, 0, sizeof(title));
memset(uncompleted_text, 0, sizeof(uncompleted_text));
memset(completed_text, 0, sizeof(completed_text));
memset(category, 0, sizeof(category));
memset(expansion, 0, sizeof(expansion));
icon = 0;
point_value = 0;
qty_req = 0;
hide = false;
unknown3a = 0;
unknown3b = 0;
}
Achievement::Achievement(Achievement *in) {
vector<struct AchievementRequirements *> *requirements_in;
vector<struct AchievementRewards *> *rewards_in;
vector<struct AchievementRequirements *>::iterator itr;
vector<struct AchievementRewards *>::iterator itr2;
struct AchievementRequirements *achievement_requirement;
struct AchievementRewards *achievement_reward;
assert(in);
id = in->GetID();
strncpy(title, in->GetTitle(), sizeof(title));
strncpy(uncompleted_text, in->GetUncompletedText(), sizeof(uncompleted_text));
strncpy(completed_text, in->GetCompletedText(), sizeof(completed_text));
strncpy(category, in->GetCategory(), sizeof(category));
strncpy(expansion, in->GetExpansion(), sizeof(expansion));
icon = in->GetIcon();
point_value = in->GetPointValue();
qty_req = in->GetQtyReq();
hide = in->GetHide();
unknown3a = in->GetUnknown3a();
unknown3b = in->GetUnknown3b();
requirements_in = in->GetRequirements();
for (itr = requirements_in->begin(); itr != requirements_in->end(); itr++) {
achievement_requirement = new struct AchievementRequirements;
achievement_requirement->achievement_id = (*itr)->achievement_id;
achievement_requirement->name = (*itr)->name;
achievement_requirement->qty_req = (*itr)->qty_req;
requirements.push_back(achievement_requirement);
}
rewards_in = in->GetRewards();
for (itr2 = rewards_in->begin(); itr2 != rewards_in->end(); itr2++) {
achievement_reward = new struct AchievementRewards;
achievement_reward->achievement_id = (*itr2)->achievement_id;
achievement_reward->reward = (*itr2)->reward;
rewards.push_back(achievement_reward);
}
}
Achievement::~Achievement() {
vector<struct AchievementRequirements *>::iterator itr;
vector<struct AchievementRewards *>::iterator itr2;
for (itr = requirements.begin(); itr != requirements.end(); itr++)
safe_delete(*itr);
for (itr2 = rewards.begin(); itr2 != rewards.end(); itr2++)
safe_delete(*itr2);
}
void Achievement::AddAchievementRequirement(struct AchievementRequirements *requirement) {
assert(requirement);
requirements.push_back(requirement);
}
void Achievement::AddAchievementReward(struct AchievementRewards *reward) {
assert(reward);
rewards.push_back(reward);
}
void AchievementUpdate::AddAchievementUpdateItems(struct AchievementUpdateItems *update_item) {
assert(update_item);
update_items.push_back(update_item);
}
MasterAchievementList::MasterAchievementList() {
m_packetsCreated = false;
masterPacket = 0;
mutex_achievements.SetName("MasterAchievementList::achievements");
}
MasterAchievementList::~MasterAchievementList() {
ClearAchievements();
}
bool MasterAchievementList::AddAchievement(Achievement *achievement) {
bool ret = false;
assert(achievement);
mutex_achievements.writelock(__FUNCTION__, __LINE__);
if (achievements.count(achievement->GetID()) == 0) {
achievements[achievement->GetID()] = achievement;
ret = true;
}
mutex_achievements.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
Achievement * MasterAchievementList::GetAchievement(int32 achievement_id) {
Achievement *achievement = 0;
mutex_achievements.readlock(__FUNCTION__, __LINE__);
if (achievements.count(achievement_id) > 0)
achievement = achievements[achievement_id];
mutex_achievements.releasereadlock(__FUNCTION__, __LINE__);
return achievement;
}
void MasterAchievementList::ClearAchievements() {
map<int32, Achievement *>::iterator itr;
mutex_achievements.writelock(__FUNCTION__, __LINE__);
for (itr = achievements.begin(); itr != achievements.end(); itr++)
safe_delete(itr->second);
achievements.clear();
mutex_achievements.releasewritelock(__FUNCTION__, __LINE__);
}
int32 MasterAchievementList::Size() {
int32 size;
mutex_achievements.readlock(__FUNCTION__, __LINE__);
size = achievements.size();
mutex_achievements.releasereadlock(__FUNCTION__, __LINE__);
return size;
}
PlayerAchievementList::PlayerAchievementList() {
}
PlayerAchievementList::~PlayerAchievementList() {
ClearAchievements();
}
bool PlayerAchievementList::AddAchievement(Achievement *achievement) {
assert(achievement);
if (achievements.count(achievement->GetID()) == 0) {
achievements[achievement->GetID()] = achievement;
return true;
}
return false;
}
Achievement * PlayerAchievementList::GetAchievement(int32 achievement_id) {
if (achievements.count(achievement_id) > 0)
return achievements[achievement_id];
return 0;
}
void PlayerAchievementList::ClearAchievements() {
map<int32, Achievement *>::iterator itr;
for (itr = achievements.begin(); itr != achievements.end(); itr++)
safe_delete(itr->second);
achievements.clear();
}
int32 PlayerAchievementList::Size() {
return achievements.size();
}
AchievementUpdate::AchievementUpdate() {
id = 0;
completed_date = 0;
}
AchievementUpdate::AchievementUpdate(AchievementUpdate *in) {
vector<struct AchievementUpdateItems *> *items_in;
vector<struct AchievementUpdateItems *>::iterator itr;
struct AchievementUpdateItems *items;
assert(in);
id = in->GetID();
completed_date = in->GetCompletedDate();
items_in = in->GetUpdateItems();
for (itr = items_in->begin(); itr != items_in->end(); itr++) {
items = new struct AchievementUpdateItems;
items->achievement_id = (*itr)->achievement_id;
items->item_update = (*itr)->item_update;
update_items.push_back(items);
}
}
AchievementUpdate::~AchievementUpdate() {
vector<struct AchievementUpdateItems *>::iterator itr;
for (itr = update_items.begin(); itr != update_items.end(); itr++)
safe_delete(*itr);
}
PlayerAchievementUpdateList::PlayerAchievementUpdateList() {
}
PlayerAchievementUpdateList::~PlayerAchievementUpdateList() {
ClearAchievementUpdates();
}
bool PlayerAchievementUpdateList::AddAchievementUpdate(AchievementUpdate *update) {
assert(update);
if (achievement_updates.count(update->GetID()) == 0) {
achievement_updates[update->GetID()] = update;
return true;
}
return false;
}
void PlayerAchievementUpdateList::ClearAchievementUpdates() {
map<int32, AchievementUpdate *>::iterator itr;
for (itr = achievement_updates.begin(); itr != achievement_updates.end(); itr++)
safe_delete(itr->second);
achievement_updates.clear();
}
int32 PlayerAchievementUpdateList::Size() {
return achievement_updates.size();
}
void MasterAchievementList::CreateMasterAchievementListPacket() {
map<int32, Achievement *>::iterator itr;
Achievement *achievement;
vector<AchievementRequirements *> *requirements = 0;
vector<AchievementRequirements *>::iterator itr2;
AchievementRequirements *requirement;
vector<AchievementRewards *> *rewards = 0;
vector<AchievementRewards *>::iterator itr3;
AchievementRewards *reward;
PacketStruct *packet;
int16 i = 0;
int16 j = 0;
int16 k = 0;
int16 version = 1096;
if (!(packet = configReader.getStruct("WS_CharacterAchievements", version))) {
return;
}
packet->setArrayLengthByName("num_achievements" , achievements.size());
for (itr = achievements.begin(); itr != achievements.end(); itr++) {
achievement = itr->second;
packet->setArrayDataByName("achievement_id", achievement->GetID(), i);
packet->setArrayDataByName("title", achievement->GetTitle(), i);
packet->setArrayDataByName("uncompleted_text", achievement->GetUncompletedText(), i);
packet->setArrayDataByName("completed_text", achievement->GetCompletedText(), i);
packet->setArrayDataByName("category", achievement->GetCategory(), i);
packet->setArrayDataByName("expansion", achievement->GetExpansion(), i);
packet->setArrayDataByName("icon", achievement->GetIcon(), i);
packet->setArrayDataByName("point_value", achievement->GetPointValue(), i);
packet->setArrayDataByName("qty_req", achievement->GetQtyReq(), i);
packet->setArrayDataByName("hide_achievement", achievement->GetHide(), i);
packet->setArrayDataByName("unknown3", achievement->GetUnknown3a(), i);
packet->setArrayDataByName("unknown3", achievement->GetUnknown3b(), i);
requirements = achievement->GetRequirements();
rewards = achievement->GetRewards();
j = 0;
k = 0;
packet->setSubArrayLengthByName("num_items", requirements->size(), i, j);
for (itr2 = requirements->begin(); itr2 != requirements->end(); itr2++) {
requirement = *itr2;
packet->setSubArrayDataByName("item_name", requirement->name.c_str(), i, j);
packet->setSubArrayDataByName("item_qty_req", requirement->qty_req, i, j);
j++;
}
packet->setSubArrayLengthByName("num_rewards", achievement->GetRewards()->size(), i, k);
for (itr3 = rewards->begin(); itr3 != rewards->end(); itr3++) {
reward = *itr3;
packet->setSubArrayDataByName("reward_item", reward->reward.c_str(), i, k);
k++;
}
i++;
}
//packet->PrintPacket();
EQ2Packet* data = packet->serialize();
masterPacket = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size);
safe_delete(packet);
safe_delete(data);
//DumpPacket(app);
m_packetsCreated = true;
}

View File

@ -0,0 +1,176 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACHIEVEMENTS_H_
#define ACHIEVEMENTS_H_
#include "../../common/types.h"
#include "../../common/Mutex.h"
#include "../Items/Items.h"
#include <map>
#include <vector>
using namespace std;
struct AchievementRewards
{
int32 achievement_id;
string reward;
};
struct AchievementRequirements
{
int32 achievement_id;
string name;
int32 qty_req;
};
struct AchievementUpdateItems
{
int32 achievement_id;
int32 item_update;
};
class Achievement {
public:
Achievement();
Achievement(Achievement *in);
virtual ~Achievement();
void SetID(int32 id) {this->id = id;}
void SetTitle(const char *title) {strncpy(this->title, title, sizeof(this->title));}
void SetUncompletedText(const char *uncompleted_text) {strncpy(this->uncompleted_text, uncompleted_text, sizeof(this->uncompleted_text));}
void SetCompletedText(const char *completed_text) {strncpy(this->completed_text, completed_text, sizeof(this->completed_text));}
void SetCategory(const char *category) {strncpy(this->category, category, sizeof(this->category));}
void SetExpansion(const char *expansion) {strncpy(this->expansion, expansion, sizeof(this->expansion));}
void SetIcon(int16 icon) {this->icon = icon;}
void SetPointValue(int32 point_value) {this->point_value = point_value;}
void SetQtyReq(int32 qty_req) {this->qty_req = qty_req;}
void SetHide(bool hide) {this->hide = hide;}
void SetUnknown3a(int32 unknown3a) {this->unknown3a = unknown3a;}
void SetUnknown3b(int32 unknown3b) {this->unknown3b = unknown3b;}
void AddAchievementRequirement(struct AchievementRequirements *requirements);
void AddAchievementReward(struct AchievementRewards *reward);
int32 GetID() {return id;}
const char * GetTitle() {return title;}
const char * GetUncompletedText() {return uncompleted_text;}
const char * GetCompletedText() {return completed_text;}
const char * GetCategory() {return category;}
const char * GetExpansion() {return expansion;}
int16 GetIcon() {return icon;}
int32 GetPointValue() {return point_value;}
int32 GetQtyReq() {return qty_req;}
bool GetHide() {return hide;}
int32 GetUnknown3a() {return unknown3a;}
int32 GetUnknown3b() {return unknown3b;}
vector<struct AchievementRequirements *> * GetRequirements() {return &requirements;}
vector<struct AchievementRewards *> * GetRewards() {return &rewards;}
private:
int32 id;
char title[512];
char uncompleted_text[512];
char completed_text[512];
char category[32];
char expansion[32];
int16 icon;
int32 point_value;
int32 qty_req;
bool hide;
int32 unknown3a;
int32 unknown3b;
vector<struct AchievementRequirements *> requirements;
vector<struct AchievementRewards *> rewards;
};
class AchievementUpdate {
public:
AchievementUpdate();
AchievementUpdate(AchievementUpdate *in);
virtual ~AchievementUpdate();
void SetID(int32 id) {this->id = id;}
void SetCompletedDate(int32 completed_date) {this->completed_date = completed_date;}
void AddAchievementUpdateItems(struct AchievementUpdateItems *update_items);
int32 GetID() {return id;}
int32 GetCompletedDate() {return completed_date;}
vector<struct AchievementUpdateItems *> * GetUpdateItems() {return &update_items;}
private:
int32 id;
int32 completed_date;
vector<struct AchievementUpdateItems *> update_items;
};
class MasterAchievementList {
public:
MasterAchievementList();
virtual ~MasterAchievementList();
bool AddAchievement(Achievement *achievement);
Achievement * GetAchievement(int32 achievement_id);
void ClearAchievements();
int32 Size();
void CreateMasterAchievementListPacket();
EQ2Packet * GetAchievementPacket() { return m_packetsCreated ? masterPacket : 0;}
EQ2Packet *masterPacket;
private:
Mutex mutex_achievements;
map<int32, Achievement *> achievements;
bool m_packetsCreated;
};
class PlayerAchievementList {
public:
PlayerAchievementList();
virtual ~PlayerAchievementList();
bool AddAchievement(Achievement *achievement);
Achievement * GetAchievement(int32 achievement_id);
void ClearAchievements();
int32 Size();
map<int32, Achievement *> * GetAchievements() {return &achievements;}
private:
map<int32, Achievement *> achievements;
};
class PlayerAchievementUpdateList {
public:
PlayerAchievementUpdateList();
virtual ~PlayerAchievementUpdateList();
bool AddAchievementUpdate(AchievementUpdate *achievement_update);
void ClearAchievementUpdates();
int32 Size();
map<int32, AchievementUpdate *> * GetAchievementUpdates() {return &achievement_updates;}
private:
map<int32, AchievementUpdate *> achievement_updates;
};
#endif

View File

@ -0,0 +1,241 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#endif
#include <mysql.h>
#include <assert.h>
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "Achievements.h"
extern MasterAchievementList master_achievement_list;
void WorldDatabase::LoadAchievements()
{
Achievement *achievement;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 aReqs_total = 0;
int32 aRewards_total = 0;
res = query.RunQuery2(Q_SELECT, "SELECT `achievement_id`,`title`,`uncompleted_text`,`completed_text`,`category`,`expansion`,`icon`,`point_value`,`qty_req`,`hide_achievement`,`unknown3a`,`unknown3b`\n"
"FROM `achievements`");
if (res)
{
while ((row = mysql_fetch_row(res)))
{
achievement = new Achievement();
achievement->SetID(atoul(row[0]));
achievement->SetTitle(row[1]);
achievement->SetUncompletedText(row[2]);
achievement->SetCompletedText(row[3]);
achievement->SetCategory(row[4]);
achievement->SetExpansion(row[5]);
achievement->SetIcon(atoi(row[6]));
achievement->SetPointValue(atoul(row[7]));
achievement->SetQtyReq(atoul(row[8]));
achievement->SetHide( atoi(row[9]) == 0 ? false : true );
achievement->SetUnknown3a(atoul(row[10]));
achievement->SetUnknown3b(atoul(row[11]));
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "\tLoading Achievement: '%s' (%u)", achievement->GetTitle(), achievement->GetID());
if (!master_achievement_list.AddAchievement(achievement))
{
LogWrite(ACHIEVEMENT__ERROR, 0, "Achievements", "Error adding achievement '%s' - duplicate ID: %u", achievement->GetTitle(), achievement->GetID());
safe_delete(achievement);
continue;
}
aReqs_total += LoadAchievementRequirements(achievement);
aRewards_total += LoadAchievementRewards(achievement);
}
}
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievements", master_achievement_list.Size());
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievement requirements", aReqs_total);
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievement rewards", aRewards_total);
}
int32 WorldDatabase::LoadAchievementRequirements(Achievement *achievement)
{
AchievementRequirements *requirements;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int16 total = 0;
assert(achievement);
res = query.RunQuery2(Q_SELECT, "SELECT `achievement_id`, `name`, `qty_req` FROM `achievements_requirements` WHERE `achievement_id` = %u", achievement->GetID());
if (res) {
while ((row = mysql_fetch_row(res))) {
requirements = new AchievementRequirements();
requirements->achievement_id = atoul(row[0]);
requirements->name = row[1];
requirements->qty_req = atoul(row[2]);
achievement->AddAchievementRequirement(requirements);
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "Loading Achievements Requirement '%s'", requirements->name.c_str());
total++;
}
}
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "Loaded %u requirements for achievement '%s' ID: %u", total, achievement->GetTitle(), achievement->GetID());
return total;
}
int32 WorldDatabase::LoadAchievementRewards(Achievement *achievement)
{
AchievementRewards *rewards;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int16 total = 0;
assert(achievement);
res = query.RunQuery2(Q_SELECT, "SELECT `achievement_id`, `reward` FROM `achievements_rewards` WHERE `achievement_id` = %u", achievement->GetID());
if (res) {
while ((row = mysql_fetch_row(res))) {
rewards = new AchievementRewards();
rewards->achievement_id = atoul(row[0]);
rewards->reward = row[1];
achievement->AddAchievementReward(rewards);
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "Loading Achievements Reward '%s'", rewards->reward.c_str());
total++;
}
}
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "Loaded %u rewards for achievement '%s' ID: %u", total, achievement->GetTitle(), achievement->GetID());
return total;
}
void WorldDatabase::LoadPlayerAchievements(Player *player) {
Achievement *achievement;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 aReqs_total = 0;
int32 aRewards_total = 0;
int32 total = 0;
assert(player);
res = query.RunQuery2(Q_SELECT, "SELECT `achievement_id`,`title`,`uncompleted_text`,`completed_text`,`category`,`expansion`,`icon`,`point_value`,`qty_req`,`hide_achievement`,`unknown3a`,`unknown3b`\n"
"FROM `achievements`");
if (res)
{
while ((row = mysql_fetch_row(res)))
{
achievement = new Achievement();
achievement->SetID(atoul(row[0]));
achievement->SetTitle(row[1]);
achievement->SetUncompletedText(row[2]);
achievement->SetCompletedText(row[3]);
achievement->SetCategory(row[4]);
achievement->SetExpansion(row[5]);
achievement->SetIcon(atoi(row[6]));
achievement->SetPointValue(atoul(row[7]));
achievement->SetQtyReq(atoul(row[8]));
achievement->SetHide( atoi(row[9]) == 0 ? false : true );
achievement->SetUnknown3a(atoul(row[10]));
achievement->SetUnknown3b(atoul(row[11]));
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "\tLoading Achievement: '%s' (%u)", achievement->GetTitle(), achievement->GetID());
if (!player->GetAchievementList()->AddAchievement(achievement))
{
LogWrite(ACHIEVEMENT__ERROR, 0, "Achievements", "Error adding achievement '%s' - duplicate ID: %u", achievement->GetTitle(), achievement->GetID());
safe_delete(achievement);
continue;
}
total++;
aReqs_total += LoadAchievementRequirements(achievement);
aRewards_total += LoadAchievementRewards(achievement);
}
}
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievements for '%s'", total, player->GetName());
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievement requirements for '%s'", aReqs_total, player->GetName());
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievement rewards for '%s'", aRewards_total, player->GetName());
}
int32 WorldDatabase::LoadPlayerAchievementsUpdates(Player *player) {
AchievementUpdate *update;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 total = 0;
int32 items_total = 0;
assert(player);
res = query.RunQuery2(Q_SELECT, "SELECT `char_id`, `achievement_id`, `completed_date` FROM character_achievements WHERE char_id = %u ", player->GetCharacterID());
if (res) {
while ((row = mysql_fetch_row(res))) {
update = new AchievementUpdate();
update->SetID(atoul(row[1]));
update->SetCompletedDate(atoul(row[2]));
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "Loading Player Achievement Update for Achievement ID: %u ", update->GetID());
if (!player->GetAchievementUpdateList()->AddAchievementUpdate(update))
{
LogWrite(ACHIEVEMENT__ERROR, 0, "Achievements", "Error adding achievement update %u - diplicate ID", update->GetID());
safe_delete(update);
continue;
}
total++;
items_total += LoadPlayerAchievementsUpdateItems(update, player->GetCharacterID());
}
}
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "Loaded %u player achievement updates", total);
return total;
}
int32 WorldDatabase::LoadPlayerAchievementsUpdateItems(AchievementUpdate *update, int32 player_id) {
AchievementUpdateItems *update_items;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 total = 0;
assert(update);
res = query.RunQuery2(Q_SELECT, "SELECT `achievement_id`, `items` FROM character_achievements_items WHERE char_id = %u AND achievement_id = %u;", player_id, update->GetID());
if (res) {
while ((row = mysql_fetch_row(res))) {
update_items = new AchievementUpdateItems();
update_items->achievement_id = atoul(row[0]);
update_items->item_update = atoul(row[1]);
update->AddAchievementUpdateItems(update_items);
LogWrite(ACHIEVEMENT__DEBUG, 5, "Achievements", "Loading Player Achievement Update Items for Achievement ID: %u ", update_items->achievement_id);
total++;
}
}
LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "Loaded %u player achievement update items", total);
return total;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __AltAdvancement__
#define __AltAdvancement__
#include <vector>
#include "../../common/types.h"
#include "../../common/EQPacket.h"
#include "../client.h"
// defines for AA tabs based on group # from DB
#define AA_CLASS 0
#define AA_SUBCLASS 1
#define AA_SHADOW 2
#define AA_HEROIC 3
#define AA_TRADESKILL 4
#define AA_PRESTIGE 5
#define AA_TRADESKILL_PRESTIGE 6
#define AA_DRAGON 7
#define AA_DRAGONCLASS 8
#define AA_FARSEAS 9
struct AltAdvanceData
{
int32 spellID;
int8 min_level;
int32 spell_crc;
string name;
string description;
int8 group;
int16 icon;
int16 icon2;
int8 col;
int8 row;
int8 rankCost;
int8 maxRank;
int32 rankPrereqID;
int8 rankPrereq;
int8 class_req;
int8 tier;
int8 req_points;
int16 req_tree_points;
string class_name;
string subclass_name;
string line_title;
int8 title_level;
int32 node_id;
};
class MasterAAList
{
public:
MasterAAList();
~MasterAAList();
/// <summary>Sorts the Alternate Advancements for the given client, creates and sends the OP_AdventureList packet.</summary>
/// <param name='client'>The Client calling this function</param>
/// <returns>EQ2Packet*</returns>
EQ2Packet* GetAAListPacket(Client* client);
/// <summary>Add Alternate Advancement data to the global list.</summary>
/// <param name='data'>The Alternate Advancement data to add.</param>
void AddAltAdvancement(AltAdvanceData* data);
/// <summary>Get the total number of Alternate Advancements in the global list.</summary>
int Size();
/// <summary>Get the Alternate Advancement data for the given spell.</summary>
/// <param name='spellID'>Spell ID to get Alternate Advancement data for.</param>
AltAdvanceData* GetAltAdvancement(int32 spellID);
/// <summary>empties the master Alternate Advancement list</summary>
void DestroyAltAdvancements();
void DisplayAA(Client* client,int8 newtemplate,int8 changemode);
private:
vector <AltAdvanceData*> AAList;
Mutex MMasterAAList;
};
struct TreeNodeData
{
int32 classID;
int32 treeID;
int32 AAtreeID;
};
class MasterAANodeList
{
public:
MasterAANodeList();
~MasterAANodeList();
void AddTreeNode(TreeNodeData* data);
int Size();
void DestroyTreeNodes();
vector<TreeNodeData*> GetTreeNodes();
private:
vector<TreeNodeData*> TreeNodeList;
};
#endif

View File

@ -0,0 +1,102 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#endif
#include <mysql.h>
#include <assert.h>
#include "../../common/Log.h"
#include "../../common/DatabaseNew.h"
#include "../WorldDatabase.h"
#include "AltAdvancement.h"
extern MasterAAList master_aa_list;
extern MasterAANodeList master_tree_nodes;
void WorldDatabase::LoadAltAdvancements()
{
Query query;
MYSQL_ROW row;
AltAdvanceData* data;
//MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`, `group`, `icon`, `icon2`, `col`, `row`, `rank_cost`, `max_cost`, `rank_prereq_id`, `rank_prereq`, `class_req`, `tier`, `class_name`, `subclass_name`, `line_title` FROM spell_aa");
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `nodeid`,`minlevel`, `spellcrc`, `name`, `description`, `aa_list_fk`, `icon_id`, `icon_backdrop`, `xcoord`, `ycoord`, `pointspertier`, `maxtier`, `firstparentid`, `firstparentrequiredtier`, `displayedclassification`,`requiredclassification`, `classificationpointsrequired`, `pointsspentintreetounlock`, `title`,`titlelevel` FROM spell_aa_nodelist");
while (result && (row = mysql_fetch_row(result))) {
data = new AltAdvanceData;
int8 i = 0;
data->spellID = strtoul(row[0], NULL, 0);
data->min_level = atoi(row[++i]);
data->spell_crc = strtoul(row[++i], NULL, 0);
data->name = string(row[++i]);
data->description = string(row[++i]);
data->group = atoi(row[++i]);
data->icon = atoi(row[++i]);
data->icon2 = atoi(row[++i]);
data->col = atoi(row[++i]);
data->row = atoi(row[++i]);
data->rankCost = atoi(row[++i]);
data->maxRank = atoi(row[++i]);
data->rankPrereqID = strtoul(row[++i], NULL, 0);
data->rankPrereq = atoi(row[++i]);
data->tier = 1;
data->class_name = string(row[++i]);
data->subclass_name = string(row[i]);
data->req_points = atoi(row[++i]);
data->req_tree_points = atoi(row[++i]);
data->line_title = string(row[++i]);
data->title_level = atoi(row[++i]);
master_aa_list.AddAltAdvancement(data);
}
LogWrite(SPELL__INFO, 0, "AA", "Loaded %u Alternate Advancement(s)", master_aa_list.Size());
}
void WorldDatabase::LoadTreeNodes()
{
Query query;
MYSQL_ROW row;
TreeNodeData* data;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT class_id, tree_node, aa_tree_id FROM spell_aa_class_list");
while (result && (row = mysql_fetch_row(result))) {
data = new TreeNodeData;
data->classID = strtoul(row[0], NULL, 0);
data->treeID = strtoul(row[1], NULL, 0);
data->AAtreeID = strtoul(row[2], NULL, 0);
master_tree_nodes.AddTreeNode(data);
}
LogWrite(SPELL__INFO, 0, "AA", "Loaded %u AA Tree Nodes", master_tree_nodes.Size());
}
void WorldDatabase::LoadPlayerAA(Player *player)
{
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `template_id`,`tab_id`,`aa_id`,`order`,treeid FROM character_aa where char_id = %i order by `order`",player->id);
while (result && (row = mysql_fetch_row(result))) {
}
LogWrite(SPELL__INFO, 0, "AA", "Loaded %u AA Tree Nodes", master_tree_nodes.Size());
}

87
old/world/Appearances.h Normal file
View File

@ -0,0 +1,87 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include <map>
using namespace std;
// Appearances must use a hash table because of the large amount that exists and the large spacing
// between their ID's. String and character arrays could not be used for the first iterator because
// it would require the same pointer to access it from the hash table, which is obviously not possible
// since the text is from the client.
// maximum amount of iterations it will attempt to find a entree
#define HASH_SEARCH_MAX 20
class Appearance
{
public:
// JA: someday add the min_client_version to the map to determine which appearance_id to set per client version
Appearance(int32 inID, const char *inName, int16 inVer)
{
if( !inName )
return;
name = string(inName);
id = inID;
min_client = inVer;
}
int32 GetID() { return id; }
const char* GetName() { return name.c_str(); }
int16 GetMinClientVersion() { return min_client; }
string GetNameString() { return name; }
private:
int32 id;
string name;
int16 min_client;
};
class Appearances
{
public:
~Appearances(){
Reset();
}
void Reset(){
ClearAppearances();
}
void ClearAppearances(){
map<int32, Appearance*>::iterator map_list;
for(map_list = appearanceMap.begin(); map_list != appearanceMap.end(); map_list++ )
safe_delete(map_list->second);
appearanceMap.clear();
}
void InsertAppearance(Appearance* a){
appearanceMap[a->GetID()] = a;
}
Appearance* FindAppearanceByID(int32 id){
if(appearanceMap.count(id) > 0)
return appearanceMap[id];
return 0;
}
private:
map<int32, Appearance*> appearanceMap;
};

765
old/world/Bots/Bot.cpp Normal file
View File

@ -0,0 +1,765 @@
#include "Bot.h"
#include "BotBrain.h"
#include "../Trade.h"
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "../classes.h"
#include "../SpellProcess.h"
extern WorldDatabase database;
extern MasterSpellList master_spell_list;
extern World world;
extern Classes classes;
Bot::Bot() : NPC() {
SetBrain(new BotBrain(this));
BotID = 0;
ShowHelm = true;
ShowCloak = true;
CanTaunt = false;
combat_target = 0;
main_tank = 0;
//AddPrimaryEntityCommand("hail", 10000, "hail", "", 0, 0);
AddSecondaryEntityCommand("invite bot", 10000, "invite", "", 0, 0);
AddSecondaryEntityCommand("bot inventory", 10000, "bot inv list", "", 0, 0);
InfoStruct* info = GetInfoStruct();
info->set_str_base(50);
info->set_sta_base(20);
info->set_wis_base(20);
info->set_intel_base(20);
info->set_agi_base(20);
camping = false;
immediate_camp = false;
}
Bot::~Bot() {
}
void Bot::GiveItem(int32 item_id) {
Item* master_item = master_item_list.GetItem(item_id);
Item* item = 0;
if (master_item)
item = new Item(master_item);
if (item) {
int8 slot = GetEquipmentList()->GetFreeSlot(item);
if (slot != 255) {
GetEquipmentList()->AddItem(slot, item);
SetEquipment(item, slot);
database.SaveBotItem(BotID, item_id, slot);
if (slot == 0) {
ChangePrimaryWeapon();
if (IsBot())
LogWrite(PLAYER__ERROR, 0, "Bot", "Changing bot primary weapon.");
}
CalculateBonuses();
}
}
}
void Bot::GiveItem(Item* item) {
if (item) {
int8 slot = GetEquipmentList()->GetFreeSlot(item);
if (slot != 255) {
GetEquipmentList()->AddItem(slot, item);
SetEquipment(item, slot);
database.SaveBotItem(BotID, item->details.item_id, slot);
if (slot == 0) {
ChangePrimaryWeapon();
if (IsBot())
LogWrite(PLAYER__ERROR, 0, "Bot", "Changing bot primary weapon.");
}
CalculateBonuses();
}
}
}
void Bot::RemoveItem(Item* item) {
int8 slot = GetEquipmentList()->GetSlotByItem(item);
if (slot != 255) {
GetEquipmentList()->RemoveItem(slot, true);
SetEquipment(0, slot);
}
}
void Bot::TradeItemAdded(Item* item) {
int8 slot = GetEquipmentList()->GetFreeSlot(item);
if (slot == 255 && item->slot_data.size() > 0) {
slot = item->slot_data[0];
AddItemToTrade(slot);
}
}
void Bot::AddItemToTrade(int8 slot) {
Item* item = GetEquipmentList()->GetItem(slot);
if (trading_slots.count(slot) == 0 && item && trade) {
trade->AddItemToTrade(this, item, 1, 255);
trading_slots.insert(slot);
}
}
bool Bot::CheckTradeItems(map<int8, TradeItemInfo>* list) {
if (!list) {
LogWrite(PLAYER__ERROR, 0, "Bot", "CheckTradeItems did not recieve a valid list of items");
return false;
}
bool ret = true;
map<int8, TradeItemInfo>::iterator itr;
for (itr = list->begin(); itr != list->end(); itr++) {
Item* item = itr->second.item;
if (item) {
if (!CanEquipItem(item)) {
// No slots means not equipable so reject the trade
ret = false;
break;
}
}
}
return ret;
}
void Bot::FinishTrade() {
trading_slots.clear();
}
bool Bot::CanEquipItem(Item* item) {
if (item) {
if (item->IsArmor() || item->IsWeapon() || item->IsFood() || item->IsRanged() || item->IsShield() || item->IsBauble() || item->IsAmmo() || item->IsThrown()) {
int16 override_level = item->GetOverrideLevel(GetAdventureClass(), GetTradeskillClass());
if (override_level > 0 && override_level <= GetLevel()) {
LogWrite(PLAYER__ERROR, 0, "Bot", "Passed in override_level check");
return true;
}
if (item->CheckClass(GetAdventureClass(), GetTradeskillClass()))
if (item->CheckLevel(GetAdventureClass(), GetTradeskillClass(), GetLevel())) {
LogWrite(PLAYER__ERROR, 0, "Bot", "Passed in normal check");
return true;
}
}
}
return false;
}
void Bot::MessageGroup(string msg) {
GroupMemberInfo* gmi = GetGroupMemberInfo();
if (gmi)
world.GetGroupManager()->GroupChatMessage(gmi->group_id, this, 0, msg.c_str());
}
void Bot::GetNewSpells() {
vector<Spell*> spells;
vector<Spell*>* spells1 = master_spell_list.GetSpellListByAdventureClass(GetAdventureClass(), (double)GetLevel(), 1);
vector<Spell*>* spells2 = master_spell_list.GetSpellListByAdventureClass(classes.GetBaseClass(GetAdventureClass()), (double)GetLevel(), 1);
vector<Spell*>* spells3 = master_spell_list.GetSpellListByAdventureClass(classes.GetSecondaryBaseClass(GetAdventureClass()), (double)GetLevel(), 1);
spells.insert(spells.end(), spells1->begin(), spells1->end());
spells.insert(spells.end(), spells2->begin(), spells2->end());
spells.insert(spells.end(), spells3->begin(), spells3->end());
vector<Spell*>::iterator itr;
map<int32, int8>* spell_list = 0;
for (itr = spells.begin(); itr != spells.end(); itr++) {
switch ((*itr)->GetSpellData()->spell_type) {
case SPELL_TYPE_DD:
spell_list = &dd_spells;
break;
case SPELL_TYPE_DOT:
spell_list = &dot_spells;
break;
case SPELL_TYPE_HEAL:
spell_list = &heal_spells;
break;
case SPELL_TYPE_HOT_WARD:
spell_list = &hot_ward_spells;
break;
case SPELL_TYPE_DEBUFF:
spell_list = &debuff_spells;
break;
case SPELL_TYPE_BUFF:
spell_list = &buff_spells;
break;
case SPELL_TYPE_COMBATBUFF:
spell_list = &combat_buff_spells;
break;
case SPELL_TYPE_TAUNT:
spell_list = &taunt_spells;
break;
case SPELL_TYPE_DETAUNT:
spell_list = &detaunt_spells;
break;
case SPELL_TYPE_REZ:
LogWrite(PLAYER__ERROR, 0, "Bot", "Adding rez spell.");
spell_list = &rez_spells;
break;
case SPELL_TYPE_CURE:
spell_list = &cure_spells;
break;
default:
spell_list = 0;
break;
}
if (spell_list && spell_list->count((*itr)->GetSpellID()) == 0)
(*spell_list)[(*itr)->GetSpellID()] = 1;
}
safe_delete(spells1);
safe_delete(spells2);
safe_delete(spells3);
}
Entity* Bot::GetCombatTarget() {
Spawn* target = GetZone()->GetSpawnByID(combat_target);
if (target && target->IsEntity())
return (Entity*)target;
combat_target = 0;
return 0;
}
Spell* Bot::SelectSpellToCast(float distance) {
Spell* spell = 0;
map<int32, int8>::iterator itr;
// Heal
spell = GetHealSpell();
if (spell)
return spell;
// Taunt
spell = GetTauntSpell();
if (spell)
return spell;
// Detaunt
spell = GetDetauntSpell();
if (spell)
return spell;
// Hot/Ward
spell = GetHoTWardSpell();
if (spell)
return spell;
// Debuff
spell = GetDebuffSpell();
if (spell)
return spell;
// Combat Buff
spell = GetCombatBuffSpell();
if (spell)
return spell;
// DoT
spell = GetDoTSpell();
if (spell)
return spell;
// DD
spell = GetDDSpell();
if (spell)
return spell;
return 0;
}
void Bot::SetRecast(Spell* spell, int32 time) {
recast_times[spell->GetSpellID()] = time;
}
bool Bot::IsSpellReady(Spell* spell) {
if (recast_times.count(spell->GetSpellID()) > 0) {
if (recast_times[spell->GetSpellID()] > Timer::GetCurrentTime2())
return false;
}
return true;
}
Spell* Bot::GetDDSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (dd_spells.size() == 0)
return 0;
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = dd_spells.begin(); itr != dd_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
return spell;
}
}
return 0;
}
Spell* Bot::GetHealSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (heal_spells.size() == 0)
return 0;
// Get an available heal spell
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = heal_spells.begin(); itr != heal_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
break;
}
}
// No heal available, return out
if (!spell)
return 0;
// There was a heal spell so find a group member that needs healing
int8 threshold = GetHealThreshold();
GroupMemberInfo* gmi = GetGroupMemberInfo();
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
if (group)
{
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
for (int8 i = 0; i < members->size(); i++) {
Entity* member = members->at(i)->member;
if(!member)
continue;
if (!member->Alive())
continue;
int8 percent = (int8)(((float)member->GetHP() / member->GetTotalHP()) * 100);
if (percent <= threshold) {
if (spell) {
SetTarget(member);
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
return spell;
}
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
return 0;
}
Spell* Bot::GetTauntSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (taunt_spells.size() == 0)
return 0;
// If not the main tank and taunts are turned off return out
if (main_tank != this && !CanTaunt)
return 0;
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = taunt_spells.begin(); itr != taunt_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
return spell;
}
}
return 0;
}
Spell* Bot::GetDetauntSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (detaunt_spells.size() == 0)
return 0;
if (!GetTarget() || !GetTarget()->IsNPC())
return 0;
NPC* target = (NPC*)GetTarget();
Entity* hated = target->Brain()->GetMostHated();
if (hated == this) {
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = detaunt_spells.begin(); itr != detaunt_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
return spell;
}
}
}
return 0;
}
Spell* Bot::GetHoTWardSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (hot_ward_spells.size() == 0)
return 0;
// Get an available spell
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = hot_ward_spells.begin(); itr != hot_ward_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
break;
}
}
// No spell available, return out
if (!spell)
return 0;
// There was a spell so find a group member that needs healing
int8 threshold = GetHealThreshold();
GroupMemberInfo* gmi = GetGroupMemberInfo();
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
if (group)
{
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
for (int8 i = 0; i < members->size(); i++) {
Entity* member = members->at(i)->member;
if(!member)
continue;
int8 percent = 0;
if (member->GetHP() > 0)
percent = (int8)(((float)member->GetHP() / member->GetTotalHP()) * 100);
if (percent <= 99 && percent > threshold) {
if (spell) {
SetTarget(member);
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
return spell;
}
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
return 0;
}
Spell* Bot::GetDebuffSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (debuff_spells.size() == 0)
return 0;
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = debuff_spells.begin(); itr != debuff_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
// If target already has this effect on them then continue to the next spell
if (((Entity*)GetTarget())->GetSpellEffect(itr->first))
continue;
return spell;
}
}
return 0;
}
Spell* Bot::GetCombatBuffSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
return 0;
}
Spell* Bot::GetDoTSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (dot_spells.size() == 0)
return 0;
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = dot_spells.begin(); itr != dot_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
// If target already has this effect on them then continue to the next spell
if (((Entity*)GetTarget())->GetSpellEffect(itr->first))
continue;
return spell;
}
}
return 0;
}
Spell* Bot::GetBuffSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (buff_spells.size() == 0)
return 0;
Spell* spell = 0;
Entity* target = 0;
map<int32, int8>::iterator itr;
for (itr = buff_spells.begin(); itr != buff_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
target = 0;
if (spell->GetSpellData()->target_type == SPELL_TARGET_SELF)
target = this;
if (spell->GetSpellData()->target_type == SPELL_TARGET_GROUP_AE)
target = this;
if (spell->GetSpellData()->target_type == SPELL_TARGET_ENEMY && spell->GetSpellData()->friendly_spell == 1)
target = (main_tank != NULL) ? main_tank : GetOwner();
if (!target)
continue;
if (!target->Alive())
continue;
// If target already has this effect on them then continue to the next spell
if (target->GetSpellEffect(itr->first))
continue;
SetTarget(target);
return spell;
}
}
return 0;
}
Spell* Bot::GetRezSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
if (rez_spells.size() == 0)
return 0;
GroupMemberInfo* gmi = GetGroupMemberInfo();
if (!gmi)
return 0;
Entity* target = 0;
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
if (group)
{
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
for (int8 i = 0; i < members->size(); i++) {
Entity* member = members->at(i)->member;
if (member && !member->Alive() && member->IsPlayer()) {
PendingResurrection* rez = members->at(i)->client->GetCurrentRez();
if (rez->active)
continue;
target = member;
break;
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
if (!target)
return 0;
Spell* spell = 0;
map<int32, int8>::iterator itr;
for (itr = rez_spells.begin(); itr != rez_spells.end(); itr++) {
spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell && IsSpellReady(spell)) {
SetTarget(target);
return spell;
}
}
return 0;
}
Spell* Bot::GetCureSpell() {
if(world.IsReloadingSubsystems())
return nullptr;
return 0;
}
int8 Bot::GetHealThreshold() {
int8 ret = 0;
switch (GetAdventureClass()) {
case PRIEST:
case CLERIC:
case TEMPLAR:
case INQUISITOR:
case DRUID:
case WARDEN:
case FURY:
case SHAMAN:
case MYSTIC:
case DEFILER:
ret = 70;
break;
default:
ret = 30;
break;
}
return ret;
}
bool Bot::ShouldMelee() {
bool ret = true;
switch (GetAdventureClass()) {
case PRIEST:
case CLERIC:
case TEMPLAR:
case INQUISITOR:
case DRUID:
case WARDEN:
case FURY:
case SHAMAN:
case MYSTIC:
case DEFILER:
case MAGE:
case SORCERER:
case WIZARD:
case WARLOCK:
case ENCHANTER:
case ILLUSIONIST:
case COERCER:
case SUMMONER:
case CONJUROR:
case NECROMANCER:
ret = false;
break;
default:
ret = true;
break;
}
if (GetTarget() == GetOwner())
ret = false;
return ret;
}
void Bot::Camp(bool immediate) {
// Copy from COMMAND_GROUP_LEAVE
camping = true;
immediate_camp = immediate;
}
void Bot::ChangeLevel(int16 old_level, int16 new_level) {
if (new_level < 1)
return;
if (GetLevel() != new_level) {
SetLevel(new_level);
if (GetGroupMemberInfo()) {
UpdateGroupMemberInfo();
world.GetGroupManager()->SendGroupUpdate(GetGroupMemberInfo()->group_id);
}
}
if (GetPet()) {
NPC* pet = (NPC*)GetPet();
if (pet->GetMaxPetLevel() == 0 || new_level <= pet->GetMaxPetLevel()) {
pet->SetLevel(new_level);
GetZone()->PlayAnimation(pet, 1753);
}
}
// level up animation
GetZone()->PlayAnimation(this, 1753);
//player->GetSkills()->IncreaseAllSkillCaps(5 * (new_level - old_level));
GetNewSpells();
//SendNewSpells(player->GetAdventureClass());
//SendNewSpells(classes.GetBaseClass(player->GetAdventureClass()));
//SendNewSpells(classes.GetSecondaryBaseClass(player->GetAdventureClass()));
GetInfoStruct()->set_level(new_level);
UpdateWeapons();
// GetPlayer()->SetLevel(new_level);
LogWrite(MISC__TODO, 1, "TODO", "Get new HP/POWER/stat based on default values from DB\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
SetTotalHPBase(new_level*new_level * 2 + 40);
SetTotalPowerBase((sint32)(new_level*new_level*2.1 + 45));
CalculateBonuses();
SetHP(GetTotalHP());
SetPower(GetTotalPower());
GetInfoStruct()->set_agi_base(new_level * 2 + 15);
GetInfoStruct()->set_intel_base(new_level * 2 + 15);
GetInfoStruct()->set_wis_base(new_level * 2 + 15);
GetInfoStruct()->set_str_base(new_level * 2 + 15);
GetInfoStruct()->set_sta_base(new_level * 2 + 15);
GetInfoStruct()->set_cold_base((int16)(new_level*1.5 + 10));
GetInfoStruct()->set_heat_base((int16)(new_level*1.5 + 10));
GetInfoStruct()->set_disease_base((int16)(new_level*1.5 + 10));
GetInfoStruct()->set_mental_base((int16)(new_level*1.5 + 10));
GetInfoStruct()->set_magic_base((int16)(new_level*1.5 + 10));
GetInfoStruct()->set_divine_base((int16)(new_level*1.5 + 10));
GetInfoStruct()->set_poison_base((int16)(new_level*1.5 + 10));
/*UpdateTimeStampFlag(LEVEL_UPDATE_FLAG);
GetPlayer()->SetCharSheetChanged(true);
Message(CHANNEL_COLOR_EXP, "You are now level %i!", new_level);
LogWrite(WORLD__DEBUG, 0, "World", "Player: %s leveled from %u to %u", GetPlayer()->GetName(), old_level, new_level);
GetPlayer()->GetSkills()->SetSkillCapsByType(1, 5 * new_level);
GetPlayer()->GetSkills()->SetSkillCapsByType(3, 5 * new_level);
GetPlayer()->GetSkills()->SetSkillCapsByType(6, 5 * new_level);
GetPlayer()->GetSkills()->SetSkillCapsByType(13, 5 * new_level);
*/
}
void Bot::Begin_Camp() {
GroupMemberInfo* gmi = GetGroupMemberInfo();
if (gmi) {
int32 group_id = gmi->group_id;
world.GetGroupManager()->RemoveGroupMember(group_id, this);
if (!world.GetGroupManager()->IsGroupIDValid(group_id)) {
// leader->Message(CHANNEL_COLOR_GROUP, "%s has left the group.", client->GetPlayer()->GetName());
}
else {
world.GetGroupManager()->GroupMessage(group_id, "%s has left the group.", GetName());
}
}
if(!immediate_camp)
{
GetZone()->PlayAnimation(this, 538);
SetVisualState(540);
GetZone()->Despawn(this, 5000);
}
camping = false;
immediate_camp = true;
if (!GetOwner())
return;
if (GetOwner()->IsPlayer())
((Player*)GetOwner())->SpawnedBots.erase(BotIndex);
}

95
old/world/Bots/Bot.h Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include "../NPC.h"
#include <set>
struct TradeItemInfo;
class Bot : public NPC {
public:
Bot();
~Bot();
int32 BotID; // DB id
int32 BotIndex; // Bot id with its owner (player)
bool IsBot() { return true; }
void GiveItem(int32 item_id);
void GiveItem(Item* item);
void RemoveItem(Item* item);
void TradeItemAdded(Item* item);
void AddItemToTrade(int8 slot);
bool CheckTradeItems(map<int8, TradeItemInfo>* list);
void FinishTrade();
void GetNewSpells();
map<int32, int8>* GetBotSpells() { return &dd_spells; }
bool ShowHelm;
bool ShowCloak;
bool CanTaunt;
Entity* GetCombatTarget();
void SetCombatTarget(int32 target) { combat_target = target; }
Spell* SelectSpellToCast(float distance);
void MessageGroup(string msg);
void SetRecast(Spell* spell, int32 time);
bool ShouldMelee();
Spell* GetNextBuffSpell(Spawn* target = 0) { return GetBuffSpell(); }
Spell* GetHealSpell();
Spell* GetRezSpell();
void SetMainTank(Entity* tank) { main_tank = tank; }
void Camp(bool immediate=false);
void ChangeLevel(int16 old_level, int16 new_level);
bool IsCamping() { return camping; }
bool IsImmediateCamp() { return immediate_camp; }
void Begin_Camp();
private:
bool CanEquipItem(Item* item);
bool IsSpellReady(Spell* spell);
Spell* GetTauntSpell();
Spell* GetDetauntSpell();
Spell* GetHoTWardSpell();
Spell* GetDebuffSpell();
Spell* GetCombatBuffSpell();
Spell* GetDoTSpell();
Spell* GetDDSpell();
Spell* GetBuffSpell();
Spell* GetCureSpell();
int8 GetHealThreshold();
set<int8> trading_slots;
int32 combat_target;
Entity* main_tank;
map<int32, int8> bot_spells;
map<int32, int8> dd_spells;
map<int32, int8> dot_spells;
map<int32, int8> heal_spells;
map<int32, int8> hot_ward_spells;
map<int32, int8> debuff_spells;
map<int32, int8> buff_spells;
map<int32, int8> combat_buff_spells;
map<int32, int8> taunt_spells;
map<int32, int8> detaunt_spells;
map<int32, int8> rez_spells;
map<int32, int8> cure_spells;
// First int32 = spell id (change to timer id later), second int32 is time the spell is available to cast again
map<int32, int32> recast_times;
std::atomic<bool> camping;
std::atomic<bool> immediate_camp;
};

205
old/world/Bots/BotBrain.cpp Normal file
View File

@ -0,0 +1,205 @@
#include "BotBrain.h"
#include "../Combat.h"
#include "../Spells.h"
#include "../../common/Log.h"
#include "../Rules/Rules.h"
extern RuleManager rule_manager;
BotBrain::BotBrain(Bot* body) : Brain(body) {
Body = body;
}
BotBrain::~BotBrain() {
}
void BotBrain::Think() {
// No ownder do nothing, probably despawn as owner should never be empty for bots
if (!m_body->GetOwner())
return;
// Not in a group yet then do nothing
if (!m_body->GetGroupMemberInfo())
return;
if (!Body->Alive())
return;
if (Body->IsMezzedOrStunned())
return;
// If combat was processed we can return out
if (ProcessCombat())
return;
// Combat failed to process so do out of combat tasks like follow the player
if (ProcessOutOfCombatSpells())
return;
// put htis here so bots don't try to follow the owner while in combat
if (Body->EngagedInCombat())
return;
// Set target to owner
Spawn* target = GetBody()->GetFollowTarget();
if(target)
{
// Get distance from the owner
float distance = GetBody()->GetDistance(target);
// If out of melee range then move closer
if (distance > rule_manager.GetZoneRule(GetBody()->GetZoneID(), R_Combat, MaxCombatRange)->GetFloat())
MoveCloser(target);
}
}
bool BotBrain::ProcessCombat() {
SetTarget();
if (Body->GetTarget() && Body->EngagedInCombat()) {
if (Body->GetTarget() && Body->GetTarget()->IsEntity() && Body->AttackAllowed((Entity*)Body->GetTarget())) {
Entity* target = (Entity*)Body->GetTarget();
float distance = Body->GetDistance(target);
if (!ProcessSpell(target, distance)) {
if (Body->ShouldMelee())
ProcessMelee(target, distance);
}
NPC* pet = (NPC*)Body->GetPet();
if (pet) {
if (pet->Brain()->GetHate(target) == 0)
pet->AddHate(target, 1);
}
}
return true;
}
return false;
}
void BotBrain::SetTarget() {
// The target issued from /bot attack
if (Body->GetCombatTarget() && Body->GetCombatTarget()->Alive()) {
Body->SetTarget(Body->GetCombatTarget());
Body->InCombat(true);
return;
}
// Assist
Entity* owner = Body->GetOwner();
if (owner && owner->EngagedInCombat()) {
if (owner->GetTarget() && owner->GetTarget()->IsEntity() && owner->GetTarget()->Alive() && owner->AttackAllowed((Entity*)owner->GetTarget())) {
Body->SetTarget(owner->GetTarget());
Body->InCombat(true);
// Add some hate to keep the bot attacking if
// the player toggles combat off
if (GetHate((Entity*)Body->GetTarget()) == 0)
AddHate((Entity*)Body->GetTarget(), 1);
return;
}
}
// Most hated
Entity* hated = GetMostHated();
if (hated && hated->Alive()) {
if (hated == Body->GetOwner()) {
ClearHate(hated);
}
else {
Body->SetTarget(hated);
Body->InCombat(true);
return;
}
}
// None of the above true so clear target and turn combat off
Body->SetTarget(0);
Body->InCombat(false);
}
bool BotBrain::ProcessSpell(Entity* target, float distance) {
if (Body->IsStifled() || Body->IsFeared())
return false;
if (Body->IsCasting())
return false;
if (!HasRecovered())
return false;
Spell* spell = Body->SelectSpellToCast(distance);
if (spell) {
// Target can change (heals for example) so recalculate distance and if out of range move closer
float distance = Body->GetDistance(Body->GetTarget());
if (distance > spell->GetSpellData()->range) {
if (Body->GetTarget()->IsEntity())
MoveCloser((Spawn*)Body->GetTarget());
}
else {
// stop movement if spell can't be cast while moving
if (!spell->GetSpellData()->cast_while_moving)
Body->CalculateRunningLocation(true);
Body->GetZone()->ProcessSpell(spell, Body, Body->GetTarget());
m_spellRecovery = (int32)(Timer::GetCurrentTime2() + (spell->GetSpellData()->cast_time * 10) + (spell->GetSpellData()->recovery * 10) + 2000);
// recast time
int32 time = Timer::GetCurrentTime2() + (spell->CalculateRecastTimer(Body));
Body->SetRecast(spell, time);
string str = "I am casting ";
str += spell->GetName();
Body->MessageGroup(str);
}
return true;
}
return false;
}
bool BotBrain::ProcessOutOfCombatSpells() {
if (Body->IsStifled() || Body->IsFeared())
return false;
if (Body->IsCasting())
return false;
if (!HasRecovered())
return false;
Spell* spell = Body->GetHealSpell();
if (!spell)
spell = Body->GetRezSpell();
if (!spell)
spell = Body->GetNextBuffSpell();
if (spell) {
// stop movement if spell can't be cast while moving
if (!spell->GetSpellData()->cast_while_moving)
Body->CalculateRunningLocation(true);
// See if we are in range of target, if not move closer
float distance = Body->GetDistance(Body->GetTarget());
if (distance > spell->GetSpellData()->range) {
if (Body->GetTarget()->IsEntity())
MoveCloser((Spawn*)Body->GetTarget());
}
else {
Body->GetZone()->ProcessSpell(spell, Body, Body->GetTarget());
m_spellRecovery = (int32)(Timer::GetCurrentTime2() + (spell->GetSpellData()->cast_time * 10) + (spell->GetSpellData()->recovery * 10) + 2000);
// recast time
int32 time = Timer::GetCurrentTime2() + (spell->CalculateRecastTimer(Body));
Body->SetRecast(spell, time);
string str = "I am casting ";
str += spell->GetName();
Body->MessageGroup(str);
}
return true;
}
return false;
}

20
old/world/Bots/BotBrain.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "../NPC_AI.h"
#include "Bot.h"
class BotBrain : public Brain {
public:
BotBrain(Bot* body);
virtual ~BotBrain();
void Think();
bool ProcessSpell(Entity* target, float distance);
bool ProcessOutOfCombatSpells();
private:
Bot* Body;
bool ProcessCombat();
void SetTarget();
};

View File

@ -0,0 +1,834 @@
#include "../Commands/Commands.h"
#include "../WorldDatabase.h"
#include "../classes.h"
#include "../races.h"
#include "../Bots/Bot.h"
#include "../../common/Log.h"
#include "../Trade.h"
#include "../PlayerGroups.h"
#include "../World.h"
#include "../../common/GlobalHeaders.h"
extern WorldDatabase database;
extern ConfigReader configReader;
extern World world;
extern MasterSpellList master_spell_list;
void Commands::Command_Bot(Client* client, Seperator* sep) {
if (sep && sep->IsSet(0)) {
if (strncasecmp("camp", sep->arg[0], 4) == 0) {
if (!client->GetPlayer()->GetTarget() || !client->GetPlayer()->GetTarget()->IsBot()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must target a bot");
return;
}
Bot* bot = (Bot*)client->GetPlayer()->GetTarget();
if (bot->GetOwner() != client->GetPlayer()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can only camp your own bots.");
return;
}
bot->Camp();
return;
}
else if (strncasecmp("attack", sep->arg[0], 6) == 0) {
if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity() && client->GetPlayer()->GetTarget()->Alive()) {
Entity* target = (Entity*)client->GetPlayer()->GetTarget();
if (client->GetPlayer()->GetDistance(target) <= 50) {
if (client->GetPlayer()->AttackAllowed(target)) {
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
if (gmi) {
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
if (group)
{
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
deque<GroupMemberInfo*>::iterator itr;
for (itr = members->begin(); itr != members->end(); itr++) {
//devn00b compile says this is no good, commenting out for now.
//if(!member)
// continue;
if ((*itr)->member && (*itr)->member->IsBot() && ((Bot*)(*itr)->member)->GetOwner() == client->GetPlayer()) {
((Bot*)(*itr)->member)->SetCombatTarget(target->GetID());
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
}
}
else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can not attack that target.");
}
else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Target is to far away.");
}
else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Not a valid target.");
return;
}
else if (strncasecmp("spells", sep->arg[0], 6) == 0) {
if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsBot()) {
Bot* bot = (Bot*)client->GetPlayer()->GetTarget();
map<int32, int8>* spells = bot->GetBotSpells();
map<int32, int8>::iterator itr;
string output;
for (itr = spells->begin(); itr != spells->end(); itr++) {
Spell* spell = master_spell_list.GetSpell(itr->first, itr->second);
if (spell) {
output += spell->GetName();
output += "\n";
}
}
client->SimpleMessage(CHANNEL_COLOR_YELLOW, output.c_str());
return;
}
}
else if (strncasecmp("maintank", sep->arg[0], 8) == 0) {
if (!client->GetPlayer()->GetTarget() || !client->GetPlayer()->GetTarget()->IsEntity()) {
client->SimpleMessage(CHANNEL_COMMAND_TEXT, "Not a valid target.");
return;
}
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
if (!gmi) {
client->SimpleMessage(CHANNEL_COMMAND_TEXT, "You are not in a group.");
return;
}
Entity* target = (Entity*)client->GetPlayer()->GetTarget();
if (!world.GetGroupManager()->IsInGroup(gmi->group_id, target)) {
client->SimpleMessage(CHANNEL_COMMAND_TEXT, "Target is not in your group.");
return;
}
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
if (group)
{
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
for (int8 i = 0; i < members->size(); i++) {
GroupMemberInfo* gmi2 = members->at(i);
if(!gmi2 || !gmi2->member)
continue;
if (gmi2->member->IsBot() && ((Bot*)gmi2->member)->GetOwner() == client->GetPlayer()) {
((Bot*)gmi2->member)->SetMainTank(target);
client->Message(CHANNEL_COMMAND_TEXT, "Setting main tank for %s to %s", gmi2->member->GetName(), target->GetName());
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
return;
}
else if (strncasecmp("delete", sep->arg[0], 6) == 0) {
if (sep->IsSet(1) && sep->IsNumber(1)) {
int32 index = atoi(sep->arg[1]);
// Check if bot is currently spawned and if so camp it out
if (client->GetPlayer()->SpawnedBots.count(index) > 0) {
Spawn* bot = client->GetCurrentZone()->GetSpawnByID(client->GetPlayer()->SpawnedBots[index]);
if (bot && bot->IsBot())
((Bot*)bot)->Camp();
}
database.DeleteBot(client->GetCharacterID(), index);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Bot has been deleted.");
return;
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must give the id (from /bot list) to delete a bot");
return;
}
}
else if (strncasecmp("follow", sep->arg[0], 6) == 0) {
if (sep->IsSet(1) && sep->IsNumber(1)) {
int32 index = atoi(sep->arg[1]);
// Check if bot is currently spawned and if so camp it out
if (client->GetPlayer()->SpawnedBots.count(index) > 0) {
Spawn* bot = client->GetCurrentZone()->GetSpawnByID(client->GetPlayer()->SpawnedBots[index]);
if (bot && bot->IsBot())
((Bot*)bot)->SetFollowTarget(client->GetPlayer(), 5);
}
return;
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must give the id (from /bot list) to have a bot follow you");
return;
}
}
else if (strncasecmp("stopfollow", sep->arg[0], 10) == 0) {
if (sep->IsSet(1) && sep->IsNumber(1)) {
int32 index = atoi(sep->arg[1]);
// Check if bot is currently spawned and if so camp it out
if (client->GetPlayer()->SpawnedBots.count(index) > 0) {
Spawn* bot = client->GetCurrentZone()->GetSpawnByID(client->GetPlayer()->SpawnedBots[index]);
if (bot && bot->IsBot())
((Bot*)bot)->SetFollowTarget(nullptr);
}
return;
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must give the id (from /bot list) to stop a following bot");
return;
}
}
else if (strncasecmp("summon", sep->arg[0], 6) == 0) {
if (sep->IsSet(1) && strncasecmp("group", sep->arg[1], 5) == 0) {
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
if (gmi) {
Player* player = client->GetPlayer();
PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
if (group)
{
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
for (int8 i = 0; i < members->size(); i++) {
Entity* member = members->at(i)->member;
if(!member)
continue;
if (member->IsBot() && ((Bot*)member)->GetOwner() == player) {
if(member->GetZone() && member->GetLocation() != player->GetLocation()) {
member->SetLocation(player->GetLocation());
}
member->SetX(player->GetX());
member->SetY(player->GetY());
member->SetZ(player->GetZ());
client->Message(CHANNEL_COLOR_YELLOW, "Summoning %s.", member->GetName());
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
return;
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not in a group.");
return;
}
}
else {
if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsBot()) {
Bot* bot = (Bot*)client->GetPlayer()->GetTarget();
Player* player = client->GetPlayer();
if (bot && bot->GetOwner() == player) {
bot->SetLocation(player->GetLocation());
bot->SetX(player->GetX());
bot->SetY(player->GetY());
bot->SetZ(player->GetZ());
client->Message(CHANNEL_COLOR_YELLOW, "Summoning %s.", bot->GetName());
return;
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can only summon your own bots.");
return;
}
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must target a bot.");
return;
}
}
}
else if (strncasecmp("test", sep->arg[0], 4) == 0) {
if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsBot()) {
((Bot*)client->GetPlayer()->GetTarget())->MessageGroup("Test message");
return;
}
}
}
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "BotCommands:");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot create [race] [gender] [class] [name]");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot customize - customize the appearance of the bot");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot list - list all the bots you have created with this character");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot spawn [id] - spawns a bot into the world, id obtained from /bot list");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot inv [give/list/remove] - manage bot equipment, for remove a slot must be provided");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot settings [helm/hood/cloak/taunt] [0/1] - Turn setting on (1) or off(0)");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot camp - removes the bot from your group and despawns them");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot attack - commands your bots to attack your target");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot spells - lists bot spells, not fully implemented yet");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot maintank - sets targeted group member as the main tank for your bots");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot delete [id] - deletes the bot with the given id (obtained from /bot list)");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot help");
}
void Commands::Command_Bot_Create(Client* client, Seperator* sep) {
int8 race = BARBARIAN;
int8 gender = 0;
int8 advClass = GUARDIAN;
string name;
if (sep) {
if (sep->IsSet(0) && sep->IsNumber(0))
race = atoi(sep->arg[0]);
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "First param of \"/bot create\" needs to be a number");
return;
}
if (sep->IsSet(1) && sep->IsNumber(1))
gender = atoi(sep->arg[1]);
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Second param of \"/bot create\" needs to be a number");
return;
}
if (sep->IsSet(2) && sep->IsNumber(2))
advClass = atoi(sep->arg[2]);
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Third param of \"/bot create\" needs to be a number");
return;
}
if (sep->IsSet(3)) {
name = string(sep->arg[3]);
transform(name.begin(), name.begin() + 1, name.begin(), ::toupper);
transform(name.begin() + 1, name.end(), name.begin() + 1, ::tolower);
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Fourth param (name) of \"/bot create\" is required");
return;
}
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Syntax: /bot create [race ID] [Gender ID] [class ID] [name]");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "All parameters are required. /bot help race or /bot help class for ID's.");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Gender ID's: 0 = Female, 1 = Male");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Ex: /bot create 0 0 3 Botty");
return;
}
int8 result = database.CheckNameFilter(name.c_str());
if (result == BADNAMELENGTH_REPLY) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Name length is invalid, must be greater then 3 characters and less then 16.");
return;
}
else if (result == NAMEINVALID_REPLY) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is invalid, can only contain letters.");
return;
}
else if (result == NAMETAKEN_REPLY) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is already taken, please choose another.");
return;
}
else if (result == NAMEFILTER_REPLY) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Name failed the filter check.");
return;
}
else if (result == UNKNOWNERROR_REPLY) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Unknown error while checking the name.");
return;
}
string race_string;
switch (race) {
case BARBARIAN:
race_string = "/barbarian/barbarian";
break;
case DARK_ELF:
race_string = "/darkelf/darkelf";
break;
case DWARF:
race_string = "/dwarf/dwarf";
break;
case ERUDITE:
race_string = "/erudite/erudite";
break;
case FROGLOK:
race_string = "/froglok/froglok";
break;
case GNOME:
race_string = "/gnome/gnome";
break;
case HALF_ELF:
race_string = "/halfelf/halfelf";
break;
case HALFLING:
race_string = "/halfling/halfling";
break;
case HIGH_ELF:
race_string = "/highelf/highelf";
break;
case HUMAN:
race_string = "/human/human";
break;
case IKSAR:
race_string = "/iksar/iksar";
break;
case KERRA:
race_string = "/kerra/kerra";
break;
case OGRE:
race_string = "/ogre/ogre";
break;
case RATONGA:
race_string = "/ratonga/ratonga";
break;
case TROLL:
race_string = "/troll/troll";
break;
case WOOD_ELF:
race_string = "/woodelf/woodelf";
break;
case FAE:
race_string = "/fae/fae_light";
break;
case ARASAI:
race_string = "/fae/fae_dark";
break;
case SARNAK:
gender == 1 ? race_string = "01/sarnak_male/sarnak" : race_string = "01/sarnak_female/sarnak";
break;
case VAMPIRE:
race_string = "/vampire/vampire";
break;
case AERAKYN:
race_string = "/aerakyn/aerakyn";
break;
}
if (race_string.length() > 0) {
string gender_string;
Bot* bot = 0;
gender == 1 ? gender_string = "male" : gender_string = "female";
vector<int16>* id_list = database.GetAppearanceIDsLikeName("ec/pc" + race_string + "_" + gender_string);
if (id_list) {
bot = new Bot();
memset(&bot->appearance, 0, sizeof(bot->appearance));
bot->appearance.pos.collision_radius = 32;
bot->secondary_command_list_id = 0;
bot->primary_command_list_id = 0;
bot->appearance.display_name = 1;
bot->appearance.show_level = 1;
bot->appearance.attackable = 1;
bot->appearance.show_command_icon = 1;
bot->appearance.targetable = 1;
bot->appearance.race = race;
bot->appearance.gender = gender;
bot->SetID(Spawn::NextID());
bot->SetX(client->GetPlayer()->GetX());
bot->SetY(client->GetPlayer()->GetY());
bot->SetZ(client->GetPlayer()->GetZ());
bot->SetHeading(client->GetPlayer()->GetHeading());
bot->SetSpawnOrigX(bot->GetX());
bot->SetSpawnOrigY(bot->GetY());
bot->SetSpawnOrigZ(bot->GetZ());
bot->SetSpawnOrigHeading(bot->GetHeading());
bot->SetLocation(client->GetPlayer()->GetLocation());
bot->SetInitialState(16512);
bot->SetModelType(id_list->at(0));
bot->SetAdventureClass(advClass);
bot->SetLevel(client->GetPlayer()->GetLevel());
bot->SetName(name.c_str());
bot->SetDifficulty(6);
bot->size = 32;
if (bot->GetTotalHP() == 0) {
bot->SetTotalHP(25 * bot->GetLevel() + 1);
bot->SetTotalHPBaseInstance(bot->GetTotalHP());
bot->SetHP(25 * bot->GetLevel() + 1);
}
if (bot->GetTotalPower() == 0) {
bot->SetTotalPower(25 * bot->GetLevel() + 1);
bot->SetTotalPowerBaseInstance(bot->GetTotalPower());
bot->SetPower(25 * bot->GetLevel() + 1);
}
bot->SetOwner(client->GetPlayer());
bot->GetNewSpells();
client->GetCurrentZone()->AddSpawn(bot);
int32 index;
int32 bot_id = database.CreateNewBot(client->GetCharacterID(), name, race, advClass, gender, id_list->at(0), index);
if (bot_id == 0) {
LogWrite(PLAYER__ERROR, 0, "Player", "Error saving bot to DB. Bot was not saved!");
client->SimpleMessage(CHANNEL_ERROR, "Error saving bot to DB. Bot was not saved!");
}
else {
bot->BotID = bot_id;
bot->BotIndex = index;
client->GetPlayer()->SpawnedBots[bot->BotIndex] = bot->GetID();
// Add Items
database.SetBotStartingItems(bot, advClass, race);
}
}
else {
client->SimpleMessage(CHANNEL_COLOR_RED, "Error finding the id list for your race, please verify the race id.");
}
safe_delete(id_list);
}
else
client->SimpleMessage(CHANNEL_COLOR_RED, "Error finding the race string, please verify the race id.");
}
void Commands::Command_Bot_Customize(Client* client, Seperator* sep) {
Bot* bot = 0;
if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsBot())
bot = (Bot*)client->GetPlayer()->GetTarget();
client->Message(CHANNEL_COLOR_RED, "This command is disabled and requires new implementation.");
/*if (bot && bot->GetOwner() == client->GetPlayer()) {
PacketStruct* packet = configReader.getStruct("WS_OpenCharCust", client->GetVersion());
if (packet) {
AppearanceData* botApp = &bot->appearance;
CharFeatures* botFeatures = &bot->features;
AppearanceData* playerApp = &client->GetPlayer()->appearance;
CharFeatures* playerFeatures = &client->GetPlayer()->features;
memcpy(&client->GetPlayer()->SavedApp, playerApp, sizeof(AppearanceData));
memcpy(&client->GetPlayer()->SavedFeatures, playerFeatures, sizeof(CharFeatures));
client->GetPlayer()->custNPC = true;
client->GetPlayer()->custNPCTarget = bot;
memcpy(playerApp, botApp, sizeof(AppearanceData));
memcpy(playerFeatures, botFeatures, sizeof(CharFeatures));
client->GetPlayer()->changed = true;
client->GetPlayer()->info_changed = true;
client->GetCurrentZone()->SendSpawnChanges(client->GetPlayer(), client);
packet->setDataByName("race_id", 255);
client->QueuePacket(packet->serialize());
}
}*/
}
void Commands::Command_Bot_Spawn(Client* client, Seperator* sep) {
if (sep && sep->IsSet(0) && sep->IsNumber(0)) {
int32 bot_id = atoi(sep->arg[0]);
if (client->GetPlayer()->SpawnedBots.count(bot_id) > 0) {
client->Message(CHANNEL_COLOR_YELLOW, "The bot with id %u is already spawned.", bot_id);
return;
}
Bot* bot = new Bot();
memset(&bot->appearance, 0, sizeof(bot->appearance));
if (database.LoadBot(client->GetCharacterID(), bot_id, bot)) {
bot->SetFollowTarget(client->GetPlayer(), 5);
bot->appearance.pos.collision_radius = 32;
bot->secondary_command_list_id = 0;
bot->primary_command_list_id = 0;
bot->appearance.display_name = 1;
bot->appearance.show_level = 1;
bot->appearance.attackable = 1;
bot->appearance.show_command_icon = 1;
bot->appearance.targetable = 1;
bot->SetID(Spawn::NextID());
bot->SetX(client->GetPlayer()->GetX());
bot->SetY(client->GetPlayer()->GetY());
bot->SetZ(client->GetPlayer()->GetZ());
bot->SetHeading(client->GetPlayer()->GetHeading());
bot->SetSpawnOrigX(bot->GetX());
bot->SetSpawnOrigY(bot->GetY());
bot->SetSpawnOrigZ(bot->GetZ());
bot->SetSpawnOrigHeading(bot->GetHeading());
bot->SetLocation(client->GetPlayer()->GetLocation());
bot->SetInitialState(16512);
bot->SetLevel(client->GetPlayer()->GetLevel());
bot->SetDifficulty(6);
bot->size = 32;
if (bot->GetTotalHP() == 0) {
bot->SetTotalHP(25 * bot->GetLevel() + 1);
bot->SetHP(25 * bot->GetLevel() + 1);
}
if (bot->GetTotalPower() == 0) {
bot->SetTotalPower(25 * bot->GetLevel() + 1);
bot->SetPower(25 * bot->GetLevel() + 1);
}
bot->SetOwner(client->GetPlayer());
bot->UpdateWeapons();
bot->CalculateBonuses();
bot->GetNewSpells();
client->GetCurrentZone()->AddSpawn(bot);
if (sep->IsSet(1) && sep->IsNumber(1) && atoi(sep->arg[1]) == 1) {
client->GetCurrentZone()->SendSpawn(bot, client);
int8 result = world.GetGroupManager()->Invite(client->GetPlayer(), bot);
if (result == 0)
client->Message(CHANNEL_COMMANDS, "You invite %s to group with you.", bot->GetName());
else if (result == 1)
client->SimpleMessage(CHANNEL_COMMANDS, "That player is already in a group.");
else if (result == 2)
client->SimpleMessage(CHANNEL_COMMANDS, "That player has been invited to another group.");
else if (result == 3)
client->SimpleMessage(CHANNEL_COMMANDS, "Your group is already full.");
else if (result == 4)
client->SimpleMessage(CHANNEL_COMMANDS, "You have a pending invitation, cancel it first.");
else if (result == 5)
client->SimpleMessage(CHANNEL_COMMANDS, "You cannot invite yourself!");
else if (result == 6)
client->SimpleMessage(CHANNEL_COMMANDS, "Could not locate the player.");
else
client->SimpleMessage(CHANNEL_COMMANDS, "Group invite failed, unknown error!");
}
client->GetPlayer()->SpawnedBots[bot_id] = bot->GetID();
if(bot->IsNPC()) {
((NPC*)bot)->HaltMovement();
}
}
else {
client->Message(CHANNEL_ERROR, "Error spawning bot (%u)", bot_id);
}
}
else {
Command_Bot(client, sep);
}
}
void Commands::Command_Bot_List(Client* client, Seperator* sep) {
string bot_list;
bot_list = database.GetBotList(client->GetCharacterID());
if (!bot_list.empty())
client->SimpleMessage(CHANNEL_COLOR_YELLOW, bot_list.c_str());
}
void Commands::Command_Bot_Inv(Client* client, Seperator* sep) {
if (sep && sep->IsSet(0)) {
if (strncasecmp("give", sep->arg[0], 4) == 0) {
if (client->GetPlayer()->trade) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are already trading.");
return;
}
if (!client->GetPlayer()->GetTarget() || !client->GetPlayer()->GetTarget()->IsBot()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must target a bot");
return;
}
Bot* bot = (Bot*)client->GetPlayer()->GetTarget();
if (bot->trade) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Bot is already in a trade...");
return;
}
if (bot->GetOwner() != client->GetPlayer()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can only trade with your own bot.");
return;
}
Trade* trade = new Trade(client->GetPlayer(), bot);
client->GetPlayer()->trade = trade;
bot->trade = trade;
}
else if (strncasecmp("list", sep->arg[0], 4) == 0) {
if (!client->GetPlayer()->GetTarget() || !client->GetPlayer()->GetTarget()->IsBot()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must target a bot");
return;
}
Bot* bot = (Bot*)client->GetPlayer()->GetTarget();
if (bot->GetOwner() != client->GetPlayer()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can only see the inventory of your own bot.");
return;
}
string item_list = "Bot Items:\nSlot\tName\n";
for (int8 i = 0; i < NUM_SLOTS; i++) {
Item* item = bot->GetEquipmentList()->GetItem(i);
if (item) {
//\\aITEM %u %u:%s\\/a
item_list += to_string(i) + ":\t" + item->CreateItemLink(client->GetVersion(), true) + "\n";
}
}
client->SimpleMessage(CHANNEL_COLOR_YELLOW, item_list.c_str());
}
else if (strncasecmp("remove", sep->arg[0], 6) == 0) {
if (sep->IsSet(1) && sep->IsNumber(1)) {
int8 slot = atoi(sep->arg[1]);
if (slot >= NUM_SLOTS) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Invalid slot");
return;
}
if (!client->GetPlayer()->GetTarget() || !client->GetPlayer()->GetTarget()->IsBot()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must target a bot.");
return;
}
Bot* bot = (Bot*)client->GetPlayer()->GetTarget();
if (bot->GetOwner() != client->GetPlayer()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can only remove items from your own bot.");
return;
}
if (client->GetPlayer()->trade) {
Trade* trade = client->GetPlayer()->trade;
if (trade->GetTradee(client->GetPlayer()) != bot) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are already in a trade.");
return;
}
bot->AddItemToTrade(slot);
}
else {
if (bot->trade) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Your bot is already trading...");
return;
}
Trade* trade = new Trade(client->GetPlayer(), bot);
client->GetPlayer()->trade = trade;
bot->trade = trade;
bot->AddItemToTrade(slot);
}
}
}
else
Command_Bot(client, sep);
}
else
Command_Bot(client, sep);
}
void Commands::Command_Bot_Settings(Client* client, Seperator* sep) {
if (sep && sep->IsSet(0) && sep->IsSet(1) && sep->IsNumber(1)) {
if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsBot()) {
Bot* bot = (Bot*)client->GetPlayer()->GetTarget();
if (bot->GetOwner() == client->GetPlayer()) {
if (strncasecmp("helm", sep->arg[0], 4) == 0) {
bot->ShowHelm = (atoi(sep->arg[1]) == 1) ? true : false;
bot->info_changed = true;
bot->changed = true;
bot->GetZone()->SendSpawnChanges(bot);
}
else if (strncasecmp("cloak", sep->arg[0], 5) == 0) {
bot->ShowCloak = (atoi(sep->arg[1]) == 1) ? true : false;
bot->info_changed = true;
bot->changed = true;
bot->GetZone()->SendSpawnChanges(bot);
}
else if (strncasecmp("taunt", sep->arg[0], 5) == 0) {
bot->CanTaunt = (atoi(sep->arg[1]) == 1) ? true : false;
}
else if (strncasecmp("hood", sep->arg[0], 4) == 0) {
bot->SetHideHood((atoi(sep->arg[0]) == 1) ? 0 : 1);
}
else
Command_Bot(client, sep);
}
else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can only change settings on your own bot.");
}
else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You must target a bot.");
}
else
Command_Bot(client, sep);
}
void Commands::Command_Bot_Help(Client* client, Seperator* sep) {
if (sep && sep->IsSet(0)) {
if (strncasecmp("race", sep->arg[0], 4) == 0) {
string title = "Race ID's";
string details;
details += "0\tBarbarian\n";
details += "1\tDark Elf\n";
details += "2\tDwarf\n";
details += "3\tErudite\n";
details += "4\tFroglok\n";
details += "5\tGnome\n";
details += "6\tHalf Elf\n";
details += "7\tHalfling\n";
details += "8\tHigh Elf\n";
details += "9\tHuman\n";
details += "10\tIksar\n";
details += "11\tKerra\n";
details += "12\tOgre\n";
details += "13\tRatonga\n";
details += "14\tTroll\n";
details += "15\tWood Elf\n";
details += "16\tFae\n";
details += "17\tArasai\n";
details += "18\tSarnak\n";
details += "19\tVampire\n";
details += "20\tAerakyn\n";
client->SendShowBook(client->GetPlayer(), title, 0, 1, details);
return;
}
else if (strncasecmp("class", sep->arg[0], 5) == 0) {
string title = "Class ID's";
string details;
details += "0\tCOMMONER\n";
details += "1\tFIGHTER\n";
details += "2\tWARRIOR\n";
details += "3\tGUARDIAN\n";
details += "4\tBERSERKER\n";
details += "5\tBRAWLER\n";
details += "6\tMONK\n";
details += "7\tBRUISER\n";
details += "8\tCRUSADER\n";
details += "9\tSHADOWKNIGHT\n";
details += "10\tPALADIN\n";
details += "11\tPRIEST\n";
details += "12\tCLERIC\n";
details += "13\tTEMPLAR\n";
details += "14\tINQUISITOR\n";
details += "15\tDRUID\n";
details += "16\tWARDEN\n";
details += "17\tFURY\n";
details += "18\tSHAMAN\n";
details += "19\tMYSTIC\n";
details += "20\tDEFILER\n";
string details2 = "21\tMAGE\n";
details2 += "22\tSORCERER\n";
details2 += "23\tWIZARD\n";
details2 += "24\tWARLOCK\n";
details2 += "25\tENCHANTER\n";
details2 += "26\tILLUSIONIST\n";
details2 += "27\tCOERCER\n";
details2 += "28\tSUMMONER\n";
details2 += "29\tCONJUROR\n";
details2 += "30\tNECROMANCER\n";
details2 += "31\tSCOUT\n";
details2 += "32\tROGUE\n";
details2 += "33\tSWASHBUCKLER\n";
details2 += "34\tBRIGAND\n";
details2 += "35\tBARD\n";
details2 += "36\tTROUBADOR\n";
details2 += "37\tDIRGE\n";
details2 += "38\tPREDATOR\n";
details2 += "39\tRANGER\n";
details2 += "40\tASSASSIN\n";
string details3 = "\\#FF0000Following aren't implemented yet.\\#000000\n";
details3 += "41\tANIMALIST\n";
details3 += "42\tBEASTLORD\n";
details3 += "43\tSHAPER\n";
details3 += "44\tCHANNELER\n";
client->SendShowBook(client->GetPlayer(), title, 0, 3, details, details2, details3);
return;
}
}
else {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Bot help is WIP.");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot help race - race id list");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "/bot help class - class id list");
}
}

467
old/world/Bots/BotDB.cpp Normal file
View File

@ -0,0 +1,467 @@
#include "../WorldDatabase.h"
#include "../../common/Log.h"
#include "Bot.h"
#include "../classes.h"
#include "../races.h"
extern Classes classes;
extern Races races;
int32 WorldDatabase::CreateNewBot(int32 char_id, string name, int8 race, int8 advClass, int8 gender, int16 model_id, int32& index) {
DatabaseResult result;
index = 0;
if (!database_new.Select(&result, "SELECT MAX(`bot_id`) FROM `bots` WHERE `char_id` = %u", char_id)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return 0;
}
if (result.Next()) {
if (result.IsNull(0))
index = 1;
else
index = result.GetInt32(0) + 1;
}
if (!database_new.Query("INSERT INTO `bots` (`char_id`, `bot_id`, `name`, `race`, `class`, `gender`, `model_type`) VALUES (%u, %u, \"%s\", %u, %u, %u, %u)", char_id, index, name.c_str(), race, advClass, gender, model_id)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return 0;
}
int32 ret = database_new.LastInsertID();
LogWrite(PLAYER__DEBUG, 0, "Player", "New bot (%s) created for player (%u)", name.c_str(), char_id);
return ret;
}
void WorldDatabase::SaveBotAppearance(Bot* bot) {
SaveBotColors(bot->BotID, "skin_color", bot->features.skin_color);
SaveBotColors(bot->BotID, "model_color", bot->features.model_color);
SaveBotColors(bot->BotID, "eye_color", bot->features.eye_color);
SaveBotColors(bot->BotID, "hair_color1", bot->features.hair_color1);
SaveBotColors(bot->BotID, "hair_color2", bot->features.hair_color2);
SaveBotColors(bot->BotID, "hair_highlight", bot->features.hair_highlight_color);
SaveBotColors(bot->BotID, "hair_type_color", bot->features.hair_type_color);
SaveBotColors(bot->BotID, "hair_type_highlight_color", bot->features.hair_type_highlight_color);
SaveBotColors(bot->BotID, "hair_face_color", bot->features.hair_face_color);
SaveBotColors(bot->BotID, "hair_face_highlight_color", bot->features.hair_face_highlight_color);
SaveBotColors(bot->BotID, "wing_color1", bot->features.wing_color1);
SaveBotColors(bot->BotID, "wing_color2", bot->features.wing_color2);
SaveBotColors(bot->BotID, "shirt_color", bot->features.shirt_color);
//SaveBotColors(bot->BotID, "unknown_chest_color", );
SaveBotColors(bot->BotID, "pants_color", bot->features.pants_color);
//SaveBotColors(bot->BotID, "unknown_legs_color", );
//SaveBotColors(bot->BotID, "unknown9", );
SaveBotFloats(bot->BotID, "eye_type", bot->features.eye_type[0], bot->features.eye_type[1], bot->features.eye_type[2]);
SaveBotFloats(bot->BotID, "ear_type", bot->features.ear_type[0], bot->features.ear_type[1], bot->features.ear_type[2]);
SaveBotFloats(bot->BotID, "eye_brow_type", bot->features.eye_brow_type[0], bot->features.eye_brow_type[1], bot->features.eye_brow_type[2]);
SaveBotFloats(bot->BotID, "cheek_type", bot->features.cheek_type[0], bot->features.cheek_type[1], bot->features.cheek_type[2]);
SaveBotFloats(bot->BotID, "lip_type", bot->features.lip_type[0], bot->features.lip_type[1], bot->features.lip_type[2]);
SaveBotFloats(bot->BotID, "chin_type", bot->features.chin_type[0], bot->features.chin_type[1], bot->features.chin_type[2]);
SaveBotFloats(bot->BotID, "nose_type", bot->features.nose_type[0], bot->features.nose_type[1], bot->features.nose_type[2]);
SaveBotFloats(bot->BotID, "body_size", bot->features.body_size, 0, 0);
SaveBotFloats(bot->BotID, "body_age", bot->features.body_age, 0, 0);
SaveBotColors(bot->BotID, "soga_skin_color", bot->features.soga_skin_color);
SaveBotColors(bot->BotID, "soga_model_color", bot->features.soga_model_color);
SaveBotColors(bot->BotID, "soga_eye_color", bot->features.soga_eye_color);
SaveBotColors(bot->BotID, "soga_hair_color1", bot->features.soga_hair_color1);
SaveBotColors(bot->BotID, "soga_hair_color2", bot->features.soga_hair_color2);
SaveBotColors(bot->BotID, "soga_hair_highlight", bot->features.soga_hair_highlight_color);
SaveBotColors(bot->BotID, "soga_hair_type_color", bot->features.soga_hair_type_color);
SaveBotColors(bot->BotID, "soga_hair_type_highlight_color", bot->features.soga_hair_type_highlight_color);
SaveBotColors(bot->BotID, "soga_hair_face_color", bot->features.soga_hair_face_color);
SaveBotColors(bot->BotID, "soga_hair_face_highlight_color", bot->features.soga_hair_face_highlight_color);
SaveBotColors(bot->BotID, "soga_wing_color1", bot->features.wing_color1);
SaveBotColors(bot->BotID, "soga_wing_color2", bot->features.wing_color2);
SaveBotColors(bot->BotID, "soga_shirt_color", bot->features.shirt_color);
//SaveBotColors(bot->BotID, "soga_unknown_chest_color", );
SaveBotColors(bot->BotID, "soga_pants_color", bot->features.pants_color);
//SaveBotColors(bot->BotID, "soga_unknown_legs_color", );
//SaveBotColors(bot->BotID, "soga_unknown13", );
SaveBotFloats(bot->BotID, "soga_eye_type", bot->features.soga_eye_type[0], bot->features.soga_eye_type[1], bot->features.soga_eye_type[2]);
SaveBotFloats(bot->BotID, "soga_ear_type", bot->features.soga_ear_type[0], bot->features.soga_ear_type[1], bot->features.soga_ear_type[2]);
SaveBotFloats(bot->BotID, "soga_eye_brow_type", bot->features.soga_eye_brow_type[0], bot->features.soga_eye_brow_type[1], bot->features.soga_eye_brow_type[2]);
SaveBotFloats(bot->BotID, "soga_cheek_type", bot->features.soga_cheek_type[0], bot->features.soga_cheek_type[1], bot->features.soga_cheek_type[2]);
SaveBotFloats(bot->BotID, "soga_lip_type", bot->features.soga_lip_type[0], bot->features.soga_lip_type[1], bot->features.soga_lip_type[2]);
SaveBotFloats(bot->BotID, "soga_chin_type", bot->features.soga_chin_type[0], bot->features.soga_chin_type[1], bot->features.soga_chin_type[2]);
SaveBotFloats(bot->BotID, "soga_nose_type", bot->features.soga_nose_type[0], bot->features.soga_nose_type[1], bot->features.soga_nose_type[2]);
if (!database_new.Query("UPDATE `bots` SET `model_type` = %u, `hair_type` = %u, `face_type` = %u, `wing_type` = %u, `chest_type` = %u, `legs_type` = %u, `soga_model_type` = %u, `soga_hair_type` = %u, `soga_face_type` = %u WHERE `id` = %u",
bot->GetModelType(), bot->GetHairType(), bot->GetFacialHairType(), bot->GetWingType(), bot->GetChestType(), bot->GetLegsType(), bot->GetSogaModelType(), bot->GetSogaHairType(), bot->GetSogaFacialHairType(), bot->BotID)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
}
void WorldDatabase::SaveBotColors(int32 bot_id, const char* type, EQ2_Color color) {
if (!database_new.Query("INSERT INTO `bot_appearance` (`bot_id`, `type`, `red`, `green`, `blue`) VALUES (%i, '%s', %i, %i, %i) ON DUPLICATE KEY UPDATE `red` = %i, `blue` = %i, `green` = %i", bot_id, type, color.red, color.green, color.blue, color.red, color.blue, color.green)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
}
void WorldDatabase::SaveBotFloats(int32 bot_id, const char* type, float float1, float float2, float float3) {
if (!database_new.Query("INSERT INTO `bot_appearance` (`bot_id`, `type`, `red`, `green`, `blue`, `signed_value`) VALUES (%i, '%s', %i, %i, %i, 1) ON DUPLICATE KEY UPDATE `red` = %i, `blue` = %i, `green` = %i", bot_id, type, float1, float2, float3, float1, float2, float3)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
}
bool WorldDatabase::LoadBot(int32 char_id, int32 bot_index, Bot* bot) {
DatabaseResult result;
if (!database_new.Select(&result, "SELECT * FROM bots WHERE `char_id` = %u AND `bot_id` = %u", char_id, bot_index)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return false;
}
if (result.Next()) {
bot->BotID = result.GetInt32(0);
bot->BotIndex = result.GetInt32(2);
bot->SetName(result.GetString(3));
bot->SetRace(result.GetInt8(4));
bot->SetAdventureClass(result.GetInt8(5));
bot->SetGender(result.GetInt8(6));
bot->SetModelType(result.GetInt16(7));
bot->SetHairType(result.GetInt16(8));
bot->SetFacialHairType(result.GetInt16(9));
bot->SetWingType(result.GetInt16(10));
bot->SetChestType(result.GetInt16(11));
bot->SetLegsType(result.GetInt16(12));
bot->SetSogaModelType(result.GetInt16(13));
bot->SetSogaHairType(result.GetInt16(14));
bot->SetSogaFacialHairType(result.GetInt16(15));
}
else
return false;
LoadBotAppearance(bot);
LoadBotEquipment(bot);
return true;
}
void WorldDatabase::LoadBotAppearance(Bot* bot) {
DatabaseResult result;
string type;
map<string, int8> appearance_types;
EQ2_Color color;
color.red = 0;
color.green = 0;
color.blue = 0;
if (!database_new.Select(&result, "SELECT distinct `type` FROM bot_appearance WHERE length(`type`) > 0 AND `bot_id` = %u", bot->BotID)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
while (result.Next()) {
type = result.GetString(0);
appearance_types[type] = GetAppearanceType(type);
if (appearance_types[type] == 255)
LogWrite(WORLD__ERROR, 0, "Appearance", "Unknown appearance type '%s' in LoadBotAppearances.", type.c_str());
}
if (!database_new.Select(&result, "SELECT `type`, `signed_value`, `red`, `green`, `blue` FROM bot_appearance WHERE length(`type`) > 0 AND bot_id = %u", bot->BotID)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
while (result.Next()) {
type = result.GetString(0);
if (appearance_types[type] < APPEARANCE_SOGA_EBT) {
color.red = result.GetInt8(2);
color.green = result.GetInt8(3);
color.blue = result.GetInt8(4);
}
switch (appearance_types[type]) {
case APPEARANCE_SOGA_HFHC: {
bot->features.soga_hair_face_highlight_color = color;
break;
}
case APPEARANCE_SOGA_HTHC: {
bot->features.soga_hair_type_highlight_color = color;
break;
}
case APPEARANCE_SOGA_HFC: {
bot->features.soga_hair_face_color = color;
break;
}
case APPEARANCE_SOGA_HTC: {
bot->features.soga_hair_type_color = color;
break;
}
case APPEARANCE_SOGA_HH: {
bot->features.soga_hair_highlight_color = color;
break;
}
case APPEARANCE_SOGA_HC1: {
bot->features.soga_hair_color1 = color;
break;
}
case APPEARANCE_SOGA_HC2: {
bot->features.soga_hair_color2 = color;
break;
}
case APPEARANCE_SOGA_SC: {
bot->features.soga_skin_color = color;
break;
}
case APPEARANCE_SOGA_EC: {
bot->features.soga_eye_color = color;
break;
}
case APPEARANCE_HTHC: {
bot->features.hair_type_highlight_color = color;
break;
}
case APPEARANCE_HFHC: {
bot->features.hair_face_highlight_color = color;
break;
}
case APPEARANCE_HTC: {
bot->features.hair_type_color = color;
break;
}
case APPEARANCE_HFC: {
bot->features.hair_face_color = color;
break;
}
case APPEARANCE_HH: {
bot->features.hair_highlight_color = color;
break;
}
case APPEARANCE_HC1: {
bot->features.hair_color1 = color;
break;
}
case APPEARANCE_HC2: {
bot->features.hair_color2 = color;
break;
}
case APPEARANCE_WC1: {
bot->features.wing_color1 = color;
break;
}
case APPEARANCE_WC2: {
bot->features.wing_color2 = color;
break;
}
case APPEARANCE_SC: {
bot->features.skin_color = color;
break;
}
case APPEARANCE_EC: {
bot->features.eye_color = color;
break;
}
case APPEARANCE_SOGA_EBT: {
for (int i = 0; i < 3; i++)
bot->features.soga_eye_brow_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_SOGA_CHEEKT: {
for (int i = 0; i < 3; i++)
bot->features.soga_cheek_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_SOGA_NT: {
for (int i = 0; i < 3; i++)
bot->features.soga_nose_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_SOGA_CHINT: {
for (int i = 0; i < 3; i++)
bot->features.soga_chin_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_SOGA_LT: {
for (int i = 0; i < 3; i++)
bot->features.soga_lip_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_SOGA_EART: {
for (int i = 0; i < 3; i++)
bot->features.soga_ear_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_SOGA_EYET: {
for (int i = 0; i < 3; i++)
bot->features.soga_eye_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_EBT: {
for (int i = 0; i < 3; i++)
bot->features.eye_brow_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_CHEEKT: {
for (int i = 0; i < 3; i++)
bot->features.cheek_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_NT: {
for (int i = 0; i < 3; i++)
bot->features.nose_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_CHINT: {
for (int i = 0; i < 3; i++)
bot->features.chin_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_EART: {
for (int i = 0; i < 3; i++)
bot->features.ear_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_EYET: {
for (int i = 0; i < 3; i++)
bot->features.eye_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_LT: {
for (int i = 0; i < 3; i++)
bot->features.lip_type[i] = result.GetSInt8(2 + i);
break;
}
case APPEARANCE_SHIRT: {
bot->features.shirt_color = color;
break;
}
case APPEARANCE_UCC: {
break;
}
case APPEARANCE_PANTS: {
bot->features.pants_color = color;
break;
}
case APPEARANCE_ULC: {
break;
}
case APPEARANCE_U9: {
break;
}
case APPEARANCE_BODY_SIZE: {
bot->features.body_size = color.red;
break;
}
case APPEARANCE_SOGA_WC1: {
break;
}
case APPEARANCE_SOGA_WC2: {
break;
}
case APPEARANCE_SOGA_SHIRT: {
break;
}
case APPEARANCE_SOGA_UCC: {
break;
}
case APPEARANCE_SOGA_PANTS: {
break;
}
case APPEARANCE_SOGA_ULC: {
break;
}
case APPEARANCE_SOGA_U13: {
break;
}
case APPEARANCE_BODY_AGE: {
bot->features.body_age = color.red;
break;
}
case APPEARANCE_MC:{
bot->features.model_color = color;
break;
}
case APPEARANCE_SMC:{
bot->features.soga_model_color = color;
break;
}
case APPEARANCE_SBS: {
bot->features.soga_body_size = color.red;
break;
}
case APPEARANCE_SBA: {
bot->features.soga_body_age = color.red;
break;
}
}
}
}
void WorldDatabase::SaveBotItem(int32 bot_id, int32 item_id, int8 slot) {
if (!database_new.Query("INSERT INTO `bot_equipment` (`bot_id`, `slot`, `item_id`) VALUES (%u, %u, %u) ON DUPLICATE KEY UPDATE `item_id` = %u", bot_id, slot, item_id, item_id)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
}
void WorldDatabase::LoadBotEquipment(Bot* bot) {
DatabaseResult result;
if (!database_new.Select(&result, "SELECT `slot`, `item_id` FROM `bot_equipment` WHERE `bot_id` = %u", bot->BotID)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
Item* master_item = 0;
Item* item = 0;
while (result.Next()) {
int8 slot = result.GetInt8(0);
int32 item_id = result.GetInt32(1);
master_item = master_item_list.GetItem(item_id);
if (master_item) {
item = new Item(master_item);
if (item) {
bot->GetEquipmentList()->AddItem(slot, item);
bot->SetEquipment(item, slot);
}
}
}
}
string WorldDatabase::GetBotList(int32 char_id) {
DatabaseResult result;
string ret;
if (!database_new.Select(&result, "SELECT `bot_id`, `name`, `race`, `class` FROM `bots` WHERE `char_id` = %u", char_id)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return ret;
}
while (result.Next()) {
ret += to_string(result.GetInt32(0)) + ": ";
ret += result.GetString(1);
ret += " the ";
ret += races.GetRaceNameCase(result.GetInt8(2));
ret += " ";
ret += classes.GetClassNameCase(result.GetInt8(3)) + "\n";
}
return ret;
}
void WorldDatabase::DeleteBot(int32 char_id, int32 bot_index) {
if (!database_new.Query("DELETE FROM `bots` WHERE `char_id` = %u AND `bot_id` = %u", char_id, bot_index)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
}
}
void WorldDatabase::SetBotStartingItems(Bot* bot, int8 class_id, int8 race_id) {
int32 bot_id = bot->BotID;
LogWrite(PLAYER__DEBUG, 0, "Bot", "Adding default items for race: %u, class: %u for bot_id: %u", race_id, class_id, bot_id);
DatabaseResult result;
if (!database_new.Select(&result, "SELECT item_id FROM starting_items WHERE class_id IN (%i, %i, %i, 255) AND race_id IN (%i, 255) ORDER BY id", classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
return;
}
while (result.Next()) {
bot->GiveItem(result.GetInt32(0));
}
}

View File

@ -0,0 +1,957 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BrokerManager.h"
#include "../Items/Items.h"
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "../World.h"
#include "../Web/PeerManager.h"
#include <cstdlib>
extern WorldDatabase database;
extern ZoneList zone_list;
extern PeerManager peer_manager;
BrokerManager::BrokerManager() {
}
void BrokerManager::AddSeller(int32 cid,
const std::string& name,
int32 hid,
bool enabled,
bool invFlag)
{
int64 prevSession = 0, prevTotal = 0;
{
std::shared_lock lock(mtx_);
auto sit = players_.find(cid);
if (sit != players_.end()) {
prevSession = sit->second.coin_session;
prevTotal = sit->second.coin_total;
}
}
SellerInfo info{cid, name, hid, enabled, invFlag, prevSession, prevTotal};
{
std::unique_lock lock(mtx_);
players_[cid] = info;
}
SavePlayerToDB(info);
peer_manager.sendPeersAddSeller(cid, hid, name, enabled, invFlag);
}
void BrokerManager::LoadSeller(int32 cid, const std::string& name,
int32 hid, bool enabled, bool invFlag,
int64 coin_session, int64 coin_total)
{
SellerInfo info{cid, name, hid, enabled, invFlag, coin_session, coin_total};
{
std::unique_lock lock(mtx_);
players_[cid] = info;
}
}
int64 BrokerManager::ResetSellerSessionCoins(int32 cid) {
database.ClearSellerSession(cid);
int64 session_coin = 0;
{
std::unique_lock lock(mtx_);
auto it = players_.find(cid);
if (it == players_.end()) return 0;
session_coin = it->second.coin_session;
it->second.coin_total += it->second.coin_session;
it->second.coin_session = 0;
}
return session_coin;
}
void BrokerManager::AddSellerSessionCoins(int32 cid, uint64 session) {
database.AddToSellerSession(cid, session);
{
std::unique_lock lock(mtx_);
auto it = players_.find(cid);
if (it == players_.end()) return;
it->second.coin_session += session;
}
}
void BrokerManager::RemoveSeller(int32 cid, bool peerCacheOnly)
{
{
std::unique_lock lock(mtx_);
players_.erase(cid);
active_items_by_char_.erase(cid);
inactive_items_by_char_.erase(cid);
}
if(!peerCacheOnly)
peer_manager.sendPeersRemoveSeller(cid);
}
void BrokerManager::AddItem(const SaleItem& item, bool peerCacheOnly)
{
{
std::unique_lock lock(mtx_);
auto& a = active_items_by_char_[item.character_id];
auto& i = inactive_items_by_char_[item.character_id];
a.erase(item.unique_id);
i.erase(item.unique_id);
if (item.for_sale) a[item.unique_id] = item;
else i[item.unique_id] = item;
}
SaveItemToDB(item);
if(!peerCacheOnly)
peer_manager.sendPeersAddItemSale(item.character_id, item.house_id, item.item_id, item.unique_id, item.cost_copper, item.inv_slot_id,
item.slot_id, item.count, item.from_inventory, item.for_sale, item.creator);
}
void BrokerManager::LoadItem(const SaleItem& item)
{
std::unique_lock lock(mtx_);
if (item.for_sale)
active_items_by_char_[item.character_id][item.unique_id] = item;
else
inactive_items_by_char_[item.character_id][item.unique_id] = item;
}
void BrokerManager::SetSaleStatus(int32 cid, int64 uid, bool for_sale)
{
std::optional<SaleItem> toUpdate;
{
std::unique_lock lock(mtx_);
auto& a = active_items_by_char_[cid];
auto& i = inactive_items_by_char_[cid];
if (for_sale) {
LogWrite(PLAYER__ERROR, 5, "Broker",
"--SetSaleStatus: %u (%u), for_sale=%u",
cid, uid, for_sale
);
if (auto it = i.find(uid); it != i.end()) {
LogWrite(PLAYER__ERROR, 5, "Broker",
"--SetSaleStatusFuckOff: %u (%u), for_sale=%u",
cid, uid, for_sale
);
SaleItem copy = it->second;
copy.for_sale = true;
i.erase(it);
a[uid] = copy;
toUpdate = copy;
}
} else {
LogWrite(PLAYER__ERROR, 5, "Broker",
"--SetSaleStatusInactive: %u (%u), for_sale=%u",
cid, uid, for_sale
);
if (auto it = a.find(uid); it != a.end()) {
LogWrite(PLAYER__ERROR, 5, "Broker",
"--SetSaleStatusFuckyou: %u (%u), for_sale=%u",
cid, uid, for_sale
);
SaleItem copy = it->second;
copy.for_sale = false;
a.erase(it);
i[uid] = copy;
toUpdate = copy;
}
}
}
if (toUpdate) {
SaveItemToDB(*toUpdate);
peer_manager.sendPeersAddItemSale(toUpdate->character_id, toUpdate->house_id, toUpdate->item_id, toUpdate->unique_id, toUpdate->cost_copper, toUpdate->inv_slot_id,
toUpdate->slot_id, toUpdate->count, toUpdate->from_inventory, toUpdate->for_sale, toUpdate->creator);
}
}
bool BrokerManager::IsItemListed(int32 cid, int64 uid) {
std::shared_lock lock(mtx_);
auto& active_map = active_items_by_char_[cid];
auto& inactive_map = inactive_items_by_char_[cid];
auto it = inactive_map.find(uid);
if (it != inactive_map.end()) {
return true;
}
auto it2 = active_map.find(uid);
if (it2 != active_map.end()) {
return true;
}
return false;
}
void BrokerManager::SetSalePrice(int32 cid, int64 uid, int64 price)
{
std::optional<SaleItem> toUpdate;
{
std::unique_lock lock(mtx_);
if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end()) {
if (auto it = ait->second.find(uid); it != ait->second.end()) {
LogWrite(PLAYER__ERROR, 5, "Broker",
"--SetSalePriceActive: %u (%u), cost=%u",
cid, uid, price
);
it->second.cost_copper = price;
toUpdate = it->second;
}
}
if (auto iit = inactive_items_by_char_.find(cid); iit != inactive_items_by_char_.end()) {
if (auto it = iit->second.find(uid); it != iit->second.end()) {
LogWrite(PLAYER__ERROR, 5, "Broker",
"--SetSalePriceInactive: %u (%u), cost=%u",
cid, uid, price
);
it->second.cost_copper = price;
toUpdate = it->second;
}
}
}
if (toUpdate) {
SaveItemToDB(*toUpdate);
peer_manager.sendPeersAddItemSale(toUpdate->character_id, toUpdate->house_id, toUpdate->item_id, toUpdate->unique_id, toUpdate->cost_copper, toUpdate->inv_slot_id,
toUpdate->slot_id, toUpdate->count, toUpdate->from_inventory, toUpdate->for_sale, toUpdate->creator);
}
}
void BrokerManager::RemoveItem(int32 cid, int64 uid, int16 qty, bool shouldDelete)
{
bool didDelete = false;
std::optional<SaleItem> snapshot;
{
std::unique_lock lock(mtx_);
if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end()) {
auto& m = ait->second;
if (auto it = m.find(uid); it != m.end()) {
if(shouldDelete)
qty = it->second.count;
it->second.count -= qty;
if (it->second.count <= 0) {
didDelete = true;
snapshot = it->second;
m.erase(it);
} else {
snapshot = it->second;
}
if (m.empty())
active_items_by_char_.erase(ait);
}
}
}
if (didDelete || shouldDelete) {
DeleteItemFromDB(cid, uid);
peer_manager.sendPeersRemoveItemSale(cid, uid);
}
else if (snapshot) {
SaveItemToDB(*snapshot);
peer_manager.sendPeersAddItemSale(snapshot->character_id, snapshot->house_id, snapshot->item_id, snapshot->unique_id, snapshot->cost_copper, snapshot->inv_slot_id,
snapshot->slot_id, snapshot->count, snapshot->from_inventory, snapshot->for_sale, snapshot->creator);
}
}
bool BrokerManager::BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity)
{
Client* seller = zone_list.GetClientByCharID(seller_cid); // establish if seller is online
if(buyer && buyer->GetCharacterID() == seller_cid) {
buyer->Message(CHANNEL_COLOR_RED, "You cannot buy from yourself!");
return false;
}
if (quantity <= 0 || !IsSaleEnabled(seller_cid) || !IsItemForSale(seller_cid, uid)) {
if(buyer)
buyer->Message(CHANNEL_COLOR_RED, "Quantity not provided (%u), sale is not enabled (sale enabled? %u) or item is not for sale (itemforsale? %u).", quantity, IsSaleEnabled(seller_cid), IsItemForSale(seller_cid, uid));
return false;
}
int64 price = GetSalePrice(seller_cid, uid) * quantity;
if(!buyer || !buyer->GetPlayer() || !buyer->GetPlayer()->RemoveCoins(price)) {
if(buyer)
buyer->Message(CHANNEL_COLOR_RED, "You do not have enough coin to purchase.");
return false;
}
if (!database.RemoveBrokerItem(seller_cid, uid, quantity)) {
buyer->GetPlayer()->AddCoins(price);
buyer->Message(CHANNEL_COLOR_RED, "Failed to remove broker item from database.");
return false;
}
Item* giveItem = nullptr;
int16 result_count = 0;
std::string creator;
bool deleteItem = false;
int16 quantity_left = 0;
// 2) Mirror in-memory
std::optional<SaleItem> toUpdate;
{
std::unique_lock lock(mtx_);
if (auto sit = active_items_by_char_.find(seller_cid); sit != active_items_by_char_.end()) {
auto& m = sit->second;
if (auto it = m.find(uid); it != m.end()) {
creator = it->second.creator;
giveItem = master_item_list.GetItem(it->second.item_id);
SaleItem copy = it->second;
toUpdate = copy;
if (it->second.count > quantity) {
it->second.count -= quantity;
quantity_left = it->second.count;
result_count = quantity;
if(seller && seller->GetPlayer()) {
seller->GetPlayer()->item_list.SetVaultItemUniqueIDCount(seller, uid, it->second.count);
}
}
else {
result_count = it->second.count;
if(seller && seller->GetPlayer()) {
seller->GetPlayer()->item_list.RemoveVaultItemFromUniqueID(seller, uid);
}
m.erase(it);
deleteItem = true;
}
if (m.empty())
active_items_by_char_.erase(sit);
}
}
}
if(!giveItem) {
buyer->GetPlayer()->AddCoins(price);
buyer->Message(CHANNEL_COLOR_RED, "Failed to find item on broker memory.");
if(toUpdate)
AddItem(*toUpdate);
return false;
}
Item* resultItem = new Item(giveItem);
resultItem->details.count = result_count;
resultItem->creator = creator;
if (buyer->GetPlayer()->item_list.HasFreeSlot() || buyer->GetPlayer()->item_list.CanStack(resultItem, false)) {
if(!buyer->AddItem(resultItem, nullptr, AddItemType::BUY_FROM_BROKER)) {
buyer->GetPlayer()->AddCoins(price);
safe_delete(resultItem);
if(toUpdate)
AddItem(*toUpdate);
return false;
}
}
else {
buyer->Message(CHANNEL_COLOR_RED, "You have no free slot available.");
buyer->GetPlayer()->AddCoins(price);
safe_delete(resultItem);
if(toUpdate)
AddItem(*toUpdate);
return false;
}
if(deleteItem) {
DeleteItemFromDB(seller_cid, uid);
DeleteCharacterItemFromDB(seller_cid, uid);
}
else if(quantity_left) {
UpdateCharacterItemDB(seller_cid, uid, quantity_left);
}
AddSellerSessionCoins(seller_cid, price);
LogSale(seller_cid, std::string(buyer->GetPlayer()->GetName()), std::string(resultItem->name), result_count, price);
if(seller) {
std::string logMsg = GetShopPurchaseMessage(buyer->GetPlayer()->GetName(), std::string(resultItem->name), result_count, price);
auto seller_info = GetSellerInfo(seller_cid);
seller->SendHouseSaleLog(logMsg, 0, seller_info ? seller_info->coin_total : 0, 0);
seller->OpenShopWindow(nullptr);
}
return true;
}
void BrokerManager::OnPeerRemoveItem(int32 cid, int64 uid)
{
std::unique_lock lock(mtx_);
if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end())
ait->second.erase(uid);
if (auto iit = inactive_items_by_char_.find(cid); iit != inactive_items_by_char_.end())
iit->second.erase(uid);
}
bool BrokerManager::IsItemForSale(int32 cid, int64 uid) const
{
std::shared_lock lock(mtx_);
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end())
return pit->second.find(uid) != pit->second.end();
return false;
}
int64 BrokerManager::GetSalePrice(int32 cid, int64 uid)
{
std::shared_lock lock(mtx_);
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) {
if (auto it = pit->second.find(uid); it != pit->second.end())
return it->second.cost_copper;
}
if (auto pit2 = inactive_items_by_char_.find(cid); pit2 != inactive_items_by_char_.end()) {
if (auto it = pit2->second.find(uid); it != pit2->second.end())
return it->second.cost_copper;
}
return 0;
}
std::vector<SaleItem> BrokerManager::GetActiveForSaleItems(int32 cid) const
{
std::shared_lock lock(mtx_);
std::vector<SaleItem> out;
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) {
for (auto const& kv : pit->second)
out.push_back(kv.second);
}
return out;
}
std::optional<SaleItem> BrokerManager::GetActiveItem(int32 cid, int64 uid) const
{
std::shared_lock lock(mtx_);
auto pit = active_items_by_char_.find(cid);
if (pit == active_items_by_char_.end())
return std::nullopt;
auto it = pit->second.find(uid);
if (it == pit->second.end())
return std::nullopt;
return it->second; // copy out the SaleItem
}
bool BrokerManager::IsSellingItems(int32 cid, bool vaultOnly) const
{
// Grab shared lock for threadsafe read
std::shared_lock lock(mtx_);
// Find this characters active sales
auto pit = active_items_by_char_.find(cid);
if (pit == active_items_by_char_.end())
return false; // no items => not selling from vault
// Scan through each SaleItem; if any has sell_from_inventory == false,
// that means its coming from the vault
for (auto const& kv : pit->second) {
const SaleItem& item = kv.second;
if ((item.for_sale && (!item.from_inventory || !vaultOnly)))
return true;
}
return false;
}
std::vector<SaleItem> BrokerManager::GetInactiveItems(int32 cid) const
{
std::shared_lock lock(mtx_);
std::vector<SaleItem> out;
if (auto pit = inactive_items_by_char_.find(cid); pit != inactive_items_by_char_.end()) {
for (auto const& kv : pit->second)
out.push_back(kv.second);
}
return out;
}
std::vector<std::pair<int64,int32>>
BrokerManager::GetUniqueIDsAndCost(int32 cid) const
{
std::shared_lock lock(mtx_);
std::vector<std::pair<int64,int32>> out;
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) {
for (auto const& kv : pit->second)
out.emplace_back(kv.second.unique_id, kv.second.cost_copper);
}
return out;
}
vector<Item*>* BrokerManager::GetItems(
const std::string& name,
int64 itype,
int64 ltype,
int64 btype,
int64 minprice,
int64 maxprice,
int8 minskill,
int8 maxskill,
const std::string& seller,
const std::string& adornment,
int8 mintier,
int8 maxtier,
int16 minlevel,
int16 maxlevel,
int8 itemclass
) const {
vector<Item*>* ret = new vector<Item*>;
string lower_name = ::ToLower(string(name.c_str()));
std::shared_lock lock(mtx_);
std::vector<SaleItem> out;
for (auto const& char_pair : active_items_by_char_) {
int32 cid = char_pair.first;
auto pit_player = players_.find(cid);
if (pit_player == players_.end())
continue;
bool allowInv = pit_player->second.sell_from_inventory;
for (auto const& kv : char_pair.second) {
auto const& itm = kv.second;
LogWrite(PLAYER__DEBUG, 5, "Broker",
"--GetItems: %u (selling: %u), allowinv: %u",
itm.unique_id, itm.for_sale, allowInv
);
if (!itm.for_sale) continue;
if (!allowInv && itm.from_inventory) continue;
Item* def = master_item_list.GetItem(itm.item_id);
if (!def) continue;
LogWrite(PLAYER__DEBUG, 5, "Broker",
"--GetItems#1: %u (selling: %u), allowinv: %u",
itm.unique_id, itm.for_sale, allowInv
);
if (!name.empty() && def->lowername.find(lower_name) == std::string::npos) continue;
if (itype!=ITEM_BROKER_TYPE_ANY && itype !=ITEM_BROKER_TYPE_ANY64BIT && !master_item_list.ShouldAddItemBrokerType(def, itype)) continue;
if (ltype!=ITEM_BROKER_SLOT_ANY && !master_item_list.ShouldAddItemBrokerSlot(def, ltype)) continue;
if (btype!=0xFFFFFFFF && !master_item_list.ShouldAddItemBrokerStat(def, btype)) continue;
LogWrite(PLAYER__DEBUG, 5, "Broker",
"--GetItems#2: %u (cost_copper: %u), seller: %s",
itm.unique_id, itm.cost_copper, seller.c_str()
);
//if (itm.cost_copper < minprice || itm.cost_copper > maxprice) continue;
//if (!seller.empty() && pit_player->second.seller_name.find(seller)==std::string::npos) continue;
/*if(mintier > 1 && def->details.tier < mintier)
continue;
if(maxtier > 0 && def->details.tier > maxtier)
continue;*/
LogWrite(PLAYER__DEBUG, 5, "Broker",
"--GetItems#3: %u (selling: %u), allowinv: %u",
itm.unique_id, itm.for_sale, allowInv
);
if(def->generic_info.adventure_default_level == 0 && def->generic_info.tradeskill_default_level == 0 && minlevel > 0 && maxlevel > 0){
if(def->details.recommended_level < minlevel)
continue;
if(def->details.recommended_level > maxlevel)
continue;
}
else{
if(minlevel > 0 && ((def->generic_info.adventure_default_level == 0 && def->generic_info.tradeskill_default_level == 0) || (def->generic_info.adventure_default_level > 0 && def->generic_info.adventure_default_level < minlevel) || (def->generic_info.tradeskill_default_level > 0 && def->generic_info.tradeskill_default_level < minlevel)))
continue;
if(maxlevel > 0 && ((def->generic_info.adventure_default_level > 0 && def->generic_info.adventure_default_level > maxlevel) || (def->generic_info.tradeskill_default_level > 0 && def->generic_info.tradeskill_default_level > maxlevel)))
continue;
}
if (itemclass>0) {
int64 bit = ((int64)2 << (itemclass-1));
if (!(def->generic_info.adventure_classes & bit) &&
!(def->generic_info.tradeskill_classes & bit))
continue;
}
LogWrite(PLAYER__DEBUG, 5, "Broker",
"--GetItemsPass: %u (selling: %u), allowinv: %u",
itm.unique_id, itm.for_sale, allowInv
);
ret->push_back(new Item(def, itm.unique_id, itm.creator, pit_player->second.seller_name, cid, itm.cost_copper, itm.count, pit_player->second.house_id, itm.from_inventory));
}
}
return ret;
}
std::string BrokerManager::GetSellerName(int32 cid) const {
std::shared_lock lock(mtx_);
auto it = players_.find(cid);
return (it != players_.end()) ? it->second.seller_name : std::string();
}
bool BrokerManager::IsSaleEnabled(int32 cid) const {
std::shared_lock lock(mtx_);
auto it = players_.find(cid);
return (it != players_.end()) ? it->second.sale_enabled : false;
}
bool BrokerManager::CanSellFromInventory(int32 cid) const {
std::shared_lock lock(mtx_);
auto it = players_.find(cid);
return (it != players_.end()) ? it->second.sell_from_inventory : false;
}
int32 BrokerManager::GetHouseID(int32 cid) const {
std::shared_lock lock(mtx_);
auto it = players_.find(cid);
return (it != players_.end()) ? it->second.house_id : -1;
}
std::optional<SellerInfo> BrokerManager::GetSellerInfo(int32 cid) const {
std::shared_lock lock(mtx_);
auto it = players_.find(cid);
if (it == players_.end())
return std::nullopt;
return it->second;
}
void BrokerManager::SavePlayerToDB(const SellerInfo& p) {
Query q;
std::ostringstream oss;
oss << "INSERT INTO broker_sellers "
"(character_id,seller_name,house_id,sale_enabled,sell_from_inventory,coin_session,coin_total) "
"VALUES("
<< p.character_id << ",'"
<< EscapeSQLString(p.seller_name) << "',"
<< p.house_id << ","
<< (p.sale_enabled ? 1 : 0) << ","
<< (p.sell_from_inventory ? 1 : 0) << ","
<< p.coin_session << ","
<< p.coin_total
<< ") ON DUPLICATE KEY UPDATE "
"seller_name=VALUES(seller_name),"
"house_id=VALUES(house_id),"
"sale_enabled=VALUES(sale_enabled),"
"sell_from_inventory=VALUES(sell_from_inventory),"
"coin_session=VALUES(coin_session), "
"coin_total=VALUES(coin_total)";
std::string sql = oss.str();
q.AddQueryAsync(p.character_id, &database, Q_INSERT,
sql.c_str()
);
}
#include <sstream>
void BrokerManager::SaveItemToDB(const SaleItem& i) {
// 2) Build full SQL
std::ostringstream oss;
oss
<< "INSERT INTO broker_items "
"(unique_id,character_id,house_id,item_id,cost_copper,for_sale,"
"inv_slot_id,slot_id,`count`,from_inventory,creator) "
"VALUES("
<< i.unique_id << ","
<< i.character_id << ","
<< i.house_id << ","
<< i.item_id << ","
<< i.cost_copper << ","
<< (i.for_sale ? 1 : 0) << ","
<< i.inv_slot_id << ","
<< i.slot_id << ","
<< i.count << ","
<< (i.from_inventory ? 1 : 0) << ",'"
<< EscapeSQLString(i.creator) << "') "
"ON DUPLICATE KEY UPDATE "
"house_id=VALUES(house_id),"
"item_id=VALUES(item_id),"
"cost_copper=VALUES(cost_copper),"
"for_sale=VALUES(for_sale),"
"inv_slot_id=VALUES(inv_slot_id),"
"slot_id=VALUES(slot_id),"
"`count`=VALUES(`count`),"
"from_inventory=VALUES(from_inventory),"
"creator=VALUES(creator)";
std::string sql = oss.str();
Query q;
q.AddQueryAsync(i.character_id, &database, Q_INSERT, sql.c_str());
}
void BrokerManager::UpdateItemInDB(const SaleItem& i) {
Query q;
std::ostringstream oss;
oss << "UPDATE broker_items SET "
<< "house_id=" << i.house_id
<< ",item_id=" << i.item_id
<< ",cost_copper=" << i.cost_copper
<< ",for_sale=" << (i.for_sale ? 1 : 0)
<< ",inv_slot_id=" << i.inv_slot_id
<< ",slot_id=" << i.slot_id
<< ",count=" << i.count
<< ",from_inventory=" << (i.from_inventory ? 1 : 0)
<< ",creator='" << EscapeSQLString(i.creator) << "' "
<< "WHERE unique_id=" << i.unique_id
<< " AND character_id=" << i.character_id;
std::string sql = oss.str();
q.AddQueryAsync(i.character_id, &database, Q_UPDATE,
sql.c_str()
);
}
void BrokerManager::DeleteItemFromDB(int32 cid, int64 uid) {
Query q;
q.AddQueryAsync(cid, &database, Q_DELETE,
"DELETE FROM broker_items WHERE unique_id=%llu AND character_id=%u",
uid, cid
);
}
void BrokerManager::DeleteCharacterItemFromDB(int32 cid, int64 uid) {
Query q;
q.AddQueryAsync(cid, &database, Q_DELETE,
"DELETE FROM character_items WHERE id=%llu AND char_id=%u",
uid, cid
);
}
void BrokerManager::UpdateCharacterItemDB(int32 cid, int64 uid, int16 count) {
Query q;
q.AddQueryAsync(cid, &database, Q_UPDATE,
"UPDATE character_items set count=%u WHERE id=%llu AND char_id=%u",
count, uid, cid
);
}
void BrokerManager::DeletePlayerFromDB(int32 cid) {
Query q;
// delete from broker_sellers
q.AddQueryAsync(cid, &database, Q_DELETE,
"DELETE FROM broker_sellers WHERE character_id=%u", cid);
// delete all their items
q.AddQueryAsync(cid, &database, Q_DELETE,
"DELETE FROM broker_items WHERE character_id=%u", cid);
}
bool BrokerManager::IsItemFromInventory(int32 cid, int64 uid) const {
std::shared_lock lock(mtx_);
// 1) Check active items for that character
auto pit = active_items_by_char_.find(cid);
if (pit != active_items_by_char_.end()) {
auto it = pit->second.find(uid);
if (it != pit->second.end())
return it->second.from_inventory;
}
// 2) Otherwise check inactive items
auto iit = inactive_items_by_char_.find(cid);
if (iit != inactive_items_by_char_.end()) {
auto it = iit->second.find(uid);
if (it != iit->second.end())
return it->second.from_inventory;
}
// 3) Not found → false
return false;
}
void BrokerManager::LockActiveItemsForClient(Client* client) const {
int32 cid = client->GetCharacterID();
auto infoOpt = GetSellerInfo(cid);
if (!infoOpt)
return;
const SellerInfo& info = *infoOpt;
{
auto items = GetActiveForSaleItems(cid);
for (auto const& itm : items) {
if (!itm.for_sale) {
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, false, true);
continue;
}
LogWrite(PLAYER__ERROR, 5, "Broker",
"--LockActiveItemsForClient: %u (selling: %u), allowinv: %u, sellfrominv: %u",
itm.unique_id, itm.for_sale, itm.from_inventory, info.sell_from_inventory
);
if (!info.sell_from_inventory && itm.from_inventory) {
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, false, true);
continue;
}
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, true, true);
}
}
}
void WorldDatabase::ClearSellerSession(int32 character_id) {
Query q;
std::ostringstream sql;
sql << "UPDATE broker_sellers SET coin_session = 0"
<< " WHERE character_id = " << character_id;
std::string full = sql.str();
q.AddQueryAsync(character_id, &database, Q_INSERT, full.c_str());
}
void WorldDatabase::AddToSellerSession(int32 character_id, int64 amount) {
Query q;
std::ostringstream sql;
sql << "UPDATE broker_sellers SET coin_session = coin_session + "
<< amount
<< " WHERE character_id = " << character_id;
std::string full = sql.str();
q.AddQueryAsync(character_id, &database, Q_INSERT, full.c_str());
}
int64 WorldDatabase::GetSellerSession(int32 character_id) {
Query query;
MYSQL_RES* result = query.RunQuery2(
Q_SELECT,
"SELECT "
"coin_session "
"FROM broker_sellers "
"WHERE character_id = %d "
"LIMIT 1",
character_id
);
if (result) {
MYSQL_ROW row;
if ((row = mysql_fetch_row(result))) {
return strtoull(row[0], NULL, 0);
}
}
return 0;
}
std::string BrokerManager::GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin) {
int64 platinum = static_cast<int64>(coin / 1000000);
// 1) Break totalCopper into denominations
int64 rem = coin % 1000000;
int64 gold = static_cast<int64>(rem / 10000);
rem %= 10000;
int64 silver = static_cast<int64>(rem / 100);
int64 copper = static_cast<int64>(rem % 100);
// 1) Timestamp
auto now = std::time(nullptr);
std::tm tm;
localtime_r(&now, &tm);
char timebuf[64];
std::strftime(timebuf, sizeof(timebuf),
"%B %d, %Y, %I:%M:%S %p", &tm);
// 2) Build the sale line
std::ostringstream msg;
msg << timebuf
<< " " << buyer_name << " buys ";
if (quantity > 1)
msg << quantity << " ";
msg << item_desc
<< " for ";
// 3) Denominations
std::vector<std::string> parts;
if (platinum > 0) parts.push_back(std::to_string(platinum) + " Platinum");
if (gold > 0) parts.push_back(std::to_string(gold) + " Gold");
if (silver > 0) parts.push_back(std::to_string(silver) + " Silver");
if (copper > 0) parts.push_back(std::to_string(copper) + " Copper");
// If all are zero (unlikely), still print "0 Copper"
if (parts.empty())
parts.push_back("0 Copper");
// Join with ", "
for (size_t i = 0; i < parts.size(); ++i) {
if (i) msg << ", ";
msg << parts[i];
}
return msg.str();
}
void BrokerManager::LogSale(int32 cid,
const std::string& buyer_name,
const std::string& item_desc,
int16 quantity,
int64 coin)
{
std::string msg = GetShopPurchaseMessage(buyer_name, item_desc, quantity, coin);
std::string esc = EscapeSQLString(msg.c_str());
std::ostringstream sql;
sql << "INSERT INTO broker_seller_log "
"(character_id,log_time,message) VALUES ("
<< cid << ",NOW(),'"
<< esc << "')";
std::string full = sql.str();
Query q;
q.AddQueryAsync(cid, &database, Q_INSERT, full.c_str());
}
void BrokerManager::LogSaleMessage(int32 cid,
const std::string& log_message)
{
auto now = std::time(nullptr);
std::tm tm;
localtime_r(&now, &tm);
char timebuf[64];
std::strftime(timebuf, sizeof(timebuf),
"%B %d, %Y, %I:%M:%S %p", &tm);
std::ostringstream msg;
msg << timebuf
<< " " << log_message;
std::string esc = EscapeSQLString(msg.str());
std::ostringstream sql;
sql << "INSERT INTO broker_seller_log "
"(character_id,log_time,message) VALUES ("
<< cid << ",NOW(),'"
<< esc << "')";
std::string full = sql.str();
Query q;
q.AddQueryAsync(cid, &database, Q_INSERT, full.c_str());
}
std::vector<SellerLog> BrokerManager::GetSellerLog(int32 cid) const {
std::vector<SellerLog> out;
Query query;
MYSQL_RES* result = query.RunQuery2(
Q_SELECT,
"SELECT "
"log_id, "
"DATE_FORMAT(log_time, '%%M %%d, %%Y, %%r') as ts, "
"message "
"FROM broker_seller_log "
"WHERE character_id=%u "
"ORDER BY log_time ASC",
cid
);
if (result) {
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
SellerLog entry;
entry.log_id = row[0] ? atoll(row[0]) : 0;
entry.timestamp = row[1] ? row[1] : "";
entry.message = row[2] ? row[2] : "";
out.push_back(std::move(entry));
}
}
return out;
}

View File

@ -0,0 +1,182 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <functional>
#include <unordered_map>
#include <vector>
#include <mutex>
#include <string>
#include <optional>
#include <shared_mutex>
#include "../../common/types.h"
// Key = (character_id, unique_id)
using Key = std::pair<int32, int64>;
class Item;
class Client;
struct SaleItem {
int64 unique_id;
int32 character_id;
int32 house_id;
int64 item_id;
int64 cost_copper;
bool for_sale;
sint32 inv_slot_id;
int16 slot_id;
int16 count;
bool from_inventory;
string creator;
};
struct SellerInfo {
int32 character_id;
std::string seller_name;
int64 house_id;
bool sale_enabled;
bool sell_from_inventory;
int64 coin_session;
int64 coin_total;
};
struct SellerLog {
int64 log_id; // auto_increment PK
std::string timestamp; // e.g. "October 20, 2005, 05:29:33 AM"
std::string message; // the human-readable text
};
class BrokerManager {
public:
BrokerManager();
// Player management
void AddSeller(int32 cid,
const std::string& name,
int32 house_id,
bool sale_enabled,
bool sell_from_inventory);
void LoadSeller(int32 cid,
const std::string& name,
int32 house_id,
bool sale_enabled,
bool sell_from_inventory,
int64 coin_session,
int64 coin_total);
int64 ResetSellerSessionCoins(int32 cid);
void AddSellerSessionCoins(int32 cid, uint64 session);
void RemoveSeller(int32 character_id, bool peerCacheOnly = false);
// Item management
void AddItem(const SaleItem& item, bool peerCacheOnly = false);
void LoadItem(const SaleItem& item);
// Activate / deactivate sale flag
void SetSaleStatus(int32 cid, int64 uid, bool for_sale);
bool IsItemListed(int32 cid, int64 uid);
void SetSalePrice(int32 cid, int64 uid, int64 price);
int64 GetSalePrice(int32 cid, int64 uid);
// Remove quantity
void RemoveItem(int32 cid, int64 uid, int16 quantity = 1, bool shouldDelete = false);
// Attempt to buy (atomic DB + in-memory + broadcast)
bool BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity);
bool IsItemForSale(int32 seller_cid, int64 uid) const;
// Called when a peer notifies that an item was sold/removed (in-memory only)
void OnPeerRemoveItem(int32 character_id, int64 unique_id);
// Queries
std::vector<SaleItem> GetActiveForSaleItems(int32 cid) const;
std::optional<SaleItem> GetActiveItem(int32 cid, int64 uid) const;
bool IsSellingItems(int32 cid, bool vaultOnly = false) const;
std::vector<SaleItem> GetInactiveItems(int32 cid) const;
// Global search API (only active_items_)
vector<Item*>* GetItems(
const std::string& name,
int64 itype,
int64 ltype,
int64 btype,
int64 minprice,
int64 maxprice,
int8 minskill,
int8 maxskill,
const std::string& seller,
const std::string& adornment,
int8 mintier,
int8 maxtier,
int16 minlevel,
int16 maxlevel,
int8 itemclass
) const;
// UI helper: get (unique_id, cost) for active items
std::vector<std::pair<int64,int32>> GetUniqueIDsAndCost(int32 cid) const;
std::optional<SellerInfo> GetSellerInfo(int32 character_id) const;
// Lookup seller name
std::string GetSellerName(int32 cid) const;
bool IsSaleEnabled(int32 cid) const;
bool CanSellFromInventory(int32 cid) const;
int32 GetHouseID(int32 cid) const;
bool IsItemFromInventory(int32 cid, int64 uid) const;
void LockActiveItemsForClient(Client* client) const;
std::string GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin);
void LogSale(int32 character_id, const std::string& buyer_name,const std::string& item_desc, int16 quantity, int64 coin);
void LogSaleMessage(int32 character_id,const std::string& log_message);
std::vector<SellerLog> GetSellerLog(int32 character_id) const;
static std::string EscapeSQLString(const std::string& s) {
std::string out;
out.reserve(s.size() * 2);
for (char c : s) {
if (c == '\'') out += "''";
else out += c;
}
return out;
}
private:
mutable std::shared_mutex mtx_;
std::unordered_map<int32,SellerInfo> players_;
std::unordered_map<
int32,
std::unordered_map<int64_t, SaleItem>
> active_items_by_char_;
std::unordered_map<
int32,
std::unordered_map<int64_t, SaleItem>
> inactive_items_by_char_;
// DB sync (async writes)
void SavePlayerToDB(const SellerInfo& p);
void SaveItemToDB(const SaleItem& i);
void UpdateItemInDB(const SaleItem& i);
void DeleteItemFromDB(int32 character_id, int64 unique_id);
void DeleteCharacterItemFromDB(int32 cid, int64 uid);
void UpdateCharacterItemDB(int32 cid, int64 uid, int16 count);
void DeletePlayerFromDB(int32 cid);
};

378
old/world/Chat/Chat.cpp Normal file
View File

@ -0,0 +1,378 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Chat.h"
#include "../../common/Log.h"
#include "../../common/ConfigReader.h"
#include "../../common/PacketStruct.h"
#include "../Rules/Rules.h"
extern RuleManager rule_manager;
//devn00b
#ifdef DISCORD
#ifndef WIN32
#include <dpp/dpp.h>
#include "ChatChannel.h"
extern ChatChannel channel;
#endif
#endif
#include "../Web/PeerManager.h"
extern ConfigReader configReader;
extern PeerManager peer_manager;
Chat::Chat() {
m_channels.SetName("Chat::Channels");
}
Chat::~Chat() {
vector<ChatChannel *>::iterator itr;
m_channels.writelock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++)
safe_delete(*itr);
m_channels.releasewritelock(__FUNCTION__, __LINE__);
}
void Chat::AddChannel(ChatChannel *channel) {
m_channels.writelock(__FUNCTION__, __LINE__);
channels.push_back(channel);
m_channels.releasewritelock(__FUNCTION__, __LINE__);
}
unsigned int Chat::GetNumChannels() {
unsigned int ret;
m_channels.readlock(__FUNCTION__, __LINE__);
ret = (unsigned int)channels.size();
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
EQ2Packet * Chat::GetWorldChannelList(Client *client) {
PacketStruct *packet_struct = configReader.getStruct("WS_AvailWorldChannels", client->GetVersion());
Player *player = client->GetPlayer();
vector<ChatChannel *> channels_to_send;
vector<ChatChannel *>::iterator itr;
ChatChannel *channel;
EQ2Packet *packet;
int32 i = 0;
bool add;
if (packet_struct == NULL) {
LogWrite(CHAT__ERROR, 0, "Chat", "Could not find packet 'WS_AvailWorldChannels' for client %s on version %i\n", player->GetName(), client->GetVersion());
return NULL;
}
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
channel = *itr;
if (channel->GetType() == CHAT_CHANNEL_TYPE_WORLD) {
add = true;
if (add && !channel->CanJoinChannelByLevel(player->GetLevel()))
add = false;
if (add && !channel->CanJoinChannelByRace(player->GetRace()))
add = false;
if (add && !channel->CanJoinChannelByClass(player->GetAdventureClass()))
add = false;
if (add)
channels_to_send.push_back(channel);
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
packet_struct->setArrayLengthByName("num_channels", channels_to_send.size());
for (itr = channels_to_send.begin(); itr != channels_to_send.end(); itr++, i++) {
packet_struct->setArrayDataByName("channel_name", (*itr)->GetName(), i);
packet_struct->setArrayDataByName("unknown", 0, i);
}
packet = packet_struct->serialize();
safe_delete(packet_struct);
return packet;
}
bool Chat::ChannelExists(const char *channel_name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = true;
break;
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
bool Chat::HasPassword(const char *channel_name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = (*itr)->HasPassword();
break;
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
bool Chat::PasswordMatches(const char *channel_name, const char *password) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = (*itr)->PasswordMatches(password);
break;
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
bool Chat::CreateChannel(const char *channel_name, bool fromPeer) {
return CreateChannel(channel_name, NULL, fromPeer);
}
bool Chat::CreateChannel(const char *channel_name, const char *password, bool fromPeer) {
LogWrite(CHAT__DEBUG, 0, "Chat", "Channel %s being created", channel_name);
ChatChannel *channel = new ChatChannel();
channel->SetName(channel_name);
channel->SetType(CHAT_CHANNEL_TYPE_CUSTOM);
if (password != NULL && strlen(password) > 0)
channel->SetPassword(password);
if(!fromPeer) {
peer_manager.sendPeersAddChatChannel(std::string(channel_name), password != nullptr ? std::string(password) : std::string(""));
}
m_channels.writelock(__FUNCTION__, __LINE__);
channels.push_back(channel);
m_channels.releasewritelock(__FUNCTION__, __LINE__);
return true;
}
bool Chat::IsInChannel(Client *client, const char *channel_name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = (*itr)->IsInChannel(client->GetCharacterID());
break;
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
bool Chat::JoinChannel(Client *client, const char *channel_name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
LogWrite(CHAT__DEBUG, 1, "Chat", "Client %s is joining channel %s", client->GetPlayer()->GetName(), channel_name);
m_channels.writelock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = (*itr)->JoinChannel(client);
break;
}
}
m_channels.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
bool Chat::LeaveChannel(Client *client, const char *channel_name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
LogWrite(CHAT__DEBUG, 1, "Chat", "Client %s is leaving channel %s", client->GetPlayer()->GetName(), channel_name);
m_channels.writelock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = (*itr)->LeaveChannel(client);
if ((*itr)->GetType() == CHAT_CHANNEL_TYPE_CUSTOM && (*itr)->GetNumClients() == 0) {
LogWrite(CHAT__DEBUG, 0, "Chat", "Custom channel %s has 0 clients left, deleting channel", channel_name);
safe_delete(*itr);
channels.erase(itr);
}
break;
}
}
m_channels.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
bool Chat::LeaveAllChannels(Client *client) {
vector<ChatChannel *>::iterator itr;
ChatChannel *channel;
bool erased;
m_channels.writelock(__FUNCTION__, __LINE__);
itr = channels.begin();
while (itr != channels.end()) {
channel = *itr;
erased = false;
if (channel->IsInChannel(client->GetCharacterID())) {
LogWrite(CHAT__DEBUG, 1, "Chat", "Client %s is leaving channel %s", client->GetPlayer()->GetName(), channel->GetName());
channel->LeaveChannel(client);
if (channel->GetType() == CHAT_CHANNEL_TYPE_CUSTOM && channel->GetNumClients() == 0) {
LogWrite(CHAT__DEBUG, 0, "Chat", "Custom channel %s has 0 clients left, deleting channel", channel->GetName());
safe_delete(*itr);
itr = channels.erase(itr);
erased = true;
}
}
if (!erased)
itr++;
}
m_channels.releasewritelock(__FUNCTION__, __LINE__);
return true;
}
bool Chat::TellChannel(Client *client, std::string charName, int8 charLanguage, const char *channel_name, const char *message, const char* name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
bool enablediscord = rule_manager.GetGlobalRule(R_Discord, DiscordEnabled)->GetBool();
const char* discordchan = rule_manager.GetGlobalRule(R_Discord, DiscordChannel)->GetString();
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
if (client && name)
ret = (*itr)->TellChannelClient(client, message, name);
else
ret = (*itr)->TellChannel(charName, charLanguage, message, name);
if(enablediscord == true && client){
if (strcmp(channel_name, discordchan) != 0){
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
#ifdef DISCORD
if (client) {
std::string whofrom = client->GetPlayer()->GetName();
std::string msg = string(message);
ret = PushDiscordMsg(msg.c_str(), whofrom.c_str());
}
#endif
}
break;
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
bool Chat::SendChannelUserList(Client *client, const char *channel_name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = (*itr)->SendChannelUserList(client);
break;
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
ChatChannel* Chat::GetChannel(const char *channel_name) {
vector<ChatChannel *>::iterator itr;
ChatChannel* ret = 0;
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
if (strncasecmp(channel_name, (*itr)->GetName(), CHAT_CHANNEL_MAX_NAME) == 0) {
ret = (*itr);
break;
}
}
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
#ifdef DISCORD
//this sends chat from EQ2EMu to Discord. Currently using webhooks. Makes things simpler code wise.
int Chat::PushDiscordMsg(const char* msg, const char* from) {
bool enablediscord = rule_manager.GetGlobalRule(R_Discord, DiscordEnabled)->GetBool();
if(enablediscord == false) {
LogWrite(INIT__INFO, 0,"Discord","Bot Disabled By Rule...");
return 0;
}
m_channels.readlock(__FUNCTION__, __LINE__);
const char* hook = rule_manager.GetGlobalRule(R_Discord, DiscordWebhookURL)->GetString();
std::string servername = net.GetWorldName();
char ourmsg[4096];
//form our message
sprintf(ourmsg,"[%s] [%s] Says: %s",from, servername.c_str(), msg);
/* send a message with this webhook */
dpp::cluster bot("");
dpp::webhook wh(hook);
bot.execute_webhook(wh, dpp::message(ourmsg));
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return 1;
}
#endif

119
old/world/Chat/Chat.h Normal file
View File

@ -0,0 +1,119 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_CHAT_H_
#define CHAT_CHAT_H_
#include <vector>
#include "../../common/types.h"
#include "../../common/EQPacket.h"
#include "../../common/Mutex.h"
#include "../client.h"
#include "ChatChannel.h"
#ifdef DISCORD
#ifndef WIN32
#pragma once
#include <dpp/dpp.h>
#endif
#endif
using namespace std;
/*
CREATING A CHANNEL
-- OP_RemoteCmdMsg --
3/14/2012 20:17:06
192.168.1.198 -> 69.174.200.73
0000: 00 09 05 9A 2A 0E 00 0F 00 63 75 73 74 6F 6D 20 ....*....custom
0010 70 61 73 73 77 6F 72 64 password
TALKING IN A CHANNEL
[11:52.23] <@Xinux> -- OP_RemoteCmdMsg --
[11:52.23] <@Xinux> 3/14/2012 20:17:25
[11:52.23] <@Xinux> 192.168.1.198 -> 69.174.200.73
[11:52.23] <@Xinux> 0000: 00 09 06 2D 2A 11 00 21 00 63 75 73 74 6F 6D 20 ...-*..!.custom
[11:52.23] <@Xinux> 0010: 20 74 68 69 73 20 69 73 20 6D 79 20 63 75 73 74 this is my cust
[11:52.23] <@Xinux> 0020 6F 6D 20 63 68 61 6E 6E 65 6C om channel
[08:37.46] <@Xinux_Work> 00 09 05 8B 00 3A 53 00 00 00 FF 3C 02 00 00 FF .....:S....<....
[08:37.46] <@Xinux_Work> FF FF FF FF FF FF FF 06 00 4C 65 69 68 69 61 07 .........Leihia.
[08:37.46] <@Xinux_Work> 00 4B 6F 65 63 68 6F 68 00 02 00 00 00 00 01 00 .Koechoh........
[08:37.46] <@Xinux_Work> 00 00 22 00 18 00 62 65 74 74 65 72 20 74 68 61 .."...better tha
[08:37.46] <@Xinux_Work> 6E 20 61 20 72 65 64 20 6F 6E 65 20 3A 50 09 00 n a red one :P..
[08:37.46] <@Xinux_Work> 4C 65 76 65 6C 5F 31 2D 39 01 01 00 00 Level_1-9....
OTHERS LEAVING AND JOINING A CHANNEL
-- OP_ClientCmdMsg::OP_EqChatChannelUpdateCmd --
3/14/2012 20:17:06
69.174.200.73 -> 192.168.1.198
0000: 00 3A 18 00 00 00 FF 88 02 03 09 00 4C 65 76 65 .:..........Leve
0010 6C 5F 31 2D 39 07 00 53 68 61 77 6E 61 68 l_1-9..Shawnah
-- OP_ClientCmdMsg::OP_EqChatChannelUpdateCmd --
3/14/2012 20:17:06
69.174.200.73 -> 192.168.1.198
0000: 00 3A 16 00 00 00 FF 88 02 03 07 00 41 75 63 74 .:..........Auct
0010 69 6F 6E 07 00 53 68 61 77 6E 61 68 ion..Shawnah
OP_EqChatChannelUpdateCmd
unknown=0 unknown1=blank join
unknown=1 unknown1=blank leave
unknown=2 unknown2=player join/leave?
unknown=3 unknown2=player join/leave?
*/
class Chat{
public:
Chat();
virtual ~Chat();
void AddChannel(ChatChannel *channel);
unsigned int GetNumChannels();
EQ2Packet * GetWorldChannelList(Client *client);
bool ChannelExists(const char *channel_name);
bool HasPassword(const char *channel_name);
bool PasswordMatches(const char *channel_name, const char *password);
bool CreateChannel(const char *channel_name, bool fromPeer = false);
bool CreateChannel(const char *channel_name, const char *password, bool fromPeer = false);
bool IsInChannel(Client *client, const char *channel_name);
bool JoinChannel(Client *client, const char *channel_name);
bool LeaveChannel(Client *client, const char *channel_name);
bool LeaveAllChannels(Client *client);
bool TellChannel(Client *client, std::string charName, int8 charLanguage, const char *channel_name, const char *message, const char* name = 0);
bool SendChannelUserList(Client *client, const char *channel_name);
//devn00b
int PushDiscordMsg(const char*, const char*);
ChatChannel* GetChannel(const char* channel_name);
private:
Mutex m_channels;
vector<ChatChannel *> channels;
};
#endif

View File

@ -0,0 +1,227 @@
#include <string.h>
#include "../../common/Log.h"
#include "../../common/ConfigReader.h"
#include "../../common/PacketStruct.h"
#include "../World.h"
#include "ChatChannel.h"
extern ConfigReader configReader;
extern ZoneList zone_list;
#define CHAT_CHANNEL_JOIN 0
#define CHAT_CHANNEL_LEAVE 1
#define CHAT_CHANNEL_OTHER_JOIN 2
#define CHAT_CHANNEL_OTHER_LEAVE 3
ChatChannel::ChatChannel() {
memset(name, 0, sizeof(name));
memset(password, 0, sizeof(password));
type = CHAT_CHANNEL_TYPE_NONE;
level_restriction = 0;
races = 0;
classes = 0;
}
ChatChannel::~ChatChannel() {
}
bool ChatChannel::IsInChannel(int32 character_id) {
vector<int32>::iterator itr;
for (itr = clients.begin(); itr != clients.end(); itr++) {
if (character_id == *itr)
return true;
}
return false;
}
bool ChatChannel::JoinChannel(Client *client) {
PacketStruct *packet_struct;
vector<int32>::iterator itr;
Client *to_client;
//send the player join packet to the joining client
if ((packet_struct = configReader.getStruct("WS_ChatChannelUpdate", client->GetVersion())) == NULL) {
LogWrite(CHAT__ERROR, 0, "Chat", "Could not find packet 'WS_ChatChannelUpdate' when client %s was trying to join channel %s", client->GetPlayer()->GetName(), name);
return false;
}
packet_struct->setDataByName("action", CHAT_CHANNEL_JOIN);
packet_struct->setDataByName("channel_name", name);
client->QueuePacket(packet_struct->serialize());
safe_delete(packet_struct);
clients.push_back(client->GetCharacterID());
//loop through everyone else in the channel and send the "other" player join packet
for (itr = clients.begin(); itr != clients.end(); itr++) {
if (client->GetCharacterID() == *itr)
continue;
if ((to_client = zone_list.GetClientByCharID(*itr)) == NULL)
continue;
if ((packet_struct = configReader.getStruct("WS_ChatChannelUpdate", to_client->GetVersion())) == NULL)
continue;
packet_struct->setDataByName("action", CHAT_CHANNEL_OTHER_JOIN);
packet_struct->setDataByName("channel_name", name);
packet_struct->setDataByName("player_name", client->GetPlayer()->GetName());
to_client->QueuePacket(packet_struct->serialize());
safe_delete(packet_struct);
}
return true;
}
bool ChatChannel::LeaveChannel(Client *client) {
vector<int32>::iterator itr;
PacketStruct *packet_struct;
Client *to_client;
bool ret = false;
for (itr = clients.begin(); itr != clients.end(); itr++) {
if (client->GetCharacterID() == *itr) {
clients.erase(itr);
ret = true;
break;
}
}
if (ret) {
//send the packet to the leaving client
if ((packet_struct = configReader.getStruct("WS_ChatChannelUpdate", client->GetVersion())) == NULL)
return false;
packet_struct->setDataByName("action", CHAT_CHANNEL_LEAVE);
packet_struct->setDataByName("channel_name", name);
client->QueuePacket(packet_struct->serialize());
safe_delete(packet_struct);
//send the leave packet to all other clients in the channel
for (itr = clients.begin(); itr != clients.end(); itr++) {
if ((to_client = zone_list.GetClientByCharID(*itr)) == NULL)
continue;
if (to_client == client) // don't need to send to self.
continue;
if ((packet_struct = configReader.getStruct("WS_ChatChannelUpdate", to_client->GetVersion())) == NULL)
continue;
packet_struct->setDataByName("action", CHAT_CHANNEL_OTHER_LEAVE);
packet_struct->setDataByName("channel_name", name);
packet_struct->setDataByName("player_name", client->GetPlayer()->GetName());
to_client->QueuePacket(packet_struct->serialize());
safe_delete(packet_struct);
}
}
return ret;
}
bool ChatChannel::TellChannel(std::string charName, int8 charLanguage, const char *message, const char* name2) {
vector<int32>::iterator itr;
PacketStruct *packet_struct;
Client *to_client;
for (itr = clients.begin(); itr != clients.end(); itr++) {
if ((to_client = zone_list.GetClientByCharID(*itr)) == NULL)
continue;
if ((packet_struct = configReader.getStruct("WS_HearChat", to_client->GetVersion())) == NULL)
continue;
packet_struct->setDataByName("unknown", 0);
packet_struct->setDataByName("from_spawn_id", 0xFFFFFFFF);
packet_struct->setDataByName("to_spawn_id", 0xFFFFFFFF);
if (charName.length() > 0){
packet_struct->setDataByName("from", charName.c_str());
} else {
char name3[128];
sprintf(name3,"[%s] from discord",name2);
packet_struct->setDataByName("from", name3);
}
packet_struct->setDataByName("to", to_client->GetPlayer()->GetName());
packet_struct->setDataByName("channel", to_client->GetMessageChannelColor(CHANNEL_CUSTOM_CHANNEL));
if(charName.length() > 0){
packet_struct->setDataByName("language", charLanguage);
}else{
packet_struct->setDataByName("language", 0);
}
packet_struct->setDataByName("message", message);
packet_struct->setDataByName("channel_name", name);
packet_struct->setDataByName("show_bubble", 1);
if(charName.length() > 0){
if (charLanguage == 0 || to_client->GetPlayer()->HasLanguage(charLanguage)) {
packet_struct->setDataByName("understood", 1);
}
} else {
packet_struct->setDataByName("understood", 1);
}
packet_struct->setDataByName("unknown4", 0);
to_client->QueuePacket(packet_struct->serialize());
safe_delete(packet_struct);
}
return true;
}
bool ChatChannel::TellChannelClient(Client* to_client, const char* message, const char* name2) {
PacketStruct *packet_struct;
if (string(name2).find('[') != string::npos)
return true;
packet_struct = configReader.getStruct("WS_HearChat", to_client->GetVersion());
if (packet_struct) {
packet_struct->setDataByName("unknown", 0);
packet_struct->setDataByName("from_spawn_id", 0xFFFFFFFF);
packet_struct->setDataByName("to_spawn_id", 0xFFFFFFFF);
packet_struct->setDataByName("from", name2);
packet_struct->setDataByName("to", to_client->GetPlayer()->GetName());
packet_struct->setDataByName("channel", to_client->GetMessageChannelColor(CHANNEL_CUSTOM_CHANNEL));
packet_struct->setDataByName("language", 0);
packet_struct->setDataByName("message", message);
packet_struct->setDataByName("channel_name", name);
packet_struct->setDataByName("show_bubble", 1);
packet_struct->setDataByName("understood", 1);
packet_struct->setDataByName("unknown4", 0);
to_client->QueuePacket(packet_struct->serialize());
}
safe_delete(packet_struct);
return true;
}
bool ChatChannel::SendChannelUserList(Client *client) {
vector<int32>::iterator itr;
PacketStruct *packet_struct;
Client *to_client;
int8 i = 0;
if ((packet_struct = configReader.getStruct("WS_WhoChannelQueryReply", client->GetVersion())) == NULL)
return false;
packet_struct->setDataByName("channel_name", name);
packet_struct->setDataByName("unknown", 0);
packet_struct->setArrayLengthByName("num_players", clients.size());
for (itr = clients.begin(); itr != clients.end(); itr++) {
if ((to_client = zone_list.GetClientByCharID(*itr)) != NULL)
packet_struct->setArrayDataByName("player_name", client->GetPlayer()->GetName(), i++);
else
packet_struct->setArrayDataByName("player_name", "<Unknown>", i++);
}
client->QueuePacket(packet_struct->serialize());
safe_delete(packet_struct);
return true;
}

View File

@ -0,0 +1,80 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_CHATCHANNEL_H_
#define CHAT_CHATCHANNEL_H_
#include "../../common/types.h"
#include "../client.h"
#include <vector>
using namespace std;
#define CHAT_CHANNEL_MAX_NAME 100
#define CHAT_CHANNEL_MAX_PASSWORD 100
enum ChatChannelType {
CHAT_CHANNEL_TYPE_NONE = 0,
CHAT_CHANNEL_TYPE_WORLD,
CHAT_CHANNEL_TYPE_CUSTOM
};
class ChatChannel {
public:
ChatChannel();
virtual ~ChatChannel();
void SetName(const char *name) {strncpy(this->name, name, CHAT_CHANNEL_MAX_NAME);}
void SetPassword(const char *password) {strncpy(this->password, password, CHAT_CHANNEL_MAX_PASSWORD);}
void SetType(ChatChannelType type) {this->type = type;}
void SetLevelRestriction(int16 level_restriction) {this->level_restriction = level_restriction;}
void SetRacesAllowed(int64 races) {this->races = races;}
void SetClassesAllowed(int64 classes) {this->classes = classes;}
const char * GetName() {return name;}
ChatChannelType GetType() {return type;}
unsigned int GetNumClients() {return clients.size();}
bool HasPassword() {return password[0] != '\0';}
bool PasswordMatches(const char *password) {return strncmp(this->password, password, CHAT_CHANNEL_MAX_PASSWORD) == 0;}
bool CanJoinChannelByLevel(int16 level) {return level >= level_restriction;}
bool CanJoinChannelByRace(int8 race_id) {return races == 0 || (1 << race_id) & races;}
bool CanJoinChannelByClass(int8 class_id) {return classes == 0 || (1 << class_id) & classes;}
bool IsInChannel(int32 character_id);
bool JoinChannel(Client *client);
bool LeaveChannel(Client *client);
bool TellChannel(std::string charName, int8 charLanguage, const char *message, const char* name2 = 0);
bool TellChannelClient(Client* to_client, const char* message, const char* name2 = 0);
bool SendChannelUserList(Client *client);
private:
char name[CHAT_CHANNEL_MAX_NAME + 1];
char password[CHAT_CHANNEL_MAX_PASSWORD + 1];
ChatChannelType type;
vector<int32> clients;
int16 level_restriction;
int64 races;
int64 classes;
};
#endif

46
old/world/Chat/ChatDB.cpp Normal file
View File

@ -0,0 +1,46 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../../common/Log.h"
#include "Chat.h"
#include "../WorldDatabase.h"
extern Chat chat;
void WorldDatabase::LoadChannels() {
DatabaseResult result;
ChatChannel *channel;
if (database_new.Select(&result, "SELECT `name`,`password`,`level_restriction`,`classes`,`races` FROM `channels`")) {
while (result.Next()) {
channel = new ChatChannel();
channel->SetName(result.GetString(0));
if (!result.IsNull(1))
channel->SetPassword(result.GetString(1));
channel->SetLevelRestriction(result.GetInt16(2));
channel->SetClassesAllowed(result.GetInt64(3));
channel->SetRacesAllowed(result.GetInt64(4));
channel->SetType(CHAT_CHANNEL_TYPE_WORLD);
chat.AddChannel(channel);
}
}
}

View File

@ -0,0 +1,475 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ClientPacketFunctions.h"
#include "WorldDatabase.h"
#include "../common/ConfigReader.h"
#include "Variables.h"
#include "World.h"
#include "classes.h"
#include "../common/Log.h"
#include "Traits/Traits.h"
extern Classes classes;
extern Commands commands;
extern WorldDatabase database;
extern ConfigReader configReader;
extern MasterSpellList master_spell_list;
extern MasterTraitList master_trait_list;
extern Variables variables;
extern World world;
void ClientPacketFunctions::SendFinishedEntitiesList ( Client* client ){
EQ2Packet* finishedEntitiesApp = new EQ2Packet(OP_DoneSendingInitialEntitiesMsg, 0, 0);
client->QueuePacket(finishedEntitiesApp);
}
void ClientPacketFunctions::SendSkillSlotMappings(Client* client){
EQ2Packet* app = client->GetPlayer()->GetSpellSlotMappingPacket(client->GetVersion());
if(app)
client->QueuePacket(app);
}
void ClientPacketFunctions::SendLoginDenied ( Client* client ){
PacketStruct* packet = configReader.getStruct("LS_LoginResponse", 1);
if(packet){
packet->setDataByName("reply_code", 1);
packet->setDataByName("unknown03", 0xFFFFFFFF);
packet->setDataByName("unknown04", 0xFFFFFFFF);
EQ2Packet* app = packet->serialize();
client->QueuePacket(app);
safe_delete(packet);
}
}
void ClientPacketFunctions::SendLoginAccepted ( Client* client ){
LogWrite(PACKET__DEBUG, 0, "Packet", "Sending Login Accepted packet (LS_LoginResponse, %i)", client->GetVersion());
PacketStruct* response_packet = configReader.getStruct("LS_LoginResponse", client->GetVersion());
if(response_packet){
response_packet->setDataByName("unknown02", 1);
response_packet->setDataByName("unknown05", -959971393);
response_packet->setDataByName("unknown08", 2);
response_packet->setDataByName("unknown09", 585);
response_packet->setDataByName("unknown10", 1597830);
response_packet->setDataByName("accountid", 3); //client->GetAccountID());
EQ2Packet* outapp = response_packet->serialize();
client->QueuePacket(outapp);
safe_delete(response_packet);
}
}
void ClientPacketFunctions::SendCommandList ( Client* client ){
EQ2Packet* app = commands.GetRemoteCommands()->serialize(client->GetVersion());
client->QueuePacket(app);
}
void ClientPacketFunctions::SendGameWorldTime ( Client* client ){
PacketStruct* packet = world.GetWorldTime(client->GetVersion());
if(packet){
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
//opcode 501 was the selection display opcode
}
void ClientPacketFunctions::SendCharacterData ( Client* client ){
client->GetPlayer()->SetCharacterID(client->GetCharacterID());
if(!client->IsReloadingZone()) {
EQ2Packet* outapp = client->GetPlayer()->serialize(client->GetPlayer(), client->GetVersion());
//DumpPacket(outapp);
client->QueuePacket(outapp);
}
}
void ClientPacketFunctions::SendCharacterSheet ( Client* client ){
EQ2Packet* app = client->GetPlayer()->GetPlayerInfo()->serialize(client->GetVersion());
client->QueuePacket(app);
if (client->GetVersion() >= 1188) {
EQ2Packet* app2 = client->GetPlayer()->GetPlayerInfo()->serializePet(client->GetVersion());
if (app2)
client->QueuePacket(app2);
}
}
void ClientPacketFunctions::SendSkillBook ( Client* client ){
EQ2Packet* app = client->GetPlayer()->skill_list.GetSkillPacket(client->GetVersion());
if(app)
client->QueuePacket(app);
}
// Jabantiz: Attempt to get the char trait list working
void ClientPacketFunctions::SendTraitList(Client* client) {
if (client->GetVersion() >= 562) {
EQ2Packet* traitApp = master_trait_list.GetTraitListPacket(client);
//DumpPacket(traitApp);
if (traitApp) {
client->QueuePacket(traitApp);
}
}
}
void ClientPacketFunctions::SendAbilities ( Client* client ){
LogWrite(MISC__TODO, 1, "TODO", " Add SendAbilities functionality\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
// this is the featherfall ability data
// later this would loop through and send all abilities
/*uchar abilityData[] ={0x11,0x00,0x00,0x00,0xff,0x15,0x02,0x00,0x0b,0x00,0x46,0x65,0x61,0x74
,0x68,0x65,0x72,0x66,0x61,0x6c,0x6c};
EQ2Packet* abilityApp = new EQ2Packet(OP_ClientCmdMsg, abilityData, sizeof(abilityData));
client->QueuePacket(abilityApp);*/
}
void ClientPacketFunctions::SendCommandNamePacket ( Client* client ){
LogWrite(MISC__TODO, 1, "TODO", " fix, this is actually quest/collection information\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
/*
PacketStruct* command_packet = configReader.getStruct("WS_CommandName", client->GetVersion());
if(command_packet){
command_packet->setDataByName("unknown03", 0x221bfb47);
char* charName = { "BogusName" };
command_packet->setMediumStringByName("character_name",charName);
EQ2Packet* outapp = command_packet->serialize();
client->QueuePacket(outapp);
safe_delete(command_packet);
}
*/
}
void ClientPacketFunctions::SendQuickBarInit ( Client* client ){
int32 count = database.LoadPlayerSkillbar(client);
if(count == 0) {
LogWrite(PACKET__DEBUG, 0, "Packet", "No character quickbar found!");
database.UpdateStartingSkillbar(client->GetCharacterID(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetRace());
database.LoadPlayerSkillbar(client);
}
EQ2Packet* quickbarApp = client->GetPlayer()->GetQuickbarPacket(client->GetVersion());
if(quickbarApp)
client->QueuePacket(quickbarApp);
}
void ClientPacketFunctions::SendCharacterMacros(Client* client) {
LogWrite(PACKET__DEBUG, 0, "Packet", "Sending Character Macro packet (WS_MacroInit, %i)", client->GetVersion());
map<int8, vector<MacroData*> >* macros = database.LoadCharacterMacros(client->GetCharacterID());
if (macros) {
PacketStruct* macro_packet = configReader.getStruct("WS_MacroInit", client->GetVersion());
if (macro_packet) {
map<int8, vector<MacroData*> >::iterator itr;
macro_packet->setArrayLengthByName("macro_count", macros->size());
int8 x = 0;
for (itr = macros->begin(); itr != macros->end(); itr++, x++) {
macro_packet->setArrayDataByName("number", itr->first, x);
if (itr->second.size() > 0) {
LogWrite(PACKET__DEBUG, 5, "Packet", "Loading Macro %i, name: %s", itr->first, itr->second[0]->name.c_str());
macro_packet->setArrayDataByName("name", itr->second[0]->name.c_str(), x);
}
if (client->GetVersion() > 373) {
char tmp_details_count[25] = { 0 };
sprintf(tmp_details_count, "macro_details_count_%i", x);
macro_packet->setArrayLengthByName(tmp_details_count, itr->second.size());
for (int8 i = 0; i < itr->second.size(); i++) {
char tmp_command[15] = { 0 };
sprintf(tmp_command, "command%i", x);
LogWrite(PACKET__DEBUG, 5, "Packet", "\tLoading Command %i: %s", itr->first, x, itr->second[i]->text.c_str());
macro_packet->setArrayDataByName(tmp_command, itr->second[i]->text.c_str(), i);
if ( i > 0 ) // itr->second[0] used below, we will delete it later
safe_delete(itr->second[i]); // delete MacroData*
}
macro_packet->setArrayDataByName("unknown2", 2, x);
macro_packet->setArrayDataByName("unknown3", 0xFFFFFFFF, x);
}
else {
if (itr->second.size() > 0)
macro_packet->setArrayDataByName("command", itr->second[0]->text.c_str(), x);
}
macro_packet->setArrayDataByName("icon", itr->second[0]->icon, x);
client->GetPlayer()->macro_icons[itr->first] = itr->second[0]->icon;
// remove itr->second[0] now that we are done with it
safe_delete(itr->second[0]); // delete MacroData*
}
EQ2Packet* packet = macro_packet->serialize();
client->QueuePacket(packet);
safe_delete(macro_packet);
}
safe_delete(macros);
}
}
void ClientPacketFunctions::SendMOTD ( Client* client ){
const char* motd = 0;
// fetch MOTD from `variables` table
Variable* var = variables.FindVariable("motd");
if( var == NULL || strlen (var->GetValue()) == 0) {
LogWrite(WORLD__WARNING, 0, "World", "No MOTD set. Sending generic message...");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Message of the Day: Welcome to EQ2Emulator! Customize this message in the `variables`.`motd` data!");
}
else {
motd = var->GetValue();
LogWrite(WORLD__DEBUG, 0, "World", "Send MOTD...");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, motd);
}
}
void ClientPacketFunctions::SendUpdateSpellBook ( Client* client ){
if(client->IsReadyForSpawns()){
EQ2Packet* app = client->GetPlayer()->GetSpellBookUpdatePacket(client->GetVersion());
if(app)
client->QueuePacket(app);
}
client->GetPlayer()->UnlockAllSpells(true);
}
void ClientPacketFunctions::SendServerControlFlagsClassic(Client* client, int32 param, int32 value) {
PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", client->GetVersion());
if(packet) {
packet->setDataByName("parameter", param);
packet->setDataByName("value", value);
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
void ClientPacketFunctions::SendServerControlFlags(Client* client, int8 param, int8 param_val, int8 value) {
PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", client->GetVersion());
if(packet) {
if (param == 1)
packet->setDataByName("parameter1", param_val);
else if (param == 2)
packet->setDataByName("parameter2", param_val);
else if (param == 3)
packet->setDataByName("parameter3", param_val);
else if (param == 4)
packet->setDataByName("parameter4", param_val);
else if (param == 5)
packet->setDataByName("parameter5", param_val);
else {
safe_delete(packet);
return;
}
packet->setDataByName("value", value);
client->QueuePacket(packet->serialize());
/*
Some other values for this packet
first param:
01 flymode
02 collisons off
04 unknown
08 heading movement only
16 forward/reverse movement only
32 low gravity
64 sit
second
2 crouch
third:
04 float when trying to jump, no movement
08 jump high, no movement
128 walk underwater
fourth:
01 moon jump underwater
04 fear
16 moon jumps
32 safe fall (float to ground)
64 cant move
fifth:
01 die
08 hover (fae)
32 flymode2?
*/
}
safe_delete(packet);
}
void ClientPacketFunctions::SendInstanceList(Client* client) {
if (client->GetPlayer()->GetCharacterInstances()->GetInstanceCount() > 0) {
PacketStruct* packet = configReader.getStruct("WS_InstanceCreated", client->GetVersion());
if (packet) {
vector<InstanceData> persist = client->GetPlayer()->GetCharacterInstances()->GetPersistentInstances();
vector<InstanceData> lockout = client->GetPlayer()->GetCharacterInstances()->GetLockoutInstances();
packet->setArrayLengthByName("num_instances", lockout.size());
for (int32 i = 0; i < lockout.size(); i++) {
InstanceData data = lockout.at(i);
packet->setArrayDataByName("unknown1", data.db_id, i); // unique id per player
packet->setArrayDataByName("instance_zone_name", data.zone_name.c_str(), i);
packet->setArrayDataByName("unknown2", 0x0B, i); // Always set to 0x0B on live packets
packet->setArrayDataByName("success_last", data.last_success_timestamp, i);
packet->setArrayDataByName("last_failure", data.last_failure_timestamp, i);
packet->setArrayDataByName("failure", data.failure_lockout_time, i);
packet->setArrayDataByName("success", data.success_lockout_time, i);
}
packet->setArrayLengthByName("num_persistent", persist.size());
for (int32 i = 0; i < persist.size(); i++) {
InstanceData data = persist.at(i);
packet->setArrayDataByName("unknown1a", data.db_id, i); // unique id per player
packet->setArrayDataByName("persistent_zone_name", data.zone_name.c_str(), i);
packet->setArrayDataByName("unknown2a", 0x0B, i); // set to 0x0B in all live packets
packet->setArrayDataByName("persist_success_timestamp", data.last_success_timestamp, i);
packet->setArrayDataByName("persist_failure_timestamp", data.last_failure_timestamp, i);
// Check min duration (last success + failure)
//if (Timer::GetUnixTimeStamp() < data.last_success_timestamp + data.failure_lockout_time*/)
//packet->setArrayDataByName("unknown3b", 1, i);
packet->setArrayDataByName("unknown3b", 1, i);
packet->setArrayDataByName("minimum_duration", data.failure_lockout_time, i);
packet->setArrayDataByName("maximum_duration", data.success_lockout_time, i);
packet->setArrayDataByName("unknown4a", 1800, i); // All live logs have 0x0708
}
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
}
void ClientPacketFunctions::SendMaintainedExamineUpdate(Client* client, int8 slot_pos, int32 update_value, int8 update_type){
if (!client || client->GetVersion() <= 561) // KoS and earlier clients don't support this packet, best to just return and not spam logs w/ errors
return;
PacketStruct* packet = configReader.getStruct("WS_UpdateMaintainedExamine", client->GetVersion());
if (packet){
packet->setSubstructDataByName("info_header", "show_name", 1);
packet->setSubstructDataByName("info_header", "packettype", 19710);
packet->setSubstructDataByName("info_header", "packetsubtype", 5);
packet->setDataByName("time_stamp", Timer::GetCurrentTime2());
packet->setDataByName("slot_pos", slot_pos);
packet->setDataByName("update_value", update_value > 0 ? update_value : 0xFFFFFFFF);
packet->setDataByName("update_type", update_type);
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
}
void ClientPacketFunctions::SendZoneChange(Client* client, char* zone_ip, int16 zone_port, int32 key) {
if (!client)
return;
PacketStruct* packet = configReader.getStruct("WS_ZoneChangeMsg", client->GetVersion());
if (packet) {
packet->setDataByName("account_id", client->GetAccountID());
packet->setDataByName("key", key);
packet->setDataByName("ip_address", zone_ip);
packet->setDataByName("port", zone_port);
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
void ClientPacketFunctions::SendStateCommand(Client* client, int32 spawn_id, int32 state) {
if (!client || !spawn_id) {
return;
}
PacketStruct* packet = configReader.getStruct("WS_StateCmd", client->GetVersion());
if (packet) {
packet->setDataByName("spawn_id", spawn_id);
packet->setDataByName("state", state);
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
void ClientPacketFunctions::SendFlyMode(Client* client, int8 flymode, bool updateCharProperty)
{
if (updateCharProperty)
database.insertCharacterProperty(client, CHAR_PROPERTY_FLYMODE, (char*)std::to_string(flymode).c_str());
if(client->GetVersion() <= 561) {
if(flymode) {
// old flymode
SendServerControlFlagsClassic(client, flymode, 1);
if(flymode == 1) {
// disable noclip
SendServerControlFlagsClassic(client, 2, 0);
}
}
else {
// disable flymode and noclip
SendServerControlFlagsClassic(client, 2, 0);
SendServerControlFlagsClassic(client, 1, 0);
}
}
else {
if(flymode == 2) {
// new flymode + noclip
SendServerControlFlags(client, 5, 32, 1);
SendServerControlFlags(client, 1, 2, 1);
}
else if(flymode == 1) {
// new flymode
SendServerControlFlags(client, 5, 32, 1);
SendServerControlFlags(client, 1, 2, 0);
}
else {
// disable flymode and noclip
SendServerControlFlags(client, 5, 32, 0);
SendServerControlFlags(client, 1, 2, 0);
}
}
client->Message(CHANNEL_STATUS, "Flymode %s, No Clip %s", flymode > 0 ? "on" : "off", flymode > 1 ? "on" : "off");
/*
CLASSIC/DOF ONLY HAS THE FIRST SET OF FLAGS
Some other values for this packet
first param:
01 flymode
02 collisons off
04 unknown
08 forward movement
16 heading movement
32 low gravity
64 sit
EVERYTHING BELOW NOT SUPPORTED BY CLASSIC/DOF
second
2 crouch
third:
04 float when trying to jump, no movement
08 jump high, no movement
fourth:
04 autorun (fear?)
16 moon jumps
32 safe fall (float to ground)
64 cant move
fifth:
01 die
08 hover (fae)
32 flymode2?
*/
}

View File

@ -0,0 +1,92 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "client.h"
struct HouseZone;
struct PlayerHouse;
struct HeroicOP;
class ClientPacketFunctions
{
public:
static void SendFinishedEntitiesList ( Client* client );
static void SendLoginDenied ( Client* client );
static void SendLoginAccepted ( Client* client );
static void SendCommandList ( Client* client );
static void SendGameWorldTime ( Client* client );
static void SendCharacterData ( Client* client );
static void SendCharacterSheet ( Client* client );
static void SendSkillBook ( Client* client );
static void SendTraitList ( Client* client );
static void SendAbilities ( Client* client );
static void SendCommandNamePacket ( Client* client );
static void SendQuickBarInit ( Client* client );
static void SendMOTD ( Client* client );
static void SendCharacterMacros(Client* client);
static void SendUpdateSpellBook ( Client* client );
static void SendSkillSlotMappings(Client* client);
static void SendRestartZoneMsg(Client* client);
static void SendServerControlFlags(Client* client, int8 param, int8 param_val, int8 value);
static void SendServerControlFlagsClassic(Client* client, int32 param, int32 value);
static void SendInstanceList(Client* client);
static void SendZoneChange(Client* client, char* zone_ip, int16 zone_port, int32 key);
static void SendStateCommand(Client* client, int32 spawn_id, int32 state);
static void SendFlyMode(Client* client, int8 flymode, bool updateCharProperty=true);
/* Tradeskills (/Tradeskills/TradeskillsPackets.cpp) */
static void SendCreateFromRecipe(Client* client, int32 recipeID);
static void SendItemCreationUI(Client* client, Recipe* recipe);
static void StopCrafting(Client* client);
static void CounterReaction(Client* client, bool countered);
static void SendAchievementList(Client* client);
/* Housing (/Housing/HousingPackets.cpp) */
static void SendHousePurchase(Client* client, HouseZone* hz, int32 spawnID);
static void SendHousingList(Client* client);
static void SendBaseHouseWindow(Client* client, HouseZone* hz, PlayerHouse* ph, int32 spawnID);
static void SendHouseVisitWindow(Client* client, vector<PlayerHouse*> houses);
static void SendLocalizedTextMessage(Client* client);
/* Heroic OP's (/HeroicOp/HeroicOpPackets.cpp) */
static void SendHeroicOPUpdate(Client* client, HeroicOP* ho);
//UI updates for trigger count and damage remaining on maintained spells
static void SendMaintainedExamineUpdate(Client* client, int8 slot_pos, int32 update_value, int8 update_type);
};

View File

@ -0,0 +1,317 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Collections.h"
#include "../../common/Log.h"
#include <assert.h>
extern MasterCollectionList master_collection_list;
Collection::Collection() {
id = 0;
memset(name, 0, sizeof(name));
memset(category, 0, sizeof(category));
level = 0;
reward_coin = 0;
reward_xp = 0;
completed = false;
save_needed = false;
}
Collection::Collection(Collection *in) {
vector<struct CollectionItem *> *collection_items_in;
vector<struct CollectionRewardItem *> *reward_items_in;
vector<struct CollectionItem *>::iterator itr;
vector<struct CollectionRewardItem *>::iterator itr2;
struct CollectionItem *collection_item;
struct CollectionRewardItem *reward_item;
assert(in);
id = in->GetID();
strncpy(name, in->GetName(), sizeof(name));
strncpy(category, in->GetCategory(), sizeof(category));
level = in->GetLevel();
reward_coin = in->GetRewardCoin();
reward_xp = in->GetRewardXP();
completed = in->GetCompleted();
save_needed = in->GetSaveNeeded();
collection_items_in = in->GetCollectionItems();
for (itr = collection_items_in->begin(); itr != collection_items_in->end(); itr++) {
collection_item = new struct CollectionItem;
collection_item->item = (*itr)->item;
collection_item->index = (*itr)->index;
collection_item->found = (*itr)->found;
collection_items.push_back(collection_item);
}
reward_items_in = in->GetRewardItems();
for (itr2 = reward_items_in->begin(); itr2 != reward_items_in->end(); itr2++) {
reward_item = new struct CollectionRewardItem;
reward_item->item = (*itr2)->item;
reward_item->quantity = (*itr2)->quantity;
reward_items.push_back(reward_item);
}
reward_items_in = in->GetSelectableRewardItems();
for (itr2 = reward_items_in->begin(); itr2 != reward_items_in->end(); itr2++) {
reward_item = new struct CollectionRewardItem;
reward_item->item = (*itr2)->item;
reward_item->quantity = (*itr2)->quantity;
selectable_reward_items.push_back(reward_item);
}
}
Collection::~Collection() {
vector<struct CollectionItem *>::iterator itr;
vector<struct CollectionRewardItem *>::iterator itr2;
for (itr = collection_items.begin(); itr != collection_items.end(); itr++)
safe_delete(*itr);
for (itr2 = reward_items.begin(); itr2 != reward_items.end(); itr2++)
safe_delete(*itr2);
for (itr2 = selectable_reward_items.begin(); itr2 != selectable_reward_items.end(); itr2++)
safe_delete(*itr2);
}
void Collection::AddCollectionItem(struct CollectionItem *collection_item) {
assert(collection_item);
collection_items.push_back(collection_item);
}
void Collection::AddRewardItem(struct CollectionRewardItem *reward_item) {
assert(reward_item);
reward_items.push_back(reward_item);
}
void Collection::AddSelectableRewardItem(struct CollectionRewardItem *reward_item) {
assert(reward_item);
selectable_reward_items.push_back(reward_item);
}
bool Collection::NeedsItem(Item *item) {
vector<struct CollectionItem *>::iterator itr;
struct CollectionItem *collection_item;
assert(item);
if (completed)
return false;
for (itr = collection_items.begin(); itr != collection_items.end(); itr++) {
collection_item = *itr;
if (collection_item->item == item->details.item_id) {
if (collection_item->found)
return false;
else
return true;
}
}
/* item is not required by this collection at all */
return false;
}
struct CollectionItem * Collection::GetCollectionItemByItemID(int32 item_id) {
vector<struct CollectionItem *>::iterator itr;
struct CollectionItem *collection_item;
for (itr = collection_items.begin(); itr != collection_items.end(); itr++) {
collection_item = *itr;
if (collection_item->item == item_id)
return collection_item;
}
return 0;
}
bool Collection::GetIsReadyToTurnIn() {
vector<struct CollectionItem *>::iterator itr;
if (completed)
return false;
for (itr = collection_items.begin(); itr != collection_items.end(); itr++) {
if (!(*itr)->found)
return false;
}
return true;
}
MasterCollectionList::MasterCollectionList() {
mutex_collections.SetName("MasterCollectionList::collections");
}
MasterCollectionList::~MasterCollectionList() {
ClearCollections();
}
bool MasterCollectionList::AddCollection(Collection *collection) {
bool ret = false;
assert(collection);
mutex_collections.writelock(__FUNCTION__, __LINE__);
if (collections.count(collection->GetID()) == 0) {
collections[collection->GetID()] = collection;
ret = true;
}
mutex_collections.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
Collection * MasterCollectionList::GetCollection(int32 collection_id) {
Collection *collection = 0;
mutex_collections.readlock(__FUNCTION__, __LINE__);
if (collections.count(collection_id) > 0)
collection = collections[collection_id];
mutex_collections.releasereadlock(__FUNCTION__, __LINE__);
return collection;
}
void MasterCollectionList::ClearCollections() {
map<int32, Collection *>::iterator itr;
mutex_collections.writelock(__FUNCTION__, __LINE__);
for (itr = collections.begin(); itr != collections.end(); itr++)
safe_delete(itr->second);
collections.clear();
mutex_collections.releasewritelock(__FUNCTION__, __LINE__);
}
int32 MasterCollectionList::Size() {
int32 size;
mutex_collections.readlock(__FUNCTION__, __LINE__);
size = collections.size();
mutex_collections.releasereadlock(__FUNCTION__, __LINE__);
return size;
}
bool MasterCollectionList::NeedsItem(Item *item) {
map<int32, Collection *>::iterator itr;
bool ret = false;
assert(item);
mutex_collections.readlock(__FUNCTION__, __LINE__);
for (itr = collections.begin(); itr != collections.end(); itr++) {
if (itr->second->NeedsItem(item)) {
ret = true;
break;
}
}
mutex_collections.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
PlayerCollectionList::PlayerCollectionList() {
}
PlayerCollectionList::~PlayerCollectionList() {
ClearCollections();
}
bool PlayerCollectionList::AddCollection(Collection *collection) {
assert(collection);
if (collections.count(collection->GetID()) == 0) {
collections[collection->GetID()] = collection;
return true;
}
return false;
}
Collection * PlayerCollectionList::GetCollection(int32 collection_id) {
if (collections.count(collection_id) > 0)
return collections[collection_id];
return 0;
}
void PlayerCollectionList::ClearCollections() {
map<int32, Collection *>::iterator itr;
for (itr = collections.begin(); itr != collections.end(); itr++)
safe_delete(itr->second);
collections.clear();
}
int32 PlayerCollectionList::Size() {
return collections.size();
}
bool PlayerCollectionList::NeedsItem(Item *item) {
map<int32, Collection *> *master_collections;
map<int32, Collection *>::iterator itr;
Collection *collection;
Mutex *master_mutex;
bool ret = false;
assert(item);
for (itr = collections.begin(); itr != collections.end(); itr++) {
if (itr->second->NeedsItem(item)) {
ret = true;
break;
}
}
/* if the player doesnt have a collection that needs the item, check the master collection list to see if there's a collection
* in there that needs the item that the player does not have yet */
if (!ret) {
master_mutex = master_collection_list.GetMutex();
master_collections = master_collection_list.GetCollections();
master_mutex->readlock(__FUNCTION__, __LINE__);
for (itr = master_collections->begin(); itr != master_collections->end(); itr++) {
collection = itr->second;
if (collection->NeedsItem(item) && !GetCollection(collection->GetID())) {
ret = true;
break;
}
}
master_mutex->releasereadlock(__FUNCTION__, __LINE__);
}
return ret;
}
bool PlayerCollectionList::HasCollectionsToHandIn() {
map<int32, Collection *>::iterator itr;
for (itr = collections.begin(); itr != collections.end(); itr++) {
if (itr->second->GetIsReadyToTurnIn())
return true;
}
return false;
}

View File

@ -0,0 +1,107 @@
#ifndef COLLECTIONS_H_
#define COLLECTIONS_H_
#include "../../common/types.h"
#include "../../common/Mutex.h"
#include "../Items/Items.h"
#include <map>
#include <vector>
using namespace std;
struct CollectionItem {
int32 item;
int8 index;
int8 found;
};
struct CollectionRewardItem {
Item *item;
int8 quantity;
};
class Collection {
public:
Collection();
Collection(Collection *in);
virtual ~Collection();
void SetID(int32 id) {this->id = id;}
void SetName(const char *name) {strncpy(this->name, name, sizeof(this->name));}
void SetCategory(const char *category) {strncpy(this->category, category, sizeof(this->category));}
void SetLevel(int8 level) {this->level = level;}
void SetCompleted(bool completed) {this->completed = completed;}
void SetSaveNeeded(bool save_needed) {this->save_needed = save_needed;}
void AddCollectionItem(struct CollectionItem *collection_item);
void AddRewardItem(struct CollectionRewardItem *reward_item);
void AddSelectableRewardItem(struct CollectionRewardItem *reward_item);
void SetRewardCoin(int64 reward_coin) {this->reward_coin = reward_coin;}
void SetRewardXP(int64 reward_xp) {this->reward_xp = reward_xp;}
bool NeedsItem(Item *item);
struct CollectionItem * GetCollectionItemByItemID(int32 item_id);
int32 GetID() {return id;}
const char * GetName() {return name;}
const char * GetCategory() {return category;}
int8 GetLevel() {return level;}
bool GetIsReadyToTurnIn();
bool GetCompleted() {return completed;}
bool GetSaveNeeded() {return save_needed;}
vector<struct CollectionItem *> * GetCollectionItems() {return &collection_items;}
vector<struct CollectionRewardItem *> * GetRewardItems() {return &reward_items;}
vector<struct CollectionRewardItem *> * GetSelectableRewardItems() {return &selectable_reward_items;}
int64 GetRewardCoin() {return reward_coin;}
int64 GetRewardXP() {return reward_xp;}
private:
int32 id;
char name[512];
char category[512];
int8 level;
int64 reward_coin;
int64 reward_xp;
bool completed;
bool save_needed;
vector<struct CollectionItem *> collection_items;
vector<struct CollectionRewardItem *> reward_items;
vector<struct CollectionRewardItem *> selectable_reward_items;
};
class MasterCollectionList {
public:
MasterCollectionList();
virtual ~MasterCollectionList();
bool AddCollection(Collection *collection);
Collection * GetCollection(int32 collection_id);
void ClearCollections();
int32 Size();
bool NeedsItem(Item *item);
Mutex * GetMutex() {return &mutex_collections;}
map<int32, Collection *> * GetCollections() {return &collections;}
private:
Mutex mutex_collections;
map<int32, Collection *> collections;
};
class PlayerCollectionList {
public:
PlayerCollectionList();
virtual ~PlayerCollectionList();
bool AddCollection(Collection *collection);
Collection * GetCollection(int32 collection_id);
void ClearCollections();
int32 Size();
bool NeedsItem(Item *item);
bool HasCollectionsToHandIn();
map<int32, Collection *> * GetCollections() {return &collections;}
private:
map<int32, Collection *> collections;
};
#endif

View File

@ -0,0 +1,295 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#endif
#include <mysql.h>
#include <assert.h>
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "Collections.h"
extern MasterCollectionList master_collection_list;
void WorldDatabase::LoadCollections()
{
Collection *collection;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 cItems_total = 0;
int32 cItems_rewards = 0;
res = query.RunQuery2(Q_SELECT, "SELECT `id`,`collection_name`,`collection_category`,`level`\n"
"FROM `collections`");
if (res)
{
while ((row = mysql_fetch_row(res)))
{
collection = new Collection();
collection->SetID(atoul(row[0]));
collection->SetName(row[1]);
collection->SetCategory(row[2]);
collection->SetLevel(atoi(row[3]));
LogWrite(COLLECTION__DEBUG, 5, "Collect", "\tLoading Collection: '%s' (%u)", collection->GetName(),collection->GetID());
if (!master_collection_list.AddCollection(collection))
{
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error adding collection '%s' - duplicate ID: %u", collection->GetName(),collection->GetID());
safe_delete(collection);
continue;
}
cItems_total += LoadCollectionItems(collection);
cItems_rewards += LoadCollectionRewards(collection);
}
}
LogWrite(COLLECTION__DEBUG, 0, "Collect", "\tLoaded %u collections", master_collection_list.Size());
LogWrite(COLLECTION__DEBUG, 0, "Collect", "\tLoaded %u collection items", cItems_total);
LogWrite(COLLECTION__DEBUG, 0, "Collect", "\tLoaded %u collection rewards", cItems_rewards);
}
int32 WorldDatabase::LoadCollectionItems(Collection *collection)
{
struct CollectionItem *collection_item;
Item *item;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 total = 0;
assert(collection);
res = query.RunQuery2(Q_SELECT, "SELECT `item_id`,`item_index`\n"
"FROM `collection_details`\n"
"WHERE `collection_id`=%u\n"
"ORDER BY `item_index` ASC",
collection->GetID());
if (res)
{
while ((row = mysql_fetch_row(res)))
{
if ((item = master_item_list.GetItem(atoul(row[0]))))
{
collection_item = new struct CollectionItem;
collection_item->item = atoul(row[0]);
collection_item->index = atoi(row[1]);
collection_item->found = 0;
LogWrite(COLLECTION__DEBUG, 5, "Collect", "\tLoading Collection Item: (%u)", atoul(row[0])); //LogWrite(COLLECTION__DEBUG, 5, "Collect", "\tLoading Collection Item: '%s' (%u)", master_item_list.GetItem(collection_item->item)->name.c_str(), atoul(row[0]));
collection->AddCollectionItem(collection_item);
total++;
}
}
}
if(query.GetErrorNumber())
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error Loading Collection Items, Query: %s, Error: %s", query.GetQuery(), query.GetError());
return total;
}
int32 WorldDatabase::LoadCollectionRewards(Collection *collection)
{
struct CollectionRewardItem *reward_item;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 total = 0;
assert(collection);
res = query.RunQuery2(Q_SELECT, "SELECT `reward_type`,`reward_value`,`reward_quantity`\n"
"FROM `collection_rewards`\n"
"WHERE `collection_id`=%u",
collection->GetID());
if (res)
{
while ((row = mysql_fetch_row(res)))
{
LogWrite(COLLECTION__DEBUG, 5, "Collect", "\tLoading Collection Reward: Type: %s, Val: %s, Qty: %u", row[0], row[1], atoi(row[2]));
if (!strcasecmp(row[0], "Item"))
{
reward_item = new struct CollectionRewardItem;
reward_item->item = master_item_list.GetItem(atoul(row[1]));
reward_item->quantity = atoi(row[2]);
collection->AddRewardItem(reward_item);
total++;
}
else if (!strcasecmp(row[0], "Selectable"))
{
reward_item = new struct CollectionRewardItem;
reward_item->item = master_item_list.GetItem(atoul(row[1]));
reward_item->quantity = atoi(row[2]);
collection->AddSelectableRewardItem(reward_item);
total++;
}
else if (!strcasecmp(row[0], "Coin"))
{
collection->SetRewardCoin(atoi64(row[1]));
total++;
}
else if (!strcasecmp(row[0], "XP"))
{
collection->SetRewardXP(atoi64(row[1]));
total++;
}
else
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error adding collection reward to collection '%s'. Unknown reward type '%s'", collection->GetName(), row[0]);
}
}
if(query.GetErrorNumber())
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error Loading Collection Rewards, Query: %s, Error: %s", query.GetQuery(), query.GetError());
return total;
}
void WorldDatabase::LoadPlayerCollections(Player *player)
{
Collection *collection;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
assert(player);
res = query.RunQuery2(Q_SELECT, "SELECT `collection_id`,`completed` FROM `character_collections` WHERE `char_id`=%u", player->GetCharacterID());
if (res)
{
while ((row = mysql_fetch_row(res)))
{
collection = new Collection(master_collection_list.GetCollection(atoul(row[0])));
collection->SetCompleted(atoi(row[1]));
if (!player->GetCollectionList()->AddCollection(collection))
{
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error adding collection %u to player '%s' - duplicate ID\n", collection->GetID(), player->GetName());
safe_delete(collection);
continue;
}
LoadPlayerCollectionItems(player, collection);
}
}
if(query.GetErrorNumber())
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error Loading Character Collections, Query: %s, Error: %s", query.GetQuery(), query.GetError());
}
void WorldDatabase::LoadPlayerCollectionItems(Player *player, Collection *collection)
{
struct CollectionItem *collection_item;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
assert(player);
assert(collection);
res = query.RunQuery2(Q_SELECT, "SELECT `collection_item_id`\n"
"FROM `character_collection_items`\n"
"WHERE `char_id`=%u\n"
"AND `collection_id`=%u",
player->GetCharacterID(), collection->GetID());
if (res)
{
while ((row = mysql_fetch_row(res)))
{
if ((collection_item = collection->GetCollectionItemByItemID(atoul(row[0]))))
collection_item->found = true;
else
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error Loading character collection items. Item ID %u does not exist in collection %s", atoul(row[0]), collection->GetName());
}
}
if(query.GetErrorNumber())
LogWrite(COLLECTION__ERROR, 0, "Collect", "Error Loading Character Collection Items, Query: %s, Error: %s", query.GetQuery(), query.GetError());
}
void WorldDatabase::SavePlayerCollections(Client *client)
{
map<int32, Collection *> *collections;
map<int32, Collection *>::iterator itr;
Collection *collection;
assert(client);
collections = client->GetPlayer()->GetCollectionList()->GetCollections();
for (itr = collections->begin(); itr != collections->end(); itr++)
{
collection = itr->second;
if (collection->GetSaveNeeded())
{
SavePlayerCollection(client, collection);
SavePlayerCollectionItems(client, collection);
collection->SetSaveNeeded(false);
}
}
}
void WorldDatabase::SavePlayerCollection(Client *client, Collection *collection)
{
Query query;
assert(client);
assert(collection);
query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "INSERT INTO `character_collections` (`char_id`,`collection_id`,`completed`)\n"
"VALUES (%u,%u,0)\n"
"ON DUPLICATE KEY UPDATE `completed`=%i",
client->GetPlayer()->GetCharacterID(), collection->GetID(),
collection->GetCompleted() ? 1 : 0);
}
void WorldDatabase::SavePlayerCollectionItems(Client *client, Collection *collection)
{
vector<struct CollectionItem *> *collection_items;
vector<struct CollectionItem *>::iterator itr;
struct CollectionItem *collection_item;
assert(client);
assert(collection);
collection_items = collection->GetCollectionItems();
for (itr = collection_items->begin(); itr != collection_items->end(); itr++)
{
collection_item = *itr;
if (collection_item->found > 0)
SavePlayerCollectionItem(client, collection, collection_item->item);
}
}
void WorldDatabase::SavePlayerCollectionItem(Client *client, Collection *collection, int32 item_id)
{
Query query;
assert(client);
assert(collection);
//assert(item);
query.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "INSERT IGNORE INTO `character_collection_items` (`char_id`,`collection_id`,`collection_item_id`)\n"
"VALUES (%u,%u,%u)",
client->GetPlayer()->GetCharacterID(), collection->GetID(), item_id);
}

2073
old/world/Combat.cpp Normal file

File diff suppressed because it is too large Load Diff

33
old/world/Combat.h Normal file
View File

@ -0,0 +1,33 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EQ2_COMBAT_H__
#define __EQ2_COMBAT_H__
#include "Player.h"
#include "../common/timer.h"
#include "NPC_AI.h"
#include "MutexList.h"
class ZoneServer;
class SpellProcess;
class LuaSpell;
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,320 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#endif
#include <mysql.h>
#include <assert.h>
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "Commands.h"
#include "ConsoleCommands.h"
map<int32, string>* WorldDatabase::GetSpawnTemplateListByName(const char* name)
{
LogWrite(COMMAND__DEBUG, 0, "Command", "Player listing spawn templates by template name ('%s')...", name);
map<int32, string>* ret = 0;
string template_name = "";
Query query;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name FROM spawn_templates WHERE name RLIKE '%s' LIMIT 0,10", getSafeEscapeString(name).c_str());
if(result && mysql_num_rows(result) > 0)
{
ret = new map<int32, string>;
MYSQL_ROW row;
while(result && (row = mysql_fetch_row(result)))
{
template_name = string(row[1]);
(*ret)[atoul(row[0])] = template_name;
LogWrite(COMMAND__DEBUG, 5, "Command", "\t%u. '%s'", atoul(row[0]), template_name.c_str());
}
}
return ret;
}
map<int32, string>* WorldDatabase::GetSpawnTemplateListByID(int32 location_id)
{
LogWrite(COMMAND__DEBUG, 0, "Command", "Player listing spawn templates by LocaionID: %u...", location_id);
map<int32, string>* ret = 0;
string template_name = "";
Query query;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name FROM spawn_templates WHERE spawn_location_id = %u", location_id);
if(result && mysql_num_rows(result) > 0)
{
ret = new map<int32, string>;
MYSQL_ROW row;
while(result && (row = mysql_fetch_row(result)))
{
template_name = string(row[1]);
(*ret)[atoul(row[0])] = template_name;
LogWrite(COMMAND__DEBUG, 5, "Command", "\t%u. '%s'", atoul(row[0]), template_name.c_str());
}
}
return ret;
}
int32 WorldDatabase::SaveSpawnTemplate(int32 placement_id, const char* template_name)
{
Query query;
string str_name = getSafeEscapeString(template_name).c_str();
LogWrite(COMMAND__DEBUG, 0, "Command", "Player saving spawn template '%s' for placement_id %u...", str_name.c_str(), placement_id);
query.RunQuery2(Q_INSERT, "INSERT INTO spawn_templates (name, spawn_location_id) VALUES ('%s', %u)", str_name.c_str(), placement_id);
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in SaveSpawnTemplate query '%s': %s", query.GetQuery(), query.GetError());
return 0;
}
int32 ret = query.GetLastInsertedID();
LogWrite(COMMAND__DEBUG, 0, "Command", "Success! Returning TemplateID: %u...", ret);
return ret;
}
bool WorldDatabase::RemoveSpawnTemplate(int32 template_id)
{
Query query;
LogWrite(COMMAND__DEBUG, 0, "Command", "Player removing spawn template ID %u...", template_id);
query.RunQuery2(Q_DELETE, "DELETE FROM spawn_templates WHERE id = %u", template_id);
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in RemoveSpawnTemplate query '%s': %s", query.GetQuery(), query.GetError());
return false;
}
if (query.GetAffectedRows() > 0 )
{
LogWrite(COMMAND__DEBUG, 0, "Command", "Success! Removed spawn template ID %u...", template_id);
return true;
}
return false;
}
int32 WorldDatabase::CreateSpawnFromTemplateByID(Client* client, int32 template_id)
{
if(client && client->GetCurrentZone() && client->GetCurrentZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE) {
return 0;
}
Query query, query2, query3, query4, query5, query6;
MYSQL_ROW row;
int32 spawn_location_id = 0;
float new_x = client->GetPlayer()->GetX();
float new_y = client->GetPlayer()->GetY();
float new_z = client->GetPlayer()->GetZ();
float new_heading = client->GetPlayer()->GetHeading();
LogWrite(COMMAND__DEBUG, 0, "Command", "Creating spawn point from templateID %u...", template_id);
LogWrite(COMMAND__DEBUG, 5, "Command", "\tCoords: %.2f %.2f %.2f...", new_x, new_y, new_z);
// find the spawn_location_id in the template we plan to duplicate
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spawn_location_id FROM spawn_templates WHERE id = %u", template_id);
if (result && (row = mysql_fetch_row(result))) {
if (row[0])
spawn_location_id = atoi(row[0]);
}
if( spawn_location_id > 0 )
{
LogWrite(COMMAND__DEBUG, 5, "Command", "\tUsing LocationID: %u...", spawn_location_id);
// insert a new spawn_location_name record
string name = "TemplateGenerated";
query2.RunQuery2(Q_INSERT, "INSERT INTO spawn_location_name (name) VALUES ('%s')", name.c_str());
if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in CreateSpawnFromTemplateByID query '%s': %s", query2.GetQuery(), query2.GetError());
return 0;
}
int32 new_location_id = query2.GetLastInsertedID();
LogWrite(COMMAND__DEBUG, 5, "Command", "Created new Spawn Location: '%s' (%u)", name.c_str(), new_location_id);
// get all spawn_location_entries that match the templates spawn_location_id value and insert as new
LogWrite(COMMAND__DEBUG, 5, "Command", "Finding existing spawn_location_entry(s) for location_id %u", spawn_location_id);
MYSQL_RES* result2 = query3.RunQuery2(Q_SELECT, "SELECT spawn_id, spawnpercentage FROM spawn_location_entry WHERE spawn_location_id = %u", spawn_location_id);
if(result2 && mysql_num_rows(result2) > 0){
MYSQL_ROW row2;
while(result2 && (row2 = mysql_fetch_row(result2)) && row2[0])
{
query4.RunQuery2(Q_INSERT, "INSERT INTO spawn_location_entry (spawn_id, spawn_location_id, spawnpercentage) VALUES (%u, %u, %i)",
atoul(row2[0]), new_location_id, atoi(row2[1]));
if(query4.GetErrorNumber() && query4.GetError() && query4.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in CreateSpawnFromTemplateByID query '%s': %s", query4.GetQuery(), query4.GetError());
return 0;
}
LogWrite(COMMAND__DEBUG, 5, "Command", "Insert Entry for spawn_id %u, location_id %u, percentage %i", atoul(row2[0]), new_location_id, atoi(row2[1]));
}
}
// get all spawn_location_placements that match the templates spawn_location_id value and insert as new
// Note: /spawn templates within current zone_id only, because of spawn_id issues (cannot template an Antonic spawn in Commonlands)
LogWrite(COMMAND__DEBUG, 5, "Command", "Finding existing spawn_location_placement(s) for location_id %u", spawn_location_id);
MYSQL_RES* result3 = query5.RunQuery2(Q_SELECT, "SELECT zone_id, x_offset, y_offset, z_offset, respawn, expire_timer, expire_offset, grid_id FROM spawn_location_placement WHERE spawn_location_id = %u", spawn_location_id);
if(result3 && mysql_num_rows(result3) > 0){
MYSQL_ROW row3;
while(result3 && (row3 = mysql_fetch_row(result3)) && row3[0])
{
query6.RunQuery2(Q_INSERT, "INSERT INTO spawn_location_placement (zone_id, spawn_location_id, x, y, z, heading, x_offset, y_offset, z_offset, respawn, expire_timer, expire_offset, grid_id) VALUES (%i, %u, %2f, %2f, %2f, %2f, %2f, %2f, %2f, %i, %i, %i, %u)",
atoi(row3[0]), new_location_id, new_x, new_y, new_z, new_heading, atof(row3[1]), atof(row3[2]), atof(row3[3]), atoi(row3[4]), atoi(row3[5]), atoi(row3[6]), atoul(row3[7]));
if(query6.GetErrorNumber() && query6.GetError() && query6.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in CreateSpawnFromTemplateByID query '%s': %s", query6.GetQuery(), query6.GetError());
return 0;
}
LogWrite(COMMAND__DEBUG, 5, "Command", "Insert Placement at new coords for location_id %u", new_location_id);
}
}
LogWrite(COMMAND__DEBUG, 0, "Command", "Success! New spawn(s) from TemplateID %u created from location %u", template_id, spawn_location_id);
return new_location_id;
}
return 0;
}
int32 WorldDatabase::CreateSpawnFromTemplateByName(Client* client, const char* template_name)
{
Query query, query1, query2, query3, query4, query5, query6;
MYSQL_ROW row;
int32 template_id = 0;
int32 spawn_location_id = 0;
float new_x = client->GetPlayer()->GetX();
float new_y = client->GetPlayer()->GetY();
float new_z = client->GetPlayer()->GetZ();
float new_heading = client->GetPlayer()->GetHeading();
LogWrite(COMMAND__DEBUG, 0, "Command", "Creating spawn point from template '%s'...", template_name);
LogWrite(COMMAND__DEBUG, 5, "Command", "\tCoords: %.2f %.2f %.2f...", new_x, new_y, new_z);
// find the spawn_location_id in the template we plan to duplicate
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, spawn_location_id FROM spawn_templates WHERE name = '%s'", template_name);
if (result && (row = mysql_fetch_row(result))) {
if (row[0])
{
template_id = atoul(row[0]);
spawn_location_id = atoi(row[1]);
}
}
if( spawn_location_id > 0 )
{
LogWrite(COMMAND__DEBUG, 5, "Command", "\tUsing LocationID: %u...", spawn_location_id);
// insert a new spawn_location_name record
string name = "TemplateGenerated";
query2.RunQuery2(Q_INSERT, "INSERT INTO spawn_location_name (name) VALUES ('%s')", name.c_str());
if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in CreateSpawnFromTemplateByID query '%s': %s", query2.GetQuery(), query2.GetError());
return 0;
}
int32 new_location_id = query2.GetLastInsertedID();
LogWrite(COMMAND__DEBUG, 5, "Command", "Created new Spawn Location: '%s' (%u)", name.c_str(), new_location_id);
// get all spawn_location_entries that match the templates spawn_location_id value and insert as new
LogWrite(COMMAND__DEBUG, 5, "Command", "Finding existing spawn_location_entry(s) for location_id %u", spawn_location_id);
MYSQL_RES* result2 = query3.RunQuery2(Q_SELECT, "SELECT spawn_id, spawnpercentage FROM spawn_location_entry WHERE spawn_location_id = %u", spawn_location_id);
if(result2 && mysql_num_rows(result2) > 0){
MYSQL_ROW row2;
while(result2 && (row2 = mysql_fetch_row(result2)) && row2[0])
{
query4.RunQuery2(Q_INSERT, "INSERT INTO spawn_location_entry (spawn_id, spawn_location_id, spawnpercentage) VALUES (%u, %u, %i)",
atoul(row2[0]), new_location_id, atoi(row2[1]));
if(query4.GetErrorNumber() && query4.GetError() && query4.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in CreateSpawnFromTemplateByID query '%s': %s", query4.GetQuery(), query4.GetError());
return 0;
}
LogWrite(COMMAND__DEBUG, 5, "Command", "Insert Entry for spawn_id %u, location_id %u, percentage %i", atoul(row2[0]), new_location_id, atoi(row2[1]));
}
}
// get all spawn_location_placements that match the templates spawn_location_id value and insert as new
// Note: /spawn templates within current zone_id only, because of spawn_id issues (cannot template an Antonic spawn in Commonlands)
LogWrite(COMMAND__DEBUG, 5, "Command", "Finding existing spawn_location_placement(s) for location_id %u", spawn_location_id);
MYSQL_RES* result3 = query5.RunQuery2(Q_SELECT, "SELECT zone_id, x_offset, y_offset, z_offset, respawn, expire_timer, expire_offset, grid_id FROM spawn_location_placement WHERE spawn_location_id = %u", spawn_location_id);
if(result3 && mysql_num_rows(result3) > 0){
MYSQL_ROW row3;
while(result3 && (row3 = mysql_fetch_row(result3)) && row3[0])
{
query6.RunQuery2(Q_INSERT, "INSERT INTO spawn_location_placement (zone_id, spawn_location_id, x, y, z, heading, x_offset, y_offset, z_offset, respawn, expire_timer, expire_offset, grid_id) VALUES (%i, %u, %2f, %2f, %2f, %2f, %2f, %2f, %2f, %i, %i, %i, %u)",
atoi(row3[0]), new_location_id, new_x, new_y, new_z, new_heading, atof(row3[1]), atof(row3[2]), atof(row3[3]), atoi(row3[4]), atoi(row3[5]), atoi(row3[6]), atoul(row3[7]));
if(query6.GetErrorNumber() && query6.GetError() && query6.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(COMMAND__ERROR, 0, "Command", "Error in CreateSpawnFromTemplateByID query '%s': %s", query6.GetQuery(), query6.GetError());
return 0;
}
LogWrite(COMMAND__DEBUG, 5, "Command", "Insert Placement at new coords for location_id %u", new_location_id);
}
}
LogWrite(COMMAND__DEBUG, 0, "Command", "Success! New spawn(s) from TemplateID %u created from location %u", template_id, spawn_location_id);
return new_location_id;
}
return 0;
}
bool WorldDatabase::SaveZoneSafeCoords(int32 zone_id, float x, float y, float z, float heading)
{
Query query;
LogWrite(COMMAND__DEBUG, 0, "Command", "Setting safe coords for zone %u (X: %.2f, Y: %.2f, Z: %.2f, H: %.2f)", zone_id, x, y, z, heading);
query.RunQuery2(Q_UPDATE, "UPDATE zones SET safe_x = %f, safe_y = %f, safe_z = %f, safe_heading = %f WHERE id = %u", x, y, z, heading, zone_id);
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(DATABASE__ERROR, 0, "DBCore", "Error in SaveZoneSafeCoords query '%s': %s", query.GetQuery(), query.GetError());
return false;
}
if (query.GetAffectedRows() > 0 )
{
LogWrite(COMMAND__DEBUG, 0, "Command", "Success! Set new safe coordinates in zone ID %u...", zone_id);
return true;
}
LogWrite(COMMAND__ERROR, 0, "Command", "FAILED! Set new safe coordinates in zone ID %u...", zone_id);
return false;
}
bool WorldDatabase::SaveSignZoneToCoords(int32 spawn_id, float x, float y, float z, float heading)
{
Query query;
LogWrite(COMMAND__DEBUG, 0, "Command", "Setting Zone-To coords for Spawn ID %u (X: %.2f, Y: %.2f, Z: %.2f, H: %.2f)", spawn_id, x, y, z, heading);
query.RunQuery2(Q_UPDATE, "UPDATE spawn_signs SET zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f WHERE spawn_id = %u", x, y, z, heading, spawn_id);
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(DATABASE__ERROR, 0, "DBCore", "Error in SaveSignZoneToCoords query '%s': %s", query.GetQuery(), query.GetError());
return false;
}
if (query.GetAffectedRows() > 0 )
{
LogWrite(COMMAND__DEBUG, 0, "Command", "Success! Set new Zone-To coordinates in zone ID %u...", spawn_id);
return true;
}
LogWrite(COMMAND__ERROR, 0, "Command", "FAILED! Set new Zone-To coordinates in zone ID %u...", spawn_id);
return false;
}

View File

@ -0,0 +1,549 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
using namespace std;
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "../../common/debug.h"
#include "../../common/Log.h"
#include "../../common/seperator.h"
#include "ConsoleCommands.h"
#include "../World.h"
#include "../Rules/Rules.h"
#include "../WorldDatabase.h"
extern volatile bool RunLoops;
bool ContinueLoops = false;
extern Variables variables;
extern ZoneList zone_list;
extern RuleManager rule_manager;
extern WorldDatabase database;
void ProcessConsoleInput(const char * cmdInput)
{
static ConsoleCommand Commands[] = {
// account controls
{ &ConsoleBanCommand, "ban", "[player] {duration} {reason}", "Ban player with {optional} duration and reason." },
{ &ConsoleUnbanCommand, "unban", "[player]", "Unban a player." },
{ &ConsoleKickCommand, "kick", "[player] {reason}", "Kick player with {optional} reason." },
// chat controls
{ &ConsoleAnnounceCommand, "announce", "[message]", "Sends Announcement message to all channels/clients." },
{ &ConsoleBroadcastCommand, "broadcast","[message]", "Sends Broadcast message to all channels/clients." },
{ &ConsoleChannelCommand, "channel", "[channel] [message]", "Sends Channel message to channel." },
{ &ConsoleTellCommand, "tell", "[player] [message]", "Sends Private message to player." },
// world system controls
{ &ConsoleGuildCommand, "guild", "[params]", "" },
{ &ConsolePlayerCommand, "player", "[params]", "" },
{ &ConsoleSetAdminPlayer, "makeadmin", "[charname] [status=0]", "" },
{ &ConsoleZoneCommand, "zone", "[command][value]", "command = help to get help" },
{ &ConsoleWorldCommand, "world", "[params]", "" },
{ &ConsoleGetMOTDCommand, "getmotd", "", "Display current MOTD" },
{ &ConsoleSetMOTDCommand, "setmotd", "[new motd]", "Sets a new MOTD" },
/// misc controls
{ &ConsoleWhoCommand, "who", "{zone id | player}", "Shows who is online globally, or in a given zone." },
{ &ConsoleReloadCommand, "reload", "[all | [type]]", "Reload main systems." },
{ &ConsoleRulesCommand, "rules", "{zone} {id}", "Show Global Ruleset (or Zone ruleset {optional})" },
{ &ConsoleShutdownCommand, "shutdown", "[delay]", "Gracefully shutdown world in [delay] sesconds." },
{ &ConsoleCancelShutdownCommand,"cancel", "", "Cancel shutdown command." },
{ &ConsoleExitCommand, "exit", "", "Brutally kills the world without mercy." },
{ &ConsoleExitCommand, "quit", "", "Brutally kills the world without mercy." },
{ &ConsoleTestCommand, "test", "", "Dev testing command." },
{ NULL, NULL, NULL, NULL },
};
Seperator *sep = new Seperator(cmdInput, ' ', 20, 100, true);
bool found = false;
uint32 i;
if (!sep)
return;
if (!strcasecmp(sep->arg[0], "help") || sep->arg[0][0] == 'h' || sep->arg[0][0] == 'H' || sep->arg[0][0] == '?') {
found = true;
printf("======================================================================================================\n");
printf("| %10s | %30s | %52s |\n", "Name", "Params", "Description");
printf("======================================================================================================\n");
for (i = 0; Commands[i].Name != NULL; i++) {
printf("| %10s | %30s | %52s |\n", Commands[i].Name, Commands[i].ParameterFormat, Commands[i].Description);
}
printf("======================================================================================================\n");
printf("-[ Help formatted for 120 chars wide screen ]-\n");
}
else {
for (i = 0; Commands[i].Name != NULL; ++i) {
if (!strcasecmp(Commands[i].Name, sep->arg[0])) {
found = true;
if (!Commands[i].CommandPointer(sep))
printf("\nError, incorrect syntax for '%s'.\n Correct syntax is: '%s'.\n\n", Commands[i].Name, Commands[i].ParameterFormat );
}
}
}
if (!found)
printf("Invalid Command '%s'! Type '?' or 'help' to get a command list.\n\n", sep->arg[0]);
fflush(stdout);
delete sep;
}
/************************************************* COMMANDS *************************************************/
bool ConsoleBanCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleUnbanCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleKickCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleAnnounceCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleBroadcastCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
char message[4096];
snprintf(message, sizeof(message), "%s %s", "BROADCAST:", sep->argplus[1]);
zone_list.TransmitBroadcast(message);
return true;
}
bool ConsoleChannelCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleTellCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleWhoCommand(Seperator *sep)
{
// zone_list.ProcessWhoQuery(who, client);
if (!strcasecmp(sep->arg[1], "zone")) {
printf("Who's Online in Zone");
if (sep->IsNumber(2)) {
printf("ID %s:\n", sep->arg[2]);
printf("===============================================================================\n");
printf("| %10s | %62s |\n", "CharID", "Name");
printf("===============================================================================\n");
}
else {
printf(" '%s':\n", sep->arg[2]);
printf("===============================================================================\n");
printf("| %10s | %62s |\n", "CharID", "Name");
printf("===============================================================================\n");
}
}
else {
printf("Who's Online (Global):\n");
printf("===============================================================================\n");
printf("| %10s | %20s | %39s |\n", "CharID", "Name", "Zone");
printf("===============================================================================\n");
}
printf("Not Implemented... yet :)\n");
printf("===============================================================================\n");
return true;
}
bool ConsoleGuildCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsolePlayerCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleSetAdminPlayer(Seperator *sep)
{
if(!sep->arg[1] || strlen(sep->arg[1]) == 0)
return false;
sint16 status = 0;
if(sep->IsNumber(2))
status = atoi(sep->arg[2]);
Client* client = zone_list.GetClientByCharName(sep->arg[1]);
if(!client) {
printf("Client not found by char name, must be logged in\n");
return true;
}
if(!client->GetPlayer()) {
printf("Player is not available for client class, try again\n");
return true;
}
client->SetAdminStatus(status);
if(status)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Admin status updated.");
database.UpdateAdminStatus(client->GetPlayer()->GetName(), status);
printf("Admin status for %s is updated to %i\n", client->GetPlayer()->GetName(), status);
return true;
}
bool ConsoleWorldCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleZoneCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 ) // has to be at least 1 arg (command)
return false;
ZoneServer* zone = 0;
if( strlen(sep->arg[2]) == 0 )
{
// process commands without values
if (!strcasecmp(sep->arg[1], "active") )
{
// not correct, but somehow need to access the Private zlist from World.h ???
list<ZoneServer*> zlist;
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
int zonesListed = 0;
printf("> List Active Zones...\n");
printf("======================================================================================================\n");
printf("| %7s | %30s | %10s | %42s |\n", "ID", "Name", "Instance", "Description");
printf("======================================================================================================\n");
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
tmp = *zone_iter;
zonesListed++;
printf("| %7d | %30s | %10d | %42s |\n", tmp->GetZoneID(), tmp->GetZoneName(), tmp->GetInstanceID(),tmp->GetZoneDescription());
}
return true;
}
else if (!strcasecmp(sep->arg[1], "help") || sep->arg[1][0] == '?')
{
printf("======================================================================================================\n");
printf("| %10s | %30s | %52s |\n", "Command", "Value", "Description");
printf("======================================================================================================\n");
printf("| %10s | %30s | %52s |\n", "active", "n/a", "List currently active zones");
printf("| %10s | %30s | %52s |\n", "list", "[name]", "Lookup zone by name");
printf("| %10s | %30s | %52s |\n", "status", "[zone_id | name | ALL]", "List zone stats");
printf("| %10s | %30s | %52s |\n", "lock", "[zone_id | name]", "Locks a zone");
printf("| %10s | %30s | %52s |\n", "unlock", "[zone_id | name]", "Unlocks a zone");
printf("| %10s | %30s | %52s |\n", "shutdown", "[zone_id | name | ALL]", "Gracefully shuts down a zone");
printf("| %10s | %30s | %52s |\n", "kill", "[zone_id | name | ALL]", "Terminates a zone");
printf("======================================================================================================\n");
return true;
}
else
return false;
}
else
{
if( !strcasecmp(sep->arg[1], "list") )
{
const char* name = 0;
name = sep->argplus[2];
map<int32, string>* zone_names = database.GetZoneList(name);
if(!zone_names)
{
printf("> No zones found.\n");
}
else
{
printf("> List zones matching '%s'...\n", sep->arg[2]);
printf("====================================================\n");
printf("| %3s | %42s |\n", "ID", "Name");
printf("====================================================\n");
map<int32, string>::iterator itr;
for(itr = zone_names->begin(); itr != zone_names->end(); itr++)
printf("| %3u | %42s |\n", itr->first, itr->second.c_str());
safe_delete(zone_names);
printf("====================================================\n");
}
return true;
}
if( !strcasecmp(sep->arg[1], "lock") )
{
if( sep->IsNumber(2) )
printf("> Locking zone ID %i...\n", atoul(sep->arg[2]));
else
printf("> Locking zone '%s'...\n", sep->arg[2]);
return true;
}
if( !strcasecmp(sep->arg[1], "unlock") )
{
if( strlen(sep->arg[2]) > 0 && sep->IsNumber(2) )
printf("> Unlocking zone ID %i...\n", atoi(sep->arg[2]));
else
printf("> Unlocking zone '%s'...\n", sep->arg[2]);
return true;
}
if( !strcasecmp(sep->arg[1], "status") )
{
if( sep->IsNumber(2) )
{
ZoneChangeDetails zone_details;
if( zone_list.GetZone(&zone_details, atoi(sep->arg[2]), "", false, false, false) )
{
printf("> Zone status for zone ID %i...\n", atoi(sep->arg[2]));
printf("============================================================================================\n");
printf("| %30s | %10s | %42s |\n", "Zone", "Param", "Value");
printf("============================================================================================\n");
printf("| %30s | %10s | %42s |\n", zone_details.zoneName.c_str(), "locked", zone_details.lockState ? "true" : "false");
}
else
{
printf("> Zone ID %i not running, so not locked.\n", atoi(sep->arg[2]));
}
}
else if( !strcasecmp(sep->arg[2], "ALL") )
{
printf("> Zone status for ALL active zones...\n");
}
else
{
printf("> Zone status for zone '%s'...\n", sep->arg[2]);
}
return true;
}
if( !strcasecmp(sep->arg[1], "shutdown") )
{
if( sep->IsNumber(2) )
printf("> Shutdown zone ID %i...\n", atoi(sep->arg[2]));
else if( !strcasecmp(sep->arg[2], "ALL") )
printf("> Shutdown ALL active zones...\n");
else
printf("> Shutdown zone '%s'...\n", sep->arg[2]);
return true;
}
if( !strcasecmp(sep->arg[1], "kill") )
{
if( sep->IsNumber(2) )
printf("> Kill zone ID %i...\n", atoi(sep->arg[2]));
else if( !strcasecmp(sep->arg[2], "ALL") )
printf("> Kill ALL active zones...\n");
else
printf("> Kill zone '%s'...\n", sep->arg[2]);
return true;
}
}
return false;
}
bool ConsoleGetMOTDCommand(Seperator *sep)
{
const char* motd = 0;
Variable* var = variables.FindVariable("motd");
if( var == NULL || strlen (var->GetValue()) == 0){
printf("No MOTD.");
}
else{
motd = var->GetValue();
printf("%s\n", motd);
}
return true;
}
bool ConsoleSetMOTDCommand(Seperator *sep)
{
if( strlen(sep->arg[1]) == 0 )
return false;
return true;
}
bool ConsoleReloadCommand(Seperator *sep)
{
#ifdef _WIN32
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(console, FOREGROUND_YELLOW_BOLD);
#else
printf("\033[1;33m");
#endif
printf("Usage: ");
#ifdef _WIN32
SetConsoleTextAttribute(console, FOREGROUND_WHITE_BOLD);
#else
printf("\033[1;37m");
#endif
printf("reload [type]\n");
#ifdef _WIN32
SetConsoleTextAttribute(console, 8);
#else
printf("\033[0m");
#endif
printf("Valid [type] paramters are:\n");
printf("===============================================================================\n");
printf("| %21s | %51s |\n", "all", "Reloads all systems (why not just restart?)");
printf("| %21s | %51s |\n", "structs", "Reloads structs (XMLs)");
printf("| %21s | %51s |\n", "items", "Reload Items data");
printf("| %21s | %51s |\n", "luasystem", "Reload LUA System Scripts");
printf("| %21s | %51s |\n", "spawnscripts", "Reload SpawnScripts");
printf("| %21s | %51s |\n", "quests", "Reload Quest Data and Scripts");
printf("| %21s | %51s |\n", "spawns", "Reload ALL Spawns from DB");
printf("| %21s | %51s |\n", "groundspawn_items", "Reload Groundspawn Items lists");
printf("| %21s | %51s |\n", "zonescripts", "Reload Zone Scripts");
printf("| %21s | %51s |\n", "entity_commands", "Reload Entity Commands");
printf("| %21s | %51s |\n", "factions", "Reload Factions");
printf("| %21s | %51s |\n", "mail", "Reload in-game Mail data");
printf("| %21s | %51s |\n", "guilds", "Reload Guilds");
printf("| %21s | %51s |\n", "locations", "Reload Locations data");
printf("===============================================================================\n");
if( strlen(sep->arg[1]) > 0 ) {
// handle reloads here
if (!strcasecmp(sep->arg[1], "spawns"))
zone_list.ReloadSpawns();
}
return true;
}
bool ConsoleShutdownCommand(Seperator *sep)
{
if ( IsNumber(sep->arg[1]) ) {
int8 shutdown_delay = atoi(sep->arg[1]);
printf("Shutdown World in %i second(s)...\n", shutdown_delay);
// shutting down gracefully, warn players.
char message[4096];
snprintf(message, sizeof(message), "BROADCAST: Server is shutting down in %s second(s)", sep->arg[1]);
zone_list.TransmitBroadcast(message);
Sleep(shutdown_delay * 1000);
}
else {
printf("Shutdown World immediately... you probably won't even see this message, huh!\n");
}
if( !ContinueLoops )
RunLoops = false;
return true;
}
bool ConsoleCancelShutdownCommand(Seperator *sep)
{
printf("Cancel World Shutdown...\n");
ContinueLoops = true;
return true;
}
bool ConsoleExitCommand(Seperator *sep)
{
// I wanted this to be a little more Terminate-y... killkillkill
printf("Terminate World immediately...\n");
RunLoops = false;
return true;
}
bool ConsoleRulesCommand(Seperator *sep)
{
/*if( strlen(sep->arg[1]) == 0 )
return false;*/
printf("Current Active Ruleset");
if (!strcasecmp(sep->arg[1], "zone"))
{
if (sep->IsNumber(2)) {
printf(" in Zone ID: %s\n", sep->arg[2]);
}
else
return false;
}
else
{
printf(" (global):\n");
}
printf("===============================================================================\n");
printf("| %20s | %20s | %29s |\n", "Category", "Type", "Value");
printf("===============================================================================\n");
return true;
}
bool ConsoleTestCommand(Seperator *sep)
{
// devs put whatever test code in here
printf("Testing Server Guild Rules values:\n");
printf("AutoJoin: %i\n", rule_manager.GetGlobalRule(R_World, GuildAutoJoin)->GetInt8());
printf("Guild ID: %i\n", rule_manager.GetGlobalRule(R_World, GuildAutoJoinID)->GetInt32());
printf("Rank: %i\n", rule_manager.GetGlobalRule(R_World, GuildAutoJoinDefaultRankID)->GetInt8());
return true;
}

View File

@ -0,0 +1,62 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CONSOLECOMMANDS_H
#define _CONSOLECOMMANDS_H
#include "../../common/seperator.h"
struct ConsoleCommand
{
bool(*CommandPointer)(Seperator *);
const char * Name; // 10 chars
const char * ParameterFormat; // 30 chars
const char * Description; // 40 chars
// = 70 chars
};
void ProcessConsoleInput(const char * command);
bool ConsoleBanCommand(Seperator *sep);
bool ConsoleUnbanCommand(Seperator *sep);
bool ConsoleKickCommand(Seperator *sep);
bool ConsoleAnnounceCommand(Seperator *sep);
bool ConsoleBroadcastCommand(Seperator *sep);
bool ConsoleChannelCommand(Seperator *sep);
bool ConsoleTellCommand(Seperator *sep);
bool ConsoleGuildCommand(Seperator *sep);
bool ConsolePlayerCommand(Seperator *sep);
bool ConsoleSetAdminPlayer(Seperator *sep);
bool ConsoleWorldCommand(Seperator *sep);
bool ConsoleZoneCommand(Seperator *sep);
bool ConsoleGetMOTDCommand(Seperator *sep);
bool ConsoleSetMOTDCommand(Seperator *sep);
bool ConsoleWhoCommand(Seperator *sep);
bool ConsoleReloadCommand(Seperator *sep);
bool ConsoleShutdownCommand(Seperator *sep);
bool ConsoleCancelShutdownCommand(Seperator *sep);
bool ConsoleExitCommand(Seperator *sep);
bool ConsoleRulesCommand(Seperator *sep);
bool ConsoleTestCommand(Seperator *sep);
#endif

442
old/world/EffectFlags.h Normal file
View File

@ -0,0 +1,442 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <shared_mutex>
#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
class LuaSpell;
// ----------------------------- Core bitflags --------------------------------
template <std::size_t NBits>
class BitFlags {
public:
static constexpr std::size_t kWordBits = 64;
static constexpr std::size_t kWords = (NBits + kWordBits - 1) / kWordBits;
constexpr BitFlags() noexcept : words_{} {}
// Single-bit ops
inline void set(std::size_t bit) noexcept { words_[word_index(bit)] |= word_mask(bit); }
inline void reset(std::size_t bit) noexcept { words_[word_index(bit)] &= ~word_mask(bit); }
inline void toggle(std::size_t bit) noexcept { words_[word_index(bit)] ^= word_mask(bit); }
inline bool test(std::size_t bit) const noexcept { return (words_[word_index(bit)] & word_mask(bit)) != 0ULL; }
// Bulk ops
inline void clear() noexcept {
for (std::size_t i = 0; i < kWords; ++i) words_[i] = 0ULL;
}
inline bool any() const noexcept {
for (std::size_t i = 0; i < kWords; ++i) if (words_[i]) return true;
return false;
}
inline bool none() const noexcept { return !any(); }
inline void import_u32(std::uint32_t legacy) noexcept {
if (!legacy) return;
const std::size_t lim = (NBits < 32 ? NBits : 32);
for (std::size_t i = 0; i < lim; ++i)
if ((legacy >> i) & 1u) set(i);
}
inline std::uint32_t export_u32() const noexcept {
std::uint32_t out = 0;
const std::size_t lim = (NBits < 32 ? NBits : 32);
for (std::size_t i = 0; i < lim; ++i)
if (test(i)) out |= (1u << i);
return out;
}
// Import a 64-bit legacy mask (low 64 bits only)
inline void import_u64(std::uint64_t legacy) noexcept {
if (!legacy) return;
const std::size_t lim = (NBits < 64 ? NBits : 64);
for (std::size_t i = 0; i < lim; ++i)
if ((legacy >> i) & 1ULL) set(i);
}
// Export low 64 bits into a uint64_t
inline std::uint64_t export_u64() const noexcept {
std::uint64_t out = 0;
const std::size_t lim = (NBits < 64 ? NBits : 64);
for (std::size_t i = 0; i < lim; ++i)
if (test(i)) out |= (1ULL << i);
return out;
}
// Set by legacy power-of-two value (find index of the single set bit)
inline void set_legacy_flag_value(std::uint64_t pow2) noexcept {
if (!pow2) return;
#if defined(__GNUC__) || defined(__clang__)
std::size_t bit = static_cast<std::size_t>(__builtin_ctzll(pow2));
#else
std::size_t bit = 0; while (bit < 64 && ((pow2 >> bit) & 1ULL) == 0ULL) ++bit;
#endif
if (bit < NBits) set(bit);
}
// Direct word access (used by TS wrapper)
static constexpr std::size_t words_count() noexcept { return kWords; }
inline std::uint64_t& word_ref(std::size_t i) noexcept { return words_[i]; }
inline const std::uint64_t& word_ref(std::size_t i) const noexcept { return words_[i]; }
private:
static constexpr std::size_t word_index(std::size_t bit) noexcept { return bit / kWordBits; }
static constexpr std::uint64_t word_mask (std::size_t bit) noexcept { return 1ULL << (bit % kWordBits); }
std::array<std::uint64_t, kWords> words_;
};
// --------------------------- Thread-safe wrapper ----------------------------
template <std::size_t NBits>
class TSBitFlags {
public:
using Snapshot = BitFlags<NBits>;
// Single-bit ops
inline void set(std::size_t bit) { std::unique_lock lk(m_); bits_.set(bit); }
inline void reset(std::size_t bit) { std::unique_lock lk(m_); bits_.reset(bit); }
inline void toggle(std::size_t bit) { std::unique_lock lk(m_); bits_.toggle(bit); }
inline bool test(std::size_t bit) const { std::shared_lock lk(m_); return bits_.test(bit); }
// Convenience
inline void add(std::size_t bit) { set(bit); }
inline void remove(std::size_t bit) { reset(bit); }
inline bool has(std::size_t bit) const { return test(bit); }
// Batch ops
inline void set_many(std::initializer_list<std::size_t> bits) {
std::unique_lock lk(m_); for (auto b : bits) bits_.set(b);
}
inline void reset_many(std::initializer_list<std::size_t> bits) {
std::unique_lock lk(m_); for (auto b : bits) bits_.reset(b);
}
// Clear / any / none
inline void clear() { std::unique_lock lk(m_); bits_.clear(); }
inline bool any() const { std::shared_lock lk(m_); return bits_.any(); }
inline bool none() const { std::shared_lock lk(m_); return bits_.none(); }
// Legacy helpers
inline void import_u32(std::uint32_t legacy) { std::unique_lock lk(m_); bits_.import_u32(legacy); }
inline std::uint32_t export_u32() const { std::shared_lock lk(m_); return bits_.export_u32(); }
inline void import_u64(std::uint64_t legacy) { std::unique_lock lk(m_); bits_.import_u64(legacy); }
inline std::uint64_t export_u64() const { std::shared_lock lk(m_); return bits_.export_u64(); }
inline void set_legacy_flag_value(std::uint64_t v){ std::unique_lock lk(m_); bits_.set_legacy_flag_value(v); }
// Snapshots (copy out/in without exposing lock)
inline Snapshot snapshot() const { std::shared_lock lk(m_); return bits_; }
inline void assign_from(const Snapshot& snap) {
std::unique_lock lk(m_);
for (std::size_t i = 0; i < Snapshot::words_count(); ++i)
bits_.word_ref(i) = snap.word_ref(i);
}
private:
mutable std::shared_mutex m_;
Snapshot bits_{};
};
// ------------------------- Project-specific typedefs ------------------------
inline constexpr std::size_t kEffectBits = 64; // limited due to DB restrictions at this time
using EffectFlags = TSBitFlags<kEffectBits>;
enum : std::size_t {
EFFECT_IDX_STUN = 0,
EFFECT_IDX_ROOT = 1,
EFFECT_IDX_MEZ = 2,
EFFECT_IDX_STIFLE = 3,
EFFECT_IDX_DAZE = 4,
EFFECT_IDX_FEAR = 5,
EFFECT_IDX_SPELLBONUS = 6,
EFFECT_IDX_SKILLBONUS = 7,
EFFECT_IDX_STEALTH = 8,
EFFECT_IDX_INVIS = 9,
EFFECT_IDX_SNARE = 10,
EFFECT_IDX_WATERWALK = 11,
EFFECT_IDX_WATERJUMP = 12,
EFFECT_IDX_FLIGHT = 13,
EFFECT_IDX_GLIDE = 14,
EFFECT_IDX_AOE_IMMUNE = 15,
EFFECT_IDX_STUN_IMMUNE = 16,
EFFECT_IDX_MEZ_IMMUNE = 17,
EFFECT_IDX_DAZE_IMMUNE = 18,
EFFECT_IDX_ROOT_IMMUNE = 19,
EFFECT_IDX_STIFLE_IMMUNE = 20,
EFFECT_IDX_FEAR_IMMUNE = 21,
EFFECT_IDX_SAFEFALL = 22,
EFFECT_IDX_ILLUSION = 23,
};
class EffectRegistryPerSpell {
public:
using Code = uint8_t; // your int8 codes
using Value = std::variant<std::monostate, int64_t, double, std::string>;
explicit EffectRegistryPerSpell(Code max_code_inclusive)
: max_code_(max_code_inclusive),
slots_(static_cast<size_t>(max_code_inclusive) + 1) {}
// --- Membership (writers) -------------------------------------------------
// Add a spell to a type. Returns true if inserted (false if already present).
bool Add(Code code, LuaSpell* spell) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
return s.members.emplace(spell, Entry{spell, std::monostate{}}).second;
}
// Add or update (set/replace the per-spell value atomically).
// Returns true if inserted new, false if updated existing.
template <typename T>
bool AddOrUpdate(Code code, LuaSpell* spell, T v) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
auto [it, inserted] = s.members.emplace(spell, Entry{spell, std::monostate{}});
it->second.val = to_variant(std::move(v));
return inserted;
}
// Remove a spell from a type. Returns true if erased.
bool Remove(Code code, LuaSpell* spell) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
return s.members.erase(spell) > 0;
}
// Clear all spells at a type
void Clear(Code code) {
if (!in_range(code)) return;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
s.members.clear();
}
// --- Queries (readers) ----------------------------------------------------
// Is there any spell registered at this type?
bool Has(Code code) const {
if (!in_range(code)) return false;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
return !s.members.empty();
}
// Is this specific spell present under this type?
bool Contains(Code code, const LuaSpell* spell) const {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
return s.members.find(const_cast<LuaSpell*>(spell)) != s.members.end();
}
// How many spells are registered at this type?
size_t Count(Code code) const {
if (!in_range(code)) return 0;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
return s.members.size();
}
// Snapshot list of spells under a type (copy out while holding a shared lock).
std::vector<LuaSpell*> Snapshot(Code code) const {
std::vector<LuaSpell*> out;
if (!in_range(code)) return out;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
out.reserve(s.members.size());
for (auto& kv : s.members) out.push_back(kv.first);
return out;
}
// Snapshot of (spell, value) pairs under a type.
struct SpellWithValue {
LuaSpell* spell{};
Value value{};
};
std::vector<SpellWithValue> SnapshotWithValues(Code code) const {
std::vector<SpellWithValue> out;
if (!in_range(code)) return out;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
out.reserve(s.members.size());
for (auto& kv : s.members) out.push_back({kv.second.ptr, kv.second.val});
return out;
}
// --- Per-spell value API --------------------------------------------------
// Set/replace the value for a specific spell under a type.
template <typename T>
bool SetValue(Code code, LuaSpell* spell, T v) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
auto it = s.members.find(spell);
if (it == s.members.end()) return false;
it->second.val = to_variant(std::move(v));
return true;
}
// Clear value for a specific spell under a type (keeps membership).
bool ClearValue(Code code, LuaSpell* spell) {
if (!in_range(code) || !spell) return false;
auto& s = slots_[code];
std::unique_lock lk(s.mtx);
auto it = s.members.find(spell);
if (it == s.members.end()) return false;
it->second.val = std::monostate{};
return true;
}
// Read value as requested type. nullopt if not set or wrong type.
template <typename T>
std::optional<T> GetValue(Code code, const LuaSpell* spell) const {
if (!in_range(code) || !spell) return std::nullopt;
auto& s = slots_[code];
std::shared_lock lk(s.mtx);
auto it = s.members.find(const_cast<LuaSpell*>(spell));
if (it == s.members.end()) return std::nullopt;
if (auto p = std::get_if<T>(&it->second.val)) return *p;
return std::nullopt;
}
// Convenience typed getters
std::optional<int64_t> GetInt (Code code, const LuaSpell* s) const { return GetValue<int64_t>(code, s); }
std::optional<double> GetFloat (Code code, const LuaSpell* s) const { return GetValue<double> (code, s); }
std::optional<std::string> GetString(Code code, const LuaSpell* s) const { return GetValue<std::string>(code, s); }
Code MaxCode() const { return max_code_; }
private:
struct Entry {
LuaSpell* ptr{};
Value val{};
};
struct Slot {
mutable std::shared_mutex mtx;
std::unordered_map<LuaSpell*, Entry> members; // key = spell*, value = (spell*, variant)
};
bool in_range(Code code) const { return code <= max_code_; }
// normalize to variant
static Value to_variant(int64_t v) { return Value{v}; }
static Value to_variant(int v) { return Value{static_cast<int64_t>(v)}; }
static Value to_variant(uint32_t v) { return Value{static_cast<int64_t>(v)}; }
static Value to_variant(float v) { return Value{static_cast<double>(v)}; }
static Value to_variant(double v) { return Value{v}; }
static Value to_variant(const char* s) { return Value{std::string(s ? s : "")}; }
static Value to_variant(std::string s) { return Value{std::move(s)}; }
Code max_code_;
std::vector<Slot> slots_;
};
#define CONTROL_EFFECT_TYPE_MEZ 1
#define CONTROL_EFFECT_TYPE_STIFLE 2
#define CONTROL_EFFECT_TYPE_DAZE 3
#define CONTROL_EFFECT_TYPE_STUN 4
#define CONTROL_EFFECT_TYPE_ROOT 5
#define CONTROL_EFFECT_TYPE_FEAR 6
#define CONTROL_EFFECT_TYPE_WALKUNDERWATER 7
#define CONTROL_EFFECT_TYPE_JUMPUNDERWATER 8
#define CONTROL_EFFECT_TYPE_INVIS 9
#define CONTROL_EFFECT_TYPE_STEALTH 10
#define CONTROL_EFFECT_TYPE_SNARE 11
#define CONTROL_EFFECT_TYPE_FLIGHT 12
#define CONTROL_EFFECT_TYPE_GLIDE 13
#define CONTROL_EFFECT_TYPE_SAFEFALL 14
#define CONTROL_EFFECT_TYPE_ILLUSION 15
#define CONTROL_MAX_EFFECTS 16
#define IMMUNITY_TYPE_MEZ 1
#define IMMUNITY_TYPE_STIFLE 2
#define IMMUNITY_TYPE_DAZE 3
#define IMMUNITY_TYPE_STUN 4
#define IMMUNITY_TYPE_ROOT 5
#define IMMUNITY_TYPE_FEAR 6
#define IMMUNITY_TYPE_AOE 7
#define IMMUNITY_TYPE_TAUNT 8
#define IMMUNITY_TYPE_RIPOSTE 9
#define IMMUNITY_TYPE_STRIKETHROUGH 10
#define IMMUNITY_MAX_TYPES 11
class ControlEffects {
public:
ControlEffects() : reg_(CONTROL_MAX_EFFECTS - 1) {} // max code = 14
bool Add(uint8_t type, LuaSpell* s) { return reg_.Add(type, s); }
template <typename T> bool AddOrUpdate(uint8_t t, LuaSpell* s, T v){ return reg_.AddOrUpdate(t, s, std::move(v)); }
bool Remove(uint8_t type, LuaSpell* s) { return reg_.Remove(type, s); }
void Clear(uint8_t type) { reg_.Clear(type); }
bool Has(uint8_t type) const { return reg_.Has(type); }
bool Contains(uint8_t type, const LuaSpell* s) const { return reg_.Contains(type, s); }
size_t Count(uint8_t type) const { return reg_.Count(type); }
std::vector<LuaSpell*> Snapshot(uint8_t type) const { return reg_.Snapshot(type); }
auto SnapshotWithValues(uint8_t type) const { return reg_.SnapshotWithValues(type); }
template <typename T> bool SetValue(uint8_t t, LuaSpell* s, T v) { return reg_.SetValue(t, s, std::move(v)); }
bool ClearValue(uint8_t t, LuaSpell* s) { return reg_.ClearValue(t, s); }
template <typename T> std::optional<T> GetValue(uint8_t t, const LuaSpell* s) const { return reg_.GetValue<T>(t, s); }
private:
EffectRegistryPerSpell reg_;
};
class Immunities {
public:
Immunities() : reg_(IMMUNITY_MAX_TYPES) {} // max code = 10
bool Add(uint8_t type, LuaSpell* s) { return reg_.Add(type, s); }
template <typename T> bool AddOrUpdate(uint8_t t, LuaSpell* s, T v){ return reg_.AddOrUpdate(t, s, std::move(v)); }
bool Remove(uint8_t type, LuaSpell* s) { return reg_.Remove(type, s); }
void Clear(uint8_t type) { reg_.Clear(type); }
bool Has(uint8_t type) const { return reg_.Has(type); }
bool Contains(uint8_t type, const LuaSpell* s) const { return reg_.Contains(type, s); }
size_t Count(uint8_t type) const { return reg_.Count(type); }
std::vector<LuaSpell*> Snapshot(uint8_t type) const { return reg_.Snapshot(type); }
auto SnapshotWithValues(uint8_t type) const { return reg_.SnapshotWithValues(type); }
template <typename T> bool SetValue(uint8_t t, LuaSpell* s, T v) { return reg_.SetValue(t, s, std::move(v)); }
bool ClearValue(uint8_t t, LuaSpell* s) { return reg_.ClearValue(t, s); }
template <typename T> std::optional<T> GetValue(uint8_t t, const LuaSpell* s) const { return reg_.GetValue<T>(t, s); }
private:
EffectRegistryPerSpell reg_;
};

4036
old/world/Entity.cpp Normal file

File diff suppressed because it is too large Load Diff

2201
old/world/Entity.h Normal file

File diff suppressed because it is too large Load Diff

183
old/world/Factions.cpp Normal file
View File

@ -0,0 +1,183 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Factions.h"
#include "client.h"
extern MasterFactionList master_faction_list;
extern ConfigReader configReader;
PlayerFaction::PlayerFaction(){
MFactionUpdateNeeded.SetName("PlayerFaction::MFactionUpdateNeeded");
}
sint32 PlayerFaction::GetMaxValue(sint8 con){
if(con < 0)
return con * 10000;
else
return (con * 10000) + 9999;
}
sint32 PlayerFaction::GetMinValue(sint8 con){
if(con <= 0)
return (con * 10000) - 9999;
else
return (con * 10000);
}
bool PlayerFaction::ShouldAttack(int32 faction_id){
return (GetCon(faction_id) <= -4);
}
sint8 PlayerFaction::GetCon(int32 faction_id){
if(faction_id <= 10){
if(faction_id == 0)
return 0;
return (faction_id-5);
}
sint32 value = GetFactionValue(faction_id);
if(value >= -9999 && value <= 9999)
return 0;
else{
if(value>= 40000)
return 4;
else if(value <= -40000)
return -4;
return (sint8)(value/10000);
}
}
int8 PlayerFaction::GetPercent(int32 faction_id){
if(faction_id <= 10)
return 0;
sint8 con = GetCon(faction_id);
sint32 value = GetFactionValue(faction_id);
if(con != 0){
if(value <= 0)
value *= -1;
if(con < 0)
con *= -1;
value -= con * 10000;
value *= 100;
return value / 10000;
}
else{
value += 10000;
value *= 100;
return value / 20000;
}
}
EQ2Packet* PlayerFaction::FactionUpdate(int16 version){
EQ2Packet* ret = 0;
Faction* faction = 0;
PacketStruct* packet = configReader.getStruct("WS_FactionUpdate", version);
MFactionUpdateNeeded.lock();
if(packet){
packet->setArrayLengthByName("num_factions", faction_update_needed.size());
for(int32 i=0;i<faction_update_needed.size();i++){
faction = master_faction_list.GetFaction(faction_update_needed[i]);
if(faction){
packet->setArrayDataByName("faction_id", faction->id, i);
packet->setArrayDataByName("name", faction->name.c_str(), i);
packet->setArrayDataByName("description", faction->description.c_str(), i);
packet->setArrayDataByName("category", faction->type.c_str(), i);
packet->setArrayDataByName("con", GetCon(faction->id), i);
packet->setArrayDataByName("percentage", GetPercent(faction->id), i);
packet->setArrayDataByName("value", GetFactionValue(faction->id), i);
}
}
ret = packet->serialize();
safe_delete(packet);
}
faction_update_needed.clear();
MFactionUpdateNeeded.unlock();
return ret;
}
sint32 PlayerFaction::GetFactionValue(int32 faction_id){
if(faction_id <= 10)
return 0;
//devn00b: This always seems to return 1, even if the player infact has no faction. since we handle this via a check in zoneserver.cpp (processfaction)
//if(faction_values.count(faction_id) == 0)
//return master_faction_list.GetDefaultFactionValue(faction_id); //faction_values[faction_id] = master_faction_list.GetDefaultFactionValue(faction_id);
return faction_values[faction_id];
}
bool PlayerFaction::ShouldIncrease(int32 faction_id){
if(faction_id <= 10 || master_faction_list.GetIncreaseAmount(faction_id) == 0)
return false;
return true;
}
bool PlayerFaction::ShouldDecrease(int32 faction_id){
if(faction_id <= 10 || master_faction_list.GetDecreaseAmount(faction_id) == 0)
return false;
return true;
}
bool PlayerFaction::IncreaseFaction(int32 faction_id, int32 amount){
if(faction_id <= 10)
return true;
bool ret = true;
if(amount == 0)
amount = master_faction_list.GetIncreaseAmount(faction_id);
faction_values[faction_id] += amount;
if(faction_values[faction_id] >= 50000){
faction_values[faction_id] = 50000;
ret = false;
}
MFactionUpdateNeeded.lock();
faction_update_needed.push_back(faction_id);
MFactionUpdateNeeded.unlock();
return ret;
}
bool PlayerFaction::DecreaseFaction(int32 faction_id, int32 amount){
if(faction_id <= 10)
return true;
bool ret = true;
if(amount == 0)
amount = master_faction_list.GetDecreaseAmount(faction_id);
if(amount != 0){
faction_values[faction_id] -= amount;
if(faction_values[faction_id] <= -50000){
faction_values[faction_id] = -50000;
ret = false;
}
}
else
ret = false;
MFactionUpdateNeeded.lock();
faction_update_needed.push_back(faction_id);
MFactionUpdateNeeded.unlock();
return ret;
}
bool PlayerFaction::SetFactionValue(int32 faction_id, sint32 value){
faction_values[faction_id] = value;
MFactionUpdateNeeded.lock();
faction_update_needed.push_back(faction_id);
MFactionUpdateNeeded.unlock();
return true;
}

139
old/world/Factions.h Normal file
View File

@ -0,0 +1,139 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EQ2_FACTIONS
#define EQ2_FACTIONS
#include "../common/ConfigReader.h"
#include "../common/Mutex.h"
struct Faction {
int32 id;
string name;
string type;
string description;
int16 negative_change;
int16 positive_change;
sint32 default_value;
};
class MasterFactionList{
public:
MasterFactionList(){
}
~MasterFactionList(){
Clear();
}
void Clear() {
map<int32,Faction*>::iterator iter;
for(iter = global_faction_list.begin();iter != global_faction_list.end(); iter++){
safe_delete(iter->second);
}
hostile_factions.clear();
friendly_factions.clear();
}
sint32 GetDefaultFactionValue(int32 faction_id){
if(global_faction_list.count(faction_id) > 0 && global_faction_list[faction_id])
return global_faction_list[faction_id]->default_value;
return 0;
}
Faction* GetFaction(char* name){
return faction_name_list[name];
}
Faction* GetFaction(int32 id){
if(global_faction_list.count(id) > 0)
return global_faction_list[id];
return 0;
}
void AddFaction(Faction* faction){
global_faction_list[faction->id] = faction;
faction_name_list[faction->name] = faction;
}
sint32 GetIncreaseAmount(int32 faction_id){
if(global_faction_list.count(faction_id) > 0 && global_faction_list[faction_id])
return global_faction_list[faction_id]->positive_change;
return 0;
}
sint32 GetDecreaseAmount(int32 faction_id){
if(global_faction_list.count(faction_id) > 0 && global_faction_list[faction_id])
return global_faction_list[faction_id]->negative_change;
return 0;
}
int32 GetFactionCount(){
return global_faction_list.size();
}
void AddHostileFaction(int32 faction_id, int32 hostile_faction_id){
hostile_factions[faction_id].push_back(hostile_faction_id);
}
void AddFriendlyFaction(int32 faction_id, int32 friendly_faction_id){
friendly_factions[faction_id].push_back(friendly_faction_id);
}
vector<int32>* GetFriendlyFactions(int32 faction_id){
if(friendly_factions.count(faction_id) > 0)
return &friendly_factions[faction_id];
else
return 0;
}
vector<int32>* GetHostileFactions(int32 faction_id){
if(hostile_factions.count(faction_id) > 0)
return &hostile_factions[faction_id];
else
return 0;
}
const char* GetFactionNameByID(int32 faction_id) {
if (faction_id > 0 && global_faction_list.count(faction_id) > 0)
return global_faction_list[faction_id]->name.c_str();
return 0;
}
private:
map<int32, vector<int32> > friendly_factions;
map<int32, vector<int32> > hostile_factions;
map<int32,Faction*> global_faction_list;
map<string,Faction*> faction_name_list;
};
class PlayerFaction{
public:
PlayerFaction();
sint32 GetMaxValue(sint8 con);
sint32 GetMinValue(sint8 con);
EQ2Packet* FactionUpdate(int16 version);
sint32 GetFactionValue(int32 faction_id);
bool ShouldIncrease(int32 faction_id);
bool ShouldDecrease(int32 faction_id);
bool IncreaseFaction(int32 faction_id, int32 amount = 0);
bool DecreaseFaction(int32 faction_id, int32 amount = 0);
bool SetFactionValue(int32 faction_id, sint32 value);
sint8 GetCon(int32 faction_id);
int8 GetPercent(int32 faction_id);
map<int32, sint32>* GetFactionValues(){
return &faction_values;
}
bool ShouldAttack(int32 faction_id);
private:
Mutex MFactionUpdateNeeded;
vector<int32> faction_update_needed;
map<int32, sint32> faction_values;
map<int32, int8> faction_percent;
};
#endif

582
old/world/GroundSpawn.cpp Normal file
View File

@ -0,0 +1,582 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GroundSpawn.h"
#include "World.h"
#include "Spells.h"
#include "Rules/Rules.h"
#include "../common/MiscFunctions.h"
#include "../common/Log.h"
extern ConfigReader configReader;
extern MasterSpellList master_spell_list;
extern World world;
extern RuleManager rule_manager;
GroundSpawn::GroundSpawn(){
packet_num = 0;
appearance.difficulty = 0;
spawn_type = 2;
appearance.pos.state = 129;
number_harvests = 0;
num_attempts_per_harvest = 0;
groundspawn_id = 0;
MHarvest.SetName("GroundSpawn::MHarvest");
MHarvestUse.SetName("GroundSpawn::MHarvestUse");
randomize_heading = true; // we by default randomize heading of groundspawns DB overrides
}
GroundSpawn::~GroundSpawn(){
}
EQ2Packet* GroundSpawn::serialize(Player* player, int16 version){
return spawn_serialize(player, version);
}
int8 GroundSpawn::GetNumberHarvests(){
return number_harvests;
}
void GroundSpawn::SetNumberHarvests(int8 val){
number_harvests = val;
}
int8 GroundSpawn::GetAttemptsPerHarvest(){
return num_attempts_per_harvest;
}
void GroundSpawn::SetAttemptsPerHarvest(int8 val){
num_attempts_per_harvest = val;
}
int32 GroundSpawn::GetGroundSpawnEntryID(){
return groundspawn_id;
}
void GroundSpawn::SetGroundSpawnEntryID(int32 val){
groundspawn_id = val;
}
void GroundSpawn::SetCollectionSkill(const char* val){
if(val)
collection_skill = string(val);
}
const char* GroundSpawn::GetCollectionSkill(){
return collection_skill.c_str();
}
void GroundSpawn::ProcessHarvest(Client* client) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Process harvesting for player '%s' (%u)", client->GetPlayer()->GetName(), client->GetPlayer()->GetID());
MHarvest.lock();
vector<GroundSpawnEntry*>* groundspawn_entries = GetZone()->GetGroundSpawnEntries(groundspawn_id);
vector<GroundSpawnEntryItem*>* groundspawn_items = GetZone()->GetGroundSpawnEntryItems(groundspawn_id);
Item* master_item = 0;
Item* master_rare = 0;
Item* item = 0;
Item* item_rare = 0;
int16 lowest_skill_level = 0;
int16 table_choice = 0;
int32 item_choice = 0;
int32 rare_choice = 0;
int8 harvest_type = 0;
int32 item_harvested = 0;
int8 reward_total = 1;
int32 rare_harvested = 0;
int8 rare_item = 0;
bool is_collection = false;
if (!groundspawn_entries || !groundspawn_items) {
LogWrite(GROUNDSPAWN__ERROR, 3, "GSpawn", "No groundspawn entries or items assigned to groundspawn id: %u", groundspawn_id);
client->Message(CHANNEL_COLOR_RED, "Error: There are no groundspawn entries or items assigned to groundspawn id: %u", groundspawn_id);
MHarvest.unlock();
return;
}
if (number_harvests == 0) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Total harvests depleated for groundspawn id: %u", groundspawn_id);
client->SimpleMessage(CHANNEL_COLOR_RED, "Error: This spawn has nothing more to harvest!");
MHarvest.unlock();
return;
}
Skill* skill = 0;
if (collection_skill == "Collecting") {
skill = client->GetPlayer()->GetSkillByName("Gathering");
is_collection = true;
}
else
skill = client->GetPlayer()->GetSkillByName(collection_skill.c_str()); // Fix: #576 - don't skill up yet with GetSkillByName(skill, true), we might be trying to harvest low level
if (!skill) {
LogWrite(GROUNDSPAWN__WARNING, 3, "GSpawn", "Player '%s' lacks the skill: '%s'", client->GetPlayer()->GetName(), collection_skill.c_str());
client->Message(CHANNEL_COLOR_RED, "Error: You do not have the '%s' skill!", collection_skill.c_str());
MHarvest.unlock();
return;
}
int16 totalSkill = skill->current_val;
int32 skillID = master_item_list.GetItemStatIDByName(collection_skill);
int16 max_skill_req_groundspawn = rule_manager.GetZoneRule(client->GetCurrentZoneID(), R_Player, MinSkillMultiplierValue)->GetInt16();
if(max_skill_req_groundspawn < 1) // can't be 0
max_skill_req_groundspawn = 1;
if(skillID != 0xFFFFFFFF)
{
((Entity*)client->GetPlayer())->MStats.lock();
totalSkill += ((Entity*)client->GetPlayer())->stats[skillID];
((Entity*)client->GetPlayer())->MStats.unlock();
}
for (int8 i = 0; i < num_attempts_per_harvest; i++) {
vector<GroundSpawnEntry*> mod_groundspawn_entries;
if (groundspawn_entries) {
vector<GroundSpawnEntry*> highest_match;
vector<GroundSpawnEntry*>::iterator itr;
GroundSpawnEntry* entry = 0; // current data
GroundSpawnEntry* selected_table = 0; // selected table data
// first, iterate through groundspawn_entries, discard tables player cannot use
for (itr = groundspawn_entries->begin(); itr != groundspawn_entries->end(); itr++) {
entry = *itr;
if(entry->min_skill_level > max_skill_req_groundspawn)
max_skill_req_groundspawn = entry->min_skill_level;
// if player lacks skill, skip table
if (entry->min_skill_level > totalSkill)
continue;
// if bonus, but player lacks level, skip table
if (entry->bonus_table && (client->GetPlayer()->GetLevel() < entry->min_adventure_level))
continue;
// build modified entries table
mod_groundspawn_entries.push_back(entry);
LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "Keeping groundspawn_entry: %i", entry->min_skill_level);
}
// if anything remains, find lowest min_skill_level in remaining set(s)
if (mod_groundspawn_entries.size() > 0) {
vector<GroundSpawnEntry*>::iterator itr;
GroundSpawnEntry* entry = 0;
for (itr = mod_groundspawn_entries.begin(); itr != mod_groundspawn_entries.end(); itr++) {
entry = *itr;
// find the low range of available tables for random roll
if (lowest_skill_level > entry->min_skill_level || lowest_skill_level == 0)
lowest_skill_level = entry->min_skill_level;
}
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Lowest Skill Level: %i", lowest_skill_level);
}
else {
// if no tables chosen, you must lack the skills
// TODO: move this check to LUA when harvest command is first selected
client->Message(CHANNEL_COLOR_RED, "You lack the skills to harvest this node!");
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "All groundspawn_entry tables tossed! No Skills? Something broke?");
MHarvest.unlock();
return;
}
// now roll to see which table to use
table_choice = MakeRandomInt(lowest_skill_level, totalSkill);
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Random INT for Table by skill level: %i", table_choice);
int16 highest_score = 0;
for (itr = mod_groundspawn_entries.begin(); itr != mod_groundspawn_entries.end(); itr++) {
entry = *itr;
// determines the highest min_skill_level in the current set of tables (if multiple tables)
if (table_choice >= entry->min_skill_level && (highest_score == 0 || highest_score < table_choice)) {
// removes old highest for the new one
highest_match.clear();
highest_score = entry->min_skill_level;
}
// if the score = level, push into highest_match set
if (highest_score == entry->min_skill_level)
highest_match.push_back(entry);
}
// if there is STILL more than 1 table player qualifies for, rand() and pick one
if (highest_match.size() > 1) {
int16 rand_index = rand() % highest_match.size();
selected_table = highest_match.at(rand_index);
}
else if (highest_match.size() > 0)
selected_table = highest_match.at(0);
// by this point, we should have 1 table who's min skill matches the score (selected_table)
if (selected_table) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Using Table: %i, %i, %i, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %i",
selected_table->min_skill_level,
selected_table->min_adventure_level,
selected_table->bonus_table,
selected_table->harvest1,
selected_table->harvest3,
selected_table->harvest5,
selected_table->harvest_imbue,
selected_table->harvest_rare,
selected_table->harvest10,
selected_table->harvest_coin);
// roll 1-100 for chance-to-harvest percentage
float chance = MakeRandomFloat(0, 100);
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Random FLOAT for harvest percentages: %.2f", chance);
// starting with the lowest %, select a harvest type + reward qty
if (chance <= selected_table->harvest10 && is_collection == false) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest 10 items + Rare Item from table : %i", selected_table->min_skill_level);
harvest_type = 6;
reward_total = 10;
}
else if (chance <= selected_table->harvest_rare && is_collection == false) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest Rare Item from table : %i", selected_table->min_skill_level);
harvest_type = 5;
}
else if (chance <= selected_table->harvest_imbue && is_collection == false) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest Imbue Item from table : %i", selected_table->min_skill_level);
harvest_type = 4;
}
else if (chance <= selected_table->harvest5 && is_collection == false) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest 5 Items from table : %i", selected_table->min_skill_level);
harvest_type = 3;
reward_total = 5;
}
else if (chance <= selected_table->harvest3 && is_collection == false) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest 3 Items from table : %i", selected_table->min_skill_level);
harvest_type = 2;
reward_total = 3;
}
else if (chance <= selected_table->harvest1 || totalSkill >= skill->max_val || is_collection) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest 1 Item from table : %i", selected_table->min_skill_level);
harvest_type = 1;
}
else
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Harvest nothing...");
float node_maxskill_multiplier = rule_manager.GetZoneRule(client->GetCurrentZoneID(), R_Player, HarvestSkillUpMultiplier)->GetFloat();
if(node_maxskill_multiplier <= 0.0f) {
node_maxskill_multiplier = 1.0f;
}
int16 skillup_max_skill_allowed = (int16)((float)max_skill_req_groundspawn*node_maxskill_multiplier);
if (!is_collection && skill && skill->current_val < skillup_max_skill_allowed) {
skill = client->GetPlayer()->GetSkillByName(collection_skill.c_str(), true); // Fix: #576 - skill up after min skill and adv level checks
}
}
// once you know how many and what type of item to harvest, pick an item from the list
if (harvest_type) {
vector<GroundSpawnEntryItem*> mod_groundspawn_items;
vector<GroundSpawnEntryItem*> mod_groundspawn_rares;
vector<GroundSpawnEntryItem*> mod_groundspawn_imbue;
vector<GroundSpawnEntryItem*>::iterator itr;
GroundSpawnEntryItem* entry = 0;
// iterate through groundspawn_items, discard items player cannot roll for
for (itr = groundspawn_items->begin(); itr != groundspawn_items->end(); itr++) {
entry = *itr;
// if this is a Rare, or an Imbue, but is_rare flag is 0, skip item
if ((harvest_type == 5 || harvest_type == 4) && entry->is_rare == 0)
continue;
// if it is a 1, 3, or 5 and is_rare = 1, skip
else if (harvest_type < 4 && entry->is_rare == 1)
continue;
// if the grid_id on the item matches player grid, or is 0, keep the item
if (!entry->grid_id || (entry->grid_id == client->GetPlayer()->GetLocation())) {
// build modified entries table
if ((entry->is_rare == 1 && harvest_type == 5) || (entry->is_rare == 1 && harvest_type == 6)) {
// if the matching item is rare, or harvest10 push to mod rares
mod_groundspawn_rares.push_back(entry);
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Keeping groundspawn_rare_item: %u", entry->item_id);
}
if (entry->is_rare == 0 && harvest_type != 4 && harvest_type != 5) {
// if the matching item is normal,or harvest 10 push to mod items
mod_groundspawn_items.push_back(entry);
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Keeping groundspawn_common_item: %u", entry->item_id);
}
if (entry->is_rare == 2 && harvest_type == 4) {
// if the matching item is imbue item, push to mod imbue
mod_groundspawn_imbue.push_back(entry);
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Keeping groundspawn_imbue_item: %u", entry->item_id);
}
}
}
// if any items remain in the list, random to see which one gets awarded
if (mod_groundspawn_items.size() > 0) {
// roll to see which item index to use
item_choice = rand() % mod_groundspawn_items.size();
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Random INT for which item to award: %i", item_choice);
// set item_id to be awarded
item_harvested = mod_groundspawn_items[item_choice]->item_id;
// if reward is rare, set flag
rare_item = mod_groundspawn_items[item_choice]->is_rare;
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID to award: %u, Rare = %i", item_harvested, item_rare);
// if 10+rare, handle additional "rare" reward
if (harvest_type == 6) {
// make sure there is a rare table to choose from!
if (mod_groundspawn_rares.size() > 0) {
// roll to see which rare index to use
rare_choice = rand() % mod_groundspawn_rares.size();
// set (rare) item_id to be awarded
rare_harvested = mod_groundspawn_rares[rare_choice]->item_id;
// we're picking a rare here, so obviously this is true ;)
rare_item = 1;
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "RARE Item ID to award: %u", rare_harvested);
}
else {
// all rare entries were eliminated above, or none are assigned. Either way, shouldn't be here!
LogWrite(GROUNDSPAWN__ERROR, 3, "GSpawn", "Groundspawn Entry for '%s' (%i) has no RARE items!", GetName(), GetID());
}
}
}
else if (mod_groundspawn_rares.size() > 0) {
// roll to see which rare index to use
item_choice = rand() % mod_groundspawn_rares.size();
// set (rare) item_id to be awarded
item_harvested = mod_groundspawn_rares[item_choice]->item_id;
// we're picking a rare here, so obviously this is true ;)
rare_item = 1;
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "RARE Item ID to award: %u", rare_harvested);
}
else if (mod_groundspawn_imbue.size() > 0) {
// roll to see which rare index to use
item_choice = rand() % mod_groundspawn_imbue.size();
// set (rare) item_id to be awarded
item_harvested = mod_groundspawn_imbue[item_choice]->item_id;
// we're picking a rare here, so obviously this is true ;)
rare_item = 0;
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "imbue Item ID to award: %u", rare_harvested);
}
else {
// all item entries were eliminated above, or none are assigned. Either way, shouldn't be here!
LogWrite(GROUNDSPAWN__ERROR, 0, "GSpawn", "Groundspawn Entry for '%s' (%i) has no items!", GetName(), GetID());
}
// if an item was harvested, send updates to client, add item to inventory
if (item_harvested) {
char tmp[200] = { 0 };
// set Normal item harvested
master_item = master_item_list.GetItem(item_harvested);
if (master_item) {
// set details of Normal item
item = new Item(master_item);
// set how many of this item the player receives
item->details.count = reward_total;
// chat box update for normal item (todo: verify output text)
client->Message(CHANNEL_HARVESTING, "You %s %i %s from the %s.", GetHarvestMessageName(true).c_str(), item->details.count, item->CreateItemLink(client->GetVersion(), true).c_str(), GetName());
// add Normal item to player inventory
bool itemDeleted = false;
client->AddItem(item, &itemDeleted);
if(!itemDeleted) {
//Check if the player has a harvesting quest for this
client->GetPlayer()->CheckQuestsHarvestUpdate(item, reward_total);
// if this is a 10+rare, handle sepErately
if (harvest_type == 6 && rare_item == 1) {
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is Normal. Qty %i", item_harvested, item->details.count);
// send Normal harvest message to client
sprintf(tmp, "\\#64FFFFYou have %s:\12\\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
client->SendPopupMessage(10, tmp, "ui_harvested_normal", 2.25, 0xFF, 0xFF, 0xFF);
client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_ITEMS_HARVESTED, item->details.count);
// set Rare item harvested
master_rare = master_item_list.GetItem(rare_harvested);
if (master_rare) {
// set details of Rare item
item_rare = new Item(master_rare);
// count of Rare is always 1
item_rare->details.count = 1;
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is RARE!", rare_harvested);
// send Rare harvest message to client
sprintf(tmp, "\\#FFFF6ERare item found!\12%s: \\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item_rare->details.count, item_rare->name.c_str());
client->Message(CHANNEL_HARVESTING, "You have found a rare item!");
client->SendPopupMessage(11, tmp, "ui_harvested_rare", 2.25, 0xFF, 0xFF, 0xFF);
client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_RARES_HARVESTED, item_rare->details.count);
// chat box update for rare item (todo: verify output text)
client->Message(CHANNEL_HARVESTING, "You %s %i %s from the %s.", GetHarvestMessageName(true).c_str(), item_rare->details.count, item->CreateItemLink(client->GetVersion(), true).c_str(), GetName());
// add Rare item to player inventory
client->AddItem(item_rare);
//Check if the player has a harvesting quest for this
client->GetPlayer()->CheckQuestsHarvestUpdate(item_rare, 1);
}
}
else if (rare_item == 1) {
// if harvest signaled rare or imbue type
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is RARE! Qty: %i", item_harvested, item->details.count);
// send Rare harvest message to client
sprintf(tmp, "\\#FFFF6ERare item found!\12%s: \\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
client->Message(CHANNEL_HARVESTING, "You have found a rare item!");
client->SendPopupMessage(11, tmp, "ui_harvested_rare", 2.25, 0xFF, 0xFF, 0xFF);
client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_RARES_HARVESTED, item->details.count);
}
else {
// send Normal harvest message to client
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "Item ID %u is Normal. Qty %i", item_harvested, item->details.count);
sprintf(tmp, "\\#64FFFFYou have %s:\12\\#C8FFFF%i %s", GetHarvestMessageName().c_str(), item->details.count, item->name.c_str());
client->SendPopupMessage(10, tmp, "ui_harvested_normal", 2.25, 0xFF, 0xFF, 0xFF);
client->GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_ITEMS_HARVESTED, item->details.count);
}
}
}
else {
// error!
LogWrite(GROUNDSPAWN__ERROR, 0, "GSpawn", "Error: Item ID Not Found - %u", item_harvested);
client->Message(CHANNEL_COLOR_RED, "Error: Unable to find item id %u", item_harvested);
}
// decrement # of pulls on this node before it despawns
number_harvests--;
}
else {
// if no item harvested
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "No item_harvested");
client->Message(CHANNEL_HARVESTING, "You failed to %s anything from %s.", GetHarvestMessageName(true, true).c_str(), GetName());
}
}
else {
// if no harvest type
LogWrite(GROUNDSPAWN__DEBUG, 3, "GSpawn", "No harvest_type");
client->Message(CHANNEL_HARVESTING, "You failed to %s anything from %s.", GetHarvestMessageName(true, true).c_str(), GetName());
}
}
} // cycle through num_attempts_per_harvest
MHarvest.unlock();
LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "Process harvest complete for player '%s' (%u)", client->GetPlayer()->GetName(), client->GetPlayer()->GetID());
}
string GroundSpawn::GetHarvestMessageName(bool present_tense, bool failure){
string ret = "";
if((collection_skill == "Gathering" ||collection_skill == "Collecting") && !present_tense)
ret = "gathered";
else if(collection_skill == "Gathering" || collection_skill == "Collecting")
ret = "gather";
else if(collection_skill == "Mining" && !present_tense)
ret = "mined";
else if(collection_skill == "Mining")
ret = "mine";
else if (collection_skill == "Fishing" && !present_tense)
ret = "fished";
else if(collection_skill == "Fishing")
ret = "fish";
else if(collection_skill == "Trapping" && !present_tense && !failure)
ret = "acquired";
else if(collection_skill == "Trapping" && failure)
ret = "trap";
else if(collection_skill == "Trapping")
ret = "acquire";
else if(collection_skill == "Foresting" && !present_tense)
ret = "forested";
else if(collection_skill == "Foresting")
ret = "forest";
else if (collection_skill == "Collecting")
ret = "collect";
return ret;
}
string GroundSpawn::GetHarvestSpellType(){
string ret = "";
if(collection_skill == "Gathering" || collection_skill == "Collecting")
ret = "gather";
else if(collection_skill == "Mining")
ret = "mine";
else if(collection_skill == "Trapping")
ret = "trap";
else if(collection_skill == "Foresting")
ret = "chop";
else if(collection_skill == "Fishing")
ret = "fish";
return ret;
}
string GroundSpawn::GetHarvestSpellName() {
string ret = "";
if (collection_skill == "Collecting")
ret = "Gathering";
else
ret = collection_skill;
return ret;
}
void GroundSpawn::HandleUse(Client* client, string type){
if(!client || (client->GetVersion() > 561 && type.length() == 0)) // older clients do not send the type
return;
//The following check disables the use of the groundspawn if spawn access is not granted
if (client) {
bool meets_quest_reqs = MeetsSpawnAccessRequirements(client->GetPlayer());
if (!meets_quest_reqs && (GetQuestsRequiredOverride() & 2) == 0)
return;
else if (meets_quest_reqs && appearance.show_command_icon != 1)
return;
}
MHarvestUse.lock();
std::string typeLwr = ToLower(type);
if(client->GetVersion() <= 561 && (typeLwr == "" || typeLwr == "collect" || typeLwr == "gather" || typeLwr == "chop" || typeLwr == "mine"))
type = GetHarvestSpellType();
if (type == GetHarvestSpellType() && MeetsSpawnAccessRequirements(client->GetPlayer())) {
Spell* spell = master_spell_list.GetSpellByName(GetHarvestSpellName().c_str());
if (spell)
client->GetCurrentZone()->ProcessSpell(spell, client->GetPlayer(), client->GetPlayer()->GetTarget(), true, true);
}
else if (appearance.show_command_icon == 1 && MeetsSpawnAccessRequirements(client->GetPlayer())) {
EntityCommand* entity_command = FindEntityCommand(type);
if (entity_command)
client->GetCurrentZone()->ProcessEntityCommand(entity_command, client->GetPlayer(), client->GetPlayer()->GetTarget());
}
MHarvestUse.unlock();
}

86
old/world/GroundSpawn.h Normal file
View File

@ -0,0 +1,86 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EQ2_GroundSpawn__
#define __EQ2_GroundSpawn__
#include "Spawn.h"
#include "client.h"
#include "../common/Mutex.h"
class GroundSpawn : public Spawn {
public:
GroundSpawn();
virtual ~GroundSpawn();
GroundSpawn* Copy(){
GroundSpawn* new_spawn = new GroundSpawn();
new_spawn->size = size;
new_spawn->SetPrimaryCommands(&primary_command_list);
new_spawn->SetSecondaryCommands(&secondary_command_list);
new_spawn->database_id = database_id;
new_spawn->primary_command_list_id = primary_command_list_id;
new_spawn->secondary_command_list_id = secondary_command_list_id;
memcpy(&new_spawn->appearance, &appearance, sizeof(AppearanceData));
new_spawn->faction_id = faction_id;
new_spawn->target = 0;
new_spawn->SetTotalHP(GetTotalHP());
new_spawn->SetTotalPower(GetTotalPower());
new_spawn->SetHP(GetHP());
new_spawn->SetPower(GetPower());
new_spawn->SetNumberHarvests(number_harvests);
new_spawn->SetAttemptsPerHarvest(num_attempts_per_harvest);
new_spawn->SetGroundSpawnEntryID(groundspawn_id);
new_spawn->SetCollectionSkill(collection_skill.c_str());
SetQuestsRequired(new_spawn);
new_spawn->forceMapCheck = forceMapCheck;
new_spawn->SetOmittedByDBFlag(IsOmittedByDBFlag());
new_spawn->SetLootTier(GetLootTier());
new_spawn->SetLootDropType(GetLootDropType());
new_spawn->SetRandomizeHeading(GetRandomizeHeading());
return new_spawn;
}
bool IsGroundSpawn(){ return true; }
EQ2Packet* serialize(Player* player, int16 version);
int8 GetNumberHarvests();
void SetNumberHarvests(int8 val);
int8 GetAttemptsPerHarvest();
void SetAttemptsPerHarvest(int8 val);
int32 GetGroundSpawnEntryID();
void SetGroundSpawnEntryID(int32 val);
void ProcessHarvest(Client* client);
void SetCollectionSkill(const char* val);
const char* GetCollectionSkill();
string GetHarvestMessageName(bool present_tense = false, bool failure = false);
string GetHarvestSpellType();
string GetHarvestSpellName();
void HandleUse(Client* client, string type);
void SetRandomizeHeading(bool val) { randomize_heading = val; }
bool GetRandomizeHeading() { return randomize_heading; }
private:
int8 number_harvests;
int8 num_attempts_per_harvest;
int32 groundspawn_id;
string collection_skill;
Mutex MHarvest;
Mutex MHarvestUse;
bool randomize_heading;
};
#endif

2462
old/world/Guilds/Guild.cpp Normal file

File diff suppressed because it is too large Load Diff

454
old/world/Guilds/Guild.h Normal file
View File

@ -0,0 +1,454 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GUILD_H_
#define GUILD_H_
#include <vector>
#include <deque>
#include <map>
#include "../../common/Mutex.h"
#include "../MutexMap.h"
using namespace std;
class ZoneServer;
class Client;
class Player;
#define GUILD_RANK_LEADER 0
#define GUILD_RANK_SENIOR_OFFICER 1
#define GUILD_RANK_OFFICER 2
#define GUILD_RANK_SENIOR_MEMBER 3
#define GUILD_RANK_MEMBER 4
#define GUILD_RANK_JUNIOR_MEMBER 5
#define GUILD_RANK_INITIATE 6
#define GUILD_RANK_RECRUIT 7
#define GUILD_PERMISSIONS_INVITE 0
#define GUILD_PERMISSIONS_RMEOVE_MEMBER 1
#define GUILD_PERMISSIONS_PROMOTE_MEMBER 2
#define GUILD_PERMISSIONS_DEMOTE_MEMBER 3
#define GUILD_PERMISSIONS_CHANGE_MOTD 6
#define GUILD_PERMISSIONS_CHANGE_PERMISSIONS 7
#define GUILD_PERMISSIONS_CHANGE_RANK_NAMES 8
#define GUILD_PERMISSIONS_SEE_OFFICER_NOTES 9
#define GUILD_PERMISSIONS_EDIT_OFFICER_NOTES 10
#define GUILD_PERMISSIONS_SEE_OFFICER_CHAT 11
#define GUILD_PERMISSIONS_SPEAK_IN_OFFICER_CHAT 12
#define GUILD_PERMISSIONS_SEE_GUILD_CHAT 13
#define GUILD_PERMISSIONS_SPEAK_IN_GUILD_CHAT 14
#define GUILD_PERMISSIONS_EDIT_PERSONAL_NOTES 15
#define GUILD_PERMISSIONS_EDIT_PERSONAL_NOTES_OTHERS 16
#define GUILD_PERMISSIONS_EDIT_EVENT_FILTERS 17
#define GUILD_PERMISSIONS_EDIT_EVENTS 18
#define GUILD_PERMISSIONS_PURCHASE_STATUS_ITEMS 19
#define GUILD_PERMISSIONS_DISPLAY_GUILD_NAME 20
#define GUILD_PERMISSIONS_SEND_EMAIL_TO_GUILD 21
#define GUILD_PERMISSIONS_BANK1_SEE_CONTENTS 22
#define GUILD_PERMISSIONS_BANK2_SEE_CONTENTS 23
#define GUILD_PERMISSIONS_BANK3_SEE_CONTENTS 24
#define GUILD_PERMISSIONS_BANK4_SEE_CONTENTS 25
#define GUILD_PERMISSIONS_BANK1_DEPOSIT 26
#define GUILD_PERMISSIONS_BANK2_DEPOSIT 27
#define GUILD_PERMISSIONS_BANK3_DEPOSIT 28
#define GUILD_PERMISSIONS_BANK4_DEPOSIT 29
#define GUILD_PERMISSIONS_BANK1_WITHDRAWL 30
#define GUILD_PERMISSIONS_BANK2_WITHDRAWL 31
#define GUILD_PERMISSIONS_BANK3_WITHDRAWL 32
#define GUILD_PERMISSIONS_BANK4_WITHDRAWL 33
#define GUILD_PERMISSIONS_EDIT_RECRUITING_SETTINGS 35
#define GUILD_PERMISSIONS_MAKE_OTHERS_RECRUITERS 36
#define GUILD_PERMISSIONS_SEE_RECRUITING_SETTINGS 37
#define GUILD_PERMISSIONS_ASSIGN_POINTS 43
#define GUILD_PERMISSIONS_RECEIVE_POINTS 44
#define GUILD_EVENT_FILTER_CATEGORY_RETAIN_HISTORY 0
#define GUILD_EVENT_FILTER_CATEGORY_BROADCAST 1
#define GUILD_EVENT_GUILD_LEVEL_UP 0
#define GUILD_EVENT_GUILD_LEVEL_DOWN 1
#define GUILD_EVENT_DISCOVERS_ITEM 2
#define GUILD_EVENT_GAINS_ADV_LEVEL_1_10 3
#define GUILD_EVENT_GAINS_ADV_LEVEL_11_20 4
#define GUILD_EVENT_GAINS_ADV_LEVEL_21_30 5
#define GUILD_EVENT_GAINS_ADV_LEVEL_31_40 6
#define GUILD_EVENT_GAINS_ADV_LEVEL_41_50 7
#define GUILD_EVENT_GAINS_TS_LEVEL_1_10 8
#define GUILD_EVENT_GAINS_TS_LEVEL_11_20 9
#define GUILD_EVENT_GAINS_TS_LEVEL_21_30 10
#define GUILD_EVENT_GAINS_TS_LEVEL_31_40 11
#define GUILD_EVENT_GAINS_TS_LEVEL_41_50 12
#define GUILD_EVENT_MEMBER_JOINS 13
#define GUILD_EVENT_MEMBER_LEAVES 14
#define GUILD_EVENT_MEMBER_PROMOTED 15
#define GUILD_EVENT_MEMBER_DEMOTED 16
#define GUILD_EVENT_COMPLETES_HERITAGE_QUEST 19
#define GUILD_EVENT_KILLS_EPIC_MONSTER 20
#define GUILD_EVENT_LOOTS_ARTIFACT 21
#define GUILD_EVENT_LOOTS_FABELED_ITEM 22
#define GUILD_EVENT_LOOTS_LEGENDARY_ITEM 23
#define GUILD_EVENT_COMPLETES_WRIT 24
#define GUILD_EVENT_LOOTS_MYTHICAL_ITEM 25
#define GUILD_EVENT_GAINS_ADV_LEVEL_10 26
#define GUILD_EVENT_GAINS_ADV_LEVEL_20 27
#define GUILD_EVENT_GAINS_ADV_LEVEL_30 28
#define GUILD_EVENT_GAINS_ADV_LEVEL_40 29
#define GUILD_EVENT_GAINS_ADV_LEVEL_50 30
#define GUILD_EVENT_GAINS_TS_LEVEL_10 31
#define GUILD_EVENT_GAINS_TS_LEVEL_20 32
#define GUILD_EVENT_GAINS_TS_LEVEL_30 33
#define GUILD_EVENT_GAINS_TS_LEVEL_40 34
#define GUILD_EVENT_GAINS_TS_LEVEL_50 35
#define GUILD_EVENT_GAINS_ADV_LEVEL_51_60 37
#define GUILD_EVENT_GAINS_TS_LEVEL_51_60 38
#define GUILD_EVENT_GAINS_ADV_LEVEL_60 39
#define GUILD_EVENT_GAINS_TS_LEVEL_60 40
#define GUILD_EVENT_GAINS_ADV_LEVEL_61_70 41
#define GUILD_EVENT_GAINS_TS_LEVEL_61_70 42
#define GUILD_EVENT_GAINS_ADV_LEVEL_70 43
#define GUILD_EVENT_GAINS_TS_LEVEL_70 44
#define GUILD_EVENT_GAINS_AA_10 45
#define GUILD_EVENT_GAINS_AA_20 46
#define GUILD_EVENT_GAINS_AA_30 47
#define GUILD_EVENT_GAINS_AA_40 48
#define GUILD_EVENT_GAINS_AA_50 49
#define GUILD_EVENT_GAINS_AA_1_10 50
#define GUILD_EVENT_GAINS_AA_11_20 51
#define GUILD_EVENT_GAINS_AA_21_30 52
#define GUILD_EVENT_GAINS_AA_31_40 53
#define GUILD_EVENT_GAINS_AA_41_50 54
#define GUILD_EVENT_BECOMES_RECRUITER 55
#define GUILD_EVENT_NO_LONGER_RECRUITER 56
#define GUILD_EVENT_HERALDY_CHANGE 57
#define GUILD_EVENT_GAINS_AA_60 58
#define GUILD_EVENT_GAINS_AA_70 59
#define GUILD_EVENT_GAINS_AA_80 60
#define GUILD_EVENT_GAINS_AA_90 61
#define GUILD_EVENT_GAINS_AA_100 62
#define GUILD_EVENT_GAINS_AA_51_60 63
#define GUILD_EVENT_GAINS_AA_61_70 64
#define GUILD_EVENT_GAINS_AA_71_80 65
#define GUILD_EVENT_GAINS_AA_81_90 66
#define GUILD_EVENT_GAINS_AA_91_100 67
#define GUILD_EVENT_GAINS_ADV_LEVEL_80 68
#define GUILD_EVENT_GAINS_TS_LEVEL_80 69
#define GUILD_EVENT_GAINS_ADV_LEVEL_71_80 70
#define GUILD_EVENT_GAINS_TS_LEVEL_71_80 71
#define GUILD_EVENT_GAINS_AA_110 72
#define GUILD_EVENT_GAINS_AA_120 73
#define GUILD_EVENT_GAINS_AA_130 74
#define GUILD_EVENT_GAINS_AA_140 75
#define GUILD_EVENT_GAINS_AA_101_110 76
#define GUILD_EVENT_GAINS_AA_111_120 77
#define GUILD_EVENT_GAINS_AA_121_130 78
#define GUILD_EVENT_GAINS_AA_131_140 79
#define GUILD_EVENT_GAINS_AA_150 80
#define GUILD_EVENT_GAINS_AA_141_150 81
#define GUILD_EVENT_GAINS_AA_160 82
#define GUILD_EVENT_GAINS_AA_170 83
#define GUILD_EVENT_GAINS_AA_180 84
#define GUILD_EVENT_GAINS_AA_190 85
#define GUILD_EVENT_GAINS_AA_200 86
#define GUILD_EVENT_GAINS_AA_151_160 87
#define GUILD_EVENT_GAINS_AA_161_170 88
#define GUILD_EVENT_GAINS_AA_171_180 89
#define GUILD_EVENT_GAINS_AA_181_190 90
#define GUILD_EVENT_GAINS_AA_191_200 91
#define GUILD_EVENT_EARNS_ACHIEVEMENT 92
#define GUILD_RECRUITING_FLAG_TRAINING 0
#define GUILD_RECRUITING_FLAG_FIGHTERS 1
#define GUILD_RECRUITING_FLAG_PRIESTS 2
#define GUILD_RECRUITING_FLAG_SCOUTS 3
#define GUILD_RECRUITING_FLAG_MAGES 4
#define GUILD_RECRUITING_FLAG_TRADESKILLERS 5
#define GUILD_RECRUITING_PLAYSTYLE_NONE 0
#define GUILD_RECRUITING_PLAYSTYLE_CASUAL 1
#define GUILD_RECRUITING_PLAYSTYLE_HARDCORE 2
#define GUILD_RECRUITING_DESC_TAG_NONE 0
#define GUILD_RECRUITING_DESC_TAG_GOOD 1
#define GUILD_RECRUITING_DESC_TAG_EVIL 2
#define GUILD_RECRUITING_DESC_TAG_CHATTY 3
#define GUILD_RECRUITING_DESC_TAG_ORGANIZED 4
#define GUILD_RECRUITING_DESC_TAG_ROLEPLAY 5
#define GUILD_RECRUITING_DESC_TAG_ENJOY_QUESTS 6
#define GUILD_RECRUITING_DESC_TAG_ENJOY_RAIDS 7
#define GUILD_RECRUITING_DESC_TAG_ODD_HOURS 8
#define GUILD_RECRUITING_DESC_TAG_CRAFTER_ORIENTED 9
#define GUILD_RECRUITING_DESC_TAG_FAMILY_FRIENDLY 10
#define GUILD_RECRUITING_DESC_TAG_MATURE_HUMOR 11
#define GUILD_RECRUITING_DESC_TAG_INMATES_RUN 12
#define GUILD_RECRUITING_DESC_TAG_VERY_FUNNY 13
#define GUILD_RECRUITING_DESC_TAG_HUMOR_CAUES_PAIN 14
#define GUILD_RECRUITING_DESC_TAG_SERIOUS 15
#define GUILD_MEMBER_FLAGS_RECRUITING_FOR_GUILD 1
#define GUILD_MEMBER_FLAGS_NOTIFY_LOGINS 2
#define GUILD_MEMBER_FLAGS_DONT_GENERATE_EVENTS 4
#define GUILD_EVENT_ACTION_LOCK 0
#define GUILD_EVENT_ACTION_UNLOCK 1
#define GUILD_EVENT_ACTION_DELETE 2
#define GUILD_MAX_LEVEL 80
#define GUILD_MAX_POINT_HISTORY 50
#define GUILD_MAX_EVENTS 500
#define GUILD_MAX_LOCKED_EVENTS 200
struct PointHistory {
int32 date;
string modified_by;
string comment;
float points;
bool saved_needed;
};
struct GuildMember {
int32 character_id;
int32 account_id;
int32 recruiter_id; //00 00 00 00 if not a guild recruiter
char name[64];
int32 guild_status;
float points;
int8 adventure_class;
int8 adventure_level;
int8 tradeskill_class;
int8 tradeskill_level;
int8 rank;
int8 member_flags;
string zone;
int32 join_date;
int32 last_login_date;
string note;
string officer_note;
string recruiter_description;
unsigned char* recruiter_picture_data;
int16 recruiter_picture_data_size;
int8 recruiting_show_adventure_class;
deque<PointHistory*> point_history;
};
struct GuildEvent {
int64 event_id;
int32 date;
int32 type;
string description;
int8 locked;
bool save_needed;
};
struct GuildBankEvent {
int64 event_id;
int32 date;
int32 type;
string description;
};
struct Bank {
string name;
deque<GuildBankEvent*> events;
};
class Guild {
public:
Guild();
virtual ~Guild();
void SetID(int32 id_in) {id = id_in;}
void SetName(const char* name, bool send_packet = true);
void SetLevel(int8 level, bool send_packet = true);
void SetFormedDate(int32 formed_date_in) {formed_date = formed_date_in;}
void SetMOTD(const char *motd, bool send_packet = true);
int32 GetID() const {return id;}
const char* GetName() const {return name;}
int8 GetLevel() const {return level;}
int32 GetFormedDate() const {return formed_date;}
const char * GetMOTD() const {return motd;}
void SetEXPCurrent(int64 exp, bool send_packet = true);
void AddEXPCurrent(sint64 exp, bool send_packet = true);
int64 GetEXPCurrent() const {return exp_current;}
void SetEXPToNextLevel(int64 exp, bool send_packet = true);
int64 GetEXPToNextLevel() const {return exp_to_next_level;}
void SetRecruitingShortDesc(const char* new_desc, bool send_packet = true);
string GetRecruitingShortDesc() const {return recruiting_short_desc;}
void SetRecruitingFullDesc(const char* new_desc, bool send_packet = true);
string GetRecruitingFullDesc() const {return recruiting_full_desc;}
void SetRecruitingMinLevel(int8 new_level, bool send_packet = true);
int8 GetRecruitingMinLevel() const {return recruiting_min_level;}
void SetRecruitingPlayStyle(int8 new_play_style, bool send_packet = true);
int8 GetRecruitingPlayStyle() const {return recruiting_play_style;}
bool SetRecruitingDescTag(int8 index, int8 tag, bool send_packet = true);
int8 GetRecruitingDescTag(int8 index);
bool SetPermission(int8 rank, int8 permission, int8 value, bool send_packet = true, bool save_needed = true);
int8 GetPermission(int8 rank, int8 permission);
bool SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet = true, bool save_needed = true);
int8 GetEventFilter(int8 event_id, int8 category);
int32 GetNumUniqueAccounts();
int32 GetNumRecruiters();
int32 GetNextRecruiterID();
int64 GetNextEventID();
GuildMember* GetGuildMemberOnline(Client* client);
GuildMember* GetGuildMember(Player* player);
GuildMember* GetGuildMember(int32 character_id);
GuildMember* GetGuildMember(const char* player_name);
vector<GuildMember*>* GetGuildRecruiters();
GuildEvent* GetGuildEvent(int64 event_id);
bool SetRankName(int8 rank, const char* name, bool send_packet = true);
const char* GetRankName(int8 rank);
bool SetRecruitingFlag(int8 flag, int8 value, bool send_packet = true);
int8 GetRecruitingFlag(int8 flag);
bool SetGuildRecruiter(Client* client, const char* name, bool value, bool send_packet = true);
bool SetGuildRecruiterDescription(Client* client, const char* description, bool send_packet = true);
bool ToggleGuildRecruiterAdventureClass(Client* client, bool send_packet = true);
bool SetGuildMemberNote(const char* name, const char* note, bool send_packet = true);
bool SetGuildOfficerNote(const char* name, const char* note, bool send_packet = true);
bool AddNewGuildMember(Client* client, const char* invited_by = 0, int8 rank = GUILD_RANK_RECRUIT);
bool AddNewGuildMember(int32 characterID, const char *invited_by, int32 join_timestamp, int8 rank);
bool AddGuildMember(GuildMember* guild_member);
void RemoveGuildMember(int32 character_id, bool send_packet = true);
void RemoveAllGuildMembers();
bool DemoteGuildMember(Client* client, const char* name, bool send_packet = true);
bool PromoteGuildMember(Client* client, const char* name, bool send_packet = true);
int32 KickGuildMember(Client* client, const char* name, bool send_packet = true);
bool InvitePlayer(Client* client, const char* name, bool send_packet = true);
bool AddPointsToAll(Client* client, float points, const char* comment = 0, bool send_packet = true);
bool AddPointsToAllOnline(Client* client, float points, const char* comment = 0, bool send_packet = true);
bool AddPointsToGroup(Client* client, float points, const char* comment = 0, bool send_packet = true);
bool AddPointsToRaid(Client* client, float points, const char* comment = 0, bool send_packet = true);
bool AddPointsToGuildMember(Client* client, float points, const char* name, const char* comment = 0, bool send_packet = true);
bool AddPointHistory(GuildMember* guild_member, int32 date, const char* modified_by, float points, const char* comment = 0, bool new_point_history = true);
void ViewGuildMemberPoints(Client* client, const char* name);
bool ChangeMemberFlag(Client* client, int8 member_flag, int8 value, bool send_packet = true);
bool UpdateGuildMemberInfo(Player* player);
bool UpdateGuildStatus(Player *player, int32 Status);
void AddGuildEvent(int64 event_id, int32 type, const char* description, int32 date, int8 locked);
void AddNewGuildEvent(int32 type, const char* description, int32 date, bool send_packet = true, ...);
bool LockGuildEvent(int64 event_id, bool lock, bool send_packet = true);
bool DeleteGuildEvent(int64 event_id, bool send_packet = true);
void SendGuildMOTD(Client* client);
void SendGuildEventList();
void SendGuildEventList(Client* client);
void SendGuildEventDetails();
void SendGuildEventDetails(Client* client);
void SendAllGuildEvents();
void SendAllGuildEvents(Client* client);
void SendOldGuildEvent(Client* client, GuildEvent* guild_event);
void SendNewGuildEvent(GuildEvent* guild_event);
void SendNewGuildEvent(Client* client, GuildEvent* guild_event);
void SendGuildEventAction(int8 action, GuildEvent* guild_event);
void SendGuildEventAction(Client* client, int8 action, GuildEvent* guild_event);
void SendGuildBankEventList();
void SendGuildBankEventList(Client* client);
void SendGuildUpdate();
void SendGuildUpdate(Client* client);
void SendGuildMemberList();
void SendGuildMemberList(Client* client);
void SendGuildMember(Player* player, bool include_zone = true);
void SendGuildMember(GuildMember* gm, bool include_zone = true);
void SendGuildMember(Client* client, GuildMember* gm, bool include_zone = true);
void SendGuildModification(float points, vector<int32>* character_ids);
void SendGuildModification(Client* client, float points, vector<int32>* character_ids);
void GuildMemberLogin(Client *client, bool first_login = false);
void GuildMemberLogoff(Player *player);
void SendGuildMemberLeave(int32 character_id);
void SendGuildMemberLeave(Client* client, int32 character_id);
void SendGuildRecruitingDetails(Client* client);
void SendGuildRecruitingImages(Client* client);
void SendGuildRecruiterInfo(Client* client, Player* player);
bool HandleGuildSay(Client* sender, const char* message);
void HandleGuildSay(std::string senderName, const char* message, int8 language);
bool HandleOfficerSay(Client* sender, const char* message);
void HandleOfficerSay(std::string senderName, const char* message, int8 language);
void SendMessageToGuild(int8 event_type, const char* message, ...);
void SendGuildChatMessage(const char* message, ...);
void SetSaveNeeded(bool val) {save_needed = val;}
bool GetSaveNeeded() {return save_needed;}
void SetMemberSaveNeeded(bool val) {member_save_needed = val;}
bool GetMemberSaveNeeded() {return member_save_needed;}
void SetEventsSaveNeeded(bool val) {events_save_needed = val;}
bool GetEventsSaveNeeded() {return events_save_needed;}
void SetRanksSaveNeeded(bool val) {ranks_save_needed = val;}
bool GetRanksSaveNeeded() {return ranks_save_needed;}
void SetEventFiltersSaveNeeded(bool val) {event_filters_save_needed = val;}
bool GetEventFiltersSaveNeeded() {return event_filters_save_needed;}
void SetPointsHistorySaveNeeded(bool val) {points_history_save_needed = val;}
bool GetPointsHistorySaveNeeded() {return points_history_save_needed;}
void SetRecruitingSaveNeeded(bool val) {recruiting_save_needed = val;}
bool GetRecruitingSaveNeeded() {return recruiting_save_needed;}
map<int32, GuildMember*>* GetGuildMembers() {return &members;}
Mutex * GetGuildMembersMutex() {return &mMembers;}
deque<GuildEvent*>* GetGuildEvents() {return &guild_events;}
MutexMap<int8, MutexMap<int8, int8>*>* GetPermissions() {return &permissions;}
MutexMap<int8, string>* GetGuildRanks() {return &ranks;}
MutexMap<int8, int8>* GetRecruitingFlags() {return &recruiting_flags;}
MutexMap<int8, int8>* GetRecruitingDescTags() {return &recruiting_desc_tags;}
int8 GetRecruitingLookingForPacketValue();
static string GetEpicMobDeathMessage(const char* player_name, const char* mob_name);
private:
int32 id;
char name[64];
int8 level;
int32 formed_date;
char motd[256];
int64 exp_current;
int64 exp_to_next_level;
string recruiting_short_desc;
string recruiting_full_desc;
int8 recruiting_min_level;
int8 recruiting_play_style;
MutexMap<int8, string> ranks;
map<int32, GuildMember*> members;
Mutex mMembers;
deque<GuildEvent*> guild_events;
MutexMap<int8, MutexMap<int8, int8>*> permissions;
MutexMap<int8, MutexMap<int8, int8>*> event_filters;
MutexMap<int8, int8> recruiting_flags;
MutexMap<int8, int8> recruiting_desc_tags;
Bank banks[4];
int32 GetPermissionsPacketValue(int8 rank, int32 start, int32 end);
int32 GetEventFilterPacketValue(int8 category, int32 start, int32 end);
bool save_needed;
bool member_save_needed;
bool events_save_needed;
bool event_filters_save_needed;
bool ranks_save_needed;
bool points_history_save_needed;
bool recruiting_save_needed;
};
class GuildList {
public:
GuildList();
virtual ~GuildList();
bool AddGuild(Guild* guild);
Guild* GetGuild(int32 guild_id);
Guild* GetGuild(const char* guild_name);
bool RemoveGuild(Guild* guild, bool delete_data = false);
bool RemoveGuild(int32 guild_id, bool delete_data = false);
int32 GetNumGuilds() {return guild_list.size();}
MutexMap<int32, Guild*>* GetGuilds() {return &guild_list;}
private:
MutexMap<int32, Guild*> guild_list;
};
#endif

View File

@ -0,0 +1,615 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#endif
#include <math.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <ios>
#include <mysql.h>
#include <assert.h>
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "Guild.h"
extern GuildList guild_list;
extern RuleManager rule_manager;
void WorldDatabase::LoadGuilds() {
int32 num_guilds = 0;
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `name`, `motd`, `level`, `xp`, `xp_needed`, `formed_on` FROM `guilds`");
while (result && (row = mysql_fetch_row(result))) {
LogWrite(GUILD__DEBUG, 1, "Guilds", "%u. %s", atoul(row[0]), row[1]);
Guild* guild = new Guild;
guild->SetID(atoul(row[0]));
guild->SetName(row[1]);
if (row[2])
guild->SetMOTD(row[2], false);
guild->SetLevel(atoi(row[3]), false);
guild->SetEXPCurrent(atoul(row[4]), false);
guild->SetEXPToNextLevel(atoul(row[5]), false);
guild->SetFormedDate(atoul(row[6]));
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoaded %i guild members.", LoadGuildMembers(guild));
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Ranks...");
LoadGuildRanks(guild);
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Event Filters...");
LoadGuildEventFilters(guild);
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Events...");
LoadGuildEvents(guild);
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Recruiting...");
LoadGuildRecruiting(guild);
guild_list.AddGuild(guild);
num_guilds++;
}
LogWrite(GUILD__INFO, 0, "Guilds", "\tLoaded %u Guild(s)", num_guilds);
}
void WorldDatabase::LoadGuild(int32 guild_id) {
Query query;
MYSQL_ROW row;
Guild* tmpGuild = guild_list.GetGuild(guild_id);
if(tmpGuild) // already loaded
return;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `name`, `motd`, `level`, `xp`, `xp_needed`, `formed_on` FROM `guilds` where id=%u", guild_id);
if (result && (row = mysql_fetch_row(result))) {
LogWrite(GUILD__DEBUG, 1, "Guilds", "%u. %s", atoul(row[0]), row[1]);
Guild* guild = new Guild;
guild->SetID(atoul(row[0]));
guild->SetName(row[1]);
if (row[2])
guild->SetMOTD(row[2], false);
guild->SetLevel(atoi(row[3]), false);
guild->SetEXPCurrent(atoul(row[4]), false);
guild->SetEXPToNextLevel(atoul(row[5]), false);
guild->SetFormedDate(atoul(row[6]));
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoaded %i guild members.", LoadGuildMembers(guild));
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Ranks...");
LoadGuildRanks(guild);
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Event Filters...");
LoadGuildEventFilters(guild);
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Events...");
LoadGuildEvents(guild);
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Recruiting...");
LoadGuildRecruiting(guild);
guild_list.AddGuild(guild);
}
}
int32 WorldDatabase::LoadGuildMembers(Guild* guild) {
int32 num_members = 0;
Query query;
MYSQL_ROW row;
char *name;
int32 char_id;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `char_id`, `recruiter_id`, `guild_status`, `points`, `rank_id`, `member_flags`, `join_date`, `note`, `officer_note`, `recruiting_message`, `recruiter_picture_data` FROM `guild_members` WHERE `guild_id`=%u", guild->GetID());
while (result && (row = mysql_fetch_row(result))) {
char_id = atoul(row[0]);
if (!(name = GetCharacterName(char_id))) {
LogWrite(GUILD__ERROR, 0, "Guilds", "WorldDatabase::LoadGuildMembers Cannot find guild member with character id %u.", char_id);
continue;
}
GuildMember* gm = new GuildMember;
gm->character_id = char_id;
gm->recruiter_id = atoul(row[1]);
gm->guild_status = atoul(row[2]);
gm->points = atof(row[3]);
gm->rank = atoi(row[4]);
gm->member_flags = atoi(row[5]);
gm->join_date = atoul(row[6]);
if (row[7])
gm->note = string(row[7]);
if (row[8])
gm->officer_note = string(row[8]);
if (row[9])
gm->recruiter_description = string(row[9]);
int16 recruiter_picture_data_size = 0;
if (row[10] && (recruiter_picture_data_size = strlen(row[10])) > 0) {
gm->recruiter_picture_data_size = recruiter_picture_data_size / 2;
gm->recruiter_picture_data = new unsigned char[gm->recruiter_picture_data_size];
unsigned char* cpy = gm->recruiter_picture_data;
const char* str = row[10];
char high, low;
for (const char* ptr = str; *ptr; ptr += 2) {
high = tolower(*ptr);
low = tolower(*(ptr+1));
if (isdigit(high))
high = high - '0';
else if (high >= 'a' && high <= 'f')
high = (high - 'a') + 10;
else {
LogWrite(GUILD__ERROR, 0, "Guilds", "Guild mate with id %u has corrupt picture data. Data not loading.", gm->character_id);
safe_delete_array(gm->recruiter_picture_data);
gm->recruiter_picture_data_size = 0;
break;
}
if (isdigit(low))
low = low - '0';
else if (low >= 'a' && low <= 'f')
low = (low - 'a') + 10;
else {
LogWrite(GUILD__ERROR, 0, "Guilds", "Guild mate with id %u has corrupt picture data. Data not loading.", gm->character_id);
safe_delete_array(gm->recruiter_picture_data);
gm->recruiter_picture_data_size = 0;
break;
}
*cpy++ = low | (high << 4);
}
/*for (int16 i = 0; i < gm->recruiter_picture_data_size; i++)
if (i<10)
printf("int:%u hex:%x\n", gm->recruiter_picture_data[i], gm->recruiter_picture_data[i]);*/
}
else {
gm->recruiter_picture_data_size = 0;
gm->recruiter_picture_data = 0;
}
strncpy(gm->name, name, sizeof(gm->name));
gm->account_id = GetCharacterAccountID(char_id);
gm->adventure_class = GetCharacterClass(char_id);
gm->adventure_level = GetCharacterLevel(char_id);
gm->tradeskill_class = 0;
gm->tradeskill_level = 0;
gm->last_login_date = GetCharacterTimeStamp(char_id);
gm->zone = GetZoneDescription(GetCharacterCurrentZoneID(char_id));
gm->recruiting_show_adventure_class = 1;
LoadGuildPointsHistory(guild, gm);
guild->AddGuildMember(gm);
safe_delete_array(name);
num_members++;
}
return num_members;
}
void WorldDatabase::LoadGuildEvents(Guild* guild) {
if (guild) {
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `event_id`, `event_date`, `event_type`, `description`, `locked` FROM `guild_events` WHERE `guild_id`=%u AND `display`=1 AND `archived`=0 ORDER BY `event_date` DESC LIMIT 0, %u", guild->GetID(), GUILD_MAX_EVENTS);
while (result && (row = mysql_fetch_row(result)))
guild->AddGuildEvent(atoi64(row[0]), atoul(row[2]), row[3], atoul(row[1]), atoi(row[4]));
}
}
void WorldDatabase::LoadGuildRanks(Guild* guild) {
if (guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Loading Ranks for guild id: %u", guild->GetID());
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `rank_id`, `rank_name`, `permission1`, `permission2` FROM `guild_ranks` WHERE `guild_id`=%u", guild->GetID());
while (result && (row = mysql_fetch_row(result))) {
int8 rank_id = atoi(row[0]);
int32 permission1 = atoul(row[2]);
int32 permission2 = atoul(row[3]);
guild->SetRankName(rank_id, row[1], false);
LogWrite(GUILD__DEBUG, 5, "Guilds", "\tLoading rank_id: %i", rank_id);
LogWrite(GUILD__DEBUG, 5, "Guilds", "\tPermission1: %ul, Permission2: %ul", permission1, permission2);
for (int32 i = 0; i <= 44; i++) {
int32 bitwise_val;
if (i < 32) {
bitwise_val = (int32)pow(2.0, (double)(i));
guild->SetPermission(rank_id, i, permission1 & bitwise_val ? 1 : 0, false);
LogWrite(GUILD__DEBUG, 5, "Guilds", "\tSetting Permission %u to %u", i, permission1 & bitwise_val ? 1 : 0);
}
else {
bitwise_val = (int32)pow(2.0, (double)(i - 32));
guild->SetPermission(rank_id, i, permission2 & bitwise_val ? 1 : 0, false);
LogWrite(GUILD__DEBUG, 5, "Guilds", "\tSetting Permission %u to %u", i, permission2 & bitwise_val ? 1 : 0);
}
}
}
}
}
void WorldDatabase::LoadGuildEventFilters(Guild* guild) {
if (guild) {
Query query;
MYSQL_ROW row;
bool event_filter_added = false;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `event_id`, `retain`, `broadcast` FROM `guild_event_filters` WHERE `guild_id`=%u", guild->GetID());
while (result && (row = mysql_fetch_row(result))) {
guild->SetEventFilter(atoi(row[0]), GUILD_EVENT_FILTER_CATEGORY_RETAIN_HISTORY, atoi(row[1]), false);
guild->SetEventFilter(atoi(row[0]), GUILD_EVENT_FILTER_CATEGORY_BROADCAST, atoi(row[2]), false);
if (!event_filter_added)
event_filter_added = true;
}
if (!event_filter_added)
LoadGuildDefaultEventFilters(guild);
}
}
void WorldDatabase::LoadGuildPointsHistory(Guild* guild, GuildMember* guild_member) {
Query query;
MYSQL_ROW row;
MYSQL_RES* result;
assert(guild);
assert(guild_member);
result = query.RunQuery2(Q_SELECT, "SELECT `points_date`, `modified_by`, `comment`, `points` FROM `guild_points_history` WHERE `guild_id`=%u AND `char_id`=%u ORDER BY `points_date` DESC", guild->GetID(), guild_member->character_id);
while (result && (row = mysql_fetch_row(result)))
guild->AddPointHistory(guild_member, atoul(row[0]), row[1], atof(row[3]), row[2], false);
}
void WorldDatabase::LoadGuildRecruiting(Guild* guild) {
if (guild) {
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `short_desc`, `full_desc`, `min_level`, `play_style`, `looking_for`, `descriptive_tag1`, `descriptive_tag2`, `descriptive_tag3`, `descriptive_tag4` FROM `guild_recruiting` WHERE `guild_id`=%u", guild->GetID());
while (result && (row = mysql_fetch_row(result))) {
if (row[0])
guild->SetRecruitingShortDesc(row[0], false);
if (row[1])
guild->SetRecruitingFullDesc(row[1], false);
guild->SetRecruitingMinLevel(atoi(row[2]), false);
guild->SetRecruitingPlayStyle(atoi(row[3]), false);
for (int32 i = 0; i <= 5; i++) {
int32 bitwise_val = (int32)pow(2.0, (double)i);
guild->SetRecruitingFlag(i, atoi(row[4]) & bitwise_val ? 1 : 0, false);
}
guild->SetRecruitingDescTag(0, atoi(row[5]), false);
guild->SetRecruitingDescTag(1, atoi(row[6]), false);
guild->SetRecruitingDescTag(2, atoi(row[7]), false);
guild->SetRecruitingDescTag(3, atoi(row[8]), false);
}
}
}
void WorldDatabase::SaveGuild(Guild* guild, bool new_guild) {
Query query;
assert(guild);
if (new_guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Saving NEW Guild '%s' (%u) data...", guild->GetName(), guild->GetID());
query.RunQuery2(Q_INSERT, "INSERT INTO `guilds` (`name`, `motd`, `level`, `xp`, `xp_needed`, `formed_on`) "
"VALUES ('%s', '%s', %i, %llu, %llu, %u)",
getSafeEscapeString(guild->GetName()).c_str(), getSafeEscapeString(guild->GetMOTD()).c_str(), guild->GetLevel(), guild->GetEXPCurrent(), guild->GetEXPToNextLevel(), guild->GetFormedDate());
guild->SetID(query.GetLastInsertedID());
}
else {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Saving Guild '%s' (%u) data...", guild->GetName(), guild->GetID());
query.RunQuery2(Q_UPDATE, "UPDATE `guilds` "
"SET `name`='%s', `motd`='%s', `level`=%i, `xp`=%llu, `xp_needed`=%llu, `formed_on`=%u WHERE `id`=%u",
getSafeEscapeString(guild->GetName()).c_str(), getSafeEscapeString(guild->GetMOTD()).c_str(), guild->GetLevel(), guild->GetEXPCurrent(), guild->GetEXPToNextLevel(), guild->GetFormedDate(), guild->GetID());
}
guild->SetSaveNeeded(false);
}
void WorldDatabase::SaveGuildMembers(Guild* guild) {
map<int32, GuildMember *>* members;
map<int32, GuildMember *>::iterator itr;
Mutex *mMembers;
GuildMember *gm;
Query query, query2;
assert(guild);
members = guild->GetGuildMembers();
mMembers = guild->GetGuildMembersMutex();
mMembers->readlock(__FUNCTION__, __LINE__);
for (itr = members->begin(); itr != members->end(); itr++) {
gm = itr->second;
LogWrite(GUILD__DEBUG, 5, "Guilds", "Saving Guild Member '%s' (%u) data...", gm->name, gm->character_id);
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_members` (`guild_id`, `char_id`, `recruiter_id`, `guild_status`, `points`, `rank_id`, `member_flags`, `join_date`, `note`, `officer_note`, `recruiting_message`, `recruiter_picture_data`) VALUES (%u, %u, %u, %u, %f, %u, %u, %u, '%s', '%s', '%s', NULL) ON DUPLICATE KEY UPDATE `guild_id`=%u, `recruiter_id`=%u, `guild_status`=%u, `points`=%f, `rank_id`=%u, `member_flags`=%u, `join_date`=%u, `note`='%s', `officer_note`='%s', `recruiting_message`='%s', `recruiter_picture_data`=NULL", guild->GetID(), gm->character_id, gm->recruiter_id, gm->guild_status, gm->points, gm->rank, gm->member_flags, gm->join_date, getSafeEscapeString(gm->note.c_str()).c_str(), getSafeEscapeString(gm->officer_note.c_str()).c_str(), getSafeEscapeString(gm->recruiter_description.c_str()).c_str(), guild->GetID(), gm->recruiter_id, gm->guild_status, gm->points, gm->rank, gm->member_flags, gm->join_date, getSafeEscapeString(gm->note.c_str()).c_str(), getSafeEscapeString(gm->officer_note.c_str()).c_str(), getSafeEscapeString(gm->recruiter_description.c_str()).c_str());
if (gm && gm->recruiter_picture_data_size > 0 && gm->recruiter_picture_data) {
stringstream ss_hex;
stringstream ss_query;
ss_hex.flags(ios::hex);
for (int16 i = 0; i < gm->recruiter_picture_data_size; i++)
ss_hex << setfill('0') << setw(2) << (int)gm->recruiter_picture_data[i];
ss_query << "UPDATE `guild_members` SET `recruiter_picture_data`='" << ss_hex.str() << "' WHERE `char_id`=" << gm->character_id;
query2.RunQuery2(ss_query.str(), Q_UPDATE);
}
}
guild->SetMemberSaveNeeded(false);
mMembers->releasereadlock(__FUNCTION__, __LINE__);
}
void WorldDatabase::SaveGuildEvents(Guild* guild) {
if (guild) {
deque<GuildEvent*>* guild_events = guild->GetGuildEvents();
deque<GuildEvent*>::iterator itr;
for (itr = guild_events->begin(); itr != guild_events->end(); itr++) {
GuildEvent* ge = *itr;
if (!ge->save_needed)
continue;
LogWrite(GUILD__DEBUG, 5, "Guilds", "Saving Guild Events for guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_events` (`guild_id`, `event_id`, `event_date`, `event_type`, `description`, `display`, `locked`, `archived`) "
"VALUES (%u, %llu, %u, %u, '%s', 1, %u, 0) "
"ON DUPLICATE KEY UPDATE `locked`=%i",
guild->GetID(), ge->event_id, ge->date, ge->type, getSafeEscapeString(ge->description.c_str()).c_str(), ge->locked, ge->locked);
ge->save_needed = false;
}
guild->SetEventsSaveNeeded(false);
}
}
void WorldDatabase::SaveGuildRanks(Guild* guild) {
if (guild) {
MutexMap<int8, MutexMap<int8, int8>*>* permissions = guild->GetPermissions();
MutexMap<int8, string>* ranks = guild->GetGuildRanks();
MutexMap<int8, string>::iterator ranks_itr = ranks->begin();
while (ranks_itr.Next()) {
int32 permission1 = 0;
int32 permission2 = 0;
for (int32 i = 0; i <= 44; i++) {
if (permissions->count(ranks_itr.first) > 0 && permissions->Get(ranks_itr.first)->count(i) > 0 && permissions->Get(ranks_itr.first)->Get(i)) {
if (i < 32)
permission1 += (int32)pow(2.0, (double)i);
else
permission2 += (int32)pow(2.0, (double)(i - 32));
}
}
LogWrite(GUILD__DEBUG, 5, "Guilds", "Saving Guild Ranks for guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_ranks` (`guild_id`, `rank_id`, `rank_name`, `permission1`, `permission2`) "
"VALUES (%u, %u, '%s', %u, %u) "
"ON DUPLICATE KEY UPDATE `rank_name`='%s', `permission1`=%u, permission2=%u",
guild->GetID(), ranks_itr.first, getSafeEscapeString(ranks_itr.second.c_str()).c_str(), permission1, permission2, getSafeEscapeString(ranks_itr.second.c_str()).c_str(), permission1, permission2);
}
guild->SetRanksSaveNeeded(false);
}
}
void WorldDatabase::SaveGuildEventFilters(Guild* guild) {
int32 i;
assert(guild);
for (i = 0; i < 93; i++) {
LogWrite(GUILD__DEBUG, 5, "Guilds", "Saving Guild EventFilters for guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_event_filters` (`guild_id`, `event_id`, `retain`, `broadcast`) "
"VALUES (%u, %u, %u, %u) "
"ON DUPLICATE KEY UPDATE `retain`=%u, `broadcast`=%u",
guild->GetID(), i, guild->GetEventFilter(i, GUILD_EVENT_FILTER_CATEGORY_RETAIN_HISTORY), guild->GetEventFilter(i, GUILD_EVENT_FILTER_CATEGORY_BROADCAST), guild->GetEventFilter(i, GUILD_EVENT_FILTER_CATEGORY_RETAIN_HISTORY), guild->GetEventFilter(i, GUILD_EVENT_FILTER_CATEGORY_BROADCAST));
}
guild->SetEventFiltersSaveNeeded(false);
}
void WorldDatabase::SaveGuildPointsHistory(Guild* guild) {
map<int32, GuildMember *> *members;
map<int32, GuildMember *>::iterator itr;
Mutex *mMembers;
deque<PointHistory *> *ph_list;
deque<PointHistory*>::iterator ph_itr;
PointHistory* ph;
assert (guild);
members = guild->GetGuildMembers();
mMembers = guild->GetGuildMembersMutex();
mMembers->readlock(__FUNCTION__, __LINE__);
for (itr = members->begin(); itr != members->end(); itr++) {
ph_list = &itr->second->point_history;
for (ph_itr = ph_list->begin(); ph_itr != ph_list->end(); ph_itr++) {
ph = *ph_itr;
if (!ph->saved_needed)
continue;
LogWrite(GUILD__DEBUG, 5, "Guilds", "Saving Guild Point History for guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_points_history` (`guild_id`, `char_id`, `points_date`, `modified_by`, `comment`, `points`) "
"VALUES (%u, %u, %u, '%s', '%s', %f)",
guild->GetID(), itr->first, ph->date, getSafeEscapeString(ph->modified_by.c_str()).c_str(), getSafeEscapeString(ph->comment.c_str()).c_str(), ph->points);
ph->saved_needed = false;
}
}
guild->SetPointsHistorySaveNeeded(false);
mMembers->releasereadlock(__FUNCTION__, __LINE__);
}
void WorldDatabase::SaveGuildRecruiting(Guild* guild) {
if (guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Saving Recruiting info for guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_recruiting` (`guild_id`, `short_desc`, `full_desc`, `min_level`, `play_style`, `looking_for`, `descriptive_tag1`, `descriptive_tag2`, `descriptive_tag3`, `descriptive_tag4`) VALUES (%u, '%s', '%s', %u, %u, %u, %u, %u, %u, %u) ON DUPLICATE KEY UPDATE `short_desc`='%s', `full_desc`='%s', `min_level`=%u, `play_style`=%u, `looking_for`=%u, `descriptive_tag1`=%u, `descriptive_tag2`=%u, `descriptive_tag3`=%u, `descriptive_tag4`=%u", guild->GetID(), getSafeEscapeString(guild->GetRecruitingShortDesc().c_str()).c_str(), getSafeEscapeString(guild->GetRecruitingFullDesc().c_str()).c_str(), guild->GetRecruitingMinLevel(), guild->GetRecruitingPlayStyle(), guild->GetRecruitingLookingForPacketValue(), guild->GetRecruitingDescTag(0), guild->GetRecruitingDescTag(1), guild->GetRecruitingDescTag(2), guild->GetRecruitingDescTag(3), getSafeEscapeString(guild->GetRecruitingShortDesc().c_str()).c_str(), getSafeEscapeString(guild->GetRecruitingFullDesc().c_str()).c_str(), guild->GetRecruitingMinLevel(), guild->GetRecruitingPlayStyle(), guild->GetRecruitingLookingForPacketValue(), guild->GetRecruitingDescTag(0), guild->GetRecruitingDescTag(1), guild->GetRecruitingDescTag(2), guild->GetRecruitingDescTag(3));
guild->SetRecruitingSaveNeeded(false);
}
}
void WorldDatabase::DeleteGuild(Guild* guild) {
if (guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Deleting Guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_DELETE, "DELETE FROM `guilds` WHERE `id`=%u", guild->GetID());
}
}
void WorldDatabase::DeleteGuildMember(Guild* guild, int32 character_id) {
if (guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Deleting Character (%u) from guild '%s' (%u)...", character_id, guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_DELETE, "DELETE FROM `guild_members` WHERE `guild_id`=%u AND `char_id`=%u", guild->GetID(), character_id);
}
}
void WorldDatabase::DeleteGuildEvent(Guild* guild, int64 event_id) {
if (guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Deleting Event (%u) from guild '%s' (%u)...", event_id, guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_DELETE, "DELETE FROM `guild_events` WHERE `guild_id`=%u AND `event_id`=%u", guild->GetID(), event_id);
}
}
void WorldDatabase::DeleteGuildPointHistory(Guild* guild, int32 character_id, PointHistory* point_history) {
if (guild && point_history) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Deleting PointHistory for Character (%u) from guild '%s' (%u)...", character_id, guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_DELETE, "DELETE FROM `guild_points_history` WHERE `guild_id`=%u AND `char_id`=%u AND `points_date`=%u", guild->GetID(), character_id, point_history->date);
}
}
void WorldDatabase::ArchiveGuildEvent(Guild* guild, GuildEvent* guild_event) {
if (guild && guild_event) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Archiving Event (%u) for guild '%s' (%u)...", guild_event->event_id, guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_UPDATE, "UPDATE `guild_events` SET `archived`=1 WHERE `guild_id`=%u AND `event_id`=%u", guild->GetID(), guild_event->event_id);
}
}
void WorldDatabase::SaveHiddenGuildEvent(Guild* guild, GuildEvent* guild_event) {
if (guild && guild_event) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Saving Hidden Event (%u) for guild '%s' (%u)...", guild_event->event_id, guild->GetName(), guild->GetID());
Query query;
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_events` (`guild_id`, `event_id`, `event_date`, `event_type`, `description`, `display`, `locked`, `archived`) VALUES (%u, %u, %u, %u, '%s', 0, %u, 0)", guild->GetID(), guild_event->event_id, guild_event->type, guild_event->date, getSafeEscapeString(guild_event->description.c_str()).c_str(), guild_event->locked);
}
}
int32 WorldDatabase::GetGuildIDByCharacterID(int32 char_id) {
if(char_id > 0)
{
LogWrite(GUILD__DEBUG, 3, "Guilds", "Look up guild ID for player ID: '%u'...", char_id);
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM guilds, guild_members WHERE guilds.id = guild_members.guild_id AND char_id = %u ", char_id);
while (result && (row = mysql_fetch_row(result))) {
if( row[0] )
return atoul(row[0]);
}
}
return 0;
}
void WorldDatabase::LoadGuildDefaultRanks(Guild* guild) {
if (guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Load/Set Default Ranks for guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT DISTINCT `rank_id`, `rank_name`, `permission1`, `permission2` FROM `guild_ranks_defaults`");
while (result && (row = mysql_fetch_row(result))) {
int8 rank_id = atoi(row[0]);
int32 permission1 = atoul(row[2]);
int32 permission2 = atoul(row[3]);
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tSetting RankID %i, permission1: %u, permission2: %u", rank_id, permission1, permission2);
guild->SetRankName(rank_id, row[1], false);
for (int32 i = 0; i <= 44; i++) {
int32 bitwise_val;
if (i < 32) {
bitwise_val = (int32)pow(2.0, (double)i);
guild->SetPermission(rank_id, i, permission1 & bitwise_val ? 1 : 0, false);
}
else {
bitwise_val = (int32)pow(2.0, (double)(i - 32));
guild->SetPermission(rank_id, i, permission2 & bitwise_val ? 1 : 0, false);
}
}
}
}
}
void WorldDatabase::LoadGuildDefaultEventFilters(Guild* guild) {
if (guild) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "Load/Set Default Event Filters for guild '%s' (%u)...", guild->GetName(), guild->GetID());
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT DISTINCT `event_id`, `retain`, `broadcast` FROM `guild_event_defaults`");
while (result && (row = mysql_fetch_row(result))) {
LogWrite(GUILD__DEBUG, 3, "Guilds", "\tSetting Event Filter %i, retain: %i, broadcast: %i", atoi(row[0]), atoi(row[1]), atoi(row[2]));
guild->SetEventFilter(atoi(row[0]), GUILD_EVENT_FILTER_CATEGORY_RETAIN_HISTORY, atoi(row[1]), false);
guild->SetEventFilter(atoi(row[0]), GUILD_EVENT_FILTER_CATEGORY_BROADCAST, atoi(row[2]), false);
}
}
}
bool WorldDatabase::AddNewPlayerToServerGuild(int32 account_id, int32 char_id)
{
// Check if this servers rule allow auto-joining Server guild
int8 autojoin = rule_manager.GetGlobalRule(R_World, GuildAutoJoin)->GetInt8();
if( autojoin )
{
// if so, what is the guild ID of the default server guild?
int32 guild_id = rule_manager.GetGlobalRule(R_World, GuildAutoJoinID)->GetInt32();
Guild* guild = 0;
guild = guild_list.GetGuild(guild_id);
if (!guild)
{
// guild was not valid, abort!
LogWrite(GUILD__ERROR, 1, "Guilds", "Guild ID %u not found! Cannot autojoin members!", guild_id);
return false;
}
else
{
// guild was found, so what default Rank to make the players? if not set, use 7 (recruit)
int8 rank_id = rule_manager.GetGlobalRule(R_World, GuildAutoJoinDefaultRankID)->GetInt8();
if(!rank_id)
rank_id = 7;
// assuming all is good, insert the new guild member here...
GuildMember *gm = new GuildMember();
gm->account_id = account_id;
gm->character_id = char_id;
char* name = GetCharacterName(gm->character_id);
strncpy(gm->name, name, sizeof(gm->name));
gm->guild_status = 0;
gm->points = 0.0;
//gm->adventure_class = player->GetAdventureClass();
//gm->adventure_level = player->GetLevel();
//gm->tradeskill_class = player->GetTradeskillClass();
//gm->tradeskill_level = player->GetTSLevel();
gm->rank = rank_id;
gm->zone = string("");
gm->join_date = Timer::GetUnixTimeStamp();
gm->last_login_date = gm->join_date;
gm->recruiter_id = 0;
gm->member_flags = GUILD_MEMBER_FLAGS_NOTIFY_LOGINS;
gm->recruiting_show_adventure_class = 1;
gm->recruiter_picture_data_size = 0;
gm->recruiter_picture_data = 0;
guild->AddGuildMember(gm);
Query query;
query.RunQuery2(Q_INSERT, "INSERT INTO `guild_members` (`guild_id`, `char_id`, `join_date`, `rank_id`) VALUES (%u, %u, %u, %i)",
guild_id, char_id, gm->join_date, rank_id);
LogWrite(GUILD__DEBUG, 3, "Guilds", "Auto-join player (%u) to server guild '%s' (%u) at rank %i...", char_id, guild->GetName(), guild_id, rank_id);
// success!
return true;
}
}
// do not auto-join server guild
return false;
}

View File

@ -0,0 +1,315 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "HeroicOp.h"
#include "../../common/Log.h"
#include "../Rules/Rules.h"
extern MasterHeroicOPList master_ho_list;
extern RuleManager rule_manager;
HeroicOP::HeroicOP() {
m_complete = 0;
m_currentStage = 0;
m_wheel = 0;
m_target = 0;
m_startTime = 0;
m_totalTime = 0;
m_shifted = false;
for (int8 i = 0; i < 6; i++)
countered[i] = 0;
}
HeroicOP::~HeroicOP() {
starters.clear();
}
void HeroicOP::SetWheel(HeroicOPWheel* val) {
if (!m_wheel)
m_wheel = val;
else
LogWrite(SPELL__ERROR, 0, "HO", "Attempted to set the wheel on a heroic op with a wheel already set");
}
void HeroicOP::SetTarget(int32 val) {
m_target = val;
}
bool HeroicOP::UpdateHeroicOP(int16 icon) {
bool ret = false;
vector<HeroicOPStarter*>::iterator itr;
vector<HeroicOPStarter*> temp;
HeroicOPStarter* starter = 0;
LogWrite(SPELL__DEBUG, 0, "HO", "Current Stage %u, wheel exists: %u, looking for icon %u", m_currentStage, m_wheel ? 1 : 0, icon);
// If no wheel is set we are dealing with a starter chain still.
if (!m_wheel) {
// Loop through the starter chains
for (itr = starters.begin(); itr != starters.end(); itr++) {
starter = *itr;
// See if the icon matches the ability at our current stage, if not add it to a list to be removed
if (starter->abilities[m_currentStage] == icon)
ret = true;
else
temp.push_back(*itr);
}
if (ret) {
// ret = true so we had a match, first thing to do is remove those that didn't match
vector<HeroicOPStarter*>::iterator remove_itr;
for (remove_itr = temp.begin(); remove_itr != temp.end(); remove_itr++)
{
std::vector<HeroicOPStarter*>::iterator it = std::find(starters.begin(), starters.end(), *remove_itr);
starters.erase(it);
}
// now advance the current stage
m_currentStage++;
// Temp pointer to hold the completed chain, if any
HeroicOPStarter* complete_starter = 0;
// now loop through those that are left and check the next stage abilities for a 0xFFFF
for (itr = starters.begin(); itr != starters.end(); itr++) {
starter = *itr;
// Found one that is 0xFFFF, means the starter chain is done, get a wheel and reset the stage to 0
if ((starter->abilities[m_currentStage] == 0xFFFF)) {
LogWrite(SPELL__DEBUG, 0, "HO", "Current Stage %u, starter reset (new stage 0)", m_currentStage);
// reset the stage
ResetStage();
// geth the wheel
m_wheel = master_ho_list.GetWheel(starter);
// store the starter chain that is completed
complete_starter = starter;
// set the start time to now
SetStartTime(Timer::GetCurrentTime2());
// set the total time to complete the real to was the admin set in rules (default 10.0f)
SetTotalTime(rule_manager.GetGlobalRule(R_Zone, HOTime)->GetFloat());
// We set a wheel so we are done, kill the loop
break;
}
}
// Check to see if the completed start chain pointer was set
if (complete_starter) {
LogWrite(SPELL__DEBUG, 0, "HO", "Current Stage %u, complete_starter set", m_currentStage);
// clear the starter list
starters.clear();
// add the completed starter back in, we do this in case we need this starter again we can just do starters.at(0), for example shifting the wheel
starters.push_back(complete_starter);
}
}
}
else {
LogWrite(SPELL__DEBUG, 0, "HO", "Current Stage %u, wheel order: %u", m_currentStage, m_wheel->order);
// Wheel was set so we need to check the order it needs to be completed in.
if (m_wheel->order == 0) {
// No order
// Flag used to see if we can shift the wheel
bool can_shift = true;
// Check the icons and flag the ability as countered if there is a match
for (int8 i = 0; i < 6; i++) {
if (countered[i] == 1) {
// progress made on this wheel so we can't shift it
can_shift = false;
}
if (m_wheel->abilities[i] == icon) {
countered[i] = 1;
ret = true;
}
}
if (ret) {
// As we found a match lets loop through to see if we completed the ho
bool finished = true;
for (int8 i = 0; i < 6; i++) {
// if the ability is not 0xFFFF and countered is 0 then we still have more to go
if (m_wheel->abilities[i] != 0xFFFF && countered[i] == 0) {
finished = false;
break;
}
}
// is we finished the ho set the complete flag
if (finished)
SetComplete(2);
}
if (!ret && can_shift && m_wheel->shift_icon == icon) {
// can shift, icon matched shift icon, and no progress made
ret = ShiftWheel();
}
}
else {
// In order
// Check to see if we can shift the wheel
if (countered[0] == 0 && icon == m_wheel->shift_icon) {
// Can only shift the icon if nothing has completed yet (countered[0] = 0)
ret = ShiftWheel();
}
// Check the current stage and compare it to the icon
else if (m_wheel->abilities[m_currentStage] == icon) {
// Is a match so flag this stage as done
countered[m_currentStage] = 1;
// Advance the stage
m_currentStage++;
// Set the return value to true
ret = true;
// Check the next stage, if it is over 6 or equal to 0xFFFF flag the HO as complete
if (m_currentStage > 6 || m_wheel->abilities[m_currentStage] == 0xFFFF)
SetComplete(2);
}
}
}
return ret;
}
void HeroicOP::AddStarterChain(HeroicOPStarter* starter) {
starters.push_back(starter);
}
bool HeroicOP::ShiftWheel() {
// Can only shift once so if we already have return out
if (HasShifted())
return false;
// Clear the wheel
m_wheel = 0;
// Get a new Wheel
SetWheel(master_ho_list.GetWheel(starters.at(0)));
// Set the ho as shifted
m_shifted = true;
return true;
}
MasterHeroicOPList::MasterHeroicOPList() {
}
MasterHeroicOPList::~MasterHeroicOPList() {
map<int8, map<HeroicOPStarter*, vector<HeroicOPWheel*> > >::iterator itr;
map<HeroicOPStarter*, vector<HeroicOPWheel*> >::iterator itr2;
vector<HeroicOPWheel*>::iterator itr3;
vector<HeroicOPStarter*> temp;
vector<HeroicOPStarter*>::iterator itr4;
// loop through the m_hoList to delete the pointers
for (itr = m_hoList.begin(); itr != m_hoList.end(); itr++) {
// loop through the second map of the m_hoList
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
// loop through the vector of the second map and delete the pointers
for (itr3 = itr2->second.begin(); itr3 != itr2->second.end(); itr3++)
safe_delete(*itr3);
// clear the vector
itr2->second.clear();
// put the starter in a temp list to delete later
temp.push_back(itr2->first);
}
// clear the seond map
itr->second.clear();
}
// clear the m_hoList
m_hoList.clear();
// Delete the starters
for (itr4 = temp.begin(); itr4 != temp.end(); itr4++)
safe_delete(*itr4);
// clear the temp vector
temp.clear();
}
void MasterHeroicOPList::AddStarter(int8 start_class, HeroicOPStarter* starter) {
if (m_hoList.count(start_class) == 0 || m_hoList[start_class].count(starter) == 0) {
m_hoList[start_class][starter]; // This adds the starter with out giving it a vector of wheels yet.
}
}
void MasterHeroicOPList::AddWheel(int32 starter_id, HeroicOPWheel* wheel) {
map<int8, map<HeroicOPStarter*, vector<HeroicOPWheel*> > >::iterator itr;
map<HeroicOPStarter*, vector<HeroicOPWheel*> >::iterator itr2;
bool found = false;
// Loop through the list and add the wheel to the correct starter
for (itr = m_hoList.begin(); itr != m_hoList.end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
if (itr2->first->id == starter_id) {
// Found a match, add the wheel, set the flag to break the first loop, and break this loop
itr2->second.push_back(wheel);
found = true;
break;
}
}
// If we found a match break the first loop
if (found)
break;
}
// No match found give an error.
if (!found)
LogWrite(SPELL__DEBUG, 0, "HO", "Attempted to add a wheel to a starter (%u) that doesn't exsist", starter_id);
}
HeroicOP* MasterHeroicOPList::GetHeroicOP(int8 class_id) {
if (m_hoList.count(class_id) == 0) {
LogWrite(SPELL__ERROR, 0, "HO", "No HO's found for the given class (%i)", class_id);
return 0;
}
map<HeroicOPStarter*, vector<HeroicOPWheel*> >::iterator itr;
HeroicOP* ret = new HeroicOP();
// Loop through the starters for this class and add them to the HO
for (itr = m_hoList[class_id].begin(); itr != m_hoList[class_id].end(); itr++)
ret->AddStarterChain(itr->first);
return ret;
}
HeroicOPWheel* MasterHeroicOPList::GetWheel(HeroicOPStarter* starter) {
if (!starter)
return 0;
if (m_hoList.count(starter->start_class) == 0) {
LogWrite(SPELL__ERROR, 0, "HO", "Start class (%u) not found", starter->start_class);
return 0;
}
if (m_hoList[starter->start_class].count(starter) == 0) {
LogWrite(SPELL__ERROR, 0, "HO", "Wheel not found for the provided starter (%u)", starter->id);
return 0;
}
int index = MakeRandomInt(0, m_hoList[starter->start_class][starter].size() - 1);
if(index < m_hoList[starter->start_class][starter].size())
return m_hoList[starter->start_class][starter].at(index);
else
LogWrite(SPELL__ERROR, 0, "HO", "Wheel index %u for heroic_ops starter ID %u NOT Found!! Wheel starter_class %u, wheel size: %u. Wheels that match starter_link_id -> Starter 'id' missing.", index, starter->id, starter->start_class, m_hoList[starter->start_class][starter].size());
return nullptr;
}

View File

@ -0,0 +1,156 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __HEROICOP_H__
#define __HEROICOP_H__
#include <map>
#include <vector>
#include "../../common/types.h"
using namespace std;
struct HeroicOPStarter {
int32 id;
int8 start_class;
int16 starter_icon;
int16 abilities[6];
};
struct HeroicOPWheel {
int8 order;
int16 shift_icon;
float chance;
int16 abilities[6];
int32 spell_id;
};
class HeroicOP {
public:
HeroicOP();
~HeroicOP();
/// <summary>Sets the complete flag for this Heroic OP</summary>
/// <param name='val'>The value to set the complete flag to, 1 = failed 2 = finished</param>
void SetComplete(int8 val) { m_complete = val; }
/// <summary>Sets the current stage of the starter chain or the wheel chain is at</summary>
/// <param name='val'>The stage to set this Heroic OP to</param>
void SetStage(int8 val) { m_currentStage = val; }
/// <summary>Sets the wheel for this Heroic OP</summary>
/// <param name='val'>The wheel we are setting the Heroic OP to</param>
void SetWheel(HeroicOPWheel* val);
/// <summary>Sets the start time for the wheel</summary>
/// <param name='val'>Value to set the start time to</param>
void SetStartTime(int32 val) { m_startTime = val; }
/// <summary>Sets the total time to complete the wheel</summary>
/// <param name='val'>Value to set the total time to</param>
void SetTotalTime(float val) { m_totalTime = val; }
/// <summary>Sets the target of this HO</summary>
/// <param name='val'>The ID of the spawn</param>
void SetTarget(int32 val);
/// <summary>Gets the complete flag for this Heroic OP</summary>
/// <returns>0 = not complete, 1 = complete, 2+= failed</returns>
int8 GetComplete() { return m_complete; }
/// <summary>Gets the wheel for this heroic op</summary>
HeroicOPWheel* GetWheel() { return m_wheel; }
/// <summary>Gets a pointer to the list of starter chains</summary>
vector<HeroicOPStarter*>* GetStarterChains() { return &starters; }
/// <summary>Gets the current stage the HO is on</summary>
int8 GetStage() { return m_currentStage; }
/// <summary>Gets the start time for the wheel</summary>
int32 GetStartTime() { return m_startTime; }
/// <summary>Gets the total time players have to complete the wheel</summary>
float GetTotalTime() { return m_totalTime; }
/// <summary>Gets the ID of this HO's target</summary>
int32 GetTarget() { return m_target; }
/// <summary></summary>
bool HasShifted() { return m_shifted; }
/// <summary>Checks to see if the given icon will advance the Heroic OP</summary>
/// <param name='icon'>The icon that is trying to advance the Heroic OP</param>
/// <returns>True if the icon advanced the HO</returns>
bool UpdateHeroicOP(int16 icon);
/// <summary>Reset the stage to 0</summary>
void ResetStage() { m_currentStage = 0; }
/// <summary>Adds a starter chain to the Heroic OP</summary>
/// <param name='starter'>The starter chain to add</param>
void AddStarterChain(HeroicOPStarter* starter);
/// <summary>Attempts to shift the wheel</summary>
bool ShiftWheel();
int8 countered[6];
private:
int8 m_complete;
int8 m_currentStage;
int32 m_startTime;
float m_totalTime;
int32 m_target;
bool m_shifted;
HeroicOPWheel* m_wheel;
vector<HeroicOPStarter*> starters;
};
class MasterHeroicOPList {
public:
MasterHeroicOPList();
~MasterHeroicOPList();
/// <summary>Adds the starter chain to the list</summary>
/// <param name='start_class'>Class id for the starter chain</param>
/// <param name='starter'>Starter chain to add</param>
void AddStarter(int8 start_class, HeroicOPStarter* starter);
/// <summary>Add the wheel chain to the list</summary>
/// <param name='starter_id'>Id of the starter this wheel belongs to</param>
/// <param name='wheel'>Wheel to add</param>
void AddWheel(int32 starter_id, HeroicOPWheel* wheel);
/// <summary>Creates a new HO</summary>
/// <param name='class_id'>Class ID starting the HO</param>
HeroicOP* GetHeroicOP(int8 class_id);
/// <summary>Gets a random wheel from the given starter</summary>
/// <param name='starter'>The starter to determine what wheels to choose from</param>
HeroicOPWheel* GetWheel(HeroicOPStarter* starter);
private:
// map<class, map<starter, vector<wheel> > >
map<int8, map<HeroicOPStarter*, vector<HeroicOPWheel*> > > m_hoList;
};
#endif

View File

@ -0,0 +1,79 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../WorldDatabase.h"
#include "../../common/Log.h"
#include "HeroicOp.h"
extern MasterHeroicOPList master_ho_list;
void WorldDatabase::LoadHOStarters() {
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `starter_class`, `starter_icon`, `ability1`, `ability2`, `ability3`, `ability4`, `ability5`, `ability6` FROM `heroic_ops` WHERE `ho_type`='Starter'");
if (result && mysql_num_rows(result) > 0) {
int32 count = 0;
while ((row = mysql_fetch_row(result))) {
HeroicOPStarter* starter = new HeroicOPStarter;
starter->id = atoul(row[0]);
starter->start_class = atoi(row[1]);
starter->starter_icon = atoi(row[2]);
starter->abilities[0] = atoi(row[3]);
starter->abilities[1] = atoi(row[4]);
starter->abilities[2] = atoi(row[5]);
starter->abilities[3] = atoi(row[6]);
starter->abilities[4] = atoi(row[7]);
starter->abilities[5] = atoi(row[8]);
master_ho_list.AddStarter(starter->start_class, starter);
count++;
}
LogWrite(WORLD__INFO, 0, "World", "- Loaded %u starter chains", count);
}
}
void WorldDatabase::LoadHOWheel() {
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `starter_link_id`, `chain_order`, `shift_icon`, `spell_id`, `chance`, `ability1`, `ability2`, `ability3`, `ability4`, `ability5`, `ability6` FROM `heroic_ops` WHERE `ho_type`='Wheel'");
if (result && mysql_num_rows(result) > 0) {
int32 count = 0;
while ((row = mysql_fetch_row(result))) {
HeroicOPWheel* wheel = new HeroicOPWheel;
wheel->order = atoi(row[1]);
wheel->shift_icon = atoi(row[2]);
wheel->spell_id = atoul(row[3]);
wheel->chance = atof(row[4]);
wheel->abilities[0] = atoi(row[5]);
wheel->abilities[1] = atoi(row[6]);
wheel->abilities[2] = atoi(row[7]);
wheel->abilities[3] = atoi(row[8]);
wheel->abilities[4] = atoi(row[9]);
wheel->abilities[5] = atoi(row[10]);
master_ho_list.AddWheel(atoul(row[0]), wheel);
count++;
}
LogWrite(WORLD__INFO, 0, "World", "- Loaded %u HO wheels", count);
}
}

View File

@ -0,0 +1,158 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../ClientPacketFunctions.h"
#include "../../common/Log.h"
#include "HeroicOp.h"
#include "../Spells.h"
extern ConfigReader configReader;
extern MasterSpellList master_spell_list;
void ClientPacketFunctions::SendHeroicOPUpdate(Client* client, HeroicOP* ho) {
if (!client || !client->GetPlayer()) {
LogWrite(PACKET__ERROR, 0, "Packets", "SendHeroicOPUpdate() called without a valid client");
return;
}
if (!ho) {
LogWrite(PACKET__ERROR, 0, "Packets", "SendHeroicOPUpdate() called without a valid HO");
return;
}
PacketStruct* packet = configReader.getStruct("WS_HeroicOpportunity", client->GetVersion());
Spell* spell = 0;
if (packet) {
packet->setDataByName("id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()));
if (ho->GetWheel()) {
spell = master_spell_list.GetSpell(ho->GetWheel()->spell_id, 1);
if (!spell) {
LogWrite(SPELL__ERROR, 0, "HO", "Unable to get the spell (%u)", ho->GetWheel()->spell_id);
return;
}
packet->setDataByName("name", spell->GetName());
packet->setDataByName("description", spell->GetDescription());
packet->setDataByName("order", ho->GetWheel()->order);
packet->setDataByName("time_total", ho->GetTotalTime());
packet->setDataByName("time_left", max(0.0f, (float)(((ho->GetStartTime() + (ho->GetTotalTime() * 1000)) - Timer::GetCurrentTime2()) / 1000)));
// This is not displayed in the wheel so set it to 0xFFFF
packet->setDataByName("starter_icon", 0xFFFF);
if (ho->HasShifted())
packet->setDataByName("shift_icon", 0xFFFF);
else
packet->setDataByName("shift_icon", ho->GetWheel()->shift_icon);
// If completed set special values
if (ho->GetComplete() > 0) {
packet->setDataByName("wheel_type", 2);
packet->setDataByName("unknown", ho->GetComplete());
}
char temp[20];
char ability[20];
// Set the icons for the whee;
for (int8 i = 1; i < 7; i++) {
strcpy(ability, "icon");
itoa(i, temp, 10);
strcat(ability, temp);
packet->setDataByName(ability, ho->GetWheel()->abilities[i-1]);
}
// Flag the icons that are completed
for (int8 i = 1; i < 7; i++) {
strcpy(ability, "countered");
itoa(i, temp, 10);
strcat(ability, temp);
packet->setDataByName(ability, ho->countered[i-1]);
}
}
else {
if (ho->GetComplete() > 0) {
// This will make the ui element vanish
packet->setDataByName("wheel_type", 5);
packet->setDataByName("unknown", 8);
}
else {
packet->setDataByName("wheel_type", 4);
}
packet->setDataByName("icon1", 0xFFFF);
packet->setDataByName("icon2", 0xFFFF);
packet->setDataByName("icon3", 0xFFFF);
packet->setDataByName("icon4", 0xFFFF);
packet->setDataByName("icon5", 0xFFFF);
packet->setDataByName("icon6", 0xFFFF);
packet->setDataByName("shift_icon", 0xFFFF);
int8 index = 1;
char temp[20];
char ability[20];
vector<HeroicOPStarter*>::iterator itr;
for (itr = ho->GetStarterChains()->begin(); itr != ho->GetStarterChains()->end(); itr++, index++) {
if (index > 6 )
break;
strcpy(ability, "icon");
itoa(index, temp, 10);
strcat(ability, temp);
packet->setDataByName(ability, (*itr)->abilities[ho->GetStage()]);
// Only set this once
if (index == 1)
packet->setDataByName("starter_icon", (*itr)->starter_icon);
}
}
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
/*
<Struct Name="WS_HeroicOpportunity" ClientVersion="1" OpcodeName="OP_UpdateOpportunityMsg">
<Data ElementName="name" Type="EQ2_16Bit_String" />
<Data ElementName="description" Type="EQ2_16Bit_String" />
<Data ElementName="id" Type="int32" />
<Data ElementName="wheel_type" Type="int8" />
<Data ElementName="unknown" Type="int8" />
<Data ElementName="order" Type="int8" />
<Data ElementName="shift_icon" Type="int16" />
<Data ElementName="starter_icon" Type="int16" />
<Data ElementName="time_total" Type="float" />
<Data ElementName="time_left" Type="float" />
<Data ElementName="icon1" Type="int16" />
<Data ElementName="icon2" Type="int16" />
<Data ElementName="icon3" Type="int16" />
<Data ElementName="icon4" Type="int16" />
<Data ElementName="icon5" Type="int16" />
<Data ElementName="icon6" Type="int16" />
<Data ElementName="countered1" Type="int16" />
<Data ElementName="countered2" Type="int16" />
<Data ElementName="countered3" Type="int16" />
<Data ElementName="countered4" Type="int16" />
<Data ElementName="countered5" Type="int16" />
<Data ElementName="countered6" Type="int16" />
</Struct>
*/

View File

@ -0,0 +1,131 @@
#include "../WorldDatabase.h"
#include "../World.h"
extern World world;
void WorldDatabase::LoadHouseZones() {
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT * FROM `houses`");
if (result && mysql_num_rows(result) > 0) {
while ((row = mysql_fetch_row(result))) {
world.AddHouseZone(atoul(row[0]), row[1], atoi64(row[2]), atoul(row[3]), atoi64(row[4]), atoul(row[5]), atoi(row[6]), atoi(row[7]), atoi(row[8]), atoul(row[9]), atoul(row[10]), atof(row[11]), atof(row[12]), atof(row[13]), atof(row[14]));
}
}
}
int64 WorldDatabase::AddPlayerHouse(int32 char_id, int32 house_id, int32 instance_id, int32 upkeep_due) {
Query query;
string insert = string("INSERT INTO character_houses (char_id, house_id, instance_id, upkeep_due) VALUES (%u, %u, %u, %u) ");
query.RunQuery2(Q_INSERT, insert.c_str(), char_id, house_id, instance_id, upkeep_due);
int64 unique_id = query.GetLastInsertedID();
return unique_id;
}
void WorldDatabase::SetHouseUpkeepDue(int32 char_id, int32 house_id, int32 instance_id, int32 upkeep_due) {
Query query;
string update = string("UPDATE character_houses set upkeep_due=%u where char_id = %u and house_id = %u and instance_id = %u");
query.RunQuery2(Q_UPDATE, update.c_str(), upkeep_due, char_id, house_id, instance_id);
}
void WorldDatabase::UpdateHouseEscrow(int32 house_id, int32 instance_id, int64 amount_coins, int32 amount_status) {
Query query;
string update = string("UPDATE character_houses set escrow_coins = %llu, escrow_status = %u where house_id = %u and instance_id = %u");
query.RunQuery2(Q_UPDATE, update.c_str(), amount_coins, amount_status, house_id, instance_id);
}
void WorldDatabase::RemovePlayerHouse(int32 char_id, int32 house_id) {
}
void WorldDatabase::LoadPlayerHouses() {
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT h.id, h.char_id, h.house_id, h.instance_id, h.upkeep_due, h.escrow_coins, h.escrow_status, c.name FROM character_houses h, characters c WHERE h.char_id = c.id");
if (result && mysql_num_rows(result) > 0) {
while ((row = mysql_fetch_row(result))) {
world.AddPlayerHouse(atoul(row[1]), atoul(row[2]), atoi64(row[0]), atoul(row[3]), atoul(row[4]), atoi64(row[5]), atoul(row[6]), row[7]);
}
}
}
void WorldDatabase::LoadDeposits(PlayerHouse* ph)
{
if (!ph)
return;
ph->deposits.clear();
ph->depositsMap.clear();
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select timestamp, amount, last_amount, status, last_status, name from character_house_deposits where house_id = %u and instance_id = %u order by timestamp asc limit 255", ph->house_id, ph->instance_id);
if (result && mysql_num_rows(result) > 0) {
while ((row = mysql_fetch_row(result))) {
Deposit d;
d.timestamp = atoul(row[0]);
int64 outVal = strtoull(row[1], NULL, 0);
d.amount = outVal;
outVal = strtoull(row[2], NULL, 0);
d.last_amount = outVal;
d.status = atoul(row[3]);
d.last_status = atoul(row[4]);
d.name = string(row[5]);
ph->deposits.push_back(d);
ph->depositsMap.insert(make_pair(d.name, d));
}
}
}
void WorldDatabase::LoadHistory(PlayerHouse* ph)
{
if (!ph)
return;
ph->history.clear();
Query query;
MYSQL_ROW row;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select timestamp, amount, status, reason, name, pos_flag from character_house_history where house_id = %u and instance_id = %u order by timestamp asc limit 255", ph->house_id, ph->instance_id);
if (result && mysql_num_rows(result) > 0) {
while ((row = mysql_fetch_row(result))) {
HouseHistory h;
h.timestamp = atoul(row[0]);
int64 outVal = strtoull(row[1], NULL, 0);
h.amount = outVal;
h.status = atoul(row[2]);
h.reason = string(row[3]);
h.name = string(row[4]);
h.pos_flag = atoul(row[5]);
ph->history.push_back(h);
}
}
}
void WorldDatabase::AddHistory(PlayerHouse* house, char* name, char* reason, int32 timestamp, int64 amount, int32 status, int8 pos_flag)
{
if (!house)
return;
HouseHistory h(Timer::GetUnixTimeStamp(), amount, string(name), string(reason), status, pos_flag);
house->history.push_back(h);
Query query;
string insert = string("INSERT INTO character_house_history (timestamp, house_id, instance_id, name, amount, status, reason, pos_flag) VALUES (%u, %u, %u, '%s', %llu, %u, '%s', %u) ");
query.RunQuery2(Q_INSERT, insert.c_str(), timestamp, house->house_id, house->instance_id, name, amount, status, reason, pos_flag);
}

View File

@ -0,0 +1,454 @@
#include "../ClientPacketFunctions.h"
#include "../World.h"
#include "../client.h"
#include "../WorldDatabase.h"
#include "../Rules/Rules.h"
extern ConfigReader configReader;
extern World world;
extern WorldDatabase database;
extern RuleManager rule_manager;
void ClientPacketFunctions::SendHousePurchase(Client* client, HouseZone* hz, int32 spawnID) {
PacketStruct* packet = configReader.getStruct("WS_PlayerHousePurchase", client->GetVersion());
if (packet) {
int8 disable_alignment_req = rule_manager.GetZoneRule(client->GetCurrentZoneID(), R_Player, DisableHouseAlignmentRequirement)->GetInt8();
packet->setDataByName("house_name", hz->name.c_str());
packet->setDataByName("house_id", hz->id);
packet->setDataByName("spawn_id", spawnID);
packet->setDataByName("purchase_coins", hz->cost_coin);
packet->setDataByName("purchase_status", hz->cost_status);
packet->setDataByName("upkeep_coins", hz->upkeep_coin);
packet->setDataByName("upkeep_status", hz->upkeep_status);
packet->setDataByName("vendor_vault_slots", hz->vault_slots);
string req;
if (hz->alignment > 0 && !disable_alignment_req) {
req = "You must be of ";
if (hz->alignment == 1)
req.append("Good");
else
req.append("Evil");
req.append(" alignment");
}
if (hz->guild_level > 0) {
if (req.length() > 0) {
req.append(", and a guild level of ");
char temp[5];
sprintf(temp, "%i", hz->guild_level);
req.append(temp);
//req.append(std::to_string(static_cast<long long>(hz->guild_level)));
}
else {
req.append("Requires a guild of level ");
char temp[5];
sprintf(temp, "%i", hz->guild_level);
req.append(temp);
//req.append(std::to_string(static_cast<long long>(hz->guild_level)))
req.append(" or above");
}
}
if (req.length() > 0) {
req.append(" in order to purchase a home within the ");
req.append(hz->name);
req.append(".");
}
packet->setDataByName("additional_reqs", req.c_str());
bool enable_buy = true;
if (hz->alignment > 0 && client->GetPlayer()->GetAlignment() != hz->alignment && !disable_alignment_req)
enable_buy = false;
if (hz->guild_level > 0 && (!client->GetPlayer()->GetGuild() || (client->GetPlayer()->GetGuild() && client->GetPlayer()->GetGuild()->GetLevel() < hz->guild_level)))
enable_buy = false;
packet->setDataByName("enable_buy", enable_buy ? 1 : 0);
//packet->PrintPacket();
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
void ClientPacketFunctions::SendHousingList(Client* client) {
if(client->GetVersion() <= 561) {
return; // not supported
}
std::vector<PlayerHouse*> houses = world.GetAllPlayerHouses(client->GetCharacterID());
// this packet must be sent first otherwise it blocks out the enter house option after paying upkeep
PacketStruct* packet = configReader.getStruct("WS_CharacterHousingList", client->GetVersion());
if(!packet) {
return;
}
packet->setArrayLengthByName("num_houses", houses.size());
for (int i = 0; i < houses.size(); i++)
{
PlayerHouse* ph = (PlayerHouse*)houses[i];
HouseZone* hz = world.GetHouseZone(ph->house_id);
string name;
name = ph->player_name;
name.append("'s ");
name.append(hz->name);
packet->setArrayDataByName("house_id", ph->unique_id, i);
string zone_name = database.GetZoneName(hz->zone_id);
if(zone_name.length() > 0)
packet->setArrayDataByName("zone", zone_name.c_str(), i);
packet->setArrayDataByName("house_city", hz->name.c_str(), i);
packet->setArrayDataByName("house_address", "", i); // need this pulled from live
packet->setArrayDataByName("house_description", name.c_str(), i);
packet->setArrayDataByName("index", i, i); // they send 2, 4, 6, 8 as the index ID's on the client..
// this seems to be some kind of timestamp, if we keep updating then in conjunction with upkeep_due
// in SendBaseHouseWindow/WS_PlayerHouseBaseScreen being a >0 number we can access 'enter house'
int32 upkeep_due = 0;
if (((sint64)ph->upkeep_due - (sint64)Timer::GetUnixTimeStamp()) > 0)
upkeep_due = ph->upkeep_due - Timer::GetUnixTimeStamp();
if ( client->GetVersion() >= 63119 )
packet->setArrayDataByName("unknown2a", 0xFFFFFFFF, i);
else
packet->setArrayDataByName("unknown2", 0xFFFFFFFF, i);
}
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
void ClientPacketFunctions::SendBaseHouseWindow(Client* client, HouseZone* hz, PlayerHouse* ph, int32 spawnID) {
// if we don't send this then the enter house option won't be available if upkeep is paid
if (!hz || !ph)
{
client->SimpleMessage(CHANNEL_COLOR_RED, "HouseZone or PlayerHouse missing and cannot send SendBaseHouseWindow");
return;
}
string name;
name = ph->player_name;
name.append("'s ");
name.append(hz->name);
if (spawnID)
SendHousingList(client);
int32 upkeep_due = 0;
if (((sint64)ph->upkeep_due - (sint64)Timer::GetUnixTimeStamp()) > 0)
upkeep_due = ph->upkeep_due - Timer::GetUnixTimeStamp();
// need this to enable the "enter house" button
PacketStruct* packet = nullptr;
if(client->GetVersion() > 561 && client->GetCurrentZone()->GetInstanceType() != PERSONAL_HOUSE_INSTANCE
&& client->GetCurrentZone()->GetInstanceType() != GUILD_HOUSE_INSTANCE) {
packet = configReader.getStruct("WS_UpdateHouseAccessDataMsg", client->GetVersion());
if(!packet) {
return; // we need this for these clients or enter house will not work properly
}
if (packet) {
packet->setDataByName("house_id", 0xFFFFFFFFFFFFFFFF);
packet->setDataByName("success", (upkeep_due > 0) ? 0xFFFFFFFF : 0);
packet->setDataByName("unknown2", 0xFFFFFFFF);
packet->setDataByName("unknown3", 0xFFFFFFFF);
}
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
packet = configReader.getStruct("WS_PlayerHouseBaseScreen", client->GetVersion());
if (packet) {
packet->setDataByName("house_id", ph->unique_id);
packet->setDataByName("spawn_id", spawnID);
packet->setDataByName("character_id", client->GetPlayer()->GetCharacterID());
packet->setDataByName("house_name", name.c_str());
packet->setDataByName("zone_name", hz->name.c_str());
packet->setDataByName("upkeep_cost_coins", hz->upkeep_coin);
packet->setDataByName("upkeep_cost_status", hz->upkeep_status);
packet->setDataByName("upkeep_due", upkeep_due);
packet->setDataByName("escrow_balance_coins", ph->escrow_coins);
packet->setDataByName("escrow_balance_status", ph->escrow_status);
// temp - set priv level to owner for now
packet->setDataByName("privlage_level", 4);
// temp - set house type to personal house for now
packet->setDataByName("house_type", 0);
if(client->GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE
|| client->GetCurrentZone()->GetInstanceType() == GUILD_HOUSE_INSTANCE) {
packet->setDataByName("inside_house", 1);
packet->setDataByName("public_access_level", 1);
}
packet->setDataByName("num_access", 0);
packet->setDataByName("num_history", 0);
// allows deposits/history to be seen -- at this point seems plausible supposed to be 'inside_house'..?
packet->setDataByName("unknown3", (ph->deposits.size() || ph->history.size()) ? 1 : 0);
packet->setArrayLengthByName("num_deposit", ph->deposits.size());
list<Deposit>::iterator itr;
int d = 0;
for (itr = ph->deposits.begin(); itr != ph->deposits.end(); itr++)
{
packet->setArrayDataByName("deposit_name", itr->name.c_str(), d);
packet->setArrayDataByName("deposit_total_coin", itr->amount, d);
packet->setArrayDataByName("deposit_time_stamp", itr->timestamp, d);
packet->setArrayDataByName("deposit_last_coin", itr->last_amount, d);
packet->setArrayDataByName("deposit_total_status", itr->status, d);
packet->setArrayDataByName("deposit_last_status", itr->last_status, d);
d++;
}
packet->setArrayLengthByName("num_history", ph->history.size());
list<HouseHistory>::iterator hitr;
d = 0;
for (hitr = ph->history.begin(); hitr != ph->history.end(); hitr++)
{
packet->setArrayDataByName("history_name", hitr->name.c_str(), d);
packet->setArrayDataByName("history_coins", hitr->amount, d);
packet->setArrayDataByName("history_status", hitr->status, d);
packet->setArrayDataByName("history_time_stamp", hitr->timestamp, d);
packet->setArrayDataByName("history_reason", hitr->reason.c_str(), d);
packet->setArrayDataByName("history_add_flag", hitr->pos_flag, d);
d++;
}
EQ2Packet* pack = packet->serialize();
//DumpPacket(pack);
client->QueuePacket(pack);
}
safe_delete(packet);
}
void ClientPacketFunctions::SendHouseVisitWindow(Client* client, vector<PlayerHouse*> houses) {
PacketStruct* packet = configReader.getStruct("WS_DisplayVisitScreen", client->GetVersion());
if (packet) {
vector<PlayerHouse*>::iterator itr;
packet->setArrayLengthByName("num_houses", houses.size());
int16 i = 0;
for (itr = houses.begin(); itr != houses.end(); itr++) {
PlayerHouse* ph = *itr;
if (ph) {
HouseZone* hz = world.GetHouseZone(ph->house_id);
if (hz) {
packet->setArrayDataByName("house_id", ph->unique_id, i);
packet->setArrayDataByName("house_owner", ph->player_name.c_str(), i);
packet->setArrayDataByName("house_location", hz->name.c_str(), i);
packet->setArrayDataByName("house_zone", client->GetCurrentZone()->GetZoneName(), i);
if ( string(client->GetPlayer()->GetName()).compare(ph->player_name) == 0 )
packet->setArrayDataByName("access_level", 4, i);
else
packet->setArrayDataByName("access_level", 1, i);
packet->setArrayDataByName("visit_flag", 0, i); // 0 = allowed to visit, 1 = owner hasn't paid upkeep
i++;
}
}
}
client->QueuePacket(packet->serialize());
}
safe_delete(packet);
}
/*
<Struct Name="WS_DisplayVisitScreen" ClientVersion="1193" OpcodeName="OP_DisplayInnVisitScreenMsg">
<Data ElementName="num_houses" Type="int16" Size="1" />
<Data ElementName="visithouse_array" Type="Array" ArraySizeVariable="num_houses">
<Data ElementName="house_id" Type="int64" />
<Data ElementName="house_owner" Type="EQ2_16Bit_String" />
<Data ElementName="house_location" Type="EQ2_16Bit_string" />
<Data ElementName="house_zone" Type="EQ2_16Bit_String" />
<Data ElementName="access_level" Type="int8" Size="1" />
<Data ElementName="unknown3" Type="int8" Size="3" />
<Data ElementName="visit_flag" Type="int8" Size="1" />
</Data>
<Data ElementName="unknown4" Type="int32" Size="1" />
<Data ElementName="unknown5" Type="int8" Size="1" />
</Struct>
*/
void ClientPacketFunctions::SendLocalizedTextMessage(Client* client)
{
/***
-- OP_ReloadLocalizedTxtMsg --
5/26/2020 19:08:41
69.174.200.100 -> 192.168.1.1
0000: 01 FF 63 01 62 00 00 00 1C 00 49 72 6F 6E 74 6F ..c.b.....Ironto
0010: 65 73 20 45 61 73 74 20 4C 61 72 67 65 20 49 6E es East Large In
0020: 6E 20 52 6F 6F 6D 07 01 00 00 00 1C 00 49 72 6F n Room.......Iro
0030: 6E 74 6F 65 73 20 45 61 73 74 20 4C 61 72 67 65 ntoes East Large
0040: 20 49 6E 6E 20 52 6F 6F 6D 07 02 00 00 00 1C 00 Inn Room.......
0050: 49 72 6F 6E 74 6F 65 73 20 45 61 73 74 20 4C 61 Irontoes East La
0060: 72 67 65 20 49 6E 6E 20 52 6F 6F 6D 07 03 00 00 rge Inn Room....
0070: 00 1C 00 49 72 6F 6E 74 6F 65 73 20 45 61 73 74 ...Irontoes East
0080: 20 4C 61 72 67 65 20 49 6E 6E 20 52 6F 6F 6D 07 Large Inn Room.
0090: 04 00 00 00 1C 00 49 72 6F 6E 74 6F 65 73 20 45 ......Irontoes E
00A0: 61 73 74 20 4C 61 72 67 65 20 49 6E 6E 20 52 6F ast Large Inn Ro
00B0: 6F 6D 07 05 00 00 00 1C 00 49 72 6F 6E 74 6F 65 om.......Irontoe
00C0: 73 20 45 61 73 74 20 4C 61 72 67 65 20 49 6E 6E s East Large Inn
00D0: 20 52 6F 6F 6D 07 06 00 00 00 1C 00 49 72 6F 6E Room.......Iron
00E0: 74 6F 65 73 20 45 61 73 74 20 4C 61 72 67 65 20 toes East Large
00F0: 49 6E 6E 20 52 6F 6F 6D 07 07 00 00 00 19 00 51 Inn Room.......Q
0100: 65 79 6E 6F 73 20 47 75 69 6C 64 20 48 61 6C 6C eynos Guild Hall
0110: 2C 20 54 69 65 72 20 31 07 08 00 00 00 16 00 4C , Tier 1.......L
0120: 69 6F 6E 27 73 20 4D 61 6E 65 20 53 75 69 74 65 ion's Mane Suite
0130: 20 52 6F 6F 6D 07 09 00 00 00 16 00 4C 69 6F 6E Room.......Lion
0140: 27 73 20 4D 61 6E 65 20 53 75 69 74 65 20 52 6F 's Mane Suite Ro
0150: 6F 6D 07 0A 00 00 00 16 00 4C 69 6F 6E 27 73 20 om.......Lion's
0160: 4D 61 6E 65 20 53 75 69 74 65 20 52 6F 6F 6D 07 Mane Suite Room.
0170: 0B 00 00 00 16 00 4C 69 6F 6E 27 73 20 4D 61 6E ......Lion's Man
0180: 65 20 53 75 69 74 65 20 52 6F 6F 6D 07 0C 00 00 e Suite Room....
0190: 00 0E 00 32 20 4C 75 63 69 65 20 53 74 72 65 65 ...2 Lucie Stree
01A0: 74 07 0D 00 00 00 0F 00 31 37 20 54 72 61 6E 71 t.......17 Tranq
01B0: 75 69 6C 20 57 61 79 07 0E 00 00 00 0E 00 38 20 uil Way.......8
01C0: 4C 75 63 69 65 20 53 74 72 65 65 74 07 0F 00 00 Lucie Street....
01D0: 00 0F 00 31 32 20 4C 75 63 69 65 20 53 74 72 65 ...12 Lucie Stre
01E0: 65 74 07 10 00 00 00 0F 00 31 38 20 4C 75 63 69 et.......18 Luci
01F0: 65 20 53 74 72 65 65 74 07 11 00 00 00 0F 00 32 e Street.......2
0200: 30 20 4C 75 63 69 65 20 53 74 72 65 65 74 07 12 0 Lucie Street..
0210: 00 00 00 0E 00 33 20 54 72 61 6E 71 75 69 6C 20 .....3 Tranquil
0220: 57 61 79 07 13 00 00 00 0E 00 37 20 54 72 61 6E Way.......7 Tran
0230: 71 75 69 6C 20 57 61 79 07 14 00 00 00 0F 00 31 quil Way.......1
0240: 33 20 54 72 61 6E 71 75 69 6C 20 57 61 79 07 15 3 Tranquil Way..
0250: 00 00 00 0F 00 31 35 20 54 72 61 6E 71 75 69 6C .....15 Tranquil
0260: 20 57 61 79 07 16 00 00 00 19 00 51 65 79 6E 6F Way.......Qeyno
0270: 73 20 47 75 69 6C 64 20 48 61 6C 6C 2C 20 54 69 s Guild Hall, Ti
0280: 65 72 20 32 07 17 00 00 00 0F 00 38 20 45 72 6F er 2.......8 Ero
0290: 6C 6C 69 73 69 20 4C 61 6E 65 07 18 00 00 00 0F llisi Lane......
02A0: 00 35 20 45 72 6F 6C 6C 69 73 69 20 4C 61 6E 65 .5 Erollisi Lane
02B0: 07 19 00 00 00 0E 00 35 20 4B 61 72 61 6E 61 20 .......5 Karana
02C0: 43 6F 75 72 74 07 1A 00 00 00 0D 00 32 20 42 61 Court.......2 Ba
02D0: 79 6C 65 20 43 6F 75 72 74 07 1B 00 00 00 0D 00 yle Court.......
02E0: 34 20 42 61 79 6C 65 20 43 6F 75 72 74 07 1C 00 4 Bayle Court...
02F0: 00 00 16 00 4C 69 6F 6E 27 73 20 4D 61 6E 65 20 ....Lion's Mane
0300: 53 75 69 74 65 20 52 6F 6F 6D 07 1D 00 00 00 16 Suite Room......
0310: 00 4C 69 6F 6E 27 73 20 4D 61 6E 65 20 53 75 69 .Lion's Mane Sui
0320: 74 65 20 52 6F 6F 6D 07 1E 00 00 00 16 00 4C 69 te Room.......Li
0330: 6F 6E 27 73 20 4D 61 6E 65 20 53 75 69 74 65 20 on's Mane Suite
0340: 52 6F 6F 6D 07 1F 00 00 00 16 00 4C 69 6F 6E 27 Room.......Lion'
0350: 73 20 4D 61 6E 65 20 53 75 69 74 65 20 52 6F 6F s Mane Suite Roo
0360: 6D 07 20 00 00 00 0E 00 35 20 4C 75 63 69 65 20 m. .....5 Lucie
0370: 53 74 72 65 65 74 07 21 00 00 00 0F 00 32 30 20 Street.!.....20
0380: 4B 61 72 61 6E 61 20 43 6F 75 72 74 07 22 00 00 Karana Court."..
0390: 00 0E 00 39 20 4C 75 63 69 65 20 53 74 72 65 65 ...9 Lucie Stree
03A0: 74 07 23 00 00 00 0F 00 31 35 20 4C 75 63 69 65 t.#.....15 Lucie
03B0: 20 53 74 72 65 65 74 07 24 00 00 00 0F 00 31 37 Street.$.....17
03C0: 20 4C 75 63 69 65 20 53 74 72 65 65 74 07 25 00 Lucie Street.%.
03D0: 00 00 0F 00 32 31 20 4C 75 63 69 65 20 53 74 72 ....21 Lucie Str
03E0: 65 65 74 07 26 00 00 00 0E 00 36 20 4B 61 72 61 eet.&.....6 Kara
03F0: 6E 61 20 43 6F 75 72 74 07 27 00 00 00 0F 00 31 na Court.'.....1
0400: 32 20 4B 61 72 61 6E 61 20 43 6F 75 72 74 07 28 2 Karana Court.(
0410: 00 00 00 0F 00 31 34 20 4B 61 72 61 6E 61 20 43 .....14 Karana C
0420: 6F 75 72 74 07 29 00 00 00 0F 00 31 38 20 4B 61 ourt.).....18 Ka
0430: 72 61 6E 61 20 43 6F 75 72 74 07 2A 00 00 00 1E rana Court.*....
0440: 00 43 6F 6E 63 6F 72 64 69 75 6D 20 54 6F 77 65 .Concordium Towe
0450: 72 20 4D 61 67 69 63 61 6C 20 4D 61 6E 6F 72 07 r Magical Manor.
0460: 2B 00 00 00 15 00 41 72 63 61 6E 65 20 41 63 61 +.....Arcane Aca
0470: 64 65 6D 79 20 50 6F 72 74 61 6C 07 2C 00 00 00 demy Portal.,...
0480: 13 00 43 6F 75 72 74 20 6F 66 20 74 68 65 20 4D ..Court of the M
0490: 61 73 74 65 72 07 2D 00 00 00 13 00 43 69 74 79 aster.-.....City
04A0: 20 6F 66 20 4D 69 73 74 20 45 73 74 61 74 65 07 of Mist Estate.
04B0: 2E 00 00 00 10 00 44 61 72 6B 6C 69 67 68 74 20 ......Darklight
04C0: 50 61 6C 61 63 65 07 2F 00 00 00 11 00 44 65 65 Palace./.....Dee
04D0: 70 77 61 74 65 72 20 52 65 74 72 65 61 74 07 30 pwater Retreat.0
04E0: 00 00 00 24 00 44 68 61 6C 67 61 72 20 50 72 65 ...$.Dhalgar Pre
04F0: 63 69 70 69 63 65 20 6F 66 20 74 68 65 20 44 65 cipice of the De
0500: 65 70 20 50 6F 72 74 61 6C 07 31 00 00 00 12 00 ep Portal.1.....
0510: 44 69 6D 65 6E 73 69 6F 6E 61 6C 20 50 6F 63 6B Dimensional Pock
0520: 65 74 07 32 00 00 00 0B 00 44 6F 6A 6F 20 50 6F et.2.....Dojo Po
0530: 72 74 61 6C 07 33 00 00 00 21 00 45 6C 61 62 6F rtal.3...!.Elabo
0540: 72 61 74 65 20 45 73 74 61 74 65 20 6F 66 20 55 rate Estate of U
0550: 6E 72 65 73 74 20 50 6F 72 74 61 6C 07 34 00 00 nrest Portal.4..
0560: 00 11 00 45 74 68 65 72 6E 65 72 65 20 45 6E 63 ...Ethernere Enc
0570: 6C 61 76 65 07 35 00 00 00 10 00 45 76 65 72 66 lave.5.....Everf
0580: 72 6F 73 74 20 50 6F 72 74 61 6C 07 36 00 00 00 rost Portal.6...
0590: 16 00 46 65 61 72 66 75 6C 20 52 65 74 72 65 61 ..Fearful Retrea
05A0: 74 20 50 6F 72 74 61 6C 07 37 00 00 00 0F 00 46 t Portal.7.....F
05B0: 65 6C 77 69 74 68 65 20 50 6F 72 74 61 6C 07 38 elwithe Portal.8
05C0: 00 00 00 10 00 46 72 65 65 62 6C 6F 6F 64 20 50 .....Freeblood P
05D0: 6F 72 74 61 6C 07 39 00 00 00 0C 00 46 72 69 67 ortal.9.....Frig
05E0: 68 74 20 4D 61 6E 6F 72 07 3A 00 00 00 11 00 47 ht Manor.:.....G
05F0: 61 6C 6C 65 6F 6E 20 6F 66 20 44 72 65 61 6D 73 alleon of Dreams
0600: 07 3B 00 00 00 14 00 48 61 6C 6C 20 6F 66 20 74 .;.....Hall of t
0610: 68 65 20 43 68 61 6D 70 69 6F 6E 07 3C 00 00 00 he Champion.<...
0620: 10 00 48 75 61 20 4D 65 69 6E 20 52 65 74 72 65 ..Hua Mein Retre
0630: 61 74 07 3D 00 00 00 1C 00 49 73 6C 65 20 6F 66 at.=.....Isle of
0640: 20 52 65 66 75 67 65 20 50 72 65 73 74 69 67 65 Refuge Prestige
0650: 20 48 6F 6D 65 07 3E 00 00 00 0F 00 4B 65 72 61 Home.>.....Kera
0660: 66 79 72 6D 27 73 20 4C 61 69 72 07 3F 00 00 00 fyrm's Lair.?...
0670: 0E 00 4B 72 6F 6D 7A 65 6B 20 50 6F 72 74 61 6C ..Kromzek Portal
0680: 07 40 00 00 00 10 00 4C 61 76 61 73 74 6F 72 6D .@.....Lavastorm
0690: 20 50 6F 72 74 61 6C 07 41 00 00 00 0E 00 4C 69 Portal.A.....Li
06A0: 62 72 61 72 79 20 50 6F 72 74 61 6C 07 42 00 00 brary Portal.B..
06B0: 00 0B 00 4D 61 72 61 20 45 73 74 61 74 65 07 43 ...Mara Estate.C
06C0: 00 00 00 21 00 4D 61 6A 27 44 75 6C 20 41 73 74 ...!.Maj'Dul Ast
06D0: 72 6F 6E 6F 6D 65 72 27 73 20 54 6F 77 65 72 20 ronomer's Tower
06E0: 50 6F 72 74 61 6C 07 44 00 00 00 14 00 4D 61 6A Portal.D.....Maj
06F0: 27 44 75 6C 20 53 75 69 74 65 20 50 6F 72 74 61 'Dul Suite Porta
0700: 6C 07 45 00 00 00 17 00 4D 69 73 74 6D 6F 6F 72 l.E.....Mistmoor
0710: 65 20 43 72 61 67 73 20 45 73 74 61 74 65 73 07 e Crags Estates.
0720: 46 00 00 00 0D 00 4F 61 6B 6D 79 73 74 20 47 6C F.....Oakmyst Gl
0730: 61 64 65 07 47 00 00 00 12 00 4F 70 65 72 61 20 ade.G.....Opera
0740: 48 6F 75 73 65 20 50 6F 72 74 61 6C 07 48 00 00 House Portal.H..
0750: 00 16 00 50 65 72 73 6F 6E 61 6C 20 47 72 6F 74 ...Personal Grot
0760: 74 6F 20 50 6F 72 74 61 6C 07 49 00 00 00 17 00 to Portal.I.....
0770: 52 75 6D 20 52 75 6E 6E 65 72 73 20 43 6F 76 65 Rum Runners Cove
0780: 20 50 6F 72 74 61 6C 07 4A 00 00 00 12 00 50 6C Portal.J.....Pl
0790: 61 6E 65 74 61 72 69 75 6D 20 50 6F 72 74 61 6C anetarium Portal
07A0: 07 4B 00 00 00 14 00 52 65 73 65 61 72 63 68 65 .K.....Researche
07B0: 72 27 73 20 53 61 6E 63 74 75 6D 07 4C 00 00 00 r's Sanctum.L...
07C0: 1E 00 52 65 73 69 64 65 6E 63 65 20 6F 66 20 74 ..Residence of t
07D0: 68 65 20 42 6C 61 64 65 73 20 50 6F 72 74 61 6C he Blades Portal
07E0: 07 4D 00 00 00 16 00 53 61 6E 63 74 75 73 20 53 .M.....Sanctus S
07F0: 65 72 75 20 50 72 6F 6D 65 6E 61 64 65 07 4E 00 eru Promenade.N.
0800: 00 00 22 00 53 61 6E 74 61 20 47 6C 75 67 27 73 ..".Santa Glug's
0810: 20 43 68 65 65 72 66 75 6C 20 48 6F 6C 69 64 61 Cheerful Holida
0820: 79 20 48 6F 6D 65 07 4F 00 00 00 17 00 53 65 63 y Home.O.....Sec
0830: 6C 75 64 65 64 20 53 61 6E 63 74 75 6D 20 50 6F luded Sanctum Po
0840: 72 74 61 6C 07 50 00 00 00 18 00 53 6B 79 62 6C rtal.P.....Skybl
0850: 61 64 65 20 53 6B 69 66 66 20 4C 61 75 6E 63 68 ade Skiff Launch
0860: 70 61 64 07 51 00 00 00 0E 00 53 6E 6F 77 79 20 pad.Q.....Snowy
0870: 44 77 65 6C 6C 69 6E 67 07 52 00 00 00 1D 00 53 Dwelling.R.....S
0880: 70 72 6F 63 6B 65 74 27 73 20 49 6E 74 65 72 6C procket's Interl
0890: 6F 63 6B 69 6E 67 20 50 6C 61 6E 65 07 53 00 00 ocking Plane.S..
08A0: 00 17 00 53 74 6F 72 6D 20 54 6F 77 65 72 20 49 ...Storm Tower I
08B0: 73 6C 65 20 50 6F 72 74 61 6C 07 54 00 00 00 21 sle Portal.T...!
08C0: 00 52 65 6C 69 63 20 54 69 6E 6B 65 72 20 50 72 .Relic Tinker Pr
08D0: 65 73 74 69 67 65 20 48 6F 6D 65 20 50 6F 72 74 estige Home Port
08E0: 61 6C 07 55 00 00 00 10 00 54 65 6E 65 62 72 6F al.U.....Tenebro
08F0: 75 73 20 50 6F 72 74 61 6C 07 56 00 00 00 10 00 us Portal.V.....
0900: 54 68 65 20 42 61 75 62 62 6C 65 73 68 69 72 65 The Baubbleshire
0910: 07 57 00 00 00 0F 00 54 69 6E 6B 65 72 65 72 27 .W.....Tinkerer'
0920: 73 20 49 73 6C 65 07 58 00 00 00 12 00 54 6F 77 s Isle.X.....Tow
0930: 65 72 20 6F 66 20 4B 6E 6F 77 6C 65 64 67 65 07 er of Knowledge.
0940: 59 00 00 00 15 00 55 6E 63 61 6E 6E 79 20 45 73 Y.....Uncanny Es
0950: 74 61 74 65 20 50 6F 72 74 61 6C 07 5A 00 00 00 tate Portal.Z...
0960: 1E 00 56 61 63 61 6E 74 20 45 73 74 61 74 65 20 ..Vacant Estate
0970: 6F 66 20 55 6E 72 65 73 74 20 50 6F 72 74 61 6C of Unrest Portal
0980: 07 5B 00 00 00 18 00 56 61 6C 65 20 6F 66 20 48 .[.....Vale of H
0990: 61 6C 66 70 69 6E 74 20 44 65 6C 69 67 68 74 07 alfpint Delight.
09A0: 5C 00 00 00 26 00 4C 69 6F 6E 27 73 20 4D 61 6E \...&.Lion's Man
09B0: 65 20 56 65 73 74 69 67 65 20 52 6F 6F 6D 20 2D e Vestige Room -
09C0: 20 4E 65 74 74 6C 65 76 69 6C 6C 65 07 5D 00 00 Nettleville.]..
09D0: 00 2C 00 4C 69 6F 6E 27 73 20 4D 61 6E 65 20 56 .,.Lion's Mane V
09E0: 65 73 74 69 67 65 20 52 6F 6F 6D 20 2D 20 53 74 estige Room - St
09F0: 61 72 63 72 65 73 74 20 43 6F 6D 6D 75 6E 65 07 arcrest Commune.
0A00: 5E 00 00 00 29 00 4C 69 6F 6E 27 73 20 4D 61 6E ^...).Lion's Man
0A10: 65 20 56 65 73 74 69 67 65 20 52 6F 6F 6D 20 2D e Vestige Room -
0A20: 20 47 72 61 79 73 74 6F 6E 65 20 59 61 72 64 07 Graystone Yard.
0A30: 5F 00 00 00 2C 00 4C 69 6F 6E 27 73 20 4D 61 6E _...,.Lion's Man
0A40: 65 20 56 65 73 74 69 67 65 20 52 6F 6F 6D 20 2D e Vestige Room -
0A50: 20 43 61 73 74 6C 65 76 69 65 77 20 48 61 6D 6C Castleview Haml
0A60: 65 74 07 60 00 00 00 2A 00 4C 69 6F 6E 27 73 20 et.`...*.Lion's
0A70: 4D 61 6E 65 20 56 65 73 74 69 67 65 20 52 6F 6F Mane Vestige Roo
0A80: 6D 20 2D 20 54 68 65 20 57 69 6C 6C 6F 77 20 57 m - The Willow W
0A90: 6F 6F 64 07 61 00 00 00 2B 00 4C 69 6F 6E 27 73 ood.a...+.Lion's
0AA0: 20 4D 61 6E 65 20 56 65 73 74 69 67 65 20 52 6F Mane Vestige Ro
0AB0: 6F 6D 20 2D 20 54 68 65 20 42 61 75 62 62 6C 65 om - The Baubble
0AC0: 73 68 69 72 65 07 62 00 00 00 FF FF FF FF shire.b.......
*/
}

5245
old/world/Items/Items.cpp Normal file

File diff suppressed because it is too large Load Diff

1298
old/world/Items/Items.h Normal file

File diff suppressed because it is too large Load Diff

1453
old/world/Items/ItemsDB.cpp Normal file

File diff suppressed because it is too large Load Diff

816
old/world/Items/Items_CoE.h Normal file
View File

@ -0,0 +1,816 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EQ2_ITEMS__
#define __EQ2_ITEMS__
#include <map>
#include <vector>
#include "../../common/types.h"
#include "../../common/DataBuffer.h"
#include "../../common/MiscFunctions.h"
#include "../Commands/Commands.h"
#include "../../common/ConfigReader.h"
using namespace std;
class MasterItemList;
class Player;
extern MasterItemList master_item_list;
#define EQ2_PRIMARY_SLOT 0
#define EQ2_SECONDARY_SLOT 1
#define EQ2_HEAD_SLOT 2
#define EQ2_CHEST_SLOT 3
#define EQ2_SHOULDERS_SLOT 4
#define EQ2_FOREARMS_SLOT 5
#define EQ2_HANDS_SLOT 6
#define EQ2_LEGS_SLOT 7
#define EQ2_FEET_SLOT 8
#define EQ2_LRING_SLOT 9
#define EQ2_RRING_SLOT 10
#define EQ2_EARS_SLOT_1 11
#define EQ2_EARS_SLOT_2 12
#define EQ2_NECK_SLOT 13
#define EQ2_LWRIST_SLOT 14
#define EQ2_RWRIST_SLOT 15
#define EQ2_RANGE_SLOT 16
#define EQ2_AMMO_SLOT 17
#define EQ2_WAIST_SLOT 18
#define EQ2_CLOAK_SLOT 19
#define EQ2_CHARM_SLOT_1 20
#define EQ2_CHARM_SLOT_2 21
#define EQ2_FOOD_SLOT 22
#define EQ2_DRINK_SLOT 23
#define EQ2_TEXTURES_SLOT 24
#define EQ2_UNKNOWN_SLOT 25
#define PRIMARY_SLOT 1
#define SECONDARY_SLOT 2
#define HEAD_SLOT 4
#define CHEST_SLOT 8
#define SHOULDERS_SLOT 16
#define FOREARMS_SLOT 32
#define HANDS_SLOT 64
#define LEGS_SLOT 128
#define FEET_SLOT 256
#define LRING_SLOT 512
#define RRING_SLOT 1024
#define EARS_SLOT_1 2048
#define EARS_SLOT_2 4096
#define NECK_SLOT 8192
#define LWRIST_SLOT 16384
#define RWRIST_SLOT 32768
#define RANGE_SLOT 65536
#define AMMO_SLOT 131072
#define WAIST_SLOT 262144
#define CLOAK_SLOT 524288
#define CHARM_SLOT_1 1048576
#define CHARM_SLOT_2 2097152
#define FOOD_SLOT 4194304
#define DRINK_SLOT 8388608
#define TEXTURES_SLOT 16777216
#define NUM_BANK_SLOTS 12
#define NUM_SHARED_BANK_SLOTS 8
#define NUM_SLOTS 25
#define NUM_INV_SLOTS 6
#define INV_SLOT1 0
#define INV_SLOT2 50
#define INV_SLOT3 100
#define INV_SLOT4 150
#define INV_SLOT5 200
#define INV_SLOT6 250
#define BANK_SLOT1 1000
#define BANK_SLOT2 1100
#define BANK_SLOT3 1200
#define BANK_SLOT4 1300
#define BANK_SLOT5 1400
#define BANK_SLOT6 1500
#define BANK_SLOT7 1600
#define BANK_SLOT8 1700
#define ATTUNED 1
#define ATTUNEABLE 2
#define ARTIFACT 4
#define LORE 8
#define TEMPORARY 16
#define NO_TRADE 32
#define NO_VALUE 64
#define NO_ZONE 128
#define NO_DESTROY 256
#define CRAFTED 512
#define GOOD_ONLY 1024
#define EVIL_ONLY 2048
#define ITEM_WIELD_TYPE_DUAL 1
#define ITEM_WIELD_TYPE_SINGLE 2
#define ITEM_WIELD_TYPE_TWO_HAND 4
#define ITEM_TYPE_NORMAL 0
#define ITEM_TYPE_WEAPON 1
#define ITEM_TYPE_RANGED 2
#define ITEM_TYPE_ARMOR 3
#define ITEM_TYPE_SHIELD 4
#define ITEM_TYPE_BAG 5
#define ITEM_TYPE_SKILL 6
#define ITEM_TYPE_RECIPE 7
#define ITEM_TYPE_FOOD 8
#define ITEM_TYPE_BAUBLE 9
#define ITEM_TYPE_HOUSE 10
#define ITEM_TYPE_THROWN 11
#define ITEM_TYPE_HOUSE_CONTAINER 12
#define ITEM_TYPE_BOOK 13
#define ITEM_TYPE_ADORNMENT 14
#define ITEM_TYPE_PATTERN 15
#define ITEM_TYPE_ARMORSET 16
#define ITEM_MENU_TYPE_GENERIC 1
#define ITEM_MENU_TYPE_EQUIP 2
#define ITEM_MENU_TYPE_BAG 4
#define ITEM_MENU_TYPE_HOUSE 8
#define ITEM_MENU_TYPE_SCRIBE 32
#define ITEM_MENU_TYPE_INVALID 128
#define ITEM_MENU_TYPE_BROKEN 512
#define ITEM_MENU_TYPE_ATTUNED 2048
#define ITEM_MENU_TYPE_ATTUNEABLE 4096
#define ITEM_MENU_TYPE_BOOK 8192
#define ITEM_MENU_TYPE_DISPLAY_CHARGES 16384
#define ITEM_MENU_TYPE_NAMEPET 65536
#define ITEM_MENU_TYPE_USE 524288
#define ITEM_MENU_TYPE_DRINK 8388608
#define ITEM_MENU_TYPE_REDEEM 536870912
#define ITEM_TAG_UNCOMMON 3 //tier tags
#define ITEM_TAG_TREASURED 4
#define ITEM_TAG_LEGENDARY 7
#define ITEM_TAG_FABLED 9
#define ITEM_TAG_MYTHICAL 12
#define ITEM_BROKER_TYPE_ANY 0xFFFFFFFF
#define ITEM_BROKER_TYPE_ADORNMENT 134217728
#define ITEM_BROKER_TYPE_AMMO 1024
#define ITEM_BROKER_TYPE_ATTUNEABLE 16384
#define ITEM_BROKER_TYPE_BAG 2048
#define ITEM_BROKER_TYPE_BAUBLE 16777216
#define ITEM_BROKER_TYPE_BOOK 128
#define ITEM_BROKER_TYPE_CHAINARMOR 2097152
#define ITEM_BROKER_TYPE_CLOAK 1073741824
#define ITEM_BROKER_TYPE_CLOTHARMOR 524288
#define ITEM_BROKER_TYPE_COLLECTABLE 67108864
#define ITEM_BROKER_TYPE_CRUSHWEAPON 4
#define ITEM_BROKER_TYPE_DRINK 131072
#define ITEM_BROKER_TYPE_FOOD 4096
#define ITEM_BROKER_TYPE_HOUSEITEM 512
#define ITEM_BROKER_TYPE_JEWELRY 262144
#define ITEM_BROKER_TYPE_LEATHERARMOR 1048576
#define ITEM_BROKER_TYPE_LORE 8192
#define ITEM_BROKER_TYPE_MISC 1
#define ITEM_BROKER_TYPE_PIERCEWEAPON 8
#define ITEM_BROKER_TYPE_PLATEARMOR 4194304
#define ITEM_BROKER_TYPE_POISON 65536
#define ITEM_BROKER_TYPE_POTION 32768
#define ITEM_BROKER_TYPE_RECIPEBOOK 8388608
#define ITEM_BROKER_TYPE_SALESDISPLAY 33554432
#define ITEM_BROKER_TYPE_SHIELD 32
#define ITEM_BROKER_TYPE_SLASHWEAPON 2
#define ITEM_BROKER_TYPE_SPELLSCROLL 64
#define ITEM_BROKER_TYPE_TINKERED 268435456
#define ITEM_BROKER_TYPE_TRADESKILL 256
#define ITEM_BROKER_SLOT_ANY 0xFFFFFFFF
#define ITEM_BROKER_SLOT_AMMO 65536
#define ITEM_BROKER_SLOT_CHARM 524288
#define ITEM_BROKER_SLOT_CHEST 32
#define ITEM_BROKER_SLOT_CLOAK 262144
#define ITEM_BROKER_SLOT_DRINK 2097152
#define ITEM_BROKER_SLOT_EARS 4096
#define ITEM_BROKER_SLOT_FEET 1024
#define ITEM_BROKER_SLOT_FOOD 1048576
#define ITEM_BROKER_SLOT_FOREARMS 128
#define ITEM_BROKER_SLOT_HANDS 256
#define ITEM_BROKER_SLOT_HEAD 16
#define ITEM_BROKER_SLOT_LEGS 512
#define ITEM_BROKER_SLOT_NECK 8192
#define ITEM_BROKER_SLOT_PRIMARY 1
#define ITEM_BROKER_SLOT_PRIMARY_2H 2
#define ITEM_BROKER_SLOT_RANGE_WEAPON 32768
#define ITEM_BROKER_SLOT_RING 2048
#define ITEM_BROKER_SLOT_SECONDARY 8
#define ITEM_BROKER_SLOT_SHOULDERS 64
#define ITEM_BROKER_SLOT_WAIST 131072
#define ITEM_BROKER_SLOT_WRIST 16384
#define ITEM_BROKER_STAT_TYPE_NONE 0
#define ITEM_BROKER_STAT_TYPE_DEF 2
#define ITEM_BROKER_STAT_TYPE_STR 4
#define ITEM_BROKER_STAT_TYPE_STA 8
#define ITEM_BROKER_STAT_TYPE_AGI 16
#define ITEM_BROKER_STAT_TYPE_WIS 32
#define ITEM_BROKER_STAT_TYPE_INT 64
#define ITEM_BROKER_STAT_TYPE_HEALTH 128
#define ITEM_BROKER_STAT_TYPE_POWER 256
#define ITEM_BROKER_STAT_TYPE_HEAT 512
#define ITEM_BROKER_STAT_TYPE_COLD 1024
#define ITEM_BROKER_STAT_TYPE_MAGIC 2048
#define ITEM_BROKER_STAT_TYPE_MENTAL 4096
#define ITEM_BROKER_STAT_TYPE_DIVINE 8192
#define ITEM_BROKER_STAT_TYPE_POISON 16384
#define ITEM_BROKER_STAT_TYPE_DISEASE 32768
#define ITEM_BROKER_STAT_TYPE_CRUSH 65536
#define ITEM_BROKER_STAT_TYPE_SLASH 131072
#define ITEM_BROKER_STAT_TYPE_PIERCE 262144
#define ITEM_BROKER_STAT_TYPE_CRITICAL 524288
#define ITEM_BROKER_STAT_TYPE_DBL_ATTACK 1048576
#define ITEM_BROKER_STAT_TYPE_ABILITY_MOD 2097152
#define ITEM_BROKER_STAT_TYPE_POTENCY 4194304
#define OVERFLOW_SLOT 0xFFFFFFFE
#define SLOT_INVALID 0xFFFF
#define ITEM_STAT_STR 0
#define ITEM_STAT_STA 1
#define ITEM_STAT_AGI 2
#define ITEM_STAT_WIS 3
#define ITEM_STAT_INT 4
#define ITEM_STAT_ADORNING 100
#define ITEM_STAT_AGGRESSION 101
#define ITEM_STAT_ARTIFICING 102
#define ITEM_STAT_ARTISTRY 103
#define ITEM_STAT_CHEMISTRY 104
#define ITEM_STAT_CRUSHING 105
#define ITEM_STAT_DEFENSE 106
#define ITEM_STAT_DEFLECTION 107
#define ITEM_STAT_DISRUPTION 108
#define ITEM_STAT_FISHING 109
#define ITEM_STAT_FLETCHING 110
#define ITEM_STAT_FOCUS 111
#define ITEM_STAT_FORESTING 112
#define ITEM_STAT_GATHERING 113
#define ITEM_STAT_METAL_SHAPING 114
#define ITEM_STAT_METALWORKING 115
#define ITEM_STAT_MINING 116
#define ITEM_STAT_MINISTRATION 117
#define ITEM_STAT_ORDINATION 118
#define ITEM_STAT_PARRY 119
#define ITEM_STAT_PIERCING 120
#define ITEM_STAT_RANGED 121
#define ITEM_STAT_SAFE_FALL 122
#define ITEM_STAT_SCRIBING 123
#define ITEM_STAT_SCULPTING 124
#define ITEM_STAT_SLASHING 125
#define ITEM_STAT_SUBJUGATION 126
#define ITEM_STAT_SWIMMING 127
#define ITEM_STAT_TAILORING 128
#define ITEM_STAT_TINKERING 129
#define ITEM_STAT_TRANSMUTING 130
#define ITEM_STAT_TRAPPING 131
#define ITEM_STAT_WEAPON_SKILLS 132
#define ITEM_STAT_VS_PHYSICAL 200
#define ITEM_STAT_VS_ELEMENTAL 201
#define ITEM_STAT_VS_NOXIOUS 202
#define ITEM_STAT_VS_ARCANE 203
//#define ITEM_STAT_VS_SLASH 200
//#define ITEM_STAT_VS_CRUSH 201
//#define ITEM_STAT_VS_PIERCE 202
//#define ITEM_STAT_VS_HEAT 203
//#define ITEM_STAT_VS_COLD 204
//#define ITEM_STAT_VS_MAGIC 205
//#define ITEM_STAT_VS_MENTAL 206
//#define ITEM_STAT_VS_DIVINE 207
//#define ITEM_STAT_VS_DISEASE 208
//#define ITEM_STAT_VS_POISON 209
//#define ITEM_STAT_VS_DROWNING 210
//#define ITEM_STAT_VS_FALLING 211
//#define ITEM_STAT_VS_PAIN 212
//#define ITEM_STAT_VS_MELEE 213
#define ITEM_STAT_DMG_SLASH 300
#define ITEM_STAT_DMG_CRUSH 301
#define ITEM_STAT_DMG_PIERCE 302
#define ITEM_STAT_DMG_HEAT 303
#define ITEM_STAT_DMG_COLD 304
#define ITEM_STAT_DMG_MAGIC 305
#define ITEM_STAT_DMG_MENTAL 306
#define ITEM_STAT_DMG_DIVINE 307
#define ITEM_STAT_DMG_DISEASE 308
#define ITEM_STAT_DMG_POISON 309
#define ITEM_STAT_DMG_DROWNING 310
#define ITEM_STAT_DMG_FALLING 311
#define ITEM_STAT_DMG_PAIN 312
#define ITEM_STAT_DMG_MELEE 313
#define ITEM_STAT_HEALTH 500
#define ITEM_STAT_POWER 501
#define ITEM_STAT_CONCENTRATION 502
#define ITEM_STAT_SAVAGERY 503
#define ITEM_STAT_HPREGEN 600
#define ITEM_STAT_MANAREGEN 601
#define ITEM_STAT_HPREGENPPT 602
#define ITEM_STAT_MPREGENPPT 603
#define ITEM_STAT_COMBATHPREGENPPT 604
#define ITEM_STAT_COMBATMPREGENPPT 605
#define ITEM_STAT_MAXHP 606
#define ITEM_STAT_MAXHPPERC 607
#define ITEM_STAT_MAXHPPERCFINAL 608
#define ITEM_STAT_SPEED 609
#define ITEM_STAT_SLOW 610
#define ITEM_STAT_MOUNTSPEED 611
#define ITEM_STAT_MOUNTAIRSPEED 612
#define ITEM_STAT_LEAPSPEED 613
#define ITEM_STAT_LEAPTIME 614
#define ITEM_STAT_GLIDEEFFICIENCY 615
#define ITEM_STAT_OFFENSIVESPEED 616
#define ITEM_STAT_ATTACKSPEED 617
#define ITEM_STAT_SPELLWEAPONATTACKSPEED 618
#define ITEM_STAT_MAXMANA 619
#define ITEM_STAT_MAXMANAPERC 620
#define ITEM_STAT_MAXATTPERC 621
#define ITEM_STAT_BLURVISION 622
#define ITEM_STAT_MAGICLEVELIMMUNITY 623
#define ITEM_STAT_HATEGAINMOD 624
#define ITEM_STAT_COMBATEXPMOD 625
#define ITEM_STAT_TRADESKILLEXPMOD 626
#define ITEM_STAT_ACHIEVEMENTEXPMOD 627
#define ITEM_STAT_SIZEMOD 628
#define ITEM_STAT_DPS 629
#define ITEM_STAT_SPELLWEAPONDPS 630
#define ITEM_STAT_STEALTH 631
#define ITEM_STAT_INVIS 632
#define ITEM_STAT_SEESTEALTH 633
#define ITEM_STAT_SEEINVIS 634
#define ITEM_STAT_EFFECTIVELEVELMOD 635
#define ITEM_STAT_RIPOSTECHANCE 636
#define ITEM_STAT_PARRYCHANCE 637
#define ITEM_STAT_DODGECHANCE 638
#define ITEM_STAT_AEAUTOATTACKCHANCE 639
#define ITEM_STAT_SPELLWEAPONAEAUTOATTACKCHANCE 640
#define ITEM_STAT_DOUBLEATTACKCHANCE 641
#define ITEM_STAT_PVPDOUBLEATTACKCHANCE 642
#define ITEM_STAT_SPELLWEAPONAEAUTOATTACKCHANCE 643
#define ITEM_STAT_PVPSPELLWEAPONDOUBLEATTACKCHANCE 644
#define ITEM_STAT_SPELLDOUBLEATTACKCHANCE 645
#define ITEM_STAT_PVPSPELLDOUBLEATTACKCHANCE 646
#define ITEM_STAT_FLURRY 647
#define ITEM_STAT_SPELLWEAPONFLURRY 648
#define ITEM_STAT_MELEEDAMAGEMULTIPLIER 649
#define ITEM_STAT_EXTRAHARVESTCHANCE 650
#define ITEM_STAT_EXTRASHIELDBLOCKCHANCE 651
#define ITEM_STAT_ITEMHPREGENPPT 652
#define ITEM_STAT_ITEMPPREGENPPT 653
#define ITEM_STAT_MELEECRITCHANCE 654
#define ITEM_STAT_CRITAVOIDANCE 655
#define ITEM_STAT_BENEFICIALCRITCHANCE 656
#define ITEM_STAT_CRITBONUS 657
#define ITEM_STAT_PVPCRITBONUS 658
#define ITEM_STAT_BASEMODIFIER 659
#define ITEM_STAT_PVPBASEMODIFIER 660
#define ITEM_STAT_UNCONSCIOUSHPMOD 661
#define ITEM_STAT_SPELLTIMEREUSEPCT 662
#define ITEM_STAT_SPELLTIMERECOVERYPCT 663
#define ITEM_STAT_SPELLTIMECASTPCT 664
#define ITEM_STAT_SPELLTIMEREUSESPELLONLY 665
#define ITEM_STAT_MELEEWEAPONRANGE 666
#define ITEM_STAT_RANGEDWEAPONRANGE 667
#define ITEM_STAT_FALLINGDAMAGEREDUCTION 668
#define ITEM_STAT_RIPOSTEDAMAGE 669
#define ITEM_STAT_MINIMUMDEFLECTIONCHANCE 670
#define ITEM_STAT_MOVEMENTWEAVE 671
#define ITEM_STAT_COMBATHPREGEN 672
#define ITEM_STAT_COMBATMANAREGEN 673
#define ITEM_STAT_CONTESTSPEEDBOOST 674
#define ITEM_STAT_TRACKINGAVOIDANCE 675
#define ITEM_STAT_STEALTHINVISSPEEDMOD 676
#define ITEM_STAT_LOOT_COIN 677
#define ITEM_STAT_ARMORMITIGATIONINCREASE 678
#define ITEM_STAT_AMMOCONSERVATION 679
#define ITEM_STAT_STRIKETHROUGH 680
#define ITEM_STAT_STATUSBONUS 681
#define ITEM_STAT_ACCURACY 682
#define ITEM_STAT_COUNTERSTRIKE 683
#define ITEM_STAT_SHIELDBASH 684
#define ITEM_STAT_WEAPONDAMAGEBONUS 685
#define ITEM_STAT_WEAPONDAMAGEBONUSMELEEONLY 686
#define ITEM_STAT_ADDITIONALRIPOSTECHANCE 687
#define ITEM_STAT_CRITICALMITIGATION 688
#define ITEM_STAT_PVPTOUGHNESS 689
#define ITEM_STAT_PVPLETHALITY 690
#define ITEM_STAT_STAMINABONUS 691
#define ITEM_STAT_WISDOMMITBONUS 692
#define ITEM_STAT_HEALRECEIVE 693
#define ITEM_STAT_HEALRECEIVEPERC 694
#define ITEM_STAT_PVPCRITICALMITIGATION 695
#define ITEM_STAT_BASEAVOIDANCEBONUS 696
#define ITEM_STAT_INCOMBATSAVAGERYREGEN 697
#define ITEM_STAT_OUTOFCOMBATSAVAGERYREGEN 698
#define ITEM_STAT_SAVAGERYREGEN 699
#define ITEM_STAT_SAVAGERYGAINMOD 6100
#define ITEM_STAT_MAXSAVAGERYLEVEL 6101
#define ITEM_STAT_SPELL_DAMAGE 700
#define ITEM_STAT_HEAL_AMOUNT 701
#define ITEM_STAT_SPELL_AND_HEAL 702
#define ITEM_STAT_COMBAT_ART_DAMAGE 703
#define ITEM_STAT_SPELL_AND_COMBAT_ART_DAMAGE 704
#define ITEM_STAT_TAUNT_AMOUNT 705
#define ITEM_STAT_TAUNT_AND_COMBAT_ART_DAMAGE 706
#define ITEM_STAT_ABILITY_MODIFIER 707
#pragma pack(1)
struct ItemStatsValues{
sint16 str;
sint16 sta;
sint16 agi;
sint16 wis;
sint16 int_;
sint16 vs_slash;
sint16 vs_crush;
sint16 vs_pierce;
sint16 vs_heat;
sint16 vs_cold;
sint16 vs_magic;
sint16 vs_mental;
sint16 vs_divine;
sint16 vs_disease;
sint16 vs_poison;
sint16 health;
sint16 power;
sint8 concentration;
};
struct ItemCore{
int32 item_id;
sint32 soe_id;
int32 bag_id;
sint32 inv_slot_id;
sint16 slot_id;
int8 index;
int16 icon;
int16 count;
int8 tier;
int8 num_slots;
int32 unique_id;
int8 num_free_slots;
int16 recommended_level;
bool item_locked;
};
#pragma pack()
struct ItemStat{
string stat_name;
int8 stat_type;
sint16 stat_subtype;
int16 stat_type_combined;
float value;
};
struct ItemLevelOverride{
int8 adventure_class;
int8 tradeskill_class;
int16 level;
};
struct ItemClass{
int8 adventure_class;
int8 tradeskill_class;
int16 level;
};
struct ItemAppearance{
int16 type;
int8 red;
int8 green;
int8 blue;
int8 highlight_red;
int8 highlight_green;
int8 highlight_blue;
};
class PlayerItemList;
class Item{
public:
#pragma pack(1)
struct ItemStatString{
EQ2_8BitString stat_string;
};
struct Generic_Info{
int8 show_name;
int8 creator_flag;
int16 item_flags;
int8 condition;
int32 weight; // num/10
int32 skill_req1;
int32 skill_req2;
int16 skill_min;
int8 item_type; //0=normal, 1=weapon, 2=range, 3=armor, 4=shield, 5=container, 6=spell scroll, 7=recipe book, 8=food/drink, 9=bauble, 10=house item, 11=ammo, 12=house container
int16 appearance_id;
int8 appearance_red;
int8 appearance_green;
int8 appearance_blue;
int8 appearance_highlight_red;
int8 appearance_highlight_green;
int8 appearance_highlight_blue;
int8 collectable;
int32 offers_quest_id;
int32 part_of_quest_id;
int16 max_charges;
int8 display_charges;
int64 adventure_classes;
int64 tradeskill_classes;
int16 adventure_default_level;
int16 tradeskill_default_level;
int8 usable;
};
struct Armor_Info {
int16 mitigation_low;
int16 mitigation_high;
};
struct Weapon_Info {
int16 wield_type;
int16 damage_low1;
int16 damage_high1;
int16 damage_low2;
int16 damage_high2;
int16 damage_low3;
int16 damage_high3;
int16 delay;
float rating;
};
struct Shield_Info {
Armor_Info armor_info;
};
struct Ranged_Info {
Weapon_Info weapon_info;
int16 range_low;
int16 range_high;
};
struct Bag_Info {
int8 num_slots;
int16 weight_reduction;
};
struct Food_Info{
int8 type; //0=water, 1=food
int8 level;
float duration;
int8 satiation;
};
struct Bauble_Info{
int16 cast;
int16 recovery;
int32 duration;
float recast;
int8 display_slot_optional;
int8 display_cast_time;
int8 display_bauble_type;
float effect_radius;
int32 max_aoe_targets;
int8 display_until_cancelled;
};
struct Book_Info{
int8 language;
EQ2_16BitString author;
EQ2_16BitString title;
};
struct Skill_Info{
int32 spell_id;
int32 spell_tier;
};
struct House_Info{
int32 status_rent_reduction;
};
struct HouseContainer_Info{
int16 disallowed_types;
int16 allowed_types;
int8 num_slots;
};
struct RecipeBook_Info{
vector<string> recipes;
int8 uses;
};
struct Thrown_Info{
sint32 range;
sint32 damage_modifier;
float hit_bonus;
int32 damage_type;
};
struct ItemEffect{
EQ2_16BitString effect;
int8 percentage;
int8 subbulletflag;
};
#pragma pack()
Item();
Item(Item* in_item);
~Item();
string lowername;
string name;
string description;
int8 stack_count;
int32 sell_price;
int32 max_sell_value;
bool save_needed;
int8 weapon_type;
string adornment;
string creator;
vector<ItemStat*> item_stats;
vector<ItemStatString*> item_string_stats;
vector<ItemLevelOverride*> item_level_overrides;
vector<ItemEffect*> item_effects;
Generic_Info generic_info;
Weapon_Info* weapon_info;
Ranged_Info* ranged_info;
Armor_Info* armor_info;
Bag_Info* bag_info;
Food_Info* food_info;
Bauble_Info* bauble_info;
Book_Info* book_info;
Skill_Info* skill_info;
RecipeBook_Info* recipebook_info;
Thrown_Info* thrown_info;
vector<int8> slot_data;
ItemCore details;
int32 spell_id;
int8 spell_tier;
string item_script;
void AddEffect(string effect, int8 percentage, int8 subbulletflag);
int32 GetMaxSellValue();
void SetMaxSellValue(int32 val);
void SetItem(Item* old_item);
int16 GetOverrideLevel(int8 adventure_class, int8 tradeskill_class);
void AddLevelOverride(int8 adventure_class, int8 tradeskill_class, int16 level);
void AddLevelOverride(ItemLevelOverride* class_);
bool CheckClassLevel(int8 adventure_class, int8 tradeskill_class, int16 level);
bool CheckClass(int8 adventure_class, int8 tradeskill_class);
bool CheckLevel(int8 adventure_class, int8 tradeskill_class, int16 level);
void SetAppearance(int16 type, int8 red, int8 green, int8 blue, int8 highlight_red, int8 highlight_green, int8 highlight_blue);
void SetAppearance(ItemAppearance* appearance);
void AddStat(ItemStat* in_stat);
void AddStatString(ItemStatString* in_stat);
void AddStat(int8 type, int16 subtype, float value, char* name = 0);
void SetWeaponType(int8 type);
int8 GetWeaponType();
bool HasSlot(int8 slot, int8 slot2 = 255);
bool IsNormal();
bool IsWeapon();
bool IsArmor();
bool IsRanged();
bool IsBag();
bool IsFood();
bool IsBauble();
bool IsSkill();
bool IsHouseItem();
bool IsHouseContainer();
bool IsShield();
bool IsAdornment();
bool IsAmmo();
bool IsBook();
bool IsChainArmor();
bool IsClothArmor();
bool IsCollectable();
bool IsCloak();
bool IsCrushWeapon();
bool IsFoodFood();
bool IsFoodDrink();
bool IsJewelry();
bool IsLeatherArmor();
bool IsMisc();
bool IsPierceWeapon();
bool IsPlateArmor();
bool IsPoison();
bool IsPotion();
bool IsRecipeBook();
bool IsSalesDisplay();
bool IsSlashWeapon();
bool IsSpellScroll();
bool IsTinkered();
bool IsTradeskill();
bool IsThrown();
void SetItemScript(string name);
const char* GetItemScript();
int32 CalculateRepairCost();
void SetItemType(int8 in_type);
void serialize(PacketStruct* packet, bool show_name = false, Player* player = 0, int16 packet_type = 0, int8 subtype = 0, bool loot_item = false);
EQ2Packet* serialize(int16 version, bool show_name = false, Player* player = 0, bool include_twice = true, int16 packet_type = 0, int8 subtype = 0, bool merchant_item = false, bool loot_item = false);
PacketStruct* PrepareItem(int16 version, bool merchant_item = false, bool loot_item = false);
bool CheckFlag(int32 flag);
void AddSlot(int8 slot_id);
void SetSlots(int32 slots);
bool needs_deletion;
};
class MasterItemList{
public:
~MasterItemList();
map<int32,Item*> items;
Item* GetItem(int32 id);
Item* GetItemByName(const char *name);
ItemStatsValues* CalculateItemBonuses(int32 item_id);
ItemStatsValues* CalculateItemBonuses(Item* desc, ItemStatsValues* values = 0);
vector<Item*>* GetItems(string name, int32 itype, int32 ltype, int32 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass);
vector<Item*>* GetItems(map<string, string> criteria);
void AddItem(Item* item);
bool IsBag(int32 item_id);
void RemoveAll();
static int64 NextUniqueID();
};
class PlayerItemList {
public:
PlayerItemList();
~PlayerItemList();
// int16 number;
map<int32, Item*> indexed_items;
map<sint32, map<int16, Item*> > items;
// map< int8, Item* > inv_items;
// map< int8, Item* > bank_items;
bool SharedBankAddAllowed(Item* item);
vector<Item*>* GetItemsFromBagID(sint32 bag_id);
vector<Item*>* GetItemsInBag(Item* bag);
Item* GetBag(int8 inventory_slot, bool lock = true);
bool HasItem(int32 id, bool include_bank = false);
Item* GetItemFromIndex(int32 index);
void MoveItem(Item* item, sint32 inv_slot, int16 slot, bool erase_old = true);
bool MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 charges);
Item* GetItemFromUniqueID(int32 item_id, bool include_bank = false, bool lock = true);
Item* GetItemFromID(int32 item_id, int8 count = 0, bool include_bank = false, bool lock = true);
bool AssignItemToFreeSlot(Item* item);
int16 GetNumberOfFreeSlots();
int16 GetNumberOfItems();
bool HasFreeSlot();
bool HasFreeBagSlot();
void DestroyItem(int16 index);
Item* CanStack(Item* item, bool include_bank = false);
void RemoveItem(Item* item, bool delete_item = false);
void AddItem(Item* item);
Item* GetItem(sint32 bag_slot, int16 slot);
EQ2Packet* serialize(Player* player, int16 version);
uchar* xor_packet;
uchar* orig_packet;
map<int32, Item*>* GetAllItems();
bool HasFreeBankSlot();
int8 FindFreeBankSlot();
private:
void Stack(Item* orig_item, Item* item);
Mutex MPlayerItems;
int16 packet_count;
};
class OverFlowItemList : public PlayerItemList {
public:
bool OverFlowSlotFull();
int8 GetNextOverFlowSlot();
bool AddItem(Item* item);
Item* GetOverFlowItem();
};
class EquipmentItemList{
public:
EquipmentItemList();
EquipmentItemList(const EquipmentItemList& list);
~EquipmentItemList();
Item* items[NUM_SLOTS];
vector<Item*>* GetAllEquippedItems();
bool HasItem(int32 id);
int8 GetNumberOfItems();
Item* GetItemFromUniqueID(int32 item_id);
Item* GetItemFromItemID(int32 item_id);
void SetItem(int8 slot_id, Item* item);
void RemoveItem(int8 slot, bool delete_item = false);
Item* GetItem(int8 slot_id);
bool AddItem(int8 slot, Item* item);
bool CheckEquipSlot(Item* tmp, int8 slot);
bool CanItemBeEquippedInSlot(Item* tmp, int8 slot);
int8 GetFreeSlot(Item* tmp, int8 slot_id = 255);
ItemStatsValues* CalculateEquipmentBonuses();
EQ2Packet* serialize(int16 version);
uchar* xor_packet;
uchar* orig_packet;
private:
Mutex MEquipmentItems;
};
#endif

915
old/world/Items/Items_DoV.h Normal file
View File

@ -0,0 +1,915 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EQ2_ITEMS__
#define __EQ2_ITEMS__
#include <map>
#include <vector>
#include "../../common/types.h"
#include "../../common/DataBuffer.h"
#include "../../common/MiscFunctions.h"
#include "../Commands/Commands.h"
#include "../../common/ConfigReader.h"
using namespace std;
class MasterItemList;
class Player;
class Entity;
extern MasterItemList master_item_list;
#define EQ2_PRIMARY_SLOT 0
#define EQ2_SECONDARY_SLOT 1
#define EQ2_HEAD_SLOT 2
#define EQ2_CHEST_SLOT 3
#define EQ2_SHOULDERS_SLOT 4
#define EQ2_FOREARMS_SLOT 5
#define EQ2_HANDS_SLOT 6
#define EQ2_LEGS_SLOT 7
#define EQ2_FEET_SLOT 8
#define EQ2_LRING_SLOT 9
#define EQ2_RRING_SLOT 10
#define EQ2_EARS_SLOT_1 11
#define EQ2_EARS_SLOT_2 12
#define EQ2_NECK_SLOT 13
#define EQ2_LWRIST_SLOT 14
#define EQ2_RWRIST_SLOT 15
#define EQ2_RANGE_SLOT 16
#define EQ2_AMMO_SLOT 17
#define EQ2_WAIST_SLOT 18
#define EQ2_CLOAK_SLOT 19
#define EQ2_CHARM_SLOT_1 20
#define EQ2_CHARM_SLOT_2 21
#define EQ2_FOOD_SLOT 22
#define EQ2_DRINK_SLOT 23
#define EQ2_TEXTURES_SLOT 24
#define EQ2_HAIR_SLOT 25
#define EQ2_BEARD_SLOT 26
#define EQ2_WINGS_SLOT 27
#define EQ2_NAKED_CHEST_SLOT 28
#define EQ2_NAKED_LEGS_SLOT 29
#define EQ2_BACK_SLOT 30
#define PRIMARY_SLOT 1
#define SECONDARY_SLOT 2
#define HEAD_SLOT 4
#define CHEST_SLOT 8
#define SHOULDERS_SLOT 16
#define FOREARMS_SLOT 32
#define HANDS_SLOT 64
#define LEGS_SLOT 128
#define FEET_SLOT 256
#define LRING_SLOT 512
#define RRING_SLOT 1024
#define EARS_SLOT_1 2048
#define EARS_SLOT_2 4096
#define NECK_SLOT 8192
#define LWRIST_SLOT 16384
#define RWRIST_SLOT 32768
#define RANGE_SLOT 65536
#define AMMO_SLOT 131072
#define WAIST_SLOT 262144
#define CLOAK_SLOT 524288
#define CHARM_SLOT_1 1048576
#define CHARM_SLOT_2 2097152
#define FOOD_SLOT 4194304
#define DRINK_SLOT 8388608
#define TEXTURES_SLOT 16777216
#define HAIR_SLOT 33554432
#define BEARD_SLOT 67108864
#define WINGS_SLOT 134217728
#define NAKED_CHEST_SLOT 268435456
#define NAKED_LEGS_SLOT 536870912
#define BACK_SLOT 1073741824
#define NUM_BANK_SLOTS 12
#define NUM_SHARED_BANK_SLOTS 8
#define NUM_SLOTS 25
#define NUM_INV_SLOTS 6
#define INV_SLOT1 0
#define INV_SLOT2 50
#define INV_SLOT3 100
#define INV_SLOT4 150
#define INV_SLOT5 200
#define INV_SLOT6 250
#define BANK_SLOT1 1000
#define BANK_SLOT2 1100
#define BANK_SLOT3 1200
#define BANK_SLOT4 1300
#define BANK_SLOT5 1400
#define BANK_SLOT6 1500
#define BANK_SLOT7 1600
#define BANK_SLOT8 1700
#define ATTUNED 1
#define ATTUNEABLE 2
#define ARTIFACT 4
#define LORE 8
#define TEMPORARY 16
#define NO_TRADE 32
#define NO_VALUE 64
#define NO_ZONE 128
#define NO_DESTROY 256
#define CRAFTED 512
#define GOOD_ONLY 1024
#define EVIL_ONLY 2048
#define ITEM_WIELD_TYPE_DUAL 1
#define ITEM_WIELD_TYPE_SINGLE 2
#define ITEM_WIELD_TYPE_TWO_HAND 4
#define ITEM_TYPE_NORMAL 0
#define ITEM_TYPE_WEAPON 1
#define ITEM_TYPE_RANGED 2
#define ITEM_TYPE_ARMOR 3
#define ITEM_TYPE_SHIELD 4
#define ITEM_TYPE_BAG 5
#define ITEM_TYPE_SKILL 6
#define ITEM_TYPE_RECIPE 7
#define ITEM_TYPE_FOOD 8
#define ITEM_TYPE_BAUBLE 9
#define ITEM_TYPE_HOUSE 10
#define ITEM_TYPE_THROWN 11
#define ITEM_TYPE_HOUSE_CONTAINER 12
#define ITEM_TYPE_BOOK 13
#define ITEM_TYPE_ADORNMENT 14
#define ITEM_TYPE_PATTERN 15
#define ITEM_TYPE_ARMORSET 16
#define ITEM_MENU_TYPE_GENERIC 1
#define ITEM_MENU_TYPE_EQUIP 2
#define ITEM_MENU_TYPE_BAG 4
#define ITEM_MENU_TYPE_HOUSE 8
#define ITEM_MENU_TYPE_SCRIBE 32
#define ITEM_MENU_TYPE_INVALID 128
#define ITEM_MENU_TYPE_BROKEN 512
#define ITEM_MENU_TYPE_ATTUNED 2048
#define ITEM_MENU_TYPE_ATTUNEABLE 4096
#define ITEM_MENU_TYPE_BOOK 8192
#define ITEM_MENU_TYPE_DISPLAY_CHARGES 16384
#define ITEM_MENU_TYPE_NAMEPET 65536
#define ITEM_MENU_TYPE_CONSUME 262144
#define ITEM_MENU_TYPE_USE 524288
#define ITEM_MENU_TYPE_DRINK 8388608
#define ITEM_MENU_TYPE_REDEEM 536870912
#define ITEM_TAG_UNCOMMON 3 //tier tags
#define ITEM_TAG_TREASURED 4
#define ITEM_TAG_LEGENDARY 7
#define ITEM_TAG_FABLED 9
#define ITEM_TAG_MYTHICAL 12
#define ITEM_BROKER_TYPE_ANY 0xFFFFFFFF
#define ITEM_BROKER_TYPE_ADORNMENT 134217728
#define ITEM_BROKER_TYPE_AMMO 1024
#define ITEM_BROKER_TYPE_ATTUNEABLE 16384
#define ITEM_BROKER_TYPE_BAG 2048
#define ITEM_BROKER_TYPE_BAUBLE 16777216
#define ITEM_BROKER_TYPE_BOOK 128
#define ITEM_BROKER_TYPE_CHAINARMOR 2097152
#define ITEM_BROKER_TYPE_CLOAK 1073741824
#define ITEM_BROKER_TYPE_CLOTHARMOR 524288
#define ITEM_BROKER_TYPE_COLLECTABLE 67108864
#define ITEM_BROKER_TYPE_CRUSHWEAPON 4
#define ITEM_BROKER_TYPE_DRINK 131072
#define ITEM_BROKER_TYPE_FOOD 4096
#define ITEM_BROKER_TYPE_HOUSEITEM 512
#define ITEM_BROKER_TYPE_JEWELRY 262144
#define ITEM_BROKER_TYPE_LEATHERARMOR 1048576
#define ITEM_BROKER_TYPE_LORE 8192
#define ITEM_BROKER_TYPE_MISC 1
#define ITEM_BROKER_TYPE_PIERCEWEAPON 8
#define ITEM_BROKER_TYPE_PLATEARMOR 4194304
#define ITEM_BROKER_TYPE_POISON 65536
#define ITEM_BROKER_TYPE_POTION 32768
#define ITEM_BROKER_TYPE_RECIPEBOOK 8388608
#define ITEM_BROKER_TYPE_SALESDISPLAY 33554432
#define ITEM_BROKER_TYPE_SHIELD 32
#define ITEM_BROKER_TYPE_SLASHWEAPON 2
#define ITEM_BROKER_TYPE_SPELLSCROLL 64
#define ITEM_BROKER_TYPE_TINKERED 268435456
#define ITEM_BROKER_TYPE_TRADESKILL 256
#define ITEM_BROKER_SLOT_ANY 0xFFFFFFFF
#define ITEM_BROKER_SLOT_AMMO 65536
#define ITEM_BROKER_SLOT_CHARM 524288
#define ITEM_BROKER_SLOT_CHEST 32
#define ITEM_BROKER_SLOT_CLOAK 262144
#define ITEM_BROKER_SLOT_DRINK 2097152
#define ITEM_BROKER_SLOT_EARS 4096
#define ITEM_BROKER_SLOT_FEET 1024
#define ITEM_BROKER_SLOT_FOOD 1048576
#define ITEM_BROKER_SLOT_FOREARMS 128
#define ITEM_BROKER_SLOT_HANDS 256
#define ITEM_BROKER_SLOT_HEAD 16
#define ITEM_BROKER_SLOT_LEGS 512
#define ITEM_BROKER_SLOT_NECK 8192
#define ITEM_BROKER_SLOT_PRIMARY 1
#define ITEM_BROKER_SLOT_PRIMARY_2H 2
#define ITEM_BROKER_SLOT_RANGE_WEAPON 32768
#define ITEM_BROKER_SLOT_RING 2048
#define ITEM_BROKER_SLOT_SECONDARY 8
#define ITEM_BROKER_SLOT_SHOULDERS 64
#define ITEM_BROKER_SLOT_WAIST 131072
#define ITEM_BROKER_SLOT_WRIST 16384
#define ITEM_BROKER_STAT_TYPE_NONE 0
#define ITEM_BROKER_STAT_TYPE_DEF 2
#define ITEM_BROKER_STAT_TYPE_STR 4
#define ITEM_BROKER_STAT_TYPE_STA 8
#define ITEM_BROKER_STAT_TYPE_AGI 16
#define ITEM_BROKER_STAT_TYPE_WIS 32
#define ITEM_BROKER_STAT_TYPE_INT 64
#define ITEM_BROKER_STAT_TYPE_HEALTH 128
#define ITEM_BROKER_STAT_TYPE_POWER 256
#define ITEM_BROKER_STAT_TYPE_HEAT 512
#define ITEM_BROKER_STAT_TYPE_COLD 1024
#define ITEM_BROKER_STAT_TYPE_MAGIC 2048
#define ITEM_BROKER_STAT_TYPE_MENTAL 4096
#define ITEM_BROKER_STAT_TYPE_DIVINE 8192
#define ITEM_BROKER_STAT_TYPE_POISON 16384
#define ITEM_BROKER_STAT_TYPE_DISEASE 32768
#define ITEM_BROKER_STAT_TYPE_CRUSH 65536
#define ITEM_BROKER_STAT_TYPE_SLASH 131072
#define ITEM_BROKER_STAT_TYPE_PIERCE 262144
#define ITEM_BROKER_STAT_TYPE_CRITICAL 524288
#define ITEM_BROKER_STAT_TYPE_DBL_ATTACK 1048576
#define ITEM_BROKER_STAT_TYPE_ABILITY_MOD 2097152
#define ITEM_BROKER_STAT_TYPE_POTENCY 4194304
#define OVERFLOW_SLOT 0xFFFFFFFE
#define SLOT_INVALID 0xFFFF
#define ITEM_STAT_STR 0
#define ITEM_STAT_STA 1
#define ITEM_STAT_AGI 2
#define ITEM_STAT_WIS 3
#define ITEM_STAT_INT 4
#define ITEM_STAT_ADORNING 100
#define ITEM_STAT_AGGRESSION 101
#define ITEM_STAT_ARTIFICING 102
#define ITEM_STAT_ARTISTRY 103
#define ITEM_STAT_CHEMISTRY 104
#define ITEM_STAT_CRUSHING 105
#define ITEM_STAT_DEFENSE 106
#define ITEM_STAT_DEFLECTION 107
#define ITEM_STAT_DISRUPTION 108
#define ITEM_STAT_FISHING 109
#define ITEM_STAT_FLETCHING 110
#define ITEM_STAT_FOCUS 111
#define ITEM_STAT_FORESTING 112
#define ITEM_STAT_GATHERING 113
#define ITEM_STAT_METAL_SHAPING 114
#define ITEM_STAT_METALWORKING 115
#define ITEM_STAT_MINING 116
#define ITEM_STAT_MINISTRATION 117
#define ITEM_STAT_ORDINATION 118
#define ITEM_STAT_PARRY 119
#define ITEM_STAT_PIERCING 120
#define ITEM_STAT_RANGED 121
#define ITEM_STAT_SAFE_FALL 122
#define ITEM_STAT_SCRIBING 123
#define ITEM_STAT_SCULPTING 124
#define ITEM_STAT_SLASHING 125
#define ITEM_STAT_SUBJUGATION 126
#define ITEM_STAT_SWIMMING 127
#define ITEM_STAT_TAILORING 128
#define ITEM_STAT_TINKERING 129
#define ITEM_STAT_TRANSMUTING 130
#define ITEM_STAT_TRAPPING 131
#define ITEM_STAT_WEAPON_SKILLS 132
#define ITEM_STAT_VS_PHYSICAL 200
#define ITEM_STAT_VS_HEAT 201 //elemental
#define ITEM_STAT_VS_POISON 202 //noxious
#define ITEM_STAT_VS_MAGIC 203 //arcane
#define ITEM_STAT_VS_DROWNING 210
#define ITEM_STAT_VS_FALLING 211
#define ITEM_STAT_VS_PAIN 212
#define ITEM_STAT_VS_MELEE 213
#define ITEM_STAT_VS_SLASH 204
#define ITEM_STAT_VS_CRUSH 205
#define ITEM_STAT_VS_PIERCE 206
//#define ITEM_STAT_VS_HEAT 203 //just so no build error
#define ITEM_STAT_VS_COLD 207
//#define ITEM_STAT_VS_MAGIC 205 //just so no build error
#define ITEM_STAT_VS_MENTAL 208
#define ITEM_STAT_VS_DIVINE 209
#define ITEM_STAT_VS_DISEASE 214
//#define ITEM_STAT_VS_POISON 209 //just so no build error
//#define ITEM_STAT_VS_DROWNING 210 //just so no build error
//#define ITEM_STAT_VS_FALLING 211 //just so no build error
//#define ITEM_STAT_VS_PAIN 212 //just so no build error
//#define ITEM_STAT_VS_MELEE 213 //just so no build error
#define ITEM_STAT_DMG_SLASH 300
#define ITEM_STAT_DMG_CRUSH 301
#define ITEM_STAT_DMG_PIERCE 302
#define ITEM_STAT_DMG_HEAT 303
#define ITEM_STAT_DMG_COLD 304
#define ITEM_STAT_DMG_MAGIC 305
#define ITEM_STAT_DMG_MENTAL 306
#define ITEM_STAT_DMG_DIVINE 307
#define ITEM_STAT_DMG_DISEASE 308
#define ITEM_STAT_DMG_POISON 309
#define ITEM_STAT_DMG_DROWNING 310
#define ITEM_STAT_DMG_FALLING 311
#define ITEM_STAT_DMG_PAIN 312
#define ITEM_STAT_DMG_MELEE 313
#define ITEM_STAT_HEALTH 500
#define ITEM_STAT_POWER 501
#define ITEM_STAT_CONCENTRATION 502
#define ITEM_STAT_HPREGEN 600
#define ITEM_STAT_MANAREGEN 601
#define ITEM_STAT_HPREGENPPT 602
#define ITEM_STAT_MPREGENPPT 603
#define ITEM_STAT_COMBATHPREGENPPT 604
#define ITEM_STAT_COMBATMPREGENPPT 605
#define ITEM_STAT_MAXHP 606
#define ITEM_STAT_MAXHPPERC 607
#define ITEM_STAT_SPEED 608
#define ITEM_STAT_SLOW 609
#define ITEM_STAT_MOUNTSPEED 610
#define ITEM_STAT_MOUNTAIRSPEED 611
#define ITEM_STAT_OFFENSIVESPEED 612
#define ITEM_STAT_ATTACKSPEED 613
#define ITEM_STAT_MAXMANA 614
#define ITEM_STAT_MAXMANAPERC 615
#define ITEM_STAT_MAXATTPERC 616
#define ITEM_STAT_BLURVISION 617
#define ITEM_STAT_MAGICLEVELIMMUNITY 618
#define ITEM_STAT_HATEGAINMOD 619
#define ITEM_STAT_COMBATEXPMOD 620
#define ITEM_STAT_TRADESKILLEXPMOD 621
#define ITEM_STAT_ACHIEVEMENTEXPMOD 622
#define ITEM_STAT_SIZEMOD 623
#define ITEM_STAT_DPS 624
#define ITEM_STAT_STEALTH 625
#define ITEM_STAT_INVIS 626
#define ITEM_STAT_SEESTEALTH 627
#define ITEM_STAT_SEEINVIS 628
#define ITEM_STAT_EFFECTIVELEVELMOD 629
#define ITEM_STAT_RIPOSTECHANCE 630
#define ITEM_STAT_PARRYCHANCE 631
#define ITEM_STAT_DODGECHANCE 632
#define ITEM_STAT_AEAUTOATTACKCHANCE 633
#define ITEM_STAT_MULTIATTACKCHANCE 634
#define ITEM_STAT_SPELLMULTIATTACKCHANCE 635
#define ITEM_STAT_FLURRY 636
#define ITEM_STAT_MELEEDAMAGEMULTIPLIER 637
#define ITEM_STAT_EXTRAHARVESTCHANCE 638
#define ITEM_STAT_EXTRASHIELDBLOCKCHANCE 639
#define ITEM_STAT_ITEMHPREGENPPT 640
#define ITEM_STAT_ITEMPPREGENPPT 641
#define ITEM_STAT_MELEECRITCHANCE 642
#define ITEM_STAT_CRITAVOIDANCE 643
#define ITEM_STAT_BENEFICIALCRITCHANCE 644
#define ITEM_STAT_CRITBONUS 645
#define ITEM_STAT_POTENCY 646
#define ITEM_STAT_UNCONSCIOUSHPMOD 647
#define ITEM_STAT_ABILITYREUSESPEED 648
#define ITEM_STAT_ABILITYRECOVERYSPEED 649
#define ITEM_STAT_ABILITYCASTINGSPEED 650
#define ITEM_STAT_SPELLREUSESPEED 651
#define ITEM_STAT_MELEEWEAPONRANGE 652
#define ITEM_STAT_RANGEDWEAPONRANGE 653
#define ITEM_STAT_FALLINGDAMAGEREDUCTION 654
#define ITEM_STAT_RIPOSTEDAMAGE 655
#define ITEM_STAT_MINIMUMDEFLECTIONCHANCE 656
#define ITEM_STAT_MOVEMENTWEAVE 657
#define ITEM_STAT_COMBATHPREGEN 658
#define ITEM_STAT_COMBATMANAREGEN 659
#define ITEM_STAT_CONTESTSPEEDBOOST 660
#define ITEM_STAT_TRACKINGAVOIDANCE 661
#define ITEM_STAT_STEALTHINVISSPEEDMOD 662
#define ITEM_STAT_LOOT_COIN 663
#define ITEM_STAT_ARMORMITIGATIONINCREASE 664
#define ITEM_STAT_AMMOCONSERVATION 665
#define ITEM_STAT_STRIKETHROUGH 666
#define ITEM_STAT_STATUSBONUS 667
#define ITEM_STAT_ACCURACY 668
#define ITEM_STAT_COUNTERSTRIKE 669
#define ITEM_STAT_SHIELDBASH 670
#define ITEM_STAT_WEAPONDAMAGEBONUS 671
#define ITEM_STAT_ADDITIONALRIPOSTECHANCE 672
#define ITEM_STAT_CRITICALMITIGATION 673
#define ITEM_STAT_PVPTOUGHNESS 674
#define ITEM_STAT_STAMINABONUS 675
#define ITEM_STAT_WISDOMITBONUS 676
#define ITEM_STAT_HEALRECEIVE 677
#define ITEM_STAT_HEALRECEIVEPERC 678
#define ITEM_STAT_PVPCRITICALMITIGATION 679
#define ITEM_STAT_BASEAVOIDANCEBONUS 680
//#define ITEM_STAT_HPREGEN 600
//#define ITEM_STAT_MANAREGEN 601
//#define ITEM_STAT_HPREGENPPT 602
//#define ITEM_STAT_MPREGENPPT 603
//#define ITEM_STAT_COMBATHPREGENPPT 604
//#define ITEM_STAT_COMBATMPREGENPPT 605
//#define ITEM_STAT_MAXHP 606
//#define ITEM_STAT_MAXHPPERC 607
//#define ITEM_STAT_SPEED 608
//#define ITEM_STAT_SLOW 609
//#define ITEM_STAT_MOUNTSPEED 610
//#define ITEM_STAT_OFFENSIVESPEED 611
//#define ITEM_STAT_ATTACKSPEED 612
//#define ITEM_STAT_MAXMANA 613
//#define ITEM_STAT_MAXMANAPERC 614
//#define ITEM_STAT_MAXATTPERC 615
//#define ITEM_STAT_BLURVISION 616
//#define ITEM_STAT_MAGICLEVELIMMUNITY 617
//#define ITEM_STAT_HATEGAINMOD 618
//#define ITEM_STAT_COMBATEXPMOD 619
//#define ITEM_STAT_TRADESKILLEXPMOD 620
//#define ITEM_STAT_ACHIEVEMENTEXPMOD 621
//#define ITEM_STAT_SIZEMOD 622
//#define ITEM_STAT_UNKNOWN 623
//#define ITEM_STAT_STEALTH 624
//#define ITEM_STAT_INVIS 625
//#define ITEM_STAT_SEESTEALTH 626
//#define ITEM_STAT_SEEINVIS 627
//#define ITEM_STAT_EFFECTIVELEVELMOD 628
//#define ITEM_STAT_RIPOSTECHANCE 629
//#define ITEM_STAT_PARRYCHANCE 630
//#define ITEM_STAT_DODGECHANCE 631
//#define ITEM_STAT_AEAUTOATTACKCHANCE 632
//#define ITEM_STAT_DOUBLEATTACKCHANCE 633
//#define ITEM_STAT_RANGEDDOUBLEATTACKCHANCE 634
//#define ITEM_STAT_SPELLDOUBLEATTACKCHANCE 635
//#define ITEM_STAT_FLURRY 636
//#define ITEM_STAT_EXTRAHARVESTCHANCE 637
//#define ITEM_STAT_EXTRASHIELDBLOCKCHANCE 638
#define ITEM_STAT_DEFLECTIONCHANCE 400 //just so no build error
//#define ITEM_STAT_ITEMHPREGENPPT 640
//#define ITEM_STAT_ITEMPPREGENPPT 641
//#define ITEM_STAT_MELEECRITCHANCE 642
//#define ITEM_STAT_RANGEDCRITCHANCE 643
//#define ITEM_STAT_DMGSPELLCRITCHANCE 644
//#define ITEM_STAT_HEALSPELLCRITCHANCE 645
//#define ITEM_STAT_MELEECRITBONUS 646
//#define ITEM_STAT_RANGEDCRITBONUS 647
//#define ITEM_STAT_DMGSPELLCRITBONUS 648
//#define ITEM_STAT_HEALSPELLCRITBONUS 649
//#define ITEM_STAT_UNCONSCIOUSHPMOD 650
//#define ITEM_STAT_SPELLTIMEREUSEPCT 651
//#define ITEM_STAT_SPELLTIMERECOVERYPCT 652
//#define ITEM_STAT_SPELLTIMECASTPCT 653
//#define ITEM_STAT_MELEEWEAPONRANGE 654
//#define ITEM_STAT_RANGEDWEAPONRANGE 655
//#define ITEM_STAT_FALLINGDAMAGEREDUCTION 656
//#define ITEM_STAT_SHIELDEFFECTIVENESS 657
//#define ITEM_STAT_RIPOSTEDAMAGE 658
//#define ITEM_STAT_MINIMUMDEFLECTIONCHANCE 659
//#define ITEM_STAT_MOVEMENTWEAVE 660
//#define ITEM_STAT_COMBATHPREGEN 661
//#define ITEM_STAT_COMBATMANAREGEN 662
//#define ITEM_STAT_CONTESTSPEEDBOOST 663
//#define ITEM_STAT_TRACKINGAVOIDANCE 664
//#define ITEM_STAT_STEALTHINVISSPEEDMOD 665
//#define ITEM_STAT_LOOT_COIN 666
//#define ITEM_STAT_ARMORMITIGATIONINCREASE 667
//#define ITEM_STAT_AMMOCONSERVATION 668
//#define ITEM_STAT_STRIKETHROUGH 669
//#define ITEM_STAT_STATUSBONUS 670
//#define ITEM_STAT_ACCURACY 671
//#define ITEM_STAT_COUNTERSTRIKE 672
//#define ITEM_STAT_SHIELDBASH 673
//#define ITEM_STAT_WEAPONDAMAGEBONUS 674
//#define ITEM_STAT_ADDITIONALRIPOSTECHANCE 675
//#define ITEM_STAT_CRITICALMITIGATION 676
//#define ITEM_STAT_COMBATARTDAMAGE 677
//#define ITEM_STAT_SPELLDAMAGE 678
//#define ITEM_STAT_HEALAMOUNT 679
//#define ITEM_STAT_TAUNTAMOUNT 680
#define ITEM_STAT_SPELL_DAMAGE 700
#define ITEM_STAT_HEAL_AMOUNT 701
#define ITEM_STAT_SPELL_AND_HEAL 702
#define ITEM_STAT_COMBAT_ART_DAMAGE 703
#define ITEM_STAT_SPELL_AND_COMBAT_ART_DAMAGE 704
#define ITEM_STAT_TAUNT_AMOUNT 705
#define ITEM_STAT_TAUNT_AND_COMBAT_ART_DAMAGE 706
#define ITEM_STAT_ABILITY_MODIFIER 707
#pragma pack(1)
struct ItemStatsValues{
sint16 str;
sint16 sta;
sint16 agi;
sint16 wis;
sint16 int_;
sint16 vs_slash;
sint16 vs_crush;
sint16 vs_pierce;
sint16 vs_heat;
sint16 vs_cold;
sint16 vs_magic;
sint16 vs_mental;
sint16 vs_divine;
sint16 vs_disease;
sint16 vs_poison;
sint16 health;
sint16 power;
sint8 concentration;
sint16 ability_modifier;
sint16 criticalmitigation;
sint16 extrashieldblockchance;
sint16 beneficialcritchance;
sint16 critbonus;
sint16 potency;
sint16 hategainmod;
sint16 abilityreusespeed;
sint16 abilitycastingspeed;
sint16 abilityrecoveryspeed;
sint16 spellreusespeed;
sint16 spellmultiattackchance;
sint16 dps;
sint16 attackspeed;
sint16 multiattackchance;
sint16 aeautoattackchance;
sint16 strikethrough;
sint16 accuracy;
sint16 offensivespeed;
};
struct ItemCore{
int32 item_id;
sint32 soe_id;
int32 bag_id;
sint32 inv_slot_id;
sint16 slot_id;
int8 index;
int16 icon;
int16 count;
int8 tier;
int8 num_slots;
int32 unique_id;
int8 num_free_slots;
int16 recommended_level;
bool item_locked;
};
#pragma pack()
struct ItemStat{
string stat_name;
int8 stat_type;
sint16 stat_subtype;
int16 stat_type_combined;
float value;
};
struct ItemLevelOverride{
int8 adventure_class;
int8 tradeskill_class;
int16 level;
};
struct ItemClass{
int8 adventure_class;
int8 tradeskill_class;
int16 level;
};
struct ItemAppearance{
int16 type;
int8 red;
int8 green;
int8 blue;
int8 highlight_red;
int8 highlight_green;
int8 highlight_blue;
};
class PlayerItemList;
class Item{
public:
#pragma pack(1)
struct ItemStatString{
EQ2_8BitString stat_string;
};
struct Generic_Info{
int8 show_name;
int8 creator_flag;
int16 item_flags;
int8 condition;
int32 weight; // num/10
int32 skill_req1;
int32 skill_req2;
int16 skill_min;
int8 item_type; //0=normal, 1=weapon, 2=range, 3=armor, 4=shield, 5=bag, 6=scroll, 7=recipe, 8=food, 9=bauble, 10=house item, 11=thrown, 12=house container, 13=adormnet, 14=??, 16=profile, 17=patter set, 18=item set, 19=book, 20=decoration, 21=dungeon maker, 22=marketplace
int16 appearance_id;
int8 appearance_red;
int8 appearance_green;
int8 appearance_blue;
int8 appearance_highlight_red;
int8 appearance_highlight_green;
int8 appearance_highlight_blue;
int8 collectable;
int32 offers_quest_id;
int32 part_of_quest_id;
int16 max_charges;
int8 display_charges;
int64 adventure_classes;
int64 tradeskill_classes;
int16 adventure_default_level;
int16 tradeskill_default_level;
int8 usable;
};
struct Armor_Info {
int16 mitigation_low;
int16 mitigation_high;
};
struct Weapon_Info {
int16 wield_type;
int16 damage_low1;
int16 damage_high1;
int16 damage_low2;
int16 damage_high2;
int16 damage_low3;
int16 damage_high3;
int16 delay;
float rating;
};
struct Shield_Info {
Armor_Info armor_info;
};
struct Ranged_Info {
Weapon_Info weapon_info;
int16 range_low;
int16 range_high;
};
struct Bag_Info {
int8 num_slots;
int16 weight_reduction;
};
struct Food_Info{
int8 type; //0=water, 1=food
int8 level;
float duration;
int8 satiation;
};
struct Bauble_Info{
int16 cast;
int16 recovery;
int32 duration;
float recast;
int8 display_slot_optional;
int8 display_cast_time;
int8 display_bauble_type;
float effect_radius;
int32 max_aoe_targets;
int8 display_until_cancelled;
};
struct Book_Info{
int8 language;
EQ2_16BitString author;
EQ2_16BitString title;
};
struct Skill_Info{
int32 spell_id;
int32 spell_tier;
};
struct House_Info{
int32 status_rent_reduction;
};
struct HouseContainer_Info{
int16 disallowed_types;
int16 allowed_types;
int8 num_slots;
};
struct RecipeBook_Info{
vector<string> recipes;
int8 uses;
};
struct Thrown_Info{
sint32 range;
sint32 damage_modifier;
float hit_bonus;
int32 damage_type;
};
struct ItemEffect{
EQ2_16BitString effect;
int8 percentage;
int8 subbulletflag;
};
#pragma pack()
Item();
Item(Item* in_item);
~Item();
string lowername;
string name;
string description;
int8 stack_count;
int32 sell_price;
int32 max_sell_value;
bool save_needed;
int8 weapon_type;
string adornment;
string creator;
vector<ItemStat*> item_stats;
vector<ItemStatString*> item_string_stats;
vector<ItemLevelOverride*> item_level_overrides;
vector<ItemEffect*> item_effects;
Generic_Info generic_info;
Weapon_Info* weapon_info;
Ranged_Info* ranged_info;
Armor_Info* armor_info;
Bag_Info* bag_info;
Food_Info* food_info;
Bauble_Info* bauble_info;
Book_Info* book_info;
Skill_Info* skill_info;
RecipeBook_Info* recipebook_info;
Thrown_Info* thrown_info;
vector<int8> slot_data;
ItemCore details;
int32 spell_id;
int8 spell_tier;
string item_script;
void AddEffect(string effect, int8 percentage, int8 subbulletflag);
int32 GetMaxSellValue();
void SetMaxSellValue(int32 val);
void SetItem(Item* old_item);
int16 GetOverrideLevel(int8 adventure_class, int8 tradeskill_class);
void AddLevelOverride(int8 adventure_class, int8 tradeskill_class, int16 level);
void AddLevelOverride(ItemLevelOverride* class_);
bool CheckClassLevel(int8 adventure_class, int8 tradeskill_class, int16 level);
bool CheckClass(int8 adventure_class, int8 tradeskill_class);
bool CheckLevel(int8 adventure_class, int8 tradeskill_class, int16 level);
void SetAppearance(int16 type, int8 red, int8 green, int8 blue, int8 highlight_red, int8 highlight_green, int8 highlight_blue);
void SetAppearance(ItemAppearance* appearance);
void AddStat(ItemStat* in_stat);
void AddStatString(ItemStatString* in_stat);
void AddStat(int8 type, int16 subtype, float value, char* name = 0);
void SetWeaponType(int8 type);
int8 GetWeaponType();
bool HasSlot(int8 slot, int8 slot2 = 255);
bool IsNormal();
bool IsWeapon();
bool IsArmor();
bool IsRanged();
bool IsBag();
bool IsFood();
bool IsBauble();
bool IsSkill();
bool IsHouseItem();
bool IsHouseContainer();
bool IsShield();
bool IsAdornment();
bool IsAmmo();
bool IsBook();
bool IsChainArmor();
bool IsClothArmor();
bool IsCollectable();
bool IsCloak();
bool IsCrushWeapon();
bool IsFoodFood();
bool IsFoodDrink();
bool IsJewelry();
bool IsLeatherArmor();
bool IsMisc();
bool IsPierceWeapon();
bool IsPlateArmor();
bool IsPoison();
bool IsPotion();
bool IsRecipeBook();
bool IsSalesDisplay();
bool IsSlashWeapon();
bool IsSpellScroll();
bool IsTinkered();
bool IsTradeskill();
bool IsThrown();
void SetItemScript(string name);
const char* GetItemScript();
int32 CalculateRepairCost();
void SetItemType(int8 in_type);
void serialize(PacketStruct* packet, bool show_name = false, Player* player = 0, int16 packet_type = 0, int8 subtype = 0, bool loot_item = false);
EQ2Packet* serialize(int16 version, bool show_name = false, Player* player = 0, bool include_twice = true, int16 packet_type = 0, int8 subtype = 0, bool merchant_item = false, bool loot_item = false);
PacketStruct* PrepareItem(int16 version, bool merchant_item = false, bool loot_item = false);
bool CheckFlag(int32 flag);
void AddSlot(int8 slot_id);
void SetSlots(int32 slots);
bool needs_deletion;
};
class MasterItemList{
public:
~MasterItemList();
map<int32,Item*> items;
Item* GetItem(int32 id);
Item* GetItemByName(const char *name);
ItemStatsValues* CalculateItemBonuses(int32 item_id, Entity* entity = 0);
ItemStatsValues* CalculateItemBonuses(Item* desc, Entity* entity = 0, ItemStatsValues* values = 0);
vector<Item*>* GetItems(string name, int32 itype, int32 ltype, int32 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass);
vector<Item*>* GetItems(map<string, string> criteria);
void AddItem(Item* item);
bool IsBag(int32 item_id);
void RemoveAll();
static int64 NextUniqueID();
};
class PlayerItemList {
public:
PlayerItemList();
~PlayerItemList();
// int16 number;
map<int32, Item*> indexed_items;
map<sint32, map<int16, Item*> > items;
// map< int8, Item* > inv_items;
// map< int8, Item* > bank_items;
bool SharedBankAddAllowed(Item* item);
vector<Item*>* GetItemsFromBagID(sint32 bag_id);
vector<Item*>* GetItemsInBag(Item* bag);
Item* GetBag(int8 inventory_slot, bool lock = true);
bool HasItem(int32 id, bool include_bank = false);
Item* GetItemFromIndex(int32 index);
void MoveItem(Item* item, sint32 inv_slot, int16 slot, bool erase_old = true);
bool MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 charges);
Item* GetItemFromUniqueID(int32 item_id, bool include_bank = false, bool lock = true);
Item* GetItemFromID(int32 item_id, int8 count = 0, bool include_bank = false, bool lock = true);
bool AssignItemToFreeSlot(Item* item);
int16 GetNumberOfFreeSlots();
int16 GetNumberOfItems();
bool HasFreeSlot();
bool HasFreeBagSlot();
void DestroyItem(int16 index);
Item* CanStack(Item* item, bool include_bank = false);
void RemoveItem(Item* item, bool delete_item = false);
void AddItem(Item* item);
Item* GetItem(sint32 bag_slot, int16 slot);
EQ2Packet* serialize(Player* player, int16 version);
uchar* xor_packet;
uchar* orig_packet;
map<int32, Item*>* GetAllItems();
bool HasFreeBankSlot();
int8 FindFreeBankSlot();
///<summary>Get the first free slot and stor them in the provided variables</summary>
///<param name='bag_id'>Will contain the bag id of the first free spot</param>
///<param name='slot'>Will contain the slot id of the first free slot</param>
///<returns>True if a free slot was found</returns>
bool GetFirstFreeSlot(sint32* bag_id, sint16* slot);
private:
void Stack(Item* orig_item, Item* item);
Mutex MPlayerItems;
int16 packet_count;
};
class OverFlowItemList : public PlayerItemList {
public:
bool OverFlowSlotFull();
int8 GetNextOverFlowSlot();
bool AddItem(Item* item);
Item* GetOverFlowItem();
};
class EquipmentItemList{
public:
EquipmentItemList();
EquipmentItemList(const EquipmentItemList& list);
~EquipmentItemList();
Item* items[NUM_SLOTS];
vector<Item*>* GetAllEquippedItems();
bool HasItem(int32 id);
int8 GetNumberOfItems();
Item* GetItemFromUniqueID(int32 item_id);
Item* GetItemFromItemID(int32 item_id);
void SetItem(int8 slot_id, Item* item);
void RemoveItem(int8 slot, bool delete_item = false);
Item* GetItem(int8 slot_id);
bool AddItem(int8 slot, Item* item);
bool CheckEquipSlot(Item* tmp, int8 slot);
bool CanItemBeEquippedInSlot(Item* tmp, int8 slot);
int8 GetFreeSlot(Item* tmp, int8 slot_id = 255);
ItemStatsValues* CalculateEquipmentBonuses(Entity* entity = 0);
EQ2Packet* serialize(int16 version);
uchar* xor_packet;
uchar* orig_packet;
private:
Mutex MEquipmentItems;
};
#endif

211
old/world/Items/Items_ToV.h Normal file
View File

@ -0,0 +1,211 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
//Item Stat defines for ToV Client
//Stat type 6 (blue stats)
#define TOV_ITEM_STAT_HPREGEN 600
#define TOV_ITEM_STAT_MANAREGEN 601
#define TOV_ITEM_STAT_HPREGENPPT 602
#define TOV_ITEM_STAT_MPREGENPPT 603
#define TOV_ITEM_STAT_COMBATHPREGENPPT 604
#define TOV_ITEM_STAT_COMBATMPREGENPPT 605
#define TOV_ITEM_STAT_MAXHP 606
#define TOV_ITEM_STAT_MAXHPPERC 607
#define TOV_ITEM_STAT_MAXHPPERCFINAL 608
#define TOV_ITEM_STAT_SPEED 609
#define TOV_ITEM_STAT_SLOW 610
#define TOV_ITEM_STAT_MOUNTSPEED 611
#define TOV_ITEM_STAT_MOUNTAIRSPEED 612
#define TOV_ITEM_STAT_LEAPSPEED 613
#define TOV_ITEM_STAT_LEAPTIME 614
#define TOV_ITEM_STAT_GLIDEEFFICIENCY 615
#define TOV_ITEM_STAT_OFFENSIVESPEED 616
#define TOV_ITEM_STAT_ATTACKSPEED 617
#define TOV_ITEM_STAT_MAXMANA 618
#define TOV_ITEM_STAT_MAXMANAPERC 619
#define TOV_ITEM_STAT_MAXATTPERC 620
#define TOV_ITEM_STAT_BLURVISION 621
#define TOV_ITEM_STAT_MAGICLEVELIMMUNITY 622
#define TOV_ITEM_STAT_HATEGAINMOD 623
#define TOV_ITEM_STAT_COMBATEXPMOD 624
#define TOV_ITEM_STAT_TRADESKILLEXPMOD 625
#define TOV_ITEM_STAT_ACHIEVEMENTEXPMOD 626
#define TOV_ITEM_STAT_SIZEMOD 627
#define TOV_ITEM_STAT_DPS 628
#define TOV_ITEM_STAT_STEALTH 629
#define TOV_ITEM_STAT_INVIS 630
#define TOV_ITEM_STAT_SEESTEALTH 631
#define TOV_ITEM_STAT_SEEINVIS 632
#define TOV_ITEM_STAT_EFFECTIVELEVELMOD 633
#define TOV_ITEM_STAT_RIPOSTECHANCE 634
#define TOV_ITEM_STAT_PARRYCHANCE 635
#define TOV_ITEM_STAT_DODGECHANCE 636
#define TOV_ITEM_STAT_AEAUTOATTACKCHANCE 637
#define TOV_ITEM_STAT_MULTIATTACKCHANCE 638 //DOUBLEATTACKCHANCE
#define TOV_ITEM_STAT_SPELLMULTIATTACKCHANCE 639
#define TOV_ITEM_STAT_FLURRY 640
#define TOV_ITEM_STAT_MELEEDAMAGEMULTIPLIER 641
#define TOV_ITEM_STAT_EXTRAHARVESTCHANCE 642
#define TOV_ITEM_STAT_EXTRASHIELDBLOCKCHANCE 643
#define TOV_ITEM_STAT_ITEMHPREGENPPT 644
#define TOV_ITEM_STAT_ITEMPPREGENPPT 645
#define TOV_ITEM_STAT_MELEECRITCHANCE 646
#define TOV_ITEM_STAT_CRITAVOIDANCE 647
#define TOV_ITEM_STAT_BENEFICIALCRITCHANCE 648
#define TOV_ITEM_STAT_CRITBONUS 649
#define TOV_ITEM_STAT_POTENCY 650 //BASEMODIFIER
#define TOV_ITEM_STAT_UNCONSCIOUSHPMOD 651
#define TOV_ITEM_STAT_ABILITYREUSESPEED 652 //SPELLTIMEREUSEPCT
#define TOV_ITEM_STAT_ABILITYRECOVERYSPEED 653 //SPELLTIMERECOVERYPCT
#define TOV_ITEM_STAT_ABILITYCASTINGSPEED 654 //SPELLTIMECASTPCT
#define TOV_ITEM_STAT_SPELLREUSESPEED 655 //SPELLTIMEREUSESPELLONLY
#define TOV_ITEM_STAT_MELEEWEAPONRANGE 656
#define TOV_ITEM_STAT_RANGEDWEAPONRANGE 657
#define TOV_ITEM_STAT_FALLINGDAMAGEREDUCTION 658
#define TOV_ITEM_STAT_RIPOSTEDAMAGE 659
#define TOV_ITEM_STAT_MINIMUMDEFLECTIONCHANCE 660
#define TOV_ITEM_STAT_MOVEMENTWEAVE 661
#define TOV_ITEM_STAT_COMBATHPREGEN 662
#define TOV_ITEM_STAT_COMBATMANAREGEN 663
#define TOV_ITEM_STAT_CONTESTSPEEDBOOST 664
#define TOV_ITEM_STAT_TRACKINGAVOIDANCE 665
#define TOV_ITEM_STAT_STEALTHINVISSPEEDMOD 666
#define TOV_ITEM_STAT_LOOT_COIN 667
#define TOV_ITEM_STAT_ARMORMITIGATIONINCREASE 668
#define TOV_ITEM_STAT_AMMOCONSERVATION 669
#define TOV_ITEM_STAT_STRIKETHROUGH 670
#define TOV_ITEM_STAT_STATUSBONUS 671
#define TOV_ITEM_STAT_ACCURACY 672
#define TOV_ITEM_STAT_COUNTERSTRIKE 673
#define TOV_ITEM_STAT_SHIELDBASH 674
#define TOV_ITEM_STAT_WEAPONDAMAGEBONUS 675
#define TOV_ITEM_STAT_SPELLWEAPONDAMAGEBONUS 676
#define TOV_ITEM_STAT_WEAPONDAMAGEBONUSMELEEONLY 677
#define TOV_ITEM_STAT_ADDITIONALRIPOSTECHANCE 678
#define TOV_ITEM_STAT_PVPTOUGHNESS 680
#define TOV_ITEM_STAT_PVPLETHALITY 681
#define TOV_ITEM_STAT_STAMINABONUS 682
#define TOV_ITEM_STAT_WISDOMMITBONUS 683
#define TOV_ITEM_STAT_HEALRECEIVE 684
#define TOV_ITEM_STAT_HEALRECEIVEPERC 685
#define TOV_ITEM_STAT_PVPCRITICALMITIGATION 686
#define TOV_ITEM_STAT_BASEAVOIDANCEBONUS 687
#define TOV_ITEM_STAT_INCOMBATSAVAGERYREGEN 688
#define TOV_ITEM_STAT_OUTOFCOMBATSAVAGERYREGEN 689
#define TOV_ITEM_STAT_SAVAGERYREGEN 690
#define TOV_ITEM_STAT_SAVAGERYGAINMOD 691
#define TOV_ITEM_STAT_MAXSAVAGERYLEVEL 692
#define TOV_ITEM_STAT_INCOMBATDISSONANCEREGEN 693
#define TOV_ITEM_STAT_OUTOFCOMBATDISSONANCEREGEN 694
#define TOV_ITEM_STAT_DISSONANCEREGEN 695
#define TOV_ITEM_STAT_DISSONANCEGAINMOD 696
#define TOV_ITEM_STAT_AEAUTOATTACKAVOID 697
//End of stat type 6 (blue stats)
//Item stat type 5 (health,power,savagery,dissonance,concentration)
#define TOV_ITEM_STAT_HEALTH 500
#define TOV_ITEM_STAT_POWER 501
#define TOV_ITEM_STAT_CONCENTRATION 502
#define TOV_ITEM_STAT_SAVAGERY 503
#define TOV_ITEM_STAT_DISSONANCE 504
//End of stat type 5
//Item stat type 3 (damage mods)
#define TOV_ITEM_STAT_DMG_SLASH 300
#define TOV_ITEM_STAT_DMG_CRUSH 301
#define TOV_ITEM_STAT_DMG_PIERCE 302
#define TOV_ITEM_STAT_DMG_HEAT 303
#define TOV_ITEM_STAT_DMG_COLD 304
#define TOV_ITEM_STAT_DMG_MAGIC 305
#define TOV_ITEM_STAT_DMG_MENTAL 306
#define TOV_ITEM_STAT_DMG_DIVINE 307
#define TOV_ITEM_STAT_DMG_DISEASE 308
#define TOV_ITEM_STAT_DMG_POISON 309
#define TOV_ITEM_STAT_DMG_DROWNING 310
#define TOV_ITEM_STAT_DMG_FALLING 311
#define TOV_ITEM_STAT_DMG_PAIN 312
#define TOV_ITEM_STAT_DMG_MELEE 313
//End of item stat 3
#define TOV_ITEM_STAT_DEFLECTIONCHANCE 400 //just so no build error
// Other stats not listed above (not sent from the server), never send these to the client
// using type 8 as it is not used by the client as far as we know
#define TOV_ITEM_STAT_DURABILITY_MOD 800
#define TOV_ITEM_STAT_DURABILITY_ADD 801
#define TOV_ITEM_STAT_PROGRESS_ADD 802
#define TOV_ITEM_STAT_PROGRESS_MOD 803
#define TOV_ITEM_STAT_SUCCESS_MOD 804
#define TOV_ITEM_STAT_CRIT_SUCCESS_MOD 805
#define TOV_ITEM_STAT_EX_DURABILITY_MOD 806
#define TOV_ITEM_STAT_EX_DURABILITY_ADD 807
#define TOV_ITEM_STAT_EX_PROGRESS_MOD 808
#define TOV_ITEM_STAT_EX_PROGRESS_ADD 809
#define TOV_ITEM_STAT_EX_SUCCESS_MOD 810
#define TOV_ITEM_STAT_EX_CRIT_SUCCESS_MOD 811
#define TOV_ITEM_STAT_EX_CRIT_FAILURE_MOD 812
#define TOV_ITEM_STAT_RARE_HARVEST_CHANCE 813
#define TOV_ITEM_STAT_MAX_CRAFTING 814
#define TOV_ITEM_STAT_COMPONENT_REFUND 815
#define TOV_ITEM_STAT_BOUNTIFUL_HARVEST 816
#define TOV_ITEM_STAT_STR 0
#define TOV_ITEM_STAT_STA 1
#define TOV_ITEM_STAT_AGI 2
#define TOV_ITEM_STAT_WIS 3
#define TOV_ITEM_STAT_INT 4
#define TOV_ITEM_STAT_ADORNING 100
#define TOV_ITEM_STAT_AGGRESSION 101
#define TOV_ITEM_STAT_ARTIFICING 102
#define TOV_ITEM_STAT_ARTISTRY 103
#define TOV_ITEM_STAT_CHEMISTRY 104
#define TOV_ITEM_STAT_CRUSHING 105
#define TOV_ITEM_STAT_DEFENSE 106
#define TOV_ITEM_STAT_DEFLECTION 107
#define TOV_ITEM_STAT_DISRUPTION 108
#define TOV_ITEM_STAT_FISHING 109
#define TOV_ITEM_STAT_FLETCHING 110
#define TOV_ITEM_STAT_FOCUS 111
#define TOV_ITEM_STAT_FORESTING 112
#define TOV_ITEM_STAT_GATHERING 113
#define TOV_ITEM_STAT_METAL_SHAPING 114
#define TOV_ITEM_STAT_METALWORKING 115
#define TOV_ITEM_STAT_MINING 116
#define TOV_ITEM_STAT_MINISTRATION 117
#define TOV_ITEM_STAT_ORDINATION 118
#define TOV_ITEM_STAT_PARRY 119
#define TOV_ITEM_STAT_PIERCING 120
#define TOV_ITEM_STAT_RANGED 121
#define TOV_ITEM_STAT_SAFE_FALL 122
#define TOV_ITEM_STAT_SCRIBING 123
#define TOV_ITEM_STAT_SCULPTING 124
#define TOV_ITEM_STAT_SLASHING 125
#define TOV_ITEM_STAT_SUBJUGATION 126
#define TOV_ITEM_STAT_SWIMMING 127
#define TOV_ITEM_STAT_TAILORING 128
#define TOV_ITEM_STAT_TINKERING 129
#define TOV_ITEM_STAT_TRANSMUTING 130
#define TOV_ITEM_STAT_TRAPPING 131
#define TOV_ITEM_STAT_WEAPON_SKILLS 132

134
old/world/Items/Loot.cpp Normal file
View File

@ -0,0 +1,134 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Loot.h"
#include "../client.h"
#include "../../common/ConfigReader.h"
#include "../classes.h"
#include "../../common/debug.h"
#include "../zoneserver.h"
#include "../Skills.h"
#include "../classes.h"
#include "../World.h"
#include "../LuaInterface.h"
#include "../../common/Log.h"
#include "../Entity.h"
#include "../Rules/Rules.h"
extern Classes classes;
extern ConfigReader configReader;
extern MasterSkillList master_skill_list;
extern RuleManager rule_manager;
// If we want to transfer functions to this file then we should just do it, but for now we don't need all this commented code here
NPC* Entity::DropChest() {
// Check to see if treasure chests are disabled in the rules
if (rule_manager.GetZoneRule(GetZoneID(), R_World, TreasureChestDisabled)->GetBool())
return 0;
if(GetChestDropTime()) {
return 0; // this is a chest! It doesn't drop itself!
}
NPC* chest = 0;
chest = new NPC();
chest->SetAttackable(0);
chest->SetShowLevel(0);
chest->SetShowName(1);
chest->SetTargetable(1);
chest->SetLevel(GetLevel());
chest->SetChestDropTime();
chest->SetTotalHP(100);
chest->SetHP(100);
chest->SetAlive(false);
// Set the brain to a blank brain so it does nothing
chest->SetBrain(new BlankBrain(chest));
// Set the x, y, z, heading, location (grid id) to that of the dead spawn
chest->SetZone(GetZone());
// heading needs to be GetHeading() - 180 so the chest faces the proper way
chest->SetHeading(GetHeading() - 180);
// Set the primary command to loot and the secondary to disarm
chest->AddPrimaryEntityCommand("loot", rule_manager.GetZoneRule(GetZoneID(), R_Loot, LootRadius)->GetFloat(), "loot", "", 0, 0);
chest->AddSecondaryEntityCommand("Disarm", rule_manager.GetZoneRule(GetZoneID(), R_Loot, LootRadius)->GetFloat(), "Disarm", "", 0, 0);
// 32 = loot icon for the mouse
chest->SetIcon(32);
// 1 = show the right click menu
chest->SetShowCommandIcon(1);
chest->SetLootMethod(this->GetLootMethod(), this->GetLootRarity(), this->GetLootGroupID());
chest->SetLootName(this->GetName());
int8 highest_tier = 0;
vector<Item*>::iterator itr;
for (itr = ((Spawn*)this)->GetLootItems()->begin(); itr != ((Spawn*)this)->GetLootItems()->end(); ) {
if ((*itr)->details.tier >= ITEM_TAG_COMMON && !(*itr)->IsBodyDrop()) {
if ((*itr)->details.tier > highest_tier)
highest_tier = (*itr)->details.tier;
// Add the item to the chest
chest->AddLootItem((*itr)->details.item_id, (*itr)->details.count);
// Remove the item from the corpse
itr = ((Spawn*)this)->GetLootItems()->erase(itr);
}
else
itr++;
}
/*4034 = small chest | 5864 = treasure chest | 5865 = ornate treasure chest | 4015 = exquisite chest*/
if (highest_tier >= ITEM_TAG_FABLED) {
chest->SetModelType(4015);
chest->SetName("Exquisite Chest");
}
else if (highest_tier >= ITEM_TAG_LEGENDARY) {
chest->SetModelType(5865);
chest->SetName("Ornate Chest");
}
else if (highest_tier >= ITEM_TAG_TREASURED) {
chest->SetModelType(5864);
chest->SetName("Treasure Chest");
}
else if (highest_tier >= ITEM_TAG_COMMON) {
chest->SetModelType(4034);
chest->SetName("Small Chest");
}
else {
safe_delete(chest);
chest = nullptr;
}
if (chest) {
chest->SetID(Spawn::NextID());
chest->SetShowHandIcon(1);
chest->SetLocation(GetLocation());
chest->SetX(GetX());
chest->SetZ(GetZ());
((Entity*)chest)->GetInfoStruct()->set_flying_type(false);
chest->is_flying_creature = false;
if(GetMap()) {
auto loc = glm::vec3(GetX(), GetZ(), GetY());
float new_z = FindBestZ(loc, nullptr);
chest->appearance.pos.Y = new_z; // don't use SetY here can cause a loop
}
else {
chest->appearance.pos.Y = GetY();
}
}
return chest;
}

23
old/world/Items/Loot.h Normal file
View File

@ -0,0 +1,23 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EQ2_LOOT__
#define __EQ2_LOOT__
#endif

210
old/world/Items/LootDB.cpp Normal file
View File

@ -0,0 +1,210 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "../World.h"
extern World world;
void WorldDatabase::LoadLoot(ZoneServer* zone)
{
// First, clear previous loot tables...
zone->ClearLootTables();
DatabaseResult result;
int32 count = 0;
if (database_new.Select(&result, "SELECT id, name, mincoin, maxcoin, maxlootitems, lootdrop_probability, coin_probability FROM loottable")) {
LogWrite(LOOT__DEBUG, 0, "Loot", "--Loading LootTables...");
LootTable* table = 0;
// Load loottable from DB
while(result.Next()) {
int32 id = result.GetInt32Str("id");
table = new LootTable;
table->name = result.GetStringStr("name");
table->mincoin = result.GetInt32Str("mincoin");
table->maxcoin = result.GetInt32Str("maxcoin");
table->maxlootitems = result.GetInt16Str("maxlootitems");
table->lootdrop_probability = result.GetFloatStr("lootdrop_probability");
table->coin_probability = result.GetFloatStr("coin_probability");
zone->AddLootTable(id, table);
LogWrite(LOOT__DEBUG, 5, "Loot", "---Loading LootTable '%s' (id: %u)", table->name.c_str(), id);
LogWrite(LOOT__DEBUG, 5, "Loot", "---min_coin: %u, max_coin: %u, max_items: %i, prob: %.2f, coin_prob: %.2f", table->mincoin, table->maxcoin, table->maxlootitems, table->lootdrop_probability, table->coin_probability);
count++;
}
LogWrite(LOOT__DEBUG, 0, "Loot", "--Loaded %u loot table%s.", count, count == 1 ? "" : "s");
}
// Now, load Loot Drops for configured loot tables
if (database_new.Select(&result, "SELECT loot_table_id, item_id, item_charges, equip_item, probability, no_drop_quest_completed FROM lootdrop")) {
count = 0;
LogWrite(LOOT__DEBUG, 0, "Loot", "--Loading LootDrops...");
LootDrop* drop = 0;
while(result.Next()) {
int32 id = result.GetInt32Str("loot_table_id");
drop = new LootDrop;
drop->item_id = result.GetInt32Str("item_id");
drop->item_charges = result.GetInt16Str("item_charges");
drop->equip_item = (result.GetInt8Str("equip_item") == 1);
drop->probability = result.GetFloatStr("probability");
drop->no_drop_quest_completed_id = result.GetInt32Str("no_drop_quest_completed");
zone->AddLootDrop(id, drop);
LogWrite(LOOT__DEBUG, 5, "Loot", "---Loading LootDrop item_id %u (tableID: %u", drop->item_id, id);
LogWrite(LOOT__DEBUG, 5, "Loot", "---charges: %i, equip_item: %i, prob: %.2f", drop->item_charges, drop->equip_item, drop->probability);
count++;
}
LogWrite(LOOT__DEBUG, 0, "Loot", "--Loaded %u loot drop%s.", count, count == 1 ? "" : "s");
}
// Finally, load loot tables into spawns that are set to use these loot tables
if (database_new.Select(&result, "SELECT spawn_id, loottable_id FROM spawn_loot")) {
count = 0;
LogWrite(LOOT__DEBUG, 0, "Loot", "--Assigning loot table(s) to spawn(s)...");
while(result.Next()) {
int32 spawn_id = result.GetInt32Str("spawn_id");
int32 table_id = result.GetInt32Str("loottable_id");
zone->AddSpawnLootList(spawn_id, table_id);
LogWrite(LOOT__DEBUG, 5, "Loot", "---Adding loot table %u to spawn %u", table_id, spawn_id);
count++;
}
LogWrite(LOOT__DEBUG, 0, "Loot", "--Loaded %u spawn loot list%s.", count, count == 1 ? "" : "s");
}
// Load global loot lists
LoadGlobalLoot(zone);
}
void WorldDatabase::LoadGlobalLoot(ZoneServer* zone) {
LogWrite(LOOT__INFO, 0, "Loot", "-Loading Global loot data...");
DatabaseResult result;
int32 count = 0;
if (database_new.Select(&result, "SELECT type, loot_table, value1, value2, value3, value4 FROM loot_global")) {
while(result.Next()) {
const char* type = result.GetStringStr("type");
int32 table_id = result.GetInt32Str("loot_table");
if (strcmp(type, "Level") == 0) {
GlobalLoot* loot = new GlobalLoot();
loot->minLevel = result.GetInt8Str("value1");
loot->maxLevel = result.GetInt8Str("value2");
loot->table_id = table_id;
loot->loot_tier = result.GetInt32Str("value4");
if (loot->minLevel > loot->maxLevel)
loot->maxLevel = loot->minLevel;
zone->AddLevelLootList(loot);
LogWrite(LOOT__DEBUG, 5, "Loot", "---Loading Level %i loot table (id: %u)", loot->minLevel, table_id);
LogWrite(LOOT__DEBUG, 5, "Loot", "---minlevel: %i, maxlevel: %i", loot->minLevel, loot->maxLevel);
}
else if (strcmp(type, "Racial") == 0) {
GlobalLoot* loot = new GlobalLoot();
int16 race_id = result.GetInt16Str("value1");
loot->minLevel = result.GetInt8Str("value2");
loot->maxLevel = result.GetInt8Str("value3");
loot->table_id = table_id;
loot->loot_tier = result.GetInt32Str("value4");
if (loot->minLevel > loot->maxLevel)
loot->maxLevel = loot->minLevel;
zone->AddRacialLootList(race_id, loot);
LogWrite(LOOT__DEBUG, 5, "Loot", "---Loading Racial %i loot table (id: %u)", race_id, table_id);
LogWrite(LOOT__DEBUG, 5, "Loot", "---minlevel: %i, maxlevel: %i", loot->minLevel, loot->maxLevel);
}
else if (strcmp(type, "Zone") == 0) {
GlobalLoot* loot = new GlobalLoot();
int32 zoneID = result.GetInt32Str("value1");
loot->minLevel = result.GetInt8Str("value2");
loot->maxLevel = result.GetInt8Str("value3");
loot->table_id = table_id;
loot->loot_tier = result.GetInt32Str("value4");
if (loot->minLevel > loot->maxLevel)
loot->maxLevel = loot->minLevel;
zone->AddZoneLootList(zoneID, loot);
LogWrite(LOOT__DEBUG, 5, "Loot", "---Loading Zone %i loot table (id: %u)", zoneID, table_id);
LogWrite(LOOT__DEBUG, 5, "Loot", "---minlevel: %i, maxlevel: %i", loot->minLevel, loot->maxLevel);
}
count++;
}
LogWrite(LOOT__DEBUG, 4, "Loot", "--Loaded %u Global loot list%s.", count, count == 1 ? "" : "s");
}
}
bool WorldDatabase::LoadSpawnLoot(ZoneServer* zone, Spawn* spawn)
{
if (!spawn->GetDatabaseID())
return false;
DatabaseResult result;
int32 count = 0;
zone->ClearSpawnLootList(spawn->GetDatabaseID());
// Finally, load loot tables into spawns that are set to use these loot tables
if (database_new.Select(&result, "SELECT spawn_id, loottable_id FROM spawn_loot where spawn_id=%u",spawn->GetDatabaseID())) {
count = 0;
LogWrite(LOOT__DEBUG, 0, "Loot", "--Assigning loot table(s) to spawn(s)...");
while (result.Next()) {
int32 spawn_id = result.GetInt32Str("spawn_id");
int32 table_id = result.GetInt32Str("loottable_id");
zone->AddSpawnLootList(spawn_id, table_id);
LogWrite(LOOT__DEBUG, 5, "Loot", "---Adding loot table %u to spawn %u", table_id, spawn_id);
count++;
}
LogWrite(LOOT__DEBUG, 4, "Loot", "--Loaded %u spawn loot list%s.", count, count == 1 ? "" : "s");
return true;
}
return false;
}
void WorldDatabase::AddLootTableToSpawn(Spawn* spawn, int32 loottable_id) {
Query query;
query.RunQuery2(Q_INSERT, "insert into spawn_loot set spawn_id=%u,loottable_id=%u", spawn->GetDatabaseID(), loottable_id);
}
bool WorldDatabase::RemoveSpawnLootTable(Spawn* spawn, int32 loottable_id) {
Query query;
if (loottable_id)
{
string delete_char = string("delete from spawn_loot where spawn_id=%i and loottable_id=%i");
query.RunQuery2(Q_DELETE, delete_char.c_str(), spawn->GetDatabaseID(), loottable_id);
}
else
{
string delete_char = string("delete from spawn_loot where spawn_id=%i");
query.RunQuery2(Q_DELETE, delete_char.c_str(), spawn->GetDatabaseID());
}
if (!query.GetAffectedRows())
{
//No error just in case ppl try doing stupid stuff
return false;
}
return true;
}

153
old/world/Languages.cpp Normal file
View File

@ -0,0 +1,153 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <string.h>
#include "Languages.h"
Language::Language(){
id = 0;
memset(name, 0, sizeof(name));
save_needed = false;
}
Language::Language(Language* language){
id = language->id;
strncpy(name, language->GetName(), sizeof(name));
save_needed = language->save_needed;
}
MasterLanguagesList::MasterLanguagesList(){
}
MasterLanguagesList::~MasterLanguagesList(){
Clear();
}
// don't bother calling this beyond its deconstructor its not thread-safe
void MasterLanguagesList::Clear(){
list<Language*>::iterator itr;
Language* language = 0;
for(itr = languages_list.begin(); itr != languages_list.end(); itr++){
language = *itr;
safe_delete(language);
}
languages_list.clear();
}
int32 MasterLanguagesList::Size(){
return languages_list.size();
}
void MasterLanguagesList::AddLanguage(Language* language){
assert(language);
languages_list.push_back(language);
}
Language* MasterLanguagesList::GetLanguage(int32 id){
list<Language*>::iterator itr;
Language* language = 0;
Language* ret = 0;
for(itr = languages_list.begin(); itr != languages_list.end(); itr++){
language = *itr;
if(language->GetID() == id){
ret = language;
break;
}
}
return ret;
}
Language* MasterLanguagesList::GetLanguageByName(const char* name){
list<Language*>::iterator itr;
Language* language = 0;
Language* ret = 0;
for(itr = languages_list.begin(); itr != languages_list.end(); itr++){
language = *itr;
if(!language)
continue;
if(!strncmp(language->GetName(), name, strlen(name))){
ret = language;
break;
}
}
return ret;
}
list<Language*>* MasterLanguagesList::GetAllLanguages(){
return &languages_list;
}
PlayerLanguagesList::PlayerLanguagesList(){
}
PlayerLanguagesList::~PlayerLanguagesList(){
}
void PlayerLanguagesList::Clear() {
list<Language*>::iterator itr;
Language* language = 0;
Language* ret = 0;
for(itr = player_languages_list.begin(); itr != player_languages_list.end(); itr++){
language = *itr;
safe_delete(language);
}
player_languages_list.clear();
}
void PlayerLanguagesList::Add(Language* language){
player_languages_list.push_back(language);
}
Language* PlayerLanguagesList::GetLanguage(int32 id){
list<Language*>::iterator itr;
Language* language = 0;
Language* ret = 0;
for(itr = player_languages_list.begin(); itr != player_languages_list.end(); itr++){
language = *itr;
if(language->GetID() == id){
ret = language;
break;
}
}
return ret;
}
Language* PlayerLanguagesList::GetLanguageByName(const char* name){
list<Language*>::iterator itr;
Language* language = 0;
Language* ret = 0;
for(itr = player_languages_list.begin(); itr != player_languages_list.end(); itr++){
language = *itr;
if(!language)
continue;
if(!strncmp(language->GetName(), name, strlen(name))){
ret = language;
break;
}
}
return ret;
}
list<Language*>* PlayerLanguagesList::GetAllLanguages(){
return &player_languages_list;
}

76
old/world/Languages.h Normal file
View File

@ -0,0 +1,76 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LANGUAGES_H_
#define LANGUAGES_H_
#include <string>
#include <list>
#include "../common/types.h"
using namespace std;
class Language {
public:
Language();
Language(Language* language);
void SetID(int32 id) {this->id = id;}
void SetName(const char *name) {strncpy(this->name, name, sizeof(this->name));}
void SetSaveNeeded(bool save_needed) {this->save_needed = save_needed;}
int32 GetID() {return id;}
const char* GetName() {return name;}
bool GetSaveNeeded() {return save_needed;}
private:
int32 id;
char name[50];
bool save_needed;
};
class MasterLanguagesList {
public:
MasterLanguagesList();
~MasterLanguagesList();
void Clear();
int32 Size();
void AddLanguage(Language* language);
Language* GetLanguage(int32 id);
Language* GetLanguageByName(const char* name);
list<Language*>* GetAllLanguages();
private:
list<Language*> languages_list;
};
class PlayerLanguagesList {
public:
PlayerLanguagesList();
~PlayerLanguagesList();
void Clear();
void Add(Language* language);
Language* GetLanguage(int32 id);
Language* GetLanguageByName(const char* name);
list<Language*>* GetAllLanguages();
private:
list<Language*> player_languages_list;
};
#endif

748
old/world/LoginServer.cpp Normal file
View File

@ -0,0 +1,748 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../common/debug.h"
#include "../common/Log.h"
#include <iostream>
using namespace std;
#include <string.h>
#include <stdio.h>
#include <iomanip>
using namespace std;
#include <stdlib.h>
#include "../common/version.h"
#include "../common/GlobalHeaders.h"
#include "../common/sha512.h"
#ifdef WIN32
#include <process.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <windows.h>
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#else // Pyro: fix for linux
#include <sys/socket.h>
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
#include <sys/types.h>
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include "../common/unix.h"
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
extern int errno;
#endif
#include "../common/servertalk.h"
#include "LoginServer.h"
#include "../common/packet_dump.h"
#include "net.h"
#include "zoneserver.h"
#include "WorldDatabase.h"
#include "Variables.h"
#include "World.h"
#include "../common/ConfigReader.h"
#include "Rules/Rules.h"
#include "Web/PeerManager.h"
#include "Web/HTTPSClientPool.h"
extern sint32 numzones;
extern sint32 numclients;
extern NetConnection net;
extern LoginServer loginserver;
extern WorldDatabase database;
extern ZoneAuth zone_auth;
extern Variables variables;
extern ZoneList zone_list;
extern ClientList client_list;
extern volatile bool RunLoops;
volatile bool LoginLoopRunning = false;
extern ConfigReader configReader;
extern RuleManager rule_manager;
extern PeerManager peer_manager;
extern HTTPSClientPool peer_https_pool;
bool AttemptingConnect = false;
LoginServer::LoginServer(const char* iAddress, int16 iPort) {
LoginServerIP = ResolveIP(iAddress);
LoginServerPort = iPort;
statusupdate_timer = new Timer(LoginServer_StatusUpdateInterval);
tcpc = new TCPConnection(false);
pTryReconnect = true;
minLockedStatus = 100;
maxPlayers = -1;
minGameFullStatus = 100;
last_checked_time = 0;
zone_updates = 0;
loginEquip_updates = 0;
}
LoginServer::~LoginServer() {
delete statusupdate_timer;
delete tcpc;
}
void LoginServer::SendImmediateEquipmentUpdatesForChar(int32 char_id) {
LogWrite(WORLD__DEBUG, 5, "World", "Sending login equipment updates for char_id: %u", char_id);
int16 count = 0;
if(!loginEquip_updates)
loginEquip_updates = database.GetEquipmentUpdates(char_id);
if(loginEquip_updates && loginEquip_updates->size() > 0)
{
map<int32, LoginEquipmentUpdate> send_map;
int32 size = 0;
MutexMap<int32, LoginEquipmentUpdate>::iterator itr = loginEquip_updates->begin();
while(itr.Next())
{
send_map[itr->first] = itr->second;
size += sizeof(EquipmentUpdate_Struct);
loginEquip_updates->erase(itr->first);
count++;
}
ServerPacket* outpack = new ServerPacket(ServerOP_LoginEquipment, size + sizeof(EquipmentUpdateList_Struct)+5);
EquipmentUpdateList_Struct* updates = (EquipmentUpdateList_Struct*)outpack->pBuffer;
updates->total_updates = count;
int32 pos = sizeof(EquipmentUpdateList_Struct);
map<int32, LoginEquipmentUpdate>::iterator send_itr;
for(send_itr = send_map.begin(); send_itr != send_map.end(); send_itr++)
{
EquipmentUpdate_Struct* update = (EquipmentUpdate_Struct*)(outpack->pBuffer + pos);
update->id = send_itr->first;
update->world_char_id = send_itr->second.world_char_id;
update->equip_type = send_itr->second.equip_type;
update->red = send_itr->second.red;
update->green = send_itr->second.green;
update->blue = send_itr->second.blue;
update->highlight_red = send_itr->second.red;
update->highlight_green = send_itr->second.green;
update->highlight_blue = send_itr->second.blue;
update->slot = send_itr->second.slot;
pos += sizeof(EquipmentUpdate_Struct);
}
SendPacket(outpack);
outpack->Deflate();
safe_delete(outpack);
}
if(loginEquip_updates && count)
loginEquip_updates->clear();
if(loginEquip_updates && loginEquip_updates->size() == 0)
{
database.UpdateLoginEquipment();
safe_delete(loginEquip_updates);
}
}
bool LoginServer::Process() {
if(last_checked_time > Timer::GetCurrentTime2())
return true;
last_checked_time = Timer::GetCurrentTime2() + 50;
bool ret = true;
if (statusupdate_timer->Check()) {
this->SendStatus();
}
/************ Get all packets from packet manager out queue and process them ************/
ServerPacket *pack = 0;
while((pack = tcpc->PopPacket()))
{
switch(pack->opcode)
{
case ServerOP_LSFatalError:
{
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): ServerOP_LSFatalError", pack->opcode, pack->opcode);
LogWrite(WORLD__ERROR, 0, "World", "Login Server returned a fatal error: %s\n", pack->pBuffer);
tcpc->Disconnect();
ret = false;
//net.ReadLoginINI(); // can't properly support with command line args now
break;
}
case ServerOP_CharTimeStamp:
{
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): ServerOP_CharTimeStamp", pack->opcode, pack->opcode);
if(pack->size != sizeof(CharacterTimeStamp_Struct))
break;
CharacterTimeStamp_Struct* cts = (CharacterTimeStamp_Struct*) pack->pBuffer;
// determine if the character exists and retrieve its latest timestamp from the world server
bool char_exist = false;
//int32 character_timestamp = database.GetCharacterTimeStamp(cts->char_id,cts->account_id,&char_exist);
if(!char_exist)
{
//Character doesn't exist, get rid of it
SendDeleteCharacter ( cts );
break;
}
break;
}
// Push Character Select "item appearances" to login_equipment table
case ServerOP_LoginEquipment:{
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): ServerOP_LoginEquipment", pack->opcode, pack->opcode);
LogWrite(MISC__TODO, 0, "TODO", "Implement map<character id <map<slot id, updatestruct> > method to update Login.\n%s, %s, %i", __FILE__, __FUNCTION__, __LINE__);
if( pack->size == sizeof(EquipmentUpdateRequest_Struct) )
{
int16 max = ((EquipmentUpdateRequest_Struct*)pack->pBuffer)->max_per_batch;
int16 count = 0;
if(!loginEquip_updates)
loginEquip_updates = database.GetEquipmentUpdates();
if(loginEquip_updates && loginEquip_updates->size() > 0)
{
map<int32, LoginEquipmentUpdate> send_map;
int32 size = 0;
MutexMap<int32, LoginEquipmentUpdate>::iterator itr = loginEquip_updates->begin();
while(itr.Next() && count < max)
{
send_map[itr->first] = itr->second;
size += sizeof(EquipmentUpdate_Struct);
loginEquip_updates->erase(itr->first);
count++;
}
ServerPacket* outpack = new ServerPacket(ServerOP_LoginEquipment, size + sizeof(EquipmentUpdateList_Struct)+5);
EquipmentUpdateList_Struct* updates = (EquipmentUpdateList_Struct*)outpack->pBuffer;
updates->total_updates = count;
int32 pos = sizeof(EquipmentUpdateList_Struct);
map<int32, LoginEquipmentUpdate>::iterator send_itr;
for(send_itr = send_map.begin(); send_itr != send_map.end(); send_itr++)
{
EquipmentUpdate_Struct* update = (EquipmentUpdate_Struct*)(outpack->pBuffer + pos);
update->id = send_itr->first;
update->world_char_id = send_itr->second.world_char_id;
update->equip_type = send_itr->second.equip_type;
update->red = send_itr->second.red;
update->green = send_itr->second.green;
update->blue = send_itr->second.blue;
update->highlight_red = send_itr->second.red;
update->highlight_green = send_itr->second.green;
update->highlight_blue = send_itr->second.blue;
update->slot = send_itr->second.slot;
pos += sizeof(EquipmentUpdate_Struct);
}
SendPacket(outpack);
outpack->Deflate();
safe_delete(outpack);
}
if(loginEquip_updates && count < max)
loginEquip_updates->clear();
if(loginEquip_updates && loginEquip_updates->size() == 0)
{
database.UpdateLoginEquipment();
safe_delete(loginEquip_updates);
}
}
break;
}
case ServerOP_ZoneUpdates:{
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): ServerOP_ZoneUpdates", pack->opcode, pack->opcode);
if(pack->size == sizeof(ZoneUpdateRequest_Struct)){
int16 max = ((ZoneUpdateRequest_Struct*)pack->pBuffer)->max_per_batch;
int16 count = 0;
if(!zone_updates)
zone_updates = database.GetZoneUpdates();
if(zone_updates && zone_updates->size() > 0){
map<int32, LoginZoneUpdate> send_map;
int32 size = 0;
MutexMap<int32, LoginZoneUpdate>::iterator itr = zone_updates->begin();
while(itr.Next() && count < max){
send_map[itr->first] = itr->second;
size += sizeof(ZoneUpdate_Struct) + itr->second.name.length() + itr->second.description.length();
zone_updates->erase(itr->first);
count++;
}
ServerPacket* outpack = new ServerPacket(ServerOP_ZoneUpdates, size + sizeof(ZoneUpdateList_Struct)+5);
ZoneUpdateList_Struct* updates = (ZoneUpdateList_Struct*)outpack->pBuffer;
updates->total_updates = count;
int32 pos = sizeof(ZoneUpdateList_Struct);
map<int32, LoginZoneUpdate>::iterator send_itr;
for(send_itr = send_map.begin(); send_itr != send_map.end(); send_itr++){
ZoneUpdate_Struct* update = (ZoneUpdate_Struct*)(outpack->pBuffer + pos);
update->zone_id = send_itr->first;
update->zone_name_length = send_itr->second.name.length();
update->zone_desc_length = send_itr->second.description.length();
strcpy(update->data, send_itr->second.name.c_str());
strcpy(update->data + send_itr->second.name.length(), send_itr->second.description.c_str());
pos += sizeof(ZoneUpdate_Struct) + send_itr->second.name.length() + send_itr->second.description.length();
}
SendPacket(outpack);
outpack->Deflate();
safe_delete(outpack);
}
if(zone_updates && count < max)
zone_updates->clear();
if(zone_updates && zone_updates->size() == 0){
database.UpdateLoginZones();
safe_delete(zone_updates);
}
}
break;
}
case ServerOP_CharacterCreate:{
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): ServerOP_CharacterCreate", pack->opcode, pack->opcode);
int16 version = 1;
if(pack->pBuffer[0] > 0)
memcpy(&version, pack->pBuffer, sizeof(int16));
//DumpPacket(pack->pBuffer,pack->size);
PacketStruct* packet = configReader.getStruct("CreateCharacter", version);
int8 resp = 0;
int32 acct_id = 0;
int32 char_id = 0;
// have to load packet to clear the buffer
if(packet && packet->LoadPacketData(pack->pBuffer+sizeof(int16),pack->size - sizeof(int16), version <= 561 ? false : true)){
if(net.world_locked) {
resp = NOSERVERSAVAIL_REPLY; // no new characters when locked
}
else {
EQ2_16BitString name = packet->getType_EQ2_16BitString_ByName("name");
resp = database.CheckNameFilter(name.data.c_str());
acct_id = packet->getType_int32_ByName("account_id");
LogWrite(WORLD__DEBUG, 0, "World", "Response: %i", (int)resp);
sint16 lowestStatus = database.GetLowestCharacterAdminStatus(acct_id);
if(lowestStatus == -2)
resp = UNKNOWNERROR_REPLY2;
else if(resp == CREATESUCCESS_REPLY)
char_id = database.SaveCharacter(packet, acct_id);
}
}
else{
LogWrite(WORLD__ERROR, 0, "World", "Invalid creation request!");
resp = UNKNOWNERROR_REPLY;
}
// send name filter response data back to the login server
SendFilterNameResponse ( resp , acct_id , char_id );
safe_delete(packet);
break;
}
case ServerOP_BasicCharUpdate: {
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): ServerOP_BasicCharUpdate", pack->opcode, pack->opcode);
if(pack->size != sizeof(CharDataUpdate_Struct))
break;
CharDataUpdate_Struct* cdu = (CharDataUpdate_Struct*) pack->pBuffer;
switch(cdu->update_field)
{
case DELETE_UPDATE_FLAG:
{
LogWrite(WORLD__DEBUG, 0, "World", "Delete character request: %i %i",cdu->account_id,cdu->char_id );
database.DeleteCharacter(cdu->account_id,cdu->char_id);
break;
}
}
break;
}
case ServerOP_UsertoWorldReq:{
LogWrite(OPCODE__DEBUG, 0, "Opcode", "Opcode 0x%X (%i): ServerOP_UsertoWorldReq", pack->opcode, pack->opcode);
UsertoWorldRequest_Struct* utwr = (UsertoWorldRequest_Struct*) pack->pBuffer;
/*int32 id = database.GetAccountIDFromLSID(utwr->lsaccountid);
sint16 status = database.CheckStatus(id);
*/
int32 access_key = 0;
ZoneChangeDetails details;
std::string name = database.loadCharacterFromLogin(&details, utwr->char_id, utwr->lsaccountid);
// if it is a accepted login, we add the zone auth request
access_key = DetermineCharacterLoginRequest ( utwr, &details, name);
if ( access_key != 0 )
{
zone_auth.PurgeInactiveAuth();
char* characterName = database.GetCharacterName( utwr->char_id );
if(characterName != 0){
ZoneAuthRequest* zar = new ZoneAuthRequest(utwr->lsaccountid,characterName,access_key);
zar->setFirstLogin ( true );
zone_auth.AddAuth(zar);
safe_delete_array(characterName);
}
}
break;
}
case ServerOP_ResetDatabase:{
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): ServerOP_ResetDatabase", pack->opcode, pack->opcode);
database.ResetDatabase();
break;
}
default:
{
LogWrite(WORLD__ERROR, 0, "World", "Unhandled opcode: %i", pack->opcode);
DumpPacket(pack);
}
}
safe_delete(pack);
// break out if ret is now false
if (!ret)
break;
}
return ret;
}
// this should always be called in a new thread
#ifdef WIN32
void AutoInitLoginServer(void *tmp) {
#else
void *AutoInitLoginServer(void *tmp) {
#endif
if (loginserver.GetState() == TCPS_Ready) {
InitLoginServer();
}
#ifndef WIN32
return 0;
#endif
}
bool InitLoginServer() {
if (loginserver.GetState() != TCPS_Ready) {
LogWrite(WORLD__ERROR, 0, "World", "InitLoginServer() while already attempting connect.");
return false;
}
if (!net.LoginServerInfo) {
LogWrite(WORLD__ERROR, 0, "World", "Login server info not loaded.");
return false;
}
AttemptingConnect = true;
int16 port;
char* address = net.GetLoginInfo(&port);
LogWrite(WORLD__INFO, 0, "World", "InitLoginServer() attempt connect to %s on port %u.", address, port);
loginserver.Connect(address, port);
return true;
}
void LoginServer::InitLoginServerVariables()
{
minLockedStatus = rule_manager.GetGlobalRule(R_World, ServerLockedOverrideStatus)->GetSInt16();
maxPlayers = rule_manager.GetGlobalRule(R_World, MaxPlayers)->GetSInt16();
minGameFullStatus = rule_manager.GetGlobalRule(R_World, MaxPlayersOverrideStatus)->GetSInt16();
}
bool LoginServer::Connect(const char* iAddress, int16 iPort) {
if(!pTryReconnect)
return false;
char errbuf[TCPConnection_ErrorBufferSize];
memset(errbuf, 0, TCPConnection_ErrorBufferSize);
if (iAddress == 0) {
LogWrite(WORLD__ERROR, 0, "World", "LoginServer::Connect: address == 0");
return false;
}
else {
if ((LoginServerIP = ResolveIP(iAddress, errbuf)) == 0) {
LogWrite(WORLD__ERROR, 0, "World", "LoginServer::Connect: Resolving IP address: '%s'", errbuf);
return false;
}
}
if (iPort != 0)
LoginServerPort = iPort;
if (LoginServerIP == 0 || LoginServerPort == 0) {
LogWrite(WORLD__ERROR, 0, "World", "LoginServer::Connect: Connect info incomplete, cannot connect");
return false;
}
if (tcpc->Connect(LoginServerIP, LoginServerPort, errbuf)) {
LogWrite(WORLD__INFO, 0, "World", "Connected to LoginServer: %s: %i", iAddress, LoginServerPort);
SendInfo();
SendStatus();
return true;
}
else {
LogWrite(WORLD__ERROR, 0, "World", "LoginServer::Connect: '%s'", errbuf);
return false;
}
}
void LoginServer::GetLatestTables(){
ServerPacket* pack = new ServerPacket(ServerOP_GetLatestTables, sizeof(GetLatestTables_Struct));
GetLatestTables_Struct* data = (GetLatestTables_Struct*)pack->pBuffer;
data->table_version = CURRENT_DATABASE_MAJORVERSION*100 + CURRENT_DATABASE_MINORVERSION;
data->data_version = CURRENT_DATABASE_MAJORVERSION*100 + CURRENT_DATABASE_MINORVERSION;
SendPacket(pack);
delete pack;
}
void LoginServer::SendInfo() {
ServerPacket* pack = new ServerPacket;
pack->opcode = ServerOP_LSInfo;
pack->size = sizeof(ServerLSInfo_Struct);
pack->pBuffer = new uchar[pack->size];
memset(pack->pBuffer, 0, pack->size);
ServerLSInfo_Struct* lsi = (ServerLSInfo_Struct*) pack->pBuffer;
strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION);
strcpy(lsi->serverversion, CURRENT_VERSION);
strcpy(lsi->name, net.GetWorldName());
strcpy(lsi->account, net.GetWorldAccount());
lsi->dbversion = CURRENT_DATABASE_MAJORVERSION*100 + CURRENT_DATABASE_MINORVERSION;
#ifdef _DEBUG
lsi->servertype = 4;
#endif
string passwdSha512 = sha512(net.GetWorldPassword());
memcpy(lsi->password, (char*)passwdSha512.c_str(), passwdSha512.length());
strcpy(lsi->address, net.GetWorldAddress());
SendPacket(pack);
delete pack;
}
void LoginServer::SendStatus() {
statusupdate_timer->Start();
ServerPacket* pack = new ServerPacket;
pack->opcode = ServerOP_LSStatus;
pack->size = sizeof(ServerLSStatus_Struct);
pack->pBuffer = new uchar[pack->size];
memset(pack->pBuffer, 0, pack->size);
ServerLSStatus_Struct* lss = (ServerLSStatus_Struct*) pack->pBuffer;
if (net.world_locked)
lss->status = -2;
else if(loginserver.maxPlayers > -1 && numclients >= loginserver.maxPlayers)
lss->status = -3;
else
lss->status = 1;
lss->num_zones = numzones;
lss->num_players = numclients;
lss->world_max_level = rule_manager.GetGlobalRule(R_Player, MaxLevel)->GetInt8();
SendPacket(pack);
delete pack;
}
void LoginServer::SendDeleteCharacter ( CharacterTimeStamp_Struct* cts ) {
ServerPacket* outpack = new ServerPacket(ServerOP_BasicCharUpdate, sizeof(CharDataUpdate_Struct));
CharDataUpdate_Struct* cdu = (CharDataUpdate_Struct*)outpack->pBuffer;
cdu->account_id = cts->account_id;
cdu->char_id = cts->char_id;
cdu->update_field = DELETE_UPDATE_FLAG;
cdu->update_data = 1;
SendPacket(outpack);
safe_delete(outpack);
}
void LoginServer::SendFilterNameResponse ( int8 resp, int32 acct_id , int32 char_id ) {
ServerPacket* outpack = new ServerPacket(ServerOP_CharacterCreate, sizeof(WorldCharNameFilterResponse_Struct));
WorldCharNameFilterResponse_Struct* wcfr = (WorldCharNameFilterResponse_Struct*)outpack->pBuffer;
wcfr->response = resp;
wcfr->account_id = acct_id;
wcfr->char_id = char_id;
SendPacket(outpack);
safe_delete(outpack);
}
int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr, ZoneChangeDetails* details, std::string name) {
LogWrite(LOGIN__TRACE, 9, "Login", "Enter: %s", __FUNCTION__);
int32 timestamp = Timer::GetUnixTimeStamp();
int32 key = static_cast<unsigned int>(MakeRandomFloat(0.01,1.0) * UINT32_MAX);
int8 response = 0;
sint16 lowestStatus = database.GetLowestCharacterAdminStatus( utwr->lsaccountid );
sint16 status = 0;
if(lowestStatus == -2)
status = -1;
else
status = database.GetCharacterAdminStatus ( utwr->lsaccountid , utwr->char_id );
if(status < 100 && zone_list.ClientConnected(utwr->lsaccountid, utwr->char_id))
status = -9;
if(status < 0){
LogWrite(WORLD__ERROR, 0, "World", "Login Rejected based on PLAY_ERROR (UserStatus) (MinStatus: %i), UserStatus: %i, CharID: %i",loginserver.minLockedStatus,status,utwr->char_id );
switch(status){
case -10:
response = PLAY_ERROR_CHAR_NOT_LOADED;
break;
case -9:
response = PLAY_ERROR_ACCOUNT_IN_USE;
break;
case -8:
response = PLAY_ERROR_LOADING_ERROR;
break;
case -1:
response = PLAY_ERROR_ACCOUNT_BANNED;
break;
default:
response = PLAY_ERROR_PROBLEM;
}
}
else if(net.world_locked == true){
LogWrite(WORLD__INFO, 0, "World", "Login Lock Check (MinStatus: %i):, UserStatus: %i, CharID: %i",loginserver.minLockedStatus,status,utwr->char_id );
// has high enough status, allow it
if(status >= loginserver.minLockedStatus)
response = 1;
}
else if(loginserver.maxPlayers > -1 && ((sint16)client_list.Count()) >= loginserver.maxPlayers)
{
LogWrite(WORLD__INFO, 0, "World", "Login GameFull Check (MinStatus: %i):, UserStatus: %i, CharID: %i",loginserver.minGameFullStatus,status,utwr->char_id );
// has high enough status, allow it
if(status >= loginserver.minGameFullStatus)
{
response = 1;
}
else
response = -3; // server full response is -3
}
else
response = 1;
bool attemptedPeer = false;
if(response == 1 && details->peerId.size() > 0 && details->peerId != "self" && name.size() > 0) {
boost::property_tree::ptree root;
root.put("account_id", utwr->lsaccountid);
root.put("character_name", std::string(name));
root.put("character_id", std::to_string(utwr->char_id));
root.put("zone_id", std::to_string(details->zoneId));
root.put("instance_id", std::to_string(details->instanceId));
root.put("login_key", std::to_string(key));
root.put("client_ip", std::string(utwr->ip_address));
root.put("world_id", std::to_string(utwr->worldid));
root.put("from_id", std::to_string(utwr->FromID));
root.put("first_login", true);
std::ostringstream jsonStream;
boost::property_tree::write_json(jsonStream, root);
std::string jsonPayload = jsonStream.str();
LogWrite(PEERING__INFO, 0, "Peering", "%s: Sending AddCharAuth for %s to peer %s:%u for existing zone %s", __FUNCTION__, name.c_str(), details->peerWebAddress.c_str(), details->peerWebPort, details->zoneName.c_str());
attemptedPeer = true;
peer_https_pool.sendPostRequestToPeerAsync(details->peerId, details->peerWebAddress, std::to_string(details->peerWebPort), "/addcharauth", jsonPayload);
}
else if(response == 1 && details->peerId == "") {
std::shared_ptr<Peer> peer = peer_manager.getHealthyPeerWithLeastClients();
if(peer != nullptr) {
boost::property_tree::ptree root;
char* characterName = database.GetCharacterName( utwr->char_id );
if(!characterName) {
LogWrite(PEERING__ERROR, 0, "Peering", "%s: AddCharAuth failed to identify character name for char id %u to peer %s:%u", __FUNCTION__, utwr->char_id, peer->webAddr.c_str(), peer->webPort);
}
else {
root.put("account_id", utwr->lsaccountid);
root.put("character_name", std::string(characterName));
root.put("character_id", std::to_string(utwr->char_id));
root.put("zone_id", std::to_string(details->zoneId));
root.put("instance_id", std::to_string(details->instanceId));
root.put("login_key", std::to_string(key));
root.put("client_ip", std::string(utwr->ip_address));
root.put("world_id", std::to_string(utwr->worldid));
root.put("from_id", std::to_string(utwr->FromID));
root.put("first_login", true);
std::ostringstream jsonStream;
boost::property_tree::write_json(jsonStream, root);
std::string jsonPayload = jsonStream.str();
LogWrite(PEERING__INFO, 0, "Peering", "%s: Sending AddCharAuth for %s to peer %s:%u for new zone %s", __FUNCTION__, characterName, peer->webAddr.c_str(), peer->webPort, details->zoneName.c_str());
attemptedPeer = true;
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addcharauth", jsonPayload);
}
}
else if(peer_manager.hasPeers()) {
LogWrite(PEERING__WARNING, 0, "Peering", "%s: AddCharAuth failed to find healthy peer for char id %u", __FUNCTION__, utwr->char_id);
}
}
/*sint32 x = database.CommandRequirement("$MAXCLIENTS");
if( (sint32)numplayers >= x && x != -1 && x != 255 && status < 80)
utwrs->response = -3;
if(status == -1)
utwrs->response = -1;
if(status == -2)
utwrs->response = -2;
*/
//printf("Response is %i for %i\n",utwrs->response,id);struct sockaddr_in sa;
if(!attemptedPeer) {
SendCharApprovedLogin(response, "", "", std::string(utwr->ip_address), 0, utwr->lsaccountid, utwr->char_id, key, utwr->worldid, utwr->FromID);
}
LogWrite(LOGIN__TRACE, 9, "Login", "Exit: %s with timestamp=%u", __FUNCTION__, timestamp);
// depending on the response determined above, this could return 0 (for failure)
return (attemptedPeer) ? 0 : key;
}
void LoginServer::SendCharApprovedLogin(int8 response, std::string peerAddress, std::string peerInternalAddress, std::string clientIP, int16 peerPort, int32 account_id, int32 char_id, int32 key, int32 world_id, int32 from_id) {
ServerPacket* outpack = new ServerPacket;
outpack->opcode = ServerOP_UsertoWorldResp;
outpack->size = sizeof(UsertoWorldResponse_Struct);
outpack->pBuffer = new uchar[outpack->size];
memset(outpack->pBuffer, 0, outpack->size);
UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer;
utwrs->response = response;
utwrs->lsaccountid = account_id;
utwrs->char_id = char_id;
utwrs->ToID = from_id;
utwrs->access_key = key;
int32 ipv4addr = 0;
int result = 0;
#ifdef WIN32
struct sockaddr_in myaddr;
ZeroMemory(&myaddr, sizeof(myaddr));
result = InetPton(AF_INET, clientIP.c_str(), &(myaddr.sin_addr));
if(result)
ipv4addr = ntohl(myaddr.sin_addr.s_addr);
#else
result = inet_pton(AF_INET, clientIP.c_str(), &ipv4addr);
if(result)
ipv4addr = ntohl(ipv4addr);
#endif
std::string internalAddress = std::string(net.GetInternalWorldAddress());
std::string address = std::string(net.GetWorldAddress());
int16 worldport = net.GetWorldPort();
if(peerAddress.size() > 0 && peerPort > 0) {
internalAddress = peerInternalAddress;
address = peerAddress;
worldport = peerPort;
}
if (((result > 0 && IsPrivateAddress(ipv4addr)) || (strcmp(address.c_str(), clientIP.c_str()) == 0)) && (internalAddress.size() > 0))
strcpy(utwrs->ip_address, internalAddress.c_str());
else
strcpy(utwrs->ip_address, address.c_str());
LogWrite(CCLIENT__INFO, 0, "World", "New client login attempt from %s, providing %s:%u as the world server address.",clientIP.c_str(), utwrs->ip_address, worldport );
utwrs->port = worldport;
utwrs->worldid = world_id;
SendPacket(outpack);
delete outpack;
}

88
old/world/LoginServer.h Normal file
View File

@ -0,0 +1,88 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOGINSERVER_H
#define LOGINSERVER_H
#include "../common/servertalk.h"
#include "../common/linked_list.h"
#include "../common/timer.h"
#include "../common/queue.h"
#include "../common/Mutex.h"
#include "../common/TCPConnection.h"
#include <deque>
#include "MutexMap.h"
#ifdef WIN32
void AutoInitLoginServer(void *tmp);
#else
void *AutoInitLoginServer(void *tmp);
#endif
bool InitLoginServer();
class LoginServer{
public:
LoginServer(const char* iAddress = 0, int16 iPort = 5999);
~LoginServer();
bool Process();
bool Connect(const char* iAddress = 0, int16 iPort = 0);
bool ConnectToUpdateServer(const char* iAddress = 0, int16 iPort = 0);
void SendInfo();
void SendStatus();
void GetLatestTables();
void SendPacket(ServerPacket* pack) { tcpc->SendPacket(pack); }
int8 GetState() { return tcpc->GetState(); }
bool Connected() { return tcpc->Connected(); }
void SendFilterNameResponse ( int8 resp , int32 acct_id , int32 char_id );
void SendDeleteCharacter ( CharacterTimeStamp_Struct* cts );
int32 DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr, ZoneChangeDetails* details, std::string name);
void SendCharApprovedLogin(int8 response, std::string peerAddress, std::string peerInternalAddress, std::string clientIP, int16 peerPort, int32 account_id, int32 char_id, int32 key, int32 world_id, int32 from_id);
void InitLoginServerVariables();
sint16 minLockedStatus;
sint16 maxPlayers;
sint16 minGameFullStatus;
void SendImmediateEquipmentUpdatesForChar(int32 char_id);
bool CanReconnect() { return pTryReconnect; }
private:
bool try_auto_update;
bool pTryReconnect;
TCPConnection* tcpc;
int32 LoginServerIP;
int32 UpdateServerIP;
int16 LoginServerPort;
uchar* data_waiting;
MutexMap<int32, LoginZoneUpdate>* zone_updates;
MutexMap<int32, LoginEquipmentUpdate>* loginEquip_updates;
int32 last_checked_time;
Timer* statusupdate_timer;
};
#endif

14952
old/world/LuaFunctions.cpp Normal file

File diff suppressed because it is too large Load Diff

703
old/world/LuaFunctions.h Normal file
View File

@ -0,0 +1,703 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LUA_FUNCTIONS_H
#define LUA_FUNCTIONS_H
#include <lua.hpp>
#include <vector>
#include <string>
#include <map>
using namespace std;
vector<string> ParseString(string strVal, char delim=',');
vector<unsigned int> ParseStringToInt32(string strVal, char delim=',');
map<string, signed int> ParseStringMap(string strVal, char delim=',');
map<unsigned int, unsigned short> ParseIntMap(string strVal, char delim = ',');
map<unsigned int, signed int> ParseSInt32Map(string strVal, char delim = ',');
//Sets
int EQ2Emu_lua_SetCurrentHP(lua_State* state);
int EQ2Emu_lua_SetMaxHP(lua_State* state);
int EQ2Emu_lua_SetMaxHPBase(lua_State* state);
int EQ2Emu_lua_SetCurrentPower(lua_State* state);
int EQ2Emu_lua_SetMaxPower(lua_State* state);
int EQ2Emu_lua_SetMaxPowerBase(lua_State* state);
int EQ2Emu_lua_ModifyMaxHP(lua_State* state);
int EQ2Emu_lua_ModifyMaxPower(lua_State* state);
int EQ2Emu_lua_SetHeading(lua_State* state);
int EQ2Emu_lua_SetModelType(lua_State* state);
int EQ2Emu_lua_SetAdventureClass(lua_State* state);
int EQ2Emu_lua_SetTradeskillClass(lua_State* state);
int EQ2Emu_lua_SetMount(lua_State* state);
int EQ2Emu_lua_SetMountColor(lua_State* state);
int EQ2Emu_lua_SetSpeed(lua_State* state);
int EQ2Emu_lua_SetPosition(lua_State* state);
int EQ2Emu_lua_AddSpellBonus(lua_State* state);
int EQ2Emu_lua_RemoveSpellBonus(lua_State* state);
int EQ2Emu_lua_AddSkillBonus(lua_State* state);
int EQ2Emu_lua_RemoveSkillBonus(lua_State* state);
int EQ2Emu_lua_AddControlEffect(lua_State* state);
int EQ2Emu_lua_RemoveControlEffect(lua_State* state);
int EQ2Emu_lua_HasControlEffect(lua_State* state);
int EQ2Emu_lua_GetBaseAggroRadius(lua_State* state);
int EQ2Emu_lua_GetAggroRadius(lua_State* state);
int EQ2Emu_lua_SetAggroRadius(lua_State* state);
int EQ2Emu_lua_SetDeity(lua_State* state);
int EQ2Emu_lua_GetDeity(lua_State* state);
int EQ2Emu_lua_SetInt(lua_State* state);
int EQ2Emu_lua_SetWis(lua_State* state);
int EQ2Emu_lua_SetSta(lua_State* state);
int EQ2Emu_lua_SetStr(lua_State* state);
int EQ2Emu_lua_SetAgi(lua_State* state);
int EQ2Emu_lua_SetIntBase(lua_State* state);
int EQ2Emu_lua_SetWisBase(lua_State* state);
int EQ2Emu_lua_SetStaBase(lua_State* state);
int EQ2Emu_lua_SetStrBase(lua_State* state);
int EQ2Emu_lua_SetAgiBase(lua_State* state);
int EQ2Emu_lua_SetLootCoin(lua_State* state);
int EQ2Emu_lua_HasCoin(lua_State* state);
int EQ2Emu_lua_SetQuestYellow(lua_State* state);
//Gets
int EQ2Emu_lua_GetLevel(lua_State* state);
int EQ2Emu_lua_GetDifficulty(lua_State* state);
int EQ2Emu_lua_GetCurrentHP(lua_State* state);
int EQ2Emu_lua_GetMaxHP(lua_State* state);
int EQ2Emu_lua_GetMaxHPBase(lua_State* state);
int EQ2Emu_lua_GetCurrentPower(lua_State* state);
int EQ2Emu_lua_GetName(lua_State* state);
int EQ2Emu_lua_GetMaxPower(lua_State* state);
int EQ2Emu_lua_GetMaxPowerBase(lua_State* state);
int EQ2Emu_lua_GetDistance(lua_State* state);
int EQ2Emu_lua_GetX(lua_State* state);
int EQ2Emu_lua_GetY(lua_State* state);
int EQ2Emu_lua_GetZ(lua_State* state);
int EQ2Emu_lua_GetHeading(lua_State* state);
int EQ2Emu_lua_GetModelType(lua_State* state);
int EQ2Emu_lua_GetRace(lua_State* state);
int EQ2Emu_lua_GetRaceName(lua_State* state);
int EQ2Emu_lua_GetMount(lua_State* state);
int EQ2Emu_lua_GetClass(lua_State* state);
int EQ2Emu_lua_GetClassName(lua_State* state);
int EQ2Emu_lua_GetArchetypeName(lua_State* state);
int EQ2Emu_lua_GetSpeed(lua_State* state);
int EQ2Emu_lua_HasMoved(lua_State* state);
int EQ2Emu_lua_GetInt(lua_State* state);
int EQ2Emu_lua_GetWis(lua_State* state);
int EQ2Emu_lua_GetSta(lua_State* state);
int EQ2Emu_lua_GetStr(lua_State* state);
int EQ2Emu_lua_GetAgi(lua_State* state);
int EQ2Emu_lua_GetIntBase(lua_State* state);
int EQ2Emu_lua_GetWisBase(lua_State* state);
int EQ2Emu_lua_GetStaBase(lua_State* state);
int EQ2Emu_lua_GetStrBase(lua_State* state);
int EQ2Emu_lua_GetAgiBase(lua_State* state);
int EQ2Emu_lua_GetLootCoin(lua_State* state);
int EQ2Emu_lua_GetSpawn(lua_State* state);
int EQ2Emu_lua_GetSpawnFromList(lua_State* state);
int EQ2Emu_lua_GetSpawnListSize(lua_State* state);
int EQ2Emu_lua_CreateSpawnList(lua_State* state);
int EQ2Emu_lua_AddSpawnToSpawnList(lua_State* state);
int EQ2Emu_lua_RemoveSpawnFromSpawnList(lua_State* state);
int EQ2Emu_lua_GetSpawnListBySpawnID(lua_State* state);
int EQ2Emu_lua_GetSpawnListByRailID(lua_State* state);
int EQ2Emu_lua_GetPassengerSpawnList(lua_State* state);
int EQ2Emu_lua_GetVariableValue(lua_State* state);
int EQ2Emu_lua_GetCoinMessage(lua_State* state);
int EQ2Emu_lua_GetSpawnByGroupID(lua_State* state);
int EQ2Emu_lua_GetSpawnByLocationID(lua_State* state);
int EQ2Emu_lua_GetID(lua_State* state);
int EQ2Emu_lua_GetSpawnID(lua_State* state);
int EQ2Emu_lua_GetSpawnGroupID(lua_State* state);
int EQ2Emu_lua_SetSpawnGroupID(lua_State* state);
int EQ2Emu_lua_AddSpawnToGroup(lua_State* state);
int EQ2Emu_lua_GetSpawnLocationID(lua_State* state);
int EQ2Emu_lua_GetSpawnLocationPlacementID(lua_State* state);
int EQ2Emu_lua_GetFactionAmount(lua_State* state);
int EQ2Emu_lua_SetFactionID(lua_State* state);
int EQ2Emu_lua_GetFactionID(lua_State* state);
int EQ2Emu_lua_ChangeFaction(lua_State* state);
int EQ2Emu_lua_GetGender(lua_State* state);
int EQ2Emu_lua_GetTarget(lua_State* state);
int EQ2Emu_lua_HasFreeSlot(lua_State* state);
int EQ2Emu_lua_HasItemEquipped(lua_State* state);
int EQ2Emu_lua_GetEquippedItemBySlot(lua_State* state);
int EQ2Emu_lua_GetEquippedItemByID(lua_State* state);
int EQ2Emu_lua_SetEquippedItemByID(lua_State* state);
int EQ2Emu_lua_SetEquippedItem(lua_State* state);
int EQ2Emu_lua_UnequipSlot(lua_State* state);
int EQ2Emu_lua_SetEquipment(lua_State* state);
int EQ2Emu_lua_GetItemByID(lua_State* state);
int EQ2Emu_lua_GetItemType(lua_State* state);
int EQ2Emu_lua_GetItemEffectType(lua_State* state);
int EQ2Emu_lua_GetSpellName(lua_State* state);
//Misc
int EQ2Emu_lua_SetAttackable(lua_State* state);
int EQ2Emu_lua_SendStateCommand(lua_State* state);
int EQ2Emu_lua_SpawnSet(lua_State* state);
int EQ2Emu_lua_KillSpawn(lua_State* state);
int EQ2Emu_lua_KillSpawnByDistance(lua_State* state);
int EQ2Emu_lua_SpawnSetByDistance(lua_State* state);
int EQ2Emu_lua_SetRequiredQuest(lua_State* state);
int EQ2Emu_lua_SetRequiredHistory(lua_State* state);
int EQ2Emu_lua_Despawn(lua_State* state);
int EQ2Emu_lua_ChangeHandIcon(lua_State* state);
int EQ2Emu_lua_SetVisualFlag(lua_State* state);
int EQ2Emu_lua_SetInfoFlag(lua_State* state);
int EQ2Emu_lua_AddHate(lua_State* state);
int EQ2Emu_lua_GetZone(lua_State* state);
int EQ2Emu_lua_GetZoneName(lua_State* state);
int EQ2Emu_lua_GetZoneID(lua_State* state);
int EQ2Emu_lua_Zone(lua_State* state);
int EQ2Emu_lua_ModifyPower(lua_State* state);
int EQ2Emu_lua_ModifyHP(lua_State* state);
int EQ2Emu_lua_ModifyTotalPower(lua_State* state);
int EQ2Emu_lua_ModifyTotalHP(lua_State* state);
int EQ2Emu_lua_SpellHeal(lua_State* state);
int EQ2Emu_lua_SpellHealPct(lua_State* state);
int EQ2Emu_lua_AddItem(lua_State* state);
int EQ2Emu_lua_SummonItem(lua_State* state);
int EQ2Emu_lua_RemoveItem(lua_State* state);
int EQ2Emu_lua_HasItem(lua_State* state);
int EQ2Emu_lua_Spawn(lua_State* state);
int EQ2Emu_lua_AddSpawnAccess(lua_State* state);
int EQ2Emu_lua_CastSpell(lua_State* state);
int EQ2Emu_lua_SpellDamage(lua_State* state);
int EQ2Emu_lua_SpellDamageExt(lua_State* state);
int EQ2Emu_lua_FaceTarget(lua_State* state);
int EQ2Emu_lua_MoveToLocation(lua_State* state);
int EQ2Emu_lua_ClearRunningLocations(lua_State* state);
int EQ2Emu_lua_Say(lua_State* state);
int EQ2Emu_lua_Shout(lua_State* state);
int EQ2Emu_lua_SayOOC(lua_State* state);
int EQ2Emu_lua_Emote(lua_State* state);
int EQ2Emu_lua_IsPlayer(lua_State* state);
int EQ2Emu_lua_GetCharacterID(lua_State* state);
int EQ2Emu_lua_MovementLoopAdd(lua_State* state);
int EQ2Emu_lua_GetCurrentZoneSafeLocation(lua_State* state);
int EQ2Emu_lua_PlayFlavor(lua_State* state);
int EQ2Emu_lua_PlayFlavorID(lua_State* state);
int EQ2Emu_lua_PlaySound(lua_State* state);
int EQ2Emu_lua_PlayVoice(lua_State* state);
int EQ2Emu_lua_PlayAnimation(lua_State* state);
int EQ2Emu_lua_PlayAnimationString(lua_State* state);
int EQ2Emu_lua_AddLootItem(lua_State* state);
int EQ2Emu_lua_HasLootItem(lua_State* state);
int EQ2Emu_lua_RemoveLootItem(lua_State* state);
int EQ2Emu_lua_AddLootCoin(lua_State* state);
int EQ2Emu_lua_GiveLoot(lua_State* state);
int EQ2Emu_lua_HasPendingLoot(lua_State* state);
int EQ2Emu_lua_HasPendingLootItem(lua_State* state);
int EQ2Emu_lua_CreateConversation(lua_State* state);
int EQ2Emu_lua_AddConversationOption(lua_State* state);
int EQ2Emu_lua_StartConversation(lua_State* state);
int EQ2Emu_lua_StartDialogConversation(lua_State* state);
//int EQ2Emu_lua_StartItemConversation(lua_State* state);
int EQ2Emu_lua_CloseConversation(lua_State* state);
int EQ2Emu_lua_CloseItemConversation(lua_State* state);
int EQ2Emu_lua_SetPlayerProximityFunction(lua_State* state);
int EQ2Emu_lua_SetLocationProximityFunction(lua_State* state);
int EQ2Emu_lua_IsBindAllowed(lua_State* state);
int EQ2Emu_lua_IsGateAllowed(lua_State* state);
int EQ2Emu_lua_Bind(lua_State* state);
int EQ2Emu_lua_Gate(lua_State* state);
int EQ2Emu_lua_IsAlive(lua_State* state);
int EQ2Emu_lua_IsSpawnGroupAlive(lua_State* state);
int EQ2Emu_lua_IsInCombat(lua_State* state);
int EQ2Emu_lua_SendMessage(lua_State* state);
int EQ2Emu_lua_SendPopUpMessage(lua_State* state);
int EQ2Emu_lua_SetServerControlFlag(lua_State* state);
int EQ2Emu_lua_ToggleTracking(lua_State* state);
int EQ2Emu_lua_AddPrimaryEntityCommand(lua_State* state);
int EQ2Emu_lua_AddSpellBookEntry(lua_State* state);
int EQ2Emu_lua_DeleteSpellBook(lua_State* state);
int EQ2Emu_lua_RemoveSpellBookEntry(lua_State* state);
int EQ2Emu_lua_SendNewAdventureSpells(lua_State* state);
int EQ2Emu_lua_SendNewTradeskillSpells(lua_State* state);
int EQ2Emu_lua_HasSpell(lua_State* state);
int EQ2Emu_lua_Attack(lua_State* state);
int EQ2Emu_lua_ApplySpellVisual(lua_State* state);
int EQ2Emu_lua_Interrupt(lua_State* state);
int EQ2Emu_lua_Stealth(lua_State* state);
int EQ2Emu_lua_IsStealthed(lua_State* state);
int EQ2Emu_lua_IsInvis(lua_State* state);
int EQ2Emu_lua_AddSpawnIDAccess(lua_State* state);
int EQ2Emu_lua_RemoveSpawnIDAccess(lua_State* state);
int EQ2Emu_lua_HasRecipeBook(lua_State* state);
int EQ2Emu_lua_SpawnMove(lua_State* state);
int EQ2Emu_lua_AddTransportSpawn(lua_State* state);
int EQ2Emu_lua_IsTransportSpawn(lua_State* state);
int EQ2Emu_lua_PerformCameraShake(lua_State* state);
//Quest Stuff
int EQ2Emu_lua_SetStepComplete(lua_State* state);
int EQ2Emu_lua_AddStepProgress(lua_State* state);
int EQ2Emu_lua_GetTaskGroupStep(lua_State* state);
int EQ2Emu_lua_QuestStepIsComplete(lua_State* state);
int EQ2Emu_lua_GetQuestStep(lua_State* state);
int EQ2Emu_lua_RegisterQuest(lua_State* state);
int EQ2Emu_lua_OfferQuest(lua_State* state);
int EQ2Emu_lua_DeleteQuest(lua_State* state);
int EQ2Emu_lua_DeleteAllQuests(lua_State* state);
int EQ2Emu_lua_SetQuestPrereqLevel(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqQuest(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqItem(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqFaction(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqClass(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqRace(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqModelType(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqTradeskillLevel(lua_State* state);
int EQ2Emu_lua_AddQuestPrereqTradeskillClass(lua_State* state);
int EQ2Emu_lua_HasQuestRewardItem(lua_State* state);
int EQ2Emu_lua_AddQuestRewardItem(lua_State* state);
int EQ2Emu_lua_AddQuestSelectableRewardItem(lua_State* state);
int EQ2Emu_lua_AddQuestRewardCoin(lua_State* state);
int EQ2Emu_lua_AddQuestRewardFaction(lua_State* state);
int EQ2Emu_lua_SetQuestRewardStatus(lua_State* state);
int EQ2Emu_lua_SetStatusTmpReward(lua_State* state);
int EQ2Emu_lua_SetCoinTmpReward(lua_State* state);
int EQ2Emu_lua_SetQuestRewardComment(lua_State* state);
int EQ2Emu_lua_SetQuestRewardExp(lua_State* state);
int EQ2Emu_lua_AddQuestStep(lua_State* state);
int EQ2Emu_lua_AddQuestStepKillLogic(lua_State* state);
int EQ2Emu_lua_AddQuestStepKill(lua_State* state);
int EQ2Emu_lua_AddQuestStepKillByRace(lua_State* state);
int EQ2Emu_lua_AddQuestStepChat(lua_State* state);
int EQ2Emu_lua_AddQuestStepObtainItem(lua_State* state);
int EQ2Emu_lua_AddQuestStepZoneLoc(lua_State* state);
int EQ2Emu_lua_AddQuestStepLocation(lua_State* state);
int EQ2Emu_lua_AddQuestStepLoc(lua_State* state);
int EQ2Emu_lua_AddQuestStepSpell(lua_State* state);
int EQ2Emu_lua_AddQuestStepCraft(lua_State* state);
int EQ2Emu_lua_AddQuestStepHarvest(lua_State* state);
int EQ2Emu_lua_AddQuestStepCompleteAction(lua_State* state);
int EQ2Emu_lua_AddQuestStepProgressAction(lua_State* state);
int EQ2Emu_lua_SetQuestCompleteAction(lua_State* state);
int EQ2Emu_lua_GiveQuestReward(lua_State* state);
int EQ2Emu_lua_UpdateQuestTaskGroupDescription(lua_State* state);
int EQ2Emu_lua_UpdateQuestStepDescription(lua_State* state);
int EQ2Emu_lua_UpdateQuestDescription(lua_State* state);
int EQ2Emu_lua_UpdateQuestZone(lua_State* state);
int EQ2Emu_lua_SetCompletedDescription(lua_State* state);
int EQ2Emu_lua_ProvidesQuest(lua_State* state);
int EQ2Emu_lua_HasQuest(lua_State* state);
int EQ2Emu_lua_HasPendingQuest(lua_State* state);
int EQ2Emu_lua_HasCompletedQuest(lua_State* state);
int EQ2Emu_lua_QuestIsComplete(lua_State* state);
int EQ2Emu_lua_QuestReturnNPC(lua_State* state);
int EQ2Emu_lua_GetQuest(lua_State* state);
int EQ2Emu_lua_AddTimer(lua_State* state);
int EQ2Emu_lua_StopTimer(lua_State* state);
int EQ2Emu_lua_Harvest(lua_State* state);
int EQ2Emu_lua_SetCompleteFlag(lua_State* state);
int EQ2Emu_lua_CanReceiveQuest(lua_State* state);
int EQ2Emu_lua_HasCollectionsToHandIn(lua_State *state);
int EQ2Emu_lua_HandInCollections(lua_State *state);
int EQ2Emu_lua_UseWidget(lua_State* state);
int EQ2Emu_lua_SummonPet(lua_State* state);
int EQ2Emu_lua_Charm(lua_State* state);
int EQ2Emu_lua_SetSpellList(lua_State* state);
int EQ2Emu_lua_GetPet(lua_State* state);
int EQ2Emu_lua_GetGroup(lua_State* state);
int EQ2Emu_lua_CreateOptionWindow(lua_State* state);
int EQ2Emu_lua_AddOptionWindowOption(lua_State* state);
int EQ2Emu_lua_SendOptionWindow(lua_State* state);
int EQ2Emu_lua_GetTradeskillClass(lua_State* state);
int EQ2Emu_lua_GetTradeskillLevel(lua_State* state);
int EQ2Emu_lua_GetTradeskillClassName(lua_State* state);
int EQ2Emu_lua_SetTradeskillLevel(lua_State* state);
int EQ2Emu_lua_SummonDeityPet(lua_State* state);
int EQ2Emu_lua_SummonCosmeticPet(lua_State* state);
int EQ2Emu_lua_DismissPet(lua_State* state);
int EQ2Emu_lua_GetCharmedPet(lua_State* state);
int EQ2Emu_lua_GetDeityPet(lua_State* state);
int EQ2Emu_lua_GetCosmeticPet(lua_State* state);
int EQ2Emu_lua_SetQuestFeatherColor(lua_State* state);
int EQ2Emu_lua_RemoveSpawnAccess(lua_State* state);
int EQ2Emu_lua_SpawnByLocationID(lua_State* state);
int EQ2Emu_lua_SpawnGroupByID(lua_State* state);
int EQ2Emu_lua_CastEntityCommand(lua_State* state);
int EQ2Emu_lua_SetLuaBrain(lua_State* state);
int EQ2Emu_lua_SetBrainTick(lua_State* state);
int EQ2Emu_lua_SetFollowTarget(lua_State* state);
int EQ2Emu_lua_GetFollowTarget(lua_State* state);
int EQ2Emu_lua_ToggleFollow(lua_State* state);
int EQ2Emu_lua_IsFollowing(lua_State* state);
int EQ2Emu_lua_SetTempVariable(lua_State* state);
int EQ2Emu_lua_GetTempVariable(lua_State* state);
int EQ2Emu_lua_GiveQuestItem(lua_State*state);
int EQ2Emu_lua_SetQuestRepeatable(lua_State* state);
int EQ2Emu_lua_AddWaypoint(lua_State* state);
int EQ2Emu_lua_RemoveWaypoint(lua_State* state);
int EQ2Emu_lua_SendWaypoints(lua_State* state);
int EQ2Emu_lua_AddWard(lua_State* state);
int EQ2Emu_lua_AddToWard(lua_State* state);
int EQ2Emu_lua_RemoveWard(lua_State* state);
int EQ2Emu_lua_GetWardAmountLeft(lua_State* state);
int EQ2Emu_lua_GetWardValue(lua_State* state);
//Combat AI related
int EQ2Emu_lua_SetTarget(lua_State* state);
int EQ2Emu_lua_IsPet(lua_State* state);
int EQ2Emu_lua_GetOwner(lua_State* state);
int EQ2Emu_lua_SetInCombat(lua_State* state);
int EQ2Emu_lua_CompareSpawns(lua_State* state);
int EQ2Emu_lua_ClearRunback(lua_State* state);
int EQ2Emu_lua_Runback(lua_State* state);
int EQ2Emu_lua_GetRunbackDistance(lua_State* state);
int EQ2Emu_lua_IsCasting(lua_State* state);
int EQ2Emu_lua_IsMezzed(lua_State* state);
int EQ2Emu_lua_IsStunned(lua_State* state);
int EQ2Emu_lua_IsMezzedOrStunned(lua_State* state);
int EQ2Emu_lua_ClearEncounter(lua_State* state);
int EQ2Emu_lua_ClearHate(lua_State* state);
int EQ2Emu_lua_GetMostHated(lua_State* state);
int EQ2Emu_lua_GetEncounterSize(lua_State* state);
int EQ2Emu_lua_HasRecovered(lua_State* state);
int EQ2Emu_lua_ProcessMelee(lua_State* state);
int EQ2Emu_lua_ProcessSpell(lua_State* state);
int EQ2Emu_lua_GetEncounter(lua_State* state);
int EQ2Emu_lua_GetHateList(lua_State* state);
int EQ2Emu_lua_HasGroup(lua_State* state);
int EQ2Emu_lua_HasSpellEffect(lua_State* state);
int EQ2Emu_lua_SetSuccessTimer(lua_State* state);
int EQ2Emu_lua_SetFailureTimer(lua_State* state);
int EQ2Emu_lua_IsGroundSpawn(lua_State* state);
int EQ2Emu_lua_CanHarvest(lua_State* state);
int EQ2Emu_lua_SummonDumbFirePet(lua_State* state);
int EQ2Emu_lua_GetSkillValue(lua_State* state);
int EQ2Emu_lua_GetSkillMaxValue(lua_State* state);
int EQ2Emu_lua_GetSkillName(lua_State* state);
int EQ2Emu_lua_SetSkillMaxValue(lua_State* state);
int EQ2Emu_lua_SetSkillValue(lua_State* state);
int EQ2Emu_lua_GetSkill(lua_State* state);
int EQ2Emu_lua_GetSkillIDByName(lua_State* state);
int EQ2Emu_lua_HasSkill(lua_State* state);
int EQ2Emu_lua_AddSkill(lua_State* state);
int EQ2Emu_lua_RemoveSkill(lua_State* state);
int EQ2Emu_lua_IncreaseSkillCapsByType(lua_State* state);
int EQ2Emu_lua_AddProc(lua_State* state);
int EQ2Emu_lua_AddProcExt(lua_State* state);
int EQ2Emu_lua_RemoveProc(lua_State* state);
int EQ2Emu_lua_Knockback(lua_State* state);
int EQ2Emu_lua_IsEpic(lua_State* state);
int EQ2Emu_lua_IsHeroic(lua_State* state);
int EQ2Emu_lua_ProcDamage(lua_State* state);
int EQ2Emu_lua_LastSpellAttackHit(lua_State* state);
int EQ2Emu_lua_IsBehind(lua_State* state);
int EQ2Emu_lua_IsFlanking(lua_State* state);
int EQ2Emu_lua_InFront(lua_State* state);
int EQ2Emu_lua_AddSpellTimer(lua_State* state);
int EQ2Emu_lua_SetItemCount(lua_State* state);
int EQ2Emu_lua_GetItemCount(lua_State* state);
int EQ2Emu_lua_Resurrect(lua_State* state);
int EQ2Emu_lua_BreatheUnderwater(lua_State* state);
int EQ2Emu_lua_BlurVision(lua_State* state);
int EQ2Emu_lua_SetVision(lua_State* state);
int EQ2Emu_lua_GetItemSkillReq(lua_State* state);
int EQ2Emu_lua_SetSpeedMultiplier(lua_State* state);
int EQ2Emu_lua_SetIllusion(lua_State* state);
int EQ2Emu_lua_ResetIllusion(lua_State* state);
int EQ2Emu_lua_AddThreatTransfer(lua_State* state);
int EQ2Emu_lua_RemoveThreatTransfer(lua_State* state);
int EQ2Emu_lua_CureByType(lua_State* state);
int EQ2Emu_lua_CureByControlEffect(lua_State* state);
int EQ2Emu_lua_AddSpawnSpellBonus(lua_State* state);
int EQ2Emu_lua_RemoveSpawnSpellBonus(lua_State* state);
int EQ2Emu_lua_CancelSpell(lua_State* state);
int EQ2Emu_lua_RemoveStealth(lua_State* state);
int EQ2Emu_lua_RemoveInvis(lua_State* state);
int EQ2Emu_lua_StartHeroicOpportunity(lua_State* state);
int EQ2Emu_lua_CopySpawnAppearance(lua_State* state);
int EQ2Emu_lua_RemoveTriggerFromSpell(lua_State* state);
int EQ2Emu_lua_GetSpellTriggerCount(lua_State* state);
int EQ2Emu_lua_SetSpellTriggerCount(lua_State* state);
int EQ2Emu_lua_HasSpellImmunity(lua_State* state);
int EQ2Emu_lua_AddImmunitySpell(lua_State* state);
int EQ2Emu_lua_RemoveImmunitySpell(lua_State* state);
int EQ2Emu_lua_SetSpellSnareValue(lua_State* state);
int EQ2Emu_lua_CheckRaceType(lua_State* state);
int EQ2Emu_lua_GetRaceType(lua_State* state);
int EQ2Emu_lua_GetRaceBaseType(lua_State* state);
int EQ2Emu_lua_GetQuestFlags(lua_State* state);
int EQ2Emu_lua_SetQuestFlags(lua_State* state);
int EQ2Emu_lua_SetQuestTimer(lua_State* state);
int EQ2Emu_lua_RemoveQuestStep(lua_State* state);
int EQ2Emu_lua_ResetQuestStep(lua_State* state);
int EQ2Emu_lua_SetQuestTimerComplete(lua_State* state);
int EQ2Emu_lua_AddQuestStepFailureAction(lua_State* state);
int EQ2Emu_lua_SetStepFailed(lua_State* state);
int EQ2Emu_lua_GetQuestCompleteCount(lua_State* state);
int EQ2Emu_lua_SetServerVariable(lua_State* state);
int EQ2Emu_lua_GetServerVariable(lua_State* state);
int EQ2Emu_lua_HasLanguage(lua_State* state);
int EQ2Emu_lua_AddLanguage(lua_State* state);
int EQ2Emu_lua_IsNight(lua_State* state);
int EQ2Emu_lua_AddMultiFloorLift(lua_State* state);
int EQ2Emu_lua_StartAutoMount(lua_State* state);
int EQ2Emu_lua_EndAutoMount(lua_State* state);
int EQ2Emu_lua_IsOnAutoMount(lua_State* state);
int EQ2Emu_lua_SetPlayerHistory(lua_State* state);
int EQ2Emu_lua_GetPlayerHistory(lua_State* state);
int EQ2Emu_lua_SetGridID(lua_State* state);
int EQ2Emu_lua_GetQuestStepProgress(lua_State* state);
int EQ2Emu_lua_SetPlayerLevel(lua_State* state);
int EQ2Emu_lua_AddCoin(lua_State* state);
int EQ2Emu_lua_RemoveCoin(lua_State* state);
int EQ2Emu_lua_GetPlayersInZone(lua_State* state);
int EQ2Emu_lua_SetSpawnAnimation(lua_State* state);
int EQ2Emu_lua_GetClientVersion(lua_State* state);
int EQ2Emu_lua_GetItemID(lua_State* state);
int EQ2Emu_lua_IsEntity(lua_State* state);
int EQ2Emu_lua_GetOrigX(lua_State* state);
int EQ2Emu_lua_GetOrigY(lua_State* state);
int EQ2Emu_lua_GetOrigZ(lua_State* state);
int EQ2Emu_lua_GetPCTOfHP(lua_State* state);
int EQ2Emu_lua_GetPCTOfPower(lua_State* state);
int EQ2Emu_lua_GetBoundZoneID(lua_State* state);
int EQ2Emu_lua_Evac(lua_State* state);
int EQ2Emu_lua_GetSpellTier(lua_State* state);
int EQ2Emu_lua_GetSpellID(lua_State* state);
int EQ2Emu_lua_StartTransmute(lua_State* state);
int EQ2Emu_lua_CompleteTransmute(lua_State* state);
int EQ2Emu_lua_ProcHate(lua_State* state);
int EQ2Emu_lua_GiveExp(lua_State* state);
int EQ2Emu_lua_DisplayText(lua_State* state);
int EQ2Emu_lua_ShowLootWindow(lua_State* state);
int EQ2Emu_lua_GetRandomSpawnByID(lua_State* state);
int EQ2Emu_lua_AddPrimaryEntityCommandAllSpawns(lua_State* state);
int EQ2Emu_lua_InstructionWindow(lua_State* state);
int EQ2Emu_lua_InstructionWindowClose(lua_State* state);
int EQ2Emu_lua_InstructionWindowGoal(lua_State* state);
int EQ2Emu_lua_ShowWindow(lua_State* state);
int EQ2Emu_lua_FlashWindow(lua_State* state);
int EQ2Emu_lua_EnableGameEvent(lua_State* state);
int EQ2Emu_lua_GetTutorialStep(lua_State* state);
int EQ2Emu_lua_SetTutorialStep(lua_State* state);
int EQ2Emu_lua_CheckLOS(lua_State* state);
int EQ2Emu_lua_CheckLOSByCoordinates(lua_State* state);
int EQ2Emu_lua_SetZoneExpansionFlag(lua_State* state);
int EQ2Emu_lua_GetZoneExpansionFlag(lua_State* state);
int EQ2Emu_lua_SetZoneHolidayFlag(lua_State* state);
int EQ2Emu_lua_GetZoneHolidayFlag(lua_State* state);
int EQ2Emu_lua_SetCanBind(lua_State* state);
int EQ2Emu_lua_GetCanBind(lua_State* state);
int EQ2Emu_lua_GetCanGate(lua_State* state);
int EQ2Emu_lua_SetCanGate(lua_State* state);
int EQ2Emu_lua_GetCanEvac(lua_State* state);
int EQ2Emu_lua_SetCanEvac(lua_State* state);
int EQ2Emu_lua_AddSpawnProximity(lua_State* state);
int EQ2Emu_lua_CanSeeInvis(lua_State* state);
int EQ2Emu_lua_SetSeeInvis(lua_State* state);
int EQ2Emu_lua_SetSeeHide(lua_State* state);
int EQ2Emu_lua_SetAccessToEntityCommand(lua_State* state);
int EQ2Emu_lua_SetAccessToEntityCommandByCharID(lua_State* state);
int EQ2Emu_lua_RemovePrimaryEntityCommand(lua_State* state);
int EQ2Emu_lua_SendUpdateDefaultCommand(lua_State* state);
int EQ2Emu_lua_SendTransporters(lua_State* state);
int EQ2Emu_lua_SetTemporaryTransportID(lua_State* state);
int EQ2Emu_lua_GetTemporaryTransportID(lua_State* state);
int EQ2Emu_lua_GetAlignment(lua_State* state);
int EQ2Emu_lua_SetAlignment(lua_State* state);
int EQ2Emu_lua_GetSpell(lua_State* state);
int EQ2Emu_lua_GetSpellData(lua_State* state);
int EQ2Emu_lua_SetSpellData(lua_State* state);
int EQ2Emu_lua_CastCustomSpell(lua_State* state);
int EQ2Emu_lua_SetSpellDataIndex(lua_State* state);
int EQ2Emu_lua_GetSpellDataIndex(lua_State* state);
int EQ2Emu_lua_SetSpellDisplayEffect(lua_State* state);
int EQ2Emu_lua_GetSpellDisplayEffect(lua_State* state);
int EQ2Emu_lua_InWater(lua_State* state);
int EQ2Emu_lua_InLava(lua_State* state);
int EQ2Emu_lua_DamageSpawn(lua_State* state);
int EQ2Emu_lua_IsInvulnerable(lua_State* state);
int EQ2Emu_lua_SetInvulnerable(lua_State* state);
int EQ2Emu_lua_GetRuleFlagBool(lua_State* state);
int EQ2Emu_lua_GetRuleFlagInt32(lua_State* state);
int EQ2Emu_lua_GetRuleFlagFloat(lua_State* state);
int EQ2Emu_lua_GetAAInfo(lua_State* state);
int EQ2Emu_lua_SetAAInfo(lua_State* state);
int EQ2Emu_lua_AddMasterTitle(lua_State* state);
int EQ2Emu_lua_AddCharacterTitle(lua_State* state);
int EQ2Emu_lua_SetCharacterTitleSuffix(lua_State* state);
int EQ2Emu_lua_SetCharacterTitlePrefix(lua_State* state);
int EQ2Emu_lua_ResetCharacterTitleSuffix(lua_State* state);
int EQ2Emu_lua_ResetCharacterTitlePrefix(lua_State* state);
int EQ2Emu_lua_GetInfoStructString(lua_State* state);
int EQ2Emu_lua_GetInfoStructUInt(lua_State* state);
int EQ2Emu_lua_GetInfoStructSInt(lua_State* state);
int EQ2Emu_lua_GetInfoStructFloat(lua_State* state);
int EQ2Emu_lua_SetInfoStructString(lua_State* state);
int EQ2Emu_lua_SetInfoStructUInt(lua_State* state);
int EQ2Emu_lua_SetInfoStructSInt(lua_State* state);
int EQ2Emu_lua_SetInfoStructFloat(lua_State* state);
int EQ2Emu_lua_SetCharSheetChanged(lua_State* state);
int EQ2Emu_lua_AddPlayerMail(lua_State* state);
int EQ2Emu_lua_AddPlayerMailByCharID(lua_State* state);
int EQ2Emu_lua_OpenDoor(lua_State* state);
int EQ2Emu_lua_CloseDoor(lua_State* state);
int EQ2Emu_lua_IsOpen(lua_State* state);
int EQ2Emu_lua_MakeRandomInt(lua_State* state);
int EQ2Emu_lua_MakeRandomFloat(lua_State* state);
int EQ2Emu_lua_AddIconValue(lua_State* state);
int EQ2Emu_lua_RemoveIconValue(lua_State* state);
int EQ2Emu_lua_GetShardID(lua_State* state);
int EQ2Emu_lua_GetShardCharID(lua_State* state);
int EQ2Emu_lua_GetShardCreatedTimestamp(lua_State* state);
int EQ2Emu_lua_DeleteDBShardID(lua_State* state);
int EQ2Emu_lua_PauseMovement(lua_State* state);
int EQ2Emu_lua_StopMovement(lua_State* state);
int EQ2Emu_lua_GetArrowColor(lua_State* state);
int EQ2Emu_lua_GetTSArrowColor(lua_State* state);
int EQ2Emu_lua_GetSpawnByRailID(lua_State* state);
int EQ2Emu_lua_SetRailID(lua_State* state);
int EQ2Emu_lua_IsZoneLoading(lua_State* state);
int EQ2Emu_lua_IsRunning(lua_State* state);
int EQ2Emu_lua_GetZoneLockoutTimer(lua_State* state);
int EQ2Emu_lua_SetWorldTime(lua_State* state);
int EQ2Emu_lua_GetWorldTimeYear(lua_State* state);
int EQ2Emu_lua_GetWorldTimeMonth(lua_State* state);
int EQ2Emu_lua_GetWorldTimeHour(lua_State* state);
int EQ2Emu_lua_GetWorldTimeMinute(lua_State* state);
int EQ2Emu_lua_SendTimeUpdate(lua_State* state);
int EQ2Emu_lua_SetLootTier(lua_State* state);
int EQ2Emu_lua_GetLootTier(lua_State* state);
int EQ2Emu_lua_SetLootDropType(lua_State* state);
int EQ2Emu_lua_GetLootDropType(lua_State* state);
int EQ2Emu_lua_DamageEquippedItems(lua_State* state);
int EQ2Emu_lua_CreateWidgetRegion(lua_State* state);
int EQ2Emu_lua_RemoveRegion(lua_State* state);
int EQ2Emu_lua_SetPlayerPOVGhost(lua_State* state);
int EQ2Emu_lua_SetCastOnAggroComplete(lua_State* state);
int EQ2Emu_lua_IsCastOnAggroComplete(lua_State* state);
int EQ2Emu_lua_AddRecipeBookToPlayer(lua_State* state);
int EQ2Emu_lua_RemoveRecipeFromPlayer(lua_State* state);
int EQ2Emu_lua_ReplaceWidgetFromClient(lua_State* state);
int EQ2Emu_lua_RemoveWidgetFromSpawnMap(lua_State* state);
int EQ2Emu_lua_RemoveWidgetFromZoneMap(lua_State* state);
int EQ2Emu_lua_SendHearCast(lua_State* state);
int EQ2Emu_lua_GetCharacterFlag(lua_State* state);
int EQ2Emu_lua_ToggleCharacterFlag(lua_State* state);
int EQ2Emu_lua_GetSpellInitialTarget(lua_State* state);
int EQ2Emu_lua_GetSpellCaster(lua_State* state);
int EQ2Emu_lua_GetCasterSpellLevel(lua_State* state);
int EQ2Emu_lua_GetSpellTargets(lua_State* state);
int EQ2Emu_lua_DespawnByLocationID(lua_State* state);
int EQ2Emu_lua_AddRespawn(lua_State* state);
int EQ2Emu_lua_CreatePersistedRespawn(lua_State* state);
int EQ2Emu_lua_CreateChoiceWindow(lua_State* state);
int EQ2Emu_lua_ClearChoice(lua_State* state);
int EQ2Emu_lua_GetChoiceSpawnID(lua_State* state);
int EQ2Emu_lua_GetZonePlayerMinLevel(lua_State* state);
int EQ2Emu_lua_GetZonePlayerMaxLevel(lua_State* state);
int EQ2Emu_lua_GetZonePlayerAvgLevel(lua_State* state);
int EQ2Emu_lua_GetZonePlayerFirstLevel(lua_State* state);
int EQ2Emu_lua_GetSpellRequiredLevel(lua_State* state);
int EQ2Emu_lua_GetExpRequiredByLevel(lua_State* state);
int EQ2Emu_lua_ShowShopWindow(lua_State* state);
int EQ2Emu_lua_SetSpawnHouseScript(lua_State* state);
int EQ2Emu_lua_SendBook(lua_State* state);
int EQ2Emu_lua_GetPickupItemID(lua_State* state);
int EQ2Emu_lua_SetHouseCharacterID(lua_State* state);
int EQ2Emu_lua_GetHouseCharacterID(lua_State* state);
int EQ2Emu_lua_ShowHouseShopMerchant(lua_State* state);
int EQ2Emu_lua_AttackAllowed(lua_State* state);
int EQ2Emu_lua_IsInRaid(lua_State* state);
int EQ2Emu_lua_InSameRaid(lua_State* state);
int EQ2Emu_lua_GetRaid(lua_State* state);
int EQ2Emu_lua_AdjustHatePosition(lua_State* state);
int EQ2Emu_lua_RemoveCharacterProperty(lua_State* state);
int EQ2Emu_lua_RemoveSpell(lua_State* state);
#endif

3265
old/world/LuaInterface.cpp Normal file

File diff suppressed because it is too large Load Diff

621
old/world/LuaInterface.h Normal file
View File

@ -0,0 +1,621 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LUA_INTERFACE_H
#define LUA_INTERFACE_H
#include <mutex>
#include <shared_mutex>
#include <unordered_set>
#include "Spawn.h"
#include "Spells.h"
#include "../common/Mutex.h"
#include "Quests.h"
#include "zoneserver.h"
#include "client.h"
#include "EffectFlags.h"
#include <lua.hpp>
using namespace std;
struct ConversationOption{
string option;
string function;
};
struct OptionWindowOption {
string optionName;
string optionDescription;
string optionCommand;
int32 optionIconSheet;
int16 optionIconID;
string optionConfirmTitle;
};
//Bitmask Values
#define EFFECT_FLAG_STUN 1
#define EFFECT_FLAG_ROOT 2
#define EFFECT_FLAG_MEZ 4
#define EFFECT_FLAG_STIFLE 8
#define EFFECT_FLAG_DAZE 16
#define EFFECT_FLAG_FEAR 32
#define EFFECT_FLAG_SPELLBONUS 64
#define EFFECT_FLAG_SKILLBONUS 128
#define EFFECT_FLAG_STEALTH 256
#define EFFECT_FLAG_INVIS 512
#define EFFECT_FLAG_SNARE 1024
#define EFFECT_FLAG_WATERWALK 2048
#define EFFECT_FLAG_WATERJUMP 4096
#define EFFECT_FLAG_FLIGHT 8192
#define EFFECT_FLAG_GLIDE 16384
#define EFFECT_FLAG_AOE_IMMUNE 32768
#define EFFECT_FLAG_STUN_IMMUNE 65536
#define EFFECT_FLAG_MEZ_IMMUNE 131072
#define EFFECT_FLAG_DAZE_IMMUNE 262144
#define EFFECT_FLAG_ROOT_IMMUNE 524288
#define EFFECT_FLAG_STIFLE_IMMUNE 1048576
#define EFFECT_FLAG_FEAR_IMMUNE 2097152
#define EFFECT_FLAG_SAFEFALL 4194304
enum class SpellFieldType {
Integer,
Float,
Boolean,
String
};
using SpellFieldGetter = std::function<std::string(SpellData*)>;
extern const std::unordered_map<std::string, std::function<void(Spell*, const std::string&)>> SpellFieldGenericSetters;
extern const std::unordered_map<std::string, std::pair<SpellFieldType, SpellFieldGetter>> SpellDataFieldAccessors;
extern std::unordered_map<std::string, std::function<void(Spell*, const std::string&)>> SpellDataFieldSetters;
struct LuaSpell{
Entity* caster;
int32 initial_caster_char_id;
int32 initial_target;
int32 initial_target_char_id;
mutable std::shared_mutex targets_mutex;
vector<int32> targets;
vector<int32> removed_targets; // previously cancelled, expired, used, so on
mutable std::shared_mutex char_id_targets_mutex;
multimap<int32, int8> char_id_targets;
Spell* spell;
lua_State* state;
string file_name;
Timer timer;
bool is_recast_timer;
int16 num_calls;
int16 num_triggers;
int8 slot_pos;
int32 damage_remaining;
bool resisted;
bool has_damaged;
bool is_damage_spell;
bool interrupted;
bool crit;
bool last_spellattack_hit;
bool cancel_after_all_triggers;
bool had_triggers;
bool had_dmg_remaining;
Mutex MScriptMutex;
EffectFlags effect_flags;
bool restored; // restored spell cross zone
std::atomic<bool> has_proc;
ZoneServer* zone;
int16 initial_caster_level;
bool is_loaded_recast;
std::unordered_set<std::string> modified_fields;
mutable std::shared_mutex spell_modify_mutex;
void AddTarget(int32 target_id) {
std::unique_lock lock(targets_mutex);
bool hasTarget = std::find(targets.begin(), targets.end(), target_id) != targets.end();
if(!hasTarget)
targets.push_back(target_id);
}
int32 GetPrimaryTargetID() const {
std::shared_lock lock(targets_mutex);
return targets.empty() ? -1 : targets[0];
}
std::optional<int32_t> GetPrimaryTarget() const {
std::shared_lock lock(targets_mutex);
if (!targets.empty())
return targets[0];
return std::nullopt;
}
std::vector<int32> GetTargets() {
std::shared_lock lock(targets_mutex);
return targets;
}
bool HasNoTargets() const {
std::shared_lock lock(targets_mutex);
return targets.empty();
}
int32 GetTargetCount() const {
std::shared_lock lock(targets_mutex);
return static_cast<int32>(targets.size());
}
bool HasTarget(int32 id) const {
std::shared_lock lock(targets_mutex);
return std::find(targets.begin(), targets.end(), id) != targets.end();
}
bool HasAnyTarget(const std::vector<int32>& ids) const {
std::shared_lock lock(targets_mutex);
return std::any_of(
ids.begin(), ids.end(),
[this](int32 id){
return std::find(targets.begin(), targets.end(), id) != targets.end();
}
);
}
std::vector<int32> GetRemovedTargets() const {
std::shared_lock lock(targets_mutex);
return removed_targets;
}
void RemoveTarget(int32 target_id) {
std::unique_lock lock(targets_mutex);
auto& v = targets;
v.erase(std::remove(v.begin(), v.end(), target_id), v.end());
removed_targets.push_back(target_id);
}
void AddRemoveTarget(int32 target_id) {
std::unique_lock lock(targets_mutex);
removed_targets.push_back(target_id);
}
void SwapTargets(std::vector<int32>& new_targets) {
std::unique_lock lock(targets_mutex);
targets.swap(new_targets);
}
void ClearTargets() {
std::unique_lock lock(targets_mutex);
targets.clear();
removed_targets.clear();
}
std::multimap<int32, int8> GetCharIDTargets() const {
std::shared_lock lock(char_id_targets_mutex);
return char_id_targets;
}
void AddCharIDTarget(int32 char_id, int8 value) {
std::unique_lock lock(char_id_targets_mutex);
bool exists = false;
auto range = char_id_targets.equal_range(char_id);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == value) {
exists = true;
break;
}
}
if(!exists)
char_id_targets.insert({char_id, value});
}
bool HasNoCharIDTargets() const {
std::shared_lock lock(char_id_targets_mutex);
return char_id_targets.empty();
}
void RemoveCharIDTarget(int32 char_id) {
std::unique_lock lock(char_id_targets_mutex);
char_id_targets.erase(char_id); // removes all entries with that key
}
void RemoveCharIDTargetAndType(int32 char_id, int8 type) {
std::unique_lock lock(char_id_targets_mutex);
auto range = char_id_targets.equal_range(char_id);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == type) {
char_id_targets.erase(it);
break; // remove only one matching pair
}
}
}
void ClearCharTargets() {
std::unique_lock lock(char_id_targets_mutex);
char_id_targets.clear();
}
void MarkFieldModified(const std::string& field) {
std::unique_lock lock(spell_modify_mutex);
modified_fields.insert(field);
}
bool IsFieldModified(const std::string& field) const {
std::shared_lock lock(spell_modify_mutex);
return modified_fields.find(field) != modified_fields.end();
}
void ClearFieldModifications() {
std::unique_lock lock(spell_modify_mutex);
modified_fields.clear();
}
std::unordered_set<std::string> GetModifiedFieldsCopy() const {
std::shared_lock lock(spell_modify_mutex);
return modified_fields; // safe shallow copy
}
bool SetSpellDataGeneric(const std::string& field, int value) {
return SetSpellDataGeneric(field, std::to_string(value));
}
bool SetSpellDataGeneric(const std::string& field, float value) {
return SetSpellDataGeneric(field, std::to_string(value));
}
bool SetSpellDataGeneric(const std::string& field, bool value) {
return SetSpellDataGeneric(field, value ? "1" : "0");
}
bool SetSpellDataGeneric(const std::string& field, const std::string& value) {
auto it = SpellFieldGenericSetters.find(field);
if (it == SpellFieldGenericSetters.end())
return false;
if (!spell)
return false;
it->second(spell, value);
return true;
}
bool SetSpellDataIndex(int idx, const std::string& value, const std::string& value2 = "");
bool SetSpellDataIndex(int idx, int value, int value2) {
return SetSpellDataIndex(idx, std::to_string(value), std::to_string(value2));
}
bool SetSpellDataIndex(int idx, float value, float value2) {
return SetSpellDataIndex(idx, std::to_string(value), std::to_string(value2));
}
bool SetSpellDataIndex(int idx, bool value) {
return SetSpellDataIndex(idx, value ? "1" : "0");
}
};
enum class LuaArgType { SINT64, INT64, SINT, INT, FLOAT, STRING, BOOL, SPAWN, ZONE, SKILL, ITEM, QUEST, SPELL /* etc */ };
struct LuaArg {
LuaArgType type;
union {
sint64 si;
sint64 i;
int32 low_i;
sint32 low_si;
float f;
bool b;
};
std::string s;
Spawn* spawn = nullptr;
ZoneServer* zone = nullptr;
Skill* skill = nullptr;
Item* item = nullptr;
Quest* quest = nullptr;
LuaSpell* spell = nullptr;
LuaArg(sint64 val) : type(LuaArgType::SINT64), si(val) {}
LuaArg(sint32 val) : type(LuaArgType::SINT), low_si(val) {}
LuaArg(sint16 val) : type(LuaArgType::SINT), low_si(val) {}
LuaArg(sint8 val) : type(LuaArgType::SINT), low_si(val) {}
LuaArg(int64 val) : type(LuaArgType::INT64), i(val) {}
LuaArg(int32 val) : type(LuaArgType::INT), low_i(val) {}
LuaArg(int16 val) : type(LuaArgType::INT), low_i(val) {}
LuaArg(int8 val) : type(LuaArgType::INT), low_i(val) {}
LuaArg(float val) : type(LuaArgType::FLOAT), f(val) {}
LuaArg(bool val) : type(LuaArgType::BOOL), b(val) {}
LuaArg(const std::string& val) : type(LuaArgType::STRING), s(val) {}
LuaArg(Spawn* val) : type(LuaArgType::SPAWN), spawn(val) {}
LuaArg(ZoneServer* val) : type(LuaArgType::ZONE), zone(val) {}
LuaArg(Skill* val) : type(LuaArgType::SKILL), skill(val) {}
LuaArg(Item* val) : type(LuaArgType::ITEM), item(val) {}
LuaArg(Quest* val) : type(LuaArgType::QUEST), quest(val) {}
LuaArg(LuaSpell* val) : type(LuaArgType::SPELL), spell(val) {}
};
class LUAUserData{
public:
LUAUserData();
virtual ~LUAUserData(){};
virtual bool IsCorrectlyInitialized();
virtual bool IsConversationOption();
virtual bool IsOptionWindow();
virtual bool IsSpawn();
virtual bool IsQuest();
virtual bool IsZone();
virtual bool IsItem();
virtual bool IsSkill();
virtual bool IsSpell();
bool correctly_initialized;
Item* item;
ZoneServer* zone;
Spawn* spawn;
vector<ConversationOption>* conversation_options;
vector<OptionWindowOption>* option_window_option;
vector<Spawn*>* spawn_list;
Quest* quest;
Skill* skill;
LuaSpell* spell;
};
class LUAConversationOptionWrapper : public LUAUserData{
public:
LUAConversationOptionWrapper();
bool IsConversationOption();
};
class LUAOptionWindowWrapper : public LUAUserData {
public:
LUAOptionWindowWrapper();
bool IsOptionWindow();
};
class LUASpawnWrapper : public LUAUserData{
public:
LUASpawnWrapper();
bool IsSpawn();
};
class LUAZoneWrapper : public LUAUserData{
public:
LUAZoneWrapper();
bool IsZone();
};
class LUAQuestWrapper : public LUAUserData{
public:
LUAQuestWrapper();
bool IsQuest();
};
class LUAItemWrapper : public LUAUserData{
public:
LUAItemWrapper();
bool IsItem();
};
class LUASkillWrapper: public LUAUserData {
public:
LUASkillWrapper();
bool IsSkill();
};
class LUASpellWrapper : public LUAUserData {
public:
LUASpellWrapper();
bool IsSpell();
};
class LuaInterface {
public:
LuaInterface();
~LuaInterface();
int GetNumberOfArgs(lua_State* state);
bool LoadItemScript(string name);
bool LoadItemScript(const char* name);
bool LoadSpawnScript(string name);
bool LoadSpawnScript(const char* name);
bool LoadZoneScript(string name);
bool LoadZoneScript(const char* name);
bool LoadPlayerScript(string name);
bool LoadPlayerScript(const char* name);
bool LoadRegionScript(string name);
bool LoadRegionScript(const char* name);
LuaSpell* LoadSpellScript(string name);
LuaSpell* LoadSpellScript(const char* name);
void RemoveSpawnFromSpell(LuaSpell* spell, Spawn* spawn);
void RemoveSpell(LuaSpell* spell, bool call_remove_function = true, bool can_delete = true, string reason = "", bool removing_all_spells = false, bool return_after_call_remove = false, Spawn* overrideTarget = nullptr);
Spawn* GetSpawn(lua_State* state, int8 arg_num = 1);
Item* GetItem(lua_State* state, int8 arg_num = 1);
Quest* GetQuest(lua_State* state, int8 arg_num = 1);
ZoneServer* GetZone(lua_State* state, int8 arg_num = 1);
Skill* GetSkill(lua_State* state, int8 arg_num = 1);
LuaSpell* GetSpell(lua_State* state, int8 arg_num = 1);
vector<ConversationOption>* GetConversation(lua_State* state, int8 arg_num = 1);
vector<OptionWindowOption>* GetOptionWindow(lua_State* state, int8 arg_num = 1);
int8 GetInt8Value(lua_State* state, int8 arg_num = 1);
int16 GetInt16Value(lua_State* state, int8 arg_num = 1);
int32 GetInt32Value(lua_State* state, int8 arg_num = 1);
sint32 GetSInt32Value(lua_State* state, int8 arg_num = 1);
int64 GetInt64Value(lua_State* state, int8 arg_num = 1);
sint64 GetSInt64Value(lua_State* state, int8 arg_num = 1);
float GetFloatValue(lua_State* state, int8 arg_num = 1);
string GetStringValue(lua_State* state, int8 arg_num = 1);
bool GetBooleanValue(lua_State*state, int8 arg_num = 1);
void Process();
void SetInt32Value(lua_State* state, int32 value);
void SetSInt32Value(lua_State* state, sint32 value);
void SetInt64Value(lua_State* state, int64 value);
void SetSInt64Value(lua_State* state, sint64 value);
void SetFloatValue(lua_State* state, float value);
void SetBooleanValue(lua_State* state, bool value);
void SetStringValue(lua_State* state, const char* value);
void SetSpawnValue(lua_State* state, Spawn* spawn);
void SetSkillValue(lua_State* state, Skill* skill);
void SetItemValue(lua_State* state, Item* item);
void SetQuestValue(lua_State* state, Quest* quest);
void SetZoneValue(lua_State* state, ZoneServer* zone);
void SetSpellValue(lua_State* state, LuaSpell* spell);
void SetConversationValue(lua_State* state, vector<ConversationOption>* conversation);
void SetOptionWindowValue(lua_State* state, vector<OptionWindowOption>* optionWindow);
std::string AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast = false, const char* function = 0, SpellScriptTimer* timer = 0, bool passLuaSpell=false, Spawn* altTarget = 0);
LuaSpell* GetCurrentSpell(lua_State* state, bool needsLock = true);
void RemoveCurrentSpell(lua_State* state, LuaSpell* cur_spell, bool needsLock = true, bool removeCurSpell = true, bool removeSpellScript = true);
bool CallSpellProcess(LuaSpell* spell, int8 num_parameters, std::string functionCalled);
LuaSpell* GetSpell(const char* name, bool use = true);
void UseItemScript(const char* name, lua_State* state, bool val);
void UseSpawnScript(const char* name, lua_State* state, bool val);
void UseZoneScript(const char* name, lua_State* state, bool val);
void UsePlayerScript(const char* name, lua_State* state, bool val);
void UseRegionScript(const char* name, lua_State* state, bool val);
lua_State* GetItemScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetSpawnScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetZoneScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetPlayerScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetRegionScript(const char* name, bool create_new = true, bool use = false);
LuaSpell* GetSpellScript(const char* name, bool create_new = true, bool use = true);
LuaSpell* CreateSpellScript(const char* name, lua_State* existState);
Quest* LoadQuest(int32 id, const char* name, const char* type, const char* zone, int8 level, const char* description, char* script_name);
const char* GetScriptName(lua_State* state);
void RemoveSpawnScript(const char* name);
bool RunItemScript(string script_name, const char* function_name, Item* item, Spawn* spawn = 0, Spawn* target = 0, sint64* returnValue = 0);
bool RunItemScriptWithReturnString(string script_name, const char* function_name, Item* item, Spawn* spawn = 0, std::string* returnValue = 0);
bool CallItemScript(lua_State* state, int8 num_parameters, std::string* returnValue = 0);
bool CallItemScript(lua_State* state, int8 num_parameters, sint64* returnValue = 0);
bool RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn = 0, const char* message = 0, bool is_door_open = false, sint32 input_value = 0, sint32* return_value = 0);
bool CallSpawnScript(lua_State* state, int8 num_parameters);
bool RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, int32 int32_arg1 = 0, const char* str_arg1 = 0, Spawn* spawn_arg1 = 0, int32 int32_arg2 = 0, const char* str_arg2 = 0, Spawn* spawn_arg2 = 0);
bool RunZoneScriptWithReturn(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, int32 int32_arg1, int32 int32_arg2, int32 int32_arg3, int32* returnValue = 0);
bool RunPlayerScriptWithReturn(const string script_name, const char* function_name, const std::vector<LuaArg>& args, sint32* returnValue = 0);
bool CallScriptInt32(lua_State* state, int8 num_parameters, int32* returnValue = 0);
bool CallScriptSInt32(lua_State* state, int8 num_parameters, sint32* returnValue = 0);
bool RunRegionScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, sint32 int32_arg1 = 0, int32* returnValue = 0);
bool CallRegionScript(lua_State* state, int8 num_parameters, int32* returnValue);
void ResetFunctionStack(lua_State* state);
void DestroySpells();
void DestroySpawnScripts();
void DestroyItemScripts();
void DestroyQuests(bool reload = false);
void DestroyZoneScripts();
void DestroyPlayerScripts();
void DestroyRegionScripts();
void SimpleLogError(const char* error);
void LogError(const char* error, ...);
bool CallQuestFunction(Quest* quest, const char* function, Spawn* player, int32 step_id = 0xFFFFFFFF, int32* returnValue = 0);
void RemoveDebugClients(Client* client);
void UpdateDebugClients(Client* client);
void ProcessErrorMessage(const char* message);
map<Client*, int32> GetDebugClients(){ return debug_clients; }
void AddUserDataPtr(LUAUserData* data, void* data_ptr = 0);
void DeleteUserDataPtrs(bool all);
void DeletePendingSpells(bool all, ZoneServer* zone = nullptr);
void DeletePendingSpell(LuaSpell* spell);
Mutex* GetSpawnScriptMutex(const char* name);
Mutex* GetItemScriptMutex(const char* name);
Mutex* GetZoneScriptMutex(const char* name);
Mutex* GetPlayerScriptMutex(const char* name);
Mutex* GetRegionScriptMutex(const char* name);
Mutex* GetSpellScriptMutex(const char* name);
Mutex* GetQuestMutex(Quest* quest);
void SetLuaSystemReloading(bool val) { lua_system_reloading = val; }
bool IsLuaSystemReloading() { return lua_system_reloading; }
void AddPendingSpellDelete(LuaSpell* spell);
void AddCustomSpell(LuaSpell* spell);
void RemoveCustomSpell(int32 id);
void FindCustomSpellLock() { MCustomSpell.readlock(); }
void FindCustomSpellUnlock() { MCustomSpell.releasereadlock(); }
LuaSpell* FindCustomSpell(int32 id);
int32 GetFreeCustomSpellID();
void SetLuaUserDataStale(void* ptr);
bool IsLuaUserDataValid(void* ptr);
private:
bool shutting_down;
bool lua_system_reloading;
map<LuaSpell*, int32> spells_pending_delete;
Timer* user_data_timer;
Timer* spell_delete_timer;
map<LUAUserData*, int32> user_data;
map<void*, LUAUserData*> user_data_ptr;
map<Client*, int32> debug_clients;
map<lua_State*, LuaSpell*> current_spells;
vector<string>* GetDirectoryListing(const char* directory);
lua_State* LoadLuaFile(const char* name);
void RegisterFunctions(lua_State* state);
map<lua_State*, string> inverse_spells;
map<int32, Quest*> quests;
map<int32, lua_State*> quest_states;
map<string, map<lua_State*, bool> > item_scripts;
map<string, map<lua_State*, bool> > spawn_scripts;
map<string, map<lua_State*, bool> > zone_scripts;
map<string, map<lua_State*, bool> > player_scripts;
map<string, map<lua_State*, bool> > region_scripts;
map<string, map<lua_State*, LuaSpell*> > spell_scripts;
map<int32, LuaSpell*> custom_spells;
std::deque<int32> custom_free_spell_ids;
map<lua_State*, string> item_inverse_scripts;
map<lua_State*, string> spawn_inverse_scripts;
map<lua_State*, string> zone_inverse_scripts;
map<lua_State*, string> player_inverse_scripts;
map<lua_State*, string> region_inverse_scripts;
map<string, Mutex*> item_scripts_mutex;
map<string, Mutex*> spawn_scripts_mutex;
map<string, Mutex*> zone_scripts_mutex;
map<string, Mutex*> player_scripts_mutex;
map<int32, Mutex*> quests_mutex;
map<string, Mutex*> region_scripts_mutex;
map<string, Mutex*> spell_scripts_mutex;
Mutex MDebugClients;
Mutex MSpells;
Mutex MSpawnScripts;
Mutex MItemScripts;
Mutex MZoneScripts;
Mutex MPlayerScripts;
Mutex MQuests;
Mutex MLUAMain;
Mutex MSpellDelete;
Mutex MCustomSpell;
Mutex MRegionScripts;
Mutex MSpellScripts;
mutable std::shared_mutex MLUAUserData;
};
#endif

202
old/world/MutexHelper.h Normal file
View File

@ -0,0 +1,202 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MUTEXHELPER_H
#define MUTEXHELPER_H
#include "../common/timer.h"
#include "../common/Mutex.h"
#include <list>
#include <map>
template<typename T>
class IsPointer {
public:
static bool ValidPointer(T key){
return false;
}
static void Delete(T key){
}
};
class Locker{
public:
Locker(){
#ifdef WIN32
InitializeCriticalSection(&CSMutex);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(&CSMutex, &attr);
pthread_mutexattr_destroy(&attr);
#endif
}
Locker(const Locker& locker){
#ifdef WIN32
InitializeCriticalSection(&CSMutex);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(&CSMutex, &attr);
pthread_mutexattr_destroy(&attr);
#endif
}
~Locker(){
#ifdef WIN32
DeleteCriticalSection(&CSMutex);
#else
// pthread_mutex_destroy(&CSMutex);
#endif
}
void lock(){
#ifdef WIN32
EnterCriticalSection(&CSMutex);
#else
pthread_mutex_lock(&CSMutex);
#endif
}
void unlock(){
#ifdef WIN32
LeaveCriticalSection(&CSMutex);
#else
pthread_mutex_unlock(&CSMutex);
#endif
}
private:
#ifdef WIN32
CRITICAL_SECTION CSMutex;
#else
pthread_mutex_t CSMutex;
#endif
};
template<typename T>
class IsPointer<T*> {
public:
static bool ValidPointer(T* key){
return true;
}
static void Delete(T* key){
if(key){
delete key;
key = 0;
}
}
};
template <typename KeyT, typename ValueT>
class DeleteData{
public:
void SetData(int type, KeyT key, ValueT value, unsigned int time){
this->type = type;
this->key = key;
this->value = value;
this->time = time;
}
void DeleteKey(){
IsPointer<KeyT>::Delete(key);
}
void DeleteValue(){
IsPointer<ValueT>::Delete(value);
}
unsigned int GetTime(){
return time;
}
int GetType(){
return type;
}
private:
int type;
KeyT key;
ValueT value;
unsigned int time;
};
template<typename T>
class HandleDeletes {
public:
HandleDeletes(){
access_count = 0;
next_delete_attempt = 0;
changing = false;
has_pending_deletes = false;
}
~HandleDeletes(){
CheckDeletes(true);
}
void AddPendingDelete(T value, unsigned int time){
if(IsPointer<T>::ValidPointer(value)){
while(changing){
Sleep(1);
}
++access_count;
pending_deletes[value] = time;
has_pending_deletes = true;
--access_count;
}
}
void CheckDeletes(bool force = false){
while(changing){
Sleep(1);
}
if(has_pending_deletes && (force || (Timer::GetCurrentTime2() > next_delete_attempt && access_count == 0))){
changing = true;
while(access_count > 0){
Sleep(1);
}
++access_count;
next_delete_attempt = Timer::GetCurrentTime2();
std::list<T> deletes;
typename std::map<T, unsigned int>::iterator pending_delete_itr;
for(pending_delete_itr = pending_deletes.begin(); pending_delete_itr != pending_deletes.end(); pending_delete_itr++){
if(force || next_delete_attempt >= pending_delete_itr->second)
deletes.push_back(pending_delete_itr->first);
}
if(deletes.size() > 0){
typename std::list<T>::iterator delete_itr;
for(delete_itr = deletes.begin(); delete_itr != deletes.end(); delete_itr++){
IsPointer<T>::Delete(*delete_itr);
pending_deletes.erase(*delete_itr);
}
has_pending_deletes = (pending_deletes.size() > 0);
}
next_delete_attempt += 1000;
--access_count;
changing = false;
}
}
private:
volatile bool changing;
volatile int access_count;
volatile unsigned int next_delete_attempt;
volatile bool has_pending_deletes;
std::map<T, unsigned int> pending_deletes;
};
#endif

277
old/world/MutexList.h Normal file
View File

@ -0,0 +1,277 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MUTEXLIST_H
#define MUTEXLIST_H
#include <list>
#include "MutexHelper.h"
#define MUTEXLIST_PENDING_ADD 1
#define MUTEXLIST_PENDING_REMOVE 2
#define MUTEXLIST_PENDING_DELETE 3
template <typename T>
class MutexList{
public:
MutexList(){
pending_changing = false;
has_pending_data = false;
pending_clear = false;
changing = false;
access_count = 0;
access_pending = 0;
}
MutexList(const MutexList& list){
pending_changing = false;
has_pending_data = false;
pending_clear = false;
changing = false;
access_count = 0;
access_pending = 0;
/*if(list.has_pending_data)
pending_data = list.pending_data;
current_data = list.current_data; */
}
~MutexList(){
while(!update(true)){
Sleep(1);
}
}
class iterator {
private:
typename std::list<T>::iterator itr; // Current element
MutexList<T>* list;
bool first_itr;
public:
iterator(){
}
iterator(MutexList<T>* list){
if(list){
this->list = list;
list->update();
this->list->AddAccess();
first_itr = true;
itr = list->current_data.begin();
if(itr != list->current_data.end())
value = *itr;
}
else
this->list = 0;
}
~iterator(){
if(list)
list->RemoveAccess();
}
bool HasNext(){
return itr != list->current_data.end();
}
bool Next(){
if(list->pending_clear)
return false;
if(first_itr)
first_itr = false;
else
itr++;
if(itr != list->current_data.end()){
value = *itr;
if(list->PendingContains(value)) //pending delete
return Next();
return true;
}
return false;
}
iterator* operator->() {
return this;
}
T value;
};
void SetChanging(){
ChangingLock.lock();
changing = true;
ChangingLock.unlock();
}
void SetNotChanging(){
ChangingLock.lock();
changing = false;
ChangingLock.unlock();
}
void AddAccess(){
AccessLock.lock();
++access_count;
AccessLock.unlock();
}
void RemoveAccess(){
AccessLock.lock();
--access_count;
AccessLock.unlock();
}
unsigned int size(bool include_pending = false){
if(include_pending){
update();
return current_data.size() + pending_data.size();
}
return current_data.size();
}
iterator begin(){
return iterator(this);
}
void clear(bool erase_all = false){
pending_clear = true;
if(erase_all){
AddAccess();
PendingLock.lock();
typename std::list<T>::iterator itr;
for(itr = current_data.begin(); itr != current_data.end(); itr++){
RemoveData(*itr);
}
PendingLock.unlock();
RemoveAccess();
}
update();
}
bool PendingContains(T key){
if(!has_pending_data)
return false;
bool ret = false;
PendingLock.lock();
ret = (pending_data.count(key) > 0 && pending_data[key] == false);
PendingLock.unlock();
return ret;
}
unsigned int count(T key){
unsigned int ret = 0;
while(changing){
Sleep(1);
}
AddAccess();
bool retry = false;
if(!changing){
typename std::list<T>::iterator iter;
for(iter = current_data.begin(); iter != current_data.end(); iter++){
if(*iter == key)
ret++;
}
}
else
retry = true;
RemoveAccess();
if(retry)
return count(key); //only occurs whenever we change to changing state at the same time as a reading state
return ret;
}
void RemoveData(T key, int32 erase_time = 0){
handle_deletes.AddPendingDelete(key, Timer::GetCurrentTime2() + erase_time);
}
void Remove(T key, bool erase = false, int32 erase_time = 0){
while(changing){
Sleep(1);
}
AddAccess();
PendingLock.lock();
pending_data[key] = false;
PendingLock.unlock();
if(erase)
RemoveData(key, erase_time);
has_pending_data = true;
RemoveAccess();
update();
}
void Add(T key){
if(count(key) > 0)
return;
while(changing){
Sleep(1);
}
AddAccess();
PendingLock.lock();
pending_data[key] = true;
PendingLock.unlock();
has_pending_data = true;
RemoveAccess();
update();
}
private:
bool update(bool force = false){
//if(access_count > 5)
// cout << "Possible error.\n";
while(changing){
Sleep(1);
}
if(pending_clear && access_count == 0){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
current_data.clear();
has_pending_data = (pending_data.size() > 0);
PendingLock.unlock();
pending_clear = false;
RemoveAccess();
SetNotChanging();
}
if(!pending_clear && has_pending_data && access_count == 0){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
typename std::map<T, bool>::iterator pending_itr;
for(pending_itr = pending_data.begin(); pending_itr != pending_data.end(); pending_itr++){
if(pending_itr->second)
current_data.push_back(pending_itr->first);
else
current_data.remove(pending_itr->first);
}
pending_data.clear();
PendingLock.unlock();
has_pending_data = false;
RemoveAccess();
SetNotChanging();
}
handle_deletes.CheckDeletes(force);
return !pending_clear && !has_pending_data;
}
Locker PendingLock;
Locker AccessLock;
Locker ChangingLock;
volatile int access_count;
std::list<T> current_data;
std::map<T, bool> pending_data;
HandleDeletes<T> handle_deletes;
volatile int access_pending;
volatile bool pending_changing;
volatile bool changing;
volatile bool has_pending_data;
volatile bool pending_clear;
};
#endif

304
old/world/MutexMap.h Normal file
View File

@ -0,0 +1,304 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MUTEXMAP_H
#define MUTEXMAP_H
#include <map>
#include "MutexHelper.h"
#define MUTEXMAP_DELETE_TYPE_KEY 1
#define MUTEXMAP_DELETE_TYPE_VALUE 2
template <typename KeyT, typename ValueT>
class MutexMap{
public:
MutexMap(){
has_pending_data = false;
pending_clear = false;
changing = false;
access_count = 0;
has_pending_deletes = false;
next_delete_attempt = 0;
delete_all = false;
}
~MutexMap(){
update(true);
PendingLock.lock();
pending_add.clear();
pending_remove.clear();
PendingLock.unlock();
}
class iterator {
private:
typename std::map<KeyT, ValueT>::iterator itr; // Current element
MutexMap* map;
bool first_itr;
public:
iterator(){
}
iterator(MutexMap* map){
this->map = map;
map->update();
map->SetChanging();
this->map->AddAccess();
map->SetNotChanging();
first_itr = true;
itr = map->current_data.begin();
if(itr != map->current_data.end()){
first = itr->first;
second = itr->second;
}
}
~iterator(){
map->RemoveAccess();
}
bool HasNext(){
return itr != map->current_data.end();
}
bool Next(){
if(map->pending_clear)
return false;
if(first_itr)
first_itr = false;
else
itr++;
if(itr != map->current_data.end()){
first = itr->first;
second = itr->second;
map->PendingLock.lock();
if(map->pending_remove.count(first) > 0){
map->PendingLock.unlock();
return Next();
}
map->PendingLock.unlock();
return true;
}
return false;
}
iterator* operator->() {
return this;
}
KeyT first;
ValueT second;
};
int count(KeyT key, bool include_pending = false){
while(changing){
Sleep(1);
}
AddAccess();
int ret = current_data.count(key);
if(include_pending){
PendingLock.lock();
ret += pending_add.count(key);
PendingLock.unlock();
}
RemoveAccess();
return ret;
}
void clear(bool delete_all = false){
pending_clear = true;
if(delete_all){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
typename std::map<KeyT, ValueT>::iterator itr;
for(itr = current_data.begin(); itr != current_data.end(); itr++){
deleteData(itr->first, MUTEXMAP_DELETE_TYPE_VALUE);
}
PendingLock.unlock();
RemoveAccess();
SetNotChanging();
}
update();
}
unsigned int size(bool include_pending = false){
if(include_pending)
return current_data.size() + pending_add.size();
return current_data.size();
}
void deleteData(KeyT key, int8 type, int32 erase_time = 0){
DeleteData<KeyT, ValueT>* del = new DeleteData<KeyT, ValueT>();
del->SetData(type, key, current_data[key], Timer::GetCurrentTime2() + erase_time);
pending_deletes[del] = true;
has_pending_deletes = true;
}
void erase(KeyT key, bool erase_key = false, bool erase_value = false, int32 erase_time = 0){
while(changing){
Sleep(1);
}
AddAccess();
if(current_data.count(key) != 0){
PendingLock.lock();
pending_remove[key] = true;
if(erase_key || erase_value){
int type = 0;
if(erase_key)
type = MUTEXMAP_DELETE_TYPE_KEY;
if(erase_value)
type += MUTEXMAP_DELETE_TYPE_VALUE;
deleteData(key, type, erase_time);
}
has_pending_data = true;
PendingLock.unlock();
}
RemoveAccess();
update();
}
iterator begin(){
return iterator(this);
}
void Put(KeyT key, ValueT value){
while(changing){
Sleep(1);
}
AddAccess();
PendingLock.lock();
pending_add[key] = value;
has_pending_data = true;
PendingLock.unlock();
RemoveAccess();
update();
}
ValueT& Get(KeyT key){
while(changing){
Sleep(1);
}
AddAccess();
if(current_data.count(key) > 0 || pending_add.count(key) == 0){
RemoveAccess();
return current_data[key];
}
RemoveAccess();
return pending_add[key];
}
private:
void AddAccess(){
AccessLock.lock();
++access_count;
AccessLock.unlock();
}
void RemoveAccess(){
AccessLock.lock();
--access_count;
AccessLock.unlock();
}
void SetChanging(){
ChangingLock.lock();
changing = true;
}
void SetNotChanging(){
changing = false;
ChangingLock.unlock();
}
void update(bool force = false){
if(pending_clear && (force || access_count == 0)){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
current_data.clear();
has_pending_data = (pending_add.size() > 0 || pending_remove.size() > 0);
pending_clear = false;
PendingLock.unlock();
RemoveAccess();
SetNotChanging();
}
if(!pending_clear && has_pending_data && (force || access_count == 0)){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
typename std::map<KeyT, bool>::iterator remove_itr;
for(remove_itr = pending_remove.begin(); remove_itr != pending_remove.end(); remove_itr++){
current_data.erase(remove_itr->first);
}
typename std::map<KeyT, ValueT>::iterator add_itr;
for(add_itr = pending_add.begin(); add_itr != pending_add.end(); add_itr++){
current_data[add_itr->first] = add_itr->second;
}
pending_add.clear();
pending_remove.clear();
has_pending_data = false;
PendingLock.unlock();
RemoveAccess();
SetNotChanging();
}
if(has_pending_deletes && (force || (Timer::GetCurrentTime2() > next_delete_attempt && access_count == 0))){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
unsigned int time = Timer::GetCurrentTime2();
typename std::list<DeleteData<KeyT, ValueT>*> deleteData;
typename std::map<DeleteData<KeyT, ValueT>*, bool>::iterator remove_itr;
for(remove_itr = pending_deletes.begin(); remove_itr != pending_deletes.end(); remove_itr++){
if(force || time >= remove_itr->first->GetTime())
deleteData.push_back(remove_itr->first);
}
DeleteData<KeyT, ValueT>* data = 0;
typename std::list<DeleteData<KeyT, ValueT>*>::iterator remove_data_itr;
for(remove_data_itr = deleteData.begin(); remove_data_itr != deleteData.end(); remove_data_itr++){
data = *remove_data_itr;
if((data->GetType() & MUTEXMAP_DELETE_TYPE_KEY) == MUTEXMAP_DELETE_TYPE_KEY){
data->DeleteKey();
}
if((data->GetType() & MUTEXMAP_DELETE_TYPE_VALUE) == MUTEXMAP_DELETE_TYPE_VALUE){
data->DeleteValue();
}
pending_deletes.erase(data);
delete data;
}
next_delete_attempt = Timer::GetCurrentTime2() + 1000;
PendingLock.unlock();
RemoveAccess();
SetNotChanging();
has_pending_deletes = (pending_deletes.size() > 0);
}
}
Locker PendingLock;
Locker AccessLock;
Locker ChangingLock;
std::map<KeyT, ValueT> current_data;
std::map<KeyT, ValueT> pending_add;
std::map<DeleteData<KeyT, ValueT>*, bool > pending_deletes;
std::map<KeyT, bool> pending_remove;
volatile unsigned int next_delete_attempt;
volatile int access_count;
volatile bool delete_all;
volatile bool changing;
volatile bool has_pending_data;
volatile bool has_pending_deletes;
volatile bool pending_clear;
};
#endif

202
old/world/MutexVector.h Normal file
View File

@ -0,0 +1,202 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MUTEXVECTOR_H
#define MUTEXVECTOR_H
#include <vector>
#include "MutexHelper.h"
#define MUTEXVECTOR_PENDING_ADD 1
#define MUTEXVECTOR_PENDING_REMOVE 2
#define MUTEXVECTOR_PENDING_DELETE 3
template <typename T>
class MutexVector{
public:
MutexVector(){
has_pending_data = false;
pending_clear = false;
changing = false;
access_count = 0;
}
~MutexVector(){
}
class iterator {
private:
typename std::vector<T>::iterator itr; // Current element
MutexVector<T>* vector;
bool first_itr;
public:
iterator(){
}
iterator(MutexVector<T>* vector){
if(vector){
this->vector = vector;
vector->update();
++this->vector->access_count;
first_itr = true;
itr = vector->current_data.begin();
if(itr != vector->current_data.end())
value = *itr;
}
else
this->vector = 0;
}
~iterator(){
if(vector)
--vector->access_count;
}
bool HasNext(){
return itr != vector->current_data.end();
}
bool Next(){
if(vector->pending_clear)
return false;
if(first_itr)
first_itr = false;
else
itr++;
if(itr != vector->current_data.end()){
value = *itr;
if(vector->pending_data.count(value) > 0 && vector->pending_data[value] == false) //pending delete
return Next();
return true;
}
return false;
}
iterator* operator->() {
return this;
}
T value;
};
void update(){
//if(access_count > 5)
// cout << "Possible error.\n";
while(changing){
Sleep(1);
}
if(pending_clear && access_count == 0){
changing = true;
while(access_count > 0){
Sleep(1);
}
++access_count;
current_data.clear();
has_pending_data = (pending_data.size() > 0);
pending_clear = false;
--access_count;
changing = false;
}
if(!pending_clear && has_pending_data && access_count == 0){
changing = true;
while(access_count > 0){
Sleep(1);
}
++access_count;
typename std::map<T, bool>::iterator pending_itr;
for(pending_itr = pending_data.begin(); pending_itr != pending_data.end(); pending_itr++){
if(pending_itr->second)
current_data.push_back(pending_itr->first);
else{
typename std::vector<T>::iterator data_itr;
for(data_itr = current_data.begin(); data_itr != current_data.end(); data_itr++){
if(*data_itr == pending_itr->first){
current_data.erase(data_itr);
break;
}
}
}
}
pending_data.clear();
has_pending_data = false;
--access_count;
changing = false;
}
handle_deletes.CheckDeletes();
}
unsigned int size(bool include_pending = false){
if(include_pending)
return current_data.size() + pending_data.size();
return current_data.size();
}
iterator begin(){
return iterator(this);
}
void clear(){
pending_clear = true;
update();
}
unsigned int count(T key){
unsigned int ret = 0;
while(changing){
Sleep(1);
}
++access_count;
typename std::list<T>::iterator iter;
for(iter = current_data.begin(); iter != current_data.end(); iter++){
if(*iter == key)
ret++;
}
--access_count;
return ret;
}
void Remove(T key, bool erase = false, unsigned int erase_time = 0){
while(changing){
Sleep(1);
}
++access_count;
pending_data[key] = false;
if(erase)
handle_deletes.AddPendingDelete(key, erase_time);
has_pending_data = true;
--access_count;
update();
}
void Add(T key){
while(changing){
Sleep(1);
}
++access_count;
pending_data[key] = true;
has_pending_data = true;
--access_count;
update();
}
T Get(unsigned int index){
while(changing){
Sleep(1);
}
return current_data[index];
}
private:
volatile int access_count;
std::vector<T> current_data;
std::map<T, bool> pending_data;
HandleDeletes<T> handle_deletes;
volatile bool changing;
volatile bool has_pending_data;
volatile bool pending_clear;
};
#endif

1089
old/world/NPC.cpp Normal file

File diff suppressed because it is too large Load Diff

217
old/world/NPC.h Normal file
View File

@ -0,0 +1,217 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EQ2_NPC__
#define __EQ2_NPC__
#include <atomic>
#include "Entity.h"
#include "MutexMap.h"
#define AI_STRATEGY_BALANCED 1
#define AI_STRATEGY_OFFENSIVE 2
#define AI_STRATEGY_DEFENSIVE 3
// Randomize Appearances
#define RANDOMIZE_GENDER 1
#define RANDOMIZE_RACE 2
#define RANDOMIZE_MODEL_TYPE 4
// Randomize appearance id (spawn_npcs table values)
#define RANDOMIZE_FACIAL_HAIR_TYPE 8 // was RANDOMIZE_FACIAL_HAIR
#define RANDOMIZE_HAIR_TYPE 16 // was RANDOMIZE_HAIR
//#define RANDOMIZE_LEGS_TYPE 32 // spare!
#define RANDOMIZE_WING_TYPE 64
// Randomize parameters (npc_appearances, sInt values)
#define RANDOMIZE_CHEEK_TYPE 128
#define RANDOMIZE_CHIN_TYPE 256
#define RANDOMIZE_EAR_TYPE 512
#define RANDOMIZE_EYE_BROW_TYPE 1024
#define RANDOMIZE_EYE_TYPE 2048
#define RANDOMIZE_LIP_TYPE 4096
#define RANDOMIZE_NOSE_TYPE 8192
// Randomize colors/hues (npc_appearances, RGB values)
#define RANDOMIZE_EYE_COLOR 16384
#define RANDOMIZE_HAIR_COLOR1 32768
#define RANDOMIZE_HAIR_COLOR2 65536
#define RANDOMIZE_HAIR_HIGHLIGHT 131072
#define RANDOMIZE_HAIR_FACE_COLOR 262144 // was RANDOMIZE_FACIAL_HAIR_COLOR
#define RANDOMIZE_HAIR_FACE_HIGHLIGHT_COLOR 524288
#define RANDOMIZE_HAIR_TYPE_COLOR 1048576 // was RANDOMIZE_HAIR_COLOR
#define RANDOMIZE_HAIR_TYPE_HIGHLIGHT_COLOR 2097152
#define RANDOMIZE_SKIN_COLOR 4194304
#define RANDOMIZE_WING_COLOR1 8388608
#define RANDOMIZE_WING_COLOR2 16777216
// All Flags On: 33554431
#define PET_TYPE_COMBAT 1
#define PET_TYPE_CHARMED 2
#define PET_TYPE_DEITY 3
#define PET_TYPE_COSMETIC 4
#define PET_TYPE_DUMBFIRE 5
enum CAST_TYPE {
CAST_ON_SPAWN=0,
CAST_ON_AGGRO=1,
MAX_CAST_TYPES=2
};
class Brain;
class NPCSpell {
public:
NPCSpell() {
}
NPCSpell(NPCSpell* inherit) {
list_id = inherit->list_id;
spell_id = inherit->spell_id;
tier = inherit->tier;
cast_on_spawn = inherit->cast_on_spawn;
cast_on_initial_aggro = inherit->cast_on_initial_aggro;
required_hp_ratio = inherit->required_hp_ratio;
}
int32 list_id;
int32 spell_id;
int8 tier;
bool cast_on_spawn;
bool cast_on_initial_aggro;
sint8 required_hp_ratio;
};
class NPC : public Entity {
public:
NPC();
NPC(NPC* old_npc);
virtual ~NPC();
void Initialize();
EQ2Packet* serialize(Player* player, int16 version);
void SetAppearanceID(int32 id){ appearance_id = id; }
int32 GetAppearanceID(){ return appearance_id; }
bool IsNPC(){ return true; }
void StartRunback(bool reset_hp_on_runback = false);
void InCombat(bool val);
bool HandleUse(Client* client, string type);
void SetRandomize(int32 value) {appearance.randomize = value;}
void AddRandomize(sint32 value) {appearance.randomize += value;}
int32 GetRandomize() {return appearance.randomize;}
bool CheckSameAppearance(string name, int16 id);
void Randomize(NPC* npc, int32 flags);
Skill* GetSkillByName(const char* name, bool check_update = false);
Skill* GetSkillByID(int32 id, bool check_update = false);
int8 GetAttackType();
void SetAIStrategy(int8 strategy);
int8 GetAIStrategy();
void SetPrimarySpellList(int32 id);
int32 GetPrimarySpellList();
void SetSecondarySpellList(int32 id);
int32 GetSecondarySpellList();
void SetPrimarySkillList(int32 id);
int32 GetPrimarySkillList();
void SetSecondarySkillList(int32 id);
int32 GetSecondarySkillList();
void SetEquipmentListID(int32 id);
int32 GetEquipmentListID();
Spell* GetNextSpell(Spawn* target, float distance);
virtual Spell* GetNextBuffSpell(Spawn* target = 0);
void SetAggroRadius(float radius, bool overrideBaseValue = false);
float GetAggroRadius();
float GetBaseAggroRadius() { return base_aggro_radius; }
void SetCastPercentage(int8 percentage);
int8 GetCastPercentage();
void SetSkills(map<string, Skill*>* in_skills);
void SetSpells(vector<NPCSpell*>* in_spells);
void SetRunbackLocation(float x, float y, float z, int32 gridid, bool set_hp_runback = false);
MovementLocation* GetRunbackLocation();
float GetRunbackDistance();
void Runback(float distance=0.0f, bool stopFollowing = true);
void ClearRunback();
virtual bool PauseMovement(int32 period_of_time_ms);
virtual bool IsPauseMovementTimerActive();
void AddSkillBonus(int32 spell_id, int32 skill_id, float value);
virtual void RemoveSkillBonus(int32 spell_id);
virtual void SetZone(ZoneServer* zone, int32 version=0);
void SetMaxPetLevel(int8 val) { m_petMaxLevel = val; }
int8 GetMaxPetLevel() { return m_petMaxLevel; }
void ProcessCombat();
/// <summary>Sets the brain this NPC should use</summary>
/// <param name="brain">The brain this npc should use</param>
void SetBrain(Brain* brain);
/// <summary>Gets the current brain this NPC uses</summary>
/// <returns>The Brain this NPC uses</returns>
::Brain* Brain() { return m_brain; }
bool m_runningBack;
sint16 m_runbackHeadingDir1;
sint16 m_runbackHeadingDir2;
int32 GetShardID() { return m_ShardID; }
void SetShardID(int32 shardid) { m_ShardID = shardid; }
int32 GetShardCharID() { return m_ShardCharID; }
void SetShardCharID(int32 charid) { m_ShardCharID = charid; }
sint64 GetShardCreatedTimestamp() { return m_ShardCreatedTimestamp; }
void SetShardCreatedTimestamp(sint64 timestamp) { m_ShardCreatedTimestamp = timestamp; }
bool HasSpells() { return has_spells; }
std::atomic<bool> m_call_runback;
std::atomic<bool> cast_on_aggro_completed;
private:
MovementLocation* runback;
int8 cast_percentage;
float aggro_radius;
float base_aggro_radius;
Spell* GetNextSpell(float distance, int8 type);
map<string, Skill*>* skills;
vector<NPCSpell*>* spells;
vector<NPCSpell*> cast_on_spells[CAST_TYPE::MAX_CAST_TYPES];
int32 primary_spell_list;
int32 secondary_spell_list;
int32 primary_skill_list;
int32 secondary_skill_list;
int32 equipment_list_id;
int8 attack_type;
int8 ai_strategy;
int32 appearance_id;
int32 npc_id;
MutexMap<int32, SkillBonus*> skill_bonus_list;
int8 m_petMaxLevel;
// Because I named the get function Brain() as well we need to use '::' to specify we are refering to
// the brain class and not the function defined above
::Brain* m_brain;
Mutex MBrain;
int32 m_ShardID;
int32 m_ShardCharID;
sint64 m_ShardCreatedTimestamp;
bool has_spells;
};
#endif

964
old/world/NPC_AI.cpp Normal file
View File

@ -0,0 +1,964 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "NPC_AI.h"
#include "Combat.h"
#include "zoneserver.h"
#include "Spells.h"
#include "../common/Log.h"
#include "LuaInterface.h"
#include "World.h"
#include "Rules/Rules.h"
extern RuleManager rule_manager;
extern LuaInterface* lua_interface;
extern World world;
/* The NEW AI code */
Brain::Brain(NPC* npc) {
// Set the npc this brain will controll
m_body = npc;
// Set the default time between calls to think to 250 miliseconds (1/4 a second)
m_tick = 250;
m_lastTick = Timer::GetCurrentTime2();
m_spellRecovery = 0;
m_playerInEncounter = false;
// Set up the mutex for the hate list
MHateList.SetName("Brain::m_hatelist");
// Set up the mutex for the encounter list
MEncounter.SetName("Brain::m_encounter");
}
Brain::~Brain() {
}
void Brain::Think() {
if (m_body->IsPet() && m_body->GetOwner() && m_body->GetOwner()->IsPlayer()) {
Player* player = (Player*)m_body->GetOwner();
if(player->GetInfoStruct()->get_pet_id() == 0) {
player->GetInfoStruct()->set_pet_id(player->GetIDWithPlayerSpawn(m_body));
player->SetCharSheetChanged(true);
}
}
// Get the entity this NPC hates the most,
// GetMostHated() will handle dead spawns so no need to check the health in this function
Entity* target = GetMostHated();
// If mezzed, stunned or feared we can't do anything so skip
if (!m_body->IsMezzedOrStunned()) {
// Not mezzed or stunned
// Get the distance to the runback location
float run_back_distance = m_body->GetRunbackDistance();
if (target) {
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s has %s targeted.", m_body->GetName(), target->GetName());
// NPC has an entity that it hates
// Set the NPC's target to the most hated entity if it is not already.
if (m_body->GetTarget() != target) {
m_body->SetTarget(target);
}
m_body->FaceTarget(target, false);
// target needs to be set before in combat is engaged
// If the NPC is not in combat then put them in combat
if (!m_body->EngagedInCombat()) {
m_body->ClearRunningLocations();
m_body->InCombat(true);
m_body->cast_on_aggro_completed = false;
m_body->GetZone()->CallSpawnScript(m_body, SPAWN_SCRIPT_AGGRO, target);
}
bool breakWaterPursuit = false;
if (m_body->IsWaterCreature() && !m_body->IsFlyingCreature() && !target->InWater())
breakWaterPursuit = true;
// Check to see if the NPC has exceeded the max chase distance
float maxChaseDist = MAX_CHASE_DISTANCE;
if(m_body->GetInfoStruct()->get_max_chase_distance() > 0.0f) {
maxChaseDist = m_body->GetInfoStruct()->get_max_chase_distance();
}
else if(rule_manager.GetZoneRule(m_body->GetZoneID(), R_Combat, MaxChaseDistance)->GetFloat() > 0.0f) {
maxChaseDist = rule_manager.GetZoneRule(m_body->GetZoneID(), R_Combat, MaxChaseDistance)->GetFloat();
}
if (run_back_distance > maxChaseDist || breakWaterPursuit) {
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "Run back distance is greater then max chase distance, run_back_distance = %f", run_back_distance);
// Over the max chase distance, Check to see if the target is is a client
if (target->IsPlayer() && ((Player*)target)->GetClient())
{
// Target is a client so send encounter break messages
if (m_body->HasSpawnGroup())
((Player*)target)->GetClient()->SimpleMessage(CHANNEL_NARRATIVE, "This encounter will no longer give encounter rewards.");
else
((Player*)target)->GetClient()->Message(CHANNEL_NARRATIVE, "%s is no longer worth any experience or treasure.", m_body->GetName());
}
// Clear the hate list for this NPC
ClearHate();
// Clear the encounter list
ClearEncounter();
}
else {
// Still within max chase distance lets to the combat stuff now
float distance = m_body->GetDistance(target);
if(!m_body->IsCasting() && (!HasRecovered() || !ProcessSpell(target, distance))) {
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s is attempting melee on %s.", m_body->GetName(), target->GetName());
m_body->FaceTarget(target, false);
ProcessMelee(target, distance);
}
}
}
else {
// Nothing in the hate list
bool wasInCombat = m_body->EngagedInCombat();
// Check to see if the NPC is still flagged as in combat for some reason
if (m_body->EngagedInCombat()) {
// If it is set the combat flag to false
m_body->InCombat(false);
// Do not set a players pet to full health once they stop combat
if (!m_body->IsPet() || (m_body->IsPet() && m_body->GetOwner() && !m_body->GetOwner()->IsPlayer()))
m_body->SetHP(m_body->GetTotalHP());
}
CheckBuffs();
// If run back distance is greater then 0 then run back
if(!m_body->EngagedInCombat() && !m_body->IsPauseMovementTimerActive())
{
if (run_back_distance > 1 || (m_body->m_call_runback && !m_body->following)) {
m_body->SetLockedNoLoot(ENCOUNTER_STATE_BROKEN);
m_body->UpdateEncounterState(ENCOUNTER_STATE_BROKEN);
m_body->GetZone()->AddChangedSpawn(m_body);
m_body->Runback(run_back_distance);
m_body->m_call_runback = false;
}
else if (m_body->GetRunbackLocation())
{
switch(m_body->GetRunbackLocation()->stage)
{
case 0:
m_body->GetZone()->movementMgr->StopNavigation((Entity*)m_body);
m_body->ClearRunningLocations();
m_body->SetX(m_body->GetRunbackLocation()->x,false);
m_body->SetZ(m_body->GetRunbackLocation()->z,false);
m_body->SetY(m_body->GetRunbackLocation()->y,false);
m_body->CalculateRunningLocation(true);
m_body->GetRunbackLocation()->stage = 1;
m_body->GetZone()->AddChangedSpawn(m_body);
break;
case 6: // artificially 1500ms per 250ms Think() call
if (m_body->GetRunbackLocation()->gridid > 0)
m_body->SetLocation(m_body->GetRunbackLocation()->gridid);
if(m_body->GetTempActionState() == 0)
m_body->SetTempActionState(-1);
m_body->SetHeading(m_body->m_runbackHeadingDir1,m_body->m_runbackHeadingDir2,false);
if(m_body->GetRunbackLocation()->reset_hp_on_runback)
m_body->SetHP(m_body->GetTotalHP());
m_body->ClearRunback();
if(m_body->GetLockedNoLoot() != ENCOUNTER_STATE_AVAILABLE && m_body->Alive()) {
m_body->SetLockedNoLoot(ENCOUNTER_STATE_AVAILABLE);
m_body->UpdateEncounterState(ENCOUNTER_STATE_AVAILABLE);
}
m_body->GetZone()->AddChangedSpawn(m_body);
break;
default: // captures case 1 up to case 5 to turn around / reset hp
m_body->GetRunbackLocation()->stage++; // artificially delay
break;
}
}
}
// If encounter size is greater then 0 then clear it
if (GetEncounterSize() > 0)
ClearEncounter();
}
}
}
sint32 Brain::GetHate(Entity* entity) {
// We will use this variable to return the value, default to 0
sint32 ret = 0;
// Lock the hate list, not altering it so do a read lock
MHateList.readlock(__FUNCTION__, __LINE__);
// First check to see if the given entity is even in the hate list
if (m_hatelist.count(entity->GetID()) > 0)
// Entity in the hate list so get the hate value for the entity
ret = m_hatelist[entity->GetID()];
// Unlock the hate list
MHateList.releasereadlock(__FUNCTION__, __LINE__);
// return the hate
return ret;
}
void Brain::AddHate(Entity* entity, sint32 hate) {
// do not aggro when running back, despite taking damage
if (m_body->IsNPC() && ((NPC*)m_body)->m_runningBack)
return;
else if (m_body->IsPet() && m_body->IsEntity() && ((Entity*)m_body)->GetOwner() == entity)
return;
if(m_body->IsImmune(IMMUNITY_TYPE_TAUNT))
{
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s is immune to taunt from entity %s.", m_body->GetName(), entity ? entity->GetName() : "(null)");
if(entity && entity->IsPlayer())
((Player*)entity)->GetClient()->GetCurrentZone()->SendDamagePacket((Spawn*)entity, (Spawn*)m_body, DAMAGE_PACKET_TYPE_RANGE_SPELL_DMG, DAMAGE_PACKET_RESULT_IMMUNE, 0, 0, "Hate");
return;
}
// Lock the hate list, we are altering the list so use write lock
MHateList.writelock(__FUNCTION__, __LINE__);
if (m_hatelist.count(entity->GetID()) > 0) {
m_hatelist[entity->GetID()] += hate;
// take into consideration that 0 or negative hate is not valid, we need to properly reset the value
if(m_hatelist[entity->GetID()] < 1) {
m_hatelist[entity->GetID()] = 1;
}
}
else
m_hatelist.insert(std::pair<int32, sint32>(entity->GetID(), hate));
entity->MHatedBy.lock();
if (entity->HatedBy.count(m_body->GetID()) == 0)
entity->HatedBy.insert(m_body->GetID());
entity->MHatedBy.unlock();
// Unlock the list
bool ownerExistsAddHate = false;
if(entity->IsPet() && entity->GetOwner()) {
map<int32, sint32>::iterator itr = m_hatelist.find(entity->GetOwner()->GetID());
if(itr == m_hatelist.end()) {
ownerExistsAddHate = true;
}
}
MHateList.releasewritelock(__FUNCTION__, __LINE__);
if(ownerExistsAddHate) {
AddHate(entity->GetOwner(), 0);
}
}
bool Brain::AdjustHatePosition(int32 id, bool increase) {
// Lock the hate list, we are altering the list so use a write lock
MHateList.writelock(__FUNCTION__, __LINE__);
auto it = m_hatelist.find(id);
if (it == m_hatelist.end()) {
MHateList.releasewritelock(__FUNCTION__, __LINE__);
return false;
}
// Build a vector of (id, hate), sorted highest→lowest hate
std::vector<std::pair<int32,sint32>> sorted;
sorted.reserve(m_hatelist.size());
for (auto &kv : m_hatelist)
sorted.emplace_back(kv.first, kv.second);
std::sort(sorted.begin(), sorted.end(),
[](auto &a, auto &b){ return a.second > b.second; });
// Locate our position
auto posIt = std::find_if(sorted.begin(), sorted.end(),
[&](auto &p){ return p.first == id; });
size_t idx = std::distance(sorted.begin(), posIt);
if (increase) {
if (idx == 0) {
MHateList.releasewritelock(__FUNCTION__, __LINE__);
return false; // no higher to go
}
sint32 aboveHate = sorted[idx - 1].second;
it->second = aboveHate + 1; // move up
}
else {
if (idx + 1 >= sorted.size()) {
MHateList.releasewritelock(__FUNCTION__, __LINE__);
return false; // already at bottom
}
sint32 belowHate = sorted[idx + 1].second;
it->second = belowHate - 1; // move lower
}
MHateList.releasewritelock(__FUNCTION__, __LINE__);
return true;
}
void Brain::ClearHate(bool lockSpawnList) {
// Lock the hate list, we are altering the list so use a write lock
MHateList.writelock(__FUNCTION__, __LINE__);
map<int32, sint32>::iterator itr;
for (itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++) {
Spawn* spawn = m_body->GetZone()->GetSpawnByID(itr->first, lockSpawnList);
if (spawn && spawn->IsEntity())
{
((Entity*)spawn)->MHatedBy.lock();
((Entity*)spawn)->HatedBy.erase(m_body->GetID());
((Entity*)spawn)->MHatedBy.unlock();
}
}
// Clear the list
m_hatelist.clear();
// Unlock the hate list
MHateList.releasewritelock(__FUNCTION__, __LINE__);
}
void Brain::ClearHate(Entity* entity) {
// Lock the hate list, we could potentially modify the list so use write lock
MHateList.writelock(__FUNCTION__, __LINE__);
// Check to see if the given entity is in the hate list
if (m_hatelist.count(entity->GetID()) > 0)
// Erase the entity from the hate list
m_hatelist.erase(entity->GetID());
entity->MHatedBy.lock();
entity->HatedBy.erase(m_body->GetID());
entity->MHatedBy.unlock();
// Unlock the hate list
MHateList.releasewritelock(__FUNCTION__, __LINE__);
}
Entity* Brain::GetMostHated() {
map<int32, sint32>::iterator itr;
int32 ret = 0;
sint32 hate = 0;
// Lock the hate list, not going to alter it so use a read lock
MHateList.readlock(__FUNCTION__, __LINE__);
if (m_hatelist.size() > 0) {
// Loop through the list looking for the entity that this NPC hates the most
for(itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++) {
// Compare the hate value for the current iteration to our stored highest value
if(itr->second > hate) {
// New high value store the entity
ret = itr->first;
// Store the value to compare with the rest of the entities
hate = itr->second;
}
}
}
// Unlock the list
MHateList.releasereadlock(__FUNCTION__, __LINE__);
Entity* hated = (Entity*)GetBody()->GetZone()->GetSpawnByID(ret);
// Check the reult to see if it is still alive
if(hated && hated->GetHP() <= 0) {
// Entity we got was dead so remove it from the list
ClearHate(hated);
// Call this function again now that we removed the dead entity
hated = GetMostHated();
}
// Return our result
return hated;
}
sint8 Brain::GetHatePercentage(Entity* entity) {
float percentage = 0.0;
MHateList.readlock(__FUNCTION__, __LINE__);
if (entity && m_hatelist.count(entity->GetID()) > 0 && m_hatelist[entity->GetID()] > 0) {
sint32 total_hate = 0;
map<int32, sint32>::iterator itr;
for (itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++)
total_hate += itr->second;
percentage = m_hatelist[entity->GetID()] / total_hate;
}
MHateList.releasereadlock(__FUNCTION__, __LINE__);
return (sint8)(percentage * 100);
}
void Brain::SendHateList(Client* client) {
MHateList.readlock(__FUNCTION__, __LINE__);
client->Message(CHANNEL_COLOR_YELLOW, "%s's HateList", m_body->GetName());
client->Message(CHANNEL_COLOR_YELLOW, "-------------------");
map<int32, sint32>::iterator itr;
if (m_hatelist.size() > 0) {
// Loop through the list looking for the entity that this NPC hates the most
for(itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++) {
Entity* ent = (Entity*)GetBody()->GetZone()->GetSpawnByID(itr->first);
// Compare the hate value for the current iteration to our stored highest value
if(ent) {
client->Message(CHANNEL_COLOR_YELLOW, "%s : %i", ent->GetName(), itr->second);
}
else {
client->Message(CHANNEL_COLOR_YELLOW, "%u (cannot identity spawn id->entity) : %i", itr->first, itr->second);
}
}
}
client->Message(CHANNEL_COLOR_YELLOW, "-------------------");
MHateList.releasereadlock(__FUNCTION__, __LINE__);
}
vector<Entity*>* Brain::GetHateList() {
vector<Entity*>* ret = new vector<Entity*>;
map<int32, sint32>::iterator itr;
// Lock the list
MHateList.readlock(__FUNCTION__, __LINE__);
// Loop over the list storing the values into the new list
for (itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++) {
Entity* ent = (Entity*)GetBody()->GetZone()->GetSpawnByID(itr->first);
if (ent)
ret->push_back(ent);
}
// Unlock the list
MHateList.releasereadlock(__FUNCTION__, __LINE__);
// Return the copy of the list
return ret;
}
void Brain::MoveCloser(Spawn* target) {
if (target && m_body->GetFollowTarget() != target)
m_body->SetFollowTarget(target, rule_manager.GetZoneRule(m_body->GetZoneID(), R_Combat, MaxCombatRange)->GetFloat());
if (m_body->GetFollowTarget() && !m_body->following) {
m_body->CalculateRunningLocation(true);
//m_body->ClearRunningLocations();
m_body->following = true;
}
}
bool Brain::ProcessSpell(Entity* target, float distance) {
if(rand()%100 > m_body->GetCastPercentage() || m_body->IsStifled() || m_body->IsFeared())
return false;
Spell* spell = m_body->GetNextSpell(target, distance);
if(spell){
Spawn* spell_target = 0;
if(spell->GetSpellData()->friendly_spell == 1){
vector<Spawn*>* group = m_body->GetSpawnGroup();
if(group && group->size() > 0){
vector<Spawn*>::iterator itr;
for(itr = group->begin(); itr != group->end(); itr++){
if((!spell_target && (*itr)->GetHP() > 0 && (*itr)->GetHP() < (*itr)->GetTotalHP()) || (spell_target && (*itr)->GetHP() > 0 && spell_target->GetHP() > (*itr)->GetHP()))
spell_target = *itr;
}
}
if(!spell_target)
spell_target = m_body;
safe_delete(group);
}
else
spell_target = target;
BrainCastSpell(spell, spell_target, false);
return true;
}
return false;
}
bool Brain::BrainCastSpell(Spell* spell, Spawn* cast_on, bool calculate_run_loc) {
if (spell) {
if(calculate_run_loc) {
m_body->CalculateRunningLocation(true);
}
m_body->GetZone()->ProcessSpell(spell, m_body, cast_on);
m_spellRecovery = (int32)(Timer::GetCurrentTime2() + (spell->GetSpellData()->cast_time * 10) + (spell->GetSpellData()->recovery * 10) + 2000);
return true;
}
return false;
}
bool Brain::CheckBuffs() {
if (!m_body->GetZone()->GetSpellProcess() || m_body->EngagedInCombat() || m_body->IsCasting() || m_body->IsMezzedOrStunned() || !m_body->Alive() || m_body->IsStifled() || !HasRecovered())
return false;
Spell* spell = m_body->GetNextBuffSpell(m_body);
bool casted_on = false;
if(!(casted_on = BrainCastSpell(spell, m_body)) && m_body->IsNPC() && ((NPC*)m_body)->HasSpells()) {
Spawn* target = nullptr;
vector<Spawn*>* group = m_body->GetSpawnGroup();
if(group && group->size() > 0){
vector<Spawn*>::iterator itr;
for(itr = group->begin(); itr != group->end(); itr++){
Spawn* spawn = (*itr);
if(spawn->IsEntity() && spawn != m_body) {
if(target) {
Spell* spell = m_body->GetNextBuffSpell(spawn);
SpellEffects* se = ((Entity*)spawn)->GetSpellEffect(spell->GetSpellData()->id);
if(!se && BrainCastSpell(spell, spawn)) {
casted_on = true;
break;
}
}
}
}
}
safe_delete(group);
}
return casted_on;
}
void Brain::ProcessMelee(Entity* target, float distance) {
if(distance > rule_manager.GetZoneRule(m_body->GetZoneID(), R_Combat, MaxCombatRange)->GetFloat())
MoveCloser((Spawn*)target);
else {
if (target) {
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s is within melee range of %s.", m_body->GetName(), target->GetName());
if (m_body->AttackAllowed(target)) {
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s is allowed to attack %s.", m_body->GetName(), target->GetName());
if (m_body->PrimaryWeaponReady() && !m_body->IsDazed() && !m_body->IsFeared()) {
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s swings its primary weapon at %s.", m_body->GetName(), target->GetName());
m_body->SetPrimaryLastAttackTime(Timer::GetCurrentTime2());
m_body->MeleeAttack(target, distance, true);
m_body->GetZone()->CallSpawnScript(m_body, SPAWN_SCRIPT_AUTO_ATTACK_TICK, target);
}
if (m_body->SecondaryWeaponReady() && !m_body->IsDazed()) {
m_body->SetSecondaryLastAttackTime(Timer::GetCurrentTime2());
m_body->MeleeAttack(target, distance, false);
}
}
}
}
}
bool Brain::HasRecovered() {
if(m_spellRecovery > Timer::GetCurrentTime2())
return false;
m_spellRecovery = 0;
return true;
}
void Brain::AddToEncounter(Entity* entity) {
// If player pet then set the entity to the pets owner
if (entity->IsPet() && entity->GetOwner() && !entity->IsBot()) {
MEncounter.writelock(__FUNCTION__, __LINE__);
bool success = AddToEncounter(entity->GetID());
MEncounter.releasewritelock(__FUNCTION__, __LINE__);
if(!success)
return;
entity = entity->GetOwner();
}
else if(entity->HasPet() && entity->GetPet()) {
MEncounter.writelock(__FUNCTION__, __LINE__);
bool success = AddToEncounter(entity->GetPet()->GetID());
MEncounter.releasewritelock(__FUNCTION__, __LINE__);
if(!success)
return;
}
// If player or bot then get the group
int32 group_id = 0;
if (entity->IsPlayer() || entity->IsBot()) {
m_playerInEncounter = true;
if (entity->GetGroupMemberInfo())
group_id = entity->GetGroupMemberInfo()->group_id;
}
// Insert the entity into the encounter list, if there is a group add all group members as well
// TODO: add raid members
MEncounter.writelock(__FUNCTION__, __LINE__);
if (group_id > 0) {
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>::iterator itr;
PlayerGroup* group = world.GetGroupManager()->GetGroup(group_id);
if (group)
{
std::vector<int32> raidGroups;
group->GetRaidGroups(&raidGroups);
if(raidGroups.size() < 1)
raidGroups.push_back(group_id);
std::vector<int32>::iterator group_itr;
for(group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
group = world.GetGroupManager()->GetGroup((*group_itr));
if(group) {
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
for (itr = members->begin(); itr != members->end(); itr++) {
if ((*itr)->member)
{
bool success = AddToEncounter((*itr)->member->GetID());
if((*itr)->client && success) {
m_encounter_playerlist.insert(make_pair((*itr)->client->GetPlayer()->GetCharacterID(), (*itr)->client->GetPlayer()->GetID()));
}
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
}
}
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
}
else {
bool success = AddToEncounter(entity->GetID());
if (success && entity->IsPlayer())
{
Player* plyr = (Player*)entity;
m_encounter_playerlist.insert(make_pair(plyr->GetCharacterID(), entity->GetID()));
}
}
MEncounter.releasewritelock(__FUNCTION__, __LINE__);
}
bool Brain::CheckLootAllowed(Entity* entity) {
bool ret = false;
vector<int32>::iterator itr;
if (m_body)
{
if ((m_body->GetLootMethod() != GroupLootMethod::METHOD_LOTTO && m_body->GetLootMethod() != GroupLootMethod::METHOD_NEED_BEFORE_GREED) && m_body->GetLooterSpawnID() > 0 && m_body->GetLooterSpawnID() != entity->GetID()) {
LogWrite(LOOT__INFO, 0, "Loot", "%s: CheckLootAllowed failed, looter spawn id %u does not match received %s(%u)", GetBody()->GetName(), m_body->GetLooterSpawnID(), entity->GetName(), entity->GetID());
return false;
}
if (rule_manager.GetZoneRule(m_body->GetZoneID(), R_Loot, AllowChestUnlockByDropTime)->GetInt8()
&& m_body->GetChestDropTime() > 0 && Timer::GetCurrentTime2() >= m_body->GetChestDropTime() + (rule_manager.GetZoneRule(m_body->GetZoneID(), R_Loot, ChestUnlockedTimeDrop)->GetInt32() * 1000)) {
return true;
}
if (rule_manager.GetZoneRule(m_body->GetZoneID(), R_Loot, AllowChestUnlockByTrapTime)->GetInt8()
&& m_body->GetTrapOpenedTime() > 0 && Timer::GetCurrentTime2() >= m_body->GetChestDropTime() + (rule_manager.GetZoneRule(m_body->GetZoneID(), R_Loot, ChestUnlockedTimeTrap)->GetInt32() * 1000)) {
return true;
}
if ((m_body->GetLootMethod() == GroupLootMethod::METHOD_LOTTO || m_body->GetLootMethod() == GroupLootMethod::METHOD_NEED_BEFORE_GREED) && m_body->HasSpawnLootWindowCompleted(entity->GetID())) {
LogWrite(LOOT__INFO, 0, "Loot", "%s: CheckLootAllowed failed, looter %s(%u) has already completed their lotto selections.", GetBody()->GetName(), entity->GetName(), entity->GetID());
return false;
}
}
// Check the encounter list to see if the given entity is in it, if so return true.
MEncounter.readlock(__FUNCTION__, __LINE__);
if (entity->IsPlayer())
{
Player* plyr = (Player*)entity;
map<int32, int32>::iterator itr = m_encounter_playerlist.find(plyr->GetCharacterID());
if (itr != m_encounter_playerlist.end())
{
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
return true;
}
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
return false;
}
for (itr = m_encounter.begin(); itr != m_encounter.end(); itr++) {
if ((*itr) == entity->GetID()) {
// found the entity in the encounter list, set return value to true and break the loop
ret = true;
break;
}
}
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
int8 Brain::GetEncounterSize() {
int8 ret = 0;
MEncounter.readlock(__FUNCTION__, __LINE__);
ret = (int8)m_encounter.size();
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
vector<int32>* Brain::GetEncounter() {
vector<int32>* ret = new vector<int32>;
vector<int32>::iterator itr;
// Lock the list
MEncounter.readlock(__FUNCTION__, __LINE__);
// Loop over the list storing the values into the new list
for (itr = m_encounter.begin(); itr != m_encounter.end(); itr++)
ret->push_back(*itr);
// Unlock the list
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
// Return the copy of the list
return ret;
}
bool Brain::IsPlayerInEncounter(int32 char_id) {
bool ret = false;
MEncounter.readlock(__FUNCTION__, __LINE__);
std::map<int32,int32>::iterator itr = m_encounter_playerlist.find(char_id);
if(itr != m_encounter_playerlist.end()) {
ret = true;
}
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
bool Brain::IsEntityInEncounter(int32 id, bool skip_read_lock) {
bool ret = false;
if(!skip_read_lock) {
MEncounter.readlock(__FUNCTION__, __LINE__);
}
vector<int32>::iterator itr;
for (itr = m_encounter.begin(); itr != m_encounter.end(); itr++) {
if ((*itr) == id) {
// found the entity in the encounter list, set return value to true and break the loop
ret = true;
break;
}
}
if(!skip_read_lock) {
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
}
return ret;
}
int32 Brain::CountPlayerBotInEncounter() {
int32 count = 0;
vector<int32>::iterator itr;
MEncounter.readlock(__FUNCTION__, __LINE__);
for (itr = m_encounter.begin(); itr != m_encounter.end(); itr++) {
Entity* ent = (Entity*)GetBody()->GetZone()->GetSpawnByID((*itr));
if (ent && (ent->IsPlayer() || ent->IsBot())) {
count++;
}
}
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
return count;
}
bool Brain::AddToEncounter(int32 id) {
if(!IsEntityInEncounter(id, true)) {
m_encounter.push_back(id);
return true;
}
return false;
}
void Brain::ClearEncounter() {
MEncounter.writelock(__FUNCTION__, __LINE__);
if(m_body) {
m_body->RemoveSpells(true);
}
m_encounter.clear();
m_encounter_playerlist.clear();
m_playerInEncounter = false;
MEncounter.releasewritelock(__FUNCTION__, __LINE__);
}
void Brain::SendEncounterList(Client* client) {
client->Message(CHANNEL_COLOR_YELLOW, "%s's EncounterList", m_body->GetName());
client->Message(CHANNEL_COLOR_YELLOW, "-------------------");
vector<int32>::iterator itr;
// Check the encounter list to see if the given entity is in it, if so return true.
MEncounter.readlock(__FUNCTION__, __LINE__);
for (itr = m_encounter.begin(); itr != m_encounter.end(); itr++) {
Entity* ent = (Entity*)GetBody()->GetZone()->GetSpawnByID((*itr));
// Compare the hate value for the current iteration to our stored highest value
if(ent) {
client->Message(CHANNEL_COLOR_YELLOW, "%s", ent->GetName());
}
else {
client->Message(CHANNEL_COLOR_YELLOW, "%u (cannot identity spawn id->entity)", (*itr));
}
}
client->Message(CHANNEL_COLOR_YELLOW, "-------------------");
MEncounter.releasereadlock(__FUNCTION__, __LINE__);
}
/* Example of how to extend the default AI */
CombatPetBrain::CombatPetBrain(NPC* body) : Brain(body) {
// Make sure to have the " : Brain(body)" so it calls the parent class constructor
// to set up the AI properly
}
CombatPetBrain::~CombatPetBrain() {
}
void CombatPetBrain::Think() {
// We are extending the base brain so make sure to call the parent Think() function.
// If we want to override then we could remove Brain::Think()
Brain::Think();
// All this Brain does is make the pet follow its owner, the combat comes from the default brain
if (GetBody()->EngagedInCombat() || !GetBody()->IsPet() || GetBody()->IsMezzedOrStunned())
return;
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "Pet AI code called for %s", GetBody()->GetName());
// If owner is a player and player has stay set then return out
if (GetBody()->GetOwner() && GetBody()->GetOwner()->IsPlayer() && ((Player*)GetBody()->GetOwner())->GetInfoStruct()->get_pet_movement() == 1)
return;
// Set target to owner
Entity* target = GetBody()->GetOwner();
GetBody()->SetTarget(target);
// Get distance from the owner
float distance = GetBody()->GetDistance(target);
// If out of melee range then move closer
if (distance > rule_manager.GetZoneRule(m_body->GetZoneID(), R_Combat, MaxCombatRange)->GetFloat())
MoveCloser((Spawn*)target);
}
/* Example of how to override the default AI */
NonCombatPetBrain::NonCombatPetBrain(NPC* body) : Brain(body) {
// Make sure to have the " : Brain(body)" so it calls the parent class constructor
// to set up the AI properly
}
NonCombatPetBrain::~NonCombatPetBrain() {
}
void NonCombatPetBrain::Think() {
// All this Brain does is make the pet follow its owner
if (!GetBody()->IsPet() || GetBody()->IsMezzedOrStunned())
return;
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "Pet AI code called for %s", GetBody()->GetName());
// Set target to owner
Entity* target = GetBody()->GetOwner();
GetBody()->SetTarget(target);
// Get distance from the owner
float distance = GetBody()->GetDistance(target);
// If out of melee range then move closer
if (distance > rule_manager.GetZoneRule(m_body->GetZoneID(), R_Combat, MaxCombatRange)->GetFloat())
MoveCloser((Spawn*)target);
}
BlankBrain::BlankBrain(NPC* body) : Brain(body) {
// Make sure to have the " : Brain(body)" so it calls the parent class constructor
// to set up the AI properly
SetTick(50000);
}
BlankBrain::~BlankBrain() {
}
void BlankBrain::Think() {
}
LuaBrain::LuaBrain(NPC* body) : Brain(body) {
}
LuaBrain::~LuaBrain() {
}
void LuaBrain::Think() {
if (!lua_interface)
return;
const char* script = GetBody()->GetSpawnScript();
if(script) {
if (!lua_interface->RunSpawnScript(script, "Think", GetBody(), GetBody()->GetTarget())) {
lua_interface->LogError("LUA LuaBrain error: was unable to call the Think function in the spawn script (%s)", script);
}
}
else {
LogWrite(NPC_AI__ERROR, 0, "NPC_AI", "Lua brain set on a spawn that doesn't have a script...");
}
}
DumbFirePetBrain::DumbFirePetBrain(NPC* body, Entity* target, int32 expire_time) : Brain(body) {
m_expireTime = Timer::GetCurrentTime2() + expire_time;
AddHate(target, INT_MAX);
}
DumbFirePetBrain::~DumbFirePetBrain() {
}
void DumbFirePetBrain::AddHate(Entity* entity, sint32 hate) {
if (!GetMostHated())
Brain::AddHate(entity, hate);
}
void DumbFirePetBrain::Think() {
Entity* target = GetMostHated();
if (target) {
if (!GetBody()->IsMezzedOrStunned()) {
// Set the NPC's target to the most hated entity if it is not already.
if (GetBody()->GetTarget() != target) {
GetBody()->SetTarget(target);
GetBody()->FaceTarget(target, false);
}
// target needs to be identified before combat setting
// If the NPC is not in combat then put them in combat
if (!GetBody()->EngagedInCombat()) {
//GetBody()->ClearRunningLocations();
GetBody()->CalculateRunningLocation(true);
GetBody()->InCombat(true);
}
float distance = GetBody()->GetDistance(target);
if(GetBody()->CheckLoS(target) && !GetBody()->IsCasting() && (!HasRecovered() || !ProcessSpell(target, distance))) {
LogWrite(NPC_AI__DEBUG, 7, "NPC_AI", "%s is attempting melee on %s.", GetBody()->GetName(), target->GetName());
GetBody()->FaceTarget(target, false);
ProcessMelee(target, distance);
}
}
}
else {
// No hated target or time expired, kill this mob
if (GetBody()->GetHP() > 0) {
GetBody()->KillSpawn(GetBody());
LogWrite(NPC_AI__DEBUG, 7, "NPC AI", "Dumbfire being killed because there is no target.");
}
}
if (Timer::GetCurrentTime2() > m_expireTime) {
if (GetBody()->GetHP() > 0) {
GetBody()->KillSpawn(GetBody());
LogWrite(NPC_AI__DEBUG, 7, "NPC AI", "Dumbfire being killed because timer expired.");
}
}
}

201
old/world/NPC_AI.h Normal file
View File

@ -0,0 +1,201 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __NPC_AI_H__
#define __NPC_AI_H__
#include "NPC.h"
#include <vector>
#include <map>
using namespace std;
class Brain {
public:
Brain(NPC* npc);
virtual ~Brain();
/// <summary>The main loop for the brain. This will do all the AI work</summary>
virtual void Think();
/* Timer related functions */
/// <summary>Gets the time between calls to Think()</summary>
/// <returns>Time in miliseconds between calls to Think()</returns>
int16 Tick() { return m_tick; }
/// <summary>Sets the time between calls to Think()</summary>
/// <param name="time">Time in miliseconds</param>
void SetTick(int16 time) { m_tick = time; }
/// <summary>Gets the timestamp of the last call to Think()</summary>
/// <returns>Timestamp of the last call to Think()</returns>
int32 LastTick() { return m_lastTick; }
/// <summary>Sets the last tick to the given time</summary>
/// <param name="time">The time to set the last tick to</param>
void SetLastTick(int32 time) { m_lastTick = time; }
/* Hate related functions */
/// <summary>Gets the amount of hate this npc has towards the given entity</summary>
/// <param name="entity">The entity to check</param>
/// <returns>The amount of hate towards the given entity</returns>
sint32 GetHate(Entity* entity);
/// <summary>Add hate for the given entity to this NPC</summary>
/// <param name="entity">The entity we are adding to this NPC's hate list</param>
/// <param name="hate">The amount of hate to add</param>
virtual void AddHate(Entity* entity, sint32 hate);
virtual bool AdjustHatePosition(int32 id, bool increase);
/// <summary>Completely clears the hate list for this npc</summary>
void ClearHate(bool lockSpawnList = false);
/// <summary>Removes the given entity from this NPC's hate list</summary>
/// <param name="entity">Entity to remove from this NPC's hate list</param>
void ClearHate(Entity* entity);
/// <summary>Get the entity this NPC hates the most</summary>
/// <returns>The entity this NPC hates the most</returns>
Entity* GetMostHated();
/// <summary>Gets a percentage of hate owned by the given entity</summary>
/// <param name="entity">Entity to get the percentage for</param>
/// <returns>Percentage of hate as a sint8</returns>
sint8 GetHatePercentage(Entity* entity);
void SendHateList(Client* client);
///<summary>Gets a list of all the entities in the hate list</summary>
vector<Entity*>* GetHateList();
/* Combat related functions */
bool BrainCastSpell(Spell* spell, Spawn* cast_on, bool calculate_run_loc = true);
/// <summary></summary>
/// <param name=""></param>
/// <param name=""></param>
virtual bool ProcessSpell(Entity* target, float distance);
/// <summary></summary>
/// <returns>True if a buff starts casting</returns>
bool CheckBuffs();
/// <summary>Has the NPC make a melee attack</summary>
/// <param name="target">The target to attack</param>
/// <param name="distance">The current distance from the target</param>
void ProcessMelee(Entity* target, float distance);
/* Encounter related functions */
/// <summary>Adds the given entity and its group and raid members to the encounter list</summary>
/// <param name="entity">Entity we are adding to the encounter list</param>
void AddToEncounter(Entity* entity);
/// <summary>Checks to see if the given entity can loot the corpse</summary>
/// <param name="entity">Entity trying to loot</param>
/// <returns>True if the entity can loot</returns>
bool CheckLootAllowed(Entity* entity);
/// <summary>Gets the size of the encounter list</summary>
/// <returns>The size of the list as an int8</returns>
int8 GetEncounterSize();
/// <summary>Clears the encounter list</summary>
void ClearEncounter();
void SendEncounterList(Client* client);
/// <summary>Gets a copy of the encounter list</summary>
/// <returns>A copy of the encounter list as a vector<Entity*>*</returns>
vector<int32>* GetEncounter();
/// <summary>Checks to see if a player is in the encounter</summary>
/// <returns>True if the encounter list contains a player</returns>
bool PlayerInEncounter() { return m_playerInEncounter; }
bool IsPlayerInEncounter(int32 char_id);
bool IsEntityInEncounter(int32 id, bool skip_read_lock = false);
int32 CountPlayerBotInEncounter();
bool AddToEncounter(int32 id);
/* Helper functions*/
/// <summary>Gets the NPC this brain controls</summary>
/// <returns>The NPC this brain controls</returns>
NPC* GetBody() { return m_body; }
/// <summary>Checks to see if the NPC can cast</summary>
/// <returns>True if the NPC can cast</returns>
bool HasRecovered();
/// <summary>Tells the NPC to move closer to the given target</summary>
/// <param name="target">The target to move closer to</param>
void MoveCloser(Spawn* target);
protected:
// m_body = the npc this brain controls
NPC* m_body;
// m_spellRecovery = time stamp for when the npc can cast again
int32 m_spellRecovery;
private:
// MHateList = mutex to lock and unlock the hate list
Mutex MHateList;
// m_hatelist = the list that stores all the hate,
// entity is the entity this npc hates and the int32 is the value for how much we hate the entity
map<int32, sint32> m_hatelist;
// m_lastTick = the last time we ran this brain
int32 m_lastTick;
// m_tick = the amount of time between Think() calls in milliseconds
int16 m_tick;
// m_encounter = list of players (entities) that will get a reward (xp/loot) for killing this npc
vector<int32> m_encounter;
map<int32, int32> m_encounter_playerlist;
// MEncounter = mutex to lock and unlock the encounter list
Mutex MEncounter;
//m_playerInEncounter = true if a player is added to the encounter
bool m_playerInEncounter;
};
// Extension of the default brain for combat pets
class CombatPetBrain : public Brain {
public:
CombatPetBrain(NPC* body);
virtual ~CombatPetBrain();
void Think();
};
class NonCombatPetBrain : public Brain {
public:
NonCombatPetBrain(NPC* body);
virtual ~NonCombatPetBrain();
void Think();
};
class BlankBrain : public Brain {
public:
BlankBrain(NPC* body);
virtual ~BlankBrain();
void Think();
};
class LuaBrain : public Brain {
public:
LuaBrain(NPC* body);
virtual ~LuaBrain();
void Think();
};
class DumbFirePetBrain : public Brain {
public:
DumbFirePetBrain(NPC* body, Entity* target, int32 expire_time);
virtual ~DumbFirePetBrain();
void Think();
void AddHate(Entity* entity, sint32 hate);
private:
int32 m_expireTime;
};
#endif

102
old/world/Object.cpp Normal file
View File

@ -0,0 +1,102 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "World.h"
#include "Object.h"
#include "Spells.h"
extern World world;
extern ConfigReader configReader;
extern MasterSpellList master_spell_list;
Object::Object(){
clickable = false;
zone_name = 0;
packet_num = 0;
appearance.activity_status = 64;
appearance.pos.state = 1;
appearance.difficulty = 0;
spawn_type = 2;
m_deviceID = 0;
}
Object::~Object(){
}
EQ2Packet* Object::serialize(Player* player, int16 version){
return spawn_serialize(player, version);
}
void Object::HandleUse(Client* client, string command){
vector<TransportDestination*> destinations;
if(GetTransporterID() > 0)
GetZone()->GetTransporters(&destinations, client, GetTransporterID());
if (destinations.size())
{
client->SetTemporaryTransportID(0);
client->ProcessTeleport(this, &destinations, GetTransporterID());
}
else if (client && command.length() > 0 && appearance.show_command_icon == 1 && MeetsSpawnAccessRequirements(client->GetPlayer())){
EntityCommand* entity_command = FindEntityCommand(command);
if (entity_command)
client->GetCurrentZone()->ProcessEntityCommand(entity_command, client->GetPlayer(), client->GetPlayer()->GetTarget());
}
}
Object* Object::Copy(){
Object* new_spawn = new Object();
new_spawn->SetCollector(IsCollector());
new_spawn->SetMerchantID(merchant_id);
new_spawn->SetMerchantType(merchant_type);
new_spawn->SetMerchantLevelRange(GetMerchantMinLevel(), GetMerchantMaxLevel());
if(GetSizeOffset() > 0){
int8 offset = GetSizeOffset()+1;
sint32 tmp_size = size + (rand()%offset - rand()%offset);
if(tmp_size < 0)
tmp_size = 1;
else if(tmp_size >= 0xFFFF)
tmp_size = 0xFFFF;
new_spawn->size = (int16)tmp_size;
}
else
new_spawn->size = size;
new_spawn->SetPrimaryCommands(&primary_command_list);
new_spawn->SetSecondaryCommands(&secondary_command_list);
new_spawn->database_id = database_id;
new_spawn->primary_command_list_id = primary_command_list_id;
new_spawn->secondary_command_list_id = secondary_command_list_id;
memcpy(&new_spawn->appearance, &appearance, sizeof(AppearanceData));
new_spawn->faction_id = faction_id;
new_spawn->target = 0;
new_spawn->SetTotalHP(GetTotalHP());
new_spawn->SetTotalPower(GetTotalPower());
new_spawn->SetHP(GetHP());
new_spawn->SetPower(GetPower());
SetQuestsRequired(new_spawn);
new_spawn->SetTransporterID(GetTransporterID());
new_spawn->SetDeviceID(GetDeviceID());
new_spawn->SetSoundsDisabled(IsSoundsDisabled());
new_spawn->SetOmittedByDBFlag(IsOmittedByDBFlag());
new_spawn->SetLootTier(GetLootTier());
new_spawn->SetLootDropType(GetLootDropType());
if(GetSpawnScriptSetDB() && GetSpawnScript())
new_spawn->SetSpawnScript(std::string(GetSpawnScript()));
return new_spawn;
}

49
old/world/Object.h Normal file
View File

@ -0,0 +1,49 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EQ2_OBJECT__
#define __EQ2_OBJECT__
#include "Spawn.h"
class Object : public Spawn{
public:
Object();
virtual ~Object();
void SetClickable(bool click){
clickable = click;
}
void SetZone(char* zone){
zone_name = zone;
}
Object* Copy();
bool IsObject(){ return true; }
void HandleUse(Client* client, string command);
bool clickable;
char* zone_name;
EQ2Packet* serialize(Player* player, int16 version);
void SetDeviceID(int8 val) { m_deviceID = val; }
int8 GetDeviceID() { return m_deviceID; }
private:
int8 m_deviceID;
};
#endif

7968
old/world/Player.cpp Normal file

File diff suppressed because it is too large Load Diff

1276
old/world/Player.h Normal file

File diff suppressed because it is too large Load Diff

2150
old/world/PlayerGroups.cpp Normal file

File diff suppressed because it is too large Load Diff

269
old/world/PlayerGroups.h Normal file
View File

@ -0,0 +1,269 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __PLAYERGROUPS_H__
#define __PLAYERGROUPS_H__
#include <deque>
#include <map>
#include <mutex>
#include <shared_mutex>
#include "Spells.h"
#include "../common/types.h"
#include "Entity.h"
using namespace std;
// GroupOptions isn't used yet
struct GroupOptions {
int8 loot_method;
int8 loot_items_rarity;
int8 auto_split;
int8 default_yell;
int8 group_lock_method;
int8 group_autolock;
int8 solo_autolock;
int8 auto_loot_method;
int8 last_looted_index;
};
/// <summary>All the generic info for the group window, plus a client pointer for players</summary>
struct GroupMemberInfo {
int32 group_id;
string name;
string zone;
sint32 hp_current;
sint32 hp_max;
sint32 power_current;
sint32 power_max;
int16 level_current;
int16 level_max;
int8 race_id;
int8 class_id;
bool leader;
Client* client;
Entity* member;
int32 mentor_target_char_id;
bool is_client;
int32 zone_id;
int32 instance_id;
string client_peer_address;
int16 client_peer_port;
bool is_raid_looter;
};
/// <summary>Represents a players group in game</summary>
class PlayerGroup {
public:
PlayerGroup(int32 id);
~PlayerGroup();
/// <summary>Adds a new member to the players group</summary>
/// <param name='member'>Entity to add to the group, can be a Player or NPC</param>
/// <returns>True if the member was added</returns>
bool AddMember(Entity* member, bool is_leader);
bool AddMemberFromPeer(std::string name, bool isleader, bool isclient, int8 class_id, sint32 hp_cur, sint32 hp_max, int16 level_cur, int16 level_max,
sint32 power_cur, sint32 power_max, int8 race_id, std::string zonename, int32 mentor_target_char_id, int32 zone_id, int32 instance_id,
std::string peer_client_address, int16 peer_client_port, bool is_raid_looter);
/// <summary>Removes a member from the players group</summary>
/// <param name='member'>Entity to remove from the player group</param>
/// <returns>True if the member was removed</param>
bool RemoveMember(Entity* member);
bool RemoveMember(std::string name, bool is_client, int32 charID);
/// <summary>Removes all members from the group and destroys the group</summary>
void Disband();
/// <summary>Sends updates to all the clients in the group</summary>
/// <param name='exclude'>Client to exclude from the update</param>
void SendGroupUpdate(Client* exclude = 0, bool forceRaidUpdate = false);
/// <summary>Gets the total number of members in the group</summary>
/// <returns>int32, number of members in the group</returns>
int32 Size() { return m_members.size(); }
/// <summary>Gets a pointer to the list of members</summary>
/// <returns>deque pointer</returns>
deque<GroupMemberInfo*>* GetMembers() { return &m_members; }
void SimpleGroupMessage(const char* message);
void SendGroupMessage(int8 type, const char* message, ...);
void GroupChatMessage(Spawn* from, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
void GroupChatMessage(std::string fromName, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
bool MakeLeader(Entity* new_leader);
std::string GetLeaderName();
bool ShareQuestWithGroup(Client* quest_sharer, Quest* quest);
void RemoveClientReference(Client* remove);
void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked = false);
Entity* GetGroupMemberByPosition(Entity* seeker, int32 mapped_position);
void SetDefaultGroupOptions(GroupOptions* options = nullptr);
bool GetDefaultGroupOptions(GroupOptions* options);
GroupOptions* GetGroupOptions() { return &group_options; }
int8 GetLastLooterIndex() { return group_options.last_looted_index; }
void SetNextLooterIndex(int8 new_index) { group_options.last_looted_index = new_index; }
int32 GetID() { return m_id; }
void GetRaidGroups(std::vector<int32>* groups);
void ReplaceRaidGroups(std::vector<int32>* groups);
bool IsInRaidGroup(int32 groupID, bool isLeaderGroup = false);
void AddGroupToRaid(int32 groupID);
void RemoveGroupFromRaid(int32 groupID);
bool IsGroupRaid();
void ClearGroupRaid();
Mutex MGroupMembers; // Mutex for the group members
private:
GroupOptions group_options;
int32 m_id; // ID of this group
deque<GroupMemberInfo*> m_members; // List of members in this group
std::vector<int32> m_raidgroups;
mutable std::shared_mutex MRaidGroups; // mutex for std vector
};
/// <summary>Responsible for managing all the player groups in the world</summary>
class PlayerGroupManager {
public:
PlayerGroupManager();
~PlayerGroupManager();
/// <summary>Adds a member to a group</summary>
/// <param name='group_id'>ID of the group to add a member to</param>
/// <param name='member'>Entity* to add to the group</param>
/// <returns>True if the member was added to the group</returns>
bool AddGroupMember(int32 group_id, Entity* member, bool is_leader = false);
bool AddGroupMemberFromPeer(int32 group_id, GroupMemberInfo* info);
/// <summary>Removes a member from a group</summary>
/// <param name='group_id'>ID of the group to remove a member from</param>
/// <param name='member'>Entity* to remove from the group</param>
/// <returns>True if the member was removed from the group</returns>
bool RemoveGroupMember(int32 group_id, Entity* member);
bool RemoveGroupMember(int32 group_id, std::string name, bool is_client, int32 charID);
/// <summary>Creates a new group with the provided Entity* as the leader</summary>
/// <param name='leader'>The Entity* that will be the leader of the group</param>
int32 NewGroup(Entity* leader, GroupOptions* goptions, int32 override_group_id = 0);
/// <summary>Removes the group from the group manager</summary>
/// <param name='group_id'>ID of the group to remove</param>
void RemoveGroup(int32 group_id);
/// <summary>Handles a player inviting another player or NPC to a group</summary>
/// <param name='leader'>Player that sent the invite</param>
/// <param name='member'>Player or NPC that is the target of the invite</param>
/// <returns>Error code if invite was unsuccessful, 0 if successful</returns>
int8 Invite(Player* leader, Entity* member);
bool AddInvite(Player* leader, Entity* member);
/// <summary>Handles accepting of a group invite</summary>
/// <param name='member'>Entity* that is accepting the invite</param>
/// <returns>Error code if accepting the invite failed, 0 if successful<returns>
int8 AcceptInvite(Entity* member, int32* group_override_id = nullptr, bool auto_add_group = true);
/// <summary>Handles declining of a group invite</summary>
/// <param name='member'>Entity* that is declining the invite</param>
void DeclineInvite(Entity* member);
/// <summary>Checks to see if there is a group with the given id in the group manager</summary>
/// <param name='group_id'>ID to check for</param>
/// <returns>True if a group with the given ID is found</returns>
bool IsGroupIDValid(int32 group_id);
/// <summary>Send updates to all the clients in the group</summary>
/// <param name='group_id'>ID of the group to send updates to</param>
/// <param name='exclude'>Client* to exclude from the update, usually the one that triggers the update</param>
void SendGroupUpdate(int32 group_id, Client* exclude = 0, bool forceRaidUpdate = false);
PlayerGroup* GetGroup(int32 group_id);
/// <summary>Read locks the group list, no changes to the list should be made when using this</summary>
/// <param name='function'>Name of the function called from, used for better debugging in the event of a deadlock</param>
/// <param name='line'>Line number that this was called from, used for better debugging in the event of a deadlock</param>
void GroupHardLock(const char* function = 0, int32 line = 0U) { MGroups.lock(); }
void GroupLock(const char* function = 0, int32 line = 0U) { MGroups.lock_shared(); }
/// <summary>Releases the readlock acquired from GroupLock()</summary>
/// <param name='function'>Name of the function called from, used for better debugging in the event of a deadlock</param>
/// <param name='line'>Line number that this was called from, used for better debugging in the event of a deadlock</param>
void ReleaseGroupHardLock(const char* function = 0, int32 line = 0U) { MGroups.unlock(); }
void ReleaseGroupLock(const char* function = 0, int32 line = 0U) { MGroups.unlock_shared(); }
void ClearPendingInvite(Entity* member);
std::string HasPendingInvite(Entity* member);
void RemoveGroupBuffs(int32 group_id, Client* client);
int32 GetGroupSize(int32 group_id);
bool HasGroupCompletedQuest(int32 group_id, int32 quest_id);
void SimpleGroupMessage(int32 group_id, const char* message);
void SendGroupMessage(int32 group_id, int8 type, const char* message, ...);
void GroupMessage(int32 group_id, const char* message, ...);
void GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
void GroupChatMessage(int32 group_id, std::string fromName, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
void SendGroupChatMessage(int32 group_id, int16 channel, const char* message, ...);
bool MakeLeader(int32 group_id, Entity* new_leader);
void UpdateGroupBuffs();
bool IsInGroup(int32 group_id, Entity* member);
Entity* IsPlayerInGroup(int32 group_id, int32 char_id);
// TODO: Any function below this comment
bool IsSpawnInGroup(int32 group_id, string name); // used in follow
Player* GetGroupLeader(int32 group_id);
void UpdateGroupMemberInfoFromPeer(int32 group_id, std::string name, bool is_client, GroupMemberInfo* updateinfo);
void SendPeerGroupData(std::string peerId);
void ClearGroupRaid(int32 groupID);
void RemoveGroupFromRaid(int32 groupID, int32 targetGroupID);
bool IsInRaidGroup(int32 groupID, int32 targetGroupID, bool isLeaderGroup = false);
bool GetDefaultGroupOptions(int32 group_id, GroupOptions* options);
void GetRaidGroups(int32 group_id, std::vector<int32>* groups);
void ReplaceRaidGroups(int32 groupID, std::vector<int32>* newGroups);
void SetGroupOptions(int32 groupID, GroupOptions* options);
void SendWhoGroupMembers(Client* client, int32 groupID);
void SendWhoRaidMembers(Client* client, int32 groupID);
int8 AcceptRaidInvite(std::string acceptorName, int32 groupID);
bool SendRaidInvite(Client* sender, Entity* target);
void SplitWithGroupOrRaid(Client* client, int32 coin_plat, int32 coin_gold, int32 coin_silver, int32 coin_copper);
bool IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, Client* client, int32 zoneID, int32 instanceID = 0);
void ClearGroupRaidLooterFlag(int32 groupID);
void EstablishRaidLevelRange(Client* client, int32* min_level, int32* max_level, int32* avg_level, int32* first_level);
private:
int32 m_nextGroupID; // Used to generate a new unique id for new groups
map<int32, PlayerGroup*> m_groups; // int32 is the group id, PlayerGroup* is a pointer to the actual group
map<string, string> m_pendingInvites; // First string is the person invited to the group, second string is the leader of the group
map<string, string> m_raidPendingInvites; // First string is the other group leader invited to the group, second string is the leader of the raid
mutable std::shared_mutex MGroups; // Mutex for the group map (m_groups)
Mutex MPendingInvites; // Mutex for the pending invites map (m_pendingInvites)
};
#endif

1880
old/world/Quests.cpp Normal file

File diff suppressed because it is too large Load Diff

467
old/world/Quests.h Normal file
View File

@ -0,0 +1,467 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QUESTS_H
#define QUESTS_H
#include <map>
#include <vector>
#include "Items/Items.h"
#define QUEST_STEP_TYPE_KILL 1
#define QUEST_STEP_TYPE_CHAT 2
#define QUEST_STEP_TYPE_OBTAIN_ITEM 3
#define QUEST_STEP_TYPE_LOCATION 4
#define QUEST_STEP_TYPE_SPELL 5
#define QUEST_STEP_TYPE_NORMAL 6
#define QUEST_STEP_TYPE_CRAFT 7
#define QUEST_STEP_TYPE_HARVEST 8
#define QUEST_STEP_TYPE_KILL_RACE_REQ 9 // kill using race type requirement instead of npc db id
#define QUEST_DISPLAY_STATUS_HIDDEN 0
#define QUEST_DISPLAY_STATUS_NO_CHECK 1
#define QUEST_DISPLAY_STATUS_YELLOW 2
#define QUEST_DISPLAY_STATUS_COMPLETED 4
#define QUEST_DISPLAY_STATUS_REPEATABLE 8
#define QUEST_DISPLAY_STATUS_CAN_SHARE 16
#define QUEST_DISPLAY_STATUS_COMPLETE_FLAG 32
#define QUEST_DISPLAY_STATUS_SHOW 64
#define QUEST_DISPLAY_STATUS_CHECK 128
#define QUEST_SHAREABLE_NONE 0
#define QUEST_SHAREABLE_ACTIVE 1
#define QUEST_SHAREABLE_DURING 2
#define QUEST_SHAREABLE_COMPLETED 4
struct QuestFactionPrereq{
int32 faction_id;
sint32 min;
sint32 max;
};
struct Location {
int32 id;
float x;
float y;
float z;
int32 zone_id;
};
class QuestStep{
public:
QuestStep(int32 in_id, int8 in_type, string in_description, vector<int32>* in_ids, int32 in_quantity, const char* in_task_group, vector<Location>* in_locations, float in_max_variation, float in_percentage, int32 in_usableitemid);
QuestStep(QuestStep* old_step);
~QuestStep();
bool CheckStepKillRaceReqUpdate(Spawn* spawn);
bool CheckStepReferencedID(int32 id);
bool CheckStepLocationUpdate(float char_x, float char_y, float char_z, int32 zone_id);
int32 AddStepProgress(int32 val);
void SetStepProgress(int32 val);
int32 GetStepProgress();
int8 GetStepType();
bool Complete();
void SetComplete();
void ResetTaskGroup();
const char* GetTaskGroup();
const char* GetDescription();
void SetDescription(string desc);
int16 GetQuestCurrentQuantity();
int16 GetQuestNeededQuantity();
map<int32, bool>* GetUpdateIDs() { return ids; }
int16 GetIcon();
void SetIcon(int16 in_icon);
const char* GetUpdateTargetName();
void SetUpdateTargetName(const char* name);
const char* GetUpdateName();
void SetUpdateName(const char* name);
int32 GetStepID();
int32 GetItemID();
bool WasUpdated();
void WasUpdated(bool val);
float GetPercentage();
void SetTaskGroup(string val) { task_group = val; }
private:
bool updated;
int32 id;
string update_name;
string update_target_name;
int16 icon;
int8 type;
string description;
std::map<int32, bool>* ids;
int32 quantity;
string task_group;
vector<Location>* locations;
float max_variation;
float percentage;
int32 usableitemid;
int32 step_progress;
};
class Player;
class Spell;
class Quest{
public:
Quest(int32 in_id);
Quest(Quest* old_quest);
~Quest();
EQ2Packet* OfferQuest(int16 version, Player* player);
EQ2Packet* QuestJournalReply(int16 version, int32 player_crc, Player* player, QuestStep* updateStep = 0, int8 update_count = 1, bool old_completed_quest=false, bool quest_failure = false, bool display_quest_helper = true);
void RegisterQuest(string in_name, string in_type, string in_zone, int8 in_level, string in_desc);
void SetPrereqLevel(int8 lvl);
void SetPrereqTSLevel(int8 lvl);
void SetPrereqMaxLevel(int8 lvl) {prereq_max_level = lvl;}
void SetPrereqMaxTSLevel(int8 lvl) {prereq_max_tslevel = lvl;}
void AddPrereqClass(int8 class_id);
void AddPrereqTradeskillClass(int8 class_id);
void AddPrereqModelType(int16 model_type);
void AddPrereqRace(int8 race);
void AddPrereqQuest(int32 quest_id);
void AddPrereqFaction(int32 faction_id, sint32 min, sint32 max = 0);
void AddPrereqItem(Item* item);
void AddRewardItem(Item* item);
void AddTmpRewardItem(Item* item);
void GetTmpRewardItemsByID(std::vector<Item*>* items);
void AddRewardItemVec(vector<Item*>* items, Item* item, bool combine_items = true);
void AddSelectableRewardItem(Item* item);
void AddRewardCoins(int32 copper, int32 silver, int32 gold, int32 plat);
void AddRewardCoinsMax(int64 coins);
void AddRewardFaction(int32 faction_id, sint32 amount);
void SetRewardStatus(int32 amount);
void SetRewardComment(string comment);
void SetRewardXP(int32 xp);
void SetRewardTSXP(int32 xp) { reward_tsexp = xp; }
bool AddQuestStep(QuestStep* step);
QuestStep* AddQuestStep(int32 id, int8 in_type, string in_description, vector<int32>* in_ids, int32 in_quantity, const char* in_task_group = 0, vector<Location>* in_locations = 0, float in_max_variation = 0, float in_percentage = 0,int32 in_usableitemid = 0);
bool SetStepComplete(int32 step);
bool AddStepProgress(int32 step_id, int32 progress);
int16 GetQuestStep();
int32 GetStepProgress(int32 step_id);
int16 GetTaskGroupStep();
bool QuestStepIsActive(int16 quest_step_id);
bool CheckQuestReferencedSpawns(Spawn* spawn);
bool CheckQuestKillUpdate(Spawn* spawn, bool update = true);
bool CheckQuestChatUpdate(int32 id, bool update = true);
bool CheckQuestItemUpdate(int32 id, int8 quantity = 1);
bool CheckQuestLocationUpdate(float char_x, float char_y, float char_z, int32 zone_id);
bool CheckQuestSpellUpdate(Spell* spell);
bool CheckQuestRefIDUpdate(int32 id, int32 quantity = 1);
int8 GetQuestLevel();
int8 GetVisible();
int32 GetQuestID();
void SetQuestID(int32 in_id);
int8 GetPrereqLevel();
int8 GetPrereqTSLevel();
int8 GetPrereqMaxLevel() {return prereq_max_level;}
int8 GetPrereqMaxTSLevel() {return prereq_max_tslevel;}
vector<QuestFactionPrereq>* GetPrereqFactions();
vector<int8>* GetPrereqRaces();
vector<int16>* GetPrereqModelTypes();
vector<int8>* GetPrereqClasses();
vector<int8>* GetPrereqTradeskillClasses();
vector<int32>* GetPrereqQuests();
vector<Item*>* GetPrereqItems();
vector<Item*>* GetRewardItems();
vector<Item*>* GetTmpRewardItems();
vector<Item*>* GetSelectableRewardItems();
map<int32, sint32>* GetRewardFactions();
void GiveQuestReward(Player* player);
void AddCompleteAction(int32 step, string action);
void AddProgressAction(int32 step, string action);
void SetName(string in_name);
void SetType(string in_type);
void SetLevel(int8 in_level);
void SetEncounterLevel(int8 level) {enc_level = level;}
void SetDescription(string desc);
void SetStepDescription(int32 step, string desc);
void SetTaskGroupDescription(int32 step, string desc, bool display_bullets);
void SetStatusTmpReward(int32 status) { tmp_reward_status = status; }
int64 GetStatusTmpReward() { return tmp_reward_status; }
void SetCoinTmpReward(int64 coins) { tmp_reward_coins = coins; }
int64 GetCoinTmpReward() { return tmp_reward_coins; }
int64 GetCoinsReward();
int64 GetCoinsRewardMax();
int64 GetGeneratedCoin();
void SetGeneratedCoin(int64 coin);
int8 GetLevel();
int8 GetEncounterLevel() { return enc_level; }
const char* GetName();
const char* GetDescription();
const char* GetType();
void SetZone(string in_zone);
const char* GetZone();
int8 GetDay();
int8 GetMonth();
int8 GetYear();
int32 GetStatusPoints();
void SetDay(int8 value);
void SetMonth(int8 value);
void SetYear(int8 value);
vector<QuestStep*>* GetQuestUpdates();
vector<QuestStep*>* GetQuestFailures();
vector<QuestStep*>* GetQuestSteps();
bool GetQuestStepCompleted(int32 step_id);
QuestStep* GetQuestStep(int32 step_id);
void SetCompleteAction(string action);
const char* GetCompleteAction();
const char* GetCompleteAction(int32 step);
void SetQuestGiver(int32 id);
int32 GetQuestGiver();
void SetQuestReturnNPC(int32 id);
int32 GetQuestReturnNPC();
Player* GetPlayer();
void SetPlayer(Player* in_player);
bool GetCompleted();
bool HasSentLastUpdate() { return has_sent_last_update; }
void SetSentLastUpdate(bool val) { has_sent_last_update = val; }
void SetCompletedDescription(string desc);
const char* GetCompletedDescription();
int32 GetExpReward();
int32 GetTSExpReward() { return reward_tsexp; }
bool GetDeleted();
void SetDeleted(bool val);
bool GetUpdateRequired();
void SetUpdateRequired(bool val);
void SetTurnedIn(bool val);
bool GetTurnedIn();
bool GetSaveNeeded(){ return needs_save; }
void SetSaveNeeded(bool val){ needs_save = val; }
void SetFeatherColor(int8 val) { m_featherColor = val; }
int8 GetFeatherColor() { return m_featherColor; }
void SetRepeatable(bool val) { m_repeatable = val; }
bool IsRepeatable() { return m_repeatable; }
void SetTracked(bool val) { m_tracked = val; }
bool GetTracked() { return m_tracked; }
bool IsTracked() { return m_tracked && !m_hidden; }
void SetCompletedFlag(bool val);
bool GetCompletedFlag() {return completed_flag;}
bool GetYellowName() {return yellow_name;}
void SetYellowName(bool val) {yellow_name = val;}
bool CheckCategoryYellow();
///<summary>Sets the custom flags for use in lua</summary>
///<param name='flags'>Value to set the flags to</param>
void SetQuestFlags(int32 flags) { m_questFlags = flags; SetSaveNeeded(true); }
///<summary>Gets the custom lua flags</summary>
///<returns>The current flags (int32)</returns>
int32 GetQuestFlags() { return m_questFlags; }
///<summary>Checks to see if the quest is hidden</summary>
///<returns>True if the quest is hidden</returns>
bool IsHidden() { return m_hidden; }
///<summary>Sets the quest hidden flag</summary>
///<param name='val'>Value to set the hidden flag to</param>
void SetHidden(bool val) { m_hidden = val; SetSaveNeeded(true); }
///<summary>Sets the quest status earned</summary>
///<param name='val'>Value to set the quest status earned</param>
void SetStatusEarned(int32 status_) { m_status = status_; SetSaveNeeded(true); }
///<summary>Gets the quest status earned</summary>
int32 GetStatusEarned() { return m_status; }
///<summary>Gets the step timer</summary>
///<returns>Unix timestamp (int32)</returns>
int32 GetStepTimer() { return m_timestamp; }
///<summary>Sets the step timer</summary>
///<param name='duration'>How long to set the timer for, in seconds</param>
void SetStepTimer(int32 duration);
///<summary>Sets the step that the timer is for</summary>
///<param name='step'>Step to set the timer for</param>
void SetTimerStep(int32 step) { m_timerStep = step; }
///<summary>Gets the step that the timer is for</summary>
int32 GetTimerStep() { return m_timerStep; }
///<summary>Adds a lua call back function for when the step fails</summary>
///<param name='step'>The step to add the call back for</param>
///<param name='action'>The lua callback function</param>
void AddFailedAction(int32 step, string action);
///<summary>Fail the given step</summary>
///<param name='step'>The step to fail</param>
void StepFailed(int32 step);
///<summary>Removes the given step from the quest</summary>
///<param name='step'>The step id to remove</param>
///<param name='client'>The client who has this quest</param>
///<returns>True if able to remove the quest</returns>
bool RemoveQuestStep(int32 step, Client* client);
int16 GetCompleteCount() { return m_completeCount; }
void SetCompleteCount(int16 val) { m_completeCount = val; }
void IncrementCompleteCount() { m_completeCount += 1; }
void SetQuestTemporaryState(bool tempState, std::string customDescription = string(""));
bool GetQuestTemporaryState() { return quest_state_temporary; }
std::string GetQuestTemporaryDescription() { return quest_temporary_description; }
void SetQuestShareableFlag(int32 flag) { quest_shareable_flag = flag; }
void SetCanDeleteQuest(bool newval) { can_delete_quest = newval; }
void SetHideReward(bool newval) { hide_reward = newval; }
void SetStatusToEarnMin(int32 value_) { status_to_earn_min = value_; }
void SetStatusToEarnMax(int32 value_) { status_to_earn_max = value_; }
int32 GetStatusToEarnMin() { return status_to_earn_min; }
int32 GetStatusToEarnMax() { return status_to_earn_max; }
int32 GetQuestShareableFlag() { return quest_shareable_flag; }
bool CanDeleteQuest() { return can_delete_quest; }
bool GetHideReward() { return hide_reward; }
bool CanShareQuestCriteria(Client* quest_sharer, bool display_client_msg = true);
Mutex MQuestSteps;
protected:
bool needs_save;
Mutex MCompletedActions;
Mutex MProgressActions;
Mutex MFailedActions;
bool turned_in;
bool update_needed;
bool deleted;
bool has_sent_last_update;
string completed_description;
map<int32, string> complete_actions;
map<int32, string> progress_actions;
map<int32, string> failed_actions;
int8 day; //only here to make our lives easier
int8 month;
int8 year;
int8 visible;
int32 id;
string name;
string type;
string zone;
int8 level;
int8 enc_level;
string description;
string complete_action;
Player* player;
vector<QuestFactionPrereq> prereq_factions;
int8 prereq_level;
int8 prereq_tslevel;
int8 prereq_max_level;
int8 prereq_max_tslevel;
vector<int16> prereq_model_types;
vector<int8> prereq_races;
vector<int8> prereq_classes;
vector<int8> prereq_tradeskillclass;
vector<int32> prereq_quests;
vector<Item*> prereq_items;
vector<Item*> reward_items;
vector<Item*> selectable_reward_items;
vector<Item*> tmp_reward_items;
int64 reward_coins;
int64 reward_coins_max;
int32 tmp_reward_status;
int64 tmp_reward_coins;
int64 generated_coin;
map<int32, sint32> reward_factions;
int32 reward_status;
string reward_comment;
int32 reward_exp;
int32 reward_tsexp;
vector<QuestStep*> step_updates;
vector<QuestStep*> step_failures;
map<int32, QuestStep*> quest_step_map;
map<QuestStep*, int32> quest_step_reverse_map;
vector<QuestStep*> quest_steps;
map<int16, string> task_group_order;
int16 task_group_num;
map<string, vector<QuestStep*> > task_group;
int32 quest_giver;
int32 return_id;
int8 m_featherColor;
bool m_repeatable;
bool m_tracked;
bool completed_flag;
bool yellow_name;
int32 m_questFlags;
bool m_hidden;
int32 m_status;
int32 m_timestamp; // timer for a quest step
int32 m_timerStep; // used for the fail action when timer expires
int16 m_completeCount;
bool quest_state_temporary;
std::string quest_temporary_description;
int32 quest_shareable_flag;
bool can_delete_quest;
int32 status_to_earn_min;
int32 status_to_earn_max;
bool hide_reward;
};
class MasterQuestList{
public:
MasterQuestList();
Quest* GetQuest(int32 id, bool copyQuest = true){
if(quests.count(id) > 0)
{
if(copyQuest)
return new Quest(quests[id]);
else
return quests[id];
}
else
return 0;
}
map<int32, Quest*>* GetQuests(){
return &quests;
}
void AddQuest(int32 id, Quest* quest){
quests[id] = quest;
}
void Reload();
void LockQuests();
void UnlockQuests();
private:
Mutex MQuests;
map<int32, Quest*> quests;
};
#endif

View File

@ -0,0 +1,120 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "RaceTypes.h"
#include <string.h>
MasterRaceTypeList::MasterRaceTypeList() {
}
MasterRaceTypeList::~MasterRaceTypeList() {
}
bool MasterRaceTypeList::AddRaceType(int16 model_id, int16 race_type_id, const char* category, const char* subcategory, const char* modelname, bool allow_override) {
if (m_raceList.count(model_id) == 0 || allow_override) {
RaceTypeStructure rts;
m_raceList[model_id].race_type_id = race_type_id;
if(category != NULL) {
strncpy(m_raceList[model_id].category, category, 64);
} else {
strcpy(m_raceList[model_id].category,"");
}
if(subcategory != NULL) {
strncpy(m_raceList[model_id].subcategory, subcategory, 64);
} else {
strcpy(m_raceList[model_id].subcategory,"");
}
if(modelname != NULL) {
strncpy(m_raceList[model_id].modelname, modelname, 64);
} else {
strcpy(m_raceList[model_id].modelname,"");
}
return true;
}
return false;
}
int16 MasterRaceTypeList::GetRaceType(int16 model_id) {
int16 ret = 0;
if (m_raceList.count(model_id) > 0)
ret = m_raceList[model_id].race_type_id;
return ret;
}
char* MasterRaceTypeList::GetRaceTypeCategory(int16 model_id) {
if(m_raceList.count(model_id) > 0 && strlen(m_raceList[model_id].category) > 0)
return m_raceList[model_id].category;
return "";
}
char* MasterRaceTypeList::GetRaceTypeSubCategory(int16 model_id) {
if(m_raceList.count(model_id) > 0 && strlen(m_raceList[model_id].subcategory) > 0)
return m_raceList[model_id].subcategory;
return "";
}
char* MasterRaceTypeList::GetRaceTypeModelName(int16 model_id) {
if(m_raceList.count(model_id) > 0 && strlen(m_raceList[model_id].modelname) > 0)
return m_raceList[model_id].modelname;
return "";
}
int16 MasterRaceTypeList::GetRaceBaseType(int16 model_id) {
int16 ret = 0;
if (m_raceList.count(model_id) == 0)
return ret;
int16 race = m_raceList[model_id].race_type_id;
if (race >= DRAGONKIND && race < FAY)
ret = DRAGONKIND;
else if (race >= FAY && race < MAGICAL)
ret = FAY;
else if (race >= MAGICAL && race < MECHANIMAGICAL)
ret = MAGICAL;
else if (race >= MECHANIMAGICAL && race < NATURAL)
ret = MECHANIMAGICAL;
else if (race >= NATURAL && race < PLANAR)
ret = NATURAL;
else if (race >= PLANAR && race < PLANT)
ret = PLANAR;
else if (race >= PLANT && race < SENTIENT)
ret = PLANT;
else if (race >= SENTIENT && race < UNDEAD)
ret = SENTIENT;
else if (race >= UNDEAD && race < WERE)
ret = UNDEAD;
else if (race >= WERE)
ret = WERE;
return ret;
}

View File

@ -0,0 +1,321 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __RACETYPES_H__
#define __RACETYPES_H__
#include "../../common/types.h"
#include <map>
#define DRAGONKIND 101
#define DRAGON 102
#define DRAKE 103
#define DRAKOTA 104
#define DROAG 105
#define FAEDRAKE 106
//FLYINGSNAKE Defined in natural as well, think is a better fit there then here
#define SOKOKAR 107
#define WURM 108
#define WYRM 109
#define WYVERN 110
#define FAY 111
#define ARASAI_NPC 112
#define BIXIE 113
#define BROWNIE 114
#define DRYAD 115
#define FAE_NPC 116
#define FAIRY 117
#define SIREN 118
#define SPIRIT 119
#define SPRITE 120
#define TREANT 121 //L&L 8
#define WISP 122
#define MAGICAL 123
#define AMORPH 124
#define CONSTRUCT 125
#define ANIMATION 126
#define BONEGOLEM 127
#define BOVOCH 128
#define CARRIONGOLEM 129
#define CLAYGOLEM 130
#define CUBE 131
#define DERVISH 132
#define DEVOURER 133
#define GARGOYLE 134
#define GOLEM 135
#define GOO 136
#define HARPY 137
#define IMP 138
#define LIVINGSTATUE 139
#define MANNEQUIN 140
#define MIMIC 141
#define MOPPET 142
#define NAGA 143
#define NAYAD 144
#define OOZE 145
#define RUMBLER 146
#define RUSTMONSTER 147
#define SATYR 148
#define SCARECROW 149
#define SPHEROID 150
#define TENTACLETERROR 151
#define TOME 152
#define UNICORN 153
#define WOODELEMENTAL 154
#define MECHANIMAGICAL 155
#define CLOCKWORK 156
#define IRONGUARDIAN 157
#define NATURAL 158
#define ANIMAL 159
#define AQUATIC 160
#define AVIAN 161
#define CANINE 162
#define EQUINE 163
#define FELINE 164
#define INSECT 165
#define PRIMATE 166
#define REPTILE 167
#define ANEMONE 168
#define APOPHELI 169
#define ARMADILLO 170
#define BADGER 171
#define BARRACUDA 172
#define BASILISK 173
#define BAT 174
#define BEAR 175
#define BEAVER 176
#define BEETLE 177
#define BOVINE 178
#define BRONTOTHERIUM 179
#define BRUTE 180
#define CAMEL 181
#define CAT 182
#define CENTIPEDE 183
#define CERBERUS 184
#define CHIMERA 185
#define CHOKIDAI 186
#define COBRA 187
#define COCKATRICE 188
#define CRAB 189
#define CROCODILE 190
#define DEER 191
#define DRAGONFLY 192
#define DUCK 193
#define EEL 194
#define ELEPHANT 195
#define FLYINGSNAKE 196
#define FROG 197
#define GOAT 198
#define GORILLA 199
#define GRIFFIN 200
#define HAWK 201
#define HIVEQUEEN 202
#define HORSE 203
#define HYENA 204
#define KHOALRAT 205
#define KYBUR 206
#define LEECH 207
#define LEOPARD 208
#define LION 209
#define LIZARD 210
#define MAMMOTH 211
#define MANTARAY 212
#define MOLERAT 213
#define MONKEY 214
#define MYTHICAL 215
#define OCTOPUS 216
#define OWLBEAR 217
#define PIG 218
#define PIRANHA 219
#define RAPTOR 220
#define RAT 221
#define RHINOCEROS 222
#define ROCKCRAWLER 223
#define SABERTOOTH 224
#define SCORPION 225
#define SEATURTLE 226
#define SHARK 227
#define SHEEP 228
#define SLUG 229
#define SNAKE 230
#define SPIDER 231
#define STIRGE 232
#define SWORDFISH 233
#define TIGER 234
#define TURTLE 235
#define VERMIN 236
#define VULRICH 237
#define WOLF 238
#define YETI 239
#define PLANAR 240
#define ABOMINATION 241
#define AIRELEMENTAL 242
#define AMYGDALAN 243
#define AVATAR 244
#define CYCLOPS 245
#define DEMON 246
#define DJINN 247
#define EARTHELEMENTAL 248
#define EFREETI 249
#define ELEMENTAL 250
#define ETHEREAL 251
#define ETHERPINE 252
#define EVILEYE 253
#define FIREELEMENTAL 254
#define GAZER 255
#define GEHEIN 256
#define GEONID 257
#define GIANT 258 //L&L 5
#define SALAMANDER 259
#define SHADOWEDMAN 260
#define SPHINX 261
#define SPORE 262
#define SUCCUBUS 263
#define VALKYRIE 264
#define VOIDBEAST 265
#define WATERELEMENTAL 266
#define WRAITH 267
#define PLANT 268
#define CARNIVOROUSPLANT 269
#define CATOPLEBAS 270
#define MANTRAP 271
#define ROOTABOMINATION 272
#define ROOTHORROR 273
#define SUCCULENT 274
#define SENTIENT 275
#define ASHLOK 276
#define AVIAK 277
#define BARBARIAN_NPC 278
#define BIRDMAN 279
#define BOARFIEND 280
#define BUGBEAR 281
#define BURYNAI 282
#define CENTAUR 283 ////L&L 4
#define COLDAIN 284
#define DAL 285
#define DARKELF_NPC 286
#define DIZOK 287
#define DRACHNID 288
#define DRAFLING 289
#define DROLVARG 290
#define DWARF_NPC 291
#define ERUDITE_NPC 292
#define ETTIN 293
#define FREEBLOOD_NPC 294
#define FROGLOK_NPC 295
#define FROSTFELLELF 296
#define FUNGUSMAN 297
#define GNOLL 298 //L&L 1
#define GNOME_NPC 299
#define GOBLIN 300 //L&L 3
#define GRUENGACH 301
#define HALFELF_NPC 302 // Not on the list from wikia but all other races were here so added them
#define HALFLING_NPC 303
#define HIGHELF_NPC 304 // Not on the list from wikia but all other races were here so added them
#define HOLGRESH 305
#define HOOLUK 306
#define HUAMEIN 307
#define HUMAN_NPC 308
#define HUMANOID 309
#define IKSAR_NPC 310
#define KERIGDAL 311
#define KERRAN_NPC 312
#define KOBOLD 313
#define LIZARDMAN 314
#define MINOTAUR 315
#define OGRE_NPC 316
#define ORC 317 //L&L 2
#define OTHMIR 318
#define RATONGA_NPC 319
#define RAVASECT 320
#define RENDADAL 321
#define ROEKILLIK 322
#define SARNAK_NPC 323
#define SKORPIKIS 324
#define SPIROC 325
#define TROGLODYTE 326
#define TROLL_NPC 327
#define ULTHORK 328
#define VULTAK 329
#define WOODELF_NPC 330
#define WRAITHGUARD 331
#define YHALEI 332
#define UNDEAD 333
#define GHOST 334
#define GHOUL 335
#define GUNTHAK 336
#define HORROR 337
#define MUMMY 338
#define SHINREEORCS 339
#define SKELETON 340 //L&L 6
#define SPECTRE 341
#define VAMPIRE_NPC 342
#define ZOMBIE 343 //L&L 7
#define WERE 344
#define AHROUNWEREWOLVES 345
#define LYKULAKWEREWOLVES 346
#define WEREWOLF 347
struct RaceTypeStructure {
int16 race_type_id;
char category[64];
char subcategory[64];
char modelname[250];
};
class MasterRaceTypeList {
public:
MasterRaceTypeList();
~MasterRaceTypeList();
/// <summary>Add a race type define to the list</summary>
/// <param name='model_id'>The id of the model</param>
/// <param name=race_type_id'>The id of the race type</param>
/// <param name=category'>The category of the race type</param>
/// <param name=subcategory'>The subcategory of the race type</param>
/// <param name=modelname'>The model name of the model id</param>
bool AddRaceType(int16 model_id, int16 race_type_id, const char* category, const char* subcategory, const char* modelname, bool allow_override = false);
/// <summary>Gets the race type for the given model</summary>
/// <param name='model_id'>The model id to get the race type for</param>
int16 GetRaceType(int16 model_id);
char* GetRaceTypeCategory(int16 model_id);
char* GetRaceTypeSubCategory(int16 model_id);
char* GetRaceTypeModelName(int16 model_id);
/// <summary>Gets the base race type for the given model</summary>
/// <param name='model_id'>The model id to get the base race type for</param>
int16 GetRaceBaseType(int16 model_id);
private:
// model id, race type id
map<int16, RaceTypeStructure> m_raceList;
};
#endif

View File

@ -0,0 +1,43 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../WorldDatabase.h"
#include "../../common/Log.h"
#include "RaceTypes.h"
extern MasterRaceTypeList race_types_list;
void WorldDatabase::LoadRaceTypes() {
DatabaseResult result;
if(database_new.Select(&result, "SELECT `model_type`, `race_id`, `category`, `subcategory`, `model_name` FROM `race_types`")) {
int32 count = 0;
while (result.Next()) {
int16 race_id = result.GetInt16Str("race_id");
if (race_id > 0) {
race_types_list.AddRaceType(result.GetInt16Str("model_type"), race_id, result.GetStringStr("category"), result.GetStringStr("subcategory"), result.GetStringStr("model_name"));
count++;
}
}
LogWrite(WORLD__INFO, 0, "World", "- Loaded %u Race Types", count);
}
}

View File

@ -0,0 +1,784 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include "../../common/debug.h"
#include "../../common/Log.h"
#include "../../common/database.h"
#include "Recipe.h"
#include "../../common/ConfigReader.h"
#include "../Items/Items.h"
#include "../World.h"
extern ConfigReader configReader;
extern MasterItemList master_item_list;
extern World world;
Recipe::Recipe() {
id = 0;
book_id = 0;
memset(name, 0, sizeof(name));
memset(book_name, 0, sizeof(book_name));
memset(book, 0, sizeof(book));
memset(device, 0, sizeof(device));
level = 0;
tier = 0;
icon = 0;
skill = 0;
technique = 0;
knowledge = 0;
classes = 0;
unknown2 = 0;
unknown3 = 0;
unknown4 = 0;
}
Recipe::~Recipe() {
map<int8, RecipeProducts*>::iterator itr;
for (itr = products.begin(); itr != products.end(); itr++)
safe_delete(itr->second);
}
Recipe::Recipe(Recipe *in){
assert(in);
id = in->GetID();
soe_id = in->GetSoeID();
book_id = in->GetBookID();
strncpy(name, in->GetName(), sizeof(name));
strncpy(description, in->GetDescription(), sizeof(description));
strncpy(book_name, in->GetBookName(), sizeof(book_name));
strncpy(book, in->GetBook(), sizeof(book));
strncpy(device, in->GetDevice(), sizeof(device));
level = in->GetLevel();
tier = in->GetTier();
icon = in->GetIcon();
skill = in->GetSkill();
technique = in->GetTechnique();
knowledge = in->GetKnowledge();
device_sub_type = in->GetDevice_Sub_Type();
classes = in->GetClasses();
unknown1 = in->GetUnknown1();
unknown2 = in->GetUnknown2();
unknown3 = in->GetUnknown3();
unknown4 = in->GetUnknown4();
product_item_id = in->GetProductID();
strncpy(product_name, in->product_name, sizeof(product_name));
product_qty = in->GetProductQuantity();
strncpy(primary_build_comp_title, in->primary_build_comp_title, sizeof(primary_build_comp_title));
strncpy(build1_comp_title, in->build1_comp_title, sizeof(build1_comp_title));
strncpy(build2_comp_title, in->build2_comp_title, sizeof(build2_comp_title));
strncpy(build3_comp_title, in->build3_comp_title, sizeof(build3_comp_title));
strncpy(build4_comp_title, in->build4_comp_title, sizeof(build4_comp_title));
strncpy(fuel_comp_title, in->fuel_comp_title, sizeof(fuel_comp_title));
build1_comp_qty = in->GetBuild1ComponentQuantity();
build2_comp_qty = in->GetBuild2ComponentQuantity();
build3_comp_qty = in->GetBuild3ComponentQuantity();
build4_comp_qty = in->GetBuild4ComponentQuantity();
fuel_comp_qty = in->GetFuelComponentQuantity();
primary_comp_qty = in->GetPrimaryComponentQuantity();
highestStage = in->GetHighestStage();
std::map<int8, RecipeProducts*>::iterator itr;
for (itr = in->products.begin(); itr != in->products.end(); itr++) {
RecipeProducts* rp = new RecipeProducts;
rp->product_id = itr->second->product_id;
rp->byproduct_id = itr->second->byproduct_id;
rp->product_qty = itr->second->product_qty;
rp->byproduct_qty = itr->second->byproduct_qty;
products.insert(make_pair(itr->first, rp));
}
std::map<int8, vector<int32>>::iterator itr2;
for (itr2 = in->components.begin(); itr2 != in->components.end(); itr2++) {
std::vector<int32> recipe_component;
std::copy(itr2->second.begin(), itr2->second.end(),
std::back_inserter(recipe_component));
components.insert(make_pair(itr2->first, recipe_component));
}
}
MasterRecipeList::MasterRecipeList() {
m_recipes.SetName("MasterRecipeList::recipes");
}
MasterRecipeList::~MasterRecipeList() {
ClearRecipes();
}
bool MasterRecipeList::AddRecipe(Recipe *recipe) {
bool ret = false;
int32 id;
assert(recipe);
id = recipe->GetID();
m_recipes.writelock(__FUNCTION__, __LINE__);
if (recipes.count(id) == 0) {
recipes[id] = recipe;
recipes_crc[recipe->GetSoeID()] = recipe;
ret = true;
}
m_recipes.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
Recipe* MasterRecipeList::GetRecipe(int32 recipe_id) {
Recipe *ret = 0;
m_recipes.readlock(__FUNCTION__, __LINE__);
if (recipes.count(recipe_id) > 0)
ret = recipes[recipe_id];
m_recipes.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
Recipe* MasterRecipeList::GetRecipeByCRC(int32 recipe_crc) {
Recipe *ret = 0;
m_recipes.readlock(__FUNCTION__, __LINE__);
if (recipes_crc.count(recipe_crc) > 0)
ret = recipes_crc[recipe_crc];
m_recipes.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
Recipe* MasterRecipeList::GetRecipeByName(const char* name) {
Recipe* ret = 0;
map<int32, Recipe*>::iterator itr;
m_recipes.readlock(__FUNCTION__, __LINE__);
for (itr = recipes.begin(); itr != recipes.end(); itr++) {
if (::ToLower(string(name)) == ::ToLower(string(itr->second->GetName()))) {
ret = itr->second;
break;
}
}
m_recipes.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
void MasterRecipeList::ClearRecipes() {
map<int32, Recipe *>::iterator itr;
m_recipes.writelock(__FUNCTION__, __LINE__);
for (itr = recipes.begin(); itr != recipes.end(); itr++)
safe_delete(itr->second);
recipes.clear();
recipes_crc.clear();
m_recipes.releasewritelock(__FUNCTION__, __LINE__);
}
int32 MasterRecipeList::Size() {
int32 ret;
m_recipes.readlock(__FUNCTION__, __LINE__);
ret = (int32)recipes.size();
m_recipes.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
vector<Recipe*> MasterRecipeList::GetRecipes(const char* book_name) {
vector<Recipe*> ret;
map<int32, Recipe *>::iterator itr;
m_recipes.writelock(__FUNCTION__, __LINE__);
for (itr = recipes.begin(); itr != recipes.end(); itr++) {
if (::ToLower(string(book_name)) == ::ToLower(string(itr->second->GetBook())))
ret.push_back(itr->second);
}
m_recipes.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
PlayerRecipeList::PlayerRecipeList(){
}
PlayerRecipeList::~PlayerRecipeList(){
ClearRecipes();
}
bool PlayerRecipeList::AddRecipe(Recipe *recipe){
std::unique_lock lock(player_recipe_mutex);
assert(recipe);
if(recipes.count(recipe->GetID()) == 0){
recipes[recipe->GetID()] = recipe;
return true;
}
return false;
}
Recipe * PlayerRecipeList::GetRecipe(int32 recipe_id){
std::shared_lock lock(player_recipe_mutex);
if (recipes.count(recipe_id) > 0)
return recipes[recipe_id];
return 0;
}
void PlayerRecipeList::ClearRecipes(){
std::unique_lock lock(player_recipe_mutex);
map<int32, Recipe *>::iterator itr;
for (itr = recipes.begin(); itr != recipes.end(); itr++)
safe_delete(itr->second);
recipes.clear();
}
bool PlayerRecipeList::RemoveRecipe(int32 recipe_id) {
std::unique_lock lock(player_recipe_mutex);
bool ret = false;
if (recipes.count(recipe_id) > 0) {
recipes.erase(recipe_id);
ret = true;
}
return ret;
}
int32 PlayerRecipeList::Size() {
std::unique_lock lock(player_recipe_mutex);
return recipes.size();
}
MasterRecipeBookList::MasterRecipeBookList(){
m_recipeBooks.SetName("MasterRecipeBookList::recipeBooks");
}
MasterRecipeBookList::~MasterRecipeBookList(){
ClearRecipeBooks();
}
bool MasterRecipeBookList::AddRecipeBook(Recipe *recipe){
bool ret = false;
int32 id = 0;
assert(recipe);
id = recipe->GetBookID();
m_recipeBooks.writelock(__FUNCTION__, __LINE__);
if(recipeBooks.count(id) == 0){
recipeBooks[id] = recipe;
ret = true;
}
m_recipeBooks.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
Recipe * MasterRecipeBookList::GetRecipeBooks(int32 recipebook_id){
Recipe *ret = 0;
m_recipeBooks.readlock(__FUNCTION__, __LINE__);
if (recipeBooks.count(recipebook_id) > 0)
ret = recipeBooks[recipebook_id];
m_recipeBooks.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
void MasterRecipeBookList::ClearRecipeBooks(){
map<int32, Recipe *>::iterator itr;
m_recipeBooks.writelock(__FUNCTION__, __LINE__);
for (itr = recipeBooks.begin(); itr != recipeBooks.end(); itr++)
safe_delete(itr->second);
recipeBooks.clear();
m_recipeBooks.releasewritelock(__FUNCTION__, __LINE__);
}
int32 MasterRecipeBookList::Size(){
int32 ret = 0;
m_recipeBooks.readlock(__FUNCTION__, __LINE__);
ret = (int32)recipeBooks.size();
m_recipeBooks.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
EQ2Packet* MasterRecipeList::GetRecipePacket(int32 recipe_id, Client* client, bool display, int8 packet_type){
Recipe *recipe = GetRecipe(recipe_id);
if(recipe){
LogWrite(TRADESKILL__DEBUG, 5, "Recipes", "Recipe ID: %u Recipe Name: %s", recipe->GetID(), recipe->GetName());
return recipe->SerializeRecipe(client, recipe, display, packet_type);
}
return 0;
}
PlayerRecipeBookList::PlayerRecipeBookList(){
}
PlayerRecipeBookList::~PlayerRecipeBookList(){
ClearRecipeBooks();
}
bool PlayerRecipeBookList::AddRecipeBook(Recipe *recipe){
assert(recipe);
if(recipeBooks.count(recipe->GetBookID()) == 0){
recipeBooks[recipe->GetBookID()] = recipe;
return true;
}
return false;
}
Recipe * PlayerRecipeBookList::GetRecipeBook(int32 recipebook_id){
if(recipeBooks.count(recipebook_id) > 0)
return recipeBooks[recipebook_id];
return 0;
}
bool PlayerRecipeBookList::HasRecipeBook(int32 book_id) {
if (recipeBooks.count(book_id) > 0)
return true;
return false;
}
void PlayerRecipeBookList::ClearRecipeBooks(){
map<int32, Recipe*>::iterator itr;
for(itr = recipeBooks.begin(); itr != recipeBooks.end(); itr++)
safe_delete(itr->second);
recipeBooks.clear();
}
EQ2Packet * Recipe::SerializeRecipe(Client *client, Recipe *recipe, bool display, int8 packet_type, int8 subpacket_type, const char *struct_name){
int16 version = 1;
Item* item = 0;
RecipeProducts* rp = 0;
vector<int32>::iterator itr;
vector<RecipeComp> comp_list;
int8 i = 0;
int32 firstID = 0;
int32 primary_comp_id = 0;
if(client)
version = client->GetVersion();
if(!struct_name)
struct_name = "WS_ExamineRecipeInfo";
PacketStruct *packet = configReader.getStruct(struct_name, version);
if(display)
packet->setSubstructDataByName("info_header", "show_name", 1);
else
packet->setSubstructDataByName("info_header", "show_popup", 1);
if(client->GetVersion() <= 561) {
packet->setSubstructDataByName("info_header", "packettype", 0x02);
}
else if(packet_type > 0)
packet->setSubstructDataByName("info_header", "packettype", GetItemPacketType(packet->GetVersion()));
else
if(version == 1096)
packet->setSubstructDataByName("info_header", "packettype",0x35FE);
if (version == 1208)
packet->setSubstructDataByName("info_header", "packettype", 0x45FE);
if(version >= 57048)
packet->setSubstructDataByName("info_header", "packettype",0x48FE);
if(subpacket_type == 0)
subpacket_type = 0x02;
packet->setSubstructDataByName("info_header", "packetsubtype", subpacket_type);
packet->setSubstructDataByName("recipe_info", "id", recipe->GetID());
packet->setSubstructDataByName("recipe_info", "unknown", 3);
packet->setSubstructDataByName("recipe_info", "level", recipe->GetLevel());
packet->setSubstructDataByName("recipe_info", "technique", recipe->GetTechnique());
packet->setSubstructDataByName("recipe_info", "skill_level", 50); //50
packet->setSubstructDataByName("recipe_info", "knowledge", recipe->GetKnowledge());
packet->setSubstructDataByName("recipe_info", "device", recipe->GetDevice());
packet->setSubstructDataByName("recipe_info", "icon", recipe->GetIcon());
packet->setSubstructDataByName("recipe_info", "unknown3", 1);
packet->setSubstructDataByName("recipe_info", "adventure_id", 0xFF);
packet->setSubstructDataByName("recipe_info", "tradeskill_id", client ? client->GetPlayer()->GetTradeskillClass() : 0);
packet->setSubstructDataByName("recipe_info", "unknown4a", 100);
packet->setSubstructDataByName("recipe_info", "unknown4aa", 1);
packet->setSubstructDataByName("recipe_info", "unknown5a", 20);//level *10
packet->setSubstructDataByName("recipe_info", "product_classes", recipe->GetClasses());
int32 HS = 0;
if (client->GetPlayer()->GetRecipeList()->GetRecipe(recipe->GetID()) == NULL)
HS = 0;
else
HS = client->GetPlayer()->GetRecipeList()->GetRecipe(recipe->GetID())->highestStage;
packet->setSubstructDataByName("recipe_info", "show_previous", HS);// recipe->highestStage);
rp = recipe->products[1];
if (rp->product_id > 0) {
item = 0;
item = master_item_list.GetItem(rp->product_id);
if (item) {
packet->setSubstructDataByName("recipe_info", "previous1_icon", item->GetIcon(client->GetVersion()));
packet->setSubstructDataByName("recipe_info", "previous1_name", "previous1_name");
packet->setSubstructDataByName("recipe_info", "previous1_qty", recipe->products[1]->product_qty);
packet->setSubstructDataByName("recipe_info", "previous1_item_id", recipe->products[1]->product_id);
packet->setSubstructDataByName("recipe_info", "previous1_item_crc", -853046774);
packet->setSubstructDataByName("recipe_info", "firstbar_icon", item->GetIcon(client->GetVersion()));
packet->setSubstructDataByName("recipe_info", "firstbar_name", "firstbar_name");
packet->setSubstructDataByName("recipe_info", "firstbar_qty", recipe->products[1]->product_qty);
packet->setSubstructDataByName("recipe_info", "firstbar_item_id", recipe->products[2]->product_id);
packet->setSubstructDataByName("recipe_info", "firstbar_item_crc", -853046774);
}
}
rp = recipe->products[2];
if (rp->product_id > 0) {
item = 0;
item = master_item_list.GetItem(rp->product_id);
if (item) {
packet->setSubstructDataByName("recipe_info", "previous2_icon", item->GetIcon(client->GetVersion()));
packet->setSubstructDataByName("recipe_info", "previous2_name", "previous2_name");
packet->setSubstructDataByName("recipe_info", "previous2_qty", recipe->products[2]->product_qty);
packet->setSubstructDataByName("recipe_info", "previous2_item_id", recipe->products[2]->product_id);
packet->setSubstructDataByName("recipe_info", "previous2_item_crc", -853046774);
packet->setSubstructDataByName("recipe_info", "secondbar_icon", item->GetIcon(client->GetVersion()));
packet->setSubstructDataByName("recipe_info", "secondbar_name", "secondbar_name");
packet->setSubstructDataByName("recipe_info", "secondbar_qty", recipe->products[2]->product_qty);
packet->setSubstructDataByName("recipe_info", "secondbar_item_id", recipe->products[2]->product_id);
packet->setSubstructDataByName("recipe_info", "secondbar_item_crc", -853046774);
}
}
rp = recipe->products[3];
if (rp->product_id > 0) {
item = 0;
item = master_item_list.GetItem(rp->product_id);
if (item) {
packet->setSubstructDataByName("recipe_info", "previous3_icon", item->GetIcon(client->GetVersion()));
packet->setSubstructDataByName("recipe_info", "previous3_name", "previous3_name");
packet->setSubstructDataByName("recipe_info", "previous3_qty", recipe->products[3]->product_qty);
packet->setSubstructDataByName("recipe_info", "previous3_item_id", recipe->products[3]->product_id);
packet->setSubstructDataByName("recipe_info", "previous3_item_crc", -853046774);
packet->setSubstructDataByName("recipe_info", "thirdbar_icon", item->GetIcon(client->GetVersion()));
packet->setSubstructDataByName("recipe_info", "thirdbar_name", "thirdbar_name");
packet->setSubstructDataByName("recipe_info", "thirdbar_qty", recipe->products[3]->product_qty);
packet->setSubstructDataByName("recipe_info", "thirdbar_item_id", recipe->products[3]->product_id);
packet->setSubstructDataByName("recipe_info", "thirdbar_item_crc", -2065846136);
}
}
//item = master_item_list.GetItemByName(recipe->GetName());// TODO: MJ we should be getting item by item number in case of multiple items with same name
item = master_item_list.GetItem(recipe->GetProductID());
if(item) {
packet->setSubstructDataByName("recipe_info", "product_icon", item->GetIcon(client->GetVersion())); //item->details.icon);
packet->setSubstructDataByName("recipe_info", "product_name", item->name.c_str()); //item->name);
packet->setSubstructDataByName("recipe_info", "product_qty", 1);
packet->setSubstructDataByName("recipe_info", "product_item_id", item->details.item_id); //item->details.item_id);
packet->setSubstructDataByName("recipe_info", "product_item_crc", 0); //item->details.item_crc);
}
rp = recipe->products[0];
if (rp->byproduct_id > 0) {
item = 0;
item = master_item_list.GetItem(rp->byproduct_id);
if (item) {
packet->setSubstructDataByName("recipe_info", "byproduct_icon", item->GetIcon(client->GetVersion()));//11
packet->setSubstructDataByName("recipe_info", "byproduct_id", item->details.item_id);
}
}
rp = recipe->products[1];
if (rp->product_id > 0) {
item = 0;
item = master_item_list.GetItem(rp->product_id);
if (item) {
packet->setSubstructDataByName("recipe_info", "byproduct_icon", item->GetIcon(client->GetVersion()));//11
packet->setSubstructDataByName("recipe_info", "byproduct_id", item->details.item_id);
}
}
item = 0;
// Check to see if we have a primary component (slot = 0)
vector<Item*> itemss;
if (recipe->components.count(0) > 0) {
if(client->GetVersion() <= 561) {
packet->setSubstructDataByName("recipe_info", "primary_count", 1);
}
int16 have_qty = 0;
vector<int32> rc = recipe->components[0];
for (itr = rc.begin(); itr != rc.end(); itr++, i++) {
item = master_item_list.GetItem(*itr);
packet->setSubstructDataByName("recipe_info", "primary_comp", recipe->primary_build_comp_title);
packet->setSubstructDataByName("recipe_info", "primary_qty", recipe->GetPrimaryComponentQuantity());
item = 0;
itemss = client->GetPlayer()->item_list.GetAllItemsFromID((*itr));
if (itemss.size() > 0) {
int16 needed_qty = recipe->GetPrimaryComponentQuantity();
for (int8 i = 0; i < itemss.size(); i++) {
have_qty += itemss[i]->details.count;
}
}
}
packet->setSubstructDataByName("recipe_info", "primary_qty_avail", have_qty);
}
int8 total_build_components = recipe->GetTotalBuildComponents();
int8 index = 0;
int8 count = 0;
if (total_build_components > 0) {
packet->setSubstructArrayLengthByName("recipe_info", "num_comps", total_build_components);
for (index = 0; index < 4; index++) {
if (recipe->components.count(index + 1) == 0)
continue;
count++;
vector<int32> rc = recipe->components[index + 1];
int16 have_qty = 0;
string comp_title;
int8 comp_qty;
for (itr = rc.begin(); itr != rc.end(); itr++, i++) {
if (index == 0) {
comp_title = recipe->build1_comp_title;
comp_qty = recipe->build1_comp_qty;
}
else if (index == 1) {
comp_title = recipe->build2_comp_title;
comp_qty = recipe->build2_comp_qty;
}
else if (index == 2) {
comp_title = recipe->build3_comp_title;
comp_qty = recipe->build3_comp_qty;
}
else if (index == 3) {
comp_title = recipe->build4_comp_title;
comp_qty = recipe->build4_comp_qty;
}
itemss = client->GetPlayer()->item_list.GetAllItemsFromID((*itr));
for (int8 j = 0; j < itemss.size(); j++) {
have_qty += itemss[j]->details.count;
}
}
packet->setArrayDataByName("build_comp", comp_title.c_str(), index);
packet->setArrayDataByName("build_comp_qty", comp_qty, index);
packet->setArrayDataByName("build_comp_qty_avail", have_qty, index);
}
}
if(client->GetVersion() <= 561) {
packet->setSubstructDataByName("recipe_info", "fuel_count", 1);
packet->setSubstructDataByName("recipe_info", "fuel_comp", recipe->fuel_comp_title);
packet->setSubstructDataByName("recipe_info", "fuel_comp_qty", recipe->fuel_comp_qty);
}
// Check to see if we have a fuel component (slot = 5)
else if (recipe->components.count(5) > 0) {
vector<int32> rc = recipe->components[5];
for (itr = rc.begin(); itr != rc.end(); itr++, i++) {
item = master_item_list.GetItem(*itr);
packet->setSubstructDataByName("recipe_info", "fuel_comp", recipe->fuel_comp_title);
packet->setSubstructDataByName("recipe_info", "fuel_comp_qty", recipe->fuel_comp_qty);
item = 0;
itemss = client->GetPlayer()->item_list.GetAllItemsFromID((*itr));
if (itemss.size() > 0) {
int16 have_qty = 0;
for (int8 i = 0; i < itemss.size(); i++) {
have_qty += itemss[i]->details.count;
}
packet->setSubstructDataByName("recipe_info", "fuel_comp_qty_avail", have_qty);
break;
}
}
}
packet->setSubstructDataByName("recipe_info", "build_comp_qty_avail_flag", 1);
packet->setSubstructDataByName("recipe_info", "unknown6", 4, 0);
packet->setSubstructDataByName("recipe_info", "min_product", 1);
packet->setSubstructDataByName("recipe_info", "max_product", 1);
packet->setSubstructDataByName("recipe_info", "available_flag", 4);
packet->setSubstructDataByName("recipe_info", "not_commissionable", 1);
packet->setSubstructDataByName("recipe_info", "recipe_name", recipe->GetName());
packet->setSubstructDataByName("recipe_info", "recipe_description", recipe->GetDescription());
//packet->PrintPacket();
EQ2Packet* data = packet->serialize();
EQ2Packet* app = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size);
safe_delete(packet);
safe_delete(data);
//DumpPacket(app);
return app;
}
void Recipe::AddBuildComp(int32 itemID, int8 slot, bool preffered) {
if (preffered)
components[slot].insert(components[slot].begin(), itemID);
else
components[slot].push_back(itemID);
}
int8 Recipe::GetTotalBuildComponents() {
int8 total_build_components = 0;
for(int i=1;i<=4;i++) {
if (components.count(i) > 0)
total_build_components++;
}
return total_build_components;
}
bool Recipe::ProvidedAllRequiredComponents(Client* client, vector<Item*>* player_components, vector<pair<int32,int16>>* player_component_pair_qty) {
vector<int32>::iterator itr;
std::vector<pair<int32,int16>> player_comp_itr;
// Check to see if we have a fuel component (slot = 5)
bool matched = false;
bool hasfuel = false;
if (components.count(5) > 0) {
vector<int32> rc = components[5];
for (itr = rc.begin(); itr != rc.end(); itr++) {
hasfuel = true;
LogWrite(TRADESKILL__INFO, 5, "Recipes", "Recipe ID: %u Recipe Name: %s, item %s (%u), fuel quantity required %u", GetID(), GetName(), fuel_comp_title, (*itr), fuel_comp_qty);
if(PlayerHasComponentByItemID(client, player_components, player_component_pair_qty, (*itr), fuel_comp_qty)) {
matched = true;
break;
}
}
}
if(hasfuel && !matched) {
LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Recipe ID: %u Recipe Name: %s, item %s (%u), lacking fuel quantity required %u", GetID(), GetName(), fuel_comp_title, (*itr), fuel_comp_qty);
return false;
}
for (int8 index = 0; index < GetTotalBuildComponents(); index++) {
if (components.count(index + 1) == 0)
continue;
vector<int32> rc = components[index + 1];
string comp_title;
int8 comp_qty;
matched = false;
for (itr = rc.begin(); itr != rc.end(); itr++) {
if (index == 0) {
comp_title = build1_comp_title;
comp_qty = build1_comp_qty;
}
else if (index == 1) {
comp_title = build2_comp_title;
comp_qty = build2_comp_qty;
}
else if (index == 2) {
comp_title = build3_comp_title;
comp_qty = build3_comp_qty;
}
else if (index == 3) {
comp_title = build4_comp_title;
comp_qty = build4_comp_qty;
}
LogWrite(TRADESKILL__INFO, 5, "Recipes", "Recipe ID: %u Recipe Name: %s, item %s (%u), index %u quantity required %u", GetID(), GetName(), comp_title.c_str(), (*itr), index, comp_qty);
if(PlayerHasComponentByItemID(client, player_components, player_component_pair_qty, (*itr), comp_qty)) {
matched = true;
break;
}
}
if(!matched) {
return false;
}
}
return true;
}
bool Recipe::PlayerHasComponentByItemID(Client* client, vector<Item*>* player_components, vector<pair<int32,int16>>* player_component_pair_qty, int32 item_id, int8 required_qty) {
vector<Item*>::iterator itr;
int16 have_qty = 0;
for(itr = player_components->begin(); itr != player_components->end(); itr++) {
LogWrite(TRADESKILL__DEBUG, 0, "Recipes", "PlayerHasComponentByItemID %u to match %u, qty %u, qtyreq: %u", (*itr)->details.item_id, item_id, (*itr)->details.count, required_qty);
if((*itr) && (*itr)->details.item_id == item_id && (*itr)->details.count >= required_qty) {
return true;
}
}
vector<Item*> itemss = client->GetPlayer()->item_list.GetAllItemsFromID(item_id);
if (itemss.size() > 0) {
for (int8 i = 0; i < itemss.size(); i++) {
have_qty += itemss[i]->details.count;
}
}
int16 track_req_qty = required_qty;
if(have_qty >= required_qty) {
LogWrite(TRADESKILL__DEBUG, 0, "Recipes", "PlayerHasComponentByItemID OVERRIDE! Inventory has item id %u, more than required for quantity %u, have %u", item_id, required_qty, have_qty);
have_qty = 0;
for (int8 i = 0; i < itemss.size(); i++) {
have_qty += itemss[i]->details.count;
int8 cur_qty = itemss[i]->details.count;
if(cur_qty > track_req_qty)
cur_qty = track_req_qty;
track_req_qty -= cur_qty;
if(!itemss[i]->TryLockItem(LockReason::LockReason_Crafting)) {
for (int8 s = 0; s < i; s++) {
itemss[i]->TryUnlockItem(LockReason::LockReason_Crafting);
}
return false;
}
player_component_pair_qty->push_back({itemss[i]->details.unique_id, cur_qty});
player_components->push_back(itemss[i]);
if(have_qty >= required_qty)
break;
}
return true;
}
return false;
}
int8 Recipe::GetItemRequiredQuantity(int32 item_id) {
vector<int32>::iterator itr;
int8 comp_qty = 0, qty = 0;
for (int8 index = 0; index < GetTotalBuildComponents(); index++) {
if (components.count(index + 1) == 0)
continue;
vector<int32> rc = components[index + 1];
string comp_title;
int8 comp_qty;
bool matched = false;
for (itr = rc.begin(); itr != rc.end(); itr++) {
if (index == 0) {
comp_qty = build1_comp_qty;
}
else if (index == 1) {
comp_qty = build2_comp_qty;
}
else if (index == 2) {
comp_qty = build3_comp_qty;
}
else if (index == 3) {
comp_qty = build4_comp_qty;
}
if((*itr) == item_id)
qty += comp_qty;
}
}
return qty;
}

272
old/world/Recipes/Recipe.h Normal file
View File

@ -0,0 +1,272 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RECIPE_H_
#define RECIPE_H_
#include "../../common/types.h"
#include "../../common/Mutex.h"
#include "../classes.h"
#include <string.h>
#include <map>
class Item;
using namespace std;
struct RecipeComp
{
int32 RecipeComp;
};
struct RecipeProducts {
int32 product_id;
int32 byproduct_id;
int8 product_qty;
int8 byproduct_qty;
};
class Recipe {
public:
Recipe();
Recipe(Recipe *in);
virtual ~Recipe();
EQ2Packet *SerializeRecipe(Client *client, Recipe *recipe, bool display, int8 packet_type = 0, int8 sub_packet_type = 0, const char *struct_name = 0);
void SetID(int32 id) {this->id = id;}
void SetSoeID(int32 soe_id) { this->soe_id = soe_id; }
void SetBookID(int32 book_id) {this->book_id = book_id;}
void SetName(const char *name) {strncpy(this->name, name, sizeof(this->name));}
void SetDescription(const char* description) { strncpy(this->description, description, sizeof(this->description)); }
void SetBookName(const char *book_name) {strncpy(this->book_name, book_name, sizeof(this->book_name));}
void SetBook(const char *book) {strncpy(this->book, book, sizeof(this->book));}
void SetDevice(const char *device) {strncpy(this->device, device, sizeof(this->device));}
void SetLevel(int8 level) {this->level = level;}
void SetTier(int8 tier) {this->tier = tier;}
void SetIcon(int16 icon) {this->icon = icon;}
void SetSkill(int32 skill) {this->skill = skill;}
void SetTechnique(int32 technique) {this->technique = technique;}
void SetKnowledge(int32 knowledge) {this->knowledge = knowledge;}
void SetClasses(int32 classes) {this->classes = classes;}
void SetDevice_Sub_Type(int8 device_sub_type) {this->device_sub_type = device_sub_type;}
void SetUnknown1(int8 unknown1) {this->unknown1 = unknown1;}
void SetUnknown2(int32 unknown2) {this->unknown2 = unknown2;}
void SetUnknown3(int32 unknown3) {this->unknown3 = unknown3;}
void SetUnknown4(int32 unknown4) {this->unknown4 = unknown4;}
void SetProductID(int32 itemID) { product_item_id = itemID; }
void SetProductQuantity(int8 qty) { product_qty = qty; }
void SetProductName(const char* productName) { strncpy(product_name, productName, sizeof(product_name)); }
void SetBuild1ComponentTitle(const char* title) { strncpy(build1_comp_title, title, sizeof(build1_comp_title)); }
void SetBuild2ComponentTitle(const char* title) { strncpy(build2_comp_title, title, sizeof(build2_comp_title)); }
void SetBuild3ComponentTitle(const char* title) { strncpy(build3_comp_title, title, sizeof(build3_comp_title)); }
void SetBuild4ComponentTitle(const char* title) { strncpy(build4_comp_title, title, sizeof(build4_comp_title)); }
void SetFuelComponentTitle(const char* title) { strncpy(fuel_comp_title, title, sizeof(fuel_comp_title)); }
void SetPrimaryComponentTitle(const char* title) { strncpy(primary_build_comp_title, title, sizeof(primary_build_comp_title)); }
void SetBuild1ComponentQuantity(int8 qty) { build1_comp_qty = qty; }
void SetBuild2ComponentQuantity(int8 qty) { build2_comp_qty = qty; }
void SetBuild3ComponentQuantity(int8 qty) { build3_comp_qty = qty; }
void SetBuild4ComponentQuantity(int8 qty) { build4_comp_qty = qty; }
void SetFuelComponentQuantity(int8 qty) { fuel_comp_qty = qty; }
void SetPrimaryComponentQuantity(int8 qty) { primary_comp_qty = qty; }
int32 GetID() {return id;}
int32 GetSoeID() { return soe_id; }
int32 GetBookID() {return book_id;}
const char * GetName() {return name;}
const char* GetDescription() { return description; }
const char * GetBookName() {return book_name;}
const char * GetBook() {return book;}
const char * GetDevice() {return device;}
int8 GetLevel() {return level;}
int8 GetTier() {return tier;}
int16 GetIcon() {return icon;}
int32 GetSkill() {return skill;}
int32 GetTechnique() {return technique;}
int32 GetKnowledge() {return knowledge;}
int32 GetClasses() {return classes;}
//class_id = classes.GetTSBaseClass(spawn->GetTradeskillClass()) bit-match on class ids 1-13
//secondary_class_id = classes.GetSecondaryTSBaseClass(spawn->GetTradeskillClass()) bit-match on class ids 1-13
//tertiary_class_id = spawn->GetTradeskillClass() (direct match)
bool CanUseRecipeByClass(Item* item, int8 class_id) {
/* any can use bit combination of 1+2
adornments = 1
artisan = 2
*/
return item->generic_info.tradeskill_classes < 4 || (1 << class_id) & item->generic_info.tradeskill_classes;
}
int8 GetDevice_Sub_Type() {return device_sub_type;}
int8 GetUnknown1() {return unknown1;}
int32 GetUnknown2() {return unknown2;}
int32 GetUnknown3() {return unknown3;}
int32 GetUnknown4() {return unknown4;}
int32 GetProductID() { return product_item_id; }
const char* GetProductTitle() { return product_name; }
int8 GetProductQuantity() { return product_qty; }
const char* GetPrimaryBuildComponentTitle() { return primary_build_comp_title; }
const char* GetBuild1ComponentTitle() { return build1_comp_title; }
const char* GetBuild2ComponentTitle() { return build2_comp_title; }
const char* GetBuild3ComponentTitle() { return build3_comp_title; }
const char* GetBuild4ComponentTitle() { return build4_comp_title; }
const char* GetFuelComponentTitle() { return fuel_comp_title; }
int16 GetBuild1ComponentQuantity() { return build1_comp_qty; }
int16 GetBuild2ComponentQuantity() { return build2_comp_qty; }
int16 GetBuild3ComponentQuantity() { return build3_comp_qty; }
int16 GetBuild4ComponentQuantity() { return build4_comp_qty; }
int16 GetFuelComponentQuantity() { return fuel_comp_qty; }
int16 GetPrimaryComponentQuantity() { return primary_comp_qty; }
///<summary>Add a build component to this recipe</summary>
///<param name="itemID">Item id of the component</param>
///<param name="slot">Slot id for this component</param>
void AddBuildComp(int32 itemID, int8 slot, bool preferred = 0);
// int8 = slot, vector = itemid
map<int8, vector<int32> > components;
// int8 = stage, RecipeProducts = products/byproducts for this stage
map<int8, RecipeProducts*> products;
int8 GetHighestStage() { return highestStage; }
void SetHighestStage(int8 val) { highestStage = val; }
int8 GetTotalBuildComponents();
bool ProvidedAllRequiredComponents(Client* client, vector<Item*>* player_components, vector<pair<int32,int16>>* player_component_pair_qty);
bool PlayerHasComponentByItemID(Client* client, vector<Item*>* player_components, vector<pair<int32,int16>>* player_component_pair_qty, int32 item_id, int8 required_qty);
int8 GetItemRequiredQuantity(int32 item_id);
private:
int32 id;
int32 soe_id;
int32 book_id;
char name[256];
char description[256];
char book_name[256];
char book[256];
char device[30];
int8 level;
int8 tier;
int16 icon;
int32 skill;
int32 technique;
int32 knowledge;
int8 device_sub_type;
int32 classes;
int8 unknown1;
int32 unknown2;
int32 unknown3;
int32 unknown4;
int32 product_item_id;
char product_name[256];
int8 product_qty;
char primary_build_comp_title[256];
char build1_comp_title[256];
char build2_comp_title[256];
char build3_comp_title[256];
char build4_comp_title[256];
char fuel_comp_title[256];
int16 build1_comp_qty;
int16 build2_comp_qty;
int16 build3_comp_qty;
int16 build4_comp_qty;
int16 fuel_comp_qty;
int16 primary_comp_qty;
int8 highestStage;
};
class MasterRecipeList {
public:
MasterRecipeList();
virtual ~MasterRecipeList();
bool AddRecipe(Recipe *recipe);
Recipe* GetRecipe(int32 recipe_id);
Recipe* GetRecipeByCRC(int32 recipe_crc);
void ClearRecipes();
int32 Size();
EQ2Packet* GetRecipePacket(int32 recipe_id, Client *client = 0, bool display = false, int8 packet_type = 0);
/// <summary>Gets all the recipes for the given book name</summary>
/// <param name="book_name">Book name to get recipes for</param>
/// <returns>A vector of all the recipes for the given book</returns>
vector<Recipe*> GetRecipes(const char* book_name);
/// <summary>Gets a recipe with the given name</summary>
/// <param name='name'>The name of the recipe to get</param>
/// <returns>Recipe* whos name matches the given name</returns>
Recipe* GetRecipeByName(const char* name);
private:
Mutex m_recipes;
map<int32, Recipe *> recipes;
map<int32, Recipe *> recipes_crc;
};
class MasterRecipeBookList {
public:
MasterRecipeBookList();
virtual ~MasterRecipeBookList();
bool AddRecipeBook(Recipe *recipe);
Recipe * GetRecipeBooks(int32 recipe_id);
void ClearRecipeBooks();
int32 Size();
private:
Mutex m_recipeBooks;
map<int32, Recipe *> recipeBooks;
};
class PlayerRecipeList {
public:
PlayerRecipeList();
virtual ~PlayerRecipeList();
bool AddRecipe(Recipe *recipe);
Recipe * GetRecipe(int32 recipe_id);
void ClearRecipes();
bool RemoveRecipe(int32 recipe_id);
int32 Size();
map<int32, Recipe *> * GetRecipes() {return &recipes;}
private:
map<int32, Recipe *> recipes;
mutable std::shared_mutex player_recipe_mutex;
};
class PlayerRecipeBookList {
public:
PlayerRecipeBookList();
virtual ~PlayerRecipeBookList();
bool AddRecipeBook(Recipe *recipe);
bool HasRecipeBook(int32 book_id);
Recipe * GetRecipeBook(int32 recipe_id);
void ClearRecipeBooks();
int32 Size();
map<int32, Recipe *> * GetRecipeBooks() {return &recipeBooks;}
private:
map<int32, Recipe *> recipeBooks;
};
#endif

View File

@ -0,0 +1,296 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#endif
#include <mysql.h>
#include <assert.h>
#include "../../common/Log.h"
#include "../WorldDatabase.h"
#include "Recipe.h"
extern MasterRecipeList master_recipe_list;
extern MasterRecipeBookList master_recipebook_list;
extern MasterItemList master_item_list;
void WorldDatabase::LoadRecipes() {
DatabaseResult res;
bool status = database_new.Select(&res,
"SELECT r.`id`,r.`soe_id`,r.`level`,r.`icon`,r.`skill_level`,r.`technique`,r.`knowledge`,r.`name`,r.`description`,i.`name` as `book`,r.`bench`,ipc.`adventure_classes`, "
"r.`stage4_id`, r.`name`, r.`stage4_qty`, pcl.`name` as primary_comp_title,r.primary_comp_qty, fcl.name as `fuel_comp_title`, r.fuel_comp_qty, "
"bc.`name` AS build_comp_title, bc.qty AS build_comp_qty, bc2.`name` AS build2_comp_title, bc2.qty AS build2_comp_qty, "
"bc3.`name` AS build3_comp_title, bc3.qty AS build3_comp_qty, bc4.`name` AS build4_comp_title, bc4.qty AS build4_comp_qty,\n"
"r.stage0_id, r.stage1_id, r.stage2_id, r.stage3_id, r.stage4_id, r.stage0_qty, r.stage1_qty, r.stage2_qty, r.stage3_qty, r.stage4_qty,\n"
"r.stage0_byp_id, r.stage1_byp_id, r.stage2_byp_id, r.stage3_byp_id, r.stage4_byp_id, r.stage0_byp_qty, r.stage1_byp_qty, r.stage2_byp_qty, r.stage3_byp_qty, r.stage4_byp_qty\n"
"FROM `recipe` r\n"
"LEFT JOIN ((SELECT recipe_id, soe_recipe_crc FROM item_details_recipe_items GROUP BY soe_recipe_crc) as idri) ON idri.soe_recipe_crc = r.soe_id\n"
"LEFT JOIN items i ON idri.recipe_id = i.id\n"
"INNER JOIN items ipc ON r.stage4_id = ipc.id\n"
"INNER JOIN recipe_comp_list pcl ON r.primary_comp_list = pcl.id\n"
"INNER JOIN recipe_comp_list fcl ON r.fuel_comp_list = fcl.id\n"
"LEFT JOIN (SELECT rsc.recipe_id, rsc.comp_list, rsc.`index`, rcl.`name`, rsc.qty FROM recipe_secondary_comp rsc INNER JOIN recipe_comp_list rcl ON rcl.id = rsc.comp_list WHERE `index` = 0) AS bc ON bc.recipe_id = r.id\n"
"LEFT JOIN (SELECT rsc.recipe_id, rsc.comp_list, rsc.`index`, rcl.`name`, rsc.qty FROM recipe_secondary_comp rsc INNER JOIN recipe_comp_list rcl ON rcl.id = rsc.comp_list WHERE `index` = 1) AS bc2 ON bc2.recipe_id = r.id\n"
"LEFT JOIN (SELECT rsc.recipe_id, rsc.comp_list, rsc.`index`, rcl.`name`, rsc.qty FROM recipe_secondary_comp rsc INNER JOIN recipe_comp_list rcl ON rcl.id = rsc.comp_list WHERE `index` = 2) AS bc3 ON bc3.recipe_id = r.id\n"
"LEFT JOIN (SELECT rsc.recipe_id, rsc.comp_list, rsc.`index`, rcl.`name`, rsc.qty FROM recipe_secondary_comp rsc INNER JOIN recipe_comp_list rcl ON rcl.id = rsc.comp_list WHERE `index` = 3) AS bc4 ON bc4.recipe_id = r.id\n"
"WHERE r.bHaveAllProducts");
if (!status)
return;
while (res.Next()) {
int32 i = 0;
Recipe* recipe = new Recipe();
recipe->SetID(res.GetInt32(i++));
recipe->SetSoeID(res.GetInt32(i++));
recipe->SetLevel(res.GetInt32(i++));
recipe->SetTier(recipe->GetLevel() / 10 + 1);
recipe->SetIcon(res.GetInt32(i++));
recipe->SetSkill(res.GetInt32(i++));
recipe->SetTechnique(res.GetInt32(i++));
recipe->SetKnowledge(res.GetInt32(i++));
recipe->SetName(res.GetString(i++));
recipe->SetDescription(res.GetString(i++));
recipe->SetBook(res.GetString(i++));
//Convert the device string
string device = res.GetString(i++);
int32 deviceID = 0;
int8 deviceSubType = 0;
recipe->SetDevice(GetDeviceName(device).c_str());
recipe->SetUnknown2(deviceID);
recipe->SetDevice_Sub_Type(deviceSubType);
recipe->SetClasses(res.GetInt64(i++));
recipe->SetUnknown3(0);
recipe->SetUnknown4(0);
LogWrite(TRADESKILL__DEBUG, 5, "Recipes", "Loading recipe: %s (%u)", recipe->GetName(), recipe->GetID());
recipe->SetProductID(res.GetInt32(i++));
recipe->SetProductName(res.GetString(i++));
recipe->SetProductQuantity(res.GetInt8(i++));
recipe->SetPrimaryComponentTitle(res.GetString(i++));
recipe->SetPrimaryComponentQuantity(res.GetInt16(i++));
recipe->SetFuelComponentTitle(res.GetString(i++));
recipe->SetFuelComponentQuantity(res.GetInt16(i++));
recipe->SetBuild1ComponentTitle(res.GetString(i++));
recipe->SetBuild1ComponentQuantity(res.GetInt16(i++));
recipe->SetBuild2ComponentTitle(res.GetString(i++));
recipe->SetBuild2ComponentQuantity(res.GetInt16(i++));
recipe->SetBuild3ComponentTitle(res.GetString(i++));
recipe->SetBuild3ComponentQuantity(res.GetInt16(i++));
recipe->SetBuild4ComponentTitle(res.GetString(i++));
recipe->SetBuild4ComponentQuantity(res.GetInt16(i++));
LogWrite(TRADESKILL__DEBUG, 7, "Recipes", "Loading recipe: %s (%u)", recipe->GetName(), recipe->GetID());
if (!master_recipe_list.AddRecipe(recipe)) {
LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error adding recipe '%s' - duplicate ID: %u", recipe->GetName(), recipe->GetID());
delete recipe;
continue;
}
//Products/By-Products
for (int8 stage = 0; stage < 5; stage++) {
RecipeProducts* rp = new RecipeProducts;
rp->product_id = res.GetInt32(i);
rp->product_qty = res.GetInt8(i + 5);
rp->byproduct_id = res.GetInt32(i + 10);
rp->byproduct_qty = res.GetInt8(i + 15);
recipe->products[stage] = rp;
i++;
}
//Advance i past all the product info
//i += 15;
}
LoadRecipeComponents();
LogWrite(TRADESKILL__DEBUG, 0, "Recipes", "\tLoaded %u recipes", master_recipe_list.Size());
}
void WorldDatabase::LoadRecipeBooks(){
Recipe *recipe;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
res = query.RunQuery2(Q_SELECT, "SELECT id, name, tradeskill_default_level FROM items WHERE item_type='Recipe'");
if (res){
while ((row = mysql_fetch_row(res))){
recipe = new Recipe();
recipe->SetBookID(atoul(row[0]));
recipe->SetBookName(row[1]);
recipe->SetLevel(atoi(row[2]));
LogWrite(TRADESKILL__DEBUG, 5, "Recipes", "Loading Recipe Books: %s (%u)", recipe->GetBookName(), recipe->GetBookID());
if (!master_recipebook_list.AddRecipeBook(recipe)){
LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error adding Recipe Book '%s' - duplicate ID: %u", recipe->GetBookName(), recipe->GetBookID());
safe_delete(recipe);
continue;
}
}
}
LogWrite(TRADESKILL__DEBUG, 0, "Recipes", "\tLoaded %u Recipe Books ", master_recipebook_list.Size());
}
void WorldDatabase::LoadPlayerRecipes(Player *player){
Recipe *recipe;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int16 total = 0;
assert(player);
res = query.RunQuery2(Q_SELECT, "SELECT recipe_id, highest_stage FROM character_recipes WHERE char_id = %u", player->GetCharacterID());
if (res) {
while ((row = mysql_fetch_row(res))){
int32 recipe_id = atoul(row[0]);
Recipe* master_recipe = master_recipe_list.GetRecipe(recipe_id);
if(!master_recipe) {
LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error adding recipe %u to player '%s' - duplicate ID", atoul(row[0]), player->GetName());
continue;
}
recipe = new Recipe(master_recipe);
recipe->SetHighestStage(atoi(row[1]));
LogWrite(TRADESKILL__DEBUG, 5, "Recipes", "Loading recipe: %s (%u) for player: %s (%u)", recipe->GetName(), recipe->GetID(), player->GetName(), player->GetCharacterID());
if (!player->GetRecipeList()->AddRecipe(recipe)){
LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error adding recipe %u to player '%s' - duplicate ID", recipe->GetID(), player->GetName());
safe_delete(recipe);
continue;
}
total++;
}
LogWrite(TRADESKILL__DEBUG, 0, "Recipes", "Loaded %u recipes for player: %s (%u)", total, player->GetName(), player->GetCharacterID());
}
}
int32 WorldDatabase::LoadPlayerRecipeBooks(int32 char_id, Player *player) {
assert(player);
LogWrite(TRADESKILL__DEBUG, 0, "Recipes", "Loading Character Recipe Books for player '%s' ...", player->GetName());
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 count = 0;
int32 old_id = 0;
int32 new_id = 0;
Recipe* recipe;
res = query.RunQuery2(Q_SELECT, "SELECT recipebook_id FROM character_recipe_books WHERE char_id = %u", char_id);
if (res && mysql_num_rows(res) > 0) {
while (res && (row = mysql_fetch_row(res))){
count++;
new_id = atoul(row[0]);
if(new_id == old_id)
continue;
Item* item = master_item_list.GetItem(new_id);
if (!item)
continue;
recipe = new Recipe();
recipe->SetBookID(new_id);
recipe->SetBookName(item->name.c_str());
LogWrite(TRADESKILL__DEBUG, 5, "Recipes", "Loading Recipe Books: %s (%u)", recipe->GetBookName(), recipe->GetBookID());
if (!player->GetRecipeBookList()->AddRecipeBook(recipe)) {
LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error adding player Recipe Book '%s' - duplicate ID: %u", recipe->GetBookName(), recipe->GetBookID());
safe_delete(recipe);
continue;
}
old_id = new_id;
}
}
return count;
}
void WorldDatabase::SavePlayerRecipeBook(Player* player, int32 recipebook_id){
Query query;
query.AddQueryAsync(player->GetCharacterID(), this, Q_INSERT, "INSERT INTO character_recipe_books (char_id, recipebook_id) VALUES(%u, %u)", player->GetCharacterID(), recipebook_id);
//if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
//LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error in SavePlayerRecipeBook query '%s' : %s", query.GetQuery(), query.GetError());
}
void WorldDatabase::SavePlayerRecipe(Player* player, int32 recipe_id) {
Query query;
query.AddQueryAsync(player->GetCharacterID(), this, Q_INSERT, "INSERT INTO character_recipes (char_id, recipe_id) VALUES (%u, %u)", player->GetCharacterID(), recipe_id);
//if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
//LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error in SavePlayerRecipeBook query '%s' : %s", query.GetQuery(), query.GetError());
}
void WorldDatabase::LoadRecipeComponents() {
DatabaseResult res;
bool status = database_new.Select(&res,
"SELECT r.id, pc.item_id AS primary_comp, fc.item_id AS fuel_comp, sc.item_id as secondary_comp, rsc.`index` + 1 AS slot\n"
"FROM recipe r\n"
"INNER JOIN (select comp_list, item_id FROM recipe_comp_list_item ) as pc ON r.primary_comp_list = pc.comp_list\n"
"INNER JOIN (select comp_list, item_id FROM recipe_comp_list_item ) as fc ON r.fuel_comp_list = fc.comp_list\n"
"LEFT JOIN recipe_secondary_comp rsc ON rsc.recipe_id = r.id\n"
"LEFT JOIN (select comp_list, item_id FROM recipe_comp_list_item) as sc ON rsc.comp_list = sc.comp_list\n"
"WHERE r.bHaveAllProducts\n"
"ORDER BY r.id, rsc.`index` ASC");
if (!status) {
return;
}
Recipe* recipe = 0;
int32 id = 0;
while (res.Next()) {
int32 tmp = res.GetInt32(0);
if (id != tmp) {
id = tmp;
recipe = master_recipe_list.GetRecipe(id);
if (!recipe) {
continue;
}
}
if (recipe && !res.IsNull(3)) {
if (find(recipe->components[0].begin(), recipe->components[0].end(), res.GetInt32(1)) == recipe->components[0].end())
recipe->AddBuildComp(res.GetInt32(1), 0);
if (find(recipe->components[5].begin(), recipe->components[5].end(), res.GetInt32(2)) == recipe->components[5].end())
recipe->AddBuildComp(res.GetInt32(2), 5);
if (find(recipe->components[res.GetInt8(4)].begin(), recipe->components[res.GetInt8(4)].end(), res.GetInt32(3)) == recipe->components[res.GetInt8(4)].end())
recipe->AddBuildComp(res.GetInt32(3), res.GetInt8(4));
}
//else
//LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error loading `recipe_build_comps`, Recipe ID: %u", id);
}
}
void WorldDatabase::UpdatePlayerRecipe(Player* player, int32 recipe_id, int8 highest_stage) {
Query query;
query.AddQueryAsync(player->GetCharacterID(), this, Q_UPDATE, "UPDATE `character_recipes` SET `highest_stage` = %i WHERE `char_id` = %u AND `recipe_id` = %u", highest_stage, player->GetCharacterID(), recipe_id);
}
/*
ALTER TABLE `character_recipes`
ADD COLUMN `highest_stage` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `recipe_id`;
*/

545
old/world/Rules/Rules.cpp Normal file
View File

@ -0,0 +1,545 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include "../../common/debug.h"
#include "../../common/Log.h"
#include "../../common/database.h"
#include "Rules.h"
extern RuleManager rule_manager;
Rule::Rule() {
category = 0;
type = 0;
strncpy(value, "", sizeof(value));
strncpy(combined, "NONE", sizeof(combined));
}
Rule::Rule(int32 category, int32 type, const char *value, const char *combined) {
this->category = category;
this->type = type;
strncpy(this->value, value, sizeof(this->value));
strncpy(this->combined, combined, sizeof(this->combined));
}
Rule::Rule (Rule *rule_in) {
category = rule_in->GetCategory();
type = rule_in->GetType();
strncpy(value, rule_in->GetValue(), sizeof(value));
strncpy(combined, rule_in->GetCombined(), sizeof(combined));
}
Rule::~Rule() {
}
RuleSet::RuleSet() {
id = 0;
memset(name, 0, sizeof(name));
m_rules.SetName("RuleSet::rules");
}
RuleSet::RuleSet(RuleSet *in_rule_set) {
assert(in_rule_set);
map<int32, map<int32, Rule *> > * in_rules = in_rule_set->GetRules();
map<int32, map<int32, Rule *> >::iterator itr;
map<int32, Rule *>::iterator itr2;
Rule * rule;
m_rules.SetName("RuleSet::rules");
id = in_rule_set->GetID();
strncpy(name, in_rule_set->GetName(), sizeof(name));
for (itr = in_rules->begin(); itr != in_rules->end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
rule = itr2->second;
rules[rule->GetCategory()][rule->GetType()] = new Rule(rule);
}
}
}
RuleSet::~RuleSet() {
ClearRules();
}
void RuleSet::CopyRulesInto(RuleSet *in_rule_set) {
assert(in_rule_set);
map<int32, map<int32, Rule *> > * in_rules = in_rule_set->GetRules();
map<int32, map<int32, Rule *> >::iterator itr;
map<int32, Rule *>::iterator itr2;
Rule * rule;
ClearRules();
m_rules.writelock(__FUNCTION__, __LINE__);
for (itr = in_rules->begin(); itr != in_rules->end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
rule = itr2->second;
rules[rule->GetCategory()][rule->GetType()] = new Rule(rule);
}
}
m_rules.releasewritelock(__FUNCTION__, __LINE__);
}
void RuleSet::AddRule(Rule *rule) {
int32 category, type;
assert(rule);
category = rule->GetCategory();
type = rule->GetType();
m_rules.writelock(__FUNCTION__, __LINE__);
if (rules[category].count(type) == 0)
rules[category][type] = rule;
else
rules[category][type]->SetValue(rule->GetValue());
m_rules.releasewritelock(__FUNCTION__, __LINE__);
}
Rule * RuleSet::GetRule(int32 category, int32 type) {
Rule *ret = 0;
m_rules.readlock(__FUNCTION__, __LINE__);
if (rules[category].count(type) > 0)
ret = rules[category][type];
m_rules.releasereadlock(__FUNCTION__, __LINE__);
if (!ret)
ret = rule_manager.GetBlankRule();
LogWrite(RULESYS__DEBUG, 5, "Rules", "Rule: %s, Value: %s", ret->GetCombined(), ret->GetValue());
return ret;
}
Rule * RuleSet::GetRule(const char *category, const char *type) {
map<int32, map<int32, Rule *> >::iterator itr;
map<int32, Rule *>::iterator itr2;
char combined[256];
Rule *ret = 0;
snprintf(combined, sizeof(combined), "%s:%s", category, type);
// Zero terminate ([max - 1] = 0) to prevent a warning/error
combined[255] = 0;
m_rules.readlock(__FUNCTION__, __LINE__);
for (itr = rules.begin(); itr != rules.end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
if (!strcmp(itr2->second->GetCombined(), combined)) {
ret = itr2->second;
break;
}
}
}
m_rules.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
void RuleSet::ClearRules() {
map<int32, map<int32, Rule *> >::iterator itr;
map<int32, Rule *>::iterator itr2;
m_rules.writelock(__FUNCTION__, __LINE__);
for (itr = rules.begin(); itr != rules.end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++)
safe_delete(itr2->second);
}
rules.clear();
m_rules.releasewritelock(__FUNCTION__, __LINE__);
}
RuleManager::RuleManager() {
m_rule_sets.SetName("RuleManager::rule_sets");
m_global_rule_set.SetName("RuleManager::global_rule_set");
m_zone_rule_sets.SetName("RuleManager::zone_rule_sets");
Init();
}
RuleManager::~RuleManager() {
Flush();
}
void RuleManager::Init()
{
#define RULE_INIT(category, type, value) rules[category][type] = new Rule(category, type, value, #category ":" #type)
/* CLIENT */
RULE_INIT(R_Client, ShowWelcomeScreen, "0");
RULE_INIT(R_Client, GroupSpellsTimer, "1000");
RULE_INIT(R_Client, QuestQueueTimer, "50"); // in milliseconds
/* FACTION */
RULE_INIT(R_Faction, AllowFactionBasedCombat, "1");
/* GUILD */
RULE_INIT(R_Guild, MaxLevel, "50");
RULE_INIT(R_Guild, MaxPlayers, "-1");
/* PLAYER */
RULE_INIT(R_Player, MaxLevel, "50");
RULE_INIT(R_Player, MaxLevelOverrideStatus, "100");
RULE_INIT(R_Player, VitalityAmount, ".5");
RULE_INIT(R_Player, VitalityFrequency, "3600");
RULE_INIT(R_Player, XPMultiplier, "1.0");
RULE_INIT(R_Player, TSXPMultiplier, "1.0");
RULE_INIT(R_Player, MaxAA, "320");
RULE_INIT(R_Player, MaxClassAA, "100");
RULE_INIT(R_Player, MaxSubclassAA, "100");
RULE_INIT(R_Player, MaxShadowsAA, "70");
RULE_INIT(R_Player, MaxHeroicAA, "50");
RULE_INIT(R_Player, MaxTradeskillAA, "40");
RULE_INIT(R_Player, MaxPrestigeAA, "25");
RULE_INIT(R_Player, MaxTradeskillPrestigeAA, "25");
RULE_INIT(R_Player, MinLastNameLevel, "20");
RULE_INIT(R_Player, MaxLastNameLength, "20");
RULE_INIT(R_Player, MinLastNameLength, "4");
RULE_INIT(R_Player, DisableHouseAlignmentRequirement, "1");
RULE_INIT(R_Player, MentorItemDecayRate, ".05"); // 5% per level lost when mentoring
RULE_INIT(R_Player, TemporaryItemLogoutTime, "1800.0"); // time in seconds (double) for temporary item to decay after being logged out for a period of time, 30 min is the default
RULE_INIT(R_Player, HeirloomItemShareExpiration, "172800.0"); // 2 days ('48 hours') in seconds
RULE_INIT(R_Player, SwimmingSkillMinSpeed, "20");
RULE_INIT(R_Player, SwimmingSkillMaxSpeed, "200");
RULE_INIT(R_Player, SwimmingSkillMinBreathLength, "30");
RULE_INIT(R_Player, SwimmingSkillMaxBreathLength, "1000");
RULE_INIT(R_Player, AutoSkillUpBaseSkills, "0"); // when set to 1 we auto skill to max value on levelling up for armor,shield,class,weapon skills
RULE_INIT(R_Player, MaxWeightStrengthMultiplier, "2.0"); // multiplier for strength to add to max weight, eg 25 str * 2.0 = 50 max weight + base weight
RULE_INIT(R_Player, BaseWeight, "50"); // base weight per class, added to max weight with the strength multiplier
RULE_INIT(R_Player, WeightPercentImpact, "0.01"); // overweight in stone speed impact (.01 = 1% per 1 stone)
RULE_INIT(R_Player, WeightPercentCap, "0.95"); // cap total impact for being overweight (.95 = 95%)
RULE_INIT(R_Player, CoinWeightPerStone, "40.0"); // coin weight per stone, 40.0 = 40 coins per 1 stone (per DoF client hover over)
RULE_INIT(R_Player, WeightInflictsSpeed, "1"); // whether weight will inflict speed, 1 = on, 0 = off
RULE_INIT(R_Player, LevelMasterySkillMultiplier, "5"); // multiplier for adventure level / recommended level when applying mastery damage to determine if they are in mastery range
RULE_INIT(R_Player, TraitTieringSelection, "1"); // when set to true limited to single trait per group, otherwise you can freely select from any group
RULE_INIT(R_Player, ClassicTraitLevelTable, "1"); // uses built in table based on Prima Guide, see Traits.cpp for more, otherwise uses the levels below
RULE_INIT(R_Player, TraitFocusSelectLevel, "9"); // x levels to receive new trait of focus, eg level/rule, level 10, rule value 5, 10/5 = 2 focus traits available at level 10
RULE_INIT(R_Player, TraitTrainingSelectLevel, "10"); // x levels to receive new trait of focus
RULE_INIT(R_Player, TraitRaceSelectLevel, "10"); // x levels to receive new trait of focus
RULE_INIT(R_Player, TraitCharacterSelectLevel, "10"); // x levels to receive new trait of focus
RULE_INIT(R_Player, StartHPBase, "40");
RULE_INIT(R_Player, StartPowerBase, "45");
RULE_INIT(R_Player, StartHPLevelMod, "2.0");
RULE_INIT(R_Player, StartPowerLevelMod, "2.1");
RULE_INIT(R_Player, AllowPlayerEquipCombat, "1");
RULE_INIT(R_Player, MaxTargetCommandDistance, "50.0"); // max distance allowed for /target command when target name is not in group
RULE_INIT(R_Player, MinSkillMultiplierValue, "30"); // min skill we use as a multiplier to note the max skill allowed by the node
RULE_INIT(R_Player, HarvestSkillUpMultiplier, "2.0"); /* multiplier for node to take the "min skill" max and use a multiplier to offset the max skill allowed to skill up on node.
** Eg. 50 min skill on node, 50*1.5=75, no one with higher than 75 skill gets a skill up
*/
RULE_INIT(R_Player, MiniDingPercentage, "10"); /* Mini ding percentage for EverQuest 2, eg. default is every 10% (triggers at 10%, 20%, 30%, 40%, 50%, .. so on). setting 20% (triggers at 20%, 40%, 60%, 80%)
** Setting to less than 10 or greater than 50 will disable completely
** Range supported is 10 - 50%.
**/
RULE_INIT(R_Player, ForceCommonerFarJourney, "1");
/* PVP */
RULE_INIT(R_PVP, AllowPVP, "0");
RULE_INIT(R_PVP, LevelRange, "4");
RULE_INIT(R_PVP, InvisPlayerDiscoveryRange, "20"); // value > 0 sets radius inner to see, = 0 means always seen, -1 = never seen
RULE_INIT(R_PVP, PVPMitigationModByLevel, "25"); // gives a bonus to mitigation for PVP combat to offset the percentage level * mod (default 25)
RULE_INIT(R_PVP, PVPType, "0"); // 0 = FFA, 1 = PVPAlignment (Bind zone), 2 = Alignment (assigned via LUA function SetAlignment)
/* COMBAT */
RULE_INIT(R_Combat, MaxCombatRange, "4.0");
RULE_INIT(R_Combat, DeathExperienceDebt, "50.00"); // divide by 100, 50/100 = .5% debt per pve death
RULE_INIT(R_Combat, PVPDeathExperienceDebt, "25.00"); // divide by 100, 25/100 = .25% debt per pvp death
RULE_INIT(R_Combat, GroupExperienceDebt, "0"); // set to 1 means we will share debt between the group
RULE_INIT(R_Combat, ExperienceToDebt, "50.00"); // percentage of xp earned to debt vs obtained xp 50/100 = 50% to debt
RULE_INIT(R_Combat, ExperienceDebtRecoveryPercent, "5.00"); // recovery percentage per period of time, 5/100 = 5% recovered (so if .5% debt, .5*.05 = .025, .5-.025=.475% debt left)
RULE_INIT(R_Combat, ExperienceDebtRecoveryPeriod, "600"); // every 10 minutes (x*60 seconds) recover ExperienceDebtRecoveryPercent
RULE_INIT(R_Combat, EnableSpiritShards, "1");
RULE_INIT(R_Combat, SpiritShardSpawnScript, "SpawnScripts/Generic/SpiritShard.lua");
RULE_INIT(R_Combat, ShardDebtRecoveryPercent, "25.00"); // recovered percentage of debt upon obtainig shard, 25/100 means 25%. If there is .5 DeathExperienceDebt, .5*25% = .125, .5 - .125 = .375
RULE_INIT(R_Combat, ShardRecoveryByRadius, "1"); // allow shards to auto pick up by radius, not requiring to click/right click the shard
RULE_INIT(R_Combat, ShardLifetime, "86400"); // default: 86400 seconds (one day)
RULE_INIT(R_Combat, EffectiveMitigationCapLevel, "80"); // level multiplier for max effective cap, level * 80 (default)
RULE_INIT(R_Combat, CalculatedMitigationCapLevel, "100"); // The cap to calculate your mitigation from is [level*100].
RULE_INIT(R_Combat, MitigationLevelEffectivenessMax, "1.5"); // ratio victim level / attacker level for max effectiveness, when victim is higher level cap can reach 1.5
RULE_INIT(R_Combat, MitigationLevelEffectivenessMin, ".5"); // ratio victim level / attacker level for min effectiveness
RULE_INIT(R_Combat, MaxMitigationAllowed, ".75"); // percentage max mitigation allowed, eg. 75% of damage can be mitigated max in PVE
RULE_INIT(R_Combat, MaxMitigationAllowedPVP, ".75"); // percentage max mitigation allowed, eg. 75% of damage can be mitigated max in PVP
RULE_INIT(R_Combat, StrengthNPC, "10"); // divider for strength NPC only str/x = additional dmg to low/high dmg
RULE_INIT(R_Combat, StrengthOther, "25"); // divider for strength other than NPC str/x = additional dmg to low/high dmg
RULE_INIT(R_Combat, MaxSkillBonusByLevel, "1.5"); // Level * 1.5 = max bonus skill allowed
RULE_INIT(R_Combat, LockedEncounterNoAttack, "1"); // when set to 1, players/group members not part of the encounter cannot attack until /yell
RULE_INIT(R_Combat, MaxChaseDistance, "0.0"); // Default is 0, uses hard coded define MAX_CHASE_DISTANCE of 80.0. If set then this is an override for zone/server level.
/* SPAWN */
RULE_INIT(R_Spawn, SpeedMultiplier, "300"); // note: this value was 1280 until 6/1/2009, then was 600 til Sep 2009, when it became 300...?
RULE_INIT(R_Spawn, ClassicRegen, "0");
RULE_INIT(R_Spawn, HailMovementPause, "5000"); // time in milliseconds the spawn is paused on hail
RULE_INIT(R_Spawn, HailDistance, "5"); // max distance to hail a spawn/npc
RULE_INIT(R_Spawn, UseHardCodeWaterModelType, "1"); // uses alternate method of setting water type by model type (hardcoded) versus relying on just DB
RULE_INIT(R_Spawn, UseHardCodeFlyingModelType, "1"); // uses alternate method of setting flying type by model type (hardcoded) versus relying on just DB
/* TIMER */
/* UI */
RULE_INIT(R_UI, MaxWhoResults, "20");
RULE_INIT(R_UI, MaxWhoOverrideStatus, "200");
/* WORLD */
RULE_INIT(R_World, DefaultStartingZoneID, "1");
RULE_INIT(R_World, EnablePOIDiscovery, "0");
RULE_INIT(R_World, GamblingTokenItemID, "2");
RULE_INIT(R_World, GuildAutoJoin, "0");
RULE_INIT(R_World, GuildAutoJoinID, "1");
RULE_INIT(R_World, GuildAutoJoinDefaultRankID, "7");
RULE_INIT(R_World, MaxPlayers, "-1");
RULE_INIT(R_World, MaxPlayersOverrideStatus, "100");
RULE_INIT(R_World, ServerLocked, "0");
RULE_INIT(R_World, ServerLockedOverrideStatus, "10");
RULE_INIT(R_World, SyncZonesWithLogin, "1");
RULE_INIT(R_World, SyncEquipWithLogin, "1");
RULE_INIT(R_World, UseBannedIPsTable, "0");
RULE_INIT(R_World, LinkDeadTimer, "120000"); // default: 2 minutes
RULE_INIT(R_World, RemoveDisconnectedClientsTimer, "30000"); // default: 30 seconds
RULE_INIT(R_World, PlayerCampTimer, "20"); // default: 20 seconds
RULE_INIT(R_World, GMCampTimer, "1"); // default: 1 second
RULE_INIT(R_World, AutoAdminPlayers, "0"); // default: No
RULE_INIT(R_World, AutoAdminGMs, "0"); // default: No
RULE_INIT(R_World, AutoAdminStatusValue, "10"); // default: 10 (CSR)
RULE_INIT(R_World, DuskTime, "20:00"); // default: 8pm
RULE_INIT(R_World, DawnTime, "8:00"); // default: 8am
RULE_INIT(R_World, ThreadedLoad, "0"); // default: no threaded loading
RULE_INIT(R_World, TradeskillSuccessChance, "87.0"); // default: 87% chance of success while crafting
RULE_INIT(R_World, TradeskillCritSuccessChance, "2.0"); // default: 2% chance of critical success while crafting
RULE_INIT(R_World, TradeskillFailChance, "10.0"); // default: 10% chance of failure while crafting
RULE_INIT(R_World, TradeskillCritFailChance, "1.0"); // default: 1% chance of critical failure while crafting
RULE_INIT(R_World, TradeskillEventChance, "15.0"); // default: 15% chance of a tradeskill event while crafting
RULE_INIT(R_World, EditorURL, "www.eq2emulator.net"); // default: www.eq2emulator.net
RULE_INIT(R_World, EditorIncludeID, "0"); // default: 0 (0 = disabled, 1 = enabled)
RULE_INIT(R_World, EditorOfficialServer, "0"); // default: 0 (0 = disabled, 1 = enabled)
RULE_INIT(R_World, SavePaperdollImage, "1"); // default: true
RULE_INIT(R_World, SaveHeadshotImage, "1"); // default: true
RULE_INIT(R_World, SendPaperdollImagesToLogin, "1"); // default: true
RULE_INIT(R_World, TreasureChestDisabled, "0"); // default: false
RULE_INIT(R_World, StartingZoneLanguages, "0"); // default: 0 (0 = Live Like, 1 = Starting City Based)
RULE_INIT(R_World, StartingZoneRuleFlag, "0"); // default: 0 - match any options available, just based on version/other fields (will not force qc/outpost)
// 1 - force split zones on alignment/deity despite client selection (queens colony/overlord outpost)
// 4 - send to 'new' starting zones, won't support old clients
// 8 - (isle of refuge)
RULE_INIT(R_World, EnforceRacialAlignment, "1");
RULE_INIT(R_World, MemoryCacheZoneMaps, "0"); // 0 disables caching the zone maps in memory, too many individual/unique zones entered may cause a lot of memory build up
RULE_INIT(R_World, AutoLockEncounter, "0"); // When set to 0 we require player to attack to lock the encounter, otherwise if 1 then npc can auto lock encounter
RULE_INIT(R_World, DisplayItemTiers, "1"); // Display item tiers when set to 1, otherwise do not
RULE_INIT(R_World, LoreAndLegendAccept, "0"); // default: 0 - L&L quests accepted only through starter books. 1 - L&L quests can be started by examining bodyparts.
//INSERT INTO `ruleset_details`(`id`, `ruleset_id`, `rule_category`, `rule_type`, `rule_value`, `description`) VALUES (NULL, '1', 'R_World', '', '', '')
/* ZONE */
RULE_INIT(R_Zone, MaxPlayers, "100");
RULE_INIT(R_Zone, MinZoneLevelOverrideStatus, "1");
RULE_INIT(R_Zone, MinZoneAccessOverrideStatus, "100");
RULE_INIT(R_Zone, WeatherEnabled, "1"); // default: 1 (0 = disabled, 1 = enabled)
RULE_INIT(R_Zone, WeatherType, "0"); // default: 1 (0 = normal, 1 = dynamic, 2 = random, 3 = chaotic)
RULE_INIT(R_Zone, MinWeatherSeverity, "0.0"); // default: 0.0 or no weather
RULE_INIT(R_Zone, MaxWeatherSeverity, "1.0"); // default: 1.0 or hard rain (range 0.0 - 1.0, rain starts at 0.75)
RULE_INIT(R_Zone, WeatherChangeFrequency, "300"); // default: 5 minutes
RULE_INIT(R_Zone, WeatherChangePerInterval, "0.02"); // default: 0.02 (slight changes)
RULE_INIT(R_Zone, WeatherChangeChance, "20"); // default: 20% (in whole percents)
RULE_INIT(R_Zone, WeatherDynamicMaxOffset, "0.08"); // default: 0.08 - dynamic weather changes can only change this max amount
RULE_INIT(R_Zone, SpawnUpdateTimer, "50"); // default: 50ms - how often to check for spawn update sends
RULE_INIT(R_Zone, CheckAttackNPC, "2000"); // default: 2 seconds, how often to for NPCs to attack eachother
RULE_INIT(R_Zone, CheckAttackPlayer, "2000"); // default: 2 seconds, how often to check for NPCs to attack players
RULE_INIT(R_Zone, HOTime, "10.0"); // default: 10 seconds, time to complete the HO wheel before it expires
/* ZONE TIMERS */
RULE_INIT(R_Zone, RegenTimer, "6000");
RULE_INIT(R_Zone, ClientSaveTimer, "60000");
RULE_INIT(R_Zone, ShutdownDelayTimer, "120000");
RULE_INIT(R_Zone, WeatherTimer, "60000"); // default: 1 minute
RULE_INIT(R_Zone, SpawnDeleteTimer, "30000"); // default: 30 seconds, how long a spawn pointer is held onto after being removed from the world before deleting it
RULE_INIT(R_Zone, UseMapUnderworldCoords, "1"); // use maps lowest Y coordinate to establish underworld markers
RULE_INIT(R_Zone, MapUnderworldCoordOffset, "-200.0"); // adds (or in the case of negative value subtracts) so that the underworld marker is lower when map is using its lowest Y coordinate
RULE_INIT(R_Zone, SharedZoneMaxPlayers, "30"); // max players in a shared zone (non instanced) before splitting to another zone, city_zone flagged are exempt
RULE_INIT(R_Loot, LootRadius, "5.0");
RULE_INIT(R_Loot, AutoDisarmChest, "1");
RULE_INIT(R_Loot, ChestTriggerRadiusGroup, "10.0"); // radius at which chest will trigger against group members
RULE_INIT(R_Loot, ChestUnlockedTimeDrop, "1200"); // time in seconds, 20 minutes by default, triggers only if AllowChestUnlockByDropTime is 1
RULE_INIT(R_Loot, AllowChestUnlockByDropTime, "1"); // when set to 1 we will start a countdown timer to allow anyone to loot once ChestUnlockedTimeDrop elapsed
RULE_INIT(R_Loot, ChestUnlockedTimeTrap, "600"); // time in seconds, 10 minutes by default
RULE_INIT(R_Loot, AllowChestUnlockByTrapTime, "1"); // when set to 1 we will allow unlocking the chest to all players after the trap is triggered (or chest is open) and period ChestUnlockedTimeTrap elapsed
RULE_INIT(R_Loot, SkipLootGrayMob, "1");
RULE_INIT(R_Loot, LootDistributionTime, "120"); // time in seconds that we allow the group to determine their loot decision (lotto/need/greed/decline).
RULE_INIT(R_Spells, NoInterruptBaseChance, "50");
RULE_INIT(R_Spells, EnableFizzleSpells, "1"); // enables/disables the 'fizzling' of spells based on can_fizzle in the spells table. This also enables increasing specialized skills for classes based on spells/abilities.
RULE_INIT(R_Spells, DefaultFizzleChance, "10.0"); // default percentage x / 100, eg 10% is 10.0
RULE_INIT(R_Spells, FizzleMaxSkill, "1.2"); // 1.0 is 100%, 1.2 is 120%, so you get 120% your max skill against a spell, no fizzle
RULE_INIT(R_Spells, FizzleDefaultSkill, ".2"); // offset against MaxSkill to average out to 100%, default of .2f so we don't go over the threshold if no skill
RULE_INIT(R_Spells, EnableCrossZoneGroupBuffs, "0"); // enables/disables allowing cross zone group buffs
RULE_INIT(R_Spells, EnableCrossZoneTargetBuffs, "0"); // enables/disables allowing cross zone target buffs
RULE_INIT(R_Spells, PlayerSpellSaveStateWaitInterval, "100"); // time in milliseconds we wait before performing a save when the spell save trigger is activated, allows additional actions to take place until the cap is hit
RULE_INIT(R_Spells, PlayerSpellSaveStateCap, "1000"); // sets a maximum wait time before we queue a spell state save to the DB, given a lot can go on in a short period with players especially in combat, maybe good to have this at a higher interval.
RULE_INIT(R_Spells, RequirePreviousTierScribe, "0"); // requires step up apprentice -> apprentice (handcrafted?) -> journeyman (handcrafted?) -> adept -> expert -> master
RULE_INIT(R_Spells, CureSpellID, "110003"); // Base Cure spell that was used after they removed cure types
RULE_INIT(R_Spells, CureCurseSpellID, "110004"); // Curse Spell ID in the spells database
RULE_INIT(R_Spells, CureNoxiousSpellID, "110005"); // Noxious/Poison Spell ID in the spells database
RULE_INIT(R_Spells, CureMagicSpellID, "210006"); // Magic/Elemental Spell ID in the spells database
RULE_INIT(R_Spells, CureTraumaSpellID, "0"); // Trauma/Mental Spell ID in the spells database
RULE_INIT(R_Spells, CureArcaneSpellID, "0"); // Arcane/Heat Spell ID in the spells database
RULE_INIT(R_Spells, MinistrationSkillID, "366253016"); // ministration skill id used to map power reduction rule MinistrationPowerReductionMax
RULE_INIT(R_Spells, MinistrationPowerReductionMax, "15.0"); // max percentage of power reduction for spells with ministration mastery skill (default is 15.0 for 15%)
RULE_INIT(R_Spells, MinistrationPowerReductionSkill, "25"); // divides by integer value to establish how much skill req for higher power reduction
RULE_INIT(R_Spells, MasterSkillReduceSpellResist, "25"); // divides by integer value to establish how much skill bonus for reducing spell resistance on target
RULE_INIT(R_Spells, UseClassicSpellLevel, "0"); // Uses fractional spell levels (eg. you gain spells inbetween levels).
RULE_INIT(R_Expansion, GlobalExpansionFlag, "0");
RULE_INIT(R_Expansion, GlobalHolidayFlag, "0");
RULE_INIT(R_World, DatabaseVersion, "0");
//devn00b
RULE_INIT(R_Discord, DiscordEnabled, "0"); //Enable/Disable built in discord bot.
RULE_INIT(R_Discord, DiscordWebhookURL, "None"); //Webhook url used for server -> discord messages.
RULE_INIT(R_Discord, DiscordBotToken, "None"); //Bot token used to connect to discord and provides discord -> server messages.
RULE_INIT(R_Discord, DiscordChannel, "Discord"); // in-game channel used for server -> discord messages.
RULE_INIT(R_Discord, DiscordListenChan, "0"); // Discord ChannelID used for discord->server messages.
#undef RULE_INIT
}
void RuleManager::Flush(bool reinit)
{
map<int32, map<int32, Rule*> >::iterator itr;
map<int32, Rule*>::iterator itr2;
for (itr = rules.begin(); itr != rules.end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++)
safe_delete(itr2->second);
}
rules.clear();
ClearRuleSets();
ClearZoneRuleSets();
if (reinit)
Init();
}
void RuleManager::LoadCodedDefaultsIntoRuleSet(RuleSet *rule_set) {
map<int32, map<int32, Rule *> >::iterator itr;
map<int32, Rule *>::iterator itr2;
assert(rule_set);
for (itr = rules.begin(); itr != rules.end(); itr++) {
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++)
rule_set->AddRule(new Rule(itr2->second));
}
}
bool RuleManager::AddRuleSet(RuleSet *rule_set) {
bool ret = false;
int32 id;
assert(rule_set);
id = rule_set->GetID();
m_rule_sets.writelock(__FUNCTION__, __LINE__);
if (rule_sets.count(id) == 0) {
rule_sets[id] = rule_set;
ret = true;
}
m_rule_sets.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
int32 RuleManager::GetNumRuleSets() {
int32 ret;
m_rule_sets.readlock(__FUNCTION__, __LINE__);
ret = rule_sets.size();
m_rule_sets.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
void RuleManager::ClearRuleSets() {
map<int32, RuleSet *>::iterator itr;
m_rule_sets.writelock(__FUNCTION__, __LINE__);
for (itr = rule_sets.begin(); itr != rule_sets.end(); itr++)
safe_delete(itr->second);
rule_sets.clear();
m_rule_sets.releasewritelock(__FUNCTION__, __LINE__);
}
bool RuleManager::SetGlobalRuleSet(int32 rule_set_id) {
if (rule_sets.count(rule_set_id) == 0)
return false;
global_rule_set.CopyRulesInto(rule_sets[rule_set_id]);
return true;
}
Rule * RuleManager::GetGlobalRule(int32 category, int32 type) {
return global_rule_set.GetRule(category, type);
}
Rule * RuleManager::GetGlobalRule(const char* category, const char* type) {
return global_rule_set.GetRule(category, type);
}
bool RuleManager::SetZoneRuleSet(int32 zone_id, int32 rule_set_id) {
bool ret = true;
RuleSet *rule_set;
m_rule_sets.readlock(__FUNCTION__, __LINE__);
if (rule_sets.count(rule_set_id) == 0)
ret = false;
rule_set = rule_sets[rule_set_id];
if (ret) {
m_zone_rule_sets.writelock(__FUNCTION__, __LINE__);
zone_rule_sets[zone_id] = rule_set;
m_zone_rule_sets.releasewritelock(__FUNCTION__, __LINE__);
}
m_rule_sets.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
Rule * RuleManager::GetZoneRule(int32 zone_id, int32 category, int32 type) {
Rule *ret = 0;
/* first try to get the zone rule */
if(zone_id) {
m_zone_rule_sets.readlock(__FUNCTION__, __LINE__);
if (zone_rule_sets.count(zone_id) > 0)
ret = zone_rule_sets[zone_id]->GetRule(category, type);
m_zone_rule_sets.releasereadlock(__FUNCTION__, __LINE__);
}
if(!ret) {
ret = GetGlobalRule(category, type);
}
return ret;
}
void RuleManager::ClearZoneRuleSets() {
m_zone_rule_sets.writelock(__FUNCTION__, __LINE__);
zone_rule_sets.clear();
m_zone_rule_sets.releasewritelock(__FUNCTION__, __LINE__);
}

Some files were not shown because too many files have changed in this diff Show More