upload C++ source

This commit is contained in:
Sky Johnson 2025-08-06 19:00:30 -05:00
parent 180f0ec3fa
commit 8bc92f035a
216 changed files with 186785 additions and 4 deletions

5
.gitignore vendored
View File

@ -16,7 +16,4 @@
*.out
# Go workspace file
go.work
# LuaJIT wrapper source
luawrapper
go.work

851
old/LoginServer/client.hpp Normal file
View File

@ -0,0 +1,851 @@
// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team - GPL License
#pragma once
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <vector>
#include <map>
#include "net.hpp"
#include "lworld.hpp"
#include "login_structs.hpp"
#include "login_account.hpp"
#include "login_database.hpp"
#include "../common/tcp.hpp"
#include "../common/timer.hpp"
#include "../common/linked_list.hpp"
#include "../common/log.hpp"
#include "../common/debug.hpp"
#include "../common/config_reader.hpp"
#include "../common/misc_functions.hpp"
#include "../common/stream/eq_stream.hpp"
#include "../common/packet/packet_dump.hpp"
#include "../common/packet/packet_struct.hpp"
#include "../common/packet/packet_functions.hpp"
#include "../common/opcodes/emu_opcodes.hpp"
using namespace std;
enum eLoginMode { None, Normal, Registration };
extern NetConnection net;
extern LWorldList world_list;
extern ClientList client_list;
extern LoginDatabase database;
extern map<int16,OpcodeManager*> EQOpcodeManager;
extern ConfigReader configReader;
class DelayQue
{
public:
// Constructor initializes timer and packet for delayed queue processing
DelayQue(Timer* in_timer, EQApplicationPacket* in_packet)
{
timer = in_timer;
packet = in_packet;
}
Timer* timer; // Timer for delay processing
EQApplicationPacket* packet; // Packet to be processed after delay
};
class Client
{
public:
// Constructor initializes client connection and default values
Client(EQStream* ieqnc)
{
eqnc = ieqnc;
ip = eqnc->GetrIP();
port = ntohs(eqnc->GetrPort());
account_id = 0;
lsadmin = 0;
worldadmin = 0;
lsstatus = 0;
version = 0;
kicked = false;
verified = false;
memset(bannedreason, 0, sizeof(bannedreason));
memset(key,0,10);
LoginMode = None;
num_updates = 0;
updatetimer = new Timer(500);
updatelisttimer = new Timer(10000);
disconnectTimer = 0;
memset(ClientSession,0,25);
request_num = 0;
login_account = 0;
createRequest = 0;
playWaitTimer = NULL;
start = false;
update_position = 0;
update_packets = 0;
needs_world_list = true;
sent_character_list = false;
}
// Destructor cleans up allocated resources and closes connection
~Client()
{
safe_delete(login_account);
eqnc->Close();
safe_delete(playWaitTimer);
safe_delete(createRequest);
safe_delete(disconnectTimer);
safe_delete(updatetimer);
}
// Main processing loop for client connections and packet handling
bool Process()
{
if(!start && !eqnc->CheckActive()) {
if(!playWaitTimer)
playWaitTimer = new Timer(5000);
else if(playWaitTimer->Check()) {
safe_delete(playWaitTimer);
return false;
}
return true;
}
else if(!start) {
safe_delete(playWaitTimer);
start = true;
}
if(disconnectTimer && disconnectTimer->Check()) {
safe_delete(disconnectTimer);
getConnection()->SendDisconnect();
}
if(!kicked) {
EQApplicationPacket *app = 0;
if(playWaitTimer != NULL && playWaitTimer->Check()) {
SendPlayFailed(PLAY_ERROR_SERVER_TIMEOUT);
safe_delete(playWaitTimer);
}
if(!needs_world_list && updatetimer && updatetimer->Check()) {
if(updatelisttimer && updatelisttimer->Check()) {
if(num_updates >= 180) { // 30 minutes
getConnection()->SendDisconnect();
}
else {
vector<PacketStruct*>::iterator itr;
if(update_packets) {
for(itr = update_packets->begin(); itr != update_packets->end(); itr++) {
safe_delete(*itr);
}
}
safe_delete(update_packets);
update_packets = world_list.GetServerListUpdate(version);
}
num_updates++;
}
else {
if(!update_packets) {
update_packets = world_list.GetServerListUpdate(version);
}
else {
if(update_position < update_packets->size()) {
QueuePacket(update_packets->at(update_position)->serialize());
update_position++;
}
else
update_position = 0;
}
}
}
while(app = eqnc->PopPacket()) {
switch(app->GetOpcode()) {
case OP_LoginRequestMsg:
HandleLoginRequest(app);
break;
case OP_KeymapLoadMsg:
// Keymap message - currently unused
break;
case OP_AllWSDescRequestMsg:
HandleWorldServerListRequest();
break;
case OP_LsClientCrashlogReplyMsg:
SaveErrorsToDB(app, "Crash Log", GetVersion());
break;
case OP_LsClientVerifylogReplyMsg:
SaveErrorsToDB(app, "Verify Log", GetVersion());
break;
case OP_LsClientAlertlogReplyMsg:
SaveErrorsToDB(app, "Alert Log", GetVersion());
break;
case OP_LsClientBaselogReplyMsg:
SaveErrorsToDB(app, "Base Log", GetVersion());
break;
case OP_AllCharactersDescRequestMsg:
break;
case OP_CreateCharacterRequestMsg:
HandleCreateCharacterRequest(app);
break;
case OP_PlayCharacterRequestMsg:
HandlePlayCharacterRequest(app);
break;
case OP_DeleteCharacterRequestMsg:
HandleDeleteCharacterRequest(app);
break;
default:
HandleUnknownOpcode(app);
}
delete app;
}
}
if(!eqnc->CheckActive()) {
return false;
}
return true;
}
// Handles login request packets and validates credentials
void HandleLoginRequest(EQApplicationPacket* app)
{
DumpPacket(app);
PacketStruct* packet = configReader.getStruct("LS_LoginRequest", 1);
if(packet && packet->LoadPacketData(app->pBuffer,app->size)) {
version = packet->getType_int16_ByName("version");
LogWrite(LOGIN__DEBUG, 0, "Login", "Classic Client Version Provided: %i", version);
if(version == 0 || EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) {
safe_delete(packet);
packet = configReader.getStruct("LS_LoginRequest", 1208);
if(packet && packet->LoadPacketData(app->pBuffer, app->size)) {
version = packet->getType_int16_ByName("version");
}
else
return;
}
LogWrite(LOGIN__DEBUG, 0, "Login", "New Client Version Provided: %i", version);
if(EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) {
LogWrite(LOGIN__ERROR, 0, "Login", "Incompatible client version provided: %i", version);
SendLoginDenied();
safe_delete(packet);
return;
}
if(EQOpcodeManager.count(GetOpcodeVersion(version)) > 0 && getConnection()) {
getConnection()->SetClientVersion(GetVersion());
EQ2_16BitString username = packet->getType_EQ2_16BitString_ByName("username");
EQ2_16BitString password = packet->getType_EQ2_16BitString_ByName("password");
LoginAccount* acct = database.LoadAccount(username.data.c_str(),password.data.c_str(), net.IsAllowingAccountCreation());
if(acct) {
Client* otherclient = client_list.FindByLSID(acct->getLoginAccountID());
if(otherclient)
otherclient->getConnection()->SendDisconnect(); // Prevent double login
}
if(acct) {
SetAccountName(username.data.c_str());
database.UpdateAccountIPAddress(acct->getLoginAccountID(), getConnection()->GetrIP());
database.UpdateAccountClientDataVersion(acct->getLoginAccountID(), version);
LogWrite(LOGIN__INFO, 0, "Login", "%s successfully logged in.", (char*)username.data.c_str());
}
else {
if(username.size > 0)
LogWrite(LOGIN__ERROR, 0, "Login", "%s login failed!", (char*)username.data.c_str());
else
LogWrite(LOGIN__ERROR, 0, "Login", "[UNKNOWN USER] login failed!");
}
if(!acct)
SendLoginDenied();
else {
needs_world_list = true;
SetLoginAccount(acct);
SendLoginAccepted();
}
}
else {
cout << "Error bad version: " << version << endl;
SendLoginDeniedBadVersion();
}
}
else {
cout << "Error loading LS_LoginRequest packet: \n";
}
safe_delete(packet);
}
// Handles world server list request and sends character list
void HandleWorldServerListRequest()
{
SendWorldList();
needs_world_list = false;
if(!sent_character_list) {
database.LoadCharacters(GetLoginAccount(), GetVersion());
sent_character_list = true;
}
SendCharList();
}
// Handles character creation requests from clients
void HandleCreateCharacterRequest(EQApplicationPacket* app)
{
PacketStruct* packet = configReader.getStruct("CreateCharacter", GetVersion());
DumpPacket(app);
playWaitTimer = new Timer(15000);
playWaitTimer->Start();
LogWrite(WORLD__INFO, 1, "World", "Character creation request from account %s", GetAccountName());
if(packet->LoadPacketData(app->pBuffer,app->size, GetVersion() <= 561 ? false : true)) {
DumpPacket(app->pBuffer, app->size);
packet->setDataByName("account_id",GetAccountID());
LWorld* world_server = world_list.FindByID(packet->getType_int32_ByName("server_id"));
if(!world_server) {
DumpPacket(app->pBuffer, app->size);
cout << GetAccountName() << " attempted creation of character with an invalid server id of: " << packet->getType_int32_ByName("server_id") << "\n";
}
else {
createRequest = packet;
ServerPacket* outpack = new ServerPacket(ServerOP_CharacterCreate, app->size+sizeof(int16));
int16 out_version = GetVersion();
memcpy(outpack->pBuffer, &out_version, sizeof(int16));
memcpy(outpack->pBuffer + sizeof(int16), app->pBuffer, app->size);
uchar* tmp = outpack->pBuffer;
if(out_version<=283)
tmp+=2;
else if(out_version == 373) {
tmp += 6;
}
else
tmp += 7;
int32 account_id = GetAccountID();
memcpy(tmp, &account_id, sizeof(int32));
world_server->SendPacket(outpack);
safe_delete(outpack);
}
}
else {
LogWrite(WORLD__ERROR, 1, "World", "Error in character creation request from account %s!", GetAccountName());
safe_delete(packet);
}
}
// Handles character play requests and forwards to world server
void HandlePlayCharacterRequest(EQApplicationPacket* app)
{
int32 char_id = 0;
int32 server_id = 0;
PacketStruct* request = configReader.getStruct("LS_PlayRequest",GetVersion());
if(request && request->LoadPacketData(app->pBuffer,app->size)) {
char_id = request->getType_int32_ByName("char_id");
if(GetVersion() <= 283) {
server_id = database.GetServer(GetAccountID(), char_id, request->getType_EQ2_16BitString_ByName("name").data);
}
else {
server_id = request->getType_int32_ByName("server_id");
}
LWorld* world = world_list.FindByID(server_id);
string name = database.GetCharacterName(char_id,server_id,GetAccountID());
if(world && name.length() > 0) {
pending_play_char_id = char_id;
ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct));
UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer;
req->char_id = char_id;
req->lsaccountid = GetAccountID();
req->worldid = server_id;
struct in_addr in;
in.s_addr = GetIP();
strcpy(req->ip_address, inet_ntoa(in));
world->SendPacket(outpack);
delete outpack;
safe_delete(playWaitTimer);
playWaitTimer = new Timer(5000);
playWaitTimer->Start();
}
else {
cout << GetAccountName() << " sent invalid Play Request: \n";
SendPlayFailed(PLAY_ERROR_PROBLEM);
DumpPacket(app);
}
}
safe_delete(request);
}
// Handles character deletion requests
void HandleDeleteCharacterRequest(EQApplicationPacket* app)
{
PacketStruct* request = configReader.getStruct("LS_DeleteCharacterRequest", GetVersion());
PacketStruct* response = configReader.getStruct("LS_DeleteCharacterResponse", GetVersion());
if(request && response && request->LoadPacketData(app->pBuffer,app->size)) {
EQ2_16BitString name = request->getType_EQ2_16BitString_ByName("name");
int32 acct_id = GetAccountID();
int32 char_id = request->getType_int32_ByName("char_id");
int32 server_id = request->getType_int32_ByName("server_id");
if(database.VerifyDelete(acct_id, char_id, name.data.c_str())) {
response->setDataByName("response", 1);
GetLoginAccount()->removeCharacter((char*)name.data.c_str(), GetVersion());
LWorld* world_server = world_list.FindByID(server_id);
if(world_server != NULL)
world_server->SendDeleteCharacter(char_id, acct_id);
}
else
response->setDataByName("response", 0);
response->setDataByName("server_id", server_id);
response->setDataByName("char_id", char_id);
response->setDataByName("account_id", account_id);
response->setMediumStringByName("name", (char*)name.data.c_str());
response->setDataByName("max_characters", 10);
EQ2Packet* outapp = response->serialize();
QueuePacket(outapp);
this->SendCharList();
}
safe_delete(request);
safe_delete(response);
}
// Handles unknown opcodes and logs them for debugging
void HandleUnknownOpcode(EQApplicationPacket* app)
{
const char* name = app->GetOpcodeName();
if(name)
LogWrite(OPCODE__DEBUG, 1, "Opcode", "%s Received %04X (%i)", name, app->GetRawOpcode(), app->GetRawOpcode());
else
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Received %04X (%i)", app->GetRawOpcode(), app->GetRawOpcode());
}
// Saves client error logs to database after decompression
void SaveErrorsToDB(EQApplicationPacket* app, char* type, int32 version)
{
int32 size = 0;
z_stream zstream;
if(version >= 546) {
memcpy(&size, app->pBuffer + sizeof(int32), sizeof(int32));
zstream.next_in = app->pBuffer + 8;
zstream.avail_in = app->size - 8;
}
else { // box set
size = 0xFFFF;
zstream.next_in = app->pBuffer + 2;
zstream.avail_in = app->size - 2;
}
size++;
char* message = new char[size];
memset(message, 0, size);
int zerror = 0;
zstream.next_out = (BYTE*)message;
zstream.avail_out = size;
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
zerror = inflateInit(&zstream);
if(zerror != Z_OK) {
safe_delete_array(message);
return;
}
zerror = inflate(&zstream, 0);
if(message && strlen(message) > 0)
database.SaveClientLog(type, message, GetLoginAccount()->getLoginName(), GetVersion());
safe_delete_array(message);
}
// Handles character creation approval from world server
void CharacterApproved(int32 server_id,int32 char_id)
{
if(createRequest && server_id == createRequest->getType_int32_ByName("server_id")) {
LWorld* world_server = world_list.FindByID(server_id);
if(!world_server)
return;
PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion());
if(packet) {
packet->setDataByName("account_id", GetAccountID());
packet->setDataByName("unknown", 0xFFFFFFFF);
packet->setDataByName("response", CREATESUCCESS_REPLY);
packet->setMediumStringByName("name", (char*)createRequest->getType_EQ2_16BitString_ByName("name").data.c_str());
EQ2Packet* outapp = packet->serialize();
QueuePacket(outapp);
safe_delete(packet);
database.SaveCharacter(createRequest, GetLoginAccount(),char_id, GetVersion());
// refresh characters for this account
database.LoadCharacters(GetLoginAccount(), GetVersion());
SendCharList();
if(GetVersion() <= 561) {
pending_play_char_id = char_id;
ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct));
UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer;
req->char_id = char_id;
req->lsaccountid = GetAccountID();
req->worldid = server_id;
struct in_addr in;
in.s_addr = GetIP();
strcpy(req->ip_address, inet_ntoa(in));
world_server->SendPacket(outpack);
delete outpack;
}
}
}
else {
cout << GetAccountName() << " received invalid CharacterApproval from server: " << server_id << endl;
}
safe_delete(createRequest);
}
// Handles character creation rejection from world server
void CharacterRejected(int8 reason_number)
{
PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion());
if(createRequest && packet) {
packet->setDataByName("account_id", GetAccountID());
int8 clientReasonNum = reason_number;
packet->setDataByName("response", clientReasonNum);
packet->setMediumStringByName("name", "");
EQ2Packet* outapp = packet->serialize();
QueuePacket(outapp);
safe_delete(packet);
}
}
// Sends character list to client
void SendCharList()
{
LogWrite(LOGIN__INFO, 0, "Login", "[%s] sending character list.", GetAccountName());
LS_CharSelectList list;
list.loadData(GetAccountID(), GetLoginAccount()->charlist, GetVersion());
EQ2Packet* outapp = list.serialize(GetVersion());
DumpPacket(outapp->pBuffer, outapp->size);
QueuePacket(outapp);
}
// Sends login denied response with bad version error
void SendLoginDeniedBadVersion()
{
EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse));
LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer;
ls_response->reply_code = 6;
ls_response->unknown03 = 0xFFFFFFFF;
ls_response->unknown04 = 0xFFFFFFFF;
QueuePacket(app);
StartDisconnectTimer();
}
// Sends login denied response for invalid credentials
void SendLoginDenied()
{
EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse));
LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer;
ls_response->reply_code = 1;
ls_response->unknown03 = 0xFFFFFFFF;
ls_response->unknown04 = 0xFFFFFFFF;
QueuePacket(app);
StartDisconnectTimer();
}
// Sends login accepted response with account details
void SendLoginAccepted(int32 account_id = 1, int8 login_response = 0)
{
PacketStruct* packet = configReader.getStruct("LS_LoginReplyMsg", GetVersion());
if(packet) {
packet->setDataByName("account_id", account_id);
packet->setDataByName("login_response", login_response);
packet->setDataByName("do_not_force_soga", 1);
packet->setDataByName("sub_level", net.GetDefaultSubscriptionLevel());
packet->setDataByName("race_flag", 0x1FFFFF);
packet->setDataByName("class_flag", 0x7FFFFFE);
packet->setMediumStringByName("username", GetAccountName());
packet->setMediumStringByName("password", GetAccountName());
packet->setDataByName("unknown5", net.GetExpansionFlag());
packet->setDataByName("unknown6", 0xFF);
packet->setDataByName("unknown6", 0xFF, 1);
packet->setDataByName("unknown6", 0xFF, 2);
packet->setDataByName("unknown10", 0xFF);
packet->setDataByName("unknown7", net.GetEnabledRaces());
packet->setDataByName("unknown7a", 0xEE);
packet->setDataByName("unknown8", net.GetCitiesFlag(), 1);
EQ2Packet* outapp = packet->serialize();
QueuePacket(outapp);
safe_delete(packet);
}
}
// Sends world server list to client
void SendWorldList()
{
EQ2Packet* pack = world_list.MakeServerListPacket(lsadmin, version);
EQ2Packet* dupe = pack->Copy();
DumpPacket(dupe->pBuffer,dupe->size);
QueuePacket(dupe);
SendLoginAccepted(0, 10); // triggers a different code path in the client to set certain flags
}
// Queues packet for transmission to client
void QueuePacket(EQ2Packet* app)
{
eqnc->EQ2QueuePacket(app);
}
// Handles world server response for character play requests
void WorldResponse(int32 worldid, int8 response, char* ip_address, int32 port, int32 access_key)
{
LWorld* world = world_list.FindByID(worldid);
if(world == 0) {
FatalError(0);
return;
}
if(response != 1) {
if(response == PLAY_ERROR_CHAR_NOT_LOADED) {
string pending_play_char_name = database.GetCharacterName(pending_play_char_id, worldid, GetAccountID());
if(database.VerifyDelete(GetAccountID(), pending_play_char_id, pending_play_char_name.c_str())) {
GetLoginAccount()->removeCharacter((char*)pending_play_char_name.c_str(), GetVersion());
}
}
FatalError(response);
return;
}
PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion());
if(response_packet) {
safe_delete(playWaitTimer);
response_packet->setDataByName("response", 1);
response_packet->setSmallStringByName("server", ip_address);
response_packet->setDataByName("port", port);
response_packet->setDataByName("account_id", GetAccountID());
response_packet->setDataByName("access_code", access_key);
EQ2Packet* outapp = response_packet->serialize();
QueuePacket(outapp);
safe_delete(response_packet);
}
}
// Handles fatal errors and sends play failed response
void FatalError(int8 response)
{
safe_delete(playWaitTimer);
SendPlayFailed(response);
}
// Sends play failed response to client
void SendPlayFailed(int8 response)
{
PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion());
if(response_packet) {
response_packet->setDataByName("response", response);
response_packet->setSmallStringByName("server", "");
response_packet->setDataByName("port", 0);
response_packet->setDataByName("account_id", GetAccountID());
response_packet->setDataByName("access_code", 0);
EQ2Packet* outapp = response_packet->serialize();
QueuePacket(outapp);
safe_delete(response_packet);
}
}
// Starts timer for delayed client disconnection
void StartDisconnectTimer()
{
if(!disconnectTimer) {
disconnectTimer = new Timer(1000);
disconnectTimer->Start();
}
}
// Accessor methods
EQStream* getConnection() { return eqnc; }
LoginAccount* GetLoginAccount() { return login_account; }
void SetLoginAccount(LoginAccount* in_account) {
login_account = in_account;
if(in_account)
account_id = in_account->getLoginAccountID();
}
int16 GetVersion() { return version; }
char* GetKey() { return key; }
void SetKey(char* in_key) { strcpy(key,in_key); }
int32 GetIP() { return ip; }
int16 GetPort() { return port; }
int32 GetAccountID() { return account_id; }
const char* GetAccountName() { return (char*)account_name.c_str(); }
void SetAccountName(const char* name) { account_name = string(name); }
bool AwaitingCharCreationRequest() {
if(createRequest)
return true;
else
return false;
}
// Public member variables
int8 LoginKey[10]; // Login encryption key
int8 ClientSession[25]; // Client session identifier
Timer* updatetimer; // Timer for client updates
Timer* updatelisttimer; // Timer for update list processing
Timer* disconnectTimer; // Timer for delayed disconnection
int16 packettotal; // Total packet count
int32 requested_server_id; // Server ID requested by client
int32 request_num; // Request number counter
LinkedList<DelayQue*> delay_que; // Queue for delayed packet processing
private:
string pending_play_char_name; // Character name for pending play request
int32 pending_play_char_id; // Character ID for pending play request
int8 update_position; // Position in update packet list
int16 num_updates; // Number of updates sent
vector<PacketStruct*>* update_packets; // List of update packets
LoginAccount* login_account; // Associated login account
EQStream* eqnc; // Network connection stream
int32 ip; // Client IP address
int16 port; // Client port number
int32 account_id; // Account ID
string account_name; // Account name
char key[10]; // Client authentication key
int8 lsadmin; // Login server admin level
sint16 worldadmin; // World server admin level
int lsstatus; // Login server status
bool kicked; // Whether client has been kicked
bool verified; // Whether client is verified
bool start; // Whether client has started
bool needs_world_list; // Whether client needs world list
int16 version; // Client version
char bannedreason[30]; // Reason for ban if applicable
bool sent_character_list; // Whether character list has been sent
eLoginMode LoginMode; // Current login mode
PacketStruct* createRequest; // Pending character creation request
Timer* playWaitTimer; // Timer for play request timeout
};
class ClientList
{
public:
// Constructor and destructor for client list management
ClientList() {}
~ClientList() {}
// Adds a new client to the list
void Add(Client* client)
{
MClientList.writelock();
client_list[client] = true;
MClientList.releasewritelock();
}
// Finds client by IP address and port
Client* Get(int32 ip, int16 port)
{
Client* ret = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++) {
if(itr->first->GetIP() == ip && itr->first->GetPort() == port) {
ret = itr->first;
break;
}
}
MClientList.releasereadlock();
return ret;
}
// Finds clients waiting for character creation and handles rejections
void FindByCreateRequest()
{
Client* client = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++) {
if(itr->first->AwaitingCharCreationRequest()) {
if(!client)
client = itr->first;
else {
client = 0; // more than 1 character waiting, dont want to send rejection to wrong one
break;
}
}
}
MClientList.releasereadlock();
if(client)
client->CharacterRejected(UNKNOWNERROR_REPLY);
}
// Finds client by login server account ID
Client* FindByLSID(int32 lsaccountid)
{
Client* client = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++) {
if(itr->first->GetAccountID() == lsaccountid) {
client = itr->first;
break;
}
}
MClientList.releasereadlock();
return client;
}
// Sends packet to all connected clients
void SendPacketToAllClients(EQ2Packet* app)
{
Client* client = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
if(client_list.size() > 0) {
for(itr = client_list.begin(); itr != client_list.end(); itr++) {
itr->first->QueuePacket(app->Copy());
}
}
safe_delete(app);
MClientList.releasereadlock();
}
// Processes all clients and removes inactive ones
void Process()
{
Client* client = 0;
vector<Client*> erase_list;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++) {
client = itr->first;
if(!client->Process())
erase_list.push_back(client);
}
MClientList.releasereadlock();
if(erase_list.size() > 0) {
vector<Client*>::iterator erase_itr;
MClientList.writelock();
for(erase_itr = erase_list.begin(); erase_itr != erase_list.end(); erase_itr++) {
client = *erase_itr;
struct in_addr in;
in.s_addr = client->getConnection()->GetRemoteIP();
net.numclients--;
LogWrite(LOGIN__INFO, 0, "Login", "Removing client from ip: %s on port %i, Account Name: %s", inet_ntoa(in), ntohs(client->getConnection()->GetRemotePort()), client->GetAccountName());
client->getConnection()->Close();
net.UpdateWindowTitle();
client_list.erase(client);
}
MClientList.releasewritelock();
}
}
private:
Mutex MClientList; // Mutex for thread-safe client list access
map<Client*, bool> client_list; // Map of active clients
};

