mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
Merge from master
This commit is contained in:
commit
34c3b8628e
@ -1,5 +1,33 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 09/16/2014 ==
|
||||
demonstar55: Implement spell formula 137 (BER AA Desperation)
|
||||
Uleat (NateDog): Fix for LoadBuffs() crash when a spell with a non-persistent Illusion effect was loaded.
|
||||
demonstar55: Fix some effect calcs + implement more (derived from the client)
|
||||
|
||||
== 09/15/2014 ==
|
||||
Kayen: Nimbus effects will now be reapplied after zoning and will be removed when associated buff fades.
|
||||
|
||||
== 09/13/2014 ==
|
||||
demonstar55: Fix rogues not having Thieves' Cant
|
||||
|
||||
== 09/09/2014 ==
|
||||
demonstar55: Incrase Mob kick/bash timer by 3
|
||||
see: http://www.eqemulator.org/forums/showthread.php?t=38734
|
||||
demonstar55: Fix slow effect on NPC special attack reuse timers
|
||||
see: http://www.eqemulator.org/forums/showthread.php?t=38734
|
||||
demonstar55: Slow fixes to bots!
|
||||
demonstar55: Revamped how NPC attack rate is set
|
||||
SQL: 2014_09_09_attack_delay.sql
|
||||
demonstar55: Added attackdelay to #npcedit
|
||||
|
||||
== 09/08/2014 ==
|
||||
demonstar55: Fix slow calc
|
||||
see: http://www.eqemulator.org/forums/showthread.php?t=38734
|
||||
|
||||
== 09/07/2014 ==
|
||||
Akkadius: Fixed ROF Augment item dupe with not checking for available slots properly and adding items to the virtual instance
|
||||
|
||||
== 09/06/2014 ==
|
||||
Uleat: Tweaked 'Smart' trading code to return main slots before sub slots in stackable and free space search processes. (If enough people ask for it, I'll add an optional rule to allow 'bag packing' - the original implementation behavior)
|
||||
|
||||
|
||||
@ -272,7 +272,7 @@ bool Database::DeleteAccount(const char* name) {
|
||||
}
|
||||
|
||||
bool Database::SetLocalPassword(uint32 accid, const char* password) {
|
||||
std::string query = StringFormat("UPDATE `account` SET `password` = MD5('%s') where id=%i;", password, accid);
|
||||
std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", EscapeString(password).c_str(), accid);
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
|
||||
@ -1582,13 +1582,12 @@ int8 ItemInst::AvailableAugmentSlot(int32 augtype) const
|
||||
|
||||
bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const {
|
||||
if (m_item->ItemClass != ItemClassCommon || !m_item)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
if ((!GetItem(slot) && m_item->AugSlotVisible[slot]) && augtype == -1 || (m_item->AugSlotType[slot] && ((1 << (m_item->AugSlotType[slot] - 1)) & augtype))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve item inside container
|
||||
|
||||
@ -212,10 +212,6 @@ RULE_REAL ( Map, FixPathingZMaxDeltaMoving, 20 ) //at runtime while pathing: max
|
||||
RULE_REAL ( Map, FixPathingZMaxDeltaWaypoint, 20 ) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply.
|
||||
RULE_REAL ( Map, FixPathingZMaxDeltaSendTo, 20 ) //at runtime in SendTo: max change in Z to allow the BestZ code to apply.
|
||||
RULE_REAL ( Map, FixPathingZMaxDeltaLoading, 45 ) //while loading each waypoint: max change in Z to allow the BestZ code to apply.
|
||||
RULE_BOOL ( Map, UseClosestZ, false) // Move mobs to the nearest Z above or below, rather than just the nearest below.
|
||||
// Only set UseClosestZ true if all your .map files generated from EQGs were created
|
||||
// with azone2.
|
||||
//
|
||||
RULE_INT ( Map, FindBestZHeightAdjust, 1) // Adds this to the current Z before seeking the best Z position
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errmsg.h>
|
||||
#include <errmsg.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
@ -96,191 +96,253 @@ Close the connection to the database
|
||||
Database::~Database()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Database::AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
|
||||
char *S1 = new char[strlen(from) * 2 + 1];
|
||||
char *S2 = new char[strlen(to) * 2 + 1];
|
||||
char *S3 = new char[strlen(message) * 2 + 1];
|
||||
DoEscapeString(S1, from, strlen(from));
|
||||
DoEscapeString(S2, to, strlen(to));
|
||||
DoEscapeString(S3, message, strlen(message));
|
||||
char *escapedFrom = new char[strlen(from) * 2 + 1];
|
||||
char *escapedTo = new char[strlen(to) * 2 + 1];
|
||||
char *escapedMessage = new char[strlen(message) * 2 + 1];
|
||||
DoEscapeString(escapedFrom, from, strlen(from));
|
||||
DoEscapeString(escapedTo, to, strlen(to));
|
||||
DoEscapeString(escapedMessage, message, strlen(message));
|
||||
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_speech` SET `from`='%s', `to`='%s', `message`='%s', `minstatus`='%i', `guilddbid`='%i', `type`='%i'", S1, S2, S3, minstatus, guilddbid, type), errbuf, 0, 0)) {
|
||||
_log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
std::string query = StringFormat("INSERT INTO `qs_player_speech` "
|
||||
"SET `from` = '%s', `to` = '%s', `message`='%s', "
|
||||
"`minstatus`='%i', `guilddbid`='%i', `type`='%i'",
|
||||
escapedFrom, escapedTo, escapedMessage, minstatus, guilddbid, type);
|
||||
safe_delete_array(escapedFrom);
|
||||
safe_delete_array(escapedTo);
|
||||
safe_delete_array(escapedMessage);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(S1);
|
||||
safe_delete_array(S2);
|
||||
safe_delete_array(S3);
|
||||
|
||||
}
|
||||
|
||||
void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount) {
|
||||
void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) {
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
uint32 lastid = 0;
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record` SET `time`=NOW(), "
|
||||
"`char1_id`='%i', `char1_pp`='%i', `char1_gp`='%i', `char1_sp`='%i', `char1_cp`='%i', `char1_items`='%i', "
|
||||
"`char2_id`='%i', `char2_pp`='%i', `char2_gp`='%i', `char2_sp`='%i', `char2_cp`='%i', `char2_items`='%i'",
|
||||
QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, QS->char1_money.silver, QS->char1_money.copper, QS->char1_count,
|
||||
QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, QS->char2_money.silver, QS->char2_money.copper, QS->char2_count),
|
||||
errbuf, 0, 0, &lastid)) {
|
||||
_log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), "
|
||||
"`char1_id` = '%i', `char1_pp` = '%i', `char1_gp` = '%i', "
|
||||
"`char1_sp` = '%i', `char1_cp` = '%i', `char1_items` = '%i', "
|
||||
"`char2_id` = '%i', `char2_pp` = '%i', `char2_gp` = '%i', "
|
||||
"`char2_sp` = '%i', `char2_cp` = '%i', `char2_items` = '%i'",
|
||||
QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold,
|
||||
QS->char1_money.silver, QS->char1_money.copper, QS->char1_count,
|
||||
QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold,
|
||||
QS->char2_money.silver, QS->char2_money.copper, QS->char2_count);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
if(DetailCount > 0) {
|
||||
for(int i = 0; i < DetailCount; i++) {
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record_entries` SET `event_id`='%i', "
|
||||
"`from_id`='%i', `from_slot`='%i', `to_id`='%i', `to_slot`='%i', `item_id`='%i', "
|
||||
"`charges`='%i', `aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
|
||||
lastid, QS->items[i].from_id, QS->items[i].from_slot, QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id,
|
||||
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
|
||||
errbuf, 0, 0))) {
|
||||
_log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
if(detailCount == 0)
|
||||
return;
|
||||
|
||||
int lastIndex = results.LastInsertedID();
|
||||
|
||||
for(int i = 0; i < detailCount; i++) {
|
||||
query = StringFormat("INSERT INTO `qs_player_trade_record_entries` SET `event_id` = '%i', "
|
||||
"`from_id` = '%i', `from_slot` = '%i', `to_id` = '%i', `to_slot` = '%i', "
|
||||
"`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', "
|
||||
"`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
|
||||
lastIndex, QS->items[i].from_id, QS->items[i].from_slot,
|
||||
QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id,
|
||||
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2,
|
||||
QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5);
|
||||
results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 detailCount) {
|
||||
|
||||
std::string query = StringFormat("INSERT INTO `qs_player_handin_record` SET `time` = NOW(), "
|
||||
"`quest_id` = '%i', `char_id` = '%i', `char_pp` = '%i', "
|
||||
"`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', "
|
||||
"`char_items` = '%i', `npc_id` = '%i', `npc_pp` = '%i', "
|
||||
"`npc_gp` = '%i', `npc_sp` = '%i', `npc_cp` = '%i', "
|
||||
"`npc_items`='%i'",
|
||||
QS->quest_id, QS->char_id, QS->char_money.platinum,
|
||||
QS->char_money.gold, QS->char_money.silver, QS->char_money.copper,
|
||||
QS->char_count, QS->npc_id, QS->npc_money.platinum,
|
||||
QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper,
|
||||
QS->npc_count);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
if(detailCount == 0)
|
||||
return;
|
||||
|
||||
int lastIndex = results.LastInsertedID();
|
||||
|
||||
for(int i = 0; i < detailCount; i++) {
|
||||
query = StringFormat("INSERT INTO `qs_player_handin_record_entries` SET `event_id` = '%i', "
|
||||
"`action_type` = '%s', `char_slot` = '%i', `item_id` = '%i', "
|
||||
"`charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', "
|
||||
"`aug_4` = '%i', `aug_5` = '%i'",
|
||||
lastIndex, QS->items[i].action_type, QS->items[i].char_slot,
|
||||
QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1,
|
||||
QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4,
|
||||
QS->items[i].aug_5);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 members){
|
||||
|
||||
std::string query = StringFormat("INSERT INTO `qs_player_npc_kill_record` "
|
||||
"SET `npc_id` = '%i', `type` = '%i', "
|
||||
"`zone_id` = '%i', `time` = NOW()",
|
||||
QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
if(members == 0)
|
||||
return;
|
||||
|
||||
int lastIndex = results.LastInsertedID();
|
||||
|
||||
for (int i = 0; i < members; i++) {
|
||||
query = StringFormat("INSERT INTO `qs_player_npc_kill_record_entries` "
|
||||
"SET `event_id` = '%i', `char_id` = '%i'",
|
||||
lastIndex, QS->Chars[i].char_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
uint32 lastid = 0;
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record` SET `time`=NOW(), `quest_id`='%i', "
|
||||
"`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i', "
|
||||
"`npc_id`='%i', `npc_pp`='%i', `npc_gp`='%i', `npc_sp`='%i', `npc_cp`='%i', `npc_items`='%i'",
|
||||
QS->quest_id, QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count,
|
||||
QS->npc_id, QS->npc_money.platinum, QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, QS->npc_count),
|
||||
errbuf, 0, 0, &lastid)) {
|
||||
_log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 items) {
|
||||
|
||||
std::string query = StringFormat("INSERT INTO `qs_player_delete_record` SET `time` = NOW(), "
|
||||
"`char_id` = '%i', `stack_size` = '%i', `char_items` = '%i'",
|
||||
QS->char_id, QS->stack_size, QS->char_count, QS->char_count);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
if(DetailCount > 0) {
|
||||
for(int i = 0; i < DetailCount; i++) {
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record_entries` SET `event_id`='%i', "
|
||||
"`action_type`='%s', `char_slot`='%i', `item_id`='%i', `charges`='%i', "
|
||||
"`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
|
||||
lastid, QS->items[i].action_type, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges,
|
||||
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
|
||||
errbuf, 0, 0))) {
|
||||
_log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(items == 0)
|
||||
return;
|
||||
|
||||
int lastIndex = results.LastInsertedID();
|
||||
|
||||
for(int i = 0; i < items; i++) {
|
||||
query = StringFormat("INSERT INTO `qs_player_delete_record_entries` SET `event_id` = '%i', "
|
||||
"`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', "
|
||||
"`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
|
||||
lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges,
|
||||
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4,
|
||||
QS->items[i].aug_5);
|
||||
results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members){
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
uint32 lastid = 0;
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record` SET `npc_id`='%i', `type`='%i', `zone_id`='%i', `time`=NOW()", QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID), errbuf, 0, 0, &lastid)) {
|
||||
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
|
||||
if(Members > 0){
|
||||
for (int i = 0; i < Members; i++) {
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record_entries` SET `event_id`='%i', `char_id`='%i'", lastid, QS->Chars[i].char_id, errbuf, 0, 0))) {
|
||||
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items) {
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
uint32 lastid = 0;
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record` SET `time`=NOW(), "
|
||||
"`char_id`='%i', `stack_size`='%i', `char_items`='%i'",
|
||||
QS->char_id, QS->stack_size, QS->char_count, QS->char_count),
|
||||
errbuf, 0, 0, &lastid)) {
|
||||
_log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
|
||||
if(Items > 0) {
|
||||
for(int i = 0; i < Items; i++) {
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record_entries` SET `event_id`='%i', "
|
||||
"`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', "
|
||||
"`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
|
||||
lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1,
|
||||
QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
|
||||
errbuf, 0, 0))) {
|
||||
_log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) {
|
||||
void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 items) {
|
||||
/* These are item moves */
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
uint32 lastid = 0;
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record` SET `time`=NOW(), "
|
||||
"`char_id`='%i', `from_slot`='%i', `to_slot`='%i', `stack_size`='%i', `char_items`='%i', `postaction`='%i'",
|
||||
QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, QS->char_count, QS->postaction),
|
||||
errbuf, 0, 0, &lastid)) {
|
||||
_log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
if(Items > 0) {
|
||||
for(int i = 0; i < Items; i++) {
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record_entries` SET `event_id`='%i', "
|
||||
"`from_slot`='%i', `to_slot`='%i', `item_id`='%i', `charges`='%i', "
|
||||
"`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", lastid,
|
||||
QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, QS->items[i].charges,
|
||||
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
|
||||
errbuf, 0, 0))) {
|
||||
_log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
}
|
||||
|
||||
std::string query = StringFormat("INSERT INTO `qs_player_move_record` SET `time` = NOW(), "
|
||||
"`char_id` = '%i', `from_slot` = '%i', `to_slot` = '%i', "
|
||||
"`stack_size` = '%i', `char_items` = '%i', `postaction` = '%i'",
|
||||
QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size,
|
||||
QS->char_count, QS->postaction);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
if(items == 0)
|
||||
return;
|
||||
|
||||
int lastIndex = results.LastInsertedID();
|
||||
|
||||
for(int i = 0; i < items; i++) {
|
||||
query = StringFormat("INSERT INTO `qs_player_move_record_entries` SET `event_id` = '%i', "
|
||||
"`from_slot` = '%i', `to_slot` = '%i', `item_id` = '%i', `charges` = '%i', "
|
||||
"`aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
|
||||
lastIndex, QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id,
|
||||
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2,
|
||||
QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5);
|
||||
results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items) {
|
||||
void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 items) {
|
||||
/* Merchant transactions are from the perspective of the merchant, not the player -U */
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
uint32 lastid = 0;
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record` SET `time`=NOW(), "
|
||||
"`zone_id`='%i', `merchant_id`='%i', `merchant_pp`='%i', `merchant_gp`='%i', `merchant_sp`='%i', `merchant_cp`='%i', `merchant_items`='%i', "
|
||||
"`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i'",
|
||||
QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, QS->merchant_money.gold, QS->merchant_money.silver, QS->merchant_money.copper, QS->merchant_count,
|
||||
QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count),
|
||||
errbuf, 0, 0, &lastid)) {
|
||||
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
std::string query = StringFormat("INSERT INTO `qs_merchant_transaction_record` SET `time` = NOW(), "
|
||||
"`zone_id` = '%i', `merchant_id` = '%i', `merchant_pp` = '%i', "
|
||||
"`merchant_gp` = '%i', `merchant_sp` = '%i', `merchant_cp` = '%i', "
|
||||
"`merchant_items` = '%i', `char_id` = '%i', `char_pp` = '%i', "
|
||||
"`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', "
|
||||
"`char_items` = '%i'",
|
||||
QS->zone_id, QS->merchant_id, QS->merchant_money.platinum,
|
||||
QS->merchant_money.gold, QS->merchant_money.silver,
|
||||
QS->merchant_money.copper, QS->merchant_count, QS->char_id,
|
||||
QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver,
|
||||
QS->char_money.copper, QS->char_count);
|
||||
auto results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
if(Items > 0) {
|
||||
for(int i = 0; i < Items; i++) {
|
||||
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id`='%i', "
|
||||
"`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', "
|
||||
"`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
|
||||
lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1,
|
||||
QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
|
||||
errbuf, 0, 0))) {
|
||||
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
}
|
||||
}
|
||||
safe_delete_array(query);
|
||||
if(items == 0)
|
||||
return;
|
||||
|
||||
int lastIndex = results.LastInsertedID();
|
||||
|
||||
for(int i = 0; i < items; i++) {
|
||||
query = StringFormat("INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id` = '%i', "
|
||||
"`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', "
|
||||
"`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
|
||||
lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges,
|
||||
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4,
|
||||
QS->items[i].aug_5);
|
||||
results = QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", results.ErrorMessage().c_str());
|
||||
_log(QUERYSERV__ERROR, "%s", query.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::GeneralQueryReceive(ServerPacket *pack) {
|
||||
@ -293,11 +355,11 @@ void Database::GeneralQueryReceive(ServerPacket *pack) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
uint32 lastid = 0;
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) {
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) {
|
||||
_log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
_log(QUERYSERV__ERROR, "%s", query);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
safe_delete(pack);
|
||||
safe_delete(Query);
|
||||
safe_delete(pack);
|
||||
safe_delete(Query);
|
||||
}
|
||||
|
||||
3
utils/sql/git/required/2014_09_09_attack_delay.sql
Normal file
3
utils/sql/git/required/2014_09_09_attack_delay.sql
Normal file
@ -0,0 +1,3 @@
|
||||
ALTER TABLE `npc_types` ADD `attack_delay` TINYINT(3) UNSIGNED DEFAULT '30' NOT NULL AFTER `attack_speed`;
|
||||
UPDATE `npc_types` SET `attack_delay` = 36 + 36 * (`attack_speed` / 100);
|
||||
UPDATE `npc_types` SET `attack_delay` = 30 WHERE `attack_speed` = 0;
|
||||
@ -1402,6 +1402,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) {
|
||||
SetRacialLanguages( &pp );
|
||||
SetRaceStartingSkills( &pp );
|
||||
SetClassStartingSkills( &pp );
|
||||
SetClassLanguages(&pp);
|
||||
pp.skills[SkillSenseHeading] = 200;
|
||||
// Some one fucking fix this to use a field name. -Doodman
|
||||
//pp.unknown3596[28] = 15; // @bp: This is to enable disc usage
|
||||
@ -1981,3 +1982,15 @@ void Client::SetRacialLanguages( PlayerProfile_Struct *pp )
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetClassLanguages(PlayerProfile_Struct *pp)
|
||||
{
|
||||
// we only need to handle one class, but custom server might want to do more
|
||||
switch(pp->class_) {
|
||||
case ROGUE:
|
||||
pp->languages[LANG_THIEVES_CANT] = 100;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -90,6 +90,7 @@ private:
|
||||
void SetClassStartingSkills( PlayerProfile_Struct *pp );
|
||||
void SetRaceStartingSkills( PlayerProfile_Struct *pp );
|
||||
void SetRacialLanguages( PlayerProfile_Struct *pp );
|
||||
void SetClassLanguages(PlayerProfile_Struct *pp);
|
||||
|
||||
ClientListEntry* cle;
|
||||
Timer CLE_keepalive_timer;
|
||||
|
||||
146
zone/attack.cpp
146
zone/attack.cpp
@ -3516,7 +3516,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
if(spell_id == SPELL_UNKNOWN) {
|
||||
damage = ReduceDamage(damage);
|
||||
mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage);
|
||||
ReduceAllDamage(damage);
|
||||
damage = ReduceAllDamage(damage);
|
||||
TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker);
|
||||
} else {
|
||||
int32 origdmg = damage;
|
||||
@ -3529,7 +3529,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
//Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes.
|
||||
Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName());
|
||||
}
|
||||
ReduceAllDamage(damage);
|
||||
damage = ReduceAllDamage(damage);
|
||||
TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker);
|
||||
}
|
||||
|
||||
@ -4803,3 +4803,145 @@ void Mob::CommonBreakInvisible()
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
}
|
||||
|
||||
void Mob::SetAttackTimer()
|
||||
{
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
}
|
||||
|
||||
void Client::SetAttackTimer()
|
||||
{
|
||||
float PermaHaste = GetPermaHaste();
|
||||
|
||||
//default value for attack timer in case they have
|
||||
//an invalid weapon equipped:
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
|
||||
Timer *TimerToUse = nullptr;
|
||||
const Item_Struct *PrimaryWeapon = nullptr;
|
||||
|
||||
for (int i = MainRange; i <= MainSecondary; i++) {
|
||||
//pick a timer
|
||||
if (i == MainPrimary)
|
||||
TimerToUse = &attack_timer;
|
||||
else if (i == MainRange)
|
||||
TimerToUse = &ranged_timer;
|
||||
else if (i == MainSecondary)
|
||||
TimerToUse = &attack_dw_timer;
|
||||
else //invalid slot (hands will always hit this)
|
||||
continue;
|
||||
|
||||
const Item_Struct *ItemToUse = nullptr;
|
||||
|
||||
//find our item
|
||||
ItemInst *ci = GetInv().GetItem(i);
|
||||
if (ci)
|
||||
ItemToUse = ci->GetItem();
|
||||
|
||||
//special offhand stuff
|
||||
if (i == MainSecondary) {
|
||||
//if we have a 2H weapon in our main hand, no dual
|
||||
if (PrimaryWeapon != nullptr) {
|
||||
if (PrimaryWeapon->ItemClass == ItemClassCommon
|
||||
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|
||||
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|
||||
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//if we cant dual wield, skip it
|
||||
if (!CanThisClassDualWield()) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//see if we have a valid weapon
|
||||
if (ItemToUse != nullptr) {
|
||||
//check type and damage/delay
|
||||
if (ItemToUse->ItemClass != ItemClassCommon
|
||||
|| ItemToUse->Damage == 0
|
||||
|| ItemToUse->Delay == 0) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
// Check to see if skill is valid
|
||||
else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) &&
|
||||
(ItemToUse->ItemType != ItemTypeMartial) &&
|
||||
(ItemToUse->ItemType != ItemType2HPiercing)) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
|
||||
int speed = 0;
|
||||
|
||||
//if we have no weapon..
|
||||
if (ItemToUse == nullptr) {
|
||||
//above checks ensure ranged weapons do not fall into here
|
||||
// Work out if we're a monk
|
||||
if ((GetClass() == MONK) || (GetClass() == BEASTLORD))
|
||||
speed = static_cast<int>((GetMonkHandToHandDelay() * (100 + DelayMod) / 100) * PermaHaste);
|
||||
else
|
||||
speed = static_cast<int>((36 * (100 + DelayMod) / 100) * PermaHaste);
|
||||
} else {
|
||||
//we have a weapon, use its delay
|
||||
// Convert weapon delay to timer resolution (milliseconds)
|
||||
//delay * 100
|
||||
speed = static_cast<int>((ItemToUse->Delay * (100 + DelayMod) / 100) * PermaHaste);
|
||||
if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) {
|
||||
float quiver_haste = GetQuiverHaste();
|
||||
if (quiver_haste > 0)
|
||||
speed *= quiver_haste;
|
||||
}
|
||||
}
|
||||
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
||||
|
||||
if (i == MainPrimary)
|
||||
PrimaryWeapon = ItemToUse;
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::SetAttackTimer()
|
||||
{
|
||||
float PermaHaste = GetPermaHaste();
|
||||
|
||||
//default value for attack timer in case they have
|
||||
//an invalid weapon equipped:
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
|
||||
Timer *TimerToUse = nullptr;
|
||||
|
||||
for (int i = MainRange; i <= MainSecondary; i++) {
|
||||
//pick a timer
|
||||
if (i == MainPrimary)
|
||||
TimerToUse = &attack_timer;
|
||||
else if (i == MainRange)
|
||||
TimerToUse = &ranged_timer;
|
||||
else if (i == MainSecondary)
|
||||
TimerToUse = &attack_dw_timer;
|
||||
else //invalid slot (hands will always hit this)
|
||||
continue;
|
||||
|
||||
//special offhand stuff
|
||||
if (i == MainSecondary) {
|
||||
//NPCs get it for free at 13
|
||||
if(GetLevel() < 13) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
|
||||
|
||||
// Technically NPCs should do some logic for weapons, but the effect is minimal
|
||||
// What they do is take the lower of their set delay and the weapon's
|
||||
// ex. Mob's delay set to 20, weapon set to 19, delay 19
|
||||
// Mob's delay set to 20, weapon set to 21, delay 20
|
||||
int speed = static_cast<int>((attack_delay * (100 + DelayMod) / 100) * PermaHaste);
|
||||
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
||||
}
|
||||
}
|
||||
|
||||
12
zone/bot.cpp
12
zone/bot.cpp
@ -8350,12 +8350,10 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
|
||||
return;
|
||||
|
||||
float HasteModifier = 0;
|
||||
if(GetHaste() >= 0){
|
||||
if (GetHaste())
|
||||
HasteModifier = 10000 / (100 + GetHaste());
|
||||
}
|
||||
else {
|
||||
HasteModifier = (100 - GetHaste());
|
||||
}
|
||||
else
|
||||
HasteModifier = 100;
|
||||
int32 dmg = 0;
|
||||
|
||||
uint16 skill_to_use = -1;
|
||||
@ -8981,10 +8979,8 @@ int32 Bot::CalcMaxMana() {
|
||||
|
||||
void Bot::SetAttackTimer() {
|
||||
float PermaHaste;
|
||||
if(GetHaste() > 0)
|
||||
if (GetHaste())
|
||||
PermaHaste = 1 / (1 + (float)GetHaste()/100);
|
||||
else if(GetHaste() < 0)
|
||||
PermaHaste = 1 * (1 - (float)GetHaste()/100);
|
||||
else
|
||||
PermaHaste = 1.0f;
|
||||
|
||||
|
||||
@ -3949,54 +3949,38 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const
|
||||
|
||||
void Client::KeyRingLoad()
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
query = new char[256];
|
||||
|
||||
sprintf(query, "SELECT item_id FROM keyring WHERE char_id='%i' ORDER BY item_id",character_id);
|
||||
if (database.RunQuery(query, strlen(query), errbuf, &result))
|
||||
{
|
||||
safe_delete_array(query);
|
||||
while(0 != (row = mysql_fetch_row(result))){
|
||||
keyring.push_back(atoi(row[0]));
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}else {
|
||||
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << errbuf << std::endl;
|
||||
safe_delete_array(query);
|
||||
std::string query = StringFormat("SELECT item_id FROM keyring "
|
||||
"WHERE char_id = '%i' ORDER BY item_id", character_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << results.ErrorMessage() << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
keyring.push_back(atoi(row[0]));
|
||||
|
||||
}
|
||||
|
||||
void Client::KeyRingAdd(uint32 item_id)
|
||||
{
|
||||
if(0==item_id)return;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
uint32 affected_rows = 0;
|
||||
query = new char[256];
|
||||
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)) {
|
||||
Message(4,"Added to keyring.");
|
||||
if(0==item_id)
|
||||
return;
|
||||
|
||||
/* 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 {
|
||||
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl;
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
keyring.push_back(item_id);
|
||||
}
|
||||
bool found = KeyRingCheck(item_id);
|
||||
if (found)
|
||||
return;
|
||||
|
||||
std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << results.ErrorMessage() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Message(4,"Added to keyring.");
|
||||
|
||||
keyring.push_back(item_id);
|
||||
}
|
||||
|
||||
bool Client::KeyRingCheck(uint32 item_id)
|
||||
@ -8282,3 +8266,19 @@ void Client::ShowNumHits()
|
||||
return;
|
||||
}
|
||||
|
||||
float Client::GetQuiverHaste()
|
||||
{
|
||||
float quiver_haste = 0;
|
||||
for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) {
|
||||
const ItemInst *pi = GetInv().GetItem(r);
|
||||
if (!pi)
|
||||
continue;
|
||||
if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) {
|
||||
float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv));
|
||||
quiver_haste = std::max(temp_wr, quiver_haste);
|
||||
}
|
||||
}
|
||||
if (quiver_haste > 0)
|
||||
quiver_haste = 1.0f / (1.0f + static_cast<float>(quiver_haste) / 100.0f);
|
||||
return quiver_haste;
|
||||
}
|
||||
|
||||
@ -218,6 +218,8 @@ public:
|
||||
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
|
||||
virtual inline bool IsBerserk() { return berserk; }
|
||||
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
|
||||
virtual void SetAttackTimer();
|
||||
float GetQuiverHaste();
|
||||
|
||||
void AI_Init();
|
||||
void AI_Start(uint32 iMoveDelay = 0);
|
||||
@ -1193,9 +1195,9 @@ public:
|
||||
int32 mod_client_xp(int32 in_exp, NPC *npc);
|
||||
uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level);
|
||||
int mod_client_haste_cap(int cap);
|
||||
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
|
||||
int mod_food_value(const Item_Struct *item, int change);
|
||||
int mod_drink_value(const Item_Struct *item, int change);
|
||||
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
|
||||
int mod_food_value(const Item_Struct *item, int change);
|
||||
int mod_drink_value(const Item_Struct *item, int change);
|
||||
|
||||
void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; }
|
||||
bool GetEngagedRaidTarget() const { return EngagedRaidTarget; }
|
||||
|
||||
@ -6781,7 +6781,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
||||
tobe_auged = user_inv.GetItem(slot_id);
|
||||
auged_with = user_inv.GetItem(MainCursor);
|
||||
|
||||
if(tobe_auged && auged_with)
|
||||
if (tobe_auged && auged_with)
|
||||
{
|
||||
if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) &&
|
||||
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
|
||||
@ -9432,11 +9432,17 @@ void Client::CompleteConnect() {
|
||||
//reapply some buffs
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for (uint32 j1 = 0; j1 < buff_count; j1++) {
|
||||
if (buffs[j1].spellid >(uint32)SPDAT_RECORDS)
|
||||
if (!IsValidSpell(buffs[j1].spellid))
|
||||
continue;
|
||||
|
||||
const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid];
|
||||
|
||||
int NimbusEffect = GetNimbusEffect(buffs[j1].spellid);
|
||||
if(NimbusEffect) {
|
||||
if(!IsNimbusEffectActive(NimbusEffect))
|
||||
SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true);
|
||||
}
|
||||
|
||||
for (int x1 = 0; x1 < EFFECT_COUNT; x1++) {
|
||||
switch (spell.effectid[x1]) {
|
||||
case SE_IllusionCopy:
|
||||
|
||||
@ -6555,6 +6555,7 @@ void command_npcedit(Client *c, const Seperator *sep)
|
||||
c->Message(0, "#npcedit qglobal - Sets an NPC's quest global flag");
|
||||
c->Message(0, "#npcedit limit - Sets an NPC's spawn limit counter");
|
||||
c->Message(0, "#npcedit Attackspeed - Sets an NPC's attack speed modifier");
|
||||
c->Message(0, "#npcedit Attackdelay - Sets an NPC's attack delay");
|
||||
c->Message(0, "#npcedit findable - Sets an NPC's findable flag");
|
||||
c->Message(0, "#npcedit wep1 - Sets an NPC's primary weapon model");
|
||||
c->Message(0, "#npcedit wep2 - Sets an NPC's secondary weapon model");
|
||||
@ -7056,6 +7057,15 @@ void command_npcedit(Client *c, const Seperator *sep)
|
||||
c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
else if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 )
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
c->Message(15,"NPCID %u now has attack_delay set to %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2]));
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_delay=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf);
|
||||
c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
else if ( strcasecmp( sep->arg[1], "findable" ) == 0 )
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
|
||||
@ -356,9 +356,11 @@ void Client::GoFish()
|
||||
inst = m_inv.GetItem(MainCursor);
|
||||
}
|
||||
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
|
||||
if(inst) {
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -470,9 +472,11 @@ void Client::ForageItem(bool guarantee) {
|
||||
inst = m_inv.GetItem(MainCursor);
|
||||
}
|
||||
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
|
||||
if(inst) {
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
|
||||
}
|
||||
}
|
||||
|
||||
int ChanceSecondForage = aabonuses.ForageAdditionalItems + itembonuses.ForageAdditionalItems + spellbonuses.ForageAdditionalItems;
|
||||
|
||||
@ -561,6 +561,18 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender)
|
||||
}
|
||||
}
|
||||
|
||||
/* This may seem pointless but the case above does not cover the following situation:
|
||||
* Group has Leader a, member b, member c
|
||||
* b and c are out of zone
|
||||
* a disconnects/quits
|
||||
* b or c zone back in and disconnects/quits
|
||||
* a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b)
|
||||
* Ultimately we should think up a better solution to this.
|
||||
*/
|
||||
if(oldmember == GetLeader()) {
|
||||
SetLeader(nullptr);
|
||||
}
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct));
|
||||
ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer;
|
||||
gl->gid = GetID();
|
||||
|
||||
@ -165,7 +165,7 @@ void Client::SendGuildSpawnAppearance() {
|
||||
switch (rank) {
|
||||
case 0: { rank = 5; break; } // GUILD_MEMBER 0
|
||||
case 1: { rank = 3; break; } // GUILD_OFFICER 1
|
||||
case 2: { rank = 1; break; } // GUILD_LEADER 2
|
||||
case 2: { rank = 1; break; } // GUILD_LEADER 2
|
||||
default: { break; } // GUILD_NONE
|
||||
}
|
||||
}
|
||||
@ -417,57 +417,36 @@ void Client::GuildChangeRank(const char* name, uint32 guild_id, uint32 oldrank,
|
||||
}*/
|
||||
|
||||
|
||||
bool ZoneDatabase::CheckGuildDoor(uint8 doorid,uint16 guild_id,const char* zone) {
|
||||
MYSQL_ROW row;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
if (!RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT guild FROM doors where doorid=%i AND zone='%s'",
|
||||
doorid-128, zone), errbuf, &result))
|
||||
{
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
return false;
|
||||
} else {
|
||||
if (mysql_num_rows(result) == 1) {
|
||||
row = mysql_fetch_row(result);
|
||||
if (atoi(row[0]) == guild_id)
|
||||
{
|
||||
mysql_free_result(result);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
}
|
||||
bool ZoneDatabase::CheckGuildDoor(uint8 doorid, uint16 guild_id, const char* zone) {
|
||||
|
||||
// code below will never be reached
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
}
|
||||
std::string query = StringFormat("SELECT guild FROM doors WHERE doorid = %i AND zone = '%s'",
|
||||
doorid-128, zone);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (results.RowCount() != 1)
|
||||
return false;
|
||||
|
||||
auto row = results.begin();
|
||||
return atoi(row[0]) == guild_id;
|
||||
}
|
||||
|
||||
bool ZoneDatabase::SetGuildDoor(uint8 doorid,uint16 guild_id, const char* zone) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
uint32 affected_rows = 0;
|
||||
|
||||
if (doorid > 127)
|
||||
doorid = doorid - 128;
|
||||
if (!RunQuery(query, MakeAnyLenString(&query,
|
||||
"UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')",
|
||||
guild_id, doorid, zone), errbuf, 0,&affected_rows))
|
||||
{
|
||||
LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
|
||||
std::string query = StringFormat("UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')",
|
||||
guild_id, doorid, zone);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
return(affected_rows > 0);
|
||||
return (results.RowsAffected() > 0);
|
||||
}
|
||||
|
||||
|
||||
@ -850,24 +850,16 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *Query="INSERT INTO `guild_bank` (`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
|
||||
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')";
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
|
||||
char* query = 0;
|
||||
|
||||
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor), errbuf))
|
||||
{
|
||||
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query, errbuf);
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
std::string query = StringFormat("INSERT INTO `guild_bank` "
|
||||
"(`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
|
||||
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')",
|
||||
GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
const Item_Struct *Item = database.GetItem(ItemID);
|
||||
|
||||
GuildBankItemUpdate_Struct gbius;
|
||||
|
||||
169
zone/mob.cpp
169
zone/mob.cpp
@ -174,8 +174,9 @@ Mob::Mob(const char* in_name,
|
||||
drakkin_heritage = in_drakkin_heritage;
|
||||
drakkin_tattoo = in_drakkin_tattoo;
|
||||
drakkin_details = in_drakkin_details;
|
||||
attack_speed= 0;
|
||||
slow_mitigation= 0;
|
||||
attack_speed = 0;
|
||||
attack_delay = 0;
|
||||
slow_mitigation = 0;
|
||||
findable = false;
|
||||
trackable = true;
|
||||
has_shieldequiped = false;
|
||||
@ -1940,159 +1941,6 @@ void Mob::Kill() {
|
||||
Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
|
||||
}
|
||||
|
||||
void Mob::SetAttackTimer() {
|
||||
float PermaHaste;
|
||||
if(GetHaste() > 0)
|
||||
PermaHaste = 1 / (1 + (float)GetHaste()/100);
|
||||
else if(GetHaste() < 0)
|
||||
PermaHaste = 1 * (1 - (float)GetHaste()/100);
|
||||
else
|
||||
PermaHaste = 1.0f;
|
||||
|
||||
//default value for attack timer in case they have
|
||||
//an invalid weapon equipped:
|
||||
attack_timer.SetAtTrigger(4000, true);
|
||||
|
||||
Timer* TimerToUse = nullptr;
|
||||
const Item_Struct* PrimaryWeapon = nullptr;
|
||||
|
||||
for (int i=MainRange; i<=MainSecondary; i++) {
|
||||
|
||||
//pick a timer
|
||||
if (i == MainPrimary)
|
||||
TimerToUse = &attack_timer;
|
||||
else if (i == MainRange)
|
||||
TimerToUse = &ranged_timer;
|
||||
else if(i == MainSecondary)
|
||||
TimerToUse = &attack_dw_timer;
|
||||
else //invalid slot (hands will always hit this)
|
||||
continue;
|
||||
|
||||
const Item_Struct* ItemToUse = nullptr;
|
||||
|
||||
//find our item
|
||||
if (IsClient()) {
|
||||
ItemInst* ci = CastToClient()->GetInv().GetItem(i);
|
||||
if (ci)
|
||||
ItemToUse = ci->GetItem();
|
||||
} else if(IsNPC())
|
||||
{
|
||||
//The code before here was fundementally flawed because equipment[]
|
||||
//isn't the same as PC inventory and also:
|
||||
//NPCs don't use weapon speed to dictate how fast they hit anyway.
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
|
||||
//special offhand stuff
|
||||
if(i == MainSecondary) {
|
||||
//if we have a 2H weapon in our main hand, no dual
|
||||
if(PrimaryWeapon != nullptr) {
|
||||
if( PrimaryWeapon->ItemClass == ItemClassCommon
|
||||
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|
||||
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|
||||
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//clients must have the skill to use it...
|
||||
if(IsClient()) {
|
||||
//if we cant dual wield, skip it
|
||||
if (!CanThisClassDualWield()) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
//NPCs get it for free at 13
|
||||
if(GetLevel() < 13) {
|
||||
attack_dw_timer.Disable();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//see if we have a valid weapon
|
||||
if(ItemToUse != nullptr) {
|
||||
//check type and damage/delay
|
||||
if(ItemToUse->ItemClass != ItemClassCommon
|
||||
|| ItemToUse->Damage == 0
|
||||
|| ItemToUse->Delay == 0) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
// Check to see if skill is valid
|
||||
else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) {
|
||||
//no weapon
|
||||
ItemToUse = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands;
|
||||
if (DelayMod < -99)
|
||||
DelayMod = -99;
|
||||
|
||||
//if we have no weapon..
|
||||
if (ItemToUse == nullptr) {
|
||||
//above checks ensure ranged weapons do not fall into here
|
||||
// Work out if we're a monk
|
||||
if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) {
|
||||
//we are a monk, use special delay
|
||||
int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
|
||||
// 1200 seemed too much, with delay 10 weapons available
|
||||
if(speed < RuleI(Combat, MinHastedDelay)) //lower bound
|
||||
speed = RuleI(Combat, MinHastedDelay);
|
||||
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic
|
||||
} else {
|
||||
//not a monk... using fist, regular delay
|
||||
int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
|
||||
if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound
|
||||
speed = RuleI(Combat, MinHastedDelay);
|
||||
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36
|
||||
}
|
||||
} else {
|
||||
//we have a weapon, use its delay
|
||||
// Convert weapon delay to timer resolution (milliseconds)
|
||||
//delay * 100
|
||||
int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
|
||||
if(speed < RuleI(Combat, MinHastedDelay))
|
||||
speed = RuleI(Combat, MinHastedDelay);
|
||||
|
||||
if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing))
|
||||
{
|
||||
if(IsClient())
|
||||
{
|
||||
float max_quiver = 0;
|
||||
for(int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++)
|
||||
{
|
||||
const ItemInst *pi = CastToClient()->GetInv().GetItem(r);
|
||||
if(!pi)
|
||||
continue;
|
||||
if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver)
|
||||
{
|
||||
float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) );
|
||||
if(temp_wr > max_quiver)
|
||||
{
|
||||
max_quiver = temp_wr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(max_quiver > 0)
|
||||
{
|
||||
float quiver_haste = 1 / (1 + max_quiver / 100);
|
||||
speed *= quiver_haste;
|
||||
}
|
||||
}
|
||||
}
|
||||
TimerToUse->SetAtTrigger(speed, true);
|
||||
}
|
||||
|
||||
if(i == MainPrimary)
|
||||
PrimaryWeapon = ItemToUse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Mob::CanThisClassDualWield(void) const {
|
||||
if(!IsClient()) {
|
||||
return(GetSkill(SkillDualWield) > 0);
|
||||
@ -4528,6 +4376,15 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID)
|
||||
|
||||
void Mob::RemoveNimbusEffect(int effectid)
|
||||
{
|
||||
if (effectid == nimbus_effect1)
|
||||
nimbus_effect1 = 0;
|
||||
|
||||
else if (effectid == nimbus_effect2)
|
||||
nimbus_effect2 = 0;
|
||||
|
||||
else if (effectid == nimbus_effect3)
|
||||
nimbus_effect3 = 0;
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct));
|
||||
RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer;
|
||||
rne->spawnid = GetID();
|
||||
@ -5068,7 +4925,7 @@ void Mob::ClearSpecialAbilities() {
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::ProcessSpecialAbilities(const std::string str) {
|
||||
void Mob::ProcessSpecialAbilities(const std::string &str) {
|
||||
ClearSpecialAbilities();
|
||||
|
||||
std::vector<std::string> sp = SplitString(str, '^');
|
||||
|
||||
@ -684,6 +684,7 @@ public:
|
||||
inline bool GetInvul(void) { return invulnerable; }
|
||||
inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; }
|
||||
virtual int GetHaste();
|
||||
inline float GetPermaHaste() { return GetHaste() ? 100.0f / (1.0f + static_cast<float>(GetHaste()) / 100.0f) : 100.0f; }
|
||||
|
||||
uint8 GetWeaponDamageBonus(const Item_Struct* Weapon);
|
||||
uint16 GetDamageTable(SkillUseTypes skillinuse);
|
||||
@ -842,7 +843,7 @@ public:
|
||||
void StopSpecialAbilityTimer(int ability);
|
||||
Timer *GetSpecialAbilityTimer(int ability);
|
||||
void ClearSpecialAbilities();
|
||||
void ProcessSpecialAbilities(const std::string str);
|
||||
void ProcessSpecialAbilities(const std::string &str);
|
||||
|
||||
Shielders_Struct shielder[MAX_SHIELDERS];
|
||||
Trade* trade;
|
||||
@ -1052,6 +1053,7 @@ protected:
|
||||
Timer attack_dw_timer;
|
||||
Timer ranged_timer;
|
||||
float attack_speed; //% increase/decrease in attack speed (not haste)
|
||||
int8 attack_delay; //delay between attacks in 10ths of seconds
|
||||
float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
|
||||
Timer tic_timer;
|
||||
Timer mana_timer;
|
||||
|
||||
@ -248,6 +248,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
delaytimer = false;
|
||||
combat_event = false;
|
||||
attack_speed = d->attack_speed;
|
||||
attack_delay = d->attack_delay;
|
||||
slow_mitigation = d->slow_mitigation;
|
||||
|
||||
EntityList::RemoveNumbers(name);
|
||||
@ -935,6 +936,8 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z,
|
||||
npc_type->WIS = 150;
|
||||
npc_type->CHA = 150;
|
||||
|
||||
npc_type->attack_delay = 30;
|
||||
|
||||
npc_type->prim_melee_type = 28;
|
||||
npc_type->sec_melee_type = 28;
|
||||
|
||||
|
||||
16
zone/npc.h
16
zone/npc.h
@ -134,7 +134,6 @@ public:
|
||||
void CalcNPCRegen();
|
||||
void CalcNPCDamage();
|
||||
|
||||
|
||||
int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
|
||||
int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
|
||||
inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;}
|
||||
@ -158,6 +157,7 @@ public:
|
||||
virtual void InitializeBuffSlots();
|
||||
virtual void UninitializeBuffSlots();
|
||||
|
||||
virtual void SetAttackTimer();
|
||||
virtual void RangedAttack(Mob* other);
|
||||
virtual void ThrowingAttack(Mob* other) { }
|
||||
int32 GetNumberOfAttacks() const { return attack_count; }
|
||||
@ -388,16 +388,16 @@ public:
|
||||
inline void SetHealScale(float amt) { healscale = amt; }
|
||||
inline float GetHealScale() { return healscale; }
|
||||
|
||||
uint32 GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void SetMerchantProbability(uint8 amt) { probability = amt; }
|
||||
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_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);
|
||||
void mod_npc_killed(Mob* oos);
|
||||
void AISpellsList(Client *c);
|
||||
|
||||
void AISpellsList(Client *c);
|
||||
|
||||
bool IsRaidTarget() const { return raid_target; };
|
||||
|
||||
protected:
|
||||
|
||||
@ -165,13 +165,12 @@ void QGlobalCache::LoadByGlobalContext()
|
||||
LoadBy(query);
|
||||
}
|
||||
|
||||
void QGlobalCache::LoadBy(const std::string 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));
|
||||
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));
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ public:
|
||||
void LoadByZoneID(uint32 zoneID); //zone
|
||||
void LoadByGlobalContext(); //zone
|
||||
protected:
|
||||
void LoadBy(const std::string query);
|
||||
void LoadBy(const std::string &query);
|
||||
std::list<QGlobal> qGlobalBucket;
|
||||
};
|
||||
|
||||
|
||||
@ -1413,10 +1413,8 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
return;
|
||||
|
||||
float HasteModifier = 0;
|
||||
if(GetHaste() > 0)
|
||||
if (GetHaste())
|
||||
HasteModifier = 10000 / (100 + GetHaste());
|
||||
else if(GetHaste() < 0)
|
||||
HasteModifier = (100 - GetHaste());
|
||||
else
|
||||
HasteModifier = 100;
|
||||
|
||||
@ -1464,7 +1462,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = KickReuseTime * 1000;
|
||||
reuse = (KickReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
@ -1484,7 +1482,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = BashReuseTime * 1000;
|
||||
reuse = (BashReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
@ -1537,7 +1535,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = KickReuseTime * 1000;
|
||||
reuse = (KickReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
@ -1562,7 +1560,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
||||
}
|
||||
}
|
||||
|
||||
reuse = BashReuseTime * 1000;
|
||||
reuse = (BashReuseTime + 3) * 1000;
|
||||
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
|
||||
did_attack = true;
|
||||
}
|
||||
|
||||
@ -202,7 +202,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
effect_value = GetMaxHP();
|
||||
|
||||
if (GetSpellPowerDistanceMod())
|
||||
effect_value = effect_value*(GetSpellPowerDistanceMod()/100);
|
||||
effect_value = effect_value*GetSpellPowerDistanceMod()/100;
|
||||
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
effect_desc[0] = 0;
|
||||
@ -3127,26 +3127,42 @@ snare has both of them negative, yet their range should work the same:
|
||||
|
||||
case 110: // confirmed 2/6/04
|
||||
//is there a reason we dont use updownsign here???
|
||||
result = ubase + (caster_level / 5); break;
|
||||
result = ubase + (caster_level / 6);
|
||||
break;
|
||||
|
||||
case 111:
|
||||
result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 6 * (caster_level - 16));
|
||||
break;
|
||||
case 112:
|
||||
result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 8 * (caster_level - 24));
|
||||
break;
|
||||
case 113:
|
||||
result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 10 * (caster_level - 34));
|
||||
break;
|
||||
case 114:
|
||||
result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
result = updownsign * (ubase + 15 * (caster_level - 44));
|
||||
break;
|
||||
|
||||
//these formula were updated according to lucy 10/16/04
|
||||
case 115: // this is only in symbol of transal
|
||||
result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 15)
|
||||
result += 7 * (caster_level - 15);
|
||||
break;
|
||||
case 116: // this is only in symbol of ryltan
|
||||
result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 24)
|
||||
result += 10 * (caster_level - 24);
|
||||
break;
|
||||
case 117: // this is only in symbol of pinzarn
|
||||
result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 34)
|
||||
result += 13 * (caster_level - 34);
|
||||
break;
|
||||
case 118: // used in naltron and a few others
|
||||
result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
result = ubase;
|
||||
if (caster_level > 44)
|
||||
result += 20 * (caster_level - 44);
|
||||
break;
|
||||
|
||||
case 119: // confirmed 2/6/04
|
||||
result = ubase + (caster_level / 8); break;
|
||||
@ -3166,6 +3182,93 @@ snare has both of them negative, yet their range should work the same:
|
||||
result = MakeRandomInt(ubase, abs(max));
|
||||
break;
|
||||
|
||||
case 124: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += caster_level - 50;
|
||||
break;
|
||||
|
||||
case 125: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 2 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 126: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 3 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 127: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 4 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 128: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 5 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 129: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 10 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 130: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 15 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 131: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 20 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 132: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 25 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 137: // used in berserker AA desperation
|
||||
result = ubase - (ubase * (GetHPRatio() / 100.0f));
|
||||
break;
|
||||
|
||||
case 138: { // unused on live?
|
||||
int maxhps = GetMaxHP() / 2;
|
||||
if (GetHP() <= maxhps)
|
||||
result = -(ubase * GetHP() / maxhps);
|
||||
else
|
||||
result = -ubase;
|
||||
break;
|
||||
}
|
||||
|
||||
case 139: // check sign
|
||||
result = ubase + (caster_level > 30 ? (caster_level - 30) / 2 : 0);
|
||||
break;
|
||||
|
||||
case 140: // check sign
|
||||
result = ubase + (caster_level > 30 ? caster_level - 30 : 0);
|
||||
break;
|
||||
|
||||
case 141: // check sign
|
||||
result = ubase + (caster_level > 30 ? (3 * caster_level - 90) / 2 : 0);
|
||||
break;
|
||||
|
||||
case 142: // check sign
|
||||
result = ubase + (caster_level > 30 ? 2 * caster_level - 60 : 0);
|
||||
break;
|
||||
|
||||
case 143: // check sign
|
||||
result = ubase + (3 * caster_level / 4);
|
||||
break;
|
||||
|
||||
//these are used in stacking effects... formula unknown
|
||||
case 201:
|
||||
case 203:
|
||||
@ -4016,6 +4119,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
||||
if (!found_numhits)
|
||||
Numhits(false);
|
||||
}
|
||||
|
||||
if (spells[buffs[slot].spellid].NimbusEffect > 0)
|
||||
RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect);
|
||||
|
||||
buffs[slot].spellid = SPELL_UNKNOWN;
|
||||
if(IsPet() && GetOwner() && GetOwner()->IsClient()) {
|
||||
|
||||
@ -4501,13 +4501,12 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
else
|
||||
{
|
||||
resist_chance -= roll;
|
||||
if(resist_chance < 1)
|
||||
{
|
||||
resist_chance = 1;
|
||||
}
|
||||
|
||||
int partial_modifier = ((150 * (roll - resist_chance)) / resist_chance);
|
||||
int partial_modifier = ((150 * (resist_chance - roll)) / resist_chance);
|
||||
|
||||
if(IsNPC())
|
||||
{
|
||||
@ -4535,17 +4534,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
if(partial_modifier < 0)
|
||||
if(partial_modifier <= 0)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
else if(partial_modifier >= 100)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(partial_modifier > 100)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
return partial_modifier;
|
||||
return (100.0f - partial_modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
216
zone/titles.cpp
216
zone/titles.cpp
@ -31,25 +31,17 @@ bool TitleManager::LoadTitles()
|
||||
{
|
||||
Titles.clear();
|
||||
|
||||
TitleEntry Title;
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = nullptr;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (!database.RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, `min_aa_points`, `max_aa_points`, `class`, `gender`, "
|
||||
"`char_id`, `status`, `item_id`, `prefix`, `suffix`, `title_set` from titles"), errbuf, &result))
|
||||
{
|
||||
LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
return(false);
|
||||
std::string query = "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, "
|
||||
"`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, "
|
||||
"`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles";
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
TitleEntry Title;
|
||||
Title.TitleID = atoi(row[0]);
|
||||
Title.SkillID = (SkillUseTypes) atoi(row[1]);
|
||||
Title.MinSkillValue = atoi(row[2]);
|
||||
@ -66,9 +58,8 @@ bool TitleManager::LoadTitles()
|
||||
Title.TitleSet = atoi(row[13]);
|
||||
Titles.push_back(Title);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
|
||||
return(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c)
|
||||
@ -244,92 +235,70 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue)
|
||||
return false;
|
||||
}
|
||||
|
||||
void TitleManager::CreateNewPlayerTitle(Client *c, const char *Title)
|
||||
void TitleManager::CreateNewPlayerTitle(Client *client, const char *title)
|
||||
{
|
||||
if(!c || !Title)
|
||||
if(!client || !title)
|
||||
return;
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = nullptr;
|
||||
MYSQL_RES *result;
|
||||
char *escTitle = new char[strlen(title) * 2 + 1];
|
||||
|
||||
char *EscTitle = new char[strlen(Title) * 2 + 1];
|
||||
client->SetAATitle(title);
|
||||
|
||||
c->SetAATitle(Title);
|
||||
|
||||
database.DoEscapeString(EscTitle, Title, strlen(Title));
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT `id` from titles where `prefix` = '%s' and char_id = %i", EscTitle, c->CharacterID()), errbuf, &result))
|
||||
{
|
||||
if(mysql_num_rows(result) > 0)
|
||||
{
|
||||
mysql_free_result(result);
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscTitle);
|
||||
return;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
database.DoEscapeString(escTitle, title, strlen(title));
|
||||
auto query = StringFormat("SELECT `id` FROM titles "
|
||||
"WHERE `prefix` = '%s' AND char_id = %i",
|
||||
escTitle, client->CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() > 0){
|
||||
safe_delete_array(escTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `prefix`) VALUES(%i, '%s')",
|
||||
c->CharacterID(), EscTitle), errbuf))
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query, errbuf);
|
||||
else
|
||||
{
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscTitle);
|
||||
query = StringFormat("INSERT INTO titles (`char_id`, `prefix`) VALUES(%i, '%s')",
|
||||
client->CharacterID(), escTitle);
|
||||
safe_delete_array(escTitle);
|
||||
results = database.QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void TitleManager::CreateNewPlayerSuffix(Client *c, const char *Suffix)
|
||||
void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix)
|
||||
{
|
||||
if(!c || !Suffix)
|
||||
if(!client || !suffix)
|
||||
return;
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = nullptr;
|
||||
MYSQL_RES *result;
|
||||
client->SetTitleSuffix(suffix);
|
||||
|
||||
char *EscSuffix = new char[strlen(Suffix) * 2 + 1];
|
||||
char *escSuffix = new char[strlen(suffix) * 2 + 1];
|
||||
database.DoEscapeString(escSuffix, suffix, strlen(suffix));
|
||||
|
||||
c->SetTitleSuffix(Suffix);
|
||||
|
||||
database.DoEscapeString(EscSuffix, Suffix, strlen(Suffix));
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query,
|
||||
"SELECT `id` from titles where `suffix` = '%s' and char_id = %i", EscSuffix, c->CharacterID()), errbuf, &result))
|
||||
{
|
||||
if(mysql_num_rows(result) > 0)
|
||||
{
|
||||
mysql_free_result(result);
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscSuffix);
|
||||
std::string query = StringFormat("SELECT `id` FROM titles "
|
||||
"WHERE `suffix` = '%s' AND char_id = %i",
|
||||
escSuffix, client->CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() > 0) {
|
||||
safe_delete_array(escSuffix);
|
||||
return;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `suffix`) VALUES(%i, '%s')",
|
||||
c->CharacterID(), EscSuffix), errbuf))
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query, errbuf);
|
||||
else
|
||||
{
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
safe_delete_array(EscSuffix);
|
||||
query = StringFormat("INSERT INTO titles (`char_id`, `suffix`) VALUES(%i, '%s')",
|
||||
client->CharacterID(), escSuffix);
|
||||
safe_delete_array(escSuffix);
|
||||
results = database.QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void Client::SetAATitle(const char *Title)
|
||||
@ -368,67 +337,48 @@ void Client::SetTitleSuffix(const char *Suffix)
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::EnableTitle(int titleset) {
|
||||
void Client::EnableTitle(int titleSet) {
|
||||
|
||||
if (CheckTitle(titleset)) {
|
||||
if (CheckTitle(titleSet))
|
||||
return;
|
||||
}
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
std::string query = StringFormat("INSERT INTO player_titlesets "
|
||||
"(char_id, title_set) VALUES (%i, %i)",
|
||||
CharacterID(), titleSet);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if(!results.Success())
|
||||
LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleSet, CharacterID());
|
||||
|
||||
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO player_titlesets (char_id, title_set) VALUES (%i, %i)", CharacterID(), titleset), errbuf)) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleset, CharacterID());
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::CheckTitle(int titleset) {
|
||||
bool Client::CheckTitle(int titleSet) {
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT `id` FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", titleset, CharacterID()), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) >= 1) {
|
||||
mysql_free_result(result);
|
||||
return(true);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
std::string query = StringFormat("SELECT `id` FROM player_titlesets "
|
||||
"WHERE `title_set`=%i AND `char_id`=%i LIMIT 1",
|
||||
titleSet, CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
else {
|
||||
LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
if (results.RowCount() == 0)
|
||||
return false;
|
||||
|
||||
return(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::RemoveTitle(int titleset) {
|
||||
void Client::RemoveTitle(int titleSet) {
|
||||
|
||||
if (!CheckTitle(titleset)) {
|
||||
if (!CheckTitle(titleSet))
|
||||
return;
|
||||
}
|
||||
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
std::string query = StringFormat("DELETE FROM player_titlesets "
|
||||
"WHERE `title_set` = %i AND `char_id` = %i",
|
||||
titleSet, CharacterID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i", titleset, CharacterID()), errbuf)) {
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
else {
|
||||
LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -567,7 +567,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@ -696,7 +696,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f;
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@ -821,7 +821,7 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
|
||||
{
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@ -1007,26 +1007,19 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
}
|
||||
|
||||
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
|
||||
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(id), 0) FROM grid WHERE zoneid = %i",
|
||||
zoneid),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 GetHighestGrid query '%s': %s", query, errbuff);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
return(res);
|
||||
std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid 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]);
|
||||
}
|
||||
|
||||
uint8 ZoneDatabase::GetGridType2(uint32 grid, uint16 zoneid) {
|
||||
|
||||
3784
zone/zonedb.cpp
3784
zone/zonedb.cpp
File diff suppressed because it is too large
Load Diff
@ -111,6 +111,7 @@ struct NPCType
|
||||
uint8 spawn_limit; //only this many may be in zone at a time (0=no limit)
|
||||
uint8 mount_color; //only used by horse class
|
||||
float attack_speed; //%+- on attack delay of the mob.
|
||||
uint8 attack_delay; //delay between attacks in 10ths of a second
|
||||
int accuracy_rating; //10 = 1% accuracy
|
||||
int avoidance_rating; //10 = 1% avoidance
|
||||
bool findable; //can be found with find command
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user