merge from master

This commit is contained in:
Arthur Ice
2014-08-26 18:49:15 -07:00
417 changed files with 6952 additions and 10088 deletions
+22 -19
View File
@@ -1,7 +1,7 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(zone_sources
AA.cpp
aa.cpp
aggro.cpp
attack.cpp
beacon.cpp
@@ -16,7 +16,7 @@ SET(zone_sources
command.cpp
corpse.cpp
doors.cpp
effects.cpp
effects.cpp
embparser.cpp
embparser_api.cpp
embperl.cpp
@@ -69,12 +69,12 @@ SET(zone_sources
map.cpp
merc.cpp
mob.cpp
MobAI.cpp
mob_ai.cpp
mod_functions.cpp
net.cpp
npc.cpp
NpcAI.cpp
Object.cpp
npc_ai.cpp
object.cpp
oriented_bounding_box.cpp
pathing.cpp
perl_client.cpp
@@ -86,17 +86,18 @@ SET(zone_sources
perl_npc.cpp
perl_object.cpp
perl_perlpacket.cpp
perl_PlayerCorpse.cpp
perl_player_corpse.cpp
perl_questitem.cpp
perl_raids.cpp
perlpacket.cpp
petitions.cpp
pets.cpp
QGlobals.cpp
qglobals.cpp
queryserv.cpp
questmgr.cpp
QuestParserCollection.cpp
quest_parser_collection.cpp
raids.cpp
RaycastMesh.cpp
raycast_mesh.cpp
spawn2.cpp
spawn2.h
spawngroup.cpp
@@ -116,18 +117,18 @@ SET(zone_sources
worldserver.cpp
zone.cpp
zone_logsys.cpp
ZoneConfig.cpp
zone_config.cpp
zonedb.cpp
zonedbasync.cpp
zoning.cpp
)
SET(zone_headers
AA.h
aa.h
basic_functions.h
beacon.h
bot.h
botStructs.h
bot_structs.h
client.h
client_logs.h
client_packet.h
@@ -175,25 +176,27 @@ SET(zone_headers
mob.h
net.h
npc.h
NpcAI.h
npc_ai.h
object.h
oriented_bounding_box.h
pathing.h
perlpacket.h
petitions.h
pets.h
QGlobals.h
QuestInterface.h
qglobals.h
quest_interface.h
queryserv.h
quest_interface.h
questmgr.h
QuestParserCollection.h
quest_parser_collection.h
raid.h
raids.h
RaycastMesh.h
raycast_mesh.h
skills.h
spawn2.cpp
spawn2.h
spawngroup.h
StringIDs.h
string_ids.h
tasks.h
titles.h
trap.h
@@ -202,7 +205,7 @@ SET(zone_headers
water_map_v2.h
worldserver.h
zone.h
ZoneConfig.h
zone_config.h
zonedb.h
zonedbasync.h
zonedump.h
+243 -276
View File
@@ -19,7 +19,7 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net)
// Test 1
#include "../common/debug.h"
#include "AA.h"
#include "aa.h"
#include "mob.h"
#include "client.h"
#include "groups.h"
@@ -34,10 +34,13 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net)
#include "../common/classes.h"
#include "../common/eq_packet_structs.h"
#include "../common/packet_dump.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "../common/logsys.h"
#include "zonedb.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "queryserv.h"
extern QueryServ* QServ;
//static data arrays, really not big enough to warrant shared mem.
AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank]
@@ -458,7 +461,7 @@ void Client::HandleAAAction(aaID activate) {
Escape();
break;
// Don't think this code is used any longer for Bestial Alignment as the AA has a spell_id and no nonspell_action.
// Don't think this code is used any longer for Bestial Alignment as the aa.has a spell_id and no nonspell_action.
case aaActionBeastialAlignment:
switch(GetBaseRace()) {
case BARBARIAN:
@@ -1039,16 +1042,16 @@ void Client::BuyAA(AA_Action* action)
else
real_cost = aa2->cost + (aa2->cost_inc * cur_level);
if(m_pp.aapoints >= real_cost && cur_level < aa2->max_level) {
SetAA(aa2->id, cur_level+1);
if (m_pp.aapoints >= real_cost && cur_level < aa2->max_level) {
SetAA(aa2->id, cur_level + 1);
mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level+1);
mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level + 1);
m_pp.aapoints -= real_cost;
Save();
if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u))
&& ((aa2->max_level == (cur_level+1)) && aa2->sof_next_id)){
&& ((aa2->max_level == (cur_level + 1)) && aa2->sof_next_id)){
SendAA(aa2->id);
SendAA(aa2->sof_next_id);
}
@@ -1059,10 +1062,28 @@ void Client::BuyAA(AA_Action* action)
//we are building these messages ourself instead of using the stringID to work around patch discrepencies
//these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2
if(cur_level<1)
Message(15,"You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1)?"points":"point");
else
Message(15,"You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level+1, real_cost, (real_cost>1)?"points":"point");
/* Initial purchase of an AA ability */
if (cur_level < 1){
Message(15, "You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1) ? "points" : "point");
/* QS: Player_Log_AA_Purchases */
if (RuleB(QueryServ, PlayerLogAAPurchases)){
std::string event_desc = StringFormat("Initial AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc);
}
}
/* Ranked purchase of an AA ability */
else{
Message(15, "You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level + 1, real_cost, (real_cost > 1) ? "points" : "point");
/* QS: Player_Log_AA_Purchases */
if (RuleB(QueryServ, PlayerLogAAPurchases)){
std::string event_desc = StringFormat("Ranked AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc);
}
}
SendAAStats();
@@ -1237,7 +1258,7 @@ void Client::SendAA(uint32 id, int seq) {
Note: There were many ways to achieve this effect - The method used proved to be the most straight forward and consistent.
Stacking does not currently work ideally for AA's that use hotkeys, therefore they will be excluded at this time.
TODO: Problem with AA hotkeys - When you reach max rank of an AA tier (ie 5/5), it automatically displays the next AA in
TODO: Problem with aa.hotkeys - When you reach max rank of an AA tier (ie 5/5), it automatically displays the next AA in
the series and you can not transfer the hotkey to the next AA series. To the best of the my ability and through many
different variations of coding I could not find an ideal solution to this issue.
@@ -1466,32 +1487,30 @@ void Zone::LoadAAs() {
bool ZoneDatabase::LoadAAEffects2() {
aa_effects.clear(); //start fresh
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT aaid, slot, effectid, base1, base2 FROM aa_effects ORDER BY aaid ASC, slot ASC"), errbuf, &result)) {
int count = 0;
while((row = mysql_fetch_row(result))!= nullptr) {
int aaid = atoi(row[0]);
int slot = atoi(row[1]);
int effectid = atoi(row[2]);
int base1 = atoi(row[3]);
int base2 = atoi(row[4]);
aa_effects[aaid][slot].skill_id = effectid;
aa_effects[aaid][slot].base1 = base1;
aa_effects[aaid][slot].base2 = base2;
aa_effects[aaid][slot].slot = slot; //not really needed, but we'll populate it just in case
count++;
}
mysql_free_result(result);
if (count < 1) //no results
LogFile->write(EQEMuLog::Error, "Error loading AA Effects, none found in the database.");
} else {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAEffects2 query: '%s': %s", query, errbuf);
const std::string query = "SELECT aaid, slot, effectid, base1, base2 FROM aa_effects ORDER BY aaid ASC, slot ASC";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAEffects2 query: '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
if (!results.RowCount()) { //no results
LogFile->write(EQEMuLog::Error, "Error loading AA Effects, none found in the database.");
return false;
}
for(auto row = results.begin(); row != results.end(); ++row) {
int aaid = atoi(row[0]);
int slot = atoi(row[1]);
int effectid = atoi(row[2]);
int base1 = atoi(row[3]);
int base2 = atoi(row[4]);
aa_effects[aaid][slot].skill_id = effectid;
aa_effects[aaid][slot].base1 = base1;
aa_effects[aaid][slot].base2 = base2;
aa_effects[aaid][slot].slot = slot; //not really needed, but we'll populate it just in case
}
return true;
}
void Client::ResetAA(){
@@ -1633,73 +1652,62 @@ void Client::InspectBuffs(Client* Inspector, int Rank)
//this really need to be renamed to LoadAAActions()
bool ZoneDatabase::LoadAAEffects() {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
memset(AA_Actions, 0, sizeof(AA_Actions)); //I hope the compiler is smart about this size...
const char *query = "SELECT aaid,rank,reuse_time,spell_id,target,nonspell_action,nonspell_mana,nonspell_duration,"
"redux_aa,redux_rate,redux_aa2,redux_rate2 FROM aa_actions";
if(RunQuery(query, static_cast<uint32>(strlen(query)), errbuf, &result)) {
//safe_delete_array(query);
int r;
while ((row = mysql_fetch_row(result))) {
r = 0;
int aaid = atoi(row[r++]);
int rank = atoi(row[r++]);
if(aaid < 0 || aaid >= aaHighestID || rank < 0 || rank >= MAX_AA_ACTION_RANKS)
continue;
AA_DBAction *caction = &AA_Actions[aaid][rank];
caction->reuse_time = atoi(row[r++]);
caction->spell_id = atoi(row[r++]);
caction->target = (aaTargetType) atoi(row[r++]);
caction->action = (aaNonspellAction) atoi(row[r++]);
caction->mana_cost = atoi(row[r++]);
caction->duration = atoi(row[r++]);
caction->redux_aa = (aaID) atoi(row[r++]);
caction->redux_rate = atoi(row[r++]);
caction->redux_aa2 = (aaID) atoi(row[r++]);
caction->redux_rate2 = atoi(row[r++]);
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in LoadAAEffects query '%s': %s", query, errbuf);;
//safe_delete_array(query);
const std::string query = "SELECT aaid, rank, reuse_time, spell_id, target, "
"nonspell_action, nonspell_mana, nonspell_duration, "
"redux_aa, redux_rate, redux_aa2, redux_rate2 FROM aa_actions";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadAAEffects query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
int aaid = atoi(row[0]);
int rank = atoi(row[1]);
if(aaid < 0 || aaid >= aaHighestID || rank < 0 || rank >= MAX_AA_ACTION_RANKS)
continue;
AA_DBAction *caction = &AA_Actions[aaid][rank];
caction->reuse_time = atoi(row[2]);
caction->spell_id = atoi(row[3]);
caction->target = (aaTargetType) atoi(row[4]);
caction->action = (aaNonspellAction) atoi(row[5]);
caction->mana_cost = atoi(row[6]);
caction->duration = atoi(row[7]);
caction->redux_aa = (aaID) atoi(row[8]);
caction->redux_rate = atoi(row[9]);
caction->redux_aa2 = (aaID) atoi(row[10]);
caction->redux_rate2 = atoi(row[11]);
}
return true;
}
//Returns the number effects an AA has when we send them to the client
//Returns the number effects an aa.has when we send them to the client
//For the purposes of sizing a packet because every skill does not
//have the same number effects, they can range from none to a few depending on AA.
//counts the # of effects by counting the different slots of an AAID in the DB.
//AndMetal: this may now be obsolete since we have Zone::GetTotalAALevels()
uint8 ZoneDatabase::GetTotalAALevels(uint32 skill_id) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int total=0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(slot) from aa_effects where aaid=%i", skill_id), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
total=atoi(row[0]);
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in GetTotalAALevels '%s: %s", query, errbuf);
safe_delete_array(query);
}
return total;
std::string query = StringFormat("SELECT count(slot) FROM aa_effects WHERE aaid = %i", skill_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTotalAALevels '%s: %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
//this will allow us to count the number of effects for an AA by pulling the info from memory instead of the database. hopefully this will same some CPU cycles
@@ -1727,59 +1735,54 @@ void ZoneDatabase::FillAAEffects(SendAA_Struct* aa_struct){
if(!aa_struct)
return;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id), errbuf, &result)) {
int ndx=0;
while((row = mysql_fetch_row(result))!=nullptr) {
aa_struct->abilities[ndx].skill_id=atoi(row[0]);
aa_struct->abilities[ndx].base1=atoi(row[1]);
aa_struct->abilities[ndx].base2=atoi(row[2]);
aa_struct->abilities[ndx].slot=atoi(row[3]);
ndx++;
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query, errbuf);
std::string query = StringFormat("SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
int index = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
aa_struct->abilities[index].skill_id=atoi(row[0]);
aa_struct->abilities[index].base1=atoi(row[1]);
aa_struct->abilities[index].base2=atoi(row[2]);
aa_struct->abilities[index].slot=atoi(row[3]);
}
safe_delete_array(query);
}
uint32 ZoneDatabase::CountAAs(){
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int count=0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(title_sid) from altadv_vars"), errbuf, &result)) {
if((row = mysql_fetch_row(result))!=nullptr)
count = atoi(row[0]);
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAAs query '%s': %s", query, errbuf);
const std::string query = "SELECT count(title_sid) FROM altadv_vars";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAAs query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
safe_delete_array(query);
return count;
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);;
}
uint32 ZoneDatabase::CountAAEffects(){
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int count=0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(id) from aa_effects"), errbuf, &result)) {
if((row = mysql_fetch_row(result))!=nullptr){
count = atoi(row[0]);
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAALevels query '%s': %s", query, errbuf);
uint32 ZoneDatabase::CountAAEffects() {
const std::string query = "SELECT count(id) FROM aa_effects";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAALevels query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
safe_delete_array(query);
return count;
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
uint32 ZoneDatabase::GetSizeAA(){
@@ -1792,164 +1795,128 @@ uint32 ZoneDatabase::GetSizeAA(){
void ZoneDatabase::LoadAAs(SendAA_Struct **load){
if(!load)
return;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id from altadv_vars order by skill_id"), errbuf, &result)) {
int skill=0,ndx=0;
while((row = mysql_fetch_row(result))!=nullptr) {
skill=atoi(row[0]);
load[ndx] = GetAASkillVars(skill);
load[ndx]->seq = ndx+1;
ndx++;
std::string query = "SELECT skill_id FROM altadv_vars ORDER BY skill_id";
auto results = QueryDatabase(query);
if (results.Success()) {
int skill = 0, index = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
skill = atoi(row[0]);
load[index] = GetAASkillVars(skill);
load[index]->seq = index+1;
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf);
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
safe_delete_array(query);
AARequiredLevelAndCost.clear();
query = "SELECT skill_id, level, cost from aa_required_level_cost order by skill_id";
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id, level, cost from aa_required_level_cost order by skill_id"), errbuf, &result))
{
AALevelCost_Struct aalcs;
while((row = mysql_fetch_row(result))!=nullptr)
{
aalcs.Level = atoi(row[1]);
aalcs.Cost = atoi(row[2]);
AARequiredLevelAndCost[atoi(row[0])] = aalcs;
}
mysql_free_result(result);
}
else
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf);
AALevelCost_Struct aalcs;
for (auto row = results.begin(); row != results.end(); ++row)
{
aalcs.Level = atoi(row[1]);
aalcs.Cost = atoi(row[2]);
AARequiredLevelAndCost[atoi(row[0])] = aalcs;
}
safe_delete_array(query);
}
SendAA_Struct* ZoneDatabase::GetAASkillVars(uint32 skill_id)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
SendAA_Struct* sendaa = nullptr;
uchar* buffer;
if (RunQuery(query, MakeAnyLenString(&query, "SET @row = 0"), errbuf)) { //initialize "row" variable in database for next query
safe_delete_array(query);
MYSQL_RES *result; //we don't really need these unless we get to this point, so why bother?
MYSQL_ROW row;
std::string query = "SET @row = 0"; //initialize "row" variable in database for next query
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return nullptr;
}
if (RunQuery(query, MakeAnyLenString(&query,
"SELECT "
"a.cost, "
"a.max_level, "
"a.hotkey_sid, "
"a.hotkey_sid2, "
"a.title_sid, "
"a.desc_sid, "
"a.type, "
"COALESCE(" //so we can return 0 if it's null
"(" //this is our derived table that has the row # that we can SELECT from, because the client is stupid
"SELECT "
"p.prereq_index_num "
"FROM "
"("
"SELECT "
"a2.skill_id, "
"@row := @row + 1 AS prereq_index_num "
"FROM "
"altadv_vars a2"
") AS p "
"WHERE "
"p.skill_id = a.prereq_skill"
"), "
"0) AS prereq_skill_index, "
"a.prereq_minpoints, "
"a.spell_type, "
"a.spell_refresh, "
"a.classes, "
"a.berserker, "
"a.spellid, "
"a.class_type, "
"a.name, "
"a.cost_inc, "
"a.aa_expansion, "
"a.special_category, "
"a.sof_type, "
"a.sof_cost_inc, "
"a.sof_max_level, "
"a.sof_next_skill, "
"a.clientver, " // Client Version 0 = None, 1 = All, 2 = Titanium/6.2, 4 = SoF 5 = SOD 6 = UF
"a.account_time_required, "
"a.sof_current_level,"
"a.sof_next_id, "
"a.level_inc "
" FROM altadv_vars a WHERE skill_id=%i", skill_id), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
int total_abilities = GetTotalAALevels(skill_id); //eventually we'll want to use zone->GetTotalAALevels(skill_id) since it should save queries to the DB
int totalsize = total_abilities * sizeof(AA_Ability) + sizeof(SendAA_Struct);
query = StringFormat("SELECT a.cost, a.max_level, a.hotkey_sid, a.hotkey_sid2, a.title_sid, a.desc_sid, a.type, "
"COALESCE(" //So we can return 0 if it's null.
"(" // this is our derived table that has the row #
// that we can SELECT from, because the client is stupid.
"SELECT p.prereq_index_num "
"FROM (SELECT a2.skill_id, @row := @row + 1 AS prereq_index_num "
"FROM altadv_vars a2) AS p "
"WHERE p.skill_id = a.prereq_skill), 0) "
"AS prereq_skill_index, a.prereq_minpoints, a.spell_type, a.spell_refresh, a.classes, "
"a.berserker, a.spellid, a.class_type, a.name, a.cost_inc, a.aa_expansion, a.special_category, "
"a.sof_type, a.sof_cost_inc, a.sof_max_level, a.sof_next_skill, "
"a.clientver, " // Client Version 0 = None, 1 = All, 2 = Titanium/6.2, 4 = SoF 5 = SOD 6 = UF
"a.account_time_required, a.sof_current_level, a.sof_next_id, a.level_inc "
"FROM altadv_vars a WHERE skill_id=%i", skill_id);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return nullptr;
}
buffer = new uchar[totalsize];
memset(buffer,0,totalsize);
sendaa = (SendAA_Struct*)buffer;
if (results.RowCount() != 1)
return nullptr;
row = mysql_fetch_row(result);
int total_abilities = GetTotalAALevels(skill_id); //eventually we'll want to use zone->GetTotalAALevels(skill_id) since it should save queries to the DB
int totalsize = total_abilities * sizeof(AA_Ability) + sizeof(SendAA_Struct);
//ATOI IS NOT UNSIGNED LONG-SAFE!!!
SendAA_Struct* sendaa = nullptr;
uchar* buffer;
sendaa->cost = atoul(row[0]);
sendaa->cost2 = sendaa->cost;
sendaa->max_level = atoul(row[1]);
sendaa->hotkey_sid = atoul(row[2]);
sendaa->id = skill_id;
sendaa->hotkey_sid2 = atoul(row[3]);
sendaa->title_sid = atoul(row[4]);
sendaa->desc_sid = atoul(row[5]);
sendaa->type = atoul(row[6]);
sendaa->prereq_skill = atoul(row[7]);
sendaa->prereq_minpoints = atoul(row[8]);
sendaa->spell_type = atoul(row[9]);
sendaa->spell_refresh = atoul(row[10]);
sendaa->classes = static_cast<uint16>(atoul(row[11]));
sendaa->berserker = static_cast<uint16>(atoul(row[12]));
sendaa->last_id = 0xFFFFFFFF;
sendaa->current_level=1;
sendaa->spellid = atoul(row[13]);
sendaa->class_type = atoul(row[14]);
strcpy(sendaa->name,row[15]);
buffer = new uchar[totalsize];
memset(buffer,0,totalsize);
sendaa = (SendAA_Struct*)buffer;
sendaa->total_abilities=total_abilities;
if(sendaa->max_level > 1)
sendaa->next_id=skill_id+1;
else
sendaa->next_id=0xFFFFFFFF;
auto row = results.begin();
//ATOI IS NOT UNSIGNED LONG-SAFE!!!
sendaa->cost = atoul(row[0]);
sendaa->cost2 = sendaa->cost;
sendaa->max_level = atoul(row[1]);
sendaa->hotkey_sid = atoul(row[2]);
sendaa->id = skill_id;
sendaa->hotkey_sid2 = atoul(row[3]);
sendaa->title_sid = atoul(row[4]);
sendaa->desc_sid = atoul(row[5]);
sendaa->type = atoul(row[6]);
sendaa->prereq_skill = atoul(row[7]);
sendaa->prereq_minpoints = atoul(row[8]);
sendaa->spell_type = atoul(row[9]);
sendaa->spell_refresh = atoul(row[10]);
sendaa->classes = static_cast<uint16>(atoul(row[11]));
sendaa->berserker = static_cast<uint16>(atoul(row[12]));
sendaa->last_id = 0xFFFFFFFF;
sendaa->current_level=1;
sendaa->spellid = atoul(row[13]);
sendaa->class_type = atoul(row[14]);
strcpy(sendaa->name,row[15]);
sendaa->total_abilities=total_abilities;
if(sendaa->max_level > 1)
sendaa->next_id=skill_id+1;
else
sendaa->next_id=0xFFFFFFFF;
sendaa->cost_inc = atoi(row[16]);
// Begin SoF Specific/Adjusted AA Fields
sendaa->aa_expansion = atoul(row[17]);
sendaa->special_category = atoul(row[18]);
sendaa->sof_type = atoul(row[19]);
sendaa->sof_cost_inc = atoi(row[20]);
sendaa->sof_max_level = atoul(row[21]);
sendaa->sof_next_skill = atoul(row[22]);
sendaa->clientver = atoul(row[23]);
sendaa->account_time_required = atoul(row[24]);
//Internal use only - not sent to client
sendaa->sof_current_level = atoul(row[25]);
sendaa->sof_next_id = atoul(row[26]);
sendaa->level_inc = static_cast<uint8>(atoul(row[27]));
sendaa->cost_inc = atoi(row[16]);
// Begin SoF Specific/Adjusted AA Fields
sendaa->aa_expansion = atoul(row[17]);
sendaa->special_category = atoul(row[18]);
sendaa->sof_type = atoul(row[19]);
sendaa->sof_cost_inc = atoi(row[20]);
sendaa->sof_max_level = atoul(row[21]);
sendaa->sof_next_skill = atoul(row[22]);
sendaa->clientver = atoul(row[23]);
sendaa->account_time_required = atoul(row[24]);
//Internal use only - not sent to client
sendaa->sof_current_level = atoul(row[25]);
sendaa->sof_next_id = atoul(row[26]);
sendaa->level_inc = static_cast<uint8>(atoul(row[27]));
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf);
safe_delete_array(query);
}
} else {
LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf);
safe_delete_array(query);
}
return sendaa;
}
+1 -1
View File
@@ -2134,7 +2134,7 @@ struct AALevelCost_Struct
uint32 Cost;
};
//assumes that no activatable AA has more than 5 ranks
//assumes that no activatable aa.has more than 5 ranks
#define MAX_AA_ACTION_RANKS 20
extern AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank]
extern std::map<uint16, AA_SwarmPet> AA_SwarmPets; //key=spell_id
+32 -3
View File
@@ -23,9 +23,9 @@
#include "map.h"
#include "../common/spdat.h"
#include "../common/skills.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "../common/rulesys.h"
#include "StringIDs.h"
#include "string_ids.h"
#include <iostream>
extern Zone* zone;
@@ -873,7 +873,34 @@ bool Mob::CombatRange(Mob* other)
if (size_mod > 10000)
size_mod = size_mod / 7;
if (DistNoRoot(*other) <= size_mod)
float _DistNoRoot = DistNoRoot(*other);
if (GetSpecialAbility(NPC_CHASE_DISTANCE)){
bool DoLoSCheck = true;
float max_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0));
float min_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1));
if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2));
DoLoSCheck = false; //Ignore line of sight check
if (max_dist == 1)
max_dist = 250.0f; //Default it to 250 if you forget to put a value
max_dist = max_dist * max_dist;
if (!min_dist)
min_dist = size_mod; //Default to melee range
else
min_dist = min_dist * min_dist;
if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist))
SetPseudoRoot(true);
else
SetPseudoRoot(false);
}
if (_DistNoRoot <= size_mod)
{
return true;
}
@@ -887,6 +914,8 @@ bool Mob::CheckLosFN(Mob* other) {
if(other)
Result = CheckLosFN(other->GetX(), other->GetY(), other->GetZ(), other->GetSize());
SetLastLosState(Result);
return Result;
}
+38 -36
View File
@@ -29,19 +29,22 @@
#include <assert.h>
#include "masterentity.h"
#include "NpcAI.h"
#include "npc_ai.h"
#include "../common/packet_dump.h"
#include "../common/eq_packet_structs.h"
#include "../common/eq_constants.h"
#include "../common/skills.h"
#include "../common/spdat.h"
#include "zone.h"
#include "StringIDs.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "water_map.h"
#include "worldserver.h"
#include "queryserv.h"
extern QueryServ* QServ;
extern WorldServer worldserver;
#ifdef _WINDOWS
@@ -1455,14 +1458,14 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
int exploss = 0;
mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill);
//
// #1: Send death packet to everyone
//
/*
#1: Send death packet to everyone
*/
uint8 killed_level = GetLevel();
SendLogoutPackets();
//make our become corpse packet, and queue to ourself before OP_Death.
/* Make self become corpse packet */
EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct));
BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer;
bc->spawn_id = GetID();
@@ -1471,7 +1474,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
bc->z = GetZ();
QueuePacket(&app2);
// make death packet
/* Make Death Packet */
EQApplicationPacket app(OP_Death, sizeof(Death_Struct));
Death_Struct* d = (Death_Struct*)app.pBuffer;
d->spawn_id = GetID();
@@ -1484,9 +1487,9 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
app.priority = 6;
entity_list.QueueClients(this, &app);
//
// #2: figure out things that affect the player dying and mark them dead
//
/*
#2: figure out things that affect the player dying and mark them dead
*/
InterruptSpell();
SetPet(0);
@@ -1541,9 +1544,9 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
//remove ourself from all proximities
ClearAllProximities();
//
// #3: exp loss and corpse generation
//
/*
#3: exp loss and corpse generation
*/
// figure out if they should lose exp
if(RuleB(Character, UseDeathExpLossMult)){
@@ -1659,27 +1662,21 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
LeftCorpse = true;
}
// if(!IsLD())//Todo: make it so an LDed client leaves corpse if its enabled
// MakeCorpse(exploss);
} else {
BuffFadeDetrimental();
}
//
// Finally, send em home
//
/*
Finally, send em home
// we change the mob variables, not pp directly, because Save() will copy
// from these and overwrite what we set in pp anyway
//
We change the mob variables, not pp directly, because Save() will copy
from these and overwrite what we set in pp anyway
*/
if(LeftCorpse && (GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover))
{
ClearDraggedCorpses();
RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000);
ClearDraggedCorpses();
RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000);
SendRespawnBinds();
}
else
@@ -1696,17 +1693,22 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
if(r)
r->MemberZoned(this);
dead_timer.Start(5000, true);
dead_timer.Start(5000, true);
m_pp.zone_id = m_pp.binds[0].zoneId;
m_pp.zoneInstance = 0;
database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id));
Save();
database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id));
Save();
GoToDeath();
}
/* QS: PlayerLogDeaths */
if (RuleB(QueryServ, PlayerLogDeaths)){
const char * killer_name = "";
if (killerMob && killerMob->GetCleanName()){ killer_name = killerMob->GetCleanName(); }
std::string event_desc = StringFormat("Died in zoneid:%i instid:%i by '%s', spellid:%i, damage:%i", this->GetZoneID(), this->GetInstanceID(), killer_name, spell, damage);
QServ->PlayerLogEvent(Player_Log_Deaths, this->CharacterID(), event_desc);
}
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0);
return true;
}
@@ -3569,12 +3571,12 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
TryTriggerOnValueAmount(true);
//fade mez if we are mezzed
if (IsMezzed()) {
if (IsMezzed() && attacker) {
mlog(COMBAT__HITS, "Breaking mez due to attack.");
entity_list.MessageClose_StringID(this, true, 100, MT_WornOff,
HAS_BEEN_AWAKENED, GetCleanName(), attacker->GetCleanName());
BuffFadeByEffect(SE_Mez);
}
}
//check stun chances if bashing
if (damage > 0 && ((skill_used == SkillBash || skill_used == SkillKick) && attacker)) {
+3 -3
View File
@@ -20,13 +20,13 @@
#include "masterentity.h"
#include "../common/packet_dump.h"
#include "../common/moremath.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "worldserver.h"
#include "../common/skills.h"
#include "../common/bodytypes.h"
#include "../common/classes.h"
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include <math.h>
#include <assert.h>
#include <iostream>
@@ -35,7 +35,7 @@
#include "../common/unix.h"
#endif
#include "StringIDs.h"
#include "string_ids.h"
void Mob::CalcBonuses()
{
+2 -2
View File
@@ -3,8 +3,8 @@
#include "bot.h"
#include "object.h"
#include "doors.h"
#include "QuestParserCollection.h"
#include "../common/StringUtil.h"
#include "quest_parser_collection.h"
#include "../common/string_util.h"
extern volatile bool ZoneLoaded;
+3 -3
View File
@@ -3,15 +3,15 @@
#ifdef BOTS
#include "botStructs.h"
#include "bot_structs.h"
#include "mob.h"
#include "client.h"
#include "pets.h"
#include "groups.h"
#include "corpse.h"
#include "zonedb.h"
#include "StringIDs.h"
#include "../common/MiscFunctions.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/debug.h"
#include "guild_mgr.h"
#include "worldserver.h"
+1 -1
View File
@@ -1,7 +1,7 @@
#ifdef BOTS
#include "bot.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
+51 -24
View File
@@ -49,21 +49,22 @@ extern volatile bool RunLoops;
#include "../common/packet_functions.h"
#include "petitions.h"
#include "../common/serverinfo.h"
#include "../common/ZoneNumbers.h"
#include "../common/zone_numbers.h"
#include "../common/moremath.h"
#include "../common/guilds.h"
#include "../common/breakdowns.h"
#include "../common/rulesys.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "forage.h"
#include "command.h"
#include "StringIDs.h"
#include "NpcAI.h"
#include "string_ids.h"
#include "npc_ai.h"
#include "client_logs.h"
#include "guild_mgr.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "queryserv.h"
extern QueryServ* QServ;
extern EntityList entity_list;
extern Zone* zone;
extern volatile bool ZoneLoaded;
@@ -629,6 +630,9 @@ bool Client::Save(uint8 iCommitNow) {
return false;
}
/* Mirror Character Data */
database.StoreCharacterLookup(this->CharacterID());
return true;
}
@@ -805,8 +809,8 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
}
}
if(RuleB(QueryServ, PlayerChatLogging)) {
/* Logs Player Chat */
if (RuleB(QueryServ, PlayerLogChat)) {
ServerPacket* pack = new ServerPacket(ServerOP_Speech, sizeof(Server_Speech_Struct) + strlen(message) + 1);
Server_Speech_Struct* sem = (Server_Speech_Struct*) pack->pBuffer;
@@ -841,7 +845,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
switch(chan_num)
{
case 0: { // GuildChat
case 0: { /* Guild Chat */
if (!IsInAGuild())
Message_StringID(MT_DefaultText, GUILD_NOT_MEMBER2); //You are not a member of any guild.
else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_SPEAK))
@@ -850,7 +854,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
Message(0, "Error: World server disconnected");
break;
}
case 2: { // GroupChat
case 2: { /* Group Chat */
Raid* raid = entity_list.GetRaidByClient(this);
if(raid) {
raid->RaidGroupSay((const char*) message, this);
@@ -863,14 +867,14 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
}
break;
}
case 15: { //raid say
case 15: { /* Raid Say */
Raid* raid = entity_list.GetRaidByClient(this);
if(raid){
raid->RaidSay((const char*) message, this);
}
break;
}
case 3: { // Shout
case 3: { /* Shout */
Mob *sender = this;
if (GetPet() && GetPet()->FindType(SE_VoiceGraft))
sender = GetPet();
@@ -878,7 +882,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message);
break;
}
case 4: { // Auction
case 4: { /* Auction */
if(RuleB(Chat, ServerWideAuction))
{
if(!global_channel_timer.Check())
@@ -917,7 +921,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
}
break;
}
case 5: { // OOC
case 5: { /* OOC */
if(RuleB(Chat, ServerWideOOC))
{
if(!global_channel_timer.Check())
@@ -964,15 +968,15 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
}
break;
}
case 6: // Broadcast
case 11: { // GMSay
case 6: /* Broadcast */
case 11: { /* GM Say */
if (!(admin >= 80))
Message(0, "Error: Only GMs can use this channel");
else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, message))
Message(0, "Error: World server disconnected");
break;
}
case 7: { // Tell
case 7: { /* Tell */
if(!global_channel_timer.Check())
{
if(strlen(targetname) == 0)
@@ -1020,7 +1024,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
Message(0, "Error: World server disconnected");
break;
}
case 8: { // /say
case 8: { /* Say */
if(message[0] == COMMAND_CHAR) {
if(command_dispatch(this, message) == -2) {
if(parse->PlayerHasQuestSub(EVENT_COMMAND)) {
@@ -2237,6 +2241,15 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){
void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){
/* Set a timestamp in an entity variable for plugin check_handin.pl in return_items
This will stopgap players from items being returned if global_npc.pl has a catch all return_items
*/
struct timeval read_time;
char buffer[50];
gettimeofday(&read_time, 0);
sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec);
this->SetEntityVariable("Stop_Return", buffer);
int32 new_value = m_pp.platinum + platinum;
if(new_value >= 0 && new_value > m_pp.platinum)
m_pp.platinum += platinum;
@@ -4020,13 +4033,17 @@ void Client::KeyRingAdd(uint32 item_id)
bool bFound = KeyRingCheck(item_id);
if(!bFound){
sprintf(query, "INSERT INTO keyring(char_id,item_id) VALUES(%i,%i)",character_id,item_id);
if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows))
{
if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) {
Message(4,"Added to keyring.");
/* QS: PlayerLogKeyringAddition */
if (RuleB(QueryServ, PlayerLogKeyringAddition)){
std::string event_desc = StringFormat("itemid:%i in zoneid:%i instid:%i", item_id, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Keyring_Addition, this->CharacterID(), event_desc);
}
safe_delete_array(query);
}
else
{
else {
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return;
@@ -6924,8 +6941,18 @@ void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount)
SendAlternateCurrencyValue(currency_id);
}
void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount)
void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method)
{
/* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */
if (method == 1){
/* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */
if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){
std::string event_desc = StringFormat("Added via Quest :: Cursor to Item :: alt_currency_id:%i amount:%i in zoneid:%i instid:%i", currency_id, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc);
}
}
if(amount == 0) {
return;
}
@@ -8268,4 +8295,4 @@ void Client::ExpeditionSay(const char *str, int ExpID) {
mysql_free_result(result);
}
}
+7 -7
View File
@@ -24,15 +24,15 @@ class Client;
#include "../common/emu_opcodes.h"
#include "../common/eq_packet_structs.h"
#include "../common/eq_constants.h"
#include "../common/EQStreamIntf.h"
#include "../common/EQPacket.h"
#include "../common/eq_stream_intf.h"
#include "../common/eq_packet.h"
#include "../common/linked_list.h"
#include "../common/extprofile.h"
#include "../common/classes.h"
#include "../common/races.h"
#include "../common/deity.h"
#include "../common/seperator.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "../common/guilds.h"
#include "../common/item_struct.h"
#include "../common/clientversions.h"
@@ -43,9 +43,9 @@ class Client;
#include "npc.h"
#include "merc.h"
#include "zone.h"
#include "AA.h"
#include "aa.h"
#include "questmgr.h"
#include "QGlobals.h"
#include "qglobals.h"
#ifdef _WINDOWS
// since windows defines these within windef.h (which windows.h include)
@@ -268,7 +268,7 @@ public:
void TradeRequestFailed(const EQApplicationPacket* app);
void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app);
void TraderUpdate(uint16 slot_id,uint32 trader_id);
void FinishTrade(Mob* with, ServerPacket* qspack = nullptr, bool finalizer = false);
void FinishTrade(Mob* with, bool finalizer = false, void* event_entry = nullptr, std::list<void*>* event_details = nullptr);
void SendZonePoints();
void SendBuyerResults(char *SearchQuery, uint32 SearchID);
@@ -1097,7 +1097,7 @@ public:
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount);
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
void SendAlternateCurrencyValues();
void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true);
uint32 GetAlternateCurrencyValue(uint32 currency_id) const;
+3 -3
View File
@@ -22,7 +22,7 @@
#include "../common/packet_dump.h"
#include "../common/packet_functions.h"
#include "../common/serverinfo.h"
#include "../common/ZoneNumbers.h"
#include "../common/zone_numbers.h"
#include "../common/moremath.h"
#include "../common/guilds.h"
#include "../common/logsys.h"
@@ -30,8 +30,8 @@
#include "worldserver.h"
#include "zonedb.h"
#include "petitions.h"
#include "StringIDs.h"
#include "NpcAI.h"
#include "string_ids.h"
#include "npc_ai.h"
// Return max stat value for level
+389 -399
View File
File diff suppressed because it is too large Load Diff
+26 -21
View File
@@ -47,10 +47,10 @@
#include "../common/packet_dump.h"
#include "worldserver.h"
#include "../common/packet_dump_file.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "../common/spdat.h"
#include "petitions.h"
#include "NpcAI.h"
#include "npc_ai.h"
#include "../common/skills.h"
#include "forage.h"
#include "zone.h"
@@ -58,12 +58,14 @@
#include "../common/faction.h"
#include "../common/crc32.h"
#include "../common/rulesys.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "map.h"
#include "guild_mgr.h"
#include <string>
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "queryserv.h"
extern QueryServ* QServ;
extern Zone* zone;
extern volatile bool ZoneLoaded;
extern WorldServer worldserver;
@@ -770,38 +772,40 @@ bool Client::Process() {
return ret;
}
//just a set of actions preformed all over in Client::Process
/* Just a set of actions preformed all over in Client::Process */
void Client::OnDisconnect(bool hard_disconnect) {
if(hard_disconnect) {
LeaveGroup();
LeaveGroup();
Raid *MyRaid = entity_list.GetRaidByClient(this);
if (MyRaid)
MyRaid->MemberZoned(this);
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
/* QS: PlayerLogConnectDisconnect */
if (RuleB(QueryServ, PlayerLogConnectDisconnect)){
std::string event_desc = StringFormat("Disconnect :: in zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc);
}
}
Mob *Other = trade->With();
if(Other)
{
mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items.");
Mob *Other = trade->With();
if(Other) {
mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items.");
FinishTrade(this);
if(Other->IsClient())
Other->CastToClient()->FinishTrade(Other);
trade->Reset();
/* Reset both sides of the trade */
trade->Reset();
Other->trade->Reset();
}
database.SetFirstLogon(CharacterID(), 0); //We change firstlogon status regardless of if a player logs out to zone or not, because we only want to trigger it on their first login from world.
//remove ourself from all proximities
/* Remove ourself from all proximities */
ClearAllProximities();
EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply);
@@ -983,17 +987,18 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
uint8 handychance = 0;
for (itr = merlist.begin(); itr != merlist.end() && i < numItemSlots; ++itr) {
MerchantList ml = *itr;
if(GetLevel() < ml.level_required) {
if (merch->CastToNPC()->GetMerchantProbability() > ml.probability)
continue;
if(GetLevel() < ml.level_required)
continue;
}
if (!(ml.classes_required & (1 << (GetClass() - 1))))
continue;
int32 fac = merch ? merch->GetPrimaryFaction() : 0;
if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) {
if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required)
continue;
}
handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1 );
+13 -6
View File
@@ -47,10 +47,10 @@
#include "../common/packet_dump.h"
#include "../common/serverinfo.h"
#include "../common/opcodemgr.h"
#include "../common/EQPacket.h"
#include "../common/eq_packet.h"
#include "../common/guilds.h"
#include "../common/rulesys.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
//#include "../common/servertalk.h" // for oocmute and revoke
#include "worldserver.h"
#include "masterentity.h"
@@ -62,17 +62,18 @@
#include "guild_mgr.h"
#include "titles.h"
#include "../common/patches/patches.h"
#include "queryserv.h"
// these should be in the headers...
extern QueryServ* QServ;
extern WorldServer worldserver;
extern TaskManager *taskmanager;
void CatchSignal(int sig_num);
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "command.h"
#include "QGlobals.h"
#include "qglobals.h"
//struct cl_struct *commandlist; // the actual linked list of commands
int commandcount; // how many commands we have
@@ -588,6 +589,12 @@ int command_realdispatch(Client *c, const char *message)
return(-1);
}
/* QS: Player_Log_Issued_Commands */
if (RuleB(QueryServ, PlayerLogIssuedCommandes)){
std::string event_desc = StringFormat("Issued command :: '%s' in zoneid:%i instid:%i", message, c->GetZoneID(), c->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc);
}
#ifdef COMMANDS_LOGGING
if(cur->access >= COMMANDS_LOGGING_MIN_STATUS) {
LogFile->write(EQEMuLog::Commands, "%s (%s) used command: %s (target=%s)", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE");
+1 -1
View File
@@ -21,7 +21,7 @@
#define COMMAND_H
#include "../common/seperator.h"
#include "../common/EQStream.h"
#include "../common/eq_stream.h"
#include "client.h"
#define COMMAND_CHAR '#'
+7 -6
View File
@@ -127,7 +127,8 @@ enum {
FLEE_PERCENT = 37,
ALLOW_BENEFICIAL = 38,
DISABLE_MELEE = 39,
MAX_SPECIAL_ATTACK = 40
NPC_CHASE_DISTANCE = 40,
MAX_SPECIAL_ATTACK = 41
};
@@ -368,10 +369,10 @@ struct StatBonuses {
bool NegateIfCombat; // Bool Drop buff if cast or melee
int8 Screech; // -1 = Will be blocked if another Screech is +(1)
int16 AlterNPCLevel; // amount of lvls +/-
int16 AStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
int16 BStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
int16 CStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
int16 DStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value
int16 AStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value
int16 BStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value
int16 CStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value
int16 DStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value
bool BerserkSPA; // berserk effect
int16 Metabolism; // Food/drink consumption rates.
bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others.
@@ -526,7 +527,7 @@ public:
Mob* With();
// Add item from cursor slot to trade bucket (automatically does bag data too)
void AddEntity(uint16 from_slot_id, uint16 trade_slot_id);
void AddEntity(uint16 trade_slot_id, uint32 stack_size);
// Audit trade
void LogTrade();
+3 -3
View File
@@ -35,12 +35,12 @@ Child of the Mob class.
#include "masterentity.h"
#include "../common/packet_functions.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "../common/crc32.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "worldserver.h"
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
extern EntityList entity_list;
extern Zone* zone;
+109 -130
View File
@@ -21,11 +21,11 @@
#include <string.h>
#include "masterentity.h"
#include "worldserver.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "zonedb.h"
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "guild_mgr.h"
#define OPEN_DOOR 0x02
@@ -253,8 +253,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger)
strcpy(tmpmsg, "Door is locked by an unknown guild");
}
sender->Message(4, tmpmsg);
// safe_delete(outapp);
// /\ possible missing line..all other 'fail' returns seem to have it
safe_delete(outapp);
return;
}
// a key is required or the door is locked but can be picked or both
@@ -569,157 +568,137 @@ void Doors::DumpDoor(){
}
int32 ZoneDatabase::GetDoorsCount(uint32* oMaxID, const char *zone_name, int16 version) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
query = new char[256];
sprintf(query, "SELECT MAX(id), count(*) FROM doors WHERE zone='%s' AND (version=%u OR version=-1)", zone_name, version);
if (RunQuery(query, strlen(query), errbuf, &result)) {
safe_delete_array(query);
row = mysql_fetch_row(result);
if (row != nullptr && row[1] != 0) {
int32 ret = atoi(row[1]);
if (oMaxID) {
if (row[0])
*oMaxID = atoi(row[0]);
else
*oMaxID = 0;
}
mysql_free_result(result);
return ret;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in GetDoorsCount query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT MAX(id), count(*) FROM doors "
"WHERE zone = '%s' AND (version = %u OR version = -1)",
zone_name, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetDoorsCount query '" << query << "' " << results.ErrorMessage() << std::endl;
return -1;
}
}
if (results.RowCount() != 1)
return -1;
auto row = results.begin();
if (!oMaxID)
return atoi(row[1]);
if (row[0])
*oMaxID = atoi(row[0]);
else
*oMaxID = 0;
return atoi(row[1]);
return -1;
}
int32 ZoneDatabase::GetDoorsCountPlusOne(const char *zone_name, int16 version) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 oMaxID = 0;
MYSQL_RES *result;
MYSQL_ROW row;
query = new char[256];
sprintf(query, "SELECT MAX(id) FROM doors WHERE zone='%s' AND version=%u", zone_name, version);
if (RunQuery(query, strlen(query), errbuf, &result)) {
safe_delete_array(query);
row = mysql_fetch_row(result);
if (row != nullptr && row[1] != 0) {
if (row[0])
oMaxID = atoi(row[0]) + 1;
else
oMaxID = 0;
mysql_free_result(result);
return oMaxID;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT MAX(id) FROM doors "
"WHERE zone = '%s' AND version = %u", zone_name, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << results.ErrorMessage() << std::endl;
return -1;
}
}
return -1;
if (results.RowCount() != 1)
return -1;
auto row = results.begin();
if (!row[0])
return 0;
return atoi(row[0]) + 1;
}
int32 ZoneDatabase::GetDoorsDBCountPlusOne(const char *zone_name, int16 version) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 oMaxID = 0;
MYSQL_RES *result;
MYSQL_ROW row;
query = new char[256];
sprintf(query, "SELECT MAX(doorid) FROM doors WHERE zone='%s' AND (version=%u OR version=-1)", zone_name, version);
if (RunQuery(query, strlen(query), errbuf, &result)) {
safe_delete_array(query);
row = mysql_fetch_row(result);
if (row != nullptr && row[1] != 0) {
if (row[0])
oMaxID = atoi(row[0]) + 1;
else
oMaxID = 0;
mysql_free_result(result);
return oMaxID;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT MAX(doorid) FROM doors "
"WHERE zone = '%s' AND (version = %u OR version = -1)",
zone_name, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << results.ErrorMessage() << std::endl;
return -1;
}
return -1;
if (results.RowCount() != 1)
return -1;
auto row = results.begin();
if (!row[0])
return 0;
return atoi(row[0]) + 1;
}
bool ZoneDatabase::LoadDoors(int32 iDoorCount, Door *into, const char *zone_name, int16 version) {
LogFile->write(EQEMuLog::Status, "Loading Doors from database...");
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
// Door tmpDoor;
MakeAnyLenString(&query, "SELECT id,doorid,zone,name,pos_x,pos_y,pos_z,heading,"
"opentype,guild,lockpick,keyitem,nokeyring,triggerdoor,triggertype,dest_zone,dest_instance,dest_x,"
"dest_y,dest_z,dest_heading,door_param,invert_state,incline,size,is_ldon_door,client_version_mask "
"FROM doors WHERE zone='%s' AND (version=%u OR version=-1) ORDER BY doorid asc", zone_name, version);
if (RunQuery(query, strlen(query), errbuf, &result)) {
safe_delete_array(query);
int32 r;
for(r = 0; (row = mysql_fetch_row(result)); r++) {
if(r >= iDoorCount) {
std::cerr << "Error, Door Count of " << iDoorCount << " exceeded." << std::endl;
break;
}
memset(&into[r], 0, sizeof(Door));
into[r].db_id = atoi(row[0]);
into[r].door_id = atoi(row[1]);
strn0cpy(into[r].zone_name,row[2],32);
strn0cpy(into[r].door_name,row[3],32);
into[r].pos_x = (float)atof(row[4]);
into[r].pos_y = (float)atof(row[5]);
into[r].pos_z = (float)atof(row[6]);
into[r].heading = (float)atof(row[7]);
into[r].opentype = atoi(row[8]);
into[r].guild_id = atoi(row[9]);
into[r].lockpick = atoi(row[10]);
into[r].keyitem = atoi(row[11]);
into[r].nokeyring = atoi(row[12]);
into[r].trigger_door = atoi(row[13]);
into[r].trigger_type = atoi(row[14]);
strn0cpy(into[r].dest_zone, row[15], 32);
into[r].dest_instance_id = atoi(row[16]);
into[r].dest_x = (float) atof(row[17]);
into[r].dest_y = (float) atof(row[18]);
into[r].dest_z = (float) atof(row[19]);
into[r].dest_heading = (float) atof(row[20]);
into[r].door_param=atoi(row[21]);
into[r].invert_state=atoi(row[22]);
into[r].incline=atoi(row[23]);
into[r].size=atoi(row[24]);
into[r].is_ldon_door=atoi(row[25]);
into[r].client_version_mask = (uint32)strtoul(row[26], nullptr, 10);
}
mysql_free_result(result);
}
else
{
std::cerr << "Error in DBLoadDoors query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT id, doorid, zone, name, pos_x, pos_y, pos_z, heading, "
"opentype, guild, lockpick, keyitem, nokeyring, triggerdoor, triggertype, "
"dest_zone, dest_instance, dest_x, dest_y, dest_z, dest_heading, "
"door_param, invert_state, incline, size, is_ldon_door, client_version_mask "
"FROM doors WHERE zone = '%s' AND (version = %u OR version = -1) "
"ORDER BY doorid asc", zone_name, version);
auto results = QueryDatabase(query);
if (!results.Success()){
std::cerr << "Error in DBLoadDoors query '" << query << "' " << results.ErrorMessage() << std::endl;
return false;
}
int32 rowIndex = 0;
for(auto row = results.begin(); row != results.end(); ++row, ++rowIndex) {
if(rowIndex >= iDoorCount) {
std::cerr << "Error, Door Count of " << iDoorCount << " exceeded." << std::endl;
break;
}
memset(&into[rowIndex], 0, sizeof(Door));
into[rowIndex].db_id = atoi(row[0]);
into[rowIndex].door_id = atoi(row[1]);
strn0cpy(into[rowIndex].zone_name,row[2],32);
strn0cpy(into[rowIndex].door_name,row[3],32);
into[rowIndex].pos_x = (float)atof(row[4]);
into[rowIndex].pos_y = (float)atof(row[5]);
into[rowIndex].pos_z = (float)atof(row[6]);
into[rowIndex].heading = (float)atof(row[7]);
into[rowIndex].opentype = atoi(row[8]);
into[rowIndex].guild_id = atoi(row[9]);
into[rowIndex].lockpick = atoi(row[10]);
into[rowIndex].keyitem = atoi(row[11]);
into[rowIndex].nokeyring = atoi(row[12]);
into[rowIndex].trigger_door = atoi(row[13]);
into[rowIndex].trigger_type = atoi(row[14]);
strn0cpy(into[rowIndex].dest_zone, row[15], 32);
into[rowIndex].dest_instance_id = atoi(row[16]);
into[rowIndex].dest_x = (float) atof(row[17]);
into[rowIndex].dest_y = (float) atof(row[18]);
into[rowIndex].dest_z = (float) atof(row[19]);
into[rowIndex].dest_heading = (float) atof(row[20]);
into[rowIndex].door_param=atoi(row[21]);
into[rowIndex].invert_state=atoi(row[22]);
into[rowIndex].incline=atoi(row[23]);
into[rowIndex].size=atoi(row[24]);
into[rowIndex].is_ldon_door=atoi(row[25]);
into[rowIndex].client_version_mask = (uint32)strtoul(row[26], nullptr, 10);
}
return true;
}
+3 -3
View File
@@ -24,11 +24,11 @@
#include "../common/packet_functions.h"
#include "petitions.h"
#include "../common/serverinfo.h"
#include "../common/ZoneNumbers.h"
#include "../common/zone_numbers.h"
#include "../common/moremath.h"
#include "../common/guilds.h"
#include "StringIDs.h"
#include "NpcAI.h"
#include "string_ids.h"
#include "npc_ai.h"
float Client::GetActSpellRange(uint16 spell_id, float range, bool IsBard)
{
+3 -3
View File
@@ -20,13 +20,13 @@
#include "../common/debug.h"
#include "../common/seperator.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/features.h"
#include "masterentity.h"
#include "embparser.h"
#include "questmgr.h"
#include "QGlobals.h"
#include "qglobals.h"
#include "zone.h"
#include <algorithm>
#include <sstream>
+2 -2
View File
@@ -20,8 +20,8 @@
#define EQMEU_EMBPARSER_H
#ifdef EMBPERL
#include "QuestParserCollection.h"
#include "QuestInterface.h"
#include "quest_parser_collection.h"
#include "quest_interface.h"
#include <string>
#include <queue>
#include <map>
+35 -1
View File
@@ -22,14 +22,16 @@
#ifdef EMBPERL_XS
#include "../common/debug.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "embparser.h"
#include "questmgr.h"
#include "embxs.h"
#include "entity.h"
#include "zone.h"
#include "queryserv.h"
extern Zone* zone;
extern QueryServ* QServ;
/*
@@ -3370,6 +3372,36 @@ XS(XS__clear_npctype_cache)
XSRETURN_EMPTY;
}
XS(XS__qs_send_query);
XS(XS__qs_send_query)
{
dXSARGS;
if (items != 1){
Perl_croak(aTHX_ "Usage: qs_send_query(query)");
}
else{
// char *Query = (char *)SvPV_nolen(ST(0));
std::string Query = (std::string)SvPV_nolen(ST(0));
QServ->SendQuery(Query);
}
XSRETURN_EMPTY;
}
XS(XS__qs_player_event);
XS(XS__qs_player_event)
{
dXSARGS;
if (items != 2){
Perl_croak(aTHX_ "Usage: qs_player_event(char_id, event_desc)");
}
else{
int char_id = (int)SvIV(ST(0));
std::string event_desc = (std::string)SvPV_nolen(ST(1));
QServ->PlayerLogEvent(Player_Log_Quest, char_id, event_desc);
}
XSRETURN_EMPTY;
}
/*
This is the callback perl will look for to setup the
quest package's XSUBs
@@ -3591,6 +3623,8 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "enablerecipe"), XS__enablerecipe, file);
newXS(strcpy(buf, "disablerecipe"), XS__disablerecipe, file);
newXS(strcpy(buf, "clear_npctype_cache"), XS__clear_npctype_cache, file);
newXS(strcpy(buf, "qs_send_query"), XS__qs_send_query, file);
newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file);
XSRETURN_YES;
}
+18 -3
View File
@@ -39,11 +39,11 @@
#include "petitions.h"
#include "../common/spdat.h"
#include "../common/features.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "../common/dbasync.h"
#include "guild_mgr.h"
#include "raids.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#ifdef _WINDOWS
#define snprintf _snprintf
@@ -622,6 +622,7 @@ void EntityList::AddCorpse(Corpse *corpse, uint32 in_id)
void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
{
npc->SetID(GetFreeID());
npc->SetMerchantProbability((uint8) MakeRandomInt(0, 99));
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
uint16 emoteid = npc->GetEmoteID();
@@ -1597,7 +1598,7 @@ Corpse *EntityList::GetCorpseByName(const char *name)
Spawn2 *EntityList::GetSpawnByID(uint32 id)
{
if (!zone)
if (!zone || !zone->IsLoaded())
return nullptr;
LinkedListIterator<Spawn2 *> iterator(zone->spawn2_list);
@@ -4169,6 +4170,20 @@ void EntityList::SignalAllClients(uint32 data)
}
}
uint16 EntityList::GetClientCount(){
uint16 ClientCount = 0;
std::list<Client*> client_list;
entity_list.GetClientList(client_list);
std::list<Client*>::iterator iter = client_list.begin();
while (iter != client_list.end()) {
Client *entry = (*iter);
entry->GetCleanName();
ClientCount++;
iter++;
}
return ClientCount;
}
void EntityList::GetMobList(std::list<Mob *> &m_list)
{
m_list.clear();
+2 -1
View File
@@ -29,7 +29,7 @@
#include "zonedb.h"
#include "zonedump.h"
#include "zonedbasync.h"
#include "QGlobals.h"
#include "qglobals.h"
class EQApplicationPacket;
@@ -398,6 +398,7 @@ public:
void UpdateFindableNPCState(NPC *n, bool Remove);
void HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode);
uint16 GetClientCount();
void GetMobList(std::list<Mob*> &m_list);
void GetNPCList(std::list<NPC*> &n_list);
void GetMercList(std::list<Merc*> &n_list);
+75 -63
View File
@@ -18,10 +18,13 @@
#include "../common/debug.h"
#include "../common/features.h"
#include "masterentity.h"
#include "StringIDs.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "queryserv.h"
extern QueryServ* QServ;
static uint32 MaxBankedGroupLeadershipPoints(int Level)
@@ -48,6 +51,15 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level)
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
/* Set a timestamp in an entity variable for plugin check_handin.pl in return_items
This will stopgap players from items being returned if global_npc.pl has a catch all return_items
*/
struct timeval read_time;
char buffer[50];
gettimeofday(&read_time, 0);
sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec);
this->SetEntityVariable("Stop_Return", buffer);
uint32 add_exp = in_add_exp;
if(!resexp && (XPRate != 0))
@@ -203,7 +215,6 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
return; // Must be invalid class/race
}
if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) {
if (isrezzexp)
this->Message_StringID(MT_Experience, REZ_REGAIN);
@@ -279,6 +290,14 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
//Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
char val1[20]={0};
Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2.
/* QS: PlayerLogAARate */
if (RuleB(QueryServ, PlayerLogAARate)){
int add_points = (m_pp.aapoints - last_unspentAA);
std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", this->CharacterID(), add_points, add_points);
QServ->SendQuery(query.c_str());
}
//Message(15, "You now have %d skill points available to spend.", m_pp.aapoints);
}
@@ -290,12 +309,10 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
if(check_level > maxlevel) {
check_level = maxlevel;
if(RuleB(Character, KeepLevelOverMax))
{
if(RuleB(Character, KeepLevelOverMax)) {
set_exp = GetEXPForLevel(GetLevel()+1);
}
else
{
else {
set_exp = GetEXPForLevel(maxlevel);
}
}
@@ -305,8 +322,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
if(MaxLevel){
if(GetLevel() >= MaxLevel){
uint32 expneeded = GetEXPForLevel(MaxLevel);
if(set_exp > expneeded)
{
if(set_exp > expneeded) {
set_exp = expneeded;
}
}
@@ -318,11 +334,11 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
if (GetLevel() == check_level-1){
Message_StringID(MT_Experience, GAIN_LEVEL,ConvertArray(check_level,val1));
SendLevelAppearance();
//Message(15, "You have gained a level! Welcome to level %i!", check_level);
/* Message(15, "You have gained a level! Welcome to level %i!", check_level); */
}
if (GetLevel() == check_level){
Message_StringID(MT_Experience, LOSE_LEVEL,ConvertArray(check_level,val1));
//Message(15, "You lost a level! You are now level %i!", check_level);
/* Message(15, "You lost a level! You are now level %i!", check_level); */
}
else
Message(15, "Welcome to level %i!", check_level);
@@ -343,8 +359,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
//If were at max level then stop gaining experience if we make it to the cap
if(GetLevel() == maxlevel - 1){
uint32 expneeded = GetEXPForLevel(maxlevel);
if(set_exp > expneeded)
{
if(set_exp > expneeded) {
set_exp = expneeded;
}
}
@@ -396,15 +411,13 @@ void Client::SetLevel(uint8 set_level, bool command)
level = set_level;
if(IsRaidGrouped())
{
if(IsRaidGrouped()) {
Raid *r = this->GetRaid();
if(r){
r->UpdateLevel(GetName(), set_level);
}
}
if(set_level > m_pp.level2)
{
if(set_level > m_pp.level2) {
if(m_pp.level2 == 0)
m_pp.points += 5;
else
@@ -414,6 +427,18 @@ void Client::SetLevel(uint8 set_level, bool command)
}
if(set_level > m_pp.level) {
parse->EventPlayer(EVENT_LEVEL_UP, this, "", 0);
/* QS: PlayerLogLevels */
if (RuleB(QueryServ, PlayerLogLevels)){
std::string event_desc = StringFormat("Leveled UP :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc);
}
}
else if (set_level < m_pp.level){
/* QS: PlayerLogLevels */
if (RuleB(QueryServ, PlayerLogLevels)){
std::string event_desc = StringFormat("Leveled DOWN :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc);
}
}
m_pp.level = set_level;
@@ -423,34 +448,34 @@ void Client::SetLevel(uint8 set_level, bool command)
lu->exp = 0;
}
else {
float tmpxp = (float) ( (float) m_pp.exp - GetEXPForLevel( GetLevel() )) /
( (float) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel()));
float tmpxp = (float) ( (float) m_pp.exp - GetEXPForLevel( GetLevel() )) / ( (float) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel()));
lu->exp = (uint32)(330.0f * tmpxp);
}
QueuePacket(outapp);
safe_delete(outapp);
this->SendAppearancePacket(AT_WhoLevel, set_level); // who level change
LogFile->write(EQEMuLog::Normal,"Setting Level for %s to %i", GetName(), set_level);
LogFile->write(EQEMuLog::Normal,"Setting Level for %s to %i", GetName(), set_level);
CalcBonuses();
if(!RuleB(Character, HealOnLevel))
{
if(!RuleB(Character, HealOnLevel)) {
int mhp = CalcMaxHP();
if(GetHP() > mhp)
SetHP(mhp);
}
else
{
else {
SetHP(CalcMaxHP()); // Why not, lets give them a free heal
}
DoTributeUpdate();
DoTributeUpdate();
SendHPUpdate();
SetMana(CalcMaxMana());
UpdateWho();
if(GetMerc())
UpdateMercLevel();
if(GetMerc())
UpdateMercLevel();
Save();
}
@@ -499,23 +524,13 @@ uint32 Client::GetEXPForLevel(uint16 check_level)
uint32 finalxp = uint32(base * mod);
finalxp = mod_client_xp_for_level(finalxp, check_level);
return(finalxp);
return finalxp;
}
void Client::AddLevelBasedExp(uint8 exp_percentage, uint8 max_level) {
if (exp_percentage > 100)
{
exp_percentage = 100;
}
if (!max_level || GetLevel() < max_level)
{
max_level = GetLevel();
}
uint32 newexp = GetEXP() + ((GetEXPForLevel(max_level + 1) - GetEXPForLevel(max_level)) * exp_percentage / 100);
void Client::AddLevelBasedExp(uint8 exp_percentage, uint8 max_level) {
if (exp_percentage > 100) { exp_percentage = 100; }
if (!max_level || GetLevel() < max_level) { max_level = GetLevel(); }
uint32 newexp = GetEXP() + ((GetEXPForLevel(max_level + 1) - GetEXPForLevel(max_level)) * exp_percentage / 100);
SetEXP(newexp, GetAAXP());
}
@@ -657,28 +672,25 @@ void Client::SendLeadershipEXPUpdate() {
}
uint32 Client::GetCharMaxLevelFromQGlobal() {
QGlobalCache *char_c = nullptr;
char_c = this->GetQGlobals();
QGlobalCache *char_c = nullptr;
char_c = this->GetQGlobals();
std::list<QGlobal> globalMap;
uint32 ntype = 0;
std::list<QGlobal> globalMap;
uint32 ntype = 0;
if(char_c) {
QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, this->CharacterID(), zone->GetZoneID());
}
if(char_c)
{
QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, this->CharacterID(), zone->GetZoneID());
}
std::list<QGlobal>::iterator iter = globalMap.begin();
uint32 gcount = 0;
while(iter != globalMap.end()) {
if((*iter).name.compare("CharMaxLevel") == 0){
return atoi((*iter).value.c_str());
}
++iter;
++gcount;
}
std::list<QGlobal>::iterator iter = globalMap.begin();
uint32 gcount = 0;
while(iter != globalMap.end())
{
if((*iter).name.compare("CharMaxLevel") == 0){
return atoi((*iter).value.c_str());
}
++iter;
++gcount;
}
return false; // Default is false
return false;
}
+1 -1
View File
@@ -23,7 +23,7 @@
#include <cstdlib>
#include "../common/rulesys.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "map.h"
#include "zone.h"
#include "pathing.h"
+58 -68
View File
@@ -30,9 +30,9 @@
#include "npc.h"
#include "water_map.h"
#include "titles.h"
#include "StringIDs.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "zonedb.h"
@@ -40,7 +40,7 @@
#define snprintf _snprintf
#endif
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
//max number of items which can be in the foraging table
//for a given zone.
@@ -85,12 +85,7 @@ CREATE TABLE fishing (
// This allows EqEmu to have zone specific foraging - BoB
uint32 ZoneDatabase::GetZoneForage(uint32 ZoneID, uint8 skill) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
uint8 index = 0;
uint32 item[FORAGE_ITEM_LIMIT];
uint32 chance[FORAGE_ITEM_LIMIT];
uint32 ret;
@@ -100,31 +95,32 @@ uint32 ZoneDatabase::GetZoneForage(uint32 ZoneID, uint8 skill) {
}
uint32 chancepool = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT itemid,chance FROM forage WHERE zoneid= '%i' and level <= '%i' LIMIT %i", ZoneID, skill, FORAGE_ITEM_LIMIT), errbuf, &result))
{
safe_delete_array(query);
while ((row = mysql_fetch_row(result)) && (index < FORAGE_ITEM_LIMIT)) {
item[index] = atoi(row[0]);
chance[index] = atoi(row[1])+chancepool;
LogFile->write(EQEMuLog::Error, "Possible Forage: %d with a %d chance", item[index], chance[index]);
chancepool = chance[index];
index++;
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in Forage query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT itemid, chance FROM "
"forage WHERE zoneid = '%i' and level <= '%i' "
"LIMIT %i", ZoneID, skill, FORAGE_ITEM_LIMIT);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in Forage query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
uint8 index = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
if (index >= FORAGE_ITEM_LIMIT)
break;
item[index] = atoi(row[0]);
chance[index] = atoi(row[1]) + chancepool;
LogFile->write(EQEMuLog::Error, "Possible Forage: %d with a %d chance", item[index], chance[index]);
chancepool = chance[index];
}
if(chancepool == 0 || index < 1)
return(0);
return 0;
if(index == 1) {
return(item[0]);
return item[0];
}
ret = 0;
@@ -143,12 +139,6 @@ LogFile->write(EQEMuLog::Error, "Possible Forage: %d with a %d chance", item[ind
uint32 ZoneDatabase::GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id, uint8 &npc_chance)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
uint8 index = 0;
uint32 item[50];
uint32 chance[50];
uint32 npc_ids[50];
@@ -161,44 +151,44 @@ uint32 ZoneDatabase::GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id,
chance[c]=0;
}
if (RunQuery(query, MakeAnyLenString(&query, "SELECT itemid,chance,npc_id,npc_chance FROM fishing WHERE (zoneid= '%i' || zoneid = 0) and skill_level <= '%i'",ZoneID, skill ), errbuf, &result))
{
safe_delete_array(query);
while ((row = mysql_fetch_row(result))&&(index<50)) {
item[index] = atoi(row[0]);
chance[index] = atoi(row[1])+chancepool;
chancepool = chance[index];
npc_ids[index] = atoi(row[2]);
npc_chances[index] = atoi(row[3]);
index++;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in Fishing query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT itemid, chance, npc_id, npc_chance "
"FROM fishing WHERE (zoneid = '%i' || zoneid = 0) AND skill_level <= '%i'",
ZoneID, skill);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in Fishing query '" << query << "' " << results.ErrorMessage() << std::endl;
return 0;
}
}
uint8 index = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
if (index >= 50)
break;
item[index] = atoi(row[0]);
chance[index] = atoi(row[1])+chancepool;
chancepool = chance[index];
npc_ids[index] = atoi(row[2]);
npc_chances[index] = atoi(row[3]);
}
npc_id = 0;
npc_chance = 0;
if (index>0) {
uint32 random = MakeRandomInt(1, chancepool);
for (int i = 0; i < index; i++)
{
if (random <= chance[i])
{
ret = item[i];
npc_id = npc_ids[i];
npc_chance = npc_chances[i];
break;
}
}
} else {
ret = 0;
}
if (index <= 0)
return 0;
uint32 random = MakeRandomInt(1, chancepool);
for (int i = 0; i < index; i++)
{
if (random > chance[i])
continue;
ret = item[i];
npc_id = npc_ids[i];
npc_chance = npc_chances[i];
break;
}
return ret;
}
+6 -7
View File
@@ -17,10 +17,10 @@
*/
#include "../common/debug.h"
#include "masterentity.h"
#include "NpcAI.h"
#include "npc_ai.h"
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "worldserver.h"
extern EntityList entity_list;
extern WorldServer worldserver;
@@ -193,11 +193,10 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (members[i] != nullptr && members[i]->IsClient()) { // If Group Member is Client
Client *c = members[i]->CastToClient();
//I could not get MoneyOnCorpse to work, so we use this
c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true);
c->Message(2, msg.c_str());
Client *c = members[i]->CastToClient();
//I could not get MoneyOnCorpse to work, so we use this
c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true);
c->Message(2, msg.c_str());
}
}
}
+4 -4
View File
@@ -25,13 +25,13 @@
#include "../common/packet_functions.h"
#include "petitions.h"
#include "../common/serverinfo.h"
#include "../common/ZoneNumbers.h"
#include "../common/zone_numbers.h"
#include "../common/moremath.h"
#include "../common/guilds.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "guild_mgr.h"
#include "StringIDs.h"
#include "NpcAI.h"
#include "string_ids.h"
#include "npc_ai.h"
extern WorldServer worldserver;
+157 -247
View File
@@ -20,7 +20,7 @@
#include "zonedb.h"
#include "worldserver.h"
#include "../common/servertalk.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "client.h"
#include "entity.h"
@@ -644,104 +644,66 @@ GuildBankManager::~GuildBankManager()
}
}
bool GuildBankManager::Load(uint32 GuildID)
bool GuildBankManager::Load(uint32 guildID)
{
const char *LoadQuery = "SELECT `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `whofor` from `guild_bank` "
"WHERE `guildid` = %i";
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if(database.RunQuery(query, MakeAnyLenString(&query, LoadQuery, GuildID), errbuf, &result))
{
GuildBank *Bank = new GuildBank;
Bank->GuildID = GuildID;
for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i)
Bank->Items.MainArea[i].ItemID = 0;
for(int i = 0; i < GUILD_BANK_DEPOSIT_AREA_SIZE; ++i)
Bank->Items.DepositArea[i].ItemID = 0;
char Donator[64], WhoFor[64];
while((row = mysql_fetch_row(result)))
{
int Area = atoi(row[0]);
int Slot = atoi(row[1]);
int ItemID = atoi(row[2]);
int Qty = atoi(row[3]);
if(row[4])
strn0cpy(Donator, row[4], sizeof(Donator));
else
Donator[0] = '\0';
int Permissions = atoi(row[5]);
if(row[6])
strn0cpy(WhoFor, row[6], sizeof(WhoFor));
else
WhoFor[0] = '\0';
if(Area == GuildBankMainArea)
{
if((Slot >= 0) && (Slot < GUILD_BANK_MAIN_AREA_SIZE))
{
Bank->Items.MainArea[Slot].ItemID = ItemID;
Bank->Items.MainArea[Slot].Quantity = Qty;
strn0cpy(Bank->Items.MainArea[Slot].Donator, Donator, sizeof(Donator));
Bank->Items.MainArea[Slot].Permissions = Permissions;
strn0cpy(Bank->Items.MainArea[Slot].WhoFor, WhoFor, sizeof(WhoFor));
}
}
else
{
if((Slot >= 0 ) && (Slot < GUILD_BANK_DEPOSIT_AREA_SIZE))
{
Bank->Items.DepositArea[Slot].ItemID = ItemID;
Bank->Items.DepositArea[Slot].Quantity = Qty;
strn0cpy(Bank->Items.DepositArea[Slot].Donator, Donator, sizeof(Donator));
Bank->Items.DepositArea[Slot].Permissions = Permissions;
strn0cpy(Bank->Items.DepositArea[Slot].WhoFor, WhoFor, sizeof(WhoFor));
}
}
}
mysql_free_result(result);
safe_delete_array(query);
Banks.push_back(Bank);
}
else
{
_log(GUILDS__BANK_ERROR, "Error Loading guild bank: %s, %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `whofor` "
"FROM `guild_bank` WHERE `guildid` = %i", guildID);
auto results = database.QueryDatabase(query);
if(!results.Success()) {
_log(GUILDS__BANK_ERROR, "Error Loading guild bank: %s, %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
return true;
GuildBank *bank = new GuildBank;
bank->GuildID = guildID;
for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i)
bank->Items.MainArea[i].ItemID = 0;
for(int i = 0; i < GUILD_BANK_DEPOSIT_AREA_SIZE; ++i)
bank->Items.DepositArea[i].ItemID = 0;
char donator[64], whoFor[64];
for (auto row = results.begin(); row != results.end(); ++row)
{
int area = atoi(row[0]);
int slot = atoi(row[1]);
int itemID = atoi(row[2]);
int qty = atoi(row[3]);
if(row[4])
strn0cpy(donator, row[4], sizeof(donator));
else
donator[0] = '\0';
int permissions = atoi(row[5]);
if(row[6])
strn0cpy(whoFor, row[6], sizeof(whoFor));
else
whoFor[0] = '\0';
if(slot < 0 ||
((area != GuildBankMainArea || slot >= GUILD_BANK_MAIN_AREA_SIZE) ||
(area == GuildBankMainArea || slot >= GUILD_BANK_DEPOSIT_AREA_SIZE)))
continue;
bank->Items.MainArea[slot].ItemID = itemID;
bank->Items.MainArea[slot].Quantity = qty;
strn0cpy(bank->Items.MainArea[slot].Donator, donator, sizeof(donator));
bank->Items.MainArea[slot].Permissions = permissions;
strn0cpy(bank->Items.MainArea[slot].WhoFor, whoFor, sizeof(whoFor));
}
Banks.push_back(bank);
return true;
}
bool GuildBankManager::IsLoaded(uint32 GuildID)
@@ -973,156 +935,127 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32
return true;
}
int GuildBankManager::Promote(uint32 GuildID, int SlotID)
int GuildBankManager::Promote(uint32 guildID, int slotID)
{
if((SlotID < 0) || (SlotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1)))
if((slotID < 0) || (slotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1)))
return -1;
std::list<GuildBank*>::iterator Iterator = GetGuildBank(GuildID);
auto iter = GetGuildBank(guildID);
if(Iterator == Banks.end())
{
if(iter == Banks.end())
return -1;
}
if((*Iterator)->Items.DepositArea[SlotID].ItemID == 0)
{
if((*iter)->Items.DepositArea[slotID].ItemID == 0)
return -1;
}
int MainSlot = -1;
int mainSlot = -1;
for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i)
if((*Iterator)->Items.MainArea[i].ItemID == 0)
{
MainSlot = i;
if((*iter)->Items.MainArea[i].ItemID == 0) {
mainSlot = i;
break;
}
if(MainSlot == -1)
if(mainSlot == -1)
return -1;
(*iter)->Items.MainArea[mainSlot].ItemID = (*iter)->Items.DepositArea[slotID].ItemID;
(*iter)->Items.MainArea[mainSlot].Quantity = (*iter)->Items.DepositArea[slotID].Quantity;
(*iter)->Items.MainArea[mainSlot].Permissions = (*iter)->Items.DepositArea[slotID].Permissions;
(*Iterator)->Items.MainArea[MainSlot].ItemID = (*Iterator)->Items.DepositArea[SlotID].ItemID;
(*Iterator)->Items.MainArea[MainSlot].Quantity = (*Iterator)->Items.DepositArea[SlotID].Quantity;
strn0cpy((*Iterator)->Items.MainArea[MainSlot].Donator, (*Iterator)->Items.DepositArea[SlotID].Donator, sizeof((*Iterator)->Items.MainArea[MainSlot].Donator));
(*Iterator)->Items.MainArea[MainSlot].Permissions = (*Iterator)->Items.DepositArea[SlotID].Permissions;
strn0cpy((*Iterator)->Items.MainArea[MainSlot].WhoFor, (*Iterator)->Items.DepositArea[SlotID].WhoFor, sizeof((*Iterator)->Items.MainArea[MainSlot].WhoFor));
const char *Query="UPDATE `guild_bank` SET `area` = 1, `slot` = %i WHERE `guildid` = %i AND `area` = 0 AND `slot` = %i LIMIT 1";
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, MainSlot, GuildID, SlotID), errbuf))
{
_log(GUILDS__BANK_ERROR, "error promoting item: %s : %s", query, errbuf);
safe_delete_array(query);
strn0cpy((*iter)->Items.MainArea[mainSlot].Donator, (*iter)->Items.DepositArea[slotID].Donator, sizeof((*iter)->Items.MainArea[mainSlot].Donator));
strn0cpy((*iter)->Items.MainArea[mainSlot].WhoFor, (*iter)->Items.DepositArea[slotID].WhoFor, sizeof((*iter)->Items.MainArea[mainSlot].WhoFor));
std::string query = StringFormat("UPDATE `guild_bank` SET `area` = 1, `slot` = %i "
"WHERE `guildid` = %i AND `area` = 0 AND `slot` = %i "
"LIMIT 1", mainSlot, guildID, slotID);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
_log(GUILDS__BANK_ERROR, "error promoting item: %s : %s", query.c_str(), results.ErrorMessage().c_str());
return -1;
}
safe_delete_array(query);
(*iter)->Items.DepositArea[slotID].ItemID = 0;
(*Iterator)->Items.DepositArea[SlotID].ItemID = 0;
const Item_Struct *Item = database.GetItem((*Iterator)->Items.MainArea[MainSlot].ItemID);
const Item_Struct *Item = database.GetItem((*iter)->Items.MainArea[mainSlot].ItemID);
GuildBankItemUpdate_Struct gbius;
if(!Item->Stackable)
gbius.Init(GuildBankItemUpdate, 1, MainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, 0, 0, 0);
gbius.Init(GuildBankItemUpdate, 1, mainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, 0, 0, 0);
else
{
if((*Iterator)->Items.MainArea[MainSlot].Quantity == Item->StackSize)
gbius.Init(GuildBankItemUpdate, 1, MainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*Iterator)->Items.MainArea[MainSlot].Quantity, 0, 0, 0);
if((*iter)->Items.MainArea[mainSlot].Quantity == Item->StackSize)
gbius.Init(GuildBankItemUpdate, 1, mainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*iter)->Items.MainArea[mainSlot].Quantity, 0, 0, 0);
else
gbius.Init(GuildBankItemUpdate, 1, MainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*Iterator)->Items.MainArea[MainSlot].Quantity, 0, 1, 0);
gbius.Init(GuildBankItemUpdate, 1, mainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*iter)->Items.MainArea[mainSlot].Quantity, 0, 1, 0);
}
strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName));
entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID);
entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID);
gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankDepositArea, 0, 0, 0, 0, 0, 0, 0);
gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankDepositArea, 0, 0, 0, 0, 0, 0, 0);
entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID);
entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID);
return MainSlot;
return mainSlot;
}
void GuildBankManager::SetPermissions(uint32 GuildID, uint16 SlotID, uint32 Permissions, const char *MemberName)
void GuildBankManager::SetPermissions(uint32 guildID, uint16 slotID, uint32 permissions, const char *memberName)
{
if((SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)))
if((slotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)))
return;
std::list<GuildBank*>::iterator Iterator = GetGuildBank(GuildID);
auto iter = GetGuildBank(guildID);
if(Iterator == Banks.end())
if(iter == Banks.end())
return;
if((*iter)->Items.MainArea[slotID].ItemID == 0)
return;
std::string query = StringFormat("UPDATE `guild_bank` SET `permissions` = %i, `whofor` = '%s' "
"WHERE `guildid` = %i AND `area` = 1 AND `slot` = %i LIMIT 1",
permissions, memberName, guildID, slotID);
auto results = database.QueryDatabase(query);
if(!results.Success())
{
_log(GUILDS__BANK_ERROR, "error changing permissions: %s : %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
if((*Iterator)->Items.MainArea[SlotID].ItemID == 0)
{
return;
}
(*iter)->Items.MainArea[slotID].Permissions = permissions;
const char *Query="UPDATE `guild_bank` SET `permissions` = %i, `whofor` = '%s' WHERE `guildid` = %i AND `area` = 1 AND `slot` = %i LIMIT 1";
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, Permissions, MemberName, GuildID, SlotID), errbuf))
{
_log(GUILDS__BANK_ERROR, "error changing permissions: %s : %s", query, errbuf);
safe_delete_array(query);
return;
}
safe_delete_array(query);
(*Iterator)->Items.MainArea[SlotID].Permissions = Permissions;
if(Permissions == GuildBankSingleMember)
strn0cpy((*Iterator)->Items.MainArea[SlotID].WhoFor, MemberName, sizeof((*Iterator)->Items.MainArea[SlotID].WhoFor));
if(permissions == GuildBankSingleMember)
strn0cpy((*iter)->Items.MainArea[slotID].WhoFor, memberName, sizeof((*iter)->Items.MainArea[slotID].WhoFor));
else
(*Iterator)->Items.MainArea[SlotID].WhoFor[0] = '\0';
(*iter)->Items.MainArea[slotID].WhoFor[0] = '\0';
const Item_Struct *Item = database.GetItem((*Iterator)->Items.MainArea[SlotID].ItemID);
const Item_Struct *Item = database.GetItem((*iter)->Items.MainArea[slotID].ItemID);
GuildBankItemUpdate_Struct gbius;
if(!Item->Stackable)
gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, (*Iterator)->Items.MainArea[SlotID].Permissions, 0, 0);
gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, (*iter)->Items.MainArea[slotID].Permissions, 0, 0);
else
{
if((*Iterator)->Items.MainArea[SlotID].Quantity == Item->StackSize)
gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*Iterator)->Items.MainArea[SlotID].Quantity, (*Iterator)->Items.MainArea[SlotID].Permissions, 0, 0);
if((*iter)->Items.MainArea[slotID].Quantity == Item->StackSize)
gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*iter)->Items.MainArea[slotID].Quantity, (*iter)->Items.MainArea[slotID].Permissions, 0, 0);
else
gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*Iterator)->Items.MainArea[SlotID].Quantity, (*Iterator)->Items.MainArea[SlotID].Permissions, 1, 0);
gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankMainArea, 1, Item->ID, Item->Icon,
(*iter)->Items.MainArea[slotID].Quantity, (*iter)->Items.MainArea[slotID].Permissions, 1, 0);
}
strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName));
strn0cpy(gbius.WhoFor, (*Iterator)->Items.MainArea[SlotID].WhoFor, sizeof(gbius.WhoFor));
strn0cpy(gbius.WhoFor, (*iter)->Items.MainArea[slotID].WhoFor, sizeof(gbius.WhoFor));
entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID);
entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID);
}
ItemInst* GuildBankManager::GetItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity)
@@ -1208,90 +1141,73 @@ std::list<GuildBank*>::iterator GuildBankManager::GetGuildBank(uint32 GuildID)
return Iterator;
}
bool GuildBankManager::DeleteItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity)
bool GuildBankManager::DeleteItem(uint32 guildID, uint16 area, uint16 slotID, uint32 quantity)
{
std::list<GuildBank*>::iterator Iterator = GetGuildBank(GuildID);
auto iter = GetGuildBank(guildID);
if(Iterator == Banks.end())
if(iter == Banks.end())
return false;
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
GuildBankItem* BankArea = nullptr;
if(Area == GuildBankMainArea)
if(area == GuildBankMainArea)
{
if(SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1))
if(slotID > (GUILD_BANK_MAIN_AREA_SIZE - 1))
return false;
BankArea = &(*Iterator)->Items.MainArea[0];
}
else
{
if(SlotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1))
BankArea = &(*iter)->Items.MainArea[0];
} else {
if(slotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1))
return false;
BankArea = &(*Iterator)->Items.DepositArea[0];
BankArea = &(*iter)->Items.DepositArea[0];
}
bool deleted = true;
bool Deleted = true;
const Item_Struct *Item = database.GetItem(BankArea[SlotID].ItemID);
if(!Item->Stackable || (Quantity >= BankArea[SlotID].Quantity))
{
const char *Query = "DELETE from `guild_bank` where `guildid` = %i AND `area` = %i AND `slot` = %i LIMIT 1";
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, SlotID), errbuf))
{
_log(GUILDS__BANK_ERROR, "Delete item failed. %s : %s", query, errbuf);
safe_delete_array(query);
const Item_Struct *Item = database.GetItem(BankArea[slotID].ItemID);
if(!Item->Stackable || (quantity >= BankArea[slotID].Quantity)) {
std::string query = StringFormat("DELETE FROM `guild_bank` WHERE `guildid` = %i "
"AND `area` = %i AND `slot` = %i LIMIT 1",
guildID, area, slotID);
auto results = database.QueryDatabase(query);
if(!results.Success()) {
_log(GUILDS__BANK_ERROR, "Delete item failed. %s : %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
BankArea[SlotID].ItemID = 0;
}
else
{
const char *Query = "UPDATE `guild_bank` SET `qty` = %i where `guildid` = %i AND `area` = %i AND `slot` = %i LIMIT 1";
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, BankArea[SlotID].Quantity - Quantity,
GuildID, Area, SlotID), errbuf))
{
_log(GUILDS__BANK_ERROR, "Update item failed. %s : %s", query, errbuf);
safe_delete_array(query);
BankArea[slotID].ItemID = 0;
} else {
std::string query = StringFormat("UPDATE `guild_bank` SET `qty` = %i WHERE `guildid` = %i "
"AND `area` = %i AND `slot` = %i LIMIT 1",
BankArea[slotID].Quantity - quantity, guildID, area, slotID);
auto results = database.QueryDatabase(query);
if(!results.Success()) {
_log(GUILDS__BANK_ERROR, "Update item failed. %s : %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
BankArea[slotID].Quantity -= quantity;
BankArea[SlotID].Quantity -= Quantity;
Deleted = false;
deleted = false;
}
GuildBankItemUpdate_Struct gbius;
if(!Deleted)
if(!deleted)
{
gbius.Init(GuildBankItemUpdate, 1, SlotID, Area, 1, Item->ID, Item->Icon, BankArea[SlotID].Quantity, BankArea[SlotID].Permissions, 1, 0);
gbius.Init(GuildBankItemUpdate, 1, slotID, area, 1, Item->ID, Item->Icon, BankArea[slotID].Quantity, BankArea[slotID].Permissions, 1, 0);
strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName));
strn0cpy(gbius.WhoFor, BankArea[SlotID].WhoFor, sizeof(gbius.WhoFor));
strn0cpy(gbius.WhoFor, BankArea[slotID].WhoFor, sizeof(gbius.WhoFor));
}
else
gbius.Init(GuildBankItemUpdate, 1, SlotID, Area, 0, 0, 0, 0, 0, 0, 0);
gbius.Init(GuildBankItemUpdate, 1, slotID, area, 0, 0, 0, 0, 0, 0, 0);
entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID);
entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID);
return true;
@@ -1422,26 +1338,20 @@ bool GuildBankManager::SplitStack(uint32 GuildID, uint16 SlotID, uint32 Quantity
return true;
}
void GuildBankManager::UpdateItemQuantity(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity)
void GuildBankManager::UpdateItemQuantity(uint32 guildID, uint16 area, uint16 slotID, uint32 quantity)
{
// Helper method for MergeStacks. Assuming all passed parameters are valid.
//
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
const char *Query = "UPDATE `guild_bank` SET `qty` = %i where `guildid` = %i AND `area` = %i AND `slot` = %i LIMIT 1";
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, Quantity, GuildID, Area, SlotID), errbuf))
{
_log(GUILDS__BANK_ERROR, "Update item quantity failed. %s : %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("UPDATE `guild_bank` SET `qty` = %i "
"WHERE `guildid` = %i AND `area` = %i "
"AND `slot` = %i LIMIT 1",
quantity, guildID, area, slotID);
auto results = database.QueryDatabase(query);
if(!results.Success()) {
_log(GUILDS__BANK_ERROR, "Update item quantity failed. %s : %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
}
bool GuildBankManager::AllowedToWithdraw(uint32 GuildID, uint16 Area, uint16 SlotID, const char *Name)
+3 -3
View File
@@ -23,9 +23,9 @@
#include <list>
#include "masterentity.h"
#include "../common/rulesys.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "hate_list.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "zone.h"
#include "water_map.h"
@@ -311,7 +311,7 @@ Mob *HateList::GetTop(Mob *center)
}
}
if (cur->ent->Sanctuary()) {
if (cur->ent->Sanctuary()) {
if(hate == -1)
{
top = cur->ent;
+43 -62
View File
@@ -18,9 +18,9 @@
#include "../common/debug.h"
#include "masterentity.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "../common/linked_list.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include <math.h>
#include <assert.h>
#include "worldserver.h"
@@ -67,70 +67,51 @@ const NPCType *Horse::GetHorseType(uint16 spell_id) {
const NPCType *Horse::BuildHorseType(uint16 spell_id) {
const char* FileName = spells[spell_id].teleport_zone;
const char* fileName = spells[spell_id].teleport_zone;
char mount_color = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT race,gender,texture,mountspeed FROM horses WHERE filename='%s'", FileName), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
NPCType* npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
strcpy(npc_type->name,"Unclaimed_Mount"); //this should never get used
strcpy(npc_type->special_abilities, "19,1^20,1^24,1");
npc_type->cur_hp = 1;
npc_type->max_hp = 1;
npc_type->race = atoi(row[0]);
npc_type->gender = atoi(row[1]); // Drogmor's are female horses. Yuck.
npc_type->class_ = 1;
npc_type->deity= 1;
npc_type->level = 1;
npc_type->npc_id = 0;
npc_type->loottable_id = 0;
npc_type->texture = atoi(row[2]);
npc_type->helmtexture = atoi(row[2]);
npc_type->runspeed = atof(row[3]);
mount_color = atoi(row[2]);
npc_type->light = 0;
npc_type->STR = 75;
npc_type->STA = 75;
npc_type->DEX = 75;
npc_type->AGI = 75;
npc_type->INT = 75;
npc_type->WIS = 75;
npc_type->CHA = 75;
horses_auto_delete.Insert(npc_type);
mysql_free_result(result);
return(npc_type);
}
else {
LogFile->write(EQEMuLog::Error, "No Database entry for mount: %s, check the horses table", FileName);
//Message(13, "Unable to find data for mount %s", FileName);
safe_delete_array(query);
}
mysql_free_result(result);
return nullptr;
}
else {
LogFile->write(EQEMuLog::Error, "Error in Mount query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT race, gender, texture, mountspeed FROM horses WHERE filename = '%s'", fileName);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in Mount query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return nullptr;
}
if (results.RowCount() != 1) {
LogFile->write(EQEMuLog::Error, "No Database entry for mount: %s, check the horses table", fileName);
return nullptr;
}
auto row = results.begin();
NPCType* npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
strcpy(npc_type->name,"Unclaimed_Mount"); //this should never get used
strcpy(npc_type->special_abilities, "19,1^20,1^24,1");
npc_type->cur_hp = 1;
npc_type->max_hp = 1;
npc_type->race = atoi(row[0]);
npc_type->gender = atoi(row[1]); // Drogmor's are female horses. Yuck.
npc_type->class_ = 1;
npc_type->deity= 1;
npc_type->level = 1;
npc_type->npc_id = 0;
npc_type->loottable_id = 0;
npc_type->texture = atoi(row[2]); // mount color
npc_type->helmtexture = atoi(row[2]); // mount color
npc_type->runspeed = atof(row[3]);
npc_type->light = 0;
npc_type->STR = 75;
npc_type->STA = 75;
npc_type->DEX = 75;
npc_type->AGI = 75;
npc_type->INT = 75;
npc_type->WIS = 75;
npc_type->CHA = 75;
horses_auto_delete.Insert(npc_type);
return npc_type;
}
void Client::SummonHorse(uint16 spell_id) {
+24 -16
View File
@@ -25,14 +25,14 @@
#include "../common/packet_functions.h"
#include "petitions.h"
#include "../common/serverinfo.h"
#include "../common/ZoneNumbers.h"
#include "../common/zone_numbers.h"
#include "../common/moremath.h"
#include "../common/guilds.h"
#include "../common/logsys.h"
#include "../common/StringUtil.h"
#include "StringIDs.h"
#include "NpcAI.h"
#include "QuestParserCollection.h"
#include "../common/string_util.h"
#include "string_ids.h"
#include "npc_ai.h"
#include "quest_parser_collection.h"
extern WorldServer worldserver;
// @merth: this needs to be touched up
@@ -200,6 +200,15 @@ bool Client::CheckLoreConflict(const Item_Struct* item) {
}
bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) {
/* Set a timestamp in an entity variable for plugin check_handin.pl in return_items
This will stopgap players from items being returned if global_npc.pl has a catch all return_items
*/
struct timeval read_time;
char buffer[50];
gettimeofday(&read_time, 0);
sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec);
this->SetEntityVariable("Recieved_Item", buffer);
// TODO: update calling methods and script apis to handle a failure return
const Item_Struct* item = database.GetItem(item_id);
@@ -813,25 +822,24 @@ bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update)
return database.SaveCursor(CharacterID(), s, e);
}
bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update)
{
bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update) {
mlog(INVENTORY__SLOTS, "Putting item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id);
if (slot_id == MainCursor)
{
return PushItemOnCursor(inst,client_update);
}
return PushItemOnCursor(inst, client_update);
else
m_inv.PutItem(slot_id, inst);
if (client_update) {
SendItemPacket(slot_id, &inst, (slot_id == MainCursor) ? ItemPacketSummonItem : ItemPacketTrade);
}
if (client_update)
SendItemPacket(slot_id, &inst, ((slot_id == MainCursor) ? ItemPacketSummonItem : ItemPacketTrade));
if (slot_id == MainCursor) {
std::list<ItemInst*>::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end();
std::list<ItemInst*>::const_iterator s = m_inv.cursor_begin(), e = m_inv.cursor_end();
return database.SaveCursor(this->CharacterID(), s, e);
} else
}
else {
return database.SaveInventory(this->CharacterID(), &inst, slot_id);
}
CalcBonuses();
}
@@ -1530,7 +1538,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
// Also sends trade information to other client of trade session
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
trade->AddEntity(src_slot_id, dst_slot_id);
trade->AddEntity(dst_slot_id, move_in->number_in_stack);
return true;
} else {
+1 -1
View File
@@ -22,7 +22,7 @@
#include "npc.h"
#include "masterentity.h"
#include "zonedb.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#ifdef _WINDOWS
#define snprintf _snprintf
#endif
+1 -1
View File
@@ -1134,7 +1134,7 @@ void Lua_Client::Signal(uint32 id) {
void Lua_Client::AddAlternateCurrencyValue(uint32 currency, int amount) {
Lua_Safe_Call_Void();
self->AddAlternateCurrencyValue(currency, amount);
self->AddAlternateCurrencyValue(currency, amount, 1);
}
void Lua_Client::SendWebLink(const char *site) {
+2 -2
View File
@@ -13,9 +13,9 @@
#include "lua_client.h"
#include "lua_npc.h"
#include "lua_entity_list.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "questmgr.h"
#include "QGlobals.h"
#include "qglobals.h"
#include "../common/timer.h"
struct Events { };
+12 -1
View File
@@ -442,6 +442,15 @@ void Lua_NPC::MerchantCloseShop() {
self->MerchantCloseShop();
}
void Lua_NPC::SetMerchantProbability(uint8 amt) {
Lua_Safe_Call_Void();
self->SetMerchantProbability(amt);
}
uint8 Lua_NPC::GetMerchantProbability() {
Lua_Safe_Call_Int();
return self->GetMerchantProbability();
}
luabind::scope lua_register_npc() {
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
@@ -532,7 +541,9 @@ luabind::scope lua_register_npc() {
.def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount)
.def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore)
.def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop)
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop);
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
.def("SetMerchantProbability", (void(Lua_NPC::*)(void))&Lua_NPC::SetMerchantProbability)
.def("GetMerchantProbability", (uint8(Lua_NPC::*)(void))&Lua_NPC::GetMerchantProbability);
}
#endif
+2
View File
@@ -114,6 +114,8 @@ public:
int GetScore();
void MerchantOpenShop();
void MerchantCloseShop();
void SetMerchantProbability(uint8 amt);
uint8 GetMerchantProbability();
};
#endif
+2 -2
View File
@@ -2,8 +2,8 @@
#define _EQE_LUA_PARSER_H
#ifdef LUA_EQEMU
#include "QuestParserCollection.h"
#include "QuestInterface.h"
#include "quest_parser_collection.h"
#include "quest_interface.h"
#include <string>
#include <list>
#include <map>
+3 -3
View File
@@ -5,12 +5,12 @@
#include <luabind/luabind.hpp>
#include <luabind/object.hpp>
#include "QuestParserCollection.h"
#include "QuestInterface.h"
#include "quest_parser_collection.h"
#include "quest_interface.h"
#include "masterentity.h"
#include "../common/seperator.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_entity.h"
+2 -2
View File
@@ -1,7 +1,7 @@
#include "../common/debug.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "map.h"
#include "RaycastMesh.h"
#include "raycast_mesh.h"
#include "zone.h"
#include <stdint.h>
#include <algorithm>
+92 -99
View File
@@ -1,17 +1,17 @@
#include "merc.h"
#include "masterentity.h"
#include "NpcAI.h"
#include "npc_ai.h"
#include "../common/packet_dump.h"
#include "../common/eq_packet_structs.h"
#include "../common/eq_constants.h"
#include "../common/skills.h"
#include "../common/spdat.h"
#include "zone.h"
#include "StringIDs.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "water_map.h"
extern volatile bool ZoneLoaded;
@@ -2773,7 +2773,7 @@ int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) {
int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (spells[spell_id].targettype == ST_Self)
return value;
@@ -2784,9 +2784,9 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
int chance = RuleI(Spells, BaseCritChance);
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
if (chance > 0){
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
if (MakeRandomInt(1,100) <= chance){
@@ -2801,29 +2801,29 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
}
ratio += RuleI(Spells, WizCritRatio); //Default is zero
if (Critical){
value = value_BaseEffect*ratio/100;
value = value_BaseEffect*ratio/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
if (target) {
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;
value = (value * GetSpellScale() / 100);
value = (value * GetSpellScale() / 100);
entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits,
OTHER_CRIT_BLAST, GetName(), itoa(-value));
@@ -2832,30 +2832,30 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
}
value = value_BaseEffect;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
if (target) {
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
value = (value * GetSpellScale() / 100);
value = (value * GetSpellScale() / 100);
return value;
}
int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if (target == nullptr)
target = this;
@@ -2863,37 +2863,37 @@ int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
int16 chance = 0;
int8 modifier = 1;
bool Critical = false;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
value = value_BaseEffect;
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
// Instant Heals
if(spells[spell_id].buffduration < 1) {
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalHealDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
if(chance && (MakeRandomInt(0,99) < chance)) {
Critical = true;
modifier = 2; //At present time no critical heal amount modifier SPA exists.
}
value *= modifier;
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
value += GetFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
value += GetFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier;
value += value*target->GetHealRate(spell_id, this)/100;
value += value*target->GetHealRate(spell_id, this)/100;
if (Critical)
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value);
@@ -2903,14 +2903,14 @@ int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
//Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value]
else {
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalRegenDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
if(chance && (MakeRandomInt(0,99) < chance))
return (value * 2);
}
@@ -5851,71 +5851,64 @@ void Client::SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02) {
FastQueuePacket(&outapp);
}
void NPC::LoadMercTypes(){
std::string errorMessage;
char* Query = 0;
char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
MYSQL_RES* DatasetResult;
MYSQL_ROW DataRow;
void NPC::LoadMercTypes() {
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT DISTINCT MTyp.dbstring, MTyp.clientversion FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, merc_types MTyp, merc_templates MTem WHERE MME.merchant_id = %i AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id AND MMTE.merc_template_id = MTem.merc_template_id AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()), TempErrorMessageBuffer, &DatasetResult)) {
errorMessage = std::string(TempErrorMessageBuffer);
}
else {
while(DataRow = mysql_fetch_row(DatasetResult)) {
MercType tempMercType;
std::string query = StringFormat("SELECT DISTINCT MTyp.dbstring, MTyp.clientversion "
"FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, "
"merc_types MTyp, merc_templates MTem "
"WHERE MME.merchant_id = %i "
"AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id "
"AND MMTE.merc_template_id = MTem.merc_template_id "
"AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()");
return;
}
tempMercType.Type = atoi(DataRow[0]);
tempMercType.ClientVersion = atoi(DataRow[1]);
for (auto row = results.begin(); row != results.end(); ++row) {
MercType tempMercType;
mercTypeList.push_back(tempMercType);
}
tempMercType.Type = atoi(row[0]);
tempMercType.ClientVersion = atoi(row[1]);
mysql_free_result(DatasetResult);
mercTypeList.push_back(tempMercType);
}
safe_delete_array(Query);
Query = 0;
if(!errorMessage.empty()) {
LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()");
}
}
void NPC::LoadMercs(){
std::string errorMessage;
char* Query = 0;
char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
MYSQL_RES* DatasetResult;
MYSQL_ROW DataRow;
std::string query = StringFormat("SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring AS merc_type_id, "
"MTem.dbstring AS merc_subtype_id, 0 AS CostFormula, "
"CASE WHEN MTem.clientversion > MTyp.clientversion "
"THEN MTem.clientversion "
"ELSE MTyp.clientversion END AS clientversion, MTem.merc_npc_type_id "
"FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, "
"merc_types MTyp, merc_templates MTem "
"WHERE MME.merchant_id = %i AND "
"MME.merc_merchant_template_id = MMTE.merc_merchant_template_id "
"AND MMTE.merc_template_id = MTem.merc_template_id "
"AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()");
return;
}
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring AS merc_type_id, MTem.dbstring AS merc_subtype_id, 0 AS CostFormula, CASE WHEN MTem.clientversion > MTyp.clientversion then MTem.clientversion ELSE MTyp.clientversion END AS clientversion, MTem.merc_npc_type_id FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, merc_types MTyp, merc_templates MTem WHERE MME.merchant_id = %i AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id AND MMTE.merc_template_id = MTem.merc_template_id AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()), TempErrorMessageBuffer, &DatasetResult)) {
errorMessage = std::string(TempErrorMessageBuffer);
}
else {
while(DataRow = mysql_fetch_row(DatasetResult)) {
MercData tempMerc;
for (auto row = results.begin(); row != results.end(); ++row) {
MercData tempMerc;
tempMerc.MercTemplateID = atoi(DataRow[0]);
tempMerc.MercType = atoi(DataRow[1]);
tempMerc.MercSubType = atoi(DataRow[2]);
tempMerc.CostFormula = atoi(DataRow[3]);
tempMerc.ClientVersion = atoi(DataRow[4]);
tempMerc.NPCID = atoi(DataRow[5]);
tempMerc.MercTemplateID = atoi(row[0]);
tempMerc.MercType = atoi(row[1]);
tempMerc.MercSubType = atoi(row[2]);
tempMerc.CostFormula = atoi(row[3]);
tempMerc.ClientVersion = atoi(row[4]);
tempMerc.NPCID = atoi(row[5]);
mercDataList.push_back(tempMerc);
}
mysql_free_result(DatasetResult);
mercDataList.push_back(tempMerc);
}
safe_delete_array(Query);
Query = 0;
if(!errorMessage.empty()) {
LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()");
}
}
int NPC::GetNumMercTypes(uint32 clientVersion)
+58 -79
View File
@@ -18,10 +18,10 @@
#include "../common/debug.h"
#include "masterentity.h"
#include "../common/spdat.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "worldserver.h"
#include "QuestParserCollection.h"
#include "../common/StringUtil.h"
#include "quest_parser_collection.h"
#include "../common/string_util.h"
#include <sstream>
#include <math.h>
@@ -183,6 +183,7 @@ Mob::Mob(const char* in_name,
has_MGB = false;
has_ProjectIllusion = false;
SpellPowerDistanceMod = 0;
last_los_check = false;
if(in_aa_title>0)
aa_title = in_aa_title;
@@ -311,7 +312,7 @@ Mob::Mob(const char* in_name,
shielder[m].shielder_id = 0;
shielder[m].shielder_bonus = 0;
}
destructibleobject = false;
wandertype=0;
pausetype=0;
@@ -341,6 +342,7 @@ Mob::Mob(const char* in_name,
viral_spells[i] = 0;
}
pStandingPetOrder = SPO_Follow;
pseudo_rooted = false;
see_invis = in_see_invis;
see_invis_undead = in_see_invis_undead != 0;
@@ -893,7 +895,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
ns->spawn.NPC = IsClient() ? 0 : 1;
ns->spawn.IsMercenary = (IsMerc() || no_target_hotkey) ? 1 : 0;
ns->spawn.petOwnerId = ownerid;
ns->spawn.haircolor = haircolor;
@@ -2392,7 +2394,7 @@ bool Mob::HateSummon() {
{
if(summon_level == 1) {
entity_list.MessageClose(this, true, 500, MT_Say, "%s says,'You will not evade me, %s!' ", GetCleanName(), target->GetCleanName() );
if (target->IsClient()) {
target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x_pos, y_pos, z_pos, target->GetHeading(), 0, SummonPC);
}
@@ -2404,12 +2406,12 @@ bool Mob::HateSummon() {
target->CastToBot()->SetPreSummonX(target->GetX());
target->CastToBot()->SetPreSummonY(target->GetY());
target->CastToBot()->SetPreSummonZ(target->GetZ());
}
#endif //BOTS
target->GMMove(x_pos, y_pos, z_pos, target->GetHeading());
}
return true;
} else if(summon_level == 2) {
entity_list.MessageClose(this, true, 500, MT_Say, "%s says,'You will not evade me, %s!'", GetCleanName(), target->GetCleanName());
@@ -3040,7 +3042,7 @@ void Mob::TriggerDefensiveProcs(const ItemInst* weapon, Mob *on, uint16 hand, in
case (-1):
skillinuse = SkillBlock;
break;
case (-2):
skillinuse = SkillParry;
break;
@@ -3273,7 +3275,7 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
if (!spellbonuses.TriggerOnValueAmount)
return;
if (spellbonuses.TriggerOnValueAmount){
int buff_count = GetMaxTotalSlots();
@@ -3294,15 +3296,15 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
if (IsHP){
if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5)
use_spell = true;
else if (base2 = 1004 && GetHPRatio() < 80)
use_spell = true;
}
else if (IsMana){
if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40))
if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40))
use_spell = true;
else if (base2 = 38311 && GetManaRatio() < 10)
use_spell = true;
}
@@ -3322,7 +3324,7 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
if (use_spell){
SpellFinished(spells[spell_id].base[i], this, 10, 0, -1, spells[spell_id].ResistDiff);
if(!TryFadeEffect(e))
BuffFadeBySlot(e);
}
@@ -3382,7 +3384,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
if (!caster)
return 0;
int32 value = 0;
//Apply innate vulnerabilities
@@ -3395,7 +3397,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
//Apply spell derived vulnerabilities
if (spellbonuses.FocusEffects[focusSpellVulnerability]){
int32 tmp_focus = 0;
int tmp_buffslot = -1;
@@ -3456,15 +3458,15 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used)
}
int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) {
int16 heal_rate = 0;
heal_rate += itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate;
heal_rate += GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, caster, spell_id);
heal_rate += itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate;
heal_rate += GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, caster, spell_id);
if(heal_rate < -99)
heal_rate = -99;
return heal_rate;
}
@@ -3474,7 +3476,7 @@ bool Mob::TryFadeEffect(int slot)
{
for(int i = 0; i < EFFECT_COUNT; i++)
{
if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways ||
if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways ||
spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect)
{
uint16 spell_id = spells[buffs[slot].spellid].base[i];
@@ -3532,7 +3534,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
else
SpellFinished(focus_trigger, target, 10, 0, -1, spells[focus_trigger].ResistDiff);
}
CheckNumHitsRemaining(NUMHIT_MatchingSpells, 0, focus_spell);
}
}
@@ -3928,36 +3930,26 @@ void Mob::TarGlobal(const char *varname, const char *value, const char *duration
}
void Mob::DelGlobal(const char *varname) {
// delglobal(varname)
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
int qgZoneid=zone->GetZoneID();
int qgCharid=0;
int qgNpcid=0;
if (this->IsNPC())
{
qgNpcid = this->GetNPCTypeID();
}
if (this->IsClient())
{
qgCharid = this->CastToClient()->CharacterID();
}
else
{
qgCharid = -qgNpcid; // make char id negative npc id as a fudge
}
if (!database.RunQuery(query,
MakeAnyLenString(&query,
"DELETE FROM quest_globals WHERE name='%s'"
" && (npcid=0 || npcid=%i) && (charid=0 || charid=%i) && (zoneid=%i || zoneid=0)",
varname,qgNpcid,qgCharid,qgZoneid),errbuf))
{
//_log(QUESTS, "DelGlobal error deleting %s : %s", varname, errbuf);
}
safe_delete_array(query);
std::string query = StringFormat("DELETE FROM quest_globals "
"WHERE name='%s' && (npcid=0 || npcid=%i) "
"&& (charid=0 || charid=%i) "
"&& (zoneid=%i || zoneid=0)",
varname, qgNpcid, qgCharid, qgZoneid);
database.QueryDatabase(query);
if(zone)
{
@@ -3980,33 +3972,22 @@ void Mob::DelGlobal(const char *varname) {
// Inserts global variable into quest_globals table
void Mob::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varname, const char *varvalue, int duration) {
char *query = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
// Make duration string either "unix_timestamp(now()) + xxx" or "NULL"
std::stringstream duration_ss;
if (duration == INT_MAX)
{
duration_ss << "NULL";
}
else
{
duration_ss << "unix_timestamp(now()) + " << duration;
}
//NOTE: this should be escaping the contents of arglist
//npcwise a malicious script can arbitrarily alter the DB
uint32 last_id = 0;
if (!database.RunQuery(query, MakeAnyLenString(&query,
"REPLACE INTO quest_globals (charid, npcid, zoneid, name, value, expdate)"
"VALUES (%i, %i, %i, '%s', '%s', %s)",
charid, npcid, zoneid, varname, varvalue, duration_ss.str().c_str()
), errbuf))
{
//_log(QUESTS, "SelGlobal error inserting %s : %s", varname, errbuf);
}
safe_delete_array(query);
std::string query = StringFormat("REPLACE INTO quest_globals "
"(charid, npcid, zoneid, name, value, expdate)"
"VALUES (%i, %i, %i, '%s', '%s', %s)",
charid, npcid, zoneid, varname, varvalue, duration_ss.str().c_str());
database.QueryDatabase(query);
if(zone)
{
@@ -4032,14 +4013,12 @@ void Mob::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varna
qgu->npc_id = npcid;
qgu->char_id = charid;
qgu->zone_id = zoneid;
if(duration == INT_MAX)
{
qgu->expdate = 0xFFFFFFFF;
}
else
{
qgu->expdate = Timer::GetTimeSeconds() + duration;
}
strcpy((char*)qgu->name, varname);
strcpy((char*)qgu->value, varvalue);
qgu->id = last_id;
@@ -4362,7 +4341,7 @@ int16 Mob::GetSkillDmgAmt(uint16 skill)
}
void Mob::MeleeLifeTap(int32 damage) {
int16 lifetap_amt = 0;
lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap + aabonuses.MeleeLifetap
+ spellbonuses.Vampirism + itembonuses.Vampirism + aabonuses.Vampirism;
@@ -4371,7 +4350,7 @@ void Mob::MeleeLifeTap(int32 damage) {
lifetap_amt = damage * lifetap_amt / 100;
mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage);
if (lifetap_amt > 0)
HealDamage(lifetap_amt); //Heal self for modified damage amount.
else
@@ -4383,9 +4362,9 @@ bool Mob::TryReflectSpell(uint32 spell_id)
{
if (!spells[spell_id].reflectable)
return false;
int chance = itembonuses.reflect_chance + spellbonuses.reflect_chance + aabonuses.reflect_chance;
if(chance && MakeRandomInt(0, 99) < chance)
return true;
@@ -4403,12 +4382,12 @@ void Mob::SpellProjectileEffect()
}
Mob* target = entity_list.GetMobID(projectile_target_id[i]);
float dist = 0;
if (target)
if (target)
dist = target->CalculateDistance(projectile_x[i], projectile_y[i], projectile_z[i]);
int increment_end = 0;
increment_end = (dist / 10) - 1; //This pretty accurately determines end time for speed for 1.5 and timer of 250 ms
@@ -4681,7 +4660,7 @@ void Mob::SlowMitigation(Mob* caster)
else if ((GetSlowMitigation() >= 74) && (GetSlowMitigation() < 101))
caster->Message_StringID(MT_SpellFailure, SLOW_SLIGHTLY_SUCCESSFUL);
else if (GetSlowMitigation() > 100)
else if (GetSlowMitigation() > 100)
caster->Message_StringID(MT_SpellFailure, SPELL_OPPOSITE_EFFECT);
}
}
@@ -4758,10 +4737,10 @@ bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) {
}
uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) {
uint16 weapon_speed = 0;
switch (hand) {
case 13:
weapon_speed = attack_timer.GetDuration();
break;
@@ -4784,7 +4763,7 @@ int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) {
if (!IsValidSpell(spell_id))
return false;
int spell_level = spells[spell_id].classes[(GetClass()%16) - 1];
int spell_level = spells[spell_id].classes[(GetClass()%16) - 1];
int effect_value = 0;
int lvlModifier = 100;
@@ -4793,16 +4772,16 @@ int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) {
if (IsValidSpell(buffs[slot].spellid)){
for (int i = 0; i < EFFECT_COUNT; i++){
if(spells[buffs[slot].spellid].effectid[i] == spelleffect) {
int critchance = spells[buffs[slot].spellid].base[i];
int critchance = spells[buffs[slot].spellid].base[i];
int decay = spells[buffs[slot].spellid].base2[i];
int lvldiff = spell_level - spells[buffs[slot].spellid].max[i];
int lvldiff = spell_level - spells[buffs[slot].spellid].max[i];
if(lvldiff > 0 && decay > 0)
{
lvlModifier -= decay*lvldiff;
lvlModifier -= decay*lvldiff;
if (lvlModifier > 0){
critchance = (critchance*lvlModifier)/100;
critchance = (critchance*lvlModifier)/100;
effect_value += critchance;
}
}
@@ -4813,7 +4792,7 @@ int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) {
}
}
}
return effect_value;
}
+6 -1
View File
@@ -464,6 +464,8 @@ public:
bool CheckLosFN(float posX, float posY, float posZ, float mobSize);
inline void SetChanged() { pLastChange = Timer::GetCurrentTime(); }
inline const uint32 LastChange() const { return pLastChange; }
inline void SetLastLosState(bool value) { last_los_check = value; }
inline bool CheckLastLosState() const { return last_los_check; }
//Quest
void QuestReward(Client *c = nullptr, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0);
@@ -752,7 +754,8 @@ public:
inline const bool IsRooted() const { return rooted || permarooted; }
inline const bool HasVirus() const { return has_virus; }
int GetSnaredAmount();
inline const bool IsPseudoRooted() const { return pseudo_rooted; }
inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; }
int GetCurWp() { return cur_wp; }
@@ -1119,6 +1122,8 @@ protected:
bool has_MGB;
bool has_ProjectIllusion;
int16 SpellPowerDistanceMod;
bool last_los_check;
bool pseudo_rooted;
// Bind wound
Timer bindwound_timer;
+221 -223
View File
@@ -23,15 +23,15 @@
#include <algorithm>
#include "npc.h"
#include "masterentity.h"
#include "NpcAI.h"
#include "npc_ai.h"
#include "map.h"
#include "../common/moremath.h"
#include "StringIDs.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "../common/features.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "water_map.h"
extern EntityList entity_list;
@@ -541,13 +541,40 @@ void NPC::AI_Start(uint32 iMoveDelay) {
void Mob::AI_Stop() {
if (!IsAIControlled())
return;
pAIControlled = false;
safe_delete(AIthink_timer);
safe_delete(AIwalking_timer);
safe_delete(AImovement_timer);
safe_delete(AItarget_check_timer)
safe_delete(AItarget_check_timer);
safe_delete(AIscanarea_timer);
safe_delete(AIfeignremember_timer);
safe_delete(PathingLOSCheckTimer);
safe_delete(PathingRouteUpdateTimerShort);
safe_delete(PathingRouteUpdateTimerLong);
attack_timer.Disable();
attack_dw_timer.Disable();
ranged_timer.Disable();
tic_timer.Disable();
mana_timer.Disable();
spellend_timer.Disable();
projectile_timer.Disable();
rewind_timer.Disable();
bindwound_timer.Disable();
stunned_timer.Disable();
spun_timer.Disable();
bardsong_timer.Disable();
gravity_timer.Disable();
viral_timer.Disable();
flee_timer.Disable();
for (int sat = 0; sat < MAX_SPECIAL_ATTACK; ++sat) {
if (SpecialAbilities[sat].timer)
SpecialAbilities[sat].timer->Disable();
}
hate_list.Wipe();
}
@@ -1181,7 +1208,7 @@ void Mob::AI_Process() {
if (GetSpecialAbility(SPECATK_FLURRY)) {
int flurry_chance = GetSpecialAbilityParam(SPECATK_FLURRY, 0);
flurry_chance = flurry_chance > 0 ? flurry_chance : RuleI(Combat, NPCFlurryChance);
flurry_chance = flurry_chance > 0 ? flurry_chance : RuleI(Combat, NPCFlurryChance);
if (MakeRandomInt(0, 99) < flurry_chance) {
ExtraAttackOptions opts;
@@ -1215,16 +1242,16 @@ void Mob::AI_Process() {
if (IsPet() || (IsNPC() && CastToNPC()->GetSwarmOwner())) {
Mob *owner = nullptr;
if (IsPet())
owner = GetOwner();
else
else
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
if (owner) {
int16 flurry_chance = owner->aabonuses.PetFlurry +
owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry;
if (flurry_chance && (MakeRandomInt(0, 99) < flurry_chance))
Flurry(nullptr);
}
@@ -1569,11 +1596,7 @@ void Mob::AI_Process() {
//Do Ranged attack here
if(doranged)
{
int attacks = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0);
attacks = attacks > 0 ? attacks : 1;
for(int i = 0; i < attacks; ++i) {
RangedAttack(target);
}
RangedAttack(target);
}
}
@@ -1604,7 +1627,7 @@ void NPC::AI_DoMovement() {
roambox_movingto_x -= movex * 2;
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y -= movey * 2;
//New coord is still invalid, ignore distance and just pick a new random coord.
//New coord is still invalid, ignore distance and just pick a new random coord.
//If we're here we may have a roambox where one side is shorter than the specified distance. Commons, Wkarana, etc.
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x = MakeRandomFloat(roambox_min_x+1,roambox_max_x-1);
@@ -2406,7 +2429,7 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
}
//If any casting variables are defined in the current list, ignore those in the parent list.
if (spell_list->fail_recast || spell_list->engaged_no_sp_recast_min || spell_list->engaged_no_sp_recast_max
if (spell_list->fail_recast || spell_list->engaged_no_sp_recast_min || spell_list->engaged_no_sp_recast_max
|| spell_list->engaged_beneficial_self_chance || spell_list->engaged_beneficial_other_chance || spell_list->engaged_detrimental_chance
|| spell_list->pursue_no_sp_recast_min || spell_list->pursue_no_sp_recast_max || spell_list->pursue_detrimental_chance
|| spell_list->idle_no_sp_recast_min || spell_list->idle_no_sp_recast_max || spell_list->idle_beneficial_chance) {
@@ -2457,7 +2480,7 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
AISpellVar.idle_no_sp_recast_min = (_idle_no_sp_recast_min) ? _idle_no_sp_recast_min : RuleI(Spells, AI_IdleNoSpellMinRecast);
AISpellVar.idle_no_sp_recast_max = (_idle_no_sp_recast_max) ? _idle_no_sp_recast_max : RuleI(Spells, AI_IdleNoSpellMaxRecast);
AISpellVar.idle_beneficial_chance = (_idle_beneficial_chance) ? _idle_beneficial_chance : RuleI(Spells, AI_IdleBeneficialChance);
if (AIspells.size() == 0)
AIautocastspell_timer->Disable();
else
@@ -2469,12 +2492,12 @@ bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) {
npc_spells_effects_id = iDBSpellsEffectsID;
AIspellsEffects.clear();
if (iDBSpellsEffectsID == 0)
if (iDBSpellsEffectsID == 0)
return false;
DBnpcspellseffects_Struct* spell_effects_list = database.GetNPCSpellsEffects(iDBSpellsEffectsID);
if (!spell_effects_list) {
return false;
}
@@ -2502,9 +2525,9 @@ bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) {
if (parentlist) {
for (i=0; i<parentlist->numentries; i++) {
if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spelleffectid > 0) {
if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base,
if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base,
parentlist->entries[i].limit, parentlist->entries[i].max))
{
{
AddSpellEffectToNPCList(parentlist->entries[i].spelleffectid,
parentlist->entries[i].base, parentlist->entries[i].limit,
parentlist->entries[i].max);
@@ -2528,10 +2551,10 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon)
{
if (!AI_HasSpellsEffects())
return;
for(int i=0; i < AIspellsEffects.size(); i++)
{
ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1,
ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1,
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
}
@@ -2541,10 +2564,10 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon)
// adds a spell to the list, taking into account priority and resorting list as needed.
void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max)
{
if(!iSpellEffectID)
return;
HasAISpellEffects = true;
AISpellsEffects_Struct t;
@@ -2627,7 +2650,7 @@ void NPC::AISpellsList(Client *c)
DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
if (iDBSpellsID == 0)
return 0;
return nullptr;
if (!npc_spells_cache) {
npc_spells_maxid = GetMaxNPCSpellsID();
@@ -2640,144 +2663,134 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
}
if (iDBSpellsID > npc_spells_maxid)
return 0;
return nullptr;
if (npc_spells_cache[iDBSpellsID]) { // it's in the cache, easy =)
return npc_spells_cache[iDBSpellsID];
}
else if (!npc_spells_loadtried[iDBSpellsID]) { // no reason to ask the DB again if we have failed once already
npc_spells_loadtried[iDBSpellsID] = true;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list, attack_proc, proc_chance, range_proc, rproc_chance, defensive_proc, dproc_chance, fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, pursue_no_sp_recast_min, pursue_no_sp_recast_max, pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, idle_b_chance from npc_spells where id=%d", iDBSpellsID), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
uint32 tmpparent_list = atoi(row[1]);
uint16 tmpattack_proc = atoi(row[2]);
uint8 tmpproc_chance = atoi(row[3]);
uint16 tmprange_proc = atoi(row[4]);
int16 tmprproc_chance = atoi(row[5]);
uint16 tmpdefensive_proc = atoi(row[6]);
int16 tmpdproc_chance = atoi(row[7]);
uint32 tmppfail_recast = atoi(row[8]);
uint32 tmpengaged_no_sp_recast_min = atoi(row[9]);
uint32 tmpengaged_no_sp_recast_max = atoi(row[10]);
uint8 tmpengaged_b_self_chance = atoi(row[11]);
uint8 tmpengaged_b_other_chance = atoi(row[12]);
uint8 tmpengaged_d_chance = atoi(row[13]);
uint32 tmppursue_no_sp_recast_min = atoi(row[14]);
uint32 tmppursue_no_sp_recast_max = atoi(row[15]);
uint8 tmppursue_d_chance = atoi(row[16]);
uint32 tmpidle_no_sp_recast_min = atoi(row[17]);
uint32 tmpidle_no_sp_recast_max = atoi(row[18]);
uint8 tmpidle_b_chance = atoi(row[19]);
mysql_free_result(result);
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spellid, type, minlevel, maxlevel, manacost, recast_delay, priority, resist_adjust from npc_spells_entries where npc_spells_id=%d ORDER BY minlevel", iDBSpellsID), errbuf, &result)) {
safe_delete_array(query);
uint32 tmpSize = sizeof(DBnpcspells_Struct) + (sizeof(DBnpcspells_entries_Struct) * mysql_num_rows(result));
npc_spells_cache[iDBSpellsID] = (DBnpcspells_Struct*) new uchar[tmpSize];
memset(npc_spells_cache[iDBSpellsID], 0, tmpSize);
npc_spells_cache[iDBSpellsID]->parent_list = tmpparent_list;
npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc;
npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance;
npc_spells_cache[iDBSpellsID]->range_proc = tmprange_proc;
npc_spells_cache[iDBSpellsID]->rproc_chance = tmpdproc_chance;
npc_spells_cache[iDBSpellsID]->defensive_proc = tmpdefensive_proc;
npc_spells_cache[iDBSpellsID]->dproc_chance = tmpdproc_chance;
npc_spells_cache[iDBSpellsID]->fail_recast = tmppfail_recast;
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_min = tmpengaged_no_sp_recast_min;
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_max = tmpengaged_no_sp_recast_max;
npc_spells_cache[iDBSpellsID]->engaged_beneficial_self_chance = tmpengaged_b_self_chance;
npc_spells_cache[iDBSpellsID]->engaged_beneficial_other_chance = tmpengaged_b_other_chance;
npc_spells_cache[iDBSpellsID]->engaged_detrimental_chance = tmpengaged_d_chance;
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_min = tmppursue_no_sp_recast_min;
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_max = tmppursue_no_sp_recast_max;
npc_spells_cache[iDBSpellsID]->pursue_detrimental_chance = tmppursue_d_chance;
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_min = tmpidle_no_sp_recast_min;
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_max = tmpidle_no_sp_recast_max;
npc_spells_cache[iDBSpellsID]->idle_beneficial_chance = tmpidle_b_chance;
npc_spells_cache[iDBSpellsID]->numentries = mysql_num_rows(result);
int j = 0;
while ((row = mysql_fetch_row(result))) {
int spell_id = atoi(row[0]);
npc_spells_cache[iDBSpellsID]->entries[j].spellid = spell_id;
npc_spells_cache[iDBSpellsID]->entries[j].type = atoi(row[1]);
npc_spells_cache[iDBSpellsID]->entries[j].minlevel = atoi(row[2]);
npc_spells_cache[iDBSpellsID]->entries[j].maxlevel = atoi(row[3]);
npc_spells_cache[iDBSpellsID]->entries[j].manacost = atoi(row[4]);
npc_spells_cache[iDBSpellsID]->entries[j].recast_delay = atoi(row[5]);
npc_spells_cache[iDBSpellsID]->entries[j].priority = atoi(row[6]);
if(row[7])
{
npc_spells_cache[iDBSpellsID]->entries[j].resist_adjust = atoi(row[7]);
}
else
{
if(IsValidSpell(spell_id))
{
npc_spells_cache[iDBSpellsID]->entries[j].resist_adjust = spells[spell_id].ResistDiff;
}
}
j++;
}
mysql_free_result(result);
return npc_spells_cache[iDBSpellsID];
}
else {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return 0;
}
}
else {
mysql_free_result(result);
}
}
else {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return 0;
}
std::string query = StringFormat("SELECT id, parent_list, attack_proc, proc_chance, "
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
"pursue_no_sp_recast_min, pursue_no_sp_recast_max, "
"pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, "
"idle_b_chance FROM npc_spells WHERE id=%d", iDBSpellsID);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << results.ErrorMessage() << std::endl;
return nullptr;
}
return 0;
}
return 0;
if (results.RowCount() != 1)
return nullptr;
auto row = results.begin();
uint32 tmpparent_list = atoi(row[1]);
uint16 tmpattack_proc = atoi(row[2]);
uint8 tmpproc_chance = atoi(row[3]);
uint16 tmprange_proc = atoi(row[4]);
int16 tmprproc_chance = atoi(row[5]);
uint16 tmpdefensive_proc = atoi(row[6]);
int16 tmpdproc_chance = atoi(row[7]);
uint32 tmppfail_recast = atoi(row[8]);
uint32 tmpengaged_no_sp_recast_min = atoi(row[9]);
uint32 tmpengaged_no_sp_recast_max = atoi(row[10]);
uint8 tmpengaged_b_self_chance = atoi(row[11]);
uint8 tmpengaged_b_other_chance = atoi(row[12]);
uint8 tmpengaged_d_chance = atoi(row[13]);
uint32 tmppursue_no_sp_recast_min = atoi(row[14]);
uint32 tmppursue_no_sp_recast_max = atoi(row[15]);
uint8 tmppursue_d_chance = atoi(row[16]);
uint32 tmpidle_no_sp_recast_min = atoi(row[17]);
uint32 tmpidle_no_sp_recast_max = atoi(row[18]);
uint8 tmpidle_b_chance = atoi(row[19]);
query = StringFormat("SELECT spellid, type, minlevel, maxlevel, "
"manacost, recast_delay, priority, resist_adjust "
"FROM npc_spells_entries "
"WHERE npc_spells_id=%d ORDER BY minlevel", iDBSpellsID);
results = QueryDatabase(query);
if (!results.Success())
{
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << results.ErrorMessage() << std::endl;
return nullptr;
}
uint32 tmpSize = sizeof(DBnpcspells_Struct) + (sizeof(DBnpcspells_entries_Struct) * results.RowCount());
npc_spells_cache[iDBSpellsID] = (DBnpcspells_Struct*) new uchar[tmpSize];
memset(npc_spells_cache[iDBSpellsID], 0, tmpSize);
npc_spells_cache[iDBSpellsID]->parent_list = tmpparent_list;
npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc;
npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance;
npc_spells_cache[iDBSpellsID]->range_proc = tmprange_proc;
npc_spells_cache[iDBSpellsID]->rproc_chance = tmpdproc_chance;
npc_spells_cache[iDBSpellsID]->defensive_proc = tmpdefensive_proc;
npc_spells_cache[iDBSpellsID]->dproc_chance = tmpdproc_chance;
npc_spells_cache[iDBSpellsID]->fail_recast = tmppfail_recast;
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_min = tmpengaged_no_sp_recast_min;
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_max = tmpengaged_no_sp_recast_max;
npc_spells_cache[iDBSpellsID]->engaged_beneficial_self_chance = tmpengaged_b_self_chance;
npc_spells_cache[iDBSpellsID]->engaged_beneficial_other_chance = tmpengaged_b_other_chance;
npc_spells_cache[iDBSpellsID]->engaged_detrimental_chance = tmpengaged_d_chance;
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_min = tmppursue_no_sp_recast_min;
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_max = tmppursue_no_sp_recast_max;
npc_spells_cache[iDBSpellsID]->pursue_detrimental_chance = tmppursue_d_chance;
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_min = tmpidle_no_sp_recast_min;
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_max = tmpidle_no_sp_recast_max;
npc_spells_cache[iDBSpellsID]->idle_beneficial_chance = tmpidle_b_chance;
npc_spells_cache[iDBSpellsID]->numentries = results.RowCount();
int entryIndex = 0;
for (row = results.begin(); row != results.end(); ++row, ++entryIndex)
{
int spell_id = atoi(row[0]);
npc_spells_cache[iDBSpellsID]->entries[entryIndex].spellid = spell_id;
npc_spells_cache[iDBSpellsID]->entries[entryIndex].type = atoi(row[1]);
npc_spells_cache[iDBSpellsID]->entries[entryIndex].minlevel = atoi(row[2]);
npc_spells_cache[iDBSpellsID]->entries[entryIndex].maxlevel = atoi(row[3]);
npc_spells_cache[iDBSpellsID]->entries[entryIndex].manacost = atoi(row[4]);
npc_spells_cache[iDBSpellsID]->entries[entryIndex].recast_delay = atoi(row[5]);
npc_spells_cache[iDBSpellsID]->entries[entryIndex].priority = atoi(row[6]);
if(row[7])
npc_spells_cache[iDBSpellsID]->entries[entryIndex].resist_adjust = atoi(row[7]);
else if(IsValidSpell(spell_id))
npc_spells_cache[iDBSpellsID]->entries[entryIndex].resist_adjust = spells[spell_id].ResistDiff;
}
return npc_spells_cache[iDBSpellsID];
}
return nullptr;
}
uint32 ZoneDatabase::GetMaxNPCSpellsID() {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells"), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
uint32 ret = 0;
if (row[0])
ret = atoi(row[0]);
mysql_free_result(result);
return ret;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in GetMaxNPCSpellsID query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = "SELECT max(id) from npc_spells";
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetMaxNPCSpellsID query '" << query << "' " << results.ErrorMessage() << std::endl;
return 0;
}
return 0;
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
if (!row[0])
return 0;
return atoi(row[0]);
}
DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEffectsID) {
if (iDBSpellsEffectsID == 0)
return 0;
return nullptr;
if (!npc_spellseffects_cache) {
npc_spellseffects_maxid = GetMaxNPCSpellsEffectsID();
@@ -2790,89 +2803,74 @@ DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEff
}
if (iDBSpellsEffectsID > npc_spellseffects_maxid)
return 0;
if (npc_spellseffects_cache[iDBSpellsEffectsID]) { // it's in the cache, easy =)
return nullptr;
if (npc_spellseffects_cache[iDBSpellsEffectsID]) // it's in the cache, easy =)
return npc_spellseffects_cache[iDBSpellsEffectsID];
}
else if (!npc_spellseffects_loadtried[iDBSpellsEffectsID]) { // no reason to ask the DB again if we have failed once already
npc_spellseffects_loadtried[iDBSpellsEffectsID] = true;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (npc_spellseffects_loadtried[iDBSpellsEffectsID])
return nullptr;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list from npc_spells_effects where id=%d", iDBSpellsEffectsID), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
uint32 tmpparent_list = atoi(row[1]);
mysql_free_result(result);
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_effect_id, minlevel, maxlevel,se_base, se_limit, se_max from npc_spells_effects_entries where npc_spells_effects_id=%d ORDER BY minlevel", iDBSpellsEffectsID), errbuf, &result)) {
safe_delete_array(query);
uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_entries_Struct) * mysql_num_rows(result));
npc_spellseffects_cache[iDBSpellsEffectsID] = (DBnpcspellseffects_Struct*) new uchar[tmpSize];
memset(npc_spellseffects_cache[iDBSpellsEffectsID], 0, tmpSize);
npc_spellseffects_cache[iDBSpellsEffectsID]->parent_list = tmpparent_list;
npc_spellseffects_cache[iDBSpellsEffectsID]->numentries = mysql_num_rows(result);
int j = 0;
while ((row = mysql_fetch_row(result))) {
int spell_effect_id = atoi(row[0]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].spelleffectid = spell_effect_id;
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].minlevel = atoi(row[1]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].maxlevel = atoi(row[2]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].base = atoi(row[3]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].limit = atoi(row[4]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].max = atoi(row[5]);
j++;
}
mysql_free_result(result);
return npc_spellseffects_cache[iDBSpellsEffectsID];
}
else {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return 0;
}
}
else {
mysql_free_result(result);
}
}
else {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return 0;
}
return 0;
}
return 0;
npc_spellseffects_loadtried[iDBSpellsEffectsID] = true;
std::string query = StringFormat("SELECT id, parent_list FROM npc_spells_effects WHERE id=%d", iDBSpellsEffectsID);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << results.ErrorMessage() << std::endl;
return nullptr;
}
if (results.RowCount() != 1)
return nullptr;
auto row = results.begin();
uint32 tmpparent_list = atoi(row[1]);
query = StringFormat("SELECT spell_effect_id, minlevel, "
"maxlevel,se_base, se_limit, se_max "
"FROM npc_spells_effects_entries "
"WHERE npc_spells_effects_id = %d ORDER BY minlevel", iDBSpellsEffectsID);
results = QueryDatabase(query);
if (!results.Success())
return nullptr;
uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_entries_Struct) * results.RowCount());
npc_spellseffects_cache[iDBSpellsEffectsID] = (DBnpcspellseffects_Struct*) new uchar[tmpSize];
memset(npc_spellseffects_cache[iDBSpellsEffectsID], 0, tmpSize);
npc_spellseffects_cache[iDBSpellsEffectsID]->parent_list = tmpparent_list;
npc_spellseffects_cache[iDBSpellsEffectsID]->numentries = results.RowCount();
int entryIndex = 0;
for (row = results.begin(); row != results.end(); ++row, ++entryIndex)
{
int spell_effect_id = atoi(row[0]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].spelleffectid = spell_effect_id;
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].minlevel = atoi(row[1]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].maxlevel = atoi(row[2]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].base = atoi(row[3]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].limit = atoi(row[4]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].max = atoi(row[5]);
}
return npc_spellseffects_cache[iDBSpellsEffectsID];
}
uint32 ZoneDatabase::GetMaxNPCSpellsEffectsID() {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells_effects"), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
uint32 ret = 0;
if (row[0])
ret = atoi(row[0]);
mysql_free_result(result);
return ret;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in GetMaxNPCSpellsEffectsID query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = "SELECT max(id) FROM npc_spells_effects";
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetMaxNPCSpellsEffectsID query '" << query << "' " << results.ErrorMessage() << std::endl;
return 0;
}
return 0;
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
if (!row[0])
return 0;
return atoi(row[0]);
}
+1 -1
View File
@@ -12,7 +12,7 @@
#include "mob.h"
#include "client.h"
#include "worldserver.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "event_codes.h"
#include "embparser.h"
#include <string>
+1 -1
View File
@@ -12,7 +12,7 @@
#include "mob.h"
#include "client.h"
#include "worldserver.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "event_codes.h"
#include "embparser.h"
#include <string>
+16 -12
View File
@@ -22,20 +22,20 @@
#include "../common/features.h"
#include "../common/queue.h"
#include "../common/timer.h"
#include "../common/EQStream.h"
#include "../common/EQStreamFactory.h"
#include "../common/eq_stream.h"
#include "../common/eq_stream_factory.h"
#include "../common/eq_packet_structs.h"
#include "../common/Mutex.h"
#include "../common/mutex.h"
#include "../common/version.h"
#include "../common/EQEMuError.h"
#include "../common/eqemu_error.h"
#include "../common/packet_dump_file.h"
#include "../common/opcodemgr.h"
#include "../common/guilds.h"
#include "../common/EQStreamIdent.h"
#include "../common/eq_stream_ident.h"
#include "../common/patches/patches.h"
#include "../common/rulesys.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/platform.h"
#include "../common/crash.h"
#include "../common/ipc_mutex.h"
@@ -43,18 +43,19 @@
#include "../common/eqemu_exception.h"
#include "../common/spdat.h"
#include "ZoneConfig.h"
#include "zone_config.h"
#include "masterentity.h"
#include "worldserver.h"
#include "net.h"
#include "zone.h"
#include "queryserv.h"
#include "command.h"
#include "ZoneConfig.h"
#include "zone_config.h"
#include "titles.h"
#include "guild_mgr.h"
#include "tasks.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "embparser.h"
#include "lua_parser.h"
#include "client_logs.h"
@@ -98,6 +99,7 @@ npcDecayTimes_Struct npcCorpseDecayTimes[100];
TitleManager title_manager;
DBAsyncFinishedQueue MTdbafq;
DBAsync *dbasync = nullptr;
QueryServ *QServ = 0;
TaskManager *taskmanager = 0;
QuestParserCollection *parse = 0;
@@ -114,6 +116,8 @@ int main(int argc, char** argv) {
const char *zone_name;
QServ = new QueryServ;
if(argc == 3) {
worldserver.SetLauncherName(argv[2]);
worldserver.SetLaunchedName(argv[1]);
@@ -622,7 +626,7 @@ void LoadSpells(EQEmu::MemoryMappedFile **mmf) {
SPDAT_RECORDS = records;
}
/* Update Window Title with relevant information */
void UpdateWindowTitle(char* iNewTitle) {
#ifdef _WINDOWS
char tmp[500];
@@ -634,7 +638,7 @@ void UpdateWindowTitle(char* iNewTitle) {
#if defined(GOTFRAGS) || defined(_EQDEBUG)
snprintf(tmp, sizeof(tmp), "%i: %s, %i clients, %i", ZoneConfig::get()->ZonePort, zone->GetShortName(), numclients, getpid());
#else
snprintf(tmp, sizeof(tmp), "%i: %s, %i clients", ZoneConfig::get()->ZonePort, zone->GetShortName(), numclients);
snprintf(tmp, sizeof(tmp), "%s :: clients: %i inst_id: %i inst_ver: %i :: port: %i", zone->GetShortName(), numclients, zone->GetInstanceID(), zone->GetInstanceVersion(), ZoneConfig::get()->ZonePort);
#endif
}
else {
+4 -4
View File
@@ -40,10 +40,10 @@
#include "../common/spdat.h"
#include "../common/bodytypes.h"
#include "spawngroup.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "StringIDs.h"
#include "string_ids.h"
//#define SPELLQUEUE //Use only if you want to be spammed by spell testing
@@ -52,7 +52,7 @@ extern Zone* zone;
extern volatile bool ZoneLoaded;
extern EntityList entity_list;
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float heading, int iflymode, bool IsCorpse)
: Mob(d->name,
+4 -1
View File
@@ -29,7 +29,7 @@ class NPC;
#include "spawn2.h"
#include "../common/loottable.h"
#include "zonedump.h"
#include "QGlobals.h"
#include "qglobals.h"
#include "../common/rulesys.h"
#ifdef _WINDOWS
@@ -390,6 +390,8 @@ public:
uint32 GetSpawnKillCount();
int GetScore();
void SetMerchantProbability(uint8 amt) { probability = amt; }
uint8 GetMerchantProbability() { return probability; }
void mod_prespawn(Spawn2 *sp);
int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other);
void mod_npc_killed_merit(Mob* c);
@@ -504,6 +506,7 @@ protected:
std::list<MercData> mercDataList;
bool raid_target;
uint8 probability;
private:
uint32 loottable_id;
View File
View File
+66 -88
View File
@@ -24,12 +24,12 @@
#include "zonedb.h"
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/features.h"
#include "StringIDs.h"
#include "string_ids.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
const char DEFAULT_OBJECT_NAME[] = "IT63_ACTORDEF";
const char DEFAULT_OBJECT_NAME_SUFFIX[] = "_ACTORDEF";
@@ -560,9 +560,6 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
// Add new Zone Object (theoretically only called for items dropped to ground)
uint32 ZoneDatabase::AddObject(uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 database_id = 0;
uint32 item_id = 0;
int16 charges = 0;
@@ -577,35 +574,30 @@ uint32 ZoneDatabase::AddObject(uint32 type, uint32 icon, const Object_Struct& ob
char* object_name = new char[len];
DoEscapeString(object_name, object.object_name, strlen(object.object_name));
// Construct query
uint32 len_query = MakeAnyLenString(&query,
"insert into object (zoneid, xpos, ypos, zpos, heading, itemid, charges, objectname, "
"type, icon) values (%i, %f, %f, %f, %f, %i, %i, '%s', %i, %i)",
object.zone_id, object.x, object.y, object.z, object.heading,
item_id, charges, object_name, type, icon);
// Save new record for object
if (!RunQuery(query, len_query, errbuf, nullptr, nullptr, &database_id)) {
LogFile->write(EQEMuLog::Error, "Unable to insert object: %s", errbuf);
}
else {
// Save container contents, if container
if (inst && inst->IsType(ItemClassContainer)) {
SaveWorldContainer(object.zone_id, database_id, inst);
}
// Save new record for object
std::string query = StringFormat("INSERT INTO object "
"(zoneid, xpos, ypos, zpos, heading, "
"itemid, charges, objectname, type, icon) "
"values (%i, %f, %f, %f, %f, %i, %i, '%s', %i, %i)",
object.zone_id, object.x, object.y, object.z, object.heading,
item_id, charges, object_name, type, icon);
safe_delete_array(object_name);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Unable to insert object: %s", results.ErrorMessage().c_str());
return 0;
}
safe_delete_array(object_name);
safe_delete_array(query);
// Save container contents, if container
if (inst && inst->IsType(ItemClassContainer))
SaveWorldContainer(object.zone_id, database_id, inst);
return database_id;
}
// Update information about existing object in database
void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 item_id = 0;
int16 charges = 0;
@@ -619,77 +611,63 @@ void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Objec
char* object_name = new char[len];
DoEscapeString(object_name, object.object_name, strlen(object.object_name));
// Construct query
uint32 len_query = MakeAnyLenString(&query,
"update object set zoneid=%i, xpos=%f, ypos=%f, zpos=%f, heading=%f, "
"itemid=%i, charges=%i, objectname='%s', type=%i, icon=%i where id=%i",
object.zone_id, object.x, object.y, object.z, object.heading,
item_id, charges, object_name, type, icon, id);
// Save new record for object
if (!RunQuery(query, len_query, errbuf)) {
LogFile->write(EQEMuLog::Error, "Unable to update object: %s", errbuf);
}
else {
// Save container contents, if container
if (inst && inst->IsType(ItemClassContainer)) {
SaveWorldContainer(object.zone_id, id, inst);
}
std::string query = StringFormat("UPDATE object SET "
"zoneid = %i, xpos = %f, ypos = %f, zpos = %f, heading = %f, "
"itemid = %i, charges = %i, objectname = '%s', type = %i, icon = %i "
"WHERE id = %i",
object.zone_id, object.x, object.y, object.z, object.heading,
item_id, charges, object_name, type, icon, id);
safe_delete_array(object_name);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Unable to update object: %s", results.ErrorMessage().c_str());
return;
}
safe_delete_array(object_name);
safe_delete_array(query);
// Save container contents, if container
if (inst && inst->IsType(ItemClassContainer))
SaveWorldContainer(object.zone_id, id, inst);
}
Ground_Spawns* ZoneDatabase::LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs){
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT max_x,max_y,max_z,min_x,min_y,heading,name,item,max_allowed,respawn_timer from ground_spawns where zoneid=%i and (version=%u OR version=-1) limit 50", zone_id, version), errbuf, &result))
{
safe_delete_array(query);
int i=0;
while( (row=mysql_fetch_row(result) ) ) {
gs->spawn[i].max_x=atof(row[0]);
gs->spawn[i].max_y=atof(row[1]);
gs->spawn[i].max_z=atof(row[2]);
gs->spawn[i].min_x=atof(row[3]);
gs->spawn[i].min_y=atof(row[4]);
gs->spawn[i].heading=atof(row[5]);
strcpy(gs->spawn[i].name,row[6]);
gs->spawn[i].item=atoi(row[7]);
gs->spawn[i].max_allowed=atoi(row[8]);
gs->spawn[i].respawntimer=atoi(row[9]);
i++;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in LoadGroundSpawns query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
Ground_Spawns* ZoneDatabase::LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs) {
std::string query = StringFormat("SELECT max_x, max_y, max_z, "
"min_x, min_y, heading, name, "
"item, max_allowed, respawn_timer "
"FROM ground_spawns "
"WHERE zoneid = %i AND (version = %u OR version = -1) "
"LIMIT 50", zone_id, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in LoadGroundSpawns query '" << query << "' " << results.ErrorMessage() << std::endl;
return gs;
}
int spawnIndex=0;
for (auto row = results.begin(); row != results.end(); ++row, ++spawnIndex) {
gs->spawn[spawnIndex].max_x=atof(row[0]);
gs->spawn[spawnIndex].max_y=atof(row[1]);
gs->spawn[spawnIndex].max_z=atof(row[2]);
gs->spawn[spawnIndex].min_x=atof(row[3]);
gs->spawn[spawnIndex].min_y=atof(row[4]);
gs->spawn[spawnIndex].heading=atof(row[5]);
strcpy(gs->spawn[spawnIndex].name,row[6]);
gs->spawn[spawnIndex].item=atoi(row[7]);
gs->spawn[spawnIndex].max_allowed=atoi(row[8]);
gs->spawn[spawnIndex].respawntimer=atoi(row[9]);
}
return gs;
}
void ZoneDatabase::DeleteObject(uint32 id)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
// Construct query
uint32 len_query = MakeAnyLenString(&query,
"delete from object where id=%i", id);
// Save new record for object
if (!RunQuery(query, len_query, errbuf)) {
LogFile->write(EQEMuLog::Error, "Unable to delete object: %s", errbuf);
// delete record of object
std::string query = StringFormat("DELETE FROM object WHERE id = %i", id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Unable to delete object: %s", results.ErrorMessage().c_str());
}
//else {
// Delete contained items, if any
// DeleteWorldContainer(id);
//}
safe_delete_array(query);
}
uint32 Object::GetDBID()
+1 -1
View File
@@ -25,7 +25,7 @@
#include "../common/linked_list.h"
#include "../common/emu_opcodes.h"
#include "../common/eq_packet_structs.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "client.h"
#include "mob.h"
#include "npc.h"
+3 -3
View File
@@ -67,12 +67,12 @@ extern volatile bool ZoneLoaded;
#include "../common/queue.h"
#include "../common/timer.h"
#include "../common/EQStream.h"
#include "../common/eq_stream.h"
#include "../common/eq_packet_structs.h"
#include "../common/Mutex.h"
#include "../common/mutex.h"
#include "../common/version.h"
#include "../common/files.h"
#include "../common/EQEMuError.h"
#include "../common/eqemu_error.h"
#include "../common/packet_dump_file.h"
#include "masterentity.h"
+1 -1
View File
@@ -7,7 +7,7 @@
#include <fstream>
#include "pathing.h"
#include "water_map.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "doors.h"
#include "client.h"
#include "zone.h"
+37 -2
View File
@@ -1259,7 +1259,24 @@ XS(XS_Client_MovePC)
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
THIS->MovePC(zoneID, x, y, z, heading);
if (THIS->IsClient()) {
THIS->MovePC(zoneID, x, y, z, heading);
}
else {
if (THIS->IsMerc())
_log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process a type Merc reference");
else if (THIS->IsNPC())
_log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process a type NPC reference");
#ifdef BOTS
else if (THIS->IsBot())
_log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process a type Bot reference");
#endif
else
_log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process an Unknown type reference");
Perl_croak(aTHX_ "THIS is not of type Client");
}
}
XSRETURN_EMPTY;
}
@@ -1288,7 +1305,25 @@ XS(XS_Client_MovePCInstance)
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
THIS->MovePC(zoneID, instanceID, x, y, z, heading);
if (THIS->IsClient()) {
THIS->MovePC(zoneID, instanceID, x, y, z, heading);
}
else {
if (THIS->IsMerc())
_log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process a type Merc reference");
else if (THIS->IsNPC())
_log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process a type NPC reference");
#ifdef BOTS
else if (THIS->IsBot())
_log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process a type Bot reference");
#endif
else
_log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process an Unknown type reference");
Perl_croak(aTHX_ "THIS is not of type Client");
Perl_croak(aTHX_ "THIS is not of type Client");
}
}
XSRETURN_EMPTY;
}
+50
View File
@@ -2145,6 +2145,54 @@ XS(XS_NPC_GetScore)
XSRETURN(1);
}
XS(XS_NPC_SetMerchantProbability);
XS(XS_NPC_SetMerchantProbability) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: NPC::SetMerchantProbability(THIS, Probability)");
{
NPC *THIS;
uint8 Probability = (uint8)SvIV(ST(1));
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
THIS->SetMerchantProbability(Probability);
}
XSRETURN_EMPTY;
}
XS(XS_NPC_GetMerchantProbability);
XS(XS_NPC_GetMerchantProbability) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: NPC::GetMerchantProbability(THIS)");
{
NPC *THIS;
uint8 RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
RETVAL = THIS->GetMerchantProbability();
XSprePUSH; PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
#ifdef __cplusplus
extern "C"
#endif
@@ -2243,6 +2291,8 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$");
newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$");
newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$");
newXSproto(strcpy(buf, "SetMerchantProbability"), XS_NPC_SetMerchantProbability, file, "$$");
newXSproto(strcpy(buf, "GetMerchantProbability"), XS_NPC_GetMerchantProbability, file, "$");
XSRETURN_YES;
}
+1 -1
View File
@@ -26,7 +26,7 @@
#undef seed
#endif
#include "../common/Item.h"
#include "../common/item.h"
#ifdef THIS /* this macro seems to leak out on some systems */
#undef THIS
+1 -1
View File
@@ -22,7 +22,7 @@
#include "entity.h"
#include "../common/opcodemgr.h"
#include "../common/packet_dump.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
PerlPacket::PerlPacket(const char *opcode, uint32 length) {
SetOpcode(opcode);
+66 -71
View File
@@ -32,7 +32,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "../common/packet_dump_file.h"
@@ -216,100 +216,95 @@ void PetitionList::UpdatePetition(Petition* pet) {
}
void ZoneDatabase::DeletePetitionFromDB(Petition* wpet) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
uint8 checkedout = 0;
if (wpet->CheckedOut()) checkedout = 0;
else checkedout = 1;
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE from petitions where petid = %i", wpet->GetID()), errbuf, 0, &affected_rows)) {
LogFile->write(EQEMuLog::Error, "Error in DeletePetitionFromDB query '%s': %s", query, errbuf);
}
safe_delete_array(query);
return;
std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", wpet->GetID());
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in DeletePetitionFromDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
void ZoneDatabase::UpdatePetitionToDB(Petition* wpet) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
uint8 checkedout = 0;
if (wpet->CheckedOut()) checkedout = 1;
else checkedout = 0;
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE petitions set gmtext = '%s', lastgm = '%s', urgency = %i, checkouts = %i, unavailables = %i, ischeckedout = %i where petid = %i", wpet->GetGMText(), wpet->GetLastGM(), wpet->GetUrgency(), wpet->GetCheckouts(), wpet->GetUnavails(), checkedout, wpet->GetID()), errbuf, 0, &affected_rows)) {
LogFile->write(EQEMuLog::Error, "Error in UpdatePetitionToDB query '%s': %s", query, errbuf);
}
safe_delete_array(query);
return;
std::string query = StringFormat("UPDATE petitions SET gmtext = '%s', lastgm = '%s', urgency = %i, "
"checkouts = %i, unavailables = %i, ischeckedout = %i "
"WHERE petid = %i",
wpet->GetGMText(), wpet->GetLastGM(), wpet->GetUrgency(),
wpet->GetCheckouts(), wpet->GetUnavails(),
wpet->CheckedOut() ? 1: 0, wpet->GetID());
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in UpdatePetitionToDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
void ZoneDatabase::InsertPetitionToDB(Petition* wpet)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
uint8 checkedout = 0;
if (wpet->CheckedOut())
checkedout = 1;
else
checkedout = 0;
uint32 len = strlen(wpet->GetPetitionText());
char* petitiontext = new char[2*len+1];
memset(petitiontext, 0, 2*len+1);
DoEscapeString(petitiontext, wpet->GetPetitionText(), len);
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO petitions (petid, charname, accountname, lastgm, petitiontext, zone, urgency, charclass, charrace, charlevel, checkouts, unavailables, ischeckedout, senttime, gmtext) values (%i,'%s','%s','%s','%s',%i,%i,%i,%i,%i,%i,%i,%i,%i, '%s')", wpet->GetID(), wpet->GetCharName(), wpet->GetAccountName(), wpet->GetLastGM(), petitiontext, wpet->GetZone(), wpet->GetUrgency(), wpet->GetCharClass(), wpet->GetCharRace(), wpet->GetCharLevel(), wpet->GetCheckouts(), wpet->GetUnavails(), checkedout, wpet->GetSentTime(), wpet->GetGMText()), errbuf, 0, &affected_rows)) {
LogFile->write(EQEMuLog::Error, "Error in InsertPetitionToDB query '%s': %s", query, errbuf);
std::string query = StringFormat("INSERT INTO petitions "
"(petid, charname, accountname, lastgm, "
"petitiontext, zone, urgency, charclass, "
"charrace, charlevel, checkouts, unavailables, "
"ischeckedout, senttime, gmtext) "
"VALUES (%i, '%s', '%s', '%s', '%s', "
"%i, %i, %i, %i, %i, "
"%i, %i, %i, %i, '%s')",
wpet->GetID(), wpet->GetCharName(), wpet->GetAccountName(), wpet->GetLastGM(),
petitiontext, wpet->GetZone(), wpet->GetUrgency(), wpet->GetCharClass(),
wpet->GetCharRace(), wpet->GetCharLevel(), wpet->GetCheckouts(), wpet->GetUnavails(),
wpet->CheckedOut()? 1: 0, wpet->GetSentTime(), wpet->GetGMText());
safe_delete_array(petitiontext);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in InsertPetitionToDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(petitiontext);
safe_delete_array(query);
#if EQDEBUG >= 5
LogFile->write(EQEMuLog::Debug, "New petition created");
#endif
return;
}
void ZoneDatabase::RefreshPetitionsFromDB()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
Petition* newpet;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, accountname, lastgm, petitiontext, zone, urgency, charclass, charrace, charlevel, checkouts, unavailables, ischeckedout, senttime, gmtext from petitions order by petid"), errbuf, &result))
{
safe_delete_array(query);
while ((row = mysql_fetch_row(result))) {
newpet = new Petition(atoi(row[0]));
newpet->SetCName(row[1]);
newpet->SetAName(row[2]);
newpet->SetLastGM(row[3]);
newpet->SetPetitionText(row[4]);
newpet->SetZone(atoi(row[5]));
newpet->SetUrgency(atoi(row[6]));
newpet->SetClass(atoi(row[7]));
newpet->SetRace(atoi(row[8]));
newpet->SetLevel(atoi(row[9]));
newpet->SetCheckouts(atoi(row[10]));
newpet->SetUnavails(atoi(row[11]));
newpet->SetSentTime2(atol(row[13]));
newpet->SetGMText(row[14]);
std::cout << "Petition " << row[0] << " pettime = " << newpet->GetSentTime() << std::endl;
if (atoi(row[12]) == 1) newpet->SetCheckedOut(true);
else newpet->SetCheckedOut(false);
petition_list.AddPetition(newpet);
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in RefreshPetitionsFromDB query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = "SELECT petid, charname, accountname, lastgm, petitiontext, "
"zone, urgency, charclass, charrace, charlevel, checkouts, "
"unavailables, ischeckedout, senttime, gmtext "
"FROM petitions ORDER BY petid";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in RefreshPetitionsFromDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
return;
for (auto row = results.begin(); row != results.end(); ++row) {
newpet = new Petition(atoi(row[0]));
newpet->SetCName(row[1]);
newpet->SetAName(row[2]);
newpet->SetLastGM(row[3]);
newpet->SetPetitionText(row[4]);
newpet->SetZone(atoi(row[5]));
newpet->SetUrgency(atoi(row[6]));
newpet->SetClass(atoi(row[7]));
newpet->SetRace(atoi(row[8]));
newpet->SetLevel(atoi(row[9]));
newpet->SetCheckouts(atoi(row[10]));
newpet->SetUnavails(atoi(row[11]));
newpet->SetSentTime2(atol(row[13]));
newpet->SetGMText(row[14]);
if (atoi(row[12]) == 1)
newpet->SetCheckedOut(true);
else
newpet->SetCheckedOut(false);
petition_list.AddPetition(newpet);
}
}
+2 -2
View File
@@ -22,8 +22,8 @@
#include "../common/types.h"
#include "zonedb.h"
#include "client.h"
#include "../common/Mutex.h"
#include "../common/MiscFunctions.h"
#include "../common/mutex.h"
#include "../common/misc_functions.h"
class Petition
{
+88 -117
View File
@@ -20,13 +20,13 @@
#include "masterentity.h"
#include "../common/packet_dump.h"
#include "../common/moremath.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "zonedb.h"
#include "worldserver.h"
#include "../common/skills.h"
#include "../common/bodytypes.h"
#include "../common/classes.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "pets.h"
#include <math.h>
#include <assert.h>
@@ -35,7 +35,7 @@
#include "../common/unix.h"
#endif
#include "StringIDs.h"
#include "string_ids.h"
///////////////////////////////////////////////////////////////////////////////
// pet related functions
@@ -362,31 +362,34 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
// handle monster summoning pet appearance
if(record.monsterflag) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result = nullptr;
MYSQL_ROW row = nullptr;
uint32 monsterid;
uint32 monsterid = 0;
// get a random npc id from the spawngroups assigned to this zone
if (database.RunQuery(query, MakeAnyLenString(&query,
"SELECT npcID FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) "
"INNER JOIN npc_types ON npc_types.id = spawnentry.npcID "
"WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) "
"AND npc_types.race NOT IN (0,1,2,3,4,5,6,7,8,9,10,11,12,44,55,67,71,72,73,77,78,81,90,92,93,94,106,112,114,127,128,130,139,141,183,236,237,238,239,254,266,329,330,378,379,380,381,382,383,404,522) "
"ORDER BY RAND() LIMIT 1", zone->GetShortName()), errbuf, &result))
{
row = mysql_fetch_row(result);
if (row)
monsterid = atoi(row[0]);
else
monsterid = 567; // since we don't have any monsters, just make it look like an earth pet for now
}
else { // if the database query failed
LogFile->write(EQEMuLog::Error, "Error querying database for monster summoning pet in zone %s (%s)", zone->GetShortName(), errbuf);
monsterid = 567;
auto query = StringFormat("SELECT npcID "
"FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) "
"INNER JOIN npc_types ON npc_types.id = spawnentry.npcID "
"WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) "
"AND npc_types.race NOT IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 44, "
"55, 67, 71, 72, 73, 77, 78, 81, 90, 92, 93, 94, 106, 112, 114, 127, 128, "
"130, 139, 141, 183, 236, 237, 238, 239, 254, 266, 329, 330, 378, 379, "
"380, 381, 382, 383, 404, 522) "
"ORDER BY RAND() LIMIT 1", zone->GetShortName());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
// if the database query failed
LogFile->write(EQEMuLog::Error, "Error querying database for monster summoning pet in zone %s (%s)", zone->GetShortName(), results.ErrorMessage().c_str());
}
if (results.RowCount() != 0) {
auto row = results.begin();
monsterid = atoi(row[0]);
}
// since we don't have any monsters, just make it look like an earth pet for now
if (monsterid == 0)
monsterid = 567;
// give the summoned pet the attributes of the monster we found
const NPCType* monster = database.GetNPCType(monsterid);
if(monster) {
@@ -396,12 +399,9 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
npc_type->gender = monster->gender;
npc_type->luclinface = monster->luclinface;
npc_type->helmtexture = monster->helmtexture;
}
else {
} else
LogFile->write(EQEMuLog::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid);
}
safe_delete_array(query);
}
//this takes ownership of the npc_type data
@@ -450,46 +450,35 @@ bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) {
}
bool ZoneDatabase::GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 querylen = 0;
MYSQL_RES *result;
MYSQL_ROW row;
std::string query;
if (petpower <= 0) {
querylen = MakeAnyLenString(&query,
"SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset FROM pets "
"WHERE type='%s' AND petpower<=0", pet_type);
}
else {
querylen = MakeAnyLenString(&query,
"SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset FROM pets "
"WHERE type='%s' AND petpower<=%d ORDER BY petpower DESC LIMIT 1", pet_type, petpower);
}
if (petpower <= 0)
query = StringFormat("SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset "
"FROM pets WHERE type='%s' AND petpower<=0", pet_type);
else
query = StringFormat("SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset "
"FROM pets WHERE type='%s' AND petpower<=%d ORDER BY petpower DESC LIMIT 1",
pet_type, petpower);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetPoweredPetEntry query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
if (RunQuery(query, querylen, errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
if (results.RowCount() != 1)
return false;
into->npc_type = atoi(row[0]);
into->temporary = atoi(row[1]);
into->petpower = atoi(row[2]);
into->petcontrol = atoi(row[3]);
into->petnaming = atoi(row[4]);
into->monsterflag = atoi(row[5]);
into->equipmentset = atoi(row[6]);
auto row = results.begin();
mysql_free_result(result);
return(true);
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in GetPoweredPetEntry query '%s': %s", query, errbuf);
safe_delete_array(query);
}
return(false);
into->npc_type = atoi(row[0]);
into->temporary = atoi(row[1]);
into->petpower = atoi(row[2]);
into->petcontrol = atoi(row[3]);
into->petnaming = atoi(row[4]);
into->monsterflag = atoi(row[5]);
into->equipmentset = atoi(row[6]);
return true;
}
Mob* Mob::GetPet() {
@@ -657,11 +646,6 @@ bool ZoneDatabase::GetBasePetItems(int32 equipmentset, uint32 *items) {
// A slot will only get an item put in it if it is empty. That way
// an equipmentset can overload a slot for the set(s) it includes.
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 querylen = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int depth = 0;
int32 curset = equipmentset;
int32 nextset = -1;
@@ -673,56 +657,43 @@ bool ZoneDatabase::GetBasePetItems(int32 equipmentset, uint32 *items) {
// query pets_equipmentset_entries with the set_id and loop over
// all of the result rows. Check if we have something in the slot
// already. If no, add the item id to the equipment array.
while (curset >= 0 && depth < 5) {
if (RunQuery(query,
MakeAnyLenString(&query, "SELECT nested_set FROM pets_equipmentset WHERE set_id='%s'", curset),
errbuf, &result))
{
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
nextset = atoi(row[0]);
mysql_free_result(result);
if (RunQuery(query,
MakeAnyLenString(&query, "SELECT slot, item_id FROM pets_equipmentset_entries WHERE set_id='%s'", curset),
errbuf, &result))
{
safe_delete_array(query);
while ((row = mysql_fetch_row(result)))
{
slot = atoi(row[0]);
if (slot >= EmuConstants::EQUIPMENT_SIZE)
continue;
if (items[slot] == 0)
items[slot] = atoi(row[1]);
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query, errbuf);
safe_delete_array(query);
}
curset = nextset;
depth++;
}
else
{
// invalid set reference, it doesn't exist
LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems equipment set '%d' does not exist", curset);
mysql_free_result(result);
return false;
}
}
else
{
LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT nested_set FROM pets_equipmentset WHERE set_id = '%s'", curset);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
} // end while
}
if (results.RowCount() != 1) {
// invalid set reference, it doesn't exist
LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems equipment set '%d' does not exist", curset);
return false;
}
auto row = results.begin();
nextset = atoi(row[0]);
query = StringFormat("SELECT slot, item_id FROM pets_equipmentset_entries WHERE set_id='%s'", curset);
results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
else {
for (row = results.begin(); row != results.end(); ++row)
{
slot = atoi(row[0]);
if (slot >= EmuConstants::EQUIPMENT_SIZE)
continue;
if (items[slot] == 0)
items[slot] = atoi(row[1]);
}
}
curset = nextset;
depth++;
}
return true;
}
+26 -62
View File
@@ -1,6 +1,6 @@
#include "../common/debug.h"
#include "../common/StringUtil.h"
#include "QGlobals.h"
#include "../common/string_util.h"
#include "qglobals.h"
#include "masterentity.h"
#include "zone.h"
#include "zonedb.h"
@@ -51,7 +51,7 @@ void QGlobalCache::Combine(std::list<QGlobal> &cacheA, std::list<QGlobal> cacheB
void QGlobalCache::GetQGlobals(std::list<QGlobal> &globals, NPC *n, Client *c, Zone *z) {
globals.clear();
QGlobalCache *npc_c = nullptr;
QGlobalCache *char_c = nullptr;
QGlobalCache *zone_c = nullptr;
@@ -139,75 +139,39 @@ void QGlobalCache::PurgeExpiredGlobals()
void QGlobalCache::LoadByNPCID(uint32 npcID)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate"
" from quest_globals where npcid = %d", npcID), errbuf, &result))
{
while((row = mysql_fetch_row(result)))
{
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF));
}
mysql_free_result(result);
}
safe_delete_array(query);
std::string query = StringFormat("SELECT name, charid, npcid, zoneid, value, expdate "
"FROM quest_globals WHERE npcid = %d", npcID);
LoadBy(query);
}
void QGlobalCache::LoadByCharID(uint32 charID)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate from"
" quest_globals where charid = %d && npcid = 0", charID), errbuf, &result))
{
while((row = mysql_fetch_row(result)))
{
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF));
}
mysql_free_result(result);
}
safe_delete_array(query);
std::string query = StringFormat("SELECT name, charid, npcid, zoneid, value, expdate "
"FROM quest_globals WHERE charid = %d && npcid = 0", charID);
LoadBy(query);
}
void QGlobalCache::LoadByZoneID(uint32 zoneID)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate from quest_globals"
" where zoneid = %d && npcid = 0 && charid = 0", zoneID), errbuf, &result))
{
while((row = mysql_fetch_row(result)))
{
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF));
}
mysql_free_result(result);
}
safe_delete_array(query);
std::string query = StringFormat("SELECT name, charid, npcid, zoneid, value, expdate "
"FROM quest_globals WHERE zoneid = %d && npcid = 0 && charid = 0", zoneID);
LoadBy(query);
}
void QGlobalCache::LoadByGlobalContext()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
std::string query = "SELECT name, charid, npcid, zoneid, value, expdate "
"FROM quest_globals WHERE zoneid = 0 && npcid = 0 && charid = 0";
LoadBy(query);
}
void QGlobalCache::LoadBy(const std::string query)
{
auto results = database.QueryDatabase(query);
if (!results.Success())
return;
for (auto row = results.begin(); row != results.end(); ++row)
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]? atoi(row[5]): 0xFFFFFFFF));
if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate from quest_globals"
" where zoneid = 0 && npcid = 0 && charid = 0"), errbuf, &result))
{
while((row = mysql_fetch_row(result)))
{
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF));
}
mysql_free_result(result);
}
safe_delete_array(query);
}
+1
View File
@@ -42,6 +42,7 @@ public:
void LoadByZoneID(uint32 zoneID); //zone
void LoadByGlobalContext(); //zone
protected:
void LoadBy(const std::string query);
std::list<QGlobal> qGlobalBucket;
};
+52
View File
@@ -0,0 +1,52 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net)
This program 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; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/debug.h"
#include "../common/servertalk.h"
#include "../common/string_util.h"
#include "queryserv.h"
#include "worldserver.h"
#include "net.h"
#include <iostream>
extern WorldServer worldserver;
extern QueryServ* QServ;
QueryServ::QueryServ(){
}
QueryServ::~QueryServ(){
}
void QueryServ::SendQuery(std::string Query)
{
ServerPacket* pack = new ServerPacket(ServerOP_QSSendQuery, Query.length() + 5);
pack->WriteUInt32(Query.length()); /* Pack Query String Size so it can be dynamically broken out at queryserv */
pack->WriteString(Query.c_str()); /* Query */
worldserver.SendPacket(pack);
safe_delete(pack);
}
void QueryServ::PlayerLogEvent(int Event_Type, int Character_ID, std::string Event_Desc)
{
std::string query = StringFormat(
"INSERT INTO `qs_player_events` (event, char_id, event_desc, time) VALUES (%i, %i, '%s', UNIX_TIMESTAMP(now()))",
Event_Type, Character_ID, EscapeString(Event_Desc).c_str());
SendQuery(query);
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef QUERYSERV_ZONE_H
#define QUERYSERV_ZONE_H
/*
enum PlayerGenericLogEventTypes
These Enums are for the generic logging table that are not complex and require more advanced logic
*/
enum PlayerGenericLogEventTypes {
Player_Log_Quest = 1,
Player_Log_Zoning,
Player_Log_Deaths,
Player_Log_Connect_State,
Player_Log_Levels,
Player_Log_Keyring_Addition,
Player_Log_QGlobal_Update,
Player_Log_Task_Updates,
Player_Log_AA_Purchases,
Player_Log_Trade_Skill_Events,
Player_Log_Issued_Commands,
Player_Log_Money_Transactions,
Player_Log_Alternate_Currency_Transactions,
};
class QueryServ{
public:
QueryServ();
~QueryServ();
void SendQuery(std::string Query);
void PlayerLogEvent(int Event_Type, int Character_ID, std::string Event_Desc);
};
#endif /* QUERYSERV_ZONE_H */
+1 -1
View File
@@ -29,7 +29,7 @@
#include "../common/unix.h"
#endif
#include "Quest.h"
#include "quest.h"
pquest_entry Quest::m_pQuests;
int Quest::m_nQuests;
View File
@@ -17,10 +17,10 @@
*/
#include "../common/debug.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "../common/features.h"
#include "QuestParserCollection.h"
#include "QuestInterface.h"
#include "quest_parser_collection.h"
#include "quest_interface.h"
#include "zone.h"
#include "questmgr.h"
@@ -600,9 +600,8 @@ QuestInterface *QuestParserCollection::GetQIByNPCQuest(uint32 npcid, std::string
}
QuestInterface *QuestParserCollection::GetQIByPlayerQuest(std::string &filename) {
if(!zone)
return nullptr;
if(!zone || !zone->IsLoaded())
return nullptr;
//first look for /quests/zone/player_v[instance_version].ext (precedence)
filename = "quests/";
@@ -20,10 +20,10 @@
#define _EQE_QUESTPARSERCOLLECTION_H
#include "../common/types.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "masterentity.h"
#include "QuestInterface.h"
#include "quest_interface.h"
#include <string.h>
#include <string>
+58 -60
View File
@@ -67,20 +67,20 @@ And then at then end of embparser.cpp, add:
#include "zonedb.h"
#include "../common/spdat.h"
#include "../common/packet_functions.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "spawn2.h"
#include "zone.h"
#include "event_codes.h"
#include "guild_mgr.h"
#include "../common/rulesys.h"
#include "QGlobals.h"
#include "QuestParserCollection.h"
#include "qglobals.h"
#include "quest_parser_collection.h"
#include "queryserv.h"
#ifdef BOTS
#include "bot.h"
#endif
extern QueryServ* QServ;
extern Zone* zone;
extern WorldServer worldserver;
extern EntityList entity_list;
@@ -172,8 +172,7 @@ void QuestManager::EndQuest() {
cur = QTimerList.erase(cur);
else
++cur;
}
}
run.owner->Depop();
}
quests_running_.pop();
@@ -1294,33 +1293,29 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti
int qgNpcid = owner->GetNPCTypeID();
/* options value determines the availability of global variables to NPCs when a quest begins
------------------------------------------------------------------
value npcid player zone
------------------------------------------------------------------
0 this this this
1 all this this
2 this all this
3 all all this
4 this this all
5 all this all
6 this all all
7 all all all
------------------------------------------------------------------
value npcid player zone
------------------------------------------------------------------
0 this this this
1 all this this
2 this all this
3 all all this
4 this this all
5 all this all
6 this all all
7 all all all
*/
if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved
{
qgCharid=initiator->CharacterID();
}
else
{
if (initiator && initiator->IsClient()){ // some events like waypoint and spawn don't have a player involved
qgCharid=initiator->CharacterID();
}
else {
qgCharid=-qgNpcid; // make char id negative npc id as a fudge
}
if (options < 0 || options > 7)
{
if (options < 0 || options > 7) {
std::cerr << "Invalid options for global var " << varname << " using defaults" << std::endl;
} // default = 0 (only this npcid,player and zone)
else
{
else {
if (options & 1)
qgNpcid=0;
if (options & 2)
@@ -1330,30 +1325,32 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti
}
InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, newvalue, QGVarDuration(duration));
/* QS: PlayerLogQGlobalUpdate */
if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){
std::string event_desc = StringFormat("Update :: qglobal:%s to qvalue:%s zoneid:%i instid:%i", varname, newvalue, initiator->GetZoneID(), initiator->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc);
}
}
/* Inserts global variable into quest_globals table */
int QuestManager::InsertQuestGlobal(
int charid, int npcid, int zoneid,
const char *varname, const char *varvalue,
int duration)
{
int QuestManager::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varname, const char *varvalue, int duration) {
char *query = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
// Make duration string either "unix_timestamp(now()) + xxx" or "NULL"
std::stringstream duration_ss;
if (duration == INT_MAX)
{
std::stringstream duration_ss;
if (duration == INT_MAX) {
duration_ss << "NULL";
}
else
{
else {
duration_ss << "unix_timestamp(now()) + " << duration;
}
//NOTE: this should be escaping the contents of arglist
//npcwise a malicious script can arbitrarily alter the DB
/*
NOTE: this should be escaping the contents of arglist
npcwise a malicious script can arbitrarily alter the DB
*/
uint32 last_id = 0;
if (!database.RunQuery(query, MakeAnyLenString(&query,
"REPLACE INTO quest_globals (charid, npcid, zoneid, name, value, expdate)"
@@ -1365,9 +1362,8 @@ int QuestManager::InsertQuestGlobal(
}
safe_delete_array(query);
if(zone)
{
//first delete our global
if(zone) {
/* Delete existing qglobal data and update zone processes */
ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct));
ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer;
qgd->npc_id = npcid;
@@ -1383,18 +1379,16 @@ int QuestManager::InsertQuestGlobal(
worldserver.SendPacket(pack);
safe_delete(pack);
//then create a new one with the new id
/* Create new qglobal data and update zone processes */
pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct));
ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer;
qgu->npc_id = npcid;
qgu->char_id = charid;
qgu->zone_id = zoneid;
if(duration == INT_MAX)
{
if(duration == INT_MAX) {
qgu->expdate = 0xFFFFFFFF;
}
else
{
else {
qgu->expdate = Timer::GetTimeSeconds() + duration;
}
strcpy((char*)qgu->name, varname);
@@ -1420,8 +1414,7 @@ int QuestManager::InsertQuestGlobal(
return 0;
}
void QuestManager::targlobal(const char *varname, const char *value, const char *duration, int qgNpcid, int qgCharid, int qgZoneid)
{
void QuestManager::targlobal(const char *varname, const char *value, const char *duration, int qgNpcid, int qgCharid, int qgZoneid) {
InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, value, QGVarDuration(duration));
}
@@ -1432,15 +1425,24 @@ void QuestManager::delglobal(const char *varname) {
int qgZoneid=zone->GetZoneID();
int qgCharid=0;
int qgNpcid=owner->GetNPCTypeID();
if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved
{
qgCharid=initiator->CharacterID();
}
else
{
else {
qgCharid=-qgNpcid; // make char id negative npc id as a fudge
}
/* QS: PlayerLogQGlobalUpdate */
if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){
std::string event_desc = StringFormat("Deleted :: qglobal:%s zoneid:%i instid:%i", varname, initiator->GetZoneID(), initiator->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc);
}
if (!database.RunQuery(query,
MakeAnyLenString(&query,
"DELETE FROM quest_globals WHERE name='%s'"
@@ -1451,8 +1453,7 @@ void QuestManager::delglobal(const char *varname) {
}
safe_delete_array(query);
if(zone)
{
if(zone) {
ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct));
ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer;
@@ -1701,17 +1702,14 @@ void QuestManager::showgrid(int grid) {
pts.push_back(pt);
// Retrieve all waypoints for this grid
if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result))
{
while((row = mysql_fetch_row(result)))
{
if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result)) {
while((row = mysql_fetch_row(result))) {
pt.x = atof(row[0]);
pt.y = atof(row[1]);
pt.z = atof(row[2]);
pts.push_back(pt);
}
mysql_free_result(result);
mysql_free_result(result);
initiator->SendPathPacket(pts);
}
else // DB query error!
+2 -2
View File
@@ -17,10 +17,10 @@
*/
#include "../common/debug.h"
#include "masterentity.h"
#include "NpcAI.h"
#include "npc_ai.h"
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "worldserver.h"
extern EntityList entity_list;
extern WorldServer worldserver;
@@ -1,4 +1,4 @@
#include "RaycastMesh.h"
#include "raycast_mesh.h"
#include <math.h>
#include <assert.h>
#include <stdio.h>
+248 -290
View File
@@ -16,7 +16,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/debug.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include <stdlib.h>
#include "spawn2.h"
#include "entity.h"
@@ -354,96 +354,86 @@ void Spawn2::DeathReset(bool realdeath)
}
bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spawn2_list, int16 version, uint32 repopdelay) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
const char *zone_name = database.GetZoneName(zoneid);
MakeAnyLenString(&query, "SELECT id, spawngroupID, x, y, z, heading, respawntime, variance, pathgrid, _condition, cond_value, enabled, animation FROM spawn2 WHERE zone='%s' AND version=%u", zone_name, version);
if (RunQuery(query, strlen(query), errbuf, &result))
{
safe_delete_array(query);
while((row = mysql_fetch_row(result)))
{
Spawn2* newSpawn = 0;
bool perl_enabled = atoi(row[11]) == 1 ? true : false;
uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000);
newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]), atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12]));
spawn2_list.Insert( newSpawn );
}
mysql_free_result(result);
}
else
{
LogFile->write(EQEMuLog::Error, "Error in PopulateZoneLists query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, "
"respawntime, variance, pathgrid, _condition, "
"cond_value, enabled, animation FROM spawn2 "
"WHERE zone = '%s' AND version = %u",
zone_name, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in PopulateZoneLists query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
Spawn2* newSpawn = 0;
bool perl_enabled = atoi(row[11]) == 1? true: false;
uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000);
newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]),
atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]),
atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12]));
spawn2_list.Insert(newSpawn);
}
return true;
}
Spawn2* ZoneDatabase::LoadSpawn2(LinkedList<Spawn2*> &spawn2_list, uint32 spawn2id, uint32 timeleft) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, spawngroupID, x, y, z, heading, respawntime, variance, pathgrid, _condition, cond_value, enabled, animation FROM spawn2 WHERE id=%i", spawn2id), errbuf, &result)) {
if (mysql_num_rows(result) == 1)
{
row = mysql_fetch_row(result);
bool perl_enabled = atoi(row[11]) == 1 ? true : false;
Spawn2* newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atoi(row[6]), atoi(row[7]), timeleft, atoi(row[8]), atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12]));
spawn2_list.Insert( newSpawn );
mysql_free_result(result);
safe_delete_array(query);
return newSpawn;
}
mysql_free_result(result);
}
std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, "
"respawntime, variance, pathgrid, _condition, "
"cond_value, enabled, animation FROM spawn2 "
"WHERE id = %i", spawn2id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadSpawn2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return nullptr;
}
LogFile->write(EQEMuLog::Error, "Error in LoadSpawn2 query '%s': %s", query, errbuf);
safe_delete_array(query);
return 0;
if (results.RowCount() != 1) {
LogFile->write(EQEMuLog::Error, "Error in LoadSpawn2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return nullptr;
}
auto row = results.begin();
bool perl_enabled = atoi(row[11]) == 1 ? true : false;
Spawn2* newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]),
atof(row[5]), atoi(row[6]), atoi(row[7]), timeleft, atoi(row[8]),
atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12]));
spawn2_list.Insert(newSpawn);
return newSpawn;
}
bool ZoneDatabase::CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, float heading, float x, float y, float z, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value)
bool ZoneDatabase::CreateSpawn2(Client *client, uint32 spawngroup, const char* zone, float heading, float x, float y, float z, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
// if(GetInverseXY()==1) {
// float temp=x;
// x=y;
// y=temp;
// }
if (RunQuery(query, MakeAnyLenString(&query,
"INSERT INTO spawn2 (spawngroupID,zone,x,y,z,heading,respawntime,variance,_condition,cond_value) Values (%i, '%s', %f, %f, %f, %f, %i, %i, %u, %i)",
spawngroup, zone, x, y, z, heading, respawn, variance, condition, cond_value
), errbuf, 0, &affected_rows)) {
safe_delete_array(query);
if (affected_rows == 1) {
if(c) c->LogSQL(query);
return true;
}
else {
return false;
}
}
else {
LogFile->write(EQEMuLog::Error, "Error in CreateSpawn2 query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("INSERT INTO spawn2 (spawngroupID, zone, x, y, z, heading, "
"respawntime, variance, _condition, cond_value) "
"VALUES (%i, '%s', %f, %f, %f, %f, %i, %i, %u, %i)",
spawngroup, zone, x, y, z, heading,
respawn, variance, condition, cond_value);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in CreateSpawn2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
}
return false;
if (results.RowsAffected() != 1)
return false;
if(client)
client->LogSQL(query.c_str());
return true;
}
uint32 Zone::CountSpawn2() {
@@ -671,177 +661,159 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) {
}
void SpawnConditionManager::UpdateDBEvent(SpawnEvent &event) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
int len;
SpawnCondition cond;
len = MakeAnyLenString(&query,
"UPDATE spawn_events SET "
"next_minute=%d, next_hour=%d, next_day=%d, next_month=%d, "
"next_year=%d, enabled=%d, strict=%d "
"WHERE id=%d",
event.next.minute, event.next.hour, event.next.day, event.next.month,
event.next.year, event.enabled?1:0, event.strict?1:0,event.id
);
if(!database.RunQuery(query, len, errbuf)) {
LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query, errbuf);
}
safe_delete_array(query);
std::string query = StringFormat("UPDATE spawn_events SET "
"next_minute = %d, next_hour = %d, "
"next_day = %d, next_month = %d, "
"next_year = %d, enabled = %d, "
"strict = %d WHERE id = %d",
event.next.minute, event.next.hour,
event.next.day, event.next.month,
event.next.year, event.enabled? 1: 0,
event.strict? 1: 0, event.id);
auto results = database.QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query.c_str(), results.ErrorMessage().c_str());
}
void SpawnConditionManager::UpdateDBCondition(const char* zone_name, uint32 instance_id, uint16 cond_id, int16 value) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
int len;
SpawnCondition cond;
len = MakeAnyLenString(&query,
"REPLACE INTO spawn_condition_values (id, value, zone, instance_id) VALUES(%u, %u, '%s', %u)",
cond_id, value, zone_name, instance_id
);
if(!database.RunQuery(query, len, errbuf)) {
LogFile->write(EQEMuLog::Error, "Unable to update spawn condition '%s': %s\n", query, errbuf);
}
safe_delete_array(query);
std::string query = StringFormat("REPLACE INTO spawn_condition_values "
"(id, value, zone, instance_id) "
"VALUES( %u, %u, '%s', %u)",
cond_id, value, zone_name, instance_id);
auto results = database.QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to update spawn condition '%s': %s\n", query.c_str(), results.ErrorMessage().c_str());
}
bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std::string &zone_name) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int len;
bool ret = false;
len = MakeAnyLenString(&query,
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict,zone "
"FROM spawn_events WHERE id=%d", event_id);
if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query);
if((row = mysql_fetch_row(result))) {
event.id = atoi(row[0]);
event.condition_id = atoi(row[1]);
event.period = atoi(row[2]);
event.next.minute = atoi(row[3]);
event.next.hour = atoi(row[4]);
event.next.day = atoi(row[5]);
event.next.month = atoi(row[6]);
event.next.year = atoi(row[7]);
event.enabled = atoi(row[8])==0?false:true;
event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]);
event.strict = atoi(row[11])==0?false:true;
zone_name = row[12];
std::string t;
EQTime::ToString(&event.next, t);
_log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s",
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, t.c_str());
ret = true;
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in LoadDBEvent query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT id, cond_id, period, "
"next_minute, next_hour, next_day, "
"next_month, next_year, enabled, "
"action, argument, strict, zone "
"FROM spawn_events WHERE id = %d", event_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadDBEvent query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
return(ret);
if (results.RowCount() == 0)
return false;
auto row = results.begin();
event.id = atoi(row[0]);
event.condition_id = atoi(row[1]);
event.period = atoi(row[2]);
event.next.minute = atoi(row[3]);
event.next.hour = atoi(row[4]);
event.next.day = atoi(row[5]);
event.next.month = atoi(row[6]);
event.next.year = atoi(row[7]);
event.enabled = atoi(row[8]) != 0;
event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]);
event.strict = atoi(row[11]) != 0;
zone_name = row[12];
std::string timeAsString;
EQTime::ToString(&event.next, timeAsString);
_log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s", event.enabled? "enabled": "disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, timeAsString.c_str());
return true;
}
bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 instance_id)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int len;
//clear out old stuff..
spawn_conditions.clear();
//load spawn conditions
SpawnCondition cond;
len = MakeAnyLenString(&query, "SELECT id, onchange, value FROM spawn_conditions WHERE zone='%s'", zone_name);
if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query);
while((row = mysql_fetch_row(result))) {
cond.condition_id = atoi(row[0]);
cond.value = atoi(row[2]);
cond.on_change = (SpawnCondition::OnChange) atoi(row[1]);
spawn_conditions[cond.condition_id] = cond;
_log(SPAWNS__CONDITIONS, "Loaded spawn condition %d with value %d and on_change %d", cond.condition_id, cond.value, cond.on_change);
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT id, onchange, value "
"FROM spawn_conditions "
"WHERE zone = '%s'", zone_name);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
//load spawn conditions
SpawnCondition cond;
cond.condition_id = atoi(row[0]);
cond.value = atoi(row[2]);
cond.on_change = (SpawnCondition::OnChange) atoi(row[1]);
spawn_conditions[cond.condition_id] = cond;
_log(SPAWNS__CONDITIONS, "Loaded spawn condition %d with value %d and on_change %d", cond.condition_id, cond.value, cond.on_change);
}
//load values
len = MakeAnyLenString(&query, "SELECT id, value FROM spawn_condition_values WHERE zone='%s' and instance_id=%u", zone_name, instance_id);
if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query);
while((row = mysql_fetch_row(result)))
{
std::map<uint16, SpawnCondition>::iterator iter = spawn_conditions.find(atoi(row[0]));
if(iter != spawn_conditions.end())
{
iter->second.value = atoi(row[1]);
}
}
mysql_free_result(result);
}
else
{
LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query, errbuf);
safe_delete_array(query);
query = StringFormat("SELECT id, value FROM spawn_condition_values "
"WHERE zone = '%s' AND instance_id = %u",
zone_name, instance_id);
results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
spawn_conditions.clear();
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
auto iter = spawn_conditions.find(atoi(row[0]));
if(iter != spawn_conditions.end())
iter->second.value = atoi(row[1]);
}
//load spawn events
SpawnEvent event;
len = MakeAnyLenString(&query,
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict "
"FROM spawn_events WHERE zone='%s'", zone_name);
if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query);
while((row = mysql_fetch_row(result))) {
event.id = atoi(row[0]);
event.condition_id = atoi(row[1]);
event.period = atoi(row[2]);
if(event.period == 0) {
LogFile->write(EQEMuLog::Error, "Refusing to load spawn event #%d because it has a period of 0\n", event.id);
continue;
}
event.next.minute = atoi(row[3]);
event.next.hour = atoi(row[4]);
event.next.day = atoi(row[5]);
event.next.month = atoi(row[6]);
event.next.year = atoi(row[7]);
event.enabled = atoi(row[8])==0?false:true;
event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]);
event.strict = atoi(row[11])==0?false:true;
spawn_events.push_back(event);
_log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d",
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict);
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions events query '%s': %s", query, errbuf);
safe_delete_array(query);
query = StringFormat("SELECT id, cond_id, period, next_minute, next_hour, "
"next_day, next_month, next_year, enabled, action, argument, strict "
"FROM spawn_events WHERE zone = '%s'", zone_name);
results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions events query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
SpawnEvent event;
event.id = atoi(row[0]);
event.condition_id = atoi(row[1]);
event.period = atoi(row[2]);
if(event.period == 0) {
LogFile->write(EQEMuLog::Error, "Refusing to load spawn event #%d because it has a period of 0\n", event.id);
continue;
}
event.next.minute = atoi(row[3]);
event.next.hour = atoi(row[4]);
event.next.day = atoi(row[5]);
event.next.month = atoi(row[6]);
event.next.year = atoi(row[7]);
event.enabled = atoi(row[8])==0?false:true;
event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]);
event.strict = atoi(row[11])==0?false:true;
spawn_events.push_back(event);
_log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d", event.enabled? "enabled": "disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict);
}
//now we need to catch up on events that happened while we were away
//and use them to alter just the condition variables.
@@ -855,18 +827,14 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
TimeOfDay_Struct tod;
zone->zone_time.getEQTimeOfDay(&tod);
std::vector<SpawnEvent>::iterator cur,end;
cur = spawn_events.begin();
end = spawn_events.end();
bool ran;
for(; cur != end; ++cur) {
for(auto cur = spawn_events.begin(); cur != spawn_events.end(); ++cur) {
SpawnEvent &cevent = *cur;
bool StrictCheck = false;
if(cevent.strict &&
cevent.next.hour == tod.hour &&
cevent.next.day == tod.day &&
cevent.next.month == tod.month &&
if(cevent.strict &&
cevent.next.hour == tod.hour &&
cevent.next.day == tod.day &&
cevent.next.month == tod.month &&
cevent.next.year == tod.year)
StrictCheck = true;
@@ -874,43 +842,42 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
if(!cevent.enabled || !StrictCheck)
SetCondition(zone->GetShortName(), zone->GetInstanceID(),cevent.condition_id,0);
if(cevent.enabled)
{
//watch for special case of all 0s, which means to reset next to now
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
memcpy(&cevent.next, &tod, sizeof(cevent.next));
//add one period
EQTime::AddMinutes(cevent.period, &cevent.next);
//save it in the db.
UpdateDBEvent(cevent);
continue; //were done with this event.
}
if(!cevent.enabled)
continue;
ran = false;
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
//this event has been triggered.
//execute the event
if(!cevent.strict || StrictCheck)
ExecEvent(cevent, false);
//add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next);
ran = true;
}
//only write it out if the event actually ran
if(ran) {
//save the event in the DB
UpdateDBEvent(cevent);
}
}
//watch for special case of all 0s, which means to reset next to now
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
memcpy(&cevent.next, &tod, sizeof(cevent.next));
//add one period
EQTime::AddMinutes(cevent.period, &cevent.next);
//save it in the db.
UpdateDBEvent(cevent);
continue; //were done with this event.
}
bool ran = false;
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
//this event has been triggered.
//execute the event
if(!cevent.strict || StrictCheck)
ExecEvent(cevent, false);
//add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next);
ran = true;
}
//only write it out if the event actually ran
if(ran)
UpdateDBEvent(cevent); //save the event in the DB
}
//now our event timers are all up to date, find our closest event.
FindNearestEvent();
return(true);
return true;
}
void SpawnConditionManager::FindNearestEvent() {
@@ -926,7 +893,7 @@ void SpawnConditionManager::FindNearestEvent() {
if(cevent.enabled)
{
//see if this event is before our last nearest
if(EQTime::IsTimeBefore(&next_event, &cevent.next))
if(EQTime::IsTimeBefore(&next_event, &cevent.next))
{
memcpy(&next_event, &cevent.next, sizeof(next_event));
next_id = cevent.id;
@@ -1162,37 +1129,28 @@ int16 SpawnConditionManager::GetCondition(const char *zone_short, uint32 instanc
}
SpawnCondition &cond = condi->second;
return(cond.value);
} else {
//this is a remote spawn condition, grab it from the DB
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int len;
int16 value;
//load spawn conditions
SpawnCondition cond;
len = MakeAnyLenString(&query, "SELECT value FROM spawn_condition_values WHERE zone='%s' AND instance_id=%u AND id=%d",
zone_short, instance_id, condition_id);
if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query);
if((row = mysql_fetch_row(result))) {
value = atoi(row[0]);
} else {
_log(SPAWNS__CONDITIONS, "Unable to load remote condition %d from zone %s in Get request.", condition_id, zone_short);
value = 0; //dunno a better thing to do...
}
mysql_free_result(result);
} else {
_log(SPAWNS__CONDITIONS, "Unable to query remote condition %d from zone %s in Get request.", condition_id, zone_short);
safe_delete_array(query);
value = 0; //dunno a better thing to do...
}
return(value);
return cond.value;
}
//this is a remote spawn condition, grab it from the DB
//load spawn conditions
std::string query = StringFormat("SELECT value FROM spawn_condition_values "
"WHERE zone = '%s' AND instance_id = %u AND id = %d",
zone_short, instance_id, condition_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
_log(SPAWNS__CONDITIONS, "Unable to query remote condition %d from zone %s in Get request.", condition_id, zone_short);
return 0; //dunno a better thing to do...
}
if (results.RowCount() == 0) {
_log(SPAWNS__CONDITIONS, "Unable to load remote condition %d from zone %s in Get request.", condition_id, zone_short);
return 0; //dunno a better thing to do...
}
auto row = results.begin();
return atoi(row[0]);
}
bool SpawnConditionManager::Check(uint16 condition, int16 min_value) {
+75 -93
View File
@@ -23,13 +23,12 @@
#include <iostream>
#include "../common/types.h"
#include "zonedb.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
extern EntityList entity_list;
SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit )
{
SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit ) {
NPCType = in_NPCType;
chance = in_chance;
npc_spawn_limit = in_npc_spawn_limit;
@@ -57,7 +56,6 @@ uint32 SpawnGroup::GetNPCType() {
int npcType = 0;
int totalchance = 0;
//check limits on this spawn group and npc type
if(!entity_list.LimitCheckGroup(id, group_spawn_limit))
return(0);
@@ -68,7 +66,6 @@ uint32 SpawnGroup::GetNPCType() {
for(; cur != end; ++cur) {
SpawnEntry *se = *cur;
//check limits on this spawn group and npc type
if(!entity_list.LimitCheckType(se->NPCType, se->npc_spawn_limit))
continue;
@@ -93,7 +90,6 @@ uint32 SpawnGroup::GetNPCType() {
roll -= se->chance;
}
}
//CODER implement random table
return npcType;
}
@@ -144,106 +140,92 @@ bool SpawnGroupList::RemoveSpawnGroup(uint32 in_id) {
}
bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnGroupList* spawn_group_list) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
// CODER new spawn code
query = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result))
{
safe_delete_array(query);
while((row = mysql_fetch_row(result))) {
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup);
}
mysql_free_result(result);
}
else
{
std::cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, "
"spawngroup.dist, spawngroup.max_x, spawngroup.min_x, "
"spawngroup.max_y, spawngroup.min_y, spawngroup.delay, "
"spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay "
"FROM spawn2, spawngroup WHERE spawn2.spawngroupID = spawngroup.ID "
"AND spawn2.version = %u and zone = '%s'", version, zone_name);
auto results = QueryDatabase(query);
if (!results.Success()) {
_log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query '%s' ", query.c_str());
return false;
}
}
query = 0;
if (RunQuery(query, MakeAnyLenString(&query,
"SELECT DISTINCT spawnentry.spawngroupID, npcid, chance, "
"npc_types.spawn_limit AS sl "
"FROM spawnentry, spawn2, npc_types "
"WHERE spawnentry.npcID=npc_types.id AND spawnentry.spawngroupID=spawn2.spawngroupID "
"AND zone='%s'", zone_name), errbuf, &result)) {
safe_delete_array(query);
while((row = mysql_fetch_row(result)))
{
SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0);
SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0]));
if (sg)
sg->AddSpawnEntry(newSpawnEntry);
else
std::cout << "Error in SpawngroupID: " << row[0] << std::endl;
}
mysql_free_result(result);
}
else
{
std::cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
for (auto row = results.begin(); row != results.end(); ++row) {
SpawnGroup* newSpawnGroup = new SpawnGroup(atoi(row[0]), row[1], atoi(row[2]), atof(row[3]),
atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]),
atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup);
}
query = StringFormat("SELECT DISTINCT spawnentry.spawngroupID, npcid, chance, "
"npc_types.spawn_limit AS sl "
"FROM spawnentry, spawn2, npc_types "
"WHERE spawnentry.npcID=npc_types.id "
"AND spawnentry.spawngroupID = spawn2.spawngroupID "
"AND zone = '%s'", zone_name);
results = QueryDatabase(query);
if (!results.Success()) {
_log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query '%'", query.c_str());
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0);
SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0]));
if (!sg) {
_log(ZONE__SPAWNS, "Error in LoadSpawnGroups %s ", query.c_str());
continue;
}
sg->AddSpawnEntry(newSpawnEntry);
}
// CODER end new spawn code
return true;
}
bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_group_list) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
// CODER new spawn code
query = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result))
{
safe_delete_array(query);
while((row = mysql_fetch_row(result))) {
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup);
}
mysql_free_result(result);
}
else
{
std::cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT DISTINCT(spawngroup.id), spawngroup.name, spawngroup.spawn_limit, "
"spawngroup.dist, spawngroup.max_x, spawngroup.min_x, "
"spawngroup.max_y, spawngroup.min_y, spawngroup.delay, "
"spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay "
"FROM spawngroup WHERE spawngroup.ID = '%i'", spawngroupid);
auto results = QueryDatabase(query);
if (!results.Success()) {
_log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query %s", query.c_str());
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
SpawnGroup* newSpawnGroup = new SpawnGroup(atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup);
}
query = StringFormat("SELECT DISTINCT(spawnentry.spawngroupID), spawnentry.npcid, "
"spawnentry.chance, spawngroup.spawn_limit FROM spawnentry, spawngroup "
"WHERE spawnentry.spawngroupID = '%i' AND spawngroup.spawn_limit = '0' "
"ORDER BY chance", spawngroupid);
results = QueryDatabase(query);
if (!results.Success()) {
_log(ZONE__SPAWNS, "Error3 in PopulateZoneLists query '%s'", query.c_str());
return false;
}
query = 0;
if (RunQuery(query, MakeAnyLenString(&query,
"SELECT DISTINCT spawnentry.spawngroupID, spawnentry.npcid, spawnentry.chance, spawngroup.spawn_limit FROM spawnentry,spawngroup WHERE spawnentry.spawngroupID='%i' AND spawngroup.spawn_limit='0' ORDER by chance", spawngroupid), errbuf, &result)) {
safe_delete_array(query);
while((row = mysql_fetch_row(result)))
{
SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0);
SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0]));
if (sg)
sg->AddSpawnEntry(newSpawnEntry);
else
std::cout << "Error in SpawngroupID: " << row[0] << std::endl;
}
mysql_free_result(result);
}
else
{
std::cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return false;
}
for(auto row = results.begin(); row != results.end(); ++row) {
SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0);
SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0]));
if (!sg) {
_log(ZONE__SPAWNS, "Error in SpawngroupID: %s ", row[0]);
continue;
}
sg->AddSpawnEntry(newSpawnEntry);
}
// CODER end new spawn code
return true;
}
+178 -231
View File
@@ -23,13 +23,11 @@
#include <math.h>
#include "masterentity.h"
#include "StringIDs.h"
#include "../common/MiscFunctions.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/rulesys.h"
int Mob::GetKickDamage() {
int multiple=(GetLevel()*100/5);
multiple += 100;
@@ -64,11 +62,9 @@ int Mob::GetBashDamage() {
}
void Mob::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) {
int item_slot = -1;
//1: Apply bonus from AC (BOOT/SHIELD/HANDS) est. 40AC=6dmg
if (IsClient()){
switch (skill){
case SkillFlyingKick:
@@ -112,10 +108,8 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
if(hate_override > -1)
hate = hate_override;
if(skill == SkillBash)
{
if(IsClient())
{
if(skill == SkillBash){
if(IsClient()){
ItemInst *item = CastToClient()->GetInv().GetItem(MainSecondary);
if(item)
{
@@ -186,6 +180,10 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer;
/* Check to see if actually have skill */
if (!MaxSkill(static_cast<SkillUseTypes>(ca_atk->m_skill)))
return;
if(GetTarget()->GetID() != ca_atk->m_target)
return; //invalid packet.
@@ -274,8 +272,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
return;
}
if ((ca_atk->m_atk == 100) && (ca_atk->m_skill == SkillFrenzy))
{
if ((ca_atk->m_atk == 100) && (ca_atk->m_skill == SkillFrenzy)){
CheckIncreaseSkill(SkillFrenzy, GetTarget(), 10);
int AtkRounds = 3;
int skillmod = 100*GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy);
@@ -311,8 +308,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
return;
}
switch(GetClass())
{
switch(GetClass()){
case BERSERKER:
case WARRIOR:
case RANGER:
@@ -384,8 +380,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
}
ReuseTime = (ReuseTime*HasteMod)/100;
if(ReuseTime > 0)
{
if(ReuseTime > 0){
p_timers.Start(pTimerCombatAbility, ReuseTime);
}
}
@@ -403,8 +398,7 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type)
SkillUseTypes skill_type; //to avoid casting... even though it "would work"
uint8 itemslot = MainFeet;
switch(unchecked_type)
{
switch(unchecked_type){
case SkillFlyingKick:{
skill_type = SkillFlyingKick;
max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, FlyingKickBonus) / 100) + 35;
@@ -484,8 +478,7 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type)
else
ht = ndamage = MakeRandomInt(min_dmg, max_dmg);
}
else
{
else{
ht = max_dmg;
}
}
@@ -535,7 +528,6 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
RogueBackstab(other,false,ReuseTime);
if (level > 54) {
if(IsClient() && CastToClient()->CheckDoubleAttack(false))
{
if(other->GetHP() > 0)
@@ -619,12 +611,10 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
}
// determine minimum hits
if (level < 51)
{
if (level < 51) {
min_hit = (level*15/10);
}
else
{
else {
// Trumpcard: Replaced switch statement with formula calc. This will give minhit increases all the way to 65.
min_hit = (level * ( level*5 - 105)) / 100;
}
@@ -636,8 +626,7 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
if(min_damage){
ndamage = min_hit;
}
else
{
else {
if (max_hit < min_hit)
max_hit = min_hit;
@@ -645,7 +634,6 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
ndamage = max_hit;
else
ndamage = MakeRandomInt(min_hit, max_hit);
}
}
}
@@ -792,28 +780,24 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) {
return;
}
SendItemAnimation(GetTarget(), AmmoItem, SkillArchery);
SendItemAnimation(GetTarget(), AmmoItem, SkillArchery);
DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo);
//EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow.
int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile;
if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && MakeRandomInt(0,99) > ChanceAvoidConsume)){
if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && MakeRandomInt(0,99) > ChanceAvoidConsume)){
DeleteItemInInventory(ammo_slot, 1, true);
mlog(COMBAT__RANGED, "Consumed one arrow from slot %d", ammo_slot);
} else {
mlog(COMBAT__RANGED, "Endless Quiver prevented ammo consumption.");
}
CheckIncreaseSkill(SkillArchery, GetTarget(), -15);
CheckIncreaseSkill(SkillArchery, GetTarget(), -15);
CommonBreakInvisible();
}
void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime)
{
void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime) {
if (!CanDoSpecialAttack(other))
return;
@@ -842,8 +826,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
if (focus) //From FcBaseEffects
WDmg += WDmg*focus/100;
if((WDmg > 0) || (ADmg > 0))
{
if((WDmg > 0) || (ADmg > 0)) {
if(WDmg < 0)
WDmg = 0;
if(ADmg < 0)
@@ -944,8 +927,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
}
//try proc on hits and misses
if((RangeWeapon != nullptr) && GetTarget() && other && !other->HasDied())
{
if((RangeWeapon != nullptr) && GetTarget() && other && !other->HasDied()){
TryWeaponProc(RangeWeapon, other, MainRange);
}
@@ -965,126 +947,134 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
void NPC::RangedAttack(Mob* other)
{
if (!other)
return;
//make sure the attack and ranged timers are up
//if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow
if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))
{
if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())){
mlog(COMBAT__RANGED, "Archery canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime());
return;
}
//if we have SPECATK_RANGED_ATK set then we range attack without weapon or ammo
const Item_Struct* weapon = nullptr;
const Item_Struct* ammo = nullptr;
if(!GetSpecialAbility(SPECATK_RANGED_ATK))
{
//find our bow and ammo return if we can't find them...
if(!CheckLosFN(other))
return;
}
int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0); //Min Range of NPC attack
int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack
int attacks = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0);
attacks = attacks > 0 ? attacks : 1;
for(int i = 0; i < attacks; ++i) {
float min_range = static_cast<float>(RuleI(Combat, MinRangedAttackDist));
float max_range = 250; // needs to be longer than 200(most spells)
if (sa_max_range)
max_range = static_cast<float>(sa_max_range);
if (sa_min_range)
min_range = static_cast<float>(sa_min_range);
mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", max_range);
max_range *= max_range;
if(DistNoRootNoZ(*other) > max_range) {
mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*other), max_range);
//target is out of range, client does a message
return;
}
else if(DistNoRootNoZ(*other) < (min_range * min_range))
return;
if(!other || !IsAttackAllowed(other) ||
IsCasting() ||
DivineAura() ||
IsStunned() ||
IsFeared() ||
IsMezzed() ||
(GetAppearance() == eaDead)){
return;
}
SkillUseTypes skillinuse = SkillArchery;
skillinuse = static_cast<SkillUseTypes>(GetRangedSkill());
if(!ammo && !GetAmmoIDfile())
ammo = database.GetItem(8005);
if(ammo)
SendItemAnimation(other, ammo, SkillArchery);
else
ProjectileAnimation(other, 0,false,0,0,0,0,GetAmmoIDfile(),skillinuse);
FaceTarget(other);
if (!other->CheckHitChance(this, skillinuse, MainRange, GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2)))
{
mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName());
other->Damage(this, 0, SPELL_UNKNOWN, skillinuse);
}
else
{
int16 WDmg = GetWeaponDamage(other, weapon);
int16 ADmg = GetWeaponDamage(other, ammo);
int32 TotalDmg = 0;
if(WDmg > 0 || ADmg > 0)
//if we have SPECATK_RANGED_ATK set then we range attack without weapon or ammo
const Item_Struct* weapon = nullptr;
const Item_Struct* ammo = nullptr;
if(!GetSpecialAbility(SPECATK_RANGED_ATK))
{
mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName());
int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types
int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier);
if(RuleB(Combat, UseIntervalAC))
TotalDmg = MaxDmg;
else
TotalDmg = MakeRandomInt(MinDmg, MaxDmg);
TotalDmg += TotalDmg * GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3) / 100; //Damage modifier
other->AvoidDamage(this, TotalDmg, false);
other->MeleeMitigation(this, TotalDmg, MinDmg);
if (TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, skillinuse);
//find our bow and ammo return if we can't find them...
return;
}
int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 4); //Min Range of NPC attack
int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack
float min_range = static_cast<float>(RuleI(Combat, MinRangedAttackDist));
float max_range = 250; // needs to be longer than 200(most spells)
if (sa_max_range)
max_range = static_cast<float>(sa_max_range);
if (sa_min_range)
min_range = static_cast<float>(sa_min_range);
mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", max_range);
max_range *= max_range;
if(DistNoRootNoZ(*other) > max_range) {
mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*other), max_range);
//target is out of range, client does a message
return;
}
else if(DistNoRootNoZ(*other) < (min_range * min_range))
return;
if(!other || !IsAttackAllowed(other) ||
IsCasting() ||
DivineAura() ||
IsStunned() ||
IsFeared() ||
IsMezzed() ||
(GetAppearance() == eaDead)){
return;
}
SkillUseTypes skillinuse = SkillArchery;
skillinuse = static_cast<SkillUseTypes>(GetRangedSkill());
if(!ammo && !GetAmmoIDfile())
ammo = database.GetItem(8005);
if(ammo)
SendItemAnimation(other, ammo, SkillArchery);
else
ProjectileAnimation(other, 0,false,0,0,0,0,GetAmmoIDfile(),skillinuse);
FaceTarget(other);
if (!other->CheckHitChance(this, skillinuse, MainRange, GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2)))
{
mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName());
other->Damage(this, 0, SPELL_UNKNOWN, skillinuse);
}
else
TotalDmg = -5;
{
int16 WDmg = GetWeaponDamage(other, weapon);
int16 ADmg = GetWeaponDamage(other, ammo);
int32 TotalDmg = 0;
if(WDmg > 0 || ADmg > 0)
{
mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName());
int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types
int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier);
if (TotalDmg > 0)
other->AddToHateList(this, TotalDmg, 0, false);
else
other->AddToHateList(this, 0, 0, false);
if(RuleB(Combat, UseIntervalAC))
TotalDmg = MaxDmg;
else
TotalDmg = MakeRandomInt(MinDmg, MaxDmg);
other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillinuse);
TotalDmg += TotalDmg * GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3) / 100; //Damage modifier
other->AvoidDamage(this, TotalDmg, false);
other->MeleeMitigation(this, TotalDmg, MinDmg);
if (TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, skillinuse);
}
if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && !other->HasDied())
TrySkillProc(other, skillinuse, 0, true, MainRange);
else
TotalDmg = -5;
if (TotalDmg > 0)
other->AddToHateList(this, TotalDmg, 0, false);
else
other->AddToHateList(this, 0, 0, false);
other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillinuse);
if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && !other->HasDied())
TrySkillProc(other, skillinuse, 0, true, MainRange);
}
//try proc on hits and misses
if(other && !other->HasDied())
TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange);
if (HasSkillProcs() && other && !other->HasDied())
TrySkillProc(other, skillinuse, 0, false, MainRange);
CommonBreakInvisible();
}
//try proc on hits and misses
if(other && !other->HasDied())
TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange);
if (HasSkillProcs() && other && !other->HasDied())
TrySkillProc(other, skillinuse, 0, false, MainRange);
CommonBreakInvisible();
}
uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg)
{
uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) {
uint16 MaxDmg = (((2 * wDmg) * GetDamageTable(SkillThrowing)) / 100);
if (MaxDmg == 0)
@@ -1096,8 +1086,7 @@ uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg)
TotalDmg = MakeRandomInt(1, MaxDmg);
minDmg = 1;
if(GetLevel() > 25)
{
if(GetLevel() > 25){
TotalDmg += ((GetLevel()-25)/3);
minDmg += ((GetLevel()-25)/3);
minDmg += minDmg * GetMeleeMinDamageMod_SE(SkillThrowing) / 100;
@@ -1190,9 +1179,8 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51
//consume ammo
DeleteItemInInventory(ammo_slot, 1, true);
CheckIncreaseSkill(SkillThrowing, GetTarget());
CommonBreakInvisible();
CheckIncreaseSkill(SkillThrowing, GetTarget());
CommonBreakInvisible();
}
void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime)
@@ -1222,8 +1210,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
if (GetClass() == ROGUE && (BehindMob(other, GetX(), GetY())))
Assassinate_Dmg = TryAssassinate(other, SkillThrowing, ranged_timer.GetDuration());
if(WDmg > 0)
{
if(WDmg > 0){
int minDmg = 1;
uint16 MaxDmg = GetThrownDamage(WDmg, TotalDmg, minDmg);
@@ -1313,8 +1300,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil
safe_delete(outapp);
}
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) {
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) {
if (!to)
return;
@@ -1448,17 +1434,11 @@ void NPC::DoClassAttacks(Mob *target) {
break;
case MONK: case MONKGM: {
uint8 satype = SkillKick;
if(level > 29) {
satype = SkillFlyingKick;
} else if(level > 24) {
satype = SkillDragonPunch;
} else if(level > 19) {
satype = SkillEagleStrike;
} else if(level > 9) {
satype = SkillTigerClaw;
} else if(level > 4) {
satype = SkillRoundKick;
}
if(level > 29) { satype = SkillFlyingKick; }
else if(level > 24) { satype = SkillDragonPunch; }
else if(level > 19) { satype = SkillEagleStrike; }
else if(level > 9) { satype = SkillTigerClaw; }
else if(level > 4) { satype = SkillRoundKick; }
reuse = MonkSpecialAttack(target, satype);
reuse *= 1000;
@@ -1467,8 +1447,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
case WARRIOR: case WARRIORGM:{
if(level >= RuleI(Combat, NPCBashKickLevel)){
if(MakeRandomInt(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference.
{
if(MakeRandomInt(0, 100) > 25){ //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference.
DoAnim(animKick);
int32 dmg = 0;
@@ -1489,8 +1468,7 @@ void NPC::DoClassAttacks(Mob *target) {
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
did_attack = true;
}
else
{
else {
DoAnim(animTailRake);
int32 dmg = 0;
@@ -1513,8 +1491,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
break;
}
case BERSERKER: case BERSERKERGM:
{
case BERSERKER: case BERSERKERGM:{
int AtkRounds = 3;
int32 max_dmg = 26 + ((GetLevel()-6) * 2);
int32 min_dmg = 0;
@@ -1530,8 +1507,7 @@ void NPC::DoClassAttacks(Mob *target) {
reuse = FrenzyReuseTime * 1000;
while(AtkRounds > 0) {
while(AtkRounds > 0) {
if (GetTarget() && (AtkRounds == 1 || MakeRandomInt(0,100) < 75)){
DoSpecialAttackDamage(GetTarget(), SkillFrenzy, max_dmg, min_dmg, -1 , reuse, true);
}
@@ -1569,8 +1545,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
case CLERIC: case CLERICGM: //clerics can bash too.
case SHADOWKNIGHT: case SHADOWKNIGHTGM:
case PALADIN: case PALADINGM:
{
case PALADIN: case PALADINGM:{
if(level >= RuleI(Combat, NPCBashKickLevel)){
DoAnim(animTailRake);
int32 dmg = 0;
@@ -1610,8 +1585,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
return;
//check range for all these abilities, they are all close combat stuff
if(!CombatRange(ca_target))
{
if(!CombatRange(ca_target)){
return;
}
@@ -1633,10 +1607,8 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
uint16 skill_to_use = -1;
if (skill == -1){
switch(GetClass())
{
if (skill == -1){
switch(GetClass()){
case WARRIOR:
case RANGER:
case BEASTLORD:
@@ -1687,14 +1659,11 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
if(skill_to_use == -1)
return;
if(skill_to_use == SkillBash)
{
if (ca_target!=this)
{
if(skill_to_use == SkillBash) {
if (ca_target!=this) {
DoAnim(animTailRake);
if(GetWeaponDamage(ca_target, GetInv().GetItem(MainSecondary)) <= 0 &&
GetWeaponDamage(ca_target, GetInv().GetItem(MainShoulders)) <= 0){
if(GetWeaponDamage(ca_target, GetInv().GetItem(MainSecondary)) <= 0 && GetWeaponDamage(ca_target, GetInv().GetItem(MainShoulders)) <= 0){
dmg = -5;
}
else{
@@ -1715,16 +1684,14 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
DoSpecialAttackDamage(ca_target, SkillBash, dmg, 1,-1,ReuseTime);
if(ReuseTime > 0 && !IsRiposte)
{
if(ReuseTime > 0 && !IsRiposte) {
p_timers.Start(pTimerCombatAbility, ReuseTime);
}
}
return;
}
if(skill_to_use == SkillFrenzy)
{
if(skill_to_use == SkillFrenzy){
CheckIncreaseSkill(SkillFrenzy, GetTarget(), 10);
int AtkRounds = 3;
int skillmod = 100*GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy);
@@ -1758,10 +1725,8 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
return;
}
if(skill_to_use == SkillKick)
{
if(ca_target!=this)
{
if(skill_to_use == SkillKick){
if(ca_target!=this){
DoAnim(animKick);
if(GetWeaponDamage(ca_target, GetInv().GetItem(MainFeet)) <= 0){
@@ -1785,12 +1750,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
}
if(skill_to_use == SkillFlyingKick ||
skill_to_use == SkillDragonPunch ||
skill_to_use == SkillEagleStrike ||
skill_to_use == SkillTigerClaw ||
skill_to_use == SkillRoundKick)
{
if(skill_to_use == SkillFlyingKick || skill_to_use == SkillDragonPunch || skill_to_use == SkillEagleStrike || skill_to_use == SkillTigerClaw || skill_to_use == SkillRoundKick) {
ReuseTime = MonkSpecialAttack(ca_target, skill_to_use) - 1;
MonkSpecialAttack(ca_target, skill_to_use);
@@ -1804,8 +1764,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]);
int TripleChance = 25;
int TripleChance = 25;
if (bDoubleSpecialAttack > 100)
TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100;
@@ -1815,8 +1774,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
}
if(skill_to_use == SkillBackstab)
{
if(skill_to_use == SkillBackstab){
ReuseTime = BackstabReuseTime-1;
if (IsRiposte)
@@ -1826,8 +1784,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
ReuseTime = (ReuseTime*HasteMod)/100;
if(ReuseTime > 0 && !IsRiposte)
{
if(ReuseTime > 0 && !IsRiposte){
p_timers.Start(pTimerCombatAbility, ReuseTime);
}
}
@@ -1894,8 +1851,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) {
tauntchance /= 100.0f;
if (tauntchance > MakeRandomFloat(0, 1)) {
if (tauntchance > MakeRandomFloat(0, 1)) {
if (hate_top && hate_top != this){
newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1;
who->CastToNPC()->AddToHateList(this, newhate);
@@ -1917,8 +1873,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) {
if (HasSkillProcs())
TrySkillProc(who, SkillTaunt, TauntReuseTime*1000);
if (Success && HasSkillProcSuccess())
TrySkillProc(who, SkillTaunt, TauntReuseTime*1000, true);
}
@@ -1939,8 +1894,7 @@ void Mob::InstillDoubt(Mob *who) {
if(!CombatRange(who))
return;
if(IsClient())
{
if(IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillIntimidation, who, 10);
}
@@ -1970,14 +1924,12 @@ void Mob::InstillDoubt(Mob *who) {
}
}
uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
//Only works on YOUR target.
if(defender && (defender->GetBodyType() == BT_Humanoid) && !defender->IsClient()
&& (skillInUse == SkillArchery) && (GetTarget() == defender)) {
uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1];
uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1];
uint8 HeadShot_Level = 0; //Get Highest Headshot Level
HeadShot_Level = aabonuses.HSLevel;
if (HeadShot_Level < spellbonuses.HSLevel)
@@ -1985,8 +1937,7 @@ uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
else if (HeadShot_Level < itembonuses.HSLevel)
HeadShot_Level = itembonuses.HSLevel;
if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){
if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){
float ProcChance = GetSpecialProcChances(MainRange);
if(ProcChance > MakeRandomFloat(0,1))
return HeadShot_Dmg;
@@ -2079,7 +2030,7 @@ float Mob::GetAssassinateProcChances(uint16 ReuseTime)
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
/*Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up.*/
/* Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up. */
ProcChance = (10 + (static_cast<float>(mydex/10) + static_cast<float>(itembonuses.HeroicDEX /10)))/100.0f;
}
@@ -2092,8 +2043,10 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
if (!CanDoSpecialAttack(other))
return;
//For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically.
//Kayen: This is unlikely to be completely accurate but use OFFENSE skill value for these effects.
/*
For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically.
Kayen: This is unlikely to be completely accurate but use OFFENSE skill value for these effects.
*/
if (skillinuse == SkillBegging)
skillinuse = SkillOffense;
@@ -2102,8 +2055,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
int Hand = MainPrimary;
if (hate == 0 && weapon_damage > 1) hate = weapon_damage;
if(weapon_damage > 0){
if(weapon_damage > 0){
if (focus) //From FcBaseEffects
weapon_damage += weapon_damage*focus/100;
@@ -2113,12 +2065,10 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
}
int32 min_hit = 1;
int32 max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100;
if(GetLevel() >= 28 && IsWarriorClass() )
{
int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr );
int32 max_hit = (2 * weapon_damage*GetDamageTable(skillinuse)) / 100;
if(GetLevel() >= 28 && IsWarriorClass() ) {
int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr );
min_hit += (int) ucDamageBonus;
max_hit += (int) ucDamageBonus;
hate += ucDamageBonus;
@@ -2137,8 +2087,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
}
}
ApplySpecialAttackMod(skillinuse, max_hit, min_hit);
ApplySpecialAttackMod(skillinuse, max_hit, min_hit);
min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100;
if(max_hit < min_hit)
@@ -2199,8 +2148,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
TrySkillProc(other, skillinuse, ReuseTime, true);
}
bool Mob::CanDoSpecialAttack(Mob *other)
{
bool Mob::CanDoSpecialAttack(Mob *other) {
//Make sure everything is valid before doing any attacks.
if (!other) {
SetTarget(nullptr);
@@ -2210,8 +2158,7 @@ bool Mob::CanDoSpecialAttack(Mob *other)
if(!GetTarget())
SetTarget(other);
if ((other == nullptr || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead))
|| HasDied() || (!IsAttackAllowed(other)))) {
if ((other == nullptr || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) || HasDied() || (!IsAttackAllowed(other)))) {
return false;
}
+11 -11
View File
@@ -20,7 +20,7 @@
#include "masterentity.h"
#include "../common/packet_dump.h"
#include "../common/moremath.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "worldserver.h"
#include "../common/skills.h"
#include "../common/bodytypes.h"
@@ -33,8 +33,8 @@
#include "../common/unix.h"
#endif
#include "StringIDs.h"
#include "QuestParserCollection.h"
#include "string_ids.h"
#include "quest_parser_collection.h"
extern Zone* zone;
extern volatile bool ZoneLoaded;
@@ -4761,24 +4761,24 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
case SE_LimitSpellClass:
if(focus_spell.base[i] < 0) { //Exclude
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass));
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass))
return(0);
}
}
else {
LimitInclude[12] = true;
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)); //Include
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) //Include
LimitInclude[13] = true;
}
break;
case SE_LimitSpellSubclass:
if(focus_spell.base[i] < 0) { //Exclude
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass));
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass))
return(0);
}
}
else {
LimitInclude[14] = true;
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)); //Include
if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) //Include
LimitInclude[15] = true;
}
break;
@@ -5093,7 +5093,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) {
if (IsValidSpell(proc_spellid)){
ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base[0], TempItemAug->ProcRate);
ProcChance = GetSympatheticProcChances(spell_id, spells[TempItemAug->Focus.Effect].base[0], TempItemAug->ProcRate);
if(MakeRandomFloat(0, 1) <= ProcChance)
SympatheticProcList.push_back(proc_spellid);
@@ -6470,4 +6470,4 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster)
SetSpellPowerDistanceMod(static_cast<int>(mod));
}
}
}
+79 -91
View File
@@ -71,13 +71,13 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include "masterentity.h"
#include "../common/packet_dump.h"
#include "../common/moremath.h"
#include "../common/Item.h"
#include "../common/item.h"
#include "worldserver.h"
#include "../common/skills.h"
#include "../common/bodytypes.h"
#include "../common/classes.h"
#include "../common/rulesys.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include <math.h>
#include <assert.h>
@@ -90,8 +90,8 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include "../common/packet_dump_file.h"
#endif
#include "StringIDs.h"
#include "QuestParserCollection.h"
#include "string_ids.h"
#include "quest_parser_collection.h"
extern Zone* zone;
extern volatile bool ZoneLoaded;
@@ -1252,7 +1252,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
}
}
if(IsClient()) {
if(IsClient()) {
CheckNumHitsRemaining(NUMHIT_MatchingSpells);
TrySympatheticProc(target, spell_id);
}
@@ -1378,7 +1378,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
mlog(AA__MESSAGE, "Project Illusion overwrote target caster: %s spell id: %d was ON", GetName(), spell_id);
targetType = ST_GroupClientAndPet;
}
if (spell_target && !spell_target->PassCastRestriction(true, spells[spell_id].CastRestriction)){
Message_StringID(13,SPELL_NEED_TAR);
return false;
@@ -1386,16 +1386,16 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
//Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets)
if (!spells[spell_id].InCombat && spells[spell_id].OutofCombat){
if (IsDetrimentalSpell(spell_id)) {
if ( (spell_target->IsNPC() && spell_target->IsEngaged()) ||
if (IsDetrimentalSpell(spell_id)) {
if ( (spell_target->IsNPC() && spell_target->IsEngaged()) ||
(spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount())){
Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string
return false;
}
}
else if (IsBeneficialSpell(spell_id)) {
if ( (IsNPC() && IsEngaged()) ||
else if (IsBeneficialSpell(spell_id)) {
if ( (IsNPC() && IsEngaged()) ||
(IsClient() && CastToClient()->GetAggroCount())){
if (IsDiscipline(spell_id))
Message_StringID(13,NO_ABILITY_IN_COMBAT);
@@ -1409,16 +1409,16 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
//Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets)
else if (spells[spell_id].InCombat && !spells[spell_id].OutofCombat){
if (IsDetrimentalSpell(spell_id)) {
if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) ||
if (IsDetrimentalSpell(spell_id)) {
if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) ||
(spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount())){
Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string
return false;
}
}
else if (IsBeneficialSpell(spell_id)) {
if ( (IsNPC() && !IsEngaged()) ||
else if (IsBeneficialSpell(spell_id)) {
if ( (IsNPC() && !IsEngaged()) ||
(IsClient() && !CastToClient()->GetAggroCount())){
if (IsDiscipline(spell_id))
Message_StringID(13,NO_ABILITY_OUT_OF_COMBAT);
@@ -1782,10 +1782,10 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
case ST_PetMaster:
{
Mob *owner = nullptr;
if (IsPet())
if (IsPet())
owner = GetOwner();
else if ((IsNPC() && CastToNPC()->GetSwarmOwner()))
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
@@ -1975,7 +1975,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
if (!TrySpellProjectile(spell_target, spell_id))
return false;
}
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
// Prevent mana usage/timers being set for beneficial buffs
@@ -2143,7 +2143,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
{
if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){
(*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this);
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust);
SpellOnTarget(spell_id, (*iter), false, true, resist_adjust);
}
}
}
@@ -2723,7 +2723,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
if ((effect2 == SE_BStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_CStacker)))
return -1;
}
if (spellbonuses.DStacker[0]) {
if ((effect2 == SE_DStacker) && (sp2.effectid[i] <= spellbonuses.DStacker[1]))
return -1;
@@ -2775,7 +2775,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.",
sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value);
}
}
}
}
} else {
mlog(SPELLS__STACKING, "%s (%d) and %s (%d) appear to be in the same line, skipping Stacking Overwrite/Blocking checks",
@@ -3564,7 +3564,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) )
{
mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName());
if (spells[spell_id].resisttype == RESIST_PHYSICAL){
Message_StringID(MT_SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name);
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
@@ -3707,7 +3707,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
TrySpellTrigger(spelltar, spell_id);
TryApplyEffect(spelltar, spell_id);
if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) {
int32 aggro_amount = CheckAggroAmount(spell_id, isproc);
mlog(SPELLS__CASTING, "Spell %d cast on %s generated %d hate", spell_id, spelltar->GetName(), aggro_amount);
@@ -3733,7 +3733,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
safe_delete(action_packet);
return false;
}
// cause the effects to the target
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness))
{
@@ -3746,11 +3746,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
return false;
}
if (IsDetrimentalSpell(spell_id)) {
CheckNumHitsRemaining(NUMHIT_OutgoingSpells);
if (spelltar)
spelltar->CheckNumHitsRemaining(NUMHIT_IncomingSpells);
}
@@ -4408,7 +4408,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
if (CharismaCheck)
{
/*
/*
Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 cha)
Charisma less than ~ 75 gives a postive modifier to resist checks at approximate ratio of -10 CHA = +6 Resist.
Mez spells do same initial resist check as a above.
@@ -4470,7 +4470,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
if (CharmTick) {
int min_charmbreakchance = ((100/RuleI(Spells, CharmBreakCheckChance))/66 * 100)*2;
if (resist_chance < min_charmbreakchance)
resist_chance = min_charmbreakchance;
}
@@ -4965,68 +4965,56 @@ int Client::FindSpellBookSlotBySpellID(uint16 spellid) {
return -1; //default
}
bool Client::SpellGlobalCheck(uint16 Spell_ID, uint16 Char_ID) {
bool Client::SpellGlobalCheck(uint16 spell_ID, uint16 char_ID) {
std::string Spell_Global_Name;
int Spell_Global_Value;
int Global_Value;
std::string spell_Global_Name;
int spell_Global_Value;
int global_Value;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT qglobal, value FROM spell_globals WHERE spellid=%i", Spell_ID), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
Spell_Global_Name = row[0];
Spell_Global_Value = atoi(row[1]);
mysql_free_result(result);
if (Spell_Global_Name.empty()) { // If the entry in the spell_globals table has nothing set for the qglobal name
return true;
}
else if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT value FROM quest_globals WHERE charid=%i AND name='%s'", Char_ID, Spell_Global_Name.c_str()), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
Global_Value = atoi(row[0]);
mysql_free_result(result);
if (Global_Value == Spell_Global_Value) { // If the values match from both tables, allow the spell to be scribed
return true;
}
else if (Global_Value > Spell_Global_Value) { // Check if the qglobal value is greater than the require spellglobal value
return true;
}
else // If no matching result found in qglobals, don't scribe this spell
{
LogFile->write(EQEMuLog::Error, "Char ID: %i Spell_globals Name: '%s' Value: '%i' did not match QGlobal Value: '%i' for Spell ID %i", Char_ID, Spell_Global_Name.c_str(), Spell_Global_Value, Global_Value, Spell_ID);
return false;
}
}
else
LogFile->write(EQEMuLog::Error, "Char ID: %i does not have the Qglobal Name: '%s' for Spell ID %i", Char_ID, Spell_Global_Name.c_str(), Spell_ID);
safe_delete_array(query);
}
else
LogFile->write(EQEMuLog::Error, "Spell ID %i query of spell_globals with Name: '%s' Value: '%i' failed", Spell_ID, Spell_Global_Name.c_str(), Spell_Global_Value);
}
else {
return true; // Spell ID isn't listed in the spells_global table, so it is not restricted from scribing
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error while querying Spell ID %i spell_globals table query '%s': %s", Spell_ID, query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT qglobal, value FROM spell_globals "
"WHERE spellid = %i", spell_ID);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error while querying Spell ID %i spell_globals table query '%s': %s", spell_ID, query.c_str(), results.ErrorMessage().c_str());
return false; // Query failed, so prevent spell from scribing just in case
}
return false; // Default is false
}
if (results.RowCount() != 1)
return true; // Spell ID isn't listed in the spells_global table, so it is not restricted from scribing
auto row = results.begin();
spell_Global_Name = row[0];
spell_Global_Value = atoi(row[1]);
if (spell_Global_Name.empty())
return true; // If the entry in the spell_globals table has nothing set for the qglobal name
query = StringFormat("SELECT value FROM quest_globals "
"WHERE charid = %i AND name = '%s'",
char_ID, spell_Global_Name.c_str());
results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Spell ID %i query of spell_globals with Name: '%s' Value: '%i' failed", spell_ID, spell_Global_Name.c_str(), spell_Global_Value);
return false;
}
if (results.RowCount() != 1) {
LogFile->write(EQEMuLog::Error, "Char ID: %i does not have the Qglobal Name: '%s' for Spell ID %i", char_ID, spell_Global_Name.c_str(), spell_ID);
return false;
}
row = results.begin();
global_Value = atoi(row[0]);
if (global_Value == spell_Global_Value)
return true; // If the values match from both tables, allow the spell to be scribed
else if (global_Value > spell_Global_Value)
return true; // Check if the qglobal value is greater than the require spellglobal value
// If no matching result found in qglobals, don't scribe this spell
LogFile->write(EQEMuLog::Error, "Char ID: %i Spell_globals Name: '%s' Value: '%i' did not match QGlobal Value: '%i' for Spell ID %i", char_ID, spell_Global_Name.c_str(), spell_Global_Value, global_Value, spell_ID);
return false;
}
// TODO get rid of this
@@ -5081,7 +5069,7 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) {
}
bool Mob::IsCombatProc(uint16 spell_id) {
if (RuleB(Spells, FocusCombatProcs))
return false;
@@ -5092,7 +5080,7 @@ bool Mob::IsCombatProc(uint16 spell_id) {
{
for (int i = 0; i < MAX_PROCS; i++){
if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id
if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id
|| RangedProcs[i].spellID == spell_id){
return true;
}
@@ -5150,7 +5138,7 @@ bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id
{
if(spell_id == SPELL_UNKNOWN)
return(false);
int i;
for (i = 0; i < MAX_PROCS; i++) {
if (DefensiveProcs[i].spellID == SPELL_UNKNOWN) {
+20 -12
View File
@@ -27,24 +27,23 @@ Copyright (C) 2001-2008 EQEMu Development Team (http://eqemulator.net)
#define strcasecmp _stricmp
#endif
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "masterentity.h"
#include "../common/features.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "mob.h"
#include "queryserv.h"
extern QueryServ* QServ;
TaskManager::TaskManager() {
for(int i=0; i<MAXTASKS; i++)
Tasks[i] = nullptr;
}
TaskManager::~TaskManager() {
for(int i=0; i<MAXTASKS; i++) {
if(Tasks[i] != nullptr) {
for(int j=0; j<Tasks[i]->ActivityCount; j++) {
@@ -61,11 +60,8 @@ TaskManager::~TaskManager() {
}
bool TaskManager::LoadTaskSets() {
const char *TaskSetQuery = "SELECT `id`, `taskid` from `tasksets` WHERE `id` > 0 AND `id` < %i "
"AND `taskid` >= 0 AND `taskid` < %i ORDER BY `id`, `taskid` ASC";
const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTaskSets: %s";
char errbuf[MYSQL_ERRMSG_SIZE];
@@ -1984,12 +1980,17 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
// Inform the client the task has been updated, both by a chat message
c->Message(0, "Your task '%s' has been updated.", Task->Title);
if(Task->Activity[ActivityID].GoalMethod != METHODQUEST)
{
if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) {
char buf[24];
snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID);
buf[23] = '\0';
parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0);
/* QS: PlayerLogTaskUpdates :: Update */
if (RuleB(QueryServ, PlayerLogTaskUpdates)){
std::string event_desc = StringFormat("Task Stage Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, c->GetZoneID(), c->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc);
}
}
// If this task is now complete, the Completed tasks will have been
@@ -2001,6 +2002,12 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
buf[23] = '\0';
parse->EventPlayer(EVENT_TASK_COMPLETE, c, buf, 0);
/* QS: PlayerLogTaskUpdates :: Complete */
if (RuleB(QueryServ, PlayerLogTaskUpdates)){
std::string event_desc = StringFormat("Task Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, c->GetZoneID(), c->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc);
}
taskmanager->SendCompletedTasksToClient(c, this);
c->SendTaskActivityComplete(ActiveTasks[TaskIndex].TaskID, 0, TaskIndex, false);
taskmanager->SaveClientState(c, this);
@@ -2010,6 +2017,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
// If Experience and/or cash rewards are set, reward them from the task even if RewardMethod is METHODQUEST
RewardTask(c, Task);
//RemoveTask(c, TaskIndex);
}
}
+1 -1
View File
@@ -19,7 +19,7 @@
#include "../common/eq_packet_structs.h"
#include "masterentity.h"
#include "titles.h"
#include "../common/StringUtil.h"
#include "../common/string_util.h"
#include "worldserver.h"
extern WorldServer worldserver;
+58 -38
View File
@@ -29,11 +29,14 @@
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "titles.h"
#include "StringIDs.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "queryserv.h"
extern QueryServ* QServ;
static const SkillUseTypes TradeskillUnknown = Skill1HBlunt; /* an arbitrary non-tradeskill */
@@ -928,20 +931,20 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
_log(TRADESKILLS__TRACE, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR());
float res = MakeRandomFloat(0, 99);
int AAChance = 0;
int aa_chance = 0;
//AA modifiers
//can we do this with nested switches?
if(spec->tradeskill == SkillAlchemy){
switch(GetAA(aaAlchemyMastery)){
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -949,13 +952,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if(spec->tradeskill == SkillJewelryMaking){
switch(GetAA(aaJewelCraftMastery)){
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -964,13 +967,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (spec->tradeskill == SkillBlacksmithing) {
switch(GetAA(aaBlacksmithingMastery)) {
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -978,13 +981,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (spec->tradeskill == SkillBaking) {
switch(GetAA(aaBakingMastery)) {
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -992,13 +995,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (spec->tradeskill == SkillBrewing) {
switch(GetAA(aaBrewingMastery)) {
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -1006,13 +1009,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (spec->tradeskill == SkillFletching) {
switch(GetAA(aaFletchingMastery2)) {
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -1020,13 +1023,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (spec->tradeskill == SkillPottery) {
switch(GetAA(aaPotteryMastery)) {
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -1034,13 +1037,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (spec->tradeskill == SkillTailoring) {
switch(GetAA(aaTailoringMastery)) {
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
@@ -1048,26 +1051,26 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (spec->tradeskill == SkillResearch) {
switch(GetAA(aaArcaneTongues)) {
case 1:
AAChance = 10;
aa_chance = 10;
break;
case 2:
AAChance = 25;
aa_chance = 25;
break;
case 3:
AAChance = 50;
aa_chance = 50;
break;
}
}
chance = mod_tradeskill_chance(chance, spec);
if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){
if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < aa_chance){
success_modifier = 1;
if(over_trivial < 0)
CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill);
Message_StringID(4,TRADESKILL_SUCCEED,spec->name.c_str());
Message_StringID(4, TRADESKILL_SUCCEED, spec->name.c_str());
_log(TRADESKILLS__TRACE, "Tradeskill success");
@@ -1076,16 +1079,24 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
//should we check this crap?
SummonItem(itr->first, itr->second);
item = database.GetItem(itr->first);
if (this->GetGroup())
{
entity_list.MessageGroup(this,true,MT_Skills,"%s has successfully fashioned %s!",GetName(),item->Name);
if (this->GetGroup()) {
entity_list.MessageGroup(this, true, MT_Skills, "%s has successfully fashioned %s!", GetName(), item->Name);
}
/* QS: Player_Log_Trade_Skill_Events */
if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){
std::string event_desc = StringFormat("Success :: fashioned recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc);
}
if(RuleB(TaskSystem, EnableTaskSystem))
UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second);
++itr;
}
return(true);
} else {
}
/* Tradeskill Fail */
else {
success_modifier = 2; // Halves the chance
if(over_trivial < 0)
@@ -1097,6 +1108,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
if (this->GetGroup())
{
entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its");
}
/* QS: Player_Log_Trade_Skill_Events */
if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){
std::string event_desc = StringFormat("Failed :: recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc);
}
itr = spec->onfail.begin();
@@ -1106,6 +1124,8 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
++itr;
}
/* Salvage Item rolls */
// Rolls on each item, is possible to return everything
int SalvageChance = aabonuses.SalvageChance + itembonuses.SalvageChance + spellbonuses.SalvageChance;
// Skip check if not a normal TS or if a quest recipe these should be nofail, but check amyways
+517 -218
View File
@@ -17,12 +17,15 @@
*/
#include "../common/debug.h"
#include "masterentity.h"
#include "StringIDs.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
#include "worldserver.h"
#include "queryserv.h"
extern WorldServer worldserver;
extern QueryServ* QServ;
// The maximum amount of a single bazaar/barter transaction expressed in copper.
// Equivalent to 2 Million plat
@@ -71,8 +74,9 @@ void Trade::Start(uint32 mob_id, bool initiate_with)
}
// Add item from a given slot to trade bucket (automatically does bag data too)
void Trade::AddEntity(uint16 from_slot_id, uint16 trade_slot_id)
{
void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) {
// TODO: review for inventory saves / consider changing return type to bool so failure can be passed to desync handler
if (!owner || !owner->IsClient()) {
// This should never happen
LogFile->write(EQEMuLog::Debug, "Programming error: NPC's should not call Trade::AddEntity()");
@@ -87,45 +91,56 @@ void Trade::AddEntity(uint16 from_slot_id, uint16 trade_slot_id)
// Item always goes into trade bucket from cursor
Client* client = owner->CastToClient();
const ItemInst* inst = client->GetInv().GetItem(MainCursor);
ItemInst* inst = client->GetInv().GetItem(MainCursor);
if (!inst) {
client->Message(13, "Error: Could not find item on your cursor!");
return;
}
_log(TRADING__HOLDER, "%s added item '%s' to trade slot %i", owner->GetName(), inst->GetItem()->Name, trade_slot_id);
ItemInst* inst2 = client->GetInv().GetItem(trade_slot_id);
int new_charges = 0;
if (!inst2 || !inst2->GetItem()) {
// Send all item data to other client
SendItemData(inst, trade_slot_id);
// Move item on cursor to the trade slots
client->PutItemInInventory(trade_slot_id, *inst);
}
else
{
if (client->GetInv().GetItem(MainCursor)->GetID() != client->GetInv().GetItem(trade_slot_id)->GetID()) {
// it looks like the original code attempted to allow stacking...
// (it just didn't handle partial stack move actions -U)
if (stack_size > 0) {
if (!inst->IsStackable() || !inst2 || !inst2->GetItem() || (inst->GetID() != inst2->GetID()) || (stack_size > inst->GetCharges())) {
client->Kick();
return;
}
new_charges = (inst2->GetCharges()+inst->GetCharges());
if (new_charges < inst2->GetItem()->StackSize)
{
inst2->SetCharges(new_charges);
new_charges = 0;
}
else
{
new_charges = inst->GetCharges()-(inst2->GetItem()->StackSize-inst2->GetCharges()); //Leftover charges = charges - difference
uint32 _stack_size = 0;
if ((stack_size + inst2->GetCharges()) > inst2->GetItem()->StackSize) {
_stack_size = (stack_size + inst2->GetCharges()) - inst->GetItem()->StackSize;
inst2->SetCharges(inst2->GetItem()->StackSize);
}
else {
_stack_size = inst->GetCharges() - stack_size;
inst2->SetCharges(stack_size + inst2->GetCharges());
}
_log(TRADING__HOLDER, "%s added partial item '%s' stack (qty: %i) to trade slot %i", owner->GetName(), inst->GetItem()->Name, stack_size, trade_slot_id);
if (_stack_size > 0)
inst->SetCharges(_stack_size);
else
client->DeleteItemInInventory(MainCursor);
SendItemData(inst2, trade_slot_id);
}
if (new_charges > 0)
client->GetInv().GetItem(from_slot_id)->SetCharges(new_charges);
else
client->DeleteItemInInventory(from_slot_id);//, (ItemInst&)trade_inst);
else {
if (inst2 && inst2->GetID()) {
client->Kick();
return;
}
SendItemData(inst, trade_slot_id);
_log(TRADING__HOLDER, "%s added item '%s' to trade slot %i", owner->GetName(), inst->GetItem()->Name, trade_slot_id);
client->PutItemInInventory(trade_slot_id, *inst);
client->DeleteItemInInventory(MainCursor);
}
}
// Retrieve mob the owner is trading with
@@ -304,200 +319,464 @@ void Trade::DumpTrade()
#endif
void Client::ResetTrade() {
const Item_Struct* TempItem = 0;
ItemInst* ins;
int x;
AddMoneyToPP(trade->cp, trade->sp, trade->gp, trade->pp, true);
for(x = EmuConstants::TRADE_BEGIN; x <= EmuConstants::TRADE_END; x++)
{
TempItem = 0;
ins = GetInv().GetItem(x);
if (ins)
TempItem = ins->GetItem();
if (TempItem)
{
bool is_arrow = (TempItem->ItemType == ItemTypeArrow) ? true : false;
int freeslotid = GetInv().FindFreeSlot(ins->IsType(ItemClassContainer), true, TempItem->Size, is_arrow);
if (freeslotid == INVALID_INDEX)
{
DropInst(ins);
// step 1: process bags
for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) {
const ItemInst* inst = m_inv[trade_slot];
if (inst && inst->IsType(ItemClassContainer)) {
int16 free_slot = m_inv.FindFreeSlotForTradeItem(inst);
if (free_slot != INVALID_INDEX) {
PutItemInInventory(free_slot, *inst);
SendItemPacket(free_slot, inst, ItemPacketTrade);
}
else
{
PutItemInInventory(freeslotid, *ins);
SendItemPacket(freeslotid, ins, ItemPacketTrade);
else {
DropInst(inst);
}
DeleteItemInInventory(x);
DeleteItemInInventory(trade_slot);
}
}
// step 2a: process stackables
for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) {
ItemInst* inst = GetInv().GetItem(trade_slot);
if (inst && inst->IsStackable()) {
while (true) {
// there's no built-in safety check against an infinite loop..but, it should break on one of the conditional checks
int16 free_slot = m_inv.FindFreeSlotForTradeItem(inst);
if ((free_slot == MainCursor) || (free_slot == INVALID_INDEX))
break;
ItemInst* partial_inst = GetInv().GetItem(free_slot);
if (!partial_inst)
break;
if (partial_inst->GetID() != inst->GetID()) {
_log(TRADING__ERROR, "Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()");
break;
}
if ((partial_inst->GetCharges() + inst->GetCharges()) > partial_inst->GetItem()->StackSize) {
int16 new_charges = (partial_inst->GetCharges() + inst->GetCharges()) - partial_inst->GetItem()->StackSize;
partial_inst->SetCharges(partial_inst->GetItem()->StackSize);
inst->SetCharges(new_charges);
}
else {
partial_inst->SetCharges(partial_inst->GetCharges() + inst->GetCharges());
inst->SetCharges(0);
}
PutItemInInventory(free_slot, *partial_inst);
SendItemPacket(free_slot, partial_inst, ItemPacketTrade);
if (inst->GetCharges() == 0) {
DeleteItemInInventory(trade_slot);
break;
}
}
}
}
// step 2b: adjust trade stack bias
// (if any partial stacks exist before the final stack, FindFreeSlotForTradeItem() will return that slot in step 3 and an overwrite will occur)
for (int16 trade_slot = EmuConstants::TRADE_END; trade_slot >= EmuConstants::TRADE_BEGIN; --trade_slot) {
ItemInst* inst = GetInv().GetItem(trade_slot);
if (inst && inst->IsStackable()) {
for (int16 bias_slot = EmuConstants::TRADE_BEGIN; bias_slot <= EmuConstants::TRADE_END; ++bias_slot) {
if (bias_slot >= trade_slot)
break;
ItemInst* bias_inst = GetInv().GetItem(bias_slot);
if (!bias_inst || (bias_inst->GetID() != inst->GetID()) || (bias_inst->GetCharges() >= bias_inst->GetItem()->StackSize))
continue;
if ((bias_inst->GetCharges() + inst->GetCharges()) > bias_inst->GetItem()->StackSize) {
int16 new_charges = (bias_inst->GetCharges() + inst->GetCharges()) - bias_inst->GetItem()->StackSize;
bias_inst->SetCharges(bias_inst->GetItem()->StackSize);
inst->SetCharges(new_charges);
}
else {
bias_inst->SetCharges(bias_inst->GetCharges() + inst->GetCharges());
inst->SetCharges(0);
}
if (inst->GetCharges() == 0) {
DeleteItemInInventory(trade_slot);
break;
}
}
}
}
// step 3: process everything else
for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) {
const ItemInst* inst = m_inv[trade_slot];
if (inst) {
int16 free_slot = m_inv.FindFreeSlotForTradeItem(inst);
if (free_slot != INVALID_INDEX) {
PutItemInInventory(free_slot, *inst);
SendItemPacket(free_slot, inst, ItemPacketTrade);
}
else {
DropInst(inst);
}
DeleteItemInInventory(trade_slot);
}
}
}
void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) {
void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, std::list<void*>* event_details) {
if(tradingWith && tradingWith->IsClient()) {
Client* other = tradingWith->CastToClient();
QSPlayerLogTrade_Struct* qs_audit = nullptr;
bool qs_log = false;
if(other) {
mlog(TRADING__CLIENT, "Finishing trade with client %s", other->GetName());
int16 slot_id;
const Item_Struct* item = nullptr;
QSPlayerLogTrade_Struct* qsaudit = nullptr;
bool QSPLT = false;
this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true);
// step 0: pre-processing
// QS code
if(qspack && RuleB(QueryServ, PlayerLogTrades)) {
qsaudit = (QSPlayerLogTrade_Struct*) qspack->pBuffer;
QSPLT = true;
if (RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) {
qs_audit = (QSPlayerLogTrade_Struct*)event_entry;
qs_log = true;
if (finalizer) {
qs_audit->char2_id = this->character_id;
if(finalizer) { qsaudit->char2_id = this->character_id; }
else { qsaudit->char1_id = this->character_id; }
qs_audit->char2_money.platinum = this->trade->pp;
qs_audit->char2_money.gold = this->trade->gp;
qs_audit->char2_money.silver = this->trade->sp;
qs_audit->char2_money.copper = this->trade->cp;
}
else {
qs_audit->char1_id = this->character_id;
qs_audit->char1_money.platinum = this->trade->pp;
qs_audit->char1_money.gold = this->trade->gp;
qs_audit->char1_money.silver = this->trade->sp;
qs_audit->char1_money.copper = this->trade->cp;
}
}
// Move each trade slot into free inventory slot
for(int16 i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_END; i++){
const ItemInst* inst = m_inv[i];
uint16 parent_offset = 0;
// step 1: process bags
for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) {
const ItemInst* inst = m_inv[trade_slot];
if(inst == nullptr) { continue; }
if (inst && inst->IsType(ItemClassContainer)) {
mlog(TRADING__CLIENT, "Giving container %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName());
mlog(TRADING__CLIENT, "Giving %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, i, other->GetName());
// TODO: need to check bag items/augments for no drop..everything for attuned...
if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) {
int16 free_slot = other->GetInv().FindFreeSlotForTradeItem(inst);
/// Log Player Trades through QueryServ if Rule Enabled
if(QSPLT) {
uint16 item_count = qsaudit->char1_count + qsaudit->char2_count;
parent_offset = item_count;
if (free_slot != INVALID_INDEX) {
if (other->PutItemInInventory(free_slot, *inst, true)) {
mlog(TRADING__CLIENT, "Container %s (%d) successfully transferred, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID);
if (qs_log) {
QSTradeItems_Struct* detail = new QSTradeItems_Struct;
qsaudit->items[item_count].from_id = this->character_id;
qsaudit->items[item_count].from_slot = i;
qsaudit->items[item_count].to_id = other->CharacterID();
qsaudit->items[item_count].to_slot = 0;
qsaudit->items[item_count].item_id = inst->GetID();
qsaudit->items[item_count].charges = inst->GetCharges();
qsaudit->items[item_count].aug_1 = inst->GetAugmentItemID(1);
qsaudit->items[item_count].aug_2 = inst->GetAugmentItemID(2);
qsaudit->items[item_count].aug_3 = inst->GetAugmentItemID(3);
qsaudit->items[item_count].aug_4 = inst->GetAugmentItemID(4);
qsaudit->items[item_count].aug_5 = inst->GetAugmentItemID(5);
detail->from_id = this->character_id;
detail->from_slot = trade_slot;
detail->to_id = other->CharacterID();
detail->to_slot = free_slot;
detail->item_id = inst->GetID();
detail->charges = 1;
detail->aug_1 = inst->GetAugmentItemID(1);
detail->aug_2 = inst->GetAugmentItemID(2);
detail->aug_3 = inst->GetAugmentItemID(3);
detail->aug_4 = inst->GetAugmentItemID(4);
detail->aug_5 = inst->GetAugmentItemID(5);
if(finalizer) { qsaudit->char2_count++; }
else { qsaudit->char1_count++; }
event_details->push_back(detail);
if (finalizer)
qs_audit->char2_count += detail->charges;
else
qs_audit->char1_count += detail->charges;
if(inst->IsType(ItemClassContainer)) {
// Pseudo-Slot ID's are generated based on how the db saves bag items...
for(uint8 j = SUB_BEGIN; j < inst->GetItem()->BagSlots; j++) {
const ItemInst* baginst = inst->GetItem(j);
//for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) {
for (uint8 sub_slot = SUB_BEGIN; (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++sub_slot) { // this is to catch ALL items
const ItemInst* bag_inst = inst->GetItem(sub_slot);
if(baginst == nullptr) { continue; }
if (bag_inst) {
detail = new QSTradeItems_Struct;
int16 k=Inventory::CalcSlotId(i, j);
item_count = qsaudit->char1_count + qsaudit->char2_count;
detail->from_id = this->character_id;
detail->from_slot = Inventory::CalcSlotId(trade_slot, sub_slot);
detail->to_id = other->CharacterID();
detail->to_slot = Inventory::CalcSlotId(free_slot, sub_slot);
detail->item_id = bag_inst->GetID();
detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges());
detail->aug_1 = bag_inst->GetAugmentItemID(1);
detail->aug_2 = bag_inst->GetAugmentItemID(2);
detail->aug_3 = bag_inst->GetAugmentItemID(3);
detail->aug_4 = bag_inst->GetAugmentItemID(4);
detail->aug_5 = bag_inst->GetAugmentItemID(5);
qsaudit->items[item_count].from_id = this->character_id;
qsaudit->items[item_count].from_slot = k;
qsaudit->items[item_count].to_id = other->CharacterID();
qsaudit->items[item_count].to_slot = 0;
qsaudit->items[item_count].item_id = baginst->GetID();
qsaudit->items[item_count].charges = baginst->GetCharges();
qsaudit->items[item_count].aug_1 = baginst->GetAugmentItemID(1);
qsaudit->items[item_count].aug_2 = baginst->GetAugmentItemID(2);
qsaudit->items[item_count].aug_3 = baginst->GetAugmentItemID(3);
qsaudit->items[item_count].aug_4 = baginst->GetAugmentItemID(4);
qsaudit->items[item_count].aug_5 = baginst->GetAugmentItemID(5);
event_details->push_back(detail);
if(finalizer) { qsaudit->char2_count++; }
else { qsaudit->char1_count++; }
}
}
}
if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) {
bool is_arrow = (inst->GetItem()->ItemType == ItemTypeArrow) ? true : false;
slot_id = other->GetInv().FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size, is_arrow);
mlog(TRADING__CLIENT, "Trying to put %s (%d) into slot %d", inst->GetItem()->Name, inst->GetItem()->ID, slot_id);
if(other->PutItemInInventory(slot_id, *inst, true)) {
mlog(TRADING__CLIENT, "Item %s (%d) successfully transfered, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID);
if(QSPLT) {
qsaudit->items[parent_offset].to_slot = slot_id;
if(inst->IsType(ItemClassContainer)) {
for(uint8 bagslot_idx = SUB_BEGIN; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) {
const ItemInst* bag_inst = inst->GetItem(bagslot_idx);
if(bag_inst == nullptr) { continue; }
int16 to_bagslot_id = Inventory::CalcSlotId(slot_id, bagslot_idx);
qsaudit->items[++parent_offset].to_slot = to_bagslot_id;
if (finalizer)
qs_audit->char2_count += detail->charges;
else
qs_audit->char1_count += detail->charges;
}
}
}
}
else {
mlog(TRADING__ERROR, "Transfer of container %s (%d) to %s failed, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName());
PushItemOnCursor(*inst, true);
}
}
else {
mlog(TRADING__ERROR, "%s's inventory is full, returning container %s (%d) to giver.", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID);
PushItemOnCursor(*inst, true);
}
}
else {
mlog(TRADING__ERROR, "Container %s (%d) is NoDrop, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID);
PushItemOnCursor(*inst, true);
mlog(TRADING__ERROR, "Unable to give item %d (%d) to %s, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName());
if(QSPLT) {
qsaudit->items[parent_offset].to_id = this->character_id;
qsaudit->items[parent_offset].to_slot = MainCursor;
if(inst->IsType(ItemClassContainer)) {
for(uint8 bagslot_idx = SUB_BEGIN; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) {
const ItemInst* bag_inst = inst->GetItem(bagslot_idx);
if(bag_inst == nullptr) { continue; }
int16 to_bagslot_id = Inventory::CalcSlotId(MainCursor, bagslot_idx);
qsaudit->items[++parent_offset].to_id = this->character_id;
qsaudit->items[parent_offset].to_slot = to_bagslot_id;
}
}
}
}
DeleteItemInInventory(i);
DeleteItemInInventory(trade_slot);
}
else {
PushItemOnCursor(*inst, true);
DeleteItemInInventory(i);
}
if(QSPLT) {
qsaudit->items[parent_offset].to_id = this->character_id;
qsaudit->items[parent_offset].to_slot = MainCursor;
// step 2a: process stackables
for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) {
ItemInst* inst = GetInv().GetItem(trade_slot);
if(inst->IsType(ItemClassContainer)) {
for(uint8 bagslot_idx = SUB_BEGIN; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) {
const ItemInst* bag_inst = inst->GetItem(bagslot_idx);
if (inst && inst->IsStackable()) {
while (true) {
// there's no built-in safety check against an infinite loop..but, it should break on one of the conditional checks
int16 partial_slot = other->GetInv().FindFreeSlotForTradeItem(inst);
if(bag_inst == nullptr) { continue; }
int16 to_bagslot_id = Inventory::CalcSlotId(MainCursor, bagslot_idx);
if ((partial_slot == MainCursor) || (partial_slot == INVALID_INDEX))
break;
qsaudit->items[++parent_offset].to_id = this->character_id;
qsaudit->items[parent_offset].to_slot = to_bagslot_id;
ItemInst* partial_inst = other->GetInv().GetItem(partial_slot);
if (!partial_inst)
break;
if (partial_inst->GetID() != inst->GetID()) {
_log(TRADING__ERROR, "Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()");
break;
}
int16 old_charges = inst->GetCharges();
int16 partial_charges = partial_inst->GetCharges();
if ((partial_inst->GetCharges() + inst->GetCharges()) > partial_inst->GetItem()->StackSize) {
int16 new_charges = (partial_inst->GetCharges() + inst->GetCharges()) - partial_inst->GetItem()->StackSize;
partial_inst->SetCharges(partial_inst->GetItem()->StackSize);
inst->SetCharges(new_charges);
}
else {
partial_inst->SetCharges(partial_inst->GetCharges() + inst->GetCharges());
inst->SetCharges(0);
}
mlog(TRADING__CLIENT, "Transferring partial stack %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName());
if (other->PutItemInInventory(partial_slot, *partial_inst, true)) {
mlog(TRADING__CLIENT, "Partial stack %s (%d) successfully transferred, deleting %i charges from trade slot.",
inst->GetItem()->Name, inst->GetItem()->ID, (old_charges - inst->GetCharges()));
if (qs_log) {
QSTradeItems_Struct* detail = new QSTradeItems_Struct;
detail->from_id = this->character_id;
detail->from_slot = trade_slot;
detail->to_id = other->CharacterID();
detail->to_slot = partial_slot;
detail->item_id = inst->GetID();
detail->charges = (old_charges - inst->GetCharges());
detail->aug_1 = 0;
detail->aug_2 = 0;
detail->aug_3 = 0;
detail->aug_4 = 0;
detail->aug_5 = 0;
event_details->push_back(detail);
if (finalizer)
qs_audit->char2_count += detail->charges;
else
qs_audit->char1_count += detail->charges;
}
}
else {
mlog(TRADING__ERROR, "Transfer of partial stack %s (%d) to %s failed, returning %i charges to trade slot.",
inst->GetItem()->Name, inst->GetItem()->ID, other->GetName(), (old_charges - inst->GetCharges()));
inst->SetCharges(old_charges);
partial_inst->SetCharges(partial_charges);
break;
}
if (inst->GetCharges() == 0) {
DeleteItemInInventory(trade_slot);
break;
}
}
}
}
// Money - look into how NPC's receive cash
this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true);
// step 2b: adjust trade stack bias
// (if any partial stacks exist before the final stack, FindFreeSlotForTradeItem() will return that slot in step 3 and an overwrite will occur)
for (int16 trade_slot = EmuConstants::TRADE_END; trade_slot >= EmuConstants::TRADE_BEGIN; --trade_slot) {
ItemInst* inst = GetInv().GetItem(trade_slot);
// This is currently setup to show character offers, not receipts
if(QSPLT) {
if(finalizer) {
qsaudit->char2_money.platinum = this->trade->pp;
qsaudit->char2_money.gold = this->trade->gp;
qsaudit->char2_money.silver = this->trade->sp;
qsaudit->char2_money.copper = this->trade->cp;
if (inst && inst->IsStackable()) {
for (int16 bias_slot = EmuConstants::TRADE_BEGIN; bias_slot <= EmuConstants::TRADE_END; ++bias_slot) {
if (bias_slot >= trade_slot)
break;
ItemInst* bias_inst = GetInv().GetItem(bias_slot);
if (!bias_inst || (bias_inst->GetID() != inst->GetID()) || (bias_inst->GetCharges() >= bias_inst->GetItem()->StackSize))
continue;
int16 old_charges = inst->GetCharges();
if ((bias_inst->GetCharges() + inst->GetCharges()) > bias_inst->GetItem()->StackSize) {
int16 new_charges = (bias_inst->GetCharges() + inst->GetCharges()) - bias_inst->GetItem()->StackSize;
bias_inst->SetCharges(bias_inst->GetItem()->StackSize);
inst->SetCharges(new_charges);
}
else {
bias_inst->SetCharges(bias_inst->GetCharges() + inst->GetCharges());
inst->SetCharges(0);
}
if (qs_log) {
QSTradeItems_Struct* detail = new QSTradeItems_Struct;
detail->from_id = this->character_id;
detail->from_slot = trade_slot;
detail->to_id = this->character_id;
detail->to_slot = bias_slot;
detail->item_id = inst->GetID();
detail->charges = (old_charges - inst->GetCharges());
detail->aug_1 = 0;
detail->aug_2 = 0;
detail->aug_3 = 0;
detail->aug_4 = 0;
detail->aug_5 = 0;
event_details->push_back(detail);
}
if (inst->GetCharges() == 0) {
DeleteItemInInventory(trade_slot);
break;
}
}
}
else {
qsaudit->char1_money.platinum = this->trade->pp;
qsaudit->char1_money.gold = this->trade->gp;
qsaudit->char1_money.silver = this->trade->sp;
qsaudit->char1_money.copper = this->trade->cp;
}
// step 3: process everything else
for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) {
const ItemInst* inst = m_inv[trade_slot];
if (inst) {
mlog(TRADING__CLIENT, "Giving item %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName());
// TODO: need to check bag items/augments for no drop..everything for attuned...
if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) {
int16 free_slot = other->GetInv().FindFreeSlotForTradeItem(inst);
if (free_slot != INVALID_INDEX) {
if (other->PutItemInInventory(free_slot, *inst, true)) {
mlog(TRADING__CLIENT, "Item %s (%d) successfully transferred, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID);
if (qs_log) {
QSTradeItems_Struct* detail = new QSTradeItems_Struct;
detail->from_id = this->character_id;
detail->from_slot = trade_slot;
detail->to_id = other->CharacterID();
detail->to_slot = free_slot;
detail->item_id = inst->GetID();
detail->charges = (!inst->IsStackable() ? 1 : inst->GetCharges());
detail->aug_1 = inst->GetAugmentItemID(1);
detail->aug_2 = inst->GetAugmentItemID(2);
detail->aug_3 = inst->GetAugmentItemID(3);
detail->aug_4 = inst->GetAugmentItemID(4);
detail->aug_5 = inst->GetAugmentItemID(5);
event_details->push_back(detail);
if (finalizer)
qs_audit->char2_count += detail->charges;
else
qs_audit->char1_count += detail->charges;
// 'step 3' should never really see containers..but, just in case...
//for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) {
for (uint8 sub_slot = SUB_BEGIN; (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++sub_slot) { // this is to catch ALL items
const ItemInst* bag_inst = inst->GetItem(sub_slot);
if (bag_inst) {
detail = new QSTradeItems_Struct;
detail->from_id = this->character_id;
detail->from_slot = trade_slot;
detail->to_id = other->CharacterID();
detail->to_slot = free_slot;
detail->item_id = bag_inst->GetID();
detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges());
detail->aug_1 = bag_inst->GetAugmentItemID(1);
detail->aug_2 = bag_inst->GetAugmentItemID(2);
detail->aug_3 = bag_inst->GetAugmentItemID(3);
detail->aug_4 = bag_inst->GetAugmentItemID(4);
detail->aug_5 = bag_inst->GetAugmentItemID(5);
event_details->push_back(detail);
if (finalizer)
qs_audit->char2_count += detail->charges;
else
qs_audit->char1_count += detail->charges;
}
}
}
}
else {
mlog(TRADING__ERROR, "Transfer of Item %s (%d) to %s failed, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName());
PushItemOnCursor(*inst, true);
}
}
else {
mlog(TRADING__ERROR, "%s's inventory is full, returning item %s (%d) to giver.", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID);
PushItemOnCursor(*inst, true);
}
}
else {
mlog(TRADING__ERROR, "Item %s (%d) is NoDrop, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID);
PushItemOnCursor(*inst, true);
}
DeleteItemInInventory(trade_slot);
}
}
@@ -505,62 +784,72 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer)
}
}
else if(tradingWith && tradingWith->IsNPC()) {
QSPlayerLogHandin_Struct* qsaudit = nullptr;
bool QSPLH = false;
QSPlayerLogHandin_Struct* qs_audit = nullptr;
bool qs_log = false;
// QS code
if(qspack && RuleB(QueryServ, PlayerLogTrades)) {
if(RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) {
// Currently provides only basic functionality. Calling method will also
// need to be modified before item returns and rewards can be logged. -U
qsaudit = (QSPlayerLogHandin_Struct*) qspack->pBuffer;
QSPLH = true;
qs_audit = (QSPlayerLogHandin_Struct*)event_entry;
qs_log = true;
qsaudit->quest_id = 0;
qsaudit->char_id = character_id;
qsaudit->char_money.platinum = trade->pp;
qsaudit->char_money.gold = trade->gp;
qsaudit->char_money.silver = trade->sp;
qsaudit->char_money.copper = trade->cp;
qsaudit->char_count = 0;
qsaudit->npc_id = tradingWith->GetNPCTypeID();
qsaudit->npc_money.platinum = 0;
qsaudit->npc_money.gold = 0;
qsaudit->npc_money.silver = 0;
qsaudit->npc_money.copper = 0;
qsaudit->npc_count = 0;
qs_audit->quest_id = 0;
qs_audit->char_id = character_id;
qs_audit->char_money.platinum = trade->pp;
qs_audit->char_money.gold = trade->gp;
qs_audit->char_money.silver = trade->sp;
qs_audit->char_money.copper = trade->cp;
qs_audit->char_count = 0;
qs_audit->npc_id = tradingWith->GetNPCTypeID();
qs_audit->npc_money.platinum = 0;
qs_audit->npc_money.gold = 0;
qs_audit->npc_money.silver = 0;
qs_audit->npc_money.copper = 0;
qs_audit->npc_count = 0;
}
if(QSPLH) { // This can be incoporated below when revisions are made -U
for(int16 slot_id = EmuConstants::TRADE_BEGIN; slot_id <= EmuConstants::TRADE_NPC_END; slot_id++) {
const ItemInst* trade_inst = m_inv[slot_id];
if(qs_log) { // This can be incorporated below when revisions are made -U
for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_NPC_END; ++trade_slot) {
const ItemInst* trade_inst = m_inv[trade_slot];
if(trade_inst) {
strcpy(qsaudit->items[qsaudit->char_count].action_type, "HANDIN");
QSHandinItems_Struct* detail = new QSHandinItems_Struct;
qsaudit->items[qsaudit->char_count].char_slot = slot_id;
qsaudit->items[qsaudit->char_count].item_id = trade_inst->GetID();
qsaudit->items[qsaudit->char_count].charges = trade_inst->GetCharges();
qsaudit->items[qsaudit->char_count].aug_1 = trade_inst->GetAugmentItemID(1);
qsaudit->items[qsaudit->char_count].aug_2 = trade_inst->GetAugmentItemID(2);
qsaudit->items[qsaudit->char_count].aug_3 = trade_inst->GetAugmentItemID(3);
qsaudit->items[qsaudit->char_count].aug_4 = trade_inst->GetAugmentItemID(4);
qsaudit->items[qsaudit->char_count++].aug_5 = trade_inst->GetAugmentItemID(5);
strcpy(detail->action_type, "HANDIN");
detail->char_slot = trade_slot;
detail->item_id = trade_inst->GetID();
detail->charges = (!trade_inst->IsStackable() ? 1 : trade_inst->GetCharges());
detail->aug_1 = trade_inst->GetAugmentItemID(1);
detail->aug_2 = trade_inst->GetAugmentItemID(2);
detail->aug_3 = trade_inst->GetAugmentItemID(3);
detail->aug_4 = trade_inst->GetAugmentItemID(4);
detail->aug_5 = trade_inst->GetAugmentItemID(5);
event_details->push_back(detail);
qs_audit->char_count += detail->charges;
if(trade_inst->IsType(ItemClassContainer)) {
for(uint8 bag_idx = SUB_BEGIN; bag_idx < trade_inst->GetItem()->BagSlots; bag_idx++) {
const ItemInst* trade_baginst = trade_inst->GetItem(bag_idx);
for (uint8 sub_slot = SUB_BEGIN; sub_slot < trade_inst->GetItem()->BagSlots; ++sub_slot) {
const ItemInst* trade_baginst = trade_inst->GetItem(sub_slot);
if(trade_baginst) {
strcpy(qsaudit->items[qsaudit->char_count].action_type, "HANDIN");
detail = new QSHandinItems_Struct;
qsaudit->items[qsaudit->char_count].char_slot = Inventory::CalcSlotId(slot_id, bag_idx);
qsaudit->items[qsaudit->char_count].item_id = trade_baginst->GetID();
qsaudit->items[qsaudit->char_count].charges = trade_baginst->GetCharges();
qsaudit->items[qsaudit->char_count].aug_1 = trade_baginst->GetAugmentItemID(1);
qsaudit->items[qsaudit->char_count].aug_2 = trade_baginst->GetAugmentItemID(2);
qsaudit->items[qsaudit->char_count].aug_3 = trade_baginst->GetAugmentItemID(3);
qsaudit->items[qsaudit->char_count].aug_4 = trade_baginst->GetAugmentItemID(4);
qsaudit->items[qsaudit->char_count++].aug_5 = trade_baginst->GetAugmentItemID(5);
strcpy(detail->action_type, "HANDIN");
detail->char_slot = Inventory::CalcSlotId(trade_slot, sub_slot);
detail->item_id = trade_baginst->GetID();
detail->charges = (!trade_inst->IsStackable() ? 1 : trade_inst->GetCharges());
detail->aug_1 = trade_baginst->GetAugmentItemID(1);
detail->aug_2 = trade_baginst->GetAugmentItemID(2);
detail->aug_3 = trade_baginst->GetAugmentItemID(3);
detail->aug_4 = trade_baginst->GetAugmentItemID(4);
detail->aug_5 = trade_baginst->GetAugmentItemID(5);
event_details->push_back(detail);
qs_audit->char_count += detail->charges;
}
}
}
@@ -623,6 +912,16 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer)
if(UpdateTasksOnDeliver(items, Cash, tradingWith->GetNPCTypeID())) {
if(!tradingWith->IsMoving())
tradingWith->FaceTarget(this);
/* Set a timestamp in an entity variable for plugin check_handin.pl in return_items
This will stopgap players from items being returned if global_npc.pl has a catch all return_items
*/
struct timeval read_time;
char buffer[50];
gettimeofday(&read_time, 0);
sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec);
this->SetEntityVariable("Stop_Return", buffer);
}
}
+30 -39
View File
@@ -20,8 +20,8 @@
#include "entity.h"
#include "masterentity.h"
#include "../common/spdat.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
/*
@@ -262,45 +262,36 @@ Mob* EntityList::GetTrapTrigger(Trap* trap) {
//todo: rewrite this to not need direct access to trap members.
bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
// int char_num = 0;
unsigned long* lengths;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id,x,y,z,effect,effectvalue,effectvalue2,skill,maxzdiff,radius,chance,message,respawn_time,respawn_var,level FROM traps WHERE zone='%s' AND version=%u", zonename, version), errbuf, &result)) {
safe_delete_array(query);
while ((row = mysql_fetch_row(result)))
{
lengths = mysql_fetch_lengths(result);
Trap* trap = new Trap();
trap->trap_id = atoi(row[0]);
trap->x = atof(row[1]);
trap->y = atof(row[2]);
trap->z = atof(row[3]);
trap->effect = atoi(row[4]);
trap->effectvalue = atoi(row[5]);
trap->effectvalue2 = atoi(row[6]);
trap->skill = atoi(row[7]);
trap->maxzdiff = atof(row[8]);
trap->radius = atof(row[9]);
trap->chance = atoi(row[10]);
trap->message = row[11];
trap->respawn_time = atoi(row[12]);
trap->respawn_var = atoi(row[13]);
trap->level = atoi(row[14]);
entity_list.AddTrap(trap);
trap->CreateHiddenTrigger();
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, "
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level "
"FROM traps WHERE zone='%s' AND version=%u", zonename, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
Trap* trap = new Trap();
trap->trap_id = atoi(row[0]);
trap->x = atof(row[1]);
trap->y = atof(row[2]);
trap->z = atof(row[3]);
trap->effect = atoi(row[4]);
trap->effectvalue = atoi(row[5]);
trap->effectvalue2 = atoi(row[6]);
trap->skill = atoi(row[7]);
trap->maxzdiff = atof(row[8]);
trap->radius = atof(row[9]);
trap->chance = atoi(row[10]);
trap->message = row[11];
trap->respawn_time = atoi(row[12]);
trap->respawn_var = atoi(row[13]);
trap->level = atoi(row[14]);
entity_list.AddTrap(trap);
trap->CreateHiddenTrigger();
}
return true;
}
+43 -51
View File
@@ -20,7 +20,7 @@
#include "../common/features.h"
#include "masterentity.h"
#include "../common/packet_dump.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include <string>
#include <map>
@@ -378,68 +378,60 @@ void Client::SendGuildTributes() {
}
bool ZoneDatabase::LoadTributes() {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
TributeData t;
memset(&t.tiers, 0, sizeof(t.tiers));
t.tier_count = 0;
TributeData tributeData;
memset(&tributeData.tiers, 0, sizeof(tributeData.tiers));
tributeData.tier_count = 0;
tribute_list.clear();
const char *query = "SELECT id,name,descr,unknown,isguild FROM tributes";
if (RunQuery(query, strlen(query), errbuf, &result)) {
int r;
while ((row = mysql_fetch_row(result))) {
r = 0;
uint32 id = atoul(row[r++]);
t.name = row[r++];
t.description = row[r++];
t.unknown = strtoul(row[r++], nullptr, 10);
t.is_guild = atol(row[r++])==0?false:true;
tribute_list[id] = t;
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes first query '%s': %s", query, errbuf);
const std::string query = "SELECT id, name, descr, unknown, isguild FROM tributes";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes first query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 id = atoul(row[0]);
tributeData.name = row[1];
tributeData.description = row[2];
tributeData.unknown = strtoul(row[3], nullptr, 10);
tributeData.is_guild = atol(row[4]) == 0? false: true;
const char *query2 = "SELECT tribute_id,level,cost,item_id FROM tribute_levels ORDER BY tribute_id,level";
if (RunQuery(query2, strlen(query2), errbuf, &result)) {
int r;
while ((row = mysql_fetch_row(result))) {
r = 0;
uint32 id = atoul(row[r++]);
tribute_list[id] = tributeData;
}
if(tribute_list.count(id) != 1) {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes: unknown tribute %lu in tribute_levels", (unsigned long)id);
continue;
}
TributeData &cur = tribute_list[id];
if(cur.tier_count >= MAX_TRIBUTE_TIERS) {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes: on tribute %lu: more tiers defined than permitted", (unsigned long)id);
continue;
}
TributeLevel_Struct &s = cur.tiers[cur.tier_count];
s.level = atoul(row[r++]);
s.cost = atoul(row[r++]);
s.tribute_item_id = atoul(row[r++]);
cur.tier_count++;
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes level query '%s': %s", query, errbuf);
const std::string query2 = "SELECT tribute_id, level, cost, item_id FROM tribute_levels ORDER BY tribute_id, level";
results = QueryDatabase(query2);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes level query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 id = atoul(row[0]);
if(tribute_list.count(id) != 1) {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes: unknown tribute %lu in tribute_levels", (unsigned long)id);
continue;
}
TributeData &cur = tribute_list[id];
if(cur.tier_count >= MAX_TRIBUTE_TIERS) {
LogFile->write(EQEMuLog::Error, "Error in LoadTributes: on tribute %lu: more tiers defined than permitted", (unsigned long)id);
continue;
}
TributeLevel_Struct &s = cur.tiers[cur.tier_count];
s.level = atoul(row[1]);
s.cost = atoul(row[2]);
s.tribute_item_id = atoul(row[3]);
cur.tier_count++;
}
return true;
}
+288 -365
View File
@@ -24,16 +24,16 @@
#include <math.h>
#include "npc.h"
#include "masterentity.h"
#include "NpcAI.h"
#include "npc_ai.h"
#include "map.h"
#include "water_map.h"
#include "../common/moremath.h"
#include "StringIDs.h"
#include "../common/MiscFunctions.h"
#include "../common/StringUtil.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
#include "../common/features.h"
#include "QuestParserCollection.h"
#include "quest_parser_collection.h"
struct wp_distance
{
@@ -869,97 +869,78 @@ void NPC::AssignWaypoints(int32 grid) {
return;
}
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
bool GridErr = false, WPErr = false;
Waypoints.clear();
roamer = false;
// Retrieve the wander and pause types for this grid
if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `type`,`type2` FROM `grid` WHERE `id`=%i AND `zoneid`=%i",grid,zone->GetZoneID()),errbuf, &result))
{
if((row = mysql_fetch_row(result)))
{
if(row[0] != 0)
wandertype = atoi(row[0]);
else
wandertype = 0;
if(row[1] != 0)
pausetype = atoi(row[1]);
else
pausetype = 0;
}
else // No grid record found in this zone for the given ID
GridErr = true;
mysql_free_result(result);
std::string query = StringFormat("SELECT `type`, `type2` FROM `grid` WHERE `id` = %i AND `zoneid` = %i", grid, zone->GetZoneID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign grid %u to mob %s: %s", grid, name, results.ErrorMessage().c_str());
return;
}
else // DB query error!
{
GridErr = true;
LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign grid %u to mob %s: %s", grid, name, errbuf);
}
safe_delete_array(query);
if(!GridErr)
{
this->CastToNPC()->SetGrid(grid); // Assign grid number
if (results.RowCount() == 0)
return;
// Retrieve all waypoints for this grid
if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z`,`pause`,`heading` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result))
{
roamer = true;
max_wp = -1; // Initialize it; will increment it for each waypoint successfully added to the list
auto row = results.begin();
while((row = mysql_fetch_row(result)))
{
if(row[0] != 0 && row[1] != 0 && row[2] != 0 && row[3] != 0)
{
wplist newwp;
newwp.index = ++max_wp;
newwp.x = atof(row[0]);
newwp.y = atof(row[1]);
newwp.z = atof(row[2]);
wandertype = atoi(row[0]);
pausetype = atoi(row[1]);
if(zone->HasMap() && RuleB(Map, FixPathingZWhenLoading) )
{
if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(newwp.x, newwp.y, newwp.z)))
{
Map::Vertex dest(newwp.x, newwp.y, newwp.z);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
this->CastToNPC()->SetGrid(grid); // Assign grid number
if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaLoading))
newwp.z = newz + 1;
}
}
// Retrieve all waypoints for this grid
query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading` "
"FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %i "
"ORDER BY `number`", grid, zone->GetZoneID());
results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign waypoints from grid %u to mob %s: %s", grid, name, results.ErrorMessage().c_str());
return;
}
roamer = true;
max_wp = 0; // Initialize it; will increment it for each waypoint successfully added to the list
for (auto row = results.begin(); row != results.end(); ++row, ++max_wp)
{
wplist newwp;
newwp.index = max_wp;
newwp.x = atof(row[0]);
newwp.y = atof(row[1]);
newwp.z = atof(row[2]);
if(zone->HasMap() && RuleB(Map, FixPathingZWhenLoading) )
{
if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(newwp.x, newwp.y, newwp.z)))
{
Map::Vertex dest(newwp.x, newwp.y, newwp.z);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaLoading))
newwp.z = newz + 1;
}
}
newwp.pause = atoi(row[3]);
newwp.heading = atof(row[4]);
Waypoints.push_back(newwp);
}
newwp.pause = atoi(row[3]);
newwp.heading = atof(row[4]);
Waypoints.push_back(newwp);
}
}
mysql_free_result(result);
}
else // DB query error!
{
WPErr = true;
LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign waypoints from grid %u to mob %s: %s", grid, name, errbuf);
}
safe_delete_array(query);
} // end if (!GridErr)
if(Waypoints.size() < 2) {
roamer = false;
} else if(!GridErr && !WPErr) {
UpdateWaypoint(0);
SetWaypointPause();
if (wandertype == 1 || wandertype == 2 || wandertype == 5)
CalculateNewWaypoint();
} else {
roamer = false;
}
UpdateWaypoint(0);
SetWaypointPause();
if (wandertype == 1 || wandertype == 2 || wandertype == 5)
CalculateNewWaypoint();
}
void Mob::SendTo(float new_x, float new_y, float new_z) {
@@ -1049,159 +1030,123 @@ int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
}
uint8 ZoneDatabase::GetGridType2(uint32 grid, uint16 zoneid) {
char *query = 0;
char errbuff[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
int type2 = 0;
if (RunQuery(query, MakeAnyLenString(&query,"SELECT type2 from grid where id = %i and zoneid = %i",grid,zoneid),errbuff,&result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
type2 = atoi( row[0] );
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in GetGridType2 query '%s': %s", query, errbuff);
safe_delete_array(query);
}
return(type2);
int type2 = 0;
std::string query = StringFormat("SELECT type2 FROM grid WHERE id = %i AND zoneid = %i", grid, zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetGridType2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
bool ZoneDatabase::GetWaypoints(uint32 grid, uint16 zoneid, uint32 num, wplist* wp) {
char *query = 0;
char errbuff[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query,"SELECT x, y, z, pause, heading from grid_entries where gridid = %i and number = %i and zoneid = %i",grid,num,zoneid),errbuff,&result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
if ( wp ) {
wp->x = atof( row[0] );
wp->y = atof( row[1] );
wp->z = atof( row[2] );
wp->pause = atoi( row[3] );
wp->heading = atof( row[4] );
}
mysql_free_result(result);
return true;
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in GetWaypoints query '%s': %s", query, errbuff);
safe_delete_array(query);
}
return false;
if (wp == nullptr)
return false;
std::string query = StringFormat("SELECT x, y, z, pause, heading FROM grid_entries "
"WHERE gridid = %i AND number = %i AND zoneid = %i", grid, num, zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetWaypoints query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
if (results.RowCount() != 1)
return false;
auto row = results.begin();
wp->x = atof(row[0]);
wp->y = atof(row[1]);
wp->z = atof(row[2]);
wp->pause = atoi(row[3]);
wp->heading = atof(row[4]);
return true;
}
void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
{
char *query = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
int matches = 0, fuzzy = 0, spawn2id = 0;
uint32 affected_rows;
float dbx = 0, dby = 0;
// looks like most of the stuff in spawn2 is straight integers
// so let's try that first
if(!RunQuery(
query,
MakeAnyLenString(
&query,
"SELECT id,x,y FROM spawn2 WHERE zone='%s' AND x=%i AND y=%i",
zone->GetShortName(), (int)x, (int)y
),
errbuf,
&result
)) {
LogFile->write(EQEMuLog::Error, "Error querying spawn2 '%s': '%s'", query, errbuf);
return;
std::string query = StringFormat("SELECT id, x, y FROM spawn2 WHERE zone = '%s' AND x = %i AND y = %i",
zone->GetShortName(), (int)x, (int)y);
auto results = QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error querying spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
// how much it's allowed to be off by
#define _GASSIGN_TOLERANCE 1.0
if(!(matches = mysql_num_rows(result))) // try a fuzzy match if that didn't find it
if(results.RowCount() == 0) // try a fuzzy match if that didn't find it
{
mysql_free_result(result);
if(!RunQuery(
query,
MakeAnyLenString(
&query,
"SELECT id,x,y FROM spawn2 WHERE zone='%s' AND "
"ABS( ABS(x) - ABS(%f) ) < %f AND "
"ABS( ABS(y) - ABS(%f) ) < %f",
zone->GetShortName(), x, _GASSIGN_TOLERANCE, y, _GASSIGN_TOLERANCE
),
errbuf,
&result
)) {
LogFile->write(EQEMuLog::Error, "Error querying fuzzy spawn2 '%s': '%s'", query, errbuf);
query = StringFormat("SELECT id,x,y FROM spawn2 WHERE zone='%s' AND "
"ABS( ABS(x) - ABS(%f) ) < %f AND "
"ABS( ABS(y) - ABS(%f) ) < %f",
zone->GetShortName(), x, _GASSIGN_TOLERANCE, y, _GASSIGN_TOLERANCE);
results = QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error querying fuzzy spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
fuzzy = 1;
if(!(matches = mysql_num_rows(result)))
mysql_free_result(result);
matches = results.RowCount();
}
if(matches)
if (matches == 0) {
client->Message(0, "ERROR: Unable to assign grid - can't find it in spawn2");
return;
}
if(matches == 1)
{
if(matches > 1)
{
client->Message(0, "ERROR: Unable to assign grid - multiple spawn2 rows match");
mysql_free_result(result);
}
else
{
row = mysql_fetch_row(result);
spawn2id = atoi(row[0]);
dbx = atof(row[1]);
dby = atof(row[2]);
if(!RunQuery(
query,
MakeAnyLenString(
&query,
"UPDATE spawn2 SET pathgrid = %d WHERE id = %d", grid, spawn2id
),
errbuf,
&result,
&affected_rows
)) {
LogFile->write(EQEMuLog::Error, "Error updating spawn2 '%s': '%s'", query, errbuf);
return;
}
if(affected_rows == 1)
{
if(client) client->LogSQL(query);
if(fuzzy)
{
float difference;
difference = sqrtf(pow(fabs(x-dbx),2) + pow(fabs(y-dby),2));
client->Message(0,
"Grid assign: spawn2 id = %d updated - fuzzy match: deviation %f",
spawn2id, difference
);
}
else
{
client->Message(0, "Grid assign: spawn2 id = %d updated - exact match", spawn2id);
}
}
else
{
client->Message(0, "ERROR: found spawn2 id %d but the update query failed", spawn2id);
}
}
client->Message(0, "ERROR: Unable to assign grid - multiple spawn2 rows match");
return;
}
else
auto row = results.begin();
spawn2id = atoi(row[0]);
dbx = atof(row[1]);
dby = atof(row[2]);
query = StringFormat("UPDATE spawn2 SET pathgrid = %d WHERE id = %d", grid, spawn2id);
results = QueryDatabase(query);
if (!results.Success())
{
client->Message(0, "ERROR: Unable to assign grid - can't find it in spawn2");
}
LogFile->write(EQEMuLog::Error, "Error updating spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return;
}
if (results.RowsAffected() != 1) {
client->Message(0, "ERROR: found spawn2 id %d but the update query failed", spawn2id);
return;
}
if(client)
client->LogSQL(query.c_str());
if(!fuzzy) {
client->Message(0, "Grid assign: spawn2 id = %d updated - exact match", spawn2id);
return;
}
float difference = sqrtf(pow(fabs(x - dbx) , 2) + pow(fabs(y - dby), 2));
client->Message(0, "Grid assign: spawn2 id = %d updated - fuzzy match: deviation %f", spawn2id, difference);
}
/******************
@@ -1211,53 +1156,57 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
* type,type2: The type and type2 values for the grid being created (ignored if grid is being deleted)
* zoneid: The ID number of the zone the grid is being created/deleted in
*/
void ZoneDatabase::ModifyGrid(Client *client, bool remove, uint32 id, uint8 type, uint8 type2, uint16 zoneid) {
void ZoneDatabase::ModifyGrid(Client *c, bool remove, uint32 id, uint8 type, uint8 type2, uint16 zoneid) {
char *query = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
if (!remove)
{
if(!RunQuery(query, MakeAnyLenString(&query,"INSERT INTO grid(id,zoneid,type,type2) VALUES(%i,%i,%i,%i)",id,zoneid,type,type2), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error creating grid entry '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
}
safe_delete_array(query);
std::string query = StringFormat("INSERT INTO grid(id, zoneid, type, type2) "
"VALUES (%i, %i, %i, %i)", id, zoneid, type, type2);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error creating grid entry '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return;
}
if(client)
client->LogSQL(query.c_str());
return;
}
else
{
if(!RunQuery(query, MakeAnyLenString(&query,"DELETE FROM grid where id=%i",id), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error deleting grid '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
}
safe_delete_array(query);
query = 0;
if(!RunQuery(query, MakeAnyLenString(&query,"DELETE FROM grid_entries WHERE zoneid=%i AND gridid=%i",zoneid,id), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error deleting grid entries '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
}
safe_delete_array(query);
}
} /*** END ZoneDatabase::ModifyGrid() ***/
std::string query = StringFormat("DELETE FROM grid where id=%i", id);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error deleting grid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
query = StringFormat("DELETE FROM grid_entries WHERE zoneid = %i AND gridid = %i", zoneid, id);
results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error deleting grid entries '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
}
/**************************************
* AddWP - Adds a new waypoint to a specific grid for a specific zone.
*/
void ZoneDatabase::AddWP(Client *c, uint32 gridid, uint32 wpnum, float xpos, float ypos, float zpos, uint32 pause, uint16 zoneid, float heading)
void ZoneDatabase::AddWP(Client *client, uint32 gridid, uint32 wpnum, float xpos, float ypos, float zpos, uint32 pause, uint16 zoneid, float heading)
{
char *query = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
if(!RunQuery(query,MakeAnyLenString(&query,"INSERT INTO grid_entries (gridid,zoneid,`number`,x,y,z,pause,heading) values (%i,%i,%i,%f,%f,%f,%i,%f)",gridid,zoneid,wpnum,xpos,ypos,zpos,pause,heading), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error adding waypoint '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
std::string query = StringFormat("INSERT INTO grid_entries (gridid, zoneid, `number`, x, y, z, pause, heading) "
"VALUES (%i, %i, %i, %f, %f, %f, %i, %f)",
gridid, zoneid, wpnum, xpos, ypos, zpos, pause, heading);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error adding waypoint '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
} /*** END ZoneDatabase::AddWP() ***/
if(client)
client->LogSQL(query.c_str());
}
/**********
@@ -1270,19 +1219,20 @@ void ZoneDatabase::AddWP(Client *c, uint32 gridid, uint32 wpnum, float xpos, flo
* wp_num: The number of the waypoint being deleted
* zoneid: The ID number of the zone that contains the waypoint being deleted
*/
void ZoneDatabase::DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num, uint16 zoneid)
void ZoneDatabase::DeleteWaypoint(Client *client, uint32 grid_num, uint32 wp_num, uint16 zoneid)
{
char *query=0;
char errbuf[MYSQL_ERRMSG_SIZE];
if(!RunQuery(query, MakeAnyLenString(&query,"DELETE FROM grid_entries where gridid=%i and zoneid=%i and `number`=%i",grid_num,zoneid,wp_num), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error deleting waypoint '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
std::string query = StringFormat("DELETE FROM grid_entries WHERE "
"gridid = %i AND zoneid = %i AND `number` = %i",
grid_num, zoneid, wp_num);
auto results = QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error deleting waypoint '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
} /*** END ZoneDatabase::DeleteWaypoint() ***/
if(client)
client->LogSQL(query.c_str());
}
/******************
@@ -1292,139 +1242,112 @@ void ZoneDatabase::DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num, uin
* Returns 0 if the function didn't have to create a new grid. If the function had to create a new grid for the spawn, then the ID of
* the created grid is returned.
*/
uint32 ZoneDatabase::AddWPForSpawn(Client *client, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading) {
uint32 ZoneDatabase::AddWPForSpawn(Client *c, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading) {
char *query = 0;
uint32 grid_num, // The grid number the spawn is assigned to (if spawn has no grid, will be the grid number we end up creating)
next_wp_num; // The waypoint number we should be assigning to the new waypoint
bool CreatedNewGrid; // Did we create a new grid in this function?
MYSQL_RES *result;
MYSQL_ROW row;
char errbuf[MYSQL_ERRMSG_SIZE];
uint32 grid_num; // The grid number the spawn is assigned to (if spawn has no grid, will be the grid number we end up creating)
uint32 next_wp_num; // The waypoint number we should be assigning to the new waypoint
bool createdNewGrid; // Did we create a new grid in this function?
// See what grid number our spawn is assigned
if(RunQuery(query, MakeAnyLenString(&query,"SELECT pathgrid FROM spawn2 WHERE id=%i",spawn2id),errbuf,&result))
{
safe_delete_array(query);
if(mysql_num_rows(result) > 0)
{
row = mysql_fetch_row(result);
grid_num = atoi(row[0]);
}
else // This spawn ID was not found in the `spawn2` table
return 0;
mysql_free_result(result);
}
else { // Query error
LogFile->write(EQEMuLog::Error, "Error setting pathgrid '%s': '%s'", query, errbuf);
std::string query = StringFormat("SELECT pathgrid FROM spawn2 WHERE id = %i", spawn2id);
auto results = QueryDatabase(query);
if (!results.Success()) {
// Query error
LogFile->write(EQEMuLog::Error, "Error setting pathgrid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (grid_num == 0) // Our spawn doesn't have a grid assigned to it -- we need to create a new grid and assign it to the spawn
{
CreatedNewGrid = true;
if((grid_num = GetFreeGrid(zoneid)) == 0) // There are no grids for the current zone -- create Grid #1
grid_num = 1;
if (results.RowCount() == 0)
return 0;
if(!RunQuery(query, MakeAnyLenString(&query,"insert into grid set id='%i',zoneid= %i, type='%i', type2='%i'",grid_num,zoneid,type1,type2), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error adding grid '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
}
safe_delete_array(query);
auto row = results.begin();
grid_num = atoi(row[0]);
query = 0;
if(!RunQuery(query, MakeAnyLenString(&query,"update spawn2 set pathgrid='%i' where id='%i'",grid_num,spawn2id), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error updating spawn2 pathing '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
}
safe_delete_array(query);
if (grid_num == 0)
{ // Our spawn doesn't have a grid assigned to it -- we need to create a new grid and assign it to the spawn
createdNewGrid = true;
grid_num = GetFreeGrid(zoneid);
if(grid_num == 0) // There are no grids for the current zone -- create Grid #1
grid_num = 1;
query = StringFormat("INSERT INTO grid SET id = '%i', zoneid = %i, type ='%i', type2 = '%i'",
grid_num, zoneid, type1, type2);
results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error adding grid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
query = StringFormat("UPDATE spawn2 SET pathgrid = '%i' WHERE id = '%i'", grid_num, spawn2id);
results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error updating spawn2 pathing '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
}
else // NPC had a grid assigned to it
CreatedNewGrid = false;
createdNewGrid = false;
// Find out what the next waypoint is for this grid
query = 0;
if(RunQuery(query, MakeAnyLenString(&query,"SELECT max(`number`) FROM grid_entries WHERE zoneid='%i' AND gridid='%i'",zoneid,grid_num),errbuf,&result))
{
safe_delete_array(query);
row = mysql_fetch_row(result);
if(row[0] != 0)
next_wp_num = atoi(row[0]) + 1;
else // No waypoints in this grid yet
next_wp_num = 1;
query = StringFormat("SELECT max(`number`) FROM grid_entries WHERE zoneid = '%i' AND gridid = '%i'", zoneid, grid_num);
mysql_free_result(result);
}
else { // Query error
LogFile->write(EQEMuLog::Error, "Error getting next waypoint id '%s': '%s'", query, errbuf);
if(!results.Success()) { // Query error
LogFile->write(EQEMuLog::Error, "Error getting next waypoint id '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
query = 0;
if(!RunQuery(query, MakeAnyLenString(&query,"INSERT INTO grid_entries(gridid,zoneid,`number`,x,y,z,pause,heading) VALUES (%i,%i,%i,%f,%f,%f,%i,%f)",grid_num,zoneid,next_wp_num,xpos,ypos,zpos,pause,heading), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error adding grid entry '%s': '%s'", query, errbuf);
} else {
if(c) c->LogSQL(query);
}
safe_delete_array(query);
row = results.begin();
if(row[0] != 0)
next_wp_num = atoi(row[0]) + 1;
else // No waypoints in this grid yet
next_wp_num = 1;
if(CreatedNewGrid)
return grid_num;
return 0;
} /*** END ZoneDatabase::AddWPForSpawn() ***/
query = StringFormat("INSERT INTO grid_entries(gridid, zoneid, `number`, x, y, z, pause, heading) "
"VALUES (%i, %i, %i, %f, %f, %f, %i, %f)",
grid_num, zoneid, next_wp_num, xpos, ypos, zpos, pause, heading);
results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error adding grid entry '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
return createdNewGrid? grid_num: 0;
}
uint32 ZoneDatabase::GetFreeGrid(uint16 zoneid) {
char *query = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query,"SELECT max(id) from grid where zoneid = %i",zoneid),errbuf,&result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
uint32 tmp=0;
if (row[0])
tmp = atoi(row[0]);
mysql_free_result(result);
tmp++;
return tmp;
}
mysql_free_result(result);
std::string query = StringFormat("SELECT max(id) FROM grid WHERE zoneid = %i", zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetFreeGrid query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
else {
LogFile->write(EQEMuLog::Error, "Error in GetFreeGrid query '%s': %s", query, errbuf);
safe_delete_array(query);
}
return 0;
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
uint32 freeGridID = 1;
freeGridID = atoi(row[0]) + 1;
return freeGridID;
}
int ZoneDatabase::GetHighestWaypoint(uint32 zoneid, uint32 gridid) {
char *query = 0;
char errbuff[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
int res = 0;
if (RunQuery(query, MakeAnyLenString(&query,
"SELECT COALESCE(MAX(number), 0) FROM grid_entries WHERE zoneid = %i AND gridid = %i",
zoneid, gridid),errbuff,&result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
res = atoi( row[0] );
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in GetHighestWaypoint query '%s': %s", query, errbuff);
safe_delete_array(query);
std::string query = StringFormat("SELECT COALESCE(MAX(number), 0) FROM grid_entries "
"WHERE zoneid = %i AND gridid = %i", zoneid, gridid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetHighestWaypoint query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
return(res);
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
void NPC::SaveGuardSpotCharm()
+4 -4
View File
@@ -36,7 +36,7 @@
#include "worldserver.h"
#include "../common/eq_packet_structs.h"
#include "../common/packet_dump.h"
#include "../common/MiscFunctions.h"
#include "../common/misc_functions.h"
#include "zonedb.h"
#include "zone.h"
#include "entity.h"
@@ -45,12 +45,12 @@
#include "petitions.h"
#include "../common/packet_functions.h"
#include "../common/md5.h"
#include "ZoneConfig.h"
#include "StringIDs.h"
#include "zone_config.h"
#include "string_ids.h"
#include "guild_mgr.h"
#include "../common/rulesys.h"
#include "titles.h"
#include "QGlobals.h"
#include "qglobals.h"
extern EntityList entity_list;
+446 -550
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -18,7 +18,7 @@
#ifndef ZONE_H
#define ZONE_H
#include "../common/Mutex.h"
#include "../common/mutex.h"
#include "../common/linked_list.h"
#include "../common/types.h"
#include "../common/eqtime.h"
@@ -32,7 +32,7 @@
#include "spawn2.h"
#include "tasks.h"
#include "pathing.h"
#include "QGlobals.h"
#include "qglobals.h"
#include <unordered_map>
class Map;
+1 -1
View File
@@ -16,7 +16,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/debug.h"
#include "ZoneConfig.h"
#include "zone_config.h"
ZoneConfig *ZoneConfig::_zone_config = nullptr;
+1 -1
View File
@@ -18,7 +18,7 @@
#ifndef __ZoneConfig_H
#define __ZoneConfig_H
#include "../common/EQEmuConfig.h"
#include "../common/eqemu_config.h"
class ZoneConfig : public EQEmuConfig {
public:
+1 -1
View File
@@ -18,7 +18,7 @@
#include "../common/debug.h"
#include "../common/logsys.h"
#include "../common/BasePacket.h"
#include "../common/base_packet.h"
#include "mob.h"
#include <stdarg.h>
#include <stdio.h>
+10 -2
View File
@@ -1,7 +1,7 @@
#include "zonedb.h"
#include "../common/Item.h"
#include "../common/StringUtil.h"
#include "../common/item.h"
#include "../common/string_util.h"
#include "../common/extprofile.h"
#include "../common/guilds.h"
#include "../common/rulesys.h"
@@ -2526,3 +2526,11 @@ bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::list<struct NPCFactio
}
return true;
}
void ZoneDatabase::StoreCharacterLookup(uint32 char_id) {
std::string c_lookup = StringFormat("REPLACE INTO `character_lookup` (id, account_id, `name`, timelaston, x, y, z, zonename, zoneid, instanceid, pktime, groupid, class, `level`, lfp, lfg, mailkey, xtargets, firstlogon, inspectmessage)"
" SELECT id, account_id, `name`, timelaston, x, y, z, zonename, zoneid, instanceid, pktime, groupid, class, `level`, lfp, lfg, mailkey, xtargets, firstlogon, inspectmessage"
" FROM `character_` "
" WHERE `id` = %i ", char_id);
QueryDatabase(c_lookup);
}
+1
View File
@@ -246,6 +246,7 @@ public:
/*
* General Character Related Stuff
*/
void StoreCharacterLookup(uint32 char_id);
bool GetAccountInfoForLogin_result(MySQLRequestResult results, int16* admin = 0, char* account_name = 0,
uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = nullptr,
uint32* account_creation = 0);

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