View File

@ -0,0 +1,164 @@
// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPL License
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
#include <cstring>
#include "packet_headers.hpp"
#include "../common/linked_list.hpp"
#include "../common/packet/packet_struct.hpp"
using namespace std;
/**
* LoginAccount class manages user account data and associated character profiles
* Handles authentication, character management, and account operations
*/
class LoginAccount
{
public:
// Default constructor - initializes account with default values
LoginAccount();
// Parameterized constructor - creates account with specified ID, name, and password
LoginAccount(int32 id, const char* in_name, const char* in_pass);
// Destructor - cleans up all character profiles and allocated memory
~LoginAccount();
// Saves account data to persistent storage
bool SaveAccount(LoginAccount* acct);
// Vector containing all character profiles associated with this account
vector<CharSelectProfile*> charlist;
// Sets the account name from C-string input
void setName(const char* in_name) { strcpy(name, in_name); }
// Sets the account password from C-string input
void setPassword(const char* in_pass) { strcpy(password, in_pass); }
// Sets the authentication status for this account
void setAuthenticated(bool in_auth) { authenticated = in_auth; }
// Sets the unique account identifier
void setAccountID(int32 id) { account_id = id; }
// Adds a character profile to the account's character list
void addCharacter(CharSelectProfile* profile)
{
charlist.push_back(profile);
}
// Removes character by PacketStruct profile reference
void removeCharacter(PacketStruct* profile);
// Removes character by name with version-specific handling
void removeCharacter(char* name, int16 version);
// Serializes character data into provided buffer for network transmission
void serializeCharacter(uchar* buffer, CharSelectProfile* profile);
// Removes and deallocates all character profiles from the account
void flushCharacters();
// Retrieves character profile by name, returns nullptr if not found
CharSelectProfile* getCharacter(char* name);
// Returns the unique account identifier
int32 getLoginAccountID() { return account_id; }
// Returns pointer to account name string
char* getLoginName() { return name; }
// Returns pointer to account password string
char* getLoginPassword() { return password; }
// Returns current authentication status
bool getLoginAuthenticated() { return authenticated; }
private:
int32 account_id; // Unique identifier for this account
char name[32]; // Account name (limited to 31 characters + null terminator)
char password[32]; // Account password (limited to 31 characters + null terminator)
bool authenticated; // Current authentication state
};
// Default constructor implementation - initializes account with default values
inline LoginAccount::LoginAccount()
{
account_id = 0;
memset(name, 0, sizeof(name));
memset(password, 0, sizeof(password));
authenticated = false;
}
// Parameterized constructor implementation - sets up account with provided credentials
inline LoginAccount::LoginAccount(int32 id, const char* in_name, const char* in_pass)
{
account_id = id;
strcpy(name, in_name);
strcpy(password, in_pass);
authenticated = false;
}
// Destructor implementation - properly cleans up all character profiles
inline LoginAccount::~LoginAccount()
{
for(auto iter = charlist.begin(); iter != charlist.end(); iter++) {
delete(*iter); // Clean up character profile memory
}
}
// Removes and deallocates all character profiles from memory
inline void LoginAccount::flushCharacters()
{
for(auto iter = charlist.begin(); iter != charlist.end(); iter++) {
delete(*iter); // Clean up each character profile
}
charlist.clear(); // Clear the vector
}
// Searches for and returns character profile by name
inline CharSelectProfile* LoginAccount::getCharacter(char* name)
{
CharSelectProfile* profile = nullptr;
EQ2_16BitString temp;
// Iterate through character list to find matching name
for(auto char_iterator = charlist.begin(); char_iterator != charlist.end(); char_iterator++) {
profile = *char_iterator;
temp = profile->packet->getType_EQ2_16BitString_ByName("name");
if(strcmp(temp.data.c_str(), name) == 0)
return profile; // Return matching profile
}
return nullptr; // Character not found
}
// Removes character from account by name with client version handling
inline void LoginAccount::removeCharacter(char* name, int16 version)
{
CharSelectProfile* profile = nullptr;
EQ2_16BitString temp;
// Search for character with matching name
for(auto iter = charlist.begin(); iter != charlist.end(); iter++) {
profile = *iter;
temp = profile->packet->getType_EQ2_16BitString_ByName("name");
if(strcmp(temp.data.c_str(), name) == 0) {
if(version <= 561) {
profile->deleted = true; // Workaround for character select crash on legacy clients
}
else {
delete(*iter); // Clean up memory
charlist.erase(iter); // Remove from vector
}
return;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
// Copyright (C) 2007 EQ2EMulator Development Team, GPL License
#pragma once
// Core login and session management opcodes
#define OP_Login2 0x0200 // Primary login authentication
#define OP_GetLoginInfo 0x0300 // Request login information from client
#define OP_LoginInfo 0x0100 // Response with login details
#define OP_SessionId 0x0900 // Session identifier assignment
#define OP_SessionKey 0x4700 // Session key exchange
#define OP_Disconnect 0x0500 // Connection termination
#define OP_AllFinish 0x0500 // Process completion acknowledgment
#define OP_Ack5 0x1500 // Generic acknowledgment packet
// Server list and status management
#define OP_SendServersFragment 0x0D00 // Fragment of server list data
#define OP_ServerList 0x4600 // Complete server list
#define OP_RequestServerStatus 0x4800 // Request current server status
#define OP_SendServerStatus 0x4A00 // Response with server status
#define OP_Version 0x5900 // Client/server version verification
// Chat system opcodes
#define OP_Chat_ChannelList 0x0600 // Available chat channels
#define OP_Chat_JoinChannel 0x0700 // Join a chat channel
#define OP_Chat_PartChannel 0x0800 // Leave a chat channel
#define OP_Chat_ChannelMessage 0x0930 // Message to channel
#define OP_Chat_Tell 0x0a00 // Private message
#define OP_Chat_SysMsg 0x0b00 // System message
#define OP_Chat_CreateChannel 0x0c00 // Create new chat channel
#define OP_Chat_ChangeChannel 0x0d00 // Modify channel settings
#define OP_Chat_DeleteChannel 0x0e00 // Remove chat channel
#define OP_Chat_UserList 0x1000 // Users in channel
#define OP_Chat_ChannelWelcome 0x2400 // Welcome message for channel
#define OP_Chat_PopupMakeWindow 0x3000 // Create popup chat window
#define OP_Chat_GuildsList 0x5500 // Available guilds list
#define OP_Chat_GuildEdit 0x5700 // Guild modification
// Account registration and management
#define OP_RegisterAccount 0x2300 // New account creation
#define OP_Reg_GetPricing 0x1a00 // Get pricing for new account signup
//#define OP_Reg_SendPricing 0x0400 // Legacy pricing send (unused)
#define OP_Reg_SendPricing 0x1b00 // Send pricing information
#define OP_Reg_GetPricing2 0x4400 // Get pricing for re-registration
#define OP_Reg_ChangeAcctLogin 0x5100 // Change account login credentials
#define OP_ChangePassword 0x4500 // Password modification
#define OP_LoginBanner 0x5200 // Login banner display
// Billing and subscription management
#define OP_BillingInfoAccepted 0x3300 // Billing information accepted
#define OP_CheckGameCardValid 0x3400 // Validate game card
#define OP_GameCardTimeLeft 0x3600 // Remaining game card time
#define OP_AccountExpired 0x4200 // Account expiration notification
#define OP_RenewAccountBillingInfo 0x7a00 // Billing renewal information

View File

@ -0,0 +1,68 @@
// Copyright (C) 2007 EQ2EMulator Development Team - GPL License
#pragma once
#include "packet_headers.hpp"
#include "../common/types.hpp"
#pragma pack(1)
// Login request packet structure containing user credentials and access information
struct LS_LoginRequest
{
EQ2_16BitString AccessCode; // Access code for login validation
EQ2_16BitString unknown1; // Unknown field - possibly session data
EQ2_16BitString username; // Player username
EQ2_16BitString password; // Player password
EQ2_16BitString unknown2[4]; // Unknown array - possibly additional auth data
int16 unknown3; // Unknown 16-bit field
int32 unknown4[2]; // Unknown 32-bit array
};
// World server status change notification structure
struct LS_WorldStatusChanged
{
int32 server_id; // Unique identifier for the world server
int8 up; // Server online status (1 = up, 0 = down)
int8 locked; // Server locked status (1 = locked, 0 = unlocked)
int8 hidden; // Server visibility status (1 = hidden, 0 = visible)
};
// Character play request for newer client versions
struct LS_PlayCharacterRequest
{
int32 character_id; // Unique character identifier
int32 server_id; // Target server identifier
int16 unknown1; // Unknown field - possibly additional flags
};
// Character play request for older client versions
struct LS_OLDPlayCharacterRequest
{
int32 character_id; // Unique character identifier
EQ2_16BitString name; // Character name string
};
// Account information structure for early client versions
struct LS_CharListAccountInfoEarlyClient
{
int32 account_id; // Unique account identifier
int32 unknown1; // Unknown field - possibly subscription data
int16 unknown2; // Unknown 16-bit field
int32 maxchars; // Maximum characters allowed on account
int8 unknown4; // Unknown field - padding or flags (15 bytes total)
};
// Complete account information structure for character list
struct LS_CharListAccountInfo
{
int32 account_id; // Unique account identifier
int32 unknown1; // Unknown field - possibly subscription type
int16 unknown2; // Unknown 16-bit field
int32 maxchars; // Maximum characters allowed on account
int8 unknown4; // Unknown field - DoF compatibility marker
int32 unknown5[4]; // Unknown array - extended account data
int8 vet_adv_bonus; // Veteran adventure bonus flag (enables 200% bonus)
int8 vet_trade_bonus; // Veteran tradeskill bonus flag (enables free lvl 90 upgrade)
}; // Total structure size: 33 bytes
#pragma pack()

1776
old/LoginServer/lworld.hpp Normal file

File diff suppressed because it is too large Load Diff

112
old/LoginServer/main.cpp Normal file
View File

@ -0,0 +1,112 @@
// Copyright (C) 2007-2021 EQ2Emulator Development Team, GPL v3 License
#include <iostream>
#include <cstdlib>
#include "net.hpp"
EQStreamFactory eqsf(LoginStream);
std::map<int16,OpcodeManager*> EQOpcodeManager;
NetConnection net;
ClientList client_list;
LWorldList world_list;
LoginDatabase database;
ConfigReader configReader;
std::map<int16, int16> EQOpcodeVersions;
Timer statTimer(60000);
volatile bool RunLoops = true;
void CatchSignal(int sig_num)
{
std::cout << "Got signal " << sig_num << std::endl;
RunLoops = false;
}
int main(int argc, char** argv)
{
if (signal(SIGINT, CatchSignal) == SIG_ERR) {
std::cerr << "Could not set signal handler" << std::endl;
}
LogStart();
LogParseConfigs();
net.WelcomeHeader();
srand(time(NULL));
if (!net.ReadLoginConfig())
return 1;
net.InitWebServer(net.GetWebLoginAddress(), net.GetWebLoginPort(), net.GetWebCertFile(), net.GetWebKeyFile(), net.GetWebKeyPassword(), net.GetWebHardcodeUser(), net.GetWebHardcodePassword());
const char* structList[] = { "CommonStructs.xml", "LoginStructs.xml" };
for (int s = 0; s < sizeof(structList) / sizeof(const char*); s++) {
LogWrite(INIT__INFO, 0, "Init", "Loading Structs File %s..", structList[s]);
if (configReader.processXML_Elements(structList[s]))
LogWrite(INIT__INFO, 0, "Init", "Loading Structs File %s completed..", structList[s]);
else {
LogWrite(INIT__ERROR, 0, "Init", "Loading Structs File %s FAILED!", structList[s]);
return 1;
}
}
LogWrite(INIT__INFO, 0, "Init", "Initialize World List..");
world_list.Init();
if (eqsf.listen_ip_address)
LogWrite(INIT__INFO, 0, "Init", "Login server listening on %s port %i", eqsf.listen_ip_address, net.GetPort());
else
LogWrite(INIT__INFO, 0, "Init", "Login server listening on port %i", net.GetPort());
if (!eqsf.Open(net.GetPort())) {
LogWrite(INIT__ERROR, 0, "Init", "Failed to open port %i.", net.GetPort());
return 1;
}
net.login_running = true;
net.login_uptime = getCurrentTimestamp();
net.UpdateWindowTitle();
EQStream* eqs;
Timer* TimeoutTimer = new Timer(5000);
TimeoutTimer->Start();
while (RunLoops) {
Timer::SetCurrentTime();
while ((eqs = eqsf.Pop())) {
struct in_addr in;
in.s_addr = eqs->GetRemoteIP();
LogWrite(LOGIN__INFO, 0, "Login", "New client from IP: %s on port %i", inet_ntoa(in), ntohs(eqs->GetRemotePort()));
Client* client = new Client(eqs);
eqs->SetClientVersion(0);
client_list.Add(client);
net.numclients++;
net.UpdateWindowTitle();
}
if (TimeoutTimer->Check()) {
eqsf.CheckTimeout();
}
if (statTimer.Check()) {
world_list.UpdateWorldStats();
database.RemoveOldWorldServerStats();
database.FixBugReport();
}
client_list.Process();
world_list.Process();
Sleep(1);
}
eqsf.Close();
world_list.Shutdown();
delete TimeoutTimer;
return 0;
}

307
old/LoginServer/net.hpp Normal file
View File

@ -0,0 +1,307 @@
#pragma once
// Copyright (C) 2007-2021 EQ2Emulator Development Team, GPL v3 License
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <atomic>
#include <iostream>
#include <string>
#include <cstring>
#include <ctime>
#include <csignal>
#include <sstream>
#include <fstream>
#include <map>
#include "lworld.hpp"
#include "client.hpp"
#include "login_database.hpp"
#include "../common/log.hpp"
#include "../common/unix.hpp"
#include "../common/crc16.hpp"
#include "../common/types.hpp"
#include "../common/debug.hpp"
#include "../common/queue.hpp"
#include "../common/timer.hpp"
#include "../common/version.hpp"
#include "../common/separator.hpp"
#include "../common/web_server.hpp"
#include "../common/json_parser.hpp"
#include "../common/data_buffer.hpp"
#include "../common/config_reader.hpp"
#include "../common/misc_functions.hpp"
#include "../common/common_defines.hpp"
#include "../common/packet/packet_struct.hpp"
#include "../common/packet/packet_functions.hpp"
#include "../common/stream/eq_stream_factory.hpp"
void CatchSignal(int sig_num);
enum eServerMode
{
Standalone,
Master,
Slave,
Mesh
};
class NetConnection
{
public:
// Constructor - initializes all network connection parameters and default values
NetConnection()
{
port = 5999;
listening_socket = 0;
memset(masteraddress, 0, sizeof(masteraddress));
uplinkport = 0;
memset(uplinkaccount, 0, sizeof(uplinkaccount));
memset(uplinkpassword, 0, sizeof(uplinkpassword));
LoginMode = Standalone;
Uplink_WrongVersion = false;
numclients = 0;
numservers = 0;
allowAccountCreation = true;
// Full expansion support flag - controls available expansions
expansionFlag = 0x7CFF;
// Cities availability flag - controls which starting cities are available
citiesFlag = 0xFF;
// Default subscription level - controls character creation restrictions
defaultSubscriptionLevel = 0xFFFFFFFF;
// Enabled races flag - controls which races are selectable
enabledRaces = 0xFFFF;
web_loginport = 0;
login_webserver = nullptr;
login_running = false;
login_uptime = getCurrentTimestamp();
}
// Destructor - cleans up web server resources
~NetConnection()
{
safe_delete(login_webserver);
}
// Updates console window title with server status information
void UpdateWindowTitle(char* iNewTitle = 0)
{
// Linux systems don't support console title updates like Windows
// This is a no-op on Linux systems
}
// Reads and parses the login server configuration from JSON file
bool ReadLoginConfig()
{
JsonParser parser(MAIN_CONFIG_FILE);
if (!parser.IsLoaded()) {
LogWrite(INIT__ERROR, 0, "Init", "Failed to find %s in server directory..", MAIN_CONFIG_FILE);
return false;
}
std::string serverport = parser.getValue("loginconfig.serverport");
std::string serverip = parser.getValue("loginconfig.serverip");
if (!parser.convertStringToUnsignedShort(serverport, port)) {
LogWrite(INIT__ERROR, 0, "Init", "Failed to translate loginconfig.serverport..");
return false;
}
if (serverip.size() > 0) {
eqsf.listen_ip_address = new char[serverip.size() + 1];
strcpy(eqsf.listen_ip_address, serverip.c_str());
}
else {
safe_delete(eqsf.listen_ip_address);
eqsf.listen_ip_address = nullptr;
}
std::string acctcreate_str = parser.getValue("loginconfig.accountcreation");
int16 allow_acct = 0;
parser.convertStringToUnsignedShort(acctcreate_str, allow_acct);
allowAccountCreation = allow_acct > 0 ? true : false;
std::string expflag_str = parser.getValue("loginconfig.expansionflag");
parser.convertStringToUnsignedInt(expflag_str, expansionFlag);
std::string citiesflag_str = parser.getValue("loginconfig.citiesflag");
parser.convertStringToUnsignedChar(citiesflag_str, citiesFlag);
std::string defaultsublevel_str = parser.getValue("loginconfig.defaultsubscriptionlevel");
parser.convertStringToUnsignedInt(defaultsublevel_str, defaultSubscriptionLevel);
std::string enableraces_str = parser.getValue("loginconfig.enabledraces");
parser.convertStringToUnsignedInt(enableraces_str, enabledRaces);
web_loginaddress = parser.getValue("loginconfig.webloginaddress");
web_certfile = parser.getValue("loginconfig.webcertfile");
web_keyfile = parser.getValue("loginconfig.webkeyfile");
web_keypassword = parser.getValue("loginconfig.webkeypassword");
web_hardcodeuser = parser.getValue("loginconfig.webhardcodeuser");
web_hardcodepassword = parser.getValue("loginconfig.webhardcodepassword");
std::string webloginport_str = parser.getValue("loginconfig.webloginport");
parser.convertStringToUnsignedShort(webloginport_str, web_loginport);
LogWrite(INIT__INFO, 0, "Init", "%s loaded..", MAIN_CONFIG_FILE);
LogWrite(INIT__INFO, 0, "Init", "Database init begin..");
if (!database.Init()) {
LogWrite(INIT__ERROR, 0, "Init", "Database init FAILED!");
LogStop();
return false;
}
LogWrite(INIT__INFO, 0, "Init", "Loading opcodes 2.0..");
EQOpcodeVersions = database.GetVersions();
std::map<int16,int16>::iterator version_itr2;
int16 version1 = 0;
for (version_itr2 = EQOpcodeVersions.begin(); version_itr2 != EQOpcodeVersions.end(); version_itr2++) {
version1 = version_itr2->first;
EQOpcodeManager[version1] = new RegularOpcodeManager();
std::map<std::string, uint16> eq = database.GetOpcodes(version1);
if (!EQOpcodeManager[version1]->LoadOpcodes(&eq)) {
LogWrite(INIT__ERROR, 0, "Init", "Loading opcodes failed. Make sure you have sourced the opcodes.sql file!");
return false;
}
}
return true;
}
// Displays the EQ2Emulator welcome header with ASCII art and information
void WelcomeHeader()
{
printf("Module: %s, Version: %s", EQ2EMU_MODULE, CURRENT_VERSION);
printf("\n\nCopyright (C) 2007-2021 EQ2Emulator. https://www.eq2emu.com \n\n");
printf("EQ2Emulator is free software: you can redistribute it and/or modify\n");
printf("it under the terms of the GNU General Public License as published by\n");
printf("the Free Software Foundation, either version 3 of the License, or\n");
printf("(at your option) any later version.\n\n");
printf("EQ2Emulator is distributed in the hope that it will be useful,\n");
printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
printf("GNU General Public License for more details.\n\n");
printf(" /$$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$$ \n");
printf("| $$_____/ /$$__ $$ /$$__ $$| $$_____/ \n");
printf("| $$ | $$ \\ $$|__/ \\ $$| $$ /$$$$$$/$$$$ /$$ /$$\n");
printf("| $$$$$ | $$ | $$ /$$$$$$/| $$$$$ | $$_ $$_ $$| $$ | $$\n");
printf("| $$__/ | $$ | $$ /$$____/ | $$__/ | $$ \\ $$ \\ $$| $$ | $$\n");
printf("| $$ | $$/$$ $$| $$ | $$ | $$ | $$ | $$| $$ | $$\n");
printf("| $$$$$$$$| $$$$$$/| $$$$$$$$| $$$$$$$$| $$ | $$ | $$| $$$$$$/\n");
printf("|________/ \\____ $$$|________/|________/|__/ |__/ |__/ \\______/ \n");
printf(" \\__/ \n\n");
printf(" Website : https://eq2emu.com \n");
printf(" Wiki : https://wiki.eq2emu.com \n");
printf(" Git : https://git.eq2emu.com \n");
printf(" Discord : https://discord.gg/5Cavm9NYQf \n\n");
printf("For more detailed logging, modify 'Level' param the log_config.xml file.\n\n");
fflush(stdout);
}
// Initializes and starts the web server for login status and world information
void InitWebServer(std::string web_ipaddr, int16 web_port, std::string cert_file, std::string key_file, std::string key_password, std::string hardcode_user, std::string hardcode_password)
{
if (web_ipaddr.size() > 0 && web_port > 0) {
try {
login_webserver = new WebServer(web_ipaddr, web_port, cert_file, key_file, key_password, hardcode_user, hardcode_password);
login_webserver->register_route("/status", NetConnection::Web_loginhandle_status);
login_webserver->register_route("/worlds", NetConnection::Web_loginhandle_worlds);
login_webserver->run();
LogWrite(INIT__INFO, 0, "Init", "Login Web Server is listening on %s:%u..", web_ipaddr.c_str(), web_port);
}
catch (const std::exception& e) {
LogWrite(INIT__ERROR, 0, "Init", "Login Web Server failed to listen on %s:%u due to reason %s", web_ipaddr.c_str(), web_port, e.what());
}
}
}
// Accessor methods for configuration values
int16 GetPort() { return port; }
void SetPort(int16 in_port) { port = in_port; }
eServerMode GetLoginMode() { return LoginMode; }
char* GetMasterAddress() { return masteraddress; }
int16 GetUplinkPort() { if (uplinkport != 0) return uplinkport; else return port; }
char* GetUplinkAccount() { return uplinkaccount; }
char* GetUplinkPassword() { return uplinkpassword; }
bool IsAllowingAccountCreation() { return allowAccountCreation; }
int32 GetExpansionFlag() { return expansionFlag; }
int8 GetCitiesFlag() { return citiesFlag; }
int32 GetDefaultSubscriptionLevel() { return defaultSubscriptionLevel; }
int32 GetEnabledRaces() { return enabledRaces; }
std::string GetWebLoginAddress() { return web_loginaddress; }
int16 GetWebLoginPort() { return web_loginport; }
std::string GetWebCertFile() { return web_certfile; }
std::string GetWebKeyFile() { return web_keyfile; }
std::string GetWebKeyPassword() { return web_keypassword; }
std::string GetWebHardcodeUser() { return web_hardcodeuser; }
std::string GetWebHardcodePassword() { return web_hardcodepassword; }
// Web server route handlers for status and world information
static void Web_loginhandle_status(const http::request<http::string_body>& req, http::response<http::string_body>& res);
static void Web_loginhandle_worlds(const http::request<http::string_body>& req, http::response<http::string_body>& res);
// Public server state variables
char address[1024];
int32 numclients;
int32 numservers;
bool login_running;
std::atomic<int64> login_uptime;
protected:
friend class LWorld;
bool Uplink_WrongVersion;
private:
// Network connection parameters
int16 port;
int listening_socket;
char masteraddress[300];
int16 uplinkport;
char uplinkaccount[300];
char uplinkpassword[300];
eServerMode LoginMode;
// Login server configuration flags
bool allowAccountCreation;
int32 expansionFlag;
int8 citiesFlag;
int32 defaultSubscriptionLevel;
int32 enabledRaces;
// Web server configuration
std::string web_loginaddress;
std::string web_certfile;
std::string web_keyfile;
std::string web_keypassword;
std::string web_hardcodeuser;
std::string web_hardcodepassword;
int16 web_loginport;
WebServer* login_webserver;
};
// Global variables - external declarations
extern EQStreamFactory eqsf;
extern std::map<int16,OpcodeManager*> EQOpcodeManager;
extern NetConnection net;
extern ClientList client_list;
extern LWorldList world_list;
extern LoginDatabase database;
extern ConfigReader configReader;
extern std::map<int16, int16> EQOpcodeVersions;
extern Timer statTimer;
extern volatile bool RunLoops;

View File

@ -0,0 +1,160 @@
// Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPLv3
#pragma once
#include <vector>
#include <string>
#include "lworld.hpp"
#include "login_structs.hpp"
#include "login_database.hpp"
#include "../common/types.hpp"
#include "../common/data_buffer.hpp"
#include "../common/config_reader.hpp"
#include "../common/misc_functions.hpp"
#include "../common/global_headers.hpp"
#include "../common/eq_common_structs.hpp"
#include "../common/packet/eq_packet.hpp"
extern ConfigReader configReader;
extern LWorldList world_list;
extern LoginDatabase database;
using std::vector;
using std::string;
// Character profile data for character selection screen
class CharSelectProfile : public DataBuffer
{
public:
// Constructor - initializes character profile with version-specific packet structure
CharSelectProfile(int16 version)
{
deleted = false;
packet = configReader.getStruct("CharSelectProfile", version);
// Initialize all 24 equipment slots to default values
for (int8 i = 0; i < 24; i++) {
packet->setEquipmentByName("equip", 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, i);
}
}
// Destructor - safely cleans up packet structure
~CharSelectProfile()
{
safe_delete(packet);
}
// Serializes character profile data for transmission
void SaveData(int16 in_version)
{
Clear();
AddData(*packet->serializeString());
}
PacketStruct* packet; // Packet structure containing character data
int16 size; // Size of serialized data
bool deleted; // Flag indicating if character is marked for deletion
};
// Character selection list containing multiple character profiles
class LS_CharSelectList : public DataBuffer
{
public:
// Serializes character list data with account information for specified client version
EQ2Packet* serialize(int16 version)
{
Clear();
AddData(num_characters);
AddData(char_data);
if (version <= 561) {
// Early client version uses simplified account info structure
LS_CharListAccountInfoEarlyClient account_info;
account_info.account_id = account_id;
account_info.unknown1 = 0xFFFFFFFF;
account_info.unknown2 = 0;
account_info.maxchars = 7; // Live has a max of 7 on gold accounts base
account_info.unknown4 = 0;
AddData(account_info);
} else {
// Later client versions use extended account info structure
LS_CharListAccountInfo account_info;
account_info.account_id = account_id;
account_info.unknown1 = 0xFFFFFFFF;
account_info.unknown2 = 0;
account_info.maxchars = database.GetMaxCharsSetting();
account_info.vet_adv_bonus = database.GetAccountBonus(account_id);
account_info.vet_trade_bonus = 0;
account_info.unknown4 = 0;
for (int i = 0; i < 3; i++)
account_info.unknown5[i] = 0xFFFFFFFF;
account_info.unknown5[3] = 0;
AddData(account_info);
}
return new EQ2Packet(OP_AllCharactersDescReplyMsg, getData(), getDataSize());
}
// Appends character data to the character list
void addChar(uchar* data, int16 size)
{
char_data.append(reinterpret_cast<char*>(data), size);
}
// Loads character data from database and populates character list for specified account
void loadData(int32 account, vector<CharSelectProfile*> charlist, int16 version)
{
account_id = account;
num_characters = 0;
char_data = "";
CharSelectProfile* character = nullptr;
for (auto itr = charlist.begin(); itr != charlist.end(); itr++) {
character = *itr;
int32 serverID = character->packet->getType_int32_ByName("server_id");
if (character->deleted) {
// Workaround for old clients <= 561 that crash if you delete a char
// Doesn't refresh the char panel correctly
character->packet->setDataByName("name", "(deleted)");
character->packet->setDataByName("charid", 0xFFFFFFFF);
character->packet->setDataByName("name", 0xFFFFFFFF);
character->packet->setDataByName("server_id", 0xFFFFFFFF);
character->packet->setDataByName("created_date", 0xFFFFFFFF);
character->packet->setDataByName("unknown1", 0xFFFFFFFF);
character->packet->setDataByName("unknown2", 0xFFFFFFFF);
character->packet->setDataByName("flags", 0xFF);
} else if (serverID == 0 || !world_list.FindByID(serverID)) {
continue;
}
num_characters++;
character->SaveData(version);
addChar(character->getData(), character->getDataSize());
}
}
int8 num_characters; // Number of characters in the list
int32 account_id; // Account ID for this character list
string char_data; // Serialized character data buffer
};
// Request packet for character deletion operations
class LS_DeleteCharacterRequest : public DataBuffer
{
public:
// Loads character deletion request data from incoming packet
void loadData(EQApplicationPacket* packet)
{
InitializeLoadData(packet->pBuffer, packet->size);
LoadData(character_number);
LoadData(server_id);
LoadData(spacer);
LoadDataString(name);
}
int32 character_number; // Character slot number to delete
int32 server_id; // Server ID where character exists
int32 spacer; // Padding/alignment bytes
EQ2_16BitString name; // Character name to delete
};

101
old/LoginServer/web.hpp Normal file
View File

@ -0,0 +1,101 @@
// Copyright (C) 2007-2021 EQ2Emulator Development Team, GPL v3
#pragma once
#include <sstream>
#include <string>
#include <map>
#include <arpa/inet.h>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "net.hpp"
#include "lworld.hpp"
extern ClientList client_list;
extern LWorldList world_list;
extern NetConnection net;
/**
* Handles web requests for login server status information.
* Generates JSON response containing login status, uptime, client count, and world count.
* @param req HTTP request object containing the incoming request
* @param res HTTP response object to populate with JSON status data
*/
void NetConnection::Web_loginhandle_status(const http::request<http::string_body>& req, http::response<http::string_body>& res)
{
// Set response content type to JSON
res.set(http::field::content_type, "application/json");
boost::property_tree::ptree pt;
// Build status information tree
pt.put("web_status", "online");
pt.put("login_status", net.login_running ? "online" : "offline");
pt.put("login_uptime", (getCurrentTimestamp() - net.login_uptime));
// Convert uptime to human-readable format
auto [days, hours, minutes, seconds] = convertTimestampDuration((getCurrentTimestamp() - net.login_uptime));
std::string uptime_str("Days: " + std::to_string(days) + ", " + "Hours: " + std::to_string(hours) + ", " + "Minutes: " + std::to_string(minutes) + ", " + "Seconds: " + std::to_string(seconds));
pt.put("login_uptime_string", uptime_str);
pt.put("world_count", world_list.GetCount(ConType::World));
pt.put("client_count", net.numclients);
// Serialize to JSON and set response body
std::ostringstream oss;
boost::property_tree::write_json(oss, pt);
std::string json = oss.str();
res.body() = json;
res.prepare_payload();
}
/**
* Handles web requests for world server list information.
* Delegates to LWorldList::PopulateWorldList to generate the response.
* @param req HTTP request object containing the incoming request
* @param res HTTP response object to be populated with world server data
*/
void NetConnection::Web_loginhandle_worlds(const http::request<http::string_body>& req, http::response<http::string_body>& res)
{
world_list.PopulateWorldList(res);
}
/**
* Populates HTTP response with JSON data containing all active world servers.
* Iterates through world map and builds JSON array of world server information
* including ID, name, status, player count, and IP address.
* @param res HTTP response object to populate with world server JSON data
*/
void LWorldList::PopulateWorldList(http::response<http::string_body>& res)
{
struct in_addr in;
res.set(http::field::content_type, "application/json");
boost::property_tree::ptree maintree;
std::ostringstream oss;
// Iterate through all worlds in the world map
std::map<int32,LWorld*>::iterator map_list;
for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) {
LWorld* world = map_list->second;
in.s_addr = world->GetIP();
// Only include World type servers in the response
if (world->GetType() == World) {
boost::property_tree::ptree pt;
pt.put("id", world->GetID());
pt.put("world_name", world->GetName());
pt.put("status", (world->GetStatus() == 1) ? "online" : "offline");
pt.put("num_players", world->GetPlayerNum());
pt.put("ip_addr", inet_ntoa(in)); // Convert IP to string format
maintree.push_back(std::make_pair("", pt));
}
}
// Build final JSON structure and serialize
boost::property_tree::ptree result;
result.add_child("WorldServers", maintree);
boost::property_tree::write_json(oss, result);
std::string json = oss.str();
res.body() = json;
res.prepare_payload();
}

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;
};

View File

@ -0,0 +1,765 @@
#include "Bot.h"
#include "BotBrain.h"
#include "../Trade.h"
#include "../../common/Log.h"
#include "../Worlddatabase.hpp"
#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);
}

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;
};

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;
}

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.hpp"
#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");
}
}

View File

@ -0,0 +1,467 @@
#include "../Worlddatabase.hpp"
#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,372 @@
/*
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/config_reader.hpp"
#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
extern ConfigReader configReader;
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) {
return CreateChannel(channel_name, NULL);
}
bool Chat::CreateChannel(const char *channel_name, const char *password) {
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)
channel->SetPassword(password);
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, 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(client, 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/WorldServer/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.hpp"
#include "../../common/packet/eq_packet.hpp"
#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 CreateChannel(const char *channel_name, const char *password);
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, 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/config_reader.hpp"
#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(Client *client, 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 (client != NULL){
packet_struct->setDataByName("from", client->GetPlayer()->GetName());
} 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(client != NULL){
packet_struct->setDataByName("language", client->GetPlayer()->GetCurrentLanguage());
}else{
packet_struct->setDataByName("language", 0);
}
packet_struct->setDataByName("message", message);
packet_struct->setDataByName("channel_name", name);
packet_struct->setDataByName("show_bubble", 1);
if(client != NULL){
if (client->GetPlayer()->GetCurrentLanguage() == 0 || to_client->GetPlayer()->HasLanguage(client->GetPlayer()->GetCurrentLanguage())) {
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,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/>.
*/
#ifndef CHAT_CHATCHANNEL_H_
#define CHAT_CHATCHANNEL_H_
#include "../../common/types.hpp"
#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(Client *client, 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

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.hpp"
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.hpp"
#include "../common/config_reader.hpp"
#include "Variables.h"
#include "World.h"
#include "classes.h"
#include "../common/log.hpp"
#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)
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,106 @@
#ifndef COLLECTIONS_H_
#define COLLECTIONS_H_
#include "../../common/types.hpp"
#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.hpp"
#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);
}

2068
old/WorldServer/Combat.cpp Normal file

File diff suppressed because it is too large Load Diff

33
old/WorldServer/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.hpp"
#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,316 @@
/*
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.hpp"
#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)
{
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.hpp"
#include "../../common/Log.h"
#include "../../common/seperator.h"
#include "ConsoleCommands.h"
#include "../World.h"
#include "../Rules/Rules.h"
#include "../Worlddatabase.hpp"
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.HandleGlobalBroadcast(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, "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.HandleGlobalBroadcast(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

4048
old/WorldServer/Entity.cpp Normal file

File diff suppressed because it is too large Load Diff

2169
old/WorldServer/Entity.h Normal file

File diff suppressed because it is too large Load Diff

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;
}

138
old/WorldServer/Factions.h Normal file
View File

@ -0,0 +1,138 @@
/*
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/config_reader.hpp"
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

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/misc_functions.hpp"
#include "../common/log.hpp"
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();
}

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"
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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,453 @@
/*
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 "../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.hpp"
#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.hpp"
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.hpp"
#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.hpp"
#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.hpp"
#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.......
*/
}

File diff suppressed because it is too large Load Diff

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,817 @@
/*
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_ITEMS__
#define __EQ2_ITEMS__
#include <map>
#include <vector>
#include "../../common/types.hpp"
#include "../../common/data_buffer.hpp"
#include "../../common/misc_functions.hpp"
#include "../Commands/Commands.h"
#include "../../common/config_reader.hpp"
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 int32 NextUniqueID();
static void ResetUniqueID(int32 new_id);
static int32 next_unique_id;
};
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

View File

@ -0,0 +1,916 @@
/*
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_ITEMS__
#define __EQ2_ITEMS__
#include <map>
#include <vector>
#include "../../common/types.hpp"
#include "../../common/data_buffer.hpp"
#include "../../common/misc_functions.hpp"
#include "../Commands/Commands.h"
#include "../../common/config_reader.hpp"
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 int32 NextUniqueID();
static void ResetUniqueID(int32 new_id);
static int32 next_unique_id;
};
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

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

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/config_reader.hpp"
#include "../classes.h"
#include "../../common/debug.hpp"
#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;
}

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

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.hpp"
#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;
}

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;
}

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.hpp"
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

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.hpp"
#include "../common/log.hpp"
#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/packet_dump.hpp"
#include "net.h"
#include "zoneserver.h"
#include "WorldDatabase.hpp"
#include "Variables.h"
#include "World.h"
#include "../common/config_reader.hpp"
#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))
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 = 0;//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;
}

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/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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,683 @@
/*
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 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_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);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,365 @@
/*
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 LUA_INTERFACE_H
#define LUA_INTERFACE_H
#include <mutex>
#include <shared_mutex>
#include "Spawn.h"
#include "Spells.h"
#include "Quests.h"
#include "zoneserver.h"
#include "client.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
struct LuaSpell{
Entity* caster;
int32 initial_caster_char_id;
int32 initial_target;
int32 initial_target_char_id;
vector<int32> targets;
vector<int32> removed_targets; // previously cancelled, expired, used, so on
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 MSpellTargets;
Mutex MScriptMutex;
int32 effect_bitmask;
bool restored; // restored spell cross zone
std::atomic<bool> has_proc;
ZoneServer* zone;
int16 initial_caster_level;
};
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 LoadRegionScript(string name);
bool LoadRegionScript(const char* name);
LuaSpell* LoadSpellScript(string name);
LuaSpell* LoadSpellScript(const char* name);
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 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* 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 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 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);
void DeletePendingSpell(LuaSpell* spell);
Mutex* GetSpawnScriptMutex(const char* name);
Mutex* GetItemScriptMutex(const char* name);
Mutex* GetZoneScriptMutex(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);
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> > 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> region_inverse_scripts;
map<string, Mutex*> item_scripts_mutex;
map<string, Mutex*> spawn_scripts_mutex;
map<string, Mutex*> zone_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 MQuests;
Mutex MLUAMain;
Mutex MSpellDelete;
Mutex MCustomSpell;
Mutex MRegionScripts;
Mutex MSpellScripts;
mutable std::shared_mutex MLUAUserData;
};
#endif

View File

@ -0,0 +1,201 @@
/*
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.hpp"
#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/WorldServer/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/WorldServer/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

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

1086
old/WorldServer/NPC.cpp Normal file

File diff suppressed because it is too large Load Diff

217
old/WorldServer/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

920
old/WorldServer/NPC_AI.cpp Normal file
View File

@ -0,0 +1,920 @@
/*
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 "NPC_AI.h"
#include "Combat.h"
#include "zoneserver.h"
#include "Spells.h"
#include "../common/log.hpp"
#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);
}
}
void Brain::ClearHate() {
// 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);
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.");
}
}
}

198
old/WorldServer/NPC_AI.h Normal file
View File

@ -0,0 +1,198 @@
/*
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 __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);
/// <summary>Completely clears the hate list for this npc</summary>
void ClearHate();
/// <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

View File

@ -0,0 +1,99 @@
/*
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 "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());
return new_spawn;
}

49
old/WorldServer/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

7757
old/WorldServer/Player.cpp Normal file

File diff suppressed because it is too large Load Diff

1268
old/WorldServer/Player.h Normal file

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,270 @@
/*
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.hpp"
#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);
void SendGroupQuests(int32 group_id, Client* client);
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/WorldServer/Quests.cpp Normal file

File diff suppressed because it is too large Load Diff

467
old/WorldServer/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.hpp"
#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.hpp"
#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,778 @@
/*
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.hpp"
#include "../../common/Log.h"
#include "../../common/database.hpp"
#include "Recipe.h"
#include "../../common/config_reader.hpp"
#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;
itemss[i]->details.item_locked = true;
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;
}

View File

@ -0,0 +1,271 @@
/*
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.hpp"
#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.hpp"
#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`;
*/

View File

@ -0,0 +1,543 @@
/*
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.hpp"
#include "../../common/Log.h"
#include "../../common/database.hpp"
#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%.
**/
/* 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__);
}

View File

@ -0,0 +1,369 @@
/*
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 RULES_H_
#define RULES_H_
#include <string.h>
#include <map>
#include "../../common/types.hpp"
using namespace std;
enum RuleCategory {
R_Client,
R_Faction,
R_Guild,
R_Player,
R_PVP,
R_Combat,
R_Spawn,
R_UI,
R_World,
R_Zone,
R_Loot,
R_Spells,
R_Expansion,
R_Discord
};
enum RuleType {
/* CLIENT */
ShowWelcomeScreen,
/* FACTION */
AllowFactionBasedCombat,
/* GUILD */
/* PLAYER */
MaxLevel,
MaxLevelOverrideStatus,
MaxPlayers,
MaxPlayersOverrideStatus,
VitalityAmount,
VitalityFrequency,
MaxAA,
MaxClassAA,
MaxSubclassAA,
MaxShadowsAA,
MaxHeroicAA,
MaxTradeskillAA,
MaxPrestigeAA,
MaxTradeskillPrestigeAA,
MaxDragonAA,
MinLastNameLevel,
MaxLastNameLength,
MinLastNameLength,
DisableHouseAlignmentRequirement,
MentorItemDecayRate,
TemporaryItemLogoutTime,
HeirloomItemShareExpiration,
SwimmingSkillMinSpeed,
SwimmingSkillMaxSpeed,
SwimmingSkillMinBreathLength,
SwimmingSkillMaxBreathLength,
AutoSkillUpBaseSkills,
MaxWeightStrengthMultiplier,
BaseWeight,
WeightPercentImpact,
WeightPercentCap,
CoinWeightPerStone,
WeightInflictsSpeed,
LevelMasterySkillMultiplier,
TraitTieringSelection,
ClassicTraitLevelTable,
TraitFocusSelectLevel,
TraitTrainingSelectLevel,
TraitRaceSelectLevel,
TraitCharacterSelectLevel,
StartHPBase,
StartPowerBase,
StartHPLevelMod,
StartPowerLevelMod,
AllowPlayerEquipCombat,
MaxTargetCommandDistance,
MinSkillMultiplierValue,
HarvestSkillUpMultiplier,
MiniDingPercentage,
/* PVP */
AllowPVP,
LevelRange,
InvisPlayerDiscoveryRange,
PVPMitigationModByLevel,
PVPType,
/* COMBAT */
MaxCombatRange,
DeathExperienceDebt,
GroupExperienceDebt,
PVPDeathExperienceDebt,
ExperienceToDebt,
ExperienceDebtRecoveryPercent,
ExperienceDebtRecoveryPeriod,
EnableSpiritShards,
SpiritShardSpawnScript,
ShardDebtRecoveryPercent,
ShardRecoveryByRadius,
ShardLifetime,
EffectiveMitigationCapLevel,
CalculatedMitigationCapLevel,
MitigationLevelEffectivenessMax,
MitigationLevelEffectivenessMin,
MaxMitigationAllowed,
MaxMitigationAllowedPVP,
StrengthNPC,
StrengthOther,
MaxSkillBonusByLevel,
LockedEncounterNoAttack,
MaxChaseDistance,
/* SPAWN */
SpeedMultiplier,
ClassicRegen,
HailMovementPause,
HailDistance,
UseHardCodeWaterModelType,
UseHardCodeFlyingModelType,
//SpeedRatio,
/* UI */
MaxWhoResults,
MaxWhoOverrideStatus,
/* WORLD */
DefaultStartingZoneID,
EnablePOIDiscovery,
GamblingTokenItemID,
GuildAutoJoin,
GuildAutoJoinID,
GuildAutoJoinDefaultRankID,
ServerLocked,
ServerLockedOverrideStatus,
SyncZonesWithLogin,
SyncEquipWithLogin,
UseBannedIPsTable,
LinkDeadTimer,
RemoveDisconnectedClientsTimer,
PlayerCampTimer,
GMCampTimer,
AutoAdminPlayers,
AutoAdminGMs,
AutoAdminStatusValue,
DuskTime,
DawnTime,
ThreadedLoad,
TradeskillSuccessChance,
TradeskillCritSuccessChance,
TradeskillFailChance,
TradeskillCritFailChance,
TradeskillEventChance,
EditorURL,
EditorIncludeID,
EditorOfficialServer,
GroupSpellsTimer,
QuestQueueTimer,
SavePaperdollImage,
SaveHeadshotImage,
SendPaperdollImagesToLogin,
TreasureChestDisabled,
StartingZoneLanguages,
StartingZoneRuleFlag,
EnforceRacialAlignment,
MemoryCacheZoneMaps,
AutoLockEncounter,
DisplayItemTiers,
LoreAndLegendAccept,
/* ZONE */
MinZoneLevelOverrideStatus,
MinZoneAccessOverrideStatus,
XPMultiplier,
TSXPMultiplier,
WeatherEnabled,
WeatherType,
MinWeatherSeverity,
MaxWeatherSeverity,
WeatherChangeFrequency,
WeatherChangePerInterval,
WeatherDynamicMaxOffset,
WeatherChangeChance,
SpawnUpdateTimer,
CheckAttackPlayer,
CheckAttackNPC,
HOTime,
UseMapUnderworldCoords,
MapUnderworldCoordOffset,
SharedZoneMaxPlayers,
/* LOOT */
LootRadius,
AutoDisarmChest, // if enabled disarm only works if you right click and disarm, clicking and opening chest won't attempt auto disarm
ChestTriggerRadiusGroup,
ChestUnlockedTimeDrop,
AllowChestUnlockByDropTime,
ChestUnlockedTimeTrap,
AllowChestUnlockByTrapTime,
/* SPELLS */
NoInterruptBaseChance,
EnableFizzleSpells,
DefaultFizzleChance,
FizzleMaxSkill,
FizzleDefaultSkill,
EnableCrossZoneGroupBuffs,
EnableCrossZoneTargetBuffs,
PlayerSpellSaveStateWaitInterval,
PlayerSpellSaveStateCap,
RequirePreviousTierScribe,
CureSpellID,
CureCurseSpellID,
CureNoxiousSpellID,
CureMagicSpellID,
CureTraumaSpellID,
CureArcaneSpellID,
MinistrationSkillID,
MinistrationPowerReductionMax,
MinistrationPowerReductionSkill,
MasterSkillReduceSpellResist,
UseClassicSpellLevel,
/* ZONE TIMERS */
RegenTimer,
ClientSaveTimer,
ShutdownDelayTimer,
WeatherTimer,
SpawnDeleteTimer,
GlobalExpansionFlag,
GlobalHolidayFlag,
DatabaseVersion,
SkipLootGrayMob,
LootDistributionTime,
DiscordEnabled,
DiscordWebhookURL,
DiscordBotToken,
DiscordChannel,
DiscordListenChan
};
class Rule {
public:
Rule();
Rule(int32 category, int32 type, const char *value, const char *combined);
Rule (Rule *rule_in);
virtual ~Rule();
void SetValue(const char *value) {strncpy(this->value, value, sizeof(this->value));}
int32 GetCategory() {return category;}
int32 GetType() {return type;}
const char * GetValue() {return value;}
const char * GetCombined() {return combined;}
int8 GetInt8() {return (int8)atoul(value);}
int16 GetInt16() {return (int16)atoul(value);}
int32 GetInt32() {return (int32)atoul(value);}
int64 GetInt64() {return (int64)atoi64(value);}
sint8 GetSInt8() {return (sint8)atoi(value);}
sint16 GetSInt16() {return (sint16)atoi(value);}
sint32 GetSInt32() {return (sint32)atoi(value);}
sint64 GetSInt64() {return (sint64)atoi64(value);}
bool GetBool() {return atoul(value) > 0 ? true : false;}
float GetFloat() {return atof(value);}
char GetChar() {return value[0];}
const char * GetString() {return value;}
private:
int32 category;
int32 type;
char value[1024];
char combined[2048];
};
class RuleSet {
public:
RuleSet();
RuleSet(RuleSet *in_rule_set);
virtual ~RuleSet();
void CopyRulesInto(RuleSet *in_rule_set);
void SetID(int32 id) {this->id = id;}
void SetName(const char *name) {strncpy(this->name, name, sizeof(this->name));}
int32 GetID() {return id;}
const char *GetName() {return name;}
void AddRule(Rule *rule);
Rule * GetRule(int32 category, int32 type);
Rule * GetRule(const char *category, const char *type);
void ClearRules();
map<int32, map<int32, Rule *> > * GetRules() {return &rules;}
private:
int32 id;
char name[64];
Mutex m_rules;
map<int32, map<int32, Rule *> > rules;
};
class RuleManager {
public:
RuleManager();
virtual ~RuleManager();
void Init();
void Flush(bool reinit=false);
void LoadCodedDefaultsIntoRuleSet(RuleSet *rule_set);
bool AddRuleSet(RuleSet *rule_set);
int32 GetNumRuleSets();
void ClearRuleSets();
Rule * GetBlankRule() {return &blank_rule;}
bool SetGlobalRuleSet(int32 rule_set_id);
Rule * GetGlobalRule(int32 category, int32 type);
Rule * GetGlobalRule(const char* category, const char* type);
bool SetZoneRuleSet(int32 zone_id, int32 rule_set_id);
Rule * GetZoneRule(int32 zone_id, int32 category, int32 type);
void ClearZoneRuleSets();
RuleSet * GetGlobalRuleSet() {return &global_rule_set;}
map<int32, map<int32, Rule *> > * GetRules() {return &rules;}
private:
Mutex m_rule_sets;
Mutex m_global_rule_set;
Mutex m_zone_rule_sets;
Rule blank_rule; /* READ ONLY */
map<int32, map<int32, Rule *> > rules; /* all of the rules loaded with their defaults (FROM THE CODE). map<category, map<type, rule>> */
map<int32, RuleSet *> rule_sets; /* all of the possible rule sets from the database. map<rule set id, rule set> */
RuleSet global_rule_set; /* the global rule set, first fill it the defaults from the code, then over ride from the database */
map<int32, RuleSet *> zone_rule_sets; /* references to a zone's rule set. map<zone id, rule set> */
};
#endif

View File

@ -0,0 +1,109 @@
/*
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/Log.h"
#include "../Worlddatabase.hpp"
extern RuleManager rule_manager;
void WorldDatabase::LoadGlobalRuleSet() {
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
int32 rule_set_id = 0;
res = query.RunQuery2(Q_SELECT, "SELECT `variable_value`\n"
"FROM `variables`\n"
"WHERE `variable_name`='default_ruleset_id'");
if (res && (row = mysql_fetch_row(res)))
{
rule_set_id = atoul(row[0]);
LogWrite(RULESYS__DEBUG, 5, "Rules", "\t\tLoading Global Ruleset id %i", rule_set_id);
}
if (rule_set_id > 0 && !rule_manager.SetGlobalRuleSet(rule_set_id))
LogWrite(RULESYS__ERROR, 0, "Rules", "Error loading global rule set. A rule set with ID %u does not exist.", rule_set_id);
else if(rule_set_id == 0)
LogWrite(RULESYS__ERROR, 0, "Rules", "Variables table is missing default_ruleset_id variable name, this means the global rules will be code-default, database entries not used. Use query such as \"insert into variables set variable_name='default_ruleset_id',variable_value='1',comment='Default ruleset';\" to resolve.");
}
void WorldDatabase::LoadRuleSets(bool reload) {
RuleSet *rule_set;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
if (reload)
rule_manager.Flush(true);
/* first load the coded defaults in */
rule_manager.LoadCodedDefaultsIntoRuleSet(rule_manager.GetGlobalRuleSet());
res = query.RunQuery2(Q_SELECT, "SELECT `ruleset_id`,`ruleset_name`\n"
"FROM `rulesets`\n"
"WHERE `ruleset_active`>0");
if (res) {
while ((row = mysql_fetch_row(res))) {
rule_set = new RuleSet();
rule_set->SetID(atoul(row[0]));
rule_set->SetName(row[1]);
if (rule_manager.AddRuleSet(rule_set))
{
LogWrite(RULESYS__DEBUG, 5, "Rules", "\t\tLoading rule set '%s' (%u)", rule_set->GetName(), rule_set->GetID());
LoadRuleSetDetails(rule_set);
}
else {
LogWrite(RULESYS__ERROR, 0, "Rules", "Unable to add rule set '%s'. A ruleset with ID %u already exists.", rule_set->GetName(), rule_set->GetID());
safe_delete(rule_set);
}
}
}
LogWrite(RULESYS__DEBUG, 3, "Rules", "--Loaded %u Rule Sets", rule_manager.GetNumRuleSets());
LoadGlobalRuleSet();
}
void WorldDatabase::LoadRuleSetDetails(RuleSet *rule_set) {
Rule *rule;
Query query;
MYSQL_ROW row;
MYSQL_RES *res;
assert(rule_set);
rule_set->CopyRulesInto(rule_manager.GetGlobalRuleSet());
res = query.RunQuery2(Q_SELECT, "SELECT `rule_category`,`rule_type`,`rule_value`\n"
"FROM `ruleset_details`\n"
"WHERE `ruleset_id`=%u",
rule_set->GetID());
if (res) {
while ((row = mysql_fetch_row(res))) {
if (!(rule = rule_set->GetRule(row[0], row[1]))) {
LogWrite(RULESYS__WARNING, 0, "Rules", "Unknown rule with category '%s' and type '%s'", row[0], row[1]);
continue;
}
LogWrite(RULESYS__DEBUG, 5, "Rules", "---Setting rule category '%s', type '%s' to value: %s", row[0], row[1], row[2]);
rule->SetValue(row[2]);
}
}
}

319
old/WorldServer/Sign.cpp Normal file
View File

@ -0,0 +1,319 @@
/*
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 "Sign.h"
#include "../common/config_reader.hpp"
#include "WorldDatabase.hpp"
#include "World.h"
#include "../common/log.hpp"
extern World world;
extern ConfigReader configReader;
extern WorldDatabase database;
extern ZoneList zone_list;
extern MasterSpellList master_spell_list;
Sign::Sign(){
widget_id = 0;
widget_x = 0;
widget_y = 0;
widget_z = 0;
appearance.pos.state = 1;
appearance.difficulty = 0;
spawn_type = 2;
appearance.activity_status = 64;
sign_type = 0;
zone_x = 0;
zone_y = 0;
zone_z = 0;
zone_heading = 0;
sign_distance = 0;
include_location = false;
include_heading = false;
zone_id = 0;
language = 0;
}
Sign::~Sign(){
}
int32 Sign::GetWidgetID(){
return widget_id;
}
EQ2Packet* Sign::serialize(Player* player, int16 version){
return spawn_serialize(player, version);
}
void Sign::SetWidgetID(int32 val){
widget_id = val;
}
void Sign::SetWidgetX(float val){
widget_x = val;
}
float Sign::GetWidgetX(){
return widget_x;
}
void Sign::SetWidgetY(float val){
widget_y = val;
}
float Sign::GetWidgetY(){
return widget_y;
}
void Sign::SetWidgetZ(float val){
widget_z = val;
}
float Sign::GetWidgetZ(){
return widget_z;
}
void Sign::SetSignIcon(int8 val){
appearance.icon = val;
}
void Sign::SetIncludeLocation(bool val){
include_location = val;
}
bool Sign::GetIncludeLocation(){
return include_location;
}
void Sign::SetIncludeHeading(bool val){
include_heading = val;
}
bool Sign::GetIncludeHeading(){
return include_heading;
}
Sign* Sign::Copy(){
Sign* new_spawn = new Sign();
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->SetCollector(IsCollector());
new_spawn->SetMerchantID(merchant_id);
new_spawn->SetMerchantType(merchant_type);
new_spawn->SetMerchantLevelRange(GetMerchantMinLevel(), GetMerchantMaxLevel());
new_spawn->SetPrimaryCommands(&primary_command_list);
new_spawn->primary_command_list_id = primary_command_list_id;
new_spawn->SetSecondaryCommands(&secondary_command_list);
new_spawn->secondary_command_list_id = secondary_command_list_id;
new_spawn->database_id = database_id;
memcpy(&new_spawn->appearance, &appearance, sizeof(AppearanceData));
new_spawn->SetWidgetID(widget_id);
new_spawn->SetWidgetX(widget_x);
new_spawn->SetWidgetY(widget_y);
new_spawn->SetWidgetZ(widget_z);
new_spawn->SetSignType(sign_type);
new_spawn->SetSignZoneX(zone_x);
new_spawn->SetSignZoneY(zone_y);
new_spawn->SetSignZoneZ(zone_z);
new_spawn->SetSignZoneHeading(zone_heading);
new_spawn->SetSignZoneID(GetSignZoneID());
new_spawn->SetSignTitle(GetSignTitle());
new_spawn->SetSignDescription(GetSignDescription());
new_spawn->SetSignDistance(sign_distance);
new_spawn->SetIncludeHeading(include_heading);
new_spawn->SetIncludeLocation(include_location);
new_spawn->SetTransporterID(GetTransporterID());
new_spawn->SetSoundsDisabled(IsSoundsDisabled());
new_spawn->SetOmittedByDBFlag(IsOmittedByDBFlag());
new_spawn->SetLootTier(GetLootTier());
new_spawn->SetLootDropType(GetLootDropType());
new_spawn->SetLanguage(GetLanguage());
return new_spawn;
}
int32 Sign::GetSignZoneID(){
return zone_id;
}
void Sign::SetSignZoneID(int32 val){
zone_id = val;
}
const char* Sign::GetSignTitle(){
if(title.length() > 0)
return title.c_str();
else
return 0;
}
void Sign::SetSignTitle(const char* val){
if(val)
title = string(val);
}
const char* Sign::GetSignDescription(){
if(description.length() > 0)
return description.c_str();
else
return 0;
}
void Sign::SetSignDescription(const char* val){
if(val)
description = string(val);
}
int8 Sign::GetSignType(){
return sign_type;
}
void Sign::SetSignType(int8 val){
sign_type = val;
}
float Sign::GetSignZoneX(){
return zone_x;
}
void Sign::SetSignZoneX(float val){
zone_x = val;
}
float Sign::GetSignZoneY(){
return zone_y;
}
void Sign::SetSignZoneY(float val){
zone_y = val;
}
float Sign::GetSignZoneZ(){
return zone_z;
}
void Sign::SetSignZoneZ(float val){
zone_z = val;
}
float Sign::GetSignZoneHeading(){
return zone_heading;
}
void Sign::SetSignZoneHeading(float val){
zone_heading = val;
}
float Sign::GetSignDistance(){
return sign_distance;
}
void Sign::SetSignDistance(float val){
sign_distance = val;
}
void Sign::HandleUse(Client* client, string command)
{
vector<TransportDestination*> destinations;
//The following check disables the use of doors and other widgets if the player does not meet the quest requirements
//If this is from a script ignore this check (client will be null)
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;
}
if( GetTransporterID() > 0 )
GetZone()->GetTransporters(&destinations, client, GetTransporterID());
if( destinations.size() )
{
client->SetTemporaryTransportID(0);
client->ProcessTeleport(this, &destinations, GetTransporterID());
}
else if( sign_type == SIGN_TYPE_ZONE && GetSignZoneID() > 0 )
{
if( GetSignDistance() == 0 || client->GetPlayer()->GetDistance(this) <= GetSignDistance() )
{
string name = database.GetZoneName(GetSignZoneID());
if( name.length() >0 )
{
if( !client->CheckZoneAccess(name.c_str()) )
return;
// determine if the coordinates should be set (returns false if they should)
// clearer, if the sign has x,y,z,heading coordinates, use them otherwise I assume we use the zones safe coords(?)
bool zone_coords_invalid = ( zone_x == 0 && zone_y == 0 && zone_z == 0 && zone_heading == 0 );
// I really hate double-negatives. Seriously?
if ( !zone_coords_invalid )
{
LogWrite(SIGN__DEBUG, 0, "Sign", "Sign has valid zone-to coordinates (%.2f, %.2f, %.2f, %.2f)", zone_x, zone_y, zone_z, zone_heading);
client->GetPlayer()->SetX(zone_x);
client->GetPlayer()->SetY(zone_y);
client->GetPlayer()->SetZ(zone_z);
client->GetPlayer()->SetHeading(zone_heading);
}
else // alert client we couldnt set the coordinates
{
LogWrite(SIGN__WARNING, 0, "Sign", "Sign has no zone-to coordinates set, using zones safe coords.");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Invalid zone in coords, taking you to a safe point.");
}
// Test if where we're going is an Instanced zone
if ( !client->TryZoneInstance(GetSignZoneID(), zone_coords_invalid) )
{
LogWrite(SIGN__DEBUG, 0, "Sign", "Sending client to instance of zone_id: %u", GetSignZoneID());
client->Zone(name.c_str(), zone_coords_invalid);
}
}
else
{
LogWrite(SIGN__WARNING, 0, "Sign", "Unable to find zone with ID: %u", GetSignZoneID());
client->Message(CHANNEL_COLOR_YELLOW, "Unable to find zone with ID: %u", GetSignZoneID());
}
}
else
{
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are too far away!");
}
}
else if (client && command.length() > 0)
{
EntityCommand* entity_command = FindEntityCommand(command);
//devn00b: Add support for marking objects
if (entity_command && strcmp(entity_command->command.c_str(), "mark") == 0) {
LogWrite(SIGN__DEBUG, 0, "Sign", "ActivateMarkReqested Sign - Command: '%s' (Should read mark)", entity_command->command.c_str());
int32 char_id = client->GetCharacterID();
database.SaveSignMark(char_id, GetWidgetID(), database.GetCharacterName(char_id), client);
return;
}
if (entity_command)
{
LogWrite(SIGN__DEBUG, 0, "Sign", "ActivateQuestRequired Sign - Command: '%s'", entity_command->command.c_str());
client->GetCurrentZone()->ProcessEntityCommand(entity_command, client->GetPlayer(), client->GetPlayer()->GetTarget());
}
}
}

91
old/WorldServer/Sign.h Normal file
View File

@ -0,0 +1,91 @@
/*
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_SIGN__
#define __EQ2_SIGN__
#include "Spawn.h"
#define SIGN_TYPE_GENERIC 0
#define SIGN_TYPE_ZONE 1
using namespace std;
class Sign : public Spawn{
public:
Sign();
virtual ~Sign();
bool IsSign(){ return true; }
int32 GetWidgetID();
void SetWidgetID(int32 val);
void SetWidgetX(float val);
float GetWidgetX();
void SetWidgetY(float val);
float GetWidgetY();
void SetWidgetZ(float val);
float GetWidgetZ();
void SetSignIcon(int8 val);
Sign* Copy();
EQ2Packet* serialize(Player *player, int16 version);
void HandleUse(Client* client, string command);
int8 GetSignType();
void SetSignType(int8 val);
float GetSignZoneX();
void SetSignZoneX(float val);
float GetSignZoneY();
void SetSignZoneY(float val);
float GetSignZoneZ();
void SetSignZoneZ(float val);
float GetSignZoneHeading();
void SetSignZoneHeading(float val);
float GetSignDistance();
void SetSignDistance(float val);
int32 GetSignZoneID();
void SetSignZoneID(int32 val);
const char* GetSignTitle();
void SetSignTitle(const char* val);
const char* GetSignDescription();
void SetSignDescription(const char* val);
void SetIncludeLocation(bool val);
bool GetIncludeLocation();
void SetIncludeHeading(bool val);
bool GetIncludeHeading();
void SetLanguage(int8 in_language) { language = in_language; }
int8 GetLanguage() { return language; }
private:
string description;
string title;
int8 sign_type;
float widget_x;
float widget_y;
float widget_z;
int32 widget_id;
float zone_x;
float zone_y;
float zone_z;
float zone_heading;
int32 zone_id;
float sign_distance;
bool include_location;
bool include_heading;
int8 language;
};
#endif

591
old/WorldServer/Skills.cpp Normal file
View File

@ -0,0 +1,591 @@
/*
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 "Skills.h"
#include "Spawn.h"
#include "LuaInterface.h"
#include "../common/log.hpp"
extern ConfigReader configReader;
extern LuaInterface* lua_interface;
MasterSkillList::MasterSkillList(){
}
MasterSkillList::~MasterSkillList(){
map<int32, Skill*>::iterator itr;
for(itr = skills.begin(); itr != skills.end(); itr++){
safe_delete(itr->second);
}
map<int16, EQ2Packet*>::iterator itr2;
for(itr2 = populate_packets.begin(); itr2 != populate_packets.end(); itr2++){
safe_delete(itr2->second);
}
}
Skill::Skill(){
skill_id = 0;
current_val = 0;
previous_val = 0;
max_val = 0;
skill_type = 0;
display = 0;
save_needed = false;
active_skill = true;
}
Skill::Skill(Skill* skill){
skill_id = skill->skill_id;
current_val = skill->current_val;
previous_val = skill->current_val;
max_val = skill->max_val;
skill_type = skill->skill_type;
display = skill->display;
short_name = skill->short_name;
name = skill->name;
description = skill->description;
save_needed = false;
active_skill = true;
}
map<int32, Skill*>* MasterSkillList::GetAllSkills(){
return &skills;
}
Skill* MasterSkillList::GetSkill(int32 skill_id){
if(skills.count(skill_id) > 0)
return skills[skill_id];
else
return 0;
}
Skill* MasterSkillList::GetSkillByName(const char* skill_name) {
Skill* skill = 0;
map<int32, Skill*>::iterator itr;
for (itr = skills.begin(); itr != skills.end(); itr++) {
Skill* current_skill = itr->second;
if (::ToLower(string(current_skill->name.data.c_str())) == ::ToLower(string(skill_name))) {
skill = current_skill;
break;
}
}
return skill;
}
int16 MasterSkillList::GetSkillCount(){
return skills.size();
}
void MasterSkillList::AddSkill(Skill* skill){
if(skill)
skills[skill->skill_id] = skill;
}
EQ2Packet* MasterSkillList::GetPopulateSkillsPacket(int16 version){
EQ2Packet* ret = 0;
int16 packet_version = configReader.GetStructVersion("WS_SkillMap", version);
if(populate_packets.count(packet_version) > 0)
ret = populate_packets[packet_version];
else{
PacketStruct* packet = configReader.getStruct("WS_SkillMap", packet_version);
int32 count = skills.size();
Skill* skill = 0;
int32 i = 0;
packet->setArrayLengthByName("skill_count", count);
map<int32, Skill*>::iterator itr;
for(itr = skills.begin(); itr != skills.end(); itr++, i++){
skill = itr->second;
packet->setArrayDataByName("skill_id", skill->skill_id, i);
packet->setArrayDataByName("short_name", &skill->short_name, i);
packet->setArrayDataByName("name", &skill->name, i);
packet->setArrayDataByName("description", &skill->description, i);
}
populate_packets[packet_version] = packet->serialize();
safe_delete(packet);
ret = populate_packets[packet_version];
}
if(ret)
return ret->Copy(); //need to return a copy as the packet is deleted after the client confirms it and we want to keep the packet to prevent constant generation of the same data
else
return ret;
}
PlayerSkillList::PlayerSkillList(){
xor_packet = 0;
orig_packet = 0;
packet_count = 0;
has_updates = false;
MSkillUpdates.SetName("PlayerSkillList::MSkillUpdates");
}
PlayerSkillList::~PlayerSkillList(){
map<int32, Skill*>::iterator itr;
for(itr = skills.begin(); itr != skills.end(); itr++){
safe_delete(itr->second);
}
MutexMap<int32, SkillBonus*>::iterator sb_itr = skill_bonus_list.begin();
while (sb_itr.Next())
RemoveSkillBonus(sb_itr.first);
safe_delete_array(xor_packet);
safe_delete_array(orig_packet);
}
void PlayerSkillList::AddSkill(Skill* new_skill){
std::unique_lock lock(MPlayerSkills);
Skill* tmpSkill = nullptr;
if(skills.count(new_skill->skill_id)) {
tmpSkill = skills[new_skill->skill_id];
}
skills[new_skill->skill_id] = new_skill;
if(tmpSkill) {
lua_interface->SetLuaUserDataStale(tmpSkill);
safe_delete(tmpSkill);
}
name_skill_map.clear();
}
void PlayerSkillList::RemoveSkill(Skill* skill) {
std::unique_lock lock(MPlayerSkills);
if (skill) {
lua_interface->SetLuaUserDataStale(skill);
skill->active_skill = false;
name_skill_map.clear();
}
}
map<int32, Skill*>* PlayerSkillList::GetAllSkills(){
return &skills;
}
void PlayerSkillList::SetSkillValuesByType(int8 type, int16 value, bool send_update) {
map<int32, Skill*>::iterator itr;
for (itr = skills.begin(); itr != skills.end(); itr++) {
if (itr->second && itr->second->skill_type == type)
SetSkill(itr->second, value, send_update);
}
}
void PlayerSkillList::SetSkillCapsByType(int8 type, int16 value){
map<int32, Skill*>::iterator itr;
for(itr = skills.begin(); itr != skills.end(); itr++){
if(itr->second && itr->second->skill_type == type)
SetSkillCap(itr->second, value);
}
}
void PlayerSkillList::IncreaseSkillCapsByType(int8 type, int16 value){
map<int32, Skill*>::iterator itr;
for(itr = skills.begin(); itr != skills.end(); itr++){
if(itr->second && itr->second->skill_type == type)
IncreaseSkillCap(itr->second, value);
}
}
void PlayerSkillList::IncreaseAllSkillCaps(int16 value){
map<int32, Skill*>::iterator itr;
for(itr = skills.begin(); itr != skills.end(); itr++){
IncreaseSkillCap(itr->second, value);
}
}
bool PlayerSkillList::HasSkill(int32 skill_id){
std::shared_lock lock(MPlayerSkills);
return (skills.count(skill_id) > 0 && skills[skill_id]->active_skill);
}
Skill* PlayerSkillList::GetSkill(int32 skill_id){
std::shared_lock lock(MPlayerSkills);
if(skills.count(skill_id) > 0 && skills[skill_id]->active_skill)
return skills[skill_id];
else
return 0;
}
void PlayerSkillList::IncreaseSkill(Skill* skill, int16 amount){
if(skill){
skill->previous_val = skill->current_val;
skill->current_val += amount;
if(skill->current_val > skill->max_val)
skill->max_val = skill->current_val;
AddSkillUpdateNeeded(skill);
skill->save_needed = true;
}
}
void PlayerSkillList::IncreaseSkill(int32 skill_id, int16 amount){
IncreaseSkill(GetSkill(skill_id), amount);
}
void PlayerSkillList::DecreaseSkill(Skill* skill, int16 amount){
if(skill){
skill->previous_val = skill->current_val;
if((skill->current_val - amount) < 0)
skill->current_val = 0;
else
skill->current_val -= amount;
skill->save_needed = true;
AddSkillUpdateNeeded(skill);
}
}
void PlayerSkillList::DecreaseSkill(int32 skill_id, int16 amount){
DecreaseSkill(GetSkill(skill_id), amount);
}
void PlayerSkillList::SetSkill(Skill* skill, int16 value, bool send_update){
if(skill){
skill->previous_val = skill->current_val;
skill->current_val = value;
if(skill->current_val > skill->max_val)
skill->max_val = skill->current_val;
skill->save_needed = true;
if(send_update)
AddSkillUpdateNeeded(skill);
}
}
void PlayerSkillList::SetSkill(int32 skill_id, int16 value, bool send_update){
SetSkill(GetSkill(skill_id), value, send_update);
}
void PlayerSkillList::IncreaseSkillCap(Skill* skill, int16 amount){
if(skill){
skill->max_val += amount;
skill->save_needed = true;
}
}
void PlayerSkillList::IncreaseSkillCap(int32 skill_id, int16 amount){
IncreaseSkillCap(GetSkill(skill_id), amount);
}
void PlayerSkillList::DecreaseSkillCap(Skill* skill, int16 amount){
if(skill){
if((skill->max_val - amount) < 0)
skill->max_val = 0;
else
skill->max_val -= amount;
if(skill->current_val > skill->max_val){
skill->previous_val = skill->current_val;
skill->current_val = skill->max_val;
}
AddSkillUpdateNeeded(skill);
skill->save_needed = true;
}
}
void PlayerSkillList::DecreaseSkillCap(int32 skill_id, int16 amount){
DecreaseSkillCap(GetSkill(skill_id), amount);
}
void PlayerSkillList::SetSkillCap(Skill* skill, int16 value){
if(skill){
skill->max_val = value;
if(skill->current_val > skill->max_val){
skill->previous_val = skill->current_val;
skill->current_val = skill->max_val;
}
AddSkillUpdateNeeded(skill);
skill->save_needed = true;
}
}
void PlayerSkillList::SetSkillCap(int32 skill_id, int16 value){
SetSkillCap(GetSkill(skill_id), value);
}
int16 PlayerSkillList::CalculateSkillValue(int32 skill_id, int16 current_val){
if (current_val > 5) {
int16 new_val = current_val;
MutexMap<int32, SkillBonus*>::iterator itr = skill_bonus_list.begin();
while (itr.Next()) {
SkillBonus* sb = itr.second;
map<int32, SkillBonusValue*>::iterator sbv_itr;
for (sbv_itr = sb->skills.begin(); sbv_itr != sb->skills.end(); sbv_itr++) {
SkillBonusValue* sbv = sbv_itr->second;
if (sbv->skill_id == skill_id)
new_val += (int16)sbv->value;
}
}
return new_val;
}
return current_val;
}
int16 PlayerSkillList::CalculateSkillMaxValue(int32 skill_id, int16 max_val) {
int16 new_val = max_val;
MutexMap<int32, SkillBonus*>::iterator itr = skill_bonus_list.begin();
while (itr.Next()) {
SkillBonus* sb = itr->second;
map<int32, SkillBonusValue*>::iterator sbv_itr;
for (sbv_itr = sb->skills.begin(); sbv_itr != sb->skills.end(); sbv_itr++) {
SkillBonusValue* sbv = sbv_itr->second;
if (sbv->skill_id == skill_id)
new_val += (int16)sbv->value;
}
}
return new_val;
}
EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){
std::unique_lock lock(MPlayerSkills);
PacketStruct* packet = configReader.getStruct("WS_UpdateSkillBook", version);
if(packet){
int16 skill_count = 0;
map<int32, Skill*>::iterator itr;
for (itr = skills.begin(); itr != skills.end(); itr++) {
if (itr->second && itr->second->active_skill)
skill_count++;
}
int16 size = 0;
if (version > 561) {
size = 21 * skill_count + 8;
}
else if (version < 373) {
size = 12 * skill_count + 6;
}
else if (version <= 373) {
size = 15 * skill_count + 6;
}
else if (version <= 561) {
size = 21 * skill_count + 7;
}
if (skill_count > packet_count) {
uchar* tmp = 0;
if (orig_packet) {
tmp = new uchar[size];
memset(tmp, 0, size);
memcpy(tmp, orig_packet, orig_packet_size);
safe_delete_array(orig_packet);
safe_delete_array(xor_packet);
orig_packet = tmp;
}
else {
orig_packet = new uchar[size];
memset(orig_packet, 0, size);
}
xor_packet = new uchar[size];
memset(xor_packet, 0, size);
}
packet_count = skill_count;
orig_packet_size = size;
packet->setArrayLengthByName("skill_count", skill_count);
Skill* skill = 0;
int32 i=0;
for(itr = skills.begin(); itr != skills.end(); itr++){
skill = itr->second;
if(skill && skill->active_skill){
int16 skill_max_with_bonuses = CalculateSkillMaxValue(skill->skill_id, skill->max_val);
int16 skill_with_bonuses = int(CalculateSkillValue(skill->skill_id, skill->current_val));
packet->setArrayDataByName("skill_id", skill->skill_id, i);
if (version <= 561 && skill->skill_type >= SKILL_TYPE_GENERAL) { //covert it to DOF types
packet->setArrayDataByName("type", skill->skill_type-2, i);
}
else if(version >= 60085 && skill->skill_type >= 12) {
packet->setArrayDataByName("type", skill->skill_type-1, i);
}
else {
packet->setArrayDataByName("type", skill->skill_type, i);
}
int16 current_val = skill->current_val;
if(skill->skill_type == SKILL_TYPE_LANGUAGE) { // 13 is language in the DB?? 14 is the skill type though
packet->setArrayDataByName("language_unknown", skill->skill_id, i);
packet->setArrayDataByName("display_maxval", 1, i);
packet->setArrayDataByName("max_val", 1, i);
}
else {
packet->setArrayDataByName("max_val", skill->max_val, i);
packet->setArrayDataByName("display_minval", skill->display, i);
packet->setArrayDataByName("display_maxval", skill->display, i);
packet->setArrayDataByName("skill_delta", 0, i);// skill_with_bonuses- skill->current_val
packet->setArrayDataByName("skill_delta2", skill_max_with_bonuses - skill->max_val, i);// skill_max_with_bonuses - skill->max_val, i);
}
packet->setArrayDataByName("current_val", current_val, i);
packet->setArrayDataByName("base_val", current_val, i);
i++;
}
}
int8 offset = 1;
if (version <= 373)
offset = 0;
EQ2Packet* ret = packet->serializeCountPacket(version, offset, orig_packet, xor_packet);
//packet->PrintPacket();
//DumpPacket(orig_packet, orig_packet_size);
//DumpPacket(ret);
safe_delete(packet);
return ret;
}
return 0;
}
bool PlayerSkillList::CheckSkillIncrease(Skill* skill){
if(!skill || skill->current_val >= skill->max_val)
return false;
// Assuming that skills will be used more at higher levels, increase chances are:
// skill val of 1 ~ 20% chance, value of 100 ~ 10%, value of 400 ~ 4%
int8 percent = (int8)(((float)((float)100/(float)(50 + skill->current_val)))*10);
if(rand()%100 < percent){ // skill increase
IncreaseSkill(skill, 1);
return true;
}
else
return false;
}
Skill* PlayerSkillList::GetSkillByName(const char* name){
std::shared_lock lock(MPlayerSkills);
if(name_skill_map.size() == 0){
map<int32, Skill*>::iterator itr;
Skill* skill = 0;
for(itr = skills.begin(); itr != skills.end(); itr++){
skill = itr->second;
name_skill_map[skill->name.data] = skill;
}
}
if(name_skill_map.count(name) > 0)
return name_skill_map[name];
else
return 0;
}
vector<Skill*>* PlayerSkillList::GetSaveNeededSkills(){
std::shared_lock lock(MPlayerSkills);
vector<Skill*>* ret = new vector<Skill*>;
map<int32, Skill*>::iterator itr;
for(itr = skills.begin(); itr != skills.end(); itr++){
if(itr->second->save_needed){
ret->push_back(itr->second);
itr->second->save_needed = false;
}
}
return ret;
}
void PlayerSkillList::AddSkillUpdateNeeded(Skill* skill){
MSkillUpdates.writelock(__FUNCTION__, __LINE__);
skill_updates.push_back(skill);
has_updates = true;
MSkillUpdates.releasewritelock(__FUNCTION__, __LINE__);
}
vector<Skill*>* PlayerSkillList::GetSkillUpdates(){
vector<Skill*>* ret = 0;
vector<Skill*>::iterator itr;
MSkillUpdates.writelock(__FUNCTION__, __LINE__);
if(skill_updates.size() > 0){
ret = new vector<Skill*>();
ret->insert(ret->begin(), skill_updates.begin(), skill_updates.end());
skill_updates.clear();
}
has_updates = false;
MSkillUpdates.releasewritelock(__FUNCTION__, __LINE__);
return ret;
}
bool PlayerSkillList::HasSkillUpdates(){
return has_updates;
}
void PlayerSkillList::AddSkillBonus(int32 spell_id, int32 skill_id, float value) {
if (value != 0) {
SkillBonus* sb;
if (skill_bonus_list.count(spell_id) == 0) {
sb = new SkillBonus;
sb->spell_id = spell_id;
skill_bonus_list.Put(spell_id, sb);
}
else
sb = skill_bonus_list.Get(spell_id);
if (sb->skills[skill_id] == nullptr) {
SkillBonusValue* sbv = new SkillBonusValue;
sbv->skill_id = skill_id;
sbv->value = value;
sb->skills[skill_id] = sbv;
}
}
}
void PlayerSkillList::ResetPackets() {
std::unique_lock lock(MPlayerSkills);
safe_delete_array(orig_packet);
safe_delete_array(xor_packet);
orig_packet_size = 0;
orig_packet = 0;
xor_packet = 0;
packet_count = 0;
}
SkillBonus* PlayerSkillList::GetSkillBonus(int32 spell_id) {
SkillBonus *ret = 0;
if (skill_bonus_list.count(spell_id) > 0)
ret = skill_bonus_list.Get(spell_id);
return ret;
}
void PlayerSkillList::RemoveSkillBonus(int32 spell_id) {
if (skill_bonus_list.count(spell_id) > 0) {
SkillBonus* sb = skill_bonus_list.Get(spell_id);
skill_bonus_list.erase(spell_id);
map<int32, SkillBonusValue*>::iterator itr;
for (itr = sb->skills.begin(); itr != sb->skills.end(); itr++)
safe_delete(itr->second);
safe_delete(sb);
}
}
int Skill::CheckDisarmSkill(int16 targetLevel, int8 chest_difficulty)
{
if (chest_difficulty < 2) // no triggers on this chest type
return 1;
if (targetLevel < 1)
targetLevel = 1;
int chest_diff_result = targetLevel * chest_difficulty;
float base_difficulty = 15.0f;
float fail_threshold = 10.0f;
float chance = ((100.0f - base_difficulty) * ((float)current_val / (float)chest_diff_result));
if (chance > (100.0f - base_difficulty))
{
chance = 100.0f - base_difficulty;
}
float d100 = (float)MakeRandomFloat(0, 100);
if (d100 <= chance)
return 1;
else
{
if (d100 > (chance + fail_threshold))
return -1;
}
return 0;
}

180
old/WorldServer/Skills.h Normal file
View File

@ -0,0 +1,180 @@
/*
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_SKILLS_H__
#define __EQ2_SKILLS_H__
#include <map>
#include <mutex>
#include <shared_mutex>
#include "../common/config_reader.hpp"
#include "../common/types.hpp"
#include "MutexMap.h"
#define SKILL_TYPE_WEAPONRY 1
#define SKILL_TYPE_SPELLCASTING 2
#define SKILL_TYPE_AVOIDANCE 3
#define SKILL_TYPE_ARMOR 4
#define SKILL_TYPE_SHIELD 5
#define SKILL_TYPE_HARVESTING 6
#define SKILL_TYPE_ARTISAN 7
#define SKILL_TYPE_CRAFTSMAN 8
#define SKILL_TYPE_OUTFITTER 9
#define SKILL_TYPE_SCHOLAR 10
#define SKILL_TYPE_GENERAL 13
#define SKILL_TYPE_LANGUAGE 14
#define SKILL_TYPE_CLASS 15
#define SKILL_TYPE_COMBAT 16
#define SKILL_TYPE_WEAPON 17
#define SKILL_TYPE_TSKNOWLEDGE 18
#define SKILL_TYPE_GENERAL_DOF 11
#define SKILL_TYPE_LANGUAGE_DOF 12
#define SKILL_TYPE_CLASS_DOF 13
#define SKILL_TYPE_COMBAT_DOF 14
#define SKILL_TYPE_WEAPON_DOF 15
#define SKILL_TYPE_TSKNOWLEDGE_DOF 16
#define SKILL_ID_SCULPTING 1039865549
#define SKILL_ID_ARTISTRY 3881305672
#define SKILL_ID_FLETCHING 3076004370
#define SKILL_ID_METALWORKING 4032608519
#define SKILL_ID_METALSHAPING 3108933728
#define SKILL_ID_TAILORING 2082133324
#define SKILL_ID_CHEMISTRY 2557647574
#define SKILL_ID_ARTIFICING 3330500131
#define SKILL_ID_SCRIBING 773137566
//the following update the current_value to the max_value as soon as the max_value is updated
#define SKILL_ID_DUALWIELD 1852383242
#define SKILL_ID_FISTS 3177806075
#define SKILL_ID_DESTROYING 3429135390
#define SKILL_ID_MAGIC_AFFINITY 2072844078
#define SKILL_ID_GREATSWORD 2292577688 // aka 2h slashing
#define SKILL_ID_GREATSPEAR 2380184628 // aka 2h piercing
#define SKILL_ID_STAFF 3180399725 // aka 2h crushing
/* Each SkillBonus is comprised of multiple possible skill bonus values. This is so one spell can modify
more than one skill */
struct SkillBonusValue {
int32 skill_id;
float value;
};
struct SkillBonus {
int32 spell_id;
map<int32, SkillBonusValue*> skills;
};
class Skill{
public:
Skill();
Skill(Skill* skill);
int32 skill_id;
int16 current_val;
int16 previous_val;
int16 max_val;
int32 skill_type;
int8 display;
EQ2_16BitString short_name;
EQ2_16BitString name;
EQ2_16BitString description;
bool save_needed;
bool active_skill;
int CheckDisarmSkill(int16 targetLevel, int8 chest_difficulty=0);
};
class MasterSkillList{
public:
MasterSkillList();
~MasterSkillList();
void AddSkill(Skill* skill);
int16 GetSkillCount();
EQ2Packet* GetPopulateSkillsPacket(int16 version);
map<int32, Skill*>* GetAllSkills();
Skill* GetSkill(int32 skill_id);
Skill* GetSkillByName(const char* skill_name);
private:
map<int32, Skill*> skills;
map<int16, EQ2Packet*> populate_packets;
};
class PlayerSkillList{
public:
PlayerSkillList();
~PlayerSkillList();
void RemoveSkill(Skill* skill);
void AddSkill(Skill* new_skill);
bool CheckSkillIncrease(Skill* skill);
Skill* GetSkillByName(const char* name);
bool HasSkill(int32 skill_id);
Skill* GetSkill(int32 skill_id);
void IncreaseSkill(Skill* skill, int16 amount);
void IncreaseSkill(int32 skill_id, int16 amount);
void DecreaseSkill(Skill* skill, int16 amount);
void DecreaseSkill(int32 skill_id, int16 amount);
void SetSkill(Skill* skill, int16 value, bool send_update = true);
void SetSkill(int32 skill_id, int16 value, bool send_update = true);
void IncreaseSkillCap(Skill* skill, int16 amount);
void IncreaseSkillCap(int32 skill_id, int16 amount);
void DecreaseSkillCap(Skill* skill, int16 amount);
void DecreaseSkillCap(int32 skill_id, int16 amount);
void SetSkillCap(Skill* skill, int16 value);
void SetSkillCap(int32 skill_id, int16 value);
void IncreaseAllSkillCaps(int16 value);
void IncreaseSkillCapsByType(int8 type, int16 value);
void SetSkillCapsByType(int8 type, int16 value);
void SetSkillValuesByType(int8 type, int16 value, bool send_update = true);
void AddSkillUpdateNeeded(Skill* skill);
void AddSkillBonus(int32 spell_id, int32 skill_id, float value);
SkillBonus* GetSkillBonus(int32 spell_id);
void RemoveSkillBonus(int32 spell_id);
int16 CalculateSkillValue(int32 skill_id, int16 current_val);
int16 CalculateSkillMaxValue(int32 skill_id, int16 max_val);
EQ2Packet* GetSkillPacket(int16 version);
vector<Skill*>* GetSaveNeededSkills();
vector<Skill*>* GetSkillUpdates();
map<int32, Skill*>* GetAllSkills();
bool HasSkillUpdates();
void ResetPackets();
private:
volatile bool has_updates;
mutable std::shared_mutex MPlayerSkills;
Mutex MSkillUpdates;
int16 packet_count;
uchar* orig_packet;
int16 orig_packet_size;
uchar* xor_packet;
map<int32, Skill*> skills;
map<string, Skill*> name_skill_map;
vector<Skill*> skill_updates;
MutexMap<int32, SkillBonus*> skill_bonus_list;
};
#endif

5531
old/WorldServer/Spawn.cpp Normal file

File diff suppressed because it is too large Load Diff

1585
old/WorldServer/Spawn.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,111 @@
/*
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_SPAWN_LISTS
#define EQ2_SPAWN_LISTS
#include "../common/types.hpp"
#include <vector>
#include <map>
#define SPAWN_ENTRY_TYPE_NPC 0
#define SPAWN_ENTRY_TYPE_OBJECT 1
#define SPAWN_ENTRY_TYPE_WIDGET 2
#define SPAWN_ENTRY_TYPE_SIGN 3
#define SPAWN_ENTRY_TYPE_GROUNDSPAWN 4
struct EntityCommand{
string name;
float distance;
string error_text;
string command;
int16 cast_time;
int32 spell_visual;
map<int32, bool> allow_or_deny; // this is a map of player IDs and whether they are allowed on the command or denied
bool default_allow_list; // if set to false then its a defaultDenyList
};
struct SpawnEntry{
int32 spawn_entry_id;
int32 spawn_location_id;
int8 spawn_type;
int32 spawn_id;
float spawn_percentage;
int32 respawn;
sint32 respawn_offset_low;
sint32 respawn_offset_high;
bool duplicated_spawn;
int32 expire_time;
int32 expire_offset;
//devn00b: added spawn location overrides, added these to accomodate.
int32 lvl_override;
int32 hp_override;
int32 mp_override;
int32 str_override;
int32 sta_override;
int32 wis_override;
int32 int_override;
int32 agi_override;
int32 heat_override;
int32 cold_override;
int32 magic_override;
int32 mental_override;
int32 divine_override;
int32 disease_override;
int32 poison_override;
int32 difficulty_override; //aka EncounterLevel
};
class SpawnLocation{
public:
SpawnLocation(){
x = 0;
y = 0;
z = 0;
heading = 0;
total_percentage = 0;
x_offset = 0;
y_offset = 0;
z_offset = 0;
placement_id = 0;
pitch = 0;
roll = 0;
grid_id = 0;
conditional = 0;
}
~SpawnLocation(){
for(int32 i=0;i<entities.size();i++)
safe_delete(entities[i]);
}
void AddSpawn(SpawnEntry* entity){ entities.push_back(entity); }
vector<SpawnEntry*> entities;
float x;
float y;
float z;
float heading;
float x_offset;
float y_offset;
float z_offset;
int32 placement_id;
float pitch;
float roll;
float total_percentage;
int32 grid_id;
string script;
int8 conditional;
};
#endif

File diff suppressed because it is too large Load Diff

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