mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-29 10:45:46 +00:00
Compare commits
147 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9733f04c9c | |||
| 7140a2054f | |||
| 1049e48aca | |||
| 33b79a3588 | |||
| f82699c39b | |||
| 15eaf4e6d1 | |||
| 7621882b4e | |||
| 9b70b73759 | |||
| 8e43134bda | |||
| d26782b093 | |||
| a2368b4ea7 | |||
| 3d6bb964df | |||
| 347ae1bc34 | |||
| 64f5bfd5ce | |||
| 86c1420f6e | |||
| 364ed921ce | |||
| 85e1518856 | |||
| 11ce399e0d | |||
| ec0989454d | |||
| e9f6031936 | |||
| 03485ef1e0 | |||
| e256175ce6 | |||
| 0f662bf70c | |||
| 4c959159c2 | |||
| d51241720a | |||
| 873d343529 | |||
| 95969ce67b | |||
| bd5cdf502e | |||
| c6a7d5a96c | |||
| 6bc4ecf390 | |||
| c94ceb5b1d | |||
| bb702335e8 | |||
| 863c0c5b58 | |||
| 76e280da4e | |||
| bcca35b7b7 | |||
| 65abaade88 | |||
| 8020e921aa | |||
| f2f5b4c1ad | |||
| 5c6f9fcdc4 | |||
| 9e243a2426 | |||
| 9a889802d3 | |||
| 1420987c4c | |||
| ed4d954ba8 | |||
| 881f937a35 | |||
| ed4e762f03 | |||
| fa1e33783a | |||
| 2fa31799f6 | |||
| 616e13acac | |||
| b50f660339 | |||
| b2aa3262a9 | |||
| b1587f0326 | |||
| 399bf96a0c | |||
| b7c409e11b | |||
| fd08e9f2ad | |||
| 9a4d01da8f | |||
| 81cf748b2b | |||
| 7d8d96c049 | |||
| f5e49441b6 | |||
| 610f3ed37f | |||
| f215874486 | |||
| 8e529105cf | |||
| 0996570b78 | |||
| 0a9732a267 | |||
| 1bc06c9c24 | |||
| e0acc937b3 | |||
| 3a10a0129a | |||
| 18dbcf16cc | |||
| bed8dc7d34 | |||
| a0fc9844fd | |||
| 9ddb56088e | |||
| 44dcf7af7d | |||
| 3262bee6c5 | |||
| 2dbd616725 | |||
| eb98563fa1 | |||
| 7f92e96ae7 | |||
| a48138dfd6 | |||
| 46980e5260 | |||
| 44f9e5495e | |||
| 97f59282cf | |||
| 2a4a5b1beb | |||
| 8369570b50 | |||
| 9ff0c414c1 | |||
| 850d1e7c28 | |||
| 2df823d2db | |||
| 2e84781594 | |||
| 833227f7f6 | |||
| e731cfd48d | |||
| cf7574d9b8 | |||
| 10d384f131 | |||
| 00b8c8ce47 | |||
| cab43f41be | |||
| fc0d589f12 | |||
| 743175d4ff | |||
| b497b07fed | |||
| aab5ed2267 | |||
| 46c9fe46e9 | |||
| e8c92c6fcc | |||
| 765eaf7f4f | |||
| 699c8cc1eb | |||
| 5839921e08 | |||
| a3bde6e1f1 | |||
| 0ece5bf178 | |||
| fbefad9eaf | |||
| 351a7a52fe | |||
| 8441ffda31 | |||
| e79747c919 | |||
| 8525d819c1 | |||
| fe600bb084 | |||
| babaff1985 | |||
| 909ca5440d | |||
| e25f64d03b | |||
| 2df66bd625 | |||
| 2028a5846c | |||
| 2fd2cd4cec | |||
| 17b175daa4 | |||
| 5cabe109da | |||
| c6e82448b6 | |||
| b846d89b5d | |||
| 322cea7342 | |||
| 205e1d404e | |||
| 9769a96ebd | |||
| 74d3192c2e | |||
| 41769a3fa8 | |||
| a7efa9d4e4 | |||
| 9ed69999a5 | |||
| e19a59b269 | |||
| d234016224 | |||
| c160d6d929 | |||
| 62ad60b4ad | |||
| d44d7c6bbd | |||
| efd97bad14 | |||
| bc884f5daf | |||
| 0f47b73a64 | |||
| e9c6e96452 | |||
| 09713311f6 | |||
| c666d9c553 | |||
| 3777e8d1ce | |||
| e60658c684 | |||
| 046da9efae | |||
| 09332f6c26 | |||
| ce507d891a | |||
| 96f122f901 | |||
| 3479525f39 | |||
| 4f9d4b0023 | |||
| bcdfd32bc0 | |||
| 693dde04e3 | |||
| f1bb019933 |
@@ -1,10 +1,43 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 09/20/2014 ==
|
||||
demonstar55: Fix crash in SendEnterWorld on illegally long names
|
||||
demonstar55: The client only lets you enter 15 characters for your name (UF at least)
|
||||
demonstar55: Add rule Spells:SHDProcIDOffByOne for pre-UF spell file, set to true, UF+ set to false
|
||||
KLS: #suspend and #ban now have required messages to record the reason for the ban/suspension.
|
||||
|
||||
== 09/19/2014 ==
|
||||
demonstar55: Added Client::Tell_StringID (used in tell queue messages)
|
||||
demonstar55: Tell queues (and offline) messages now show correctly
|
||||
demonstar55: Fix starting with capital check
|
||||
|
||||
== 09/18/2014==
|
||||
demonstar55: Implement tell queues
|
||||
Currently set to a limit of 20 by default (World:TellQueueSize) I was unable to hit the limit on live though (100+)
|
||||
The required SQL nukes the old tell queue table, which may or may not be in your DB
|
||||
Optional SQL adds the rule to the DB to allow easy of change
|
||||
Note: this does not play well with multiple sessions with the same name on (crash and relog and have multiple sessions) but normal tells don't play well either
|
||||
|
||||
== 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
|
||||
|
||||
+2
-2
@@ -659,7 +659,7 @@ the name "name" or zero if no character with that name was found
|
||||
Zero will also be returned if there is a database error.
|
||||
*/
|
||||
uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) {
|
||||
std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", charname);
|
||||
std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", EscapeString(charname).c_str());
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
@@ -1140,7 +1140,7 @@ bool Database::CheckNameFilter(const char* name, bool surname)
|
||||
else
|
||||
{
|
||||
// the minimum 4 is enforced by the client too
|
||||
if(!name || strlen(name) < 4 || strlen(name) > 64)
|
||||
if(!name || strlen(name) < 4 || strlen(name) > 15)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ MySQLRequestResult::MySQLRequestResult()
|
||||
MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer)
|
||||
: m_CurrentRow(result), m_OneBeyondRow()
|
||||
{
|
||||
m_Result = result;
|
||||
m_Result = result;
|
||||
m_RowsAffected = rowsAffected;
|
||||
m_RowCount = rowCount;
|
||||
m_ColumnCount = columnCount;
|
||||
@@ -22,12 +22,12 @@ MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, u
|
||||
m_ColumnLengths = nullptr;
|
||||
m_Fields = nullptr;
|
||||
|
||||
if (errorBuffer != nullptr)
|
||||
m_Success = true;
|
||||
if (errorBuffer != nullptr)
|
||||
m_Success = false;
|
||||
|
||||
m_Success = true;
|
||||
m_ErrorNumber = errorNumber;
|
||||
m_ErrorBuffer = errorBuffer;
|
||||
m_ErrorNumber = errorNumber;
|
||||
m_ErrorBuffer = errorBuffer;
|
||||
}
|
||||
|
||||
void MySQLRequestResult::FreeInternals()
|
||||
|
||||
@@ -171,6 +171,7 @@ RULE_INT ( World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = R
|
||||
RULE_BOOL (World, IsGMPetitionWindowEnabled, false)
|
||||
RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items.
|
||||
RULE_BOOL (World, IPLimitDisconnectAll, false)
|
||||
RULE_INT (World, TellQueueSize, 20)
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( Zone )
|
||||
@@ -320,6 +321,7 @@ RULE_INT ( Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing targe
|
||||
RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random)
|
||||
RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)
|
||||
RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others.
|
||||
RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false)
|
||||
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
#define ServerOP_QGlobalUpdate 0x0063
|
||||
#define ServerOP_QGlobalDelete 0x0064
|
||||
#define ServerOP_DepopPlayerCorpse 0x0065
|
||||
#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues
|
||||
|
||||
#define ServerOP_RaidAdd 0x0100 //in use
|
||||
#define ServerOP_RaidRemove 0x0101 //in use
|
||||
@@ -346,6 +347,7 @@ struct ServerChannelMessage_Struct {
|
||||
uint16 chan_num;
|
||||
uint32 guilddbid;
|
||||
uint16 language;
|
||||
uint8 queued; // 0 = not queued, 1 = queued, 2 = queue full, 3 = offline
|
||||
char message[0];
|
||||
};
|
||||
|
||||
@@ -1237,6 +1239,10 @@ struct ReloadWorld_Struct{
|
||||
uint32 Option;
|
||||
};
|
||||
|
||||
struct ServerRequestTellQueue_Struct {
|
||||
char name[64];
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
|
||||
+227
-165
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'World:TellQueueSize', '20', 'Maximum tell queue size.');
|
||||
@@ -0,0 +1 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SHDProcIDOffByOne', 'true', 'SHD procs are off by 1. Set true for pre-UF spell files, false for UF+.');
|
||||
@@ -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;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE `tellque`;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `account` ADD COLUMN `ban_reason` TEXT NULL DEFAULT NULL AFTER `expansion`, ADD COLUMN `suspend_reason` TEXT NULL DEFAULT NULL AFTER `ban_reason`;
|
||||
+24
-17
@@ -130,7 +130,7 @@ void Client::SendLogServer()
|
||||
|
||||
void Client::SendEnterWorld(std::string name)
|
||||
{
|
||||
char char_name[32]= { 0 };
|
||||
char char_name[64] = { 0 };
|
||||
if (pZoning && database.GetLiveChar(GetAccountID(), char_name)) {
|
||||
if(database.GetAccountIDByChar(char_name) != GetAccountID()) {
|
||||
eqs->Close();
|
||||
@@ -471,8 +471,8 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) {
|
||||
|
||||
bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app)
|
||||
{
|
||||
if (GetAccountID() == 0) {
|
||||
clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account");
|
||||
return false;
|
||||
@@ -482,7 +482,7 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) {
|
||||
uchar race = app->pBuffer[64];
|
||||
uchar clas = app->pBuffer[68];
|
||||
|
||||
clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas));
|
||||
clog(WORLD__CLIENT, "Name approval request. Name=%s, race=%s, class=%s", char_name, GetRaceName(race), GetEQClassName(clas));
|
||||
|
||||
EQApplicationPacket *outapp;
|
||||
outapp = new EQApplicationPacket;
|
||||
@@ -490,27 +490,21 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) {
|
||||
outapp->pBuffer = new uchar[1];
|
||||
outapp->size = 1;
|
||||
|
||||
bool valid;
|
||||
if(!database.CheckNameFilter(char_name)) {
|
||||
bool valid = false;
|
||||
if (!database.CheckNameFilter(char_name))
|
||||
valid = false;
|
||||
}
|
||||
else if(char_name[0] < 'A' && char_name[0] > 'Z') {
|
||||
else if (islower(char_name[0]))
|
||||
//name must begin with an upper-case letter.
|
||||
valid = false;
|
||||
}
|
||||
else if (database.ReserveName(GetAccountID(), char_name)) {
|
||||
else if (database.ReserveName(GetAccountID(), char_name))
|
||||
valid = true;
|
||||
}
|
||||
else {
|
||||
valid = false;
|
||||
}
|
||||
outapp->pBuffer[0] = valid? 1 : 0;
|
||||
|
||||
outapp->pBuffer[0] = valid ? 1 : 0;
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
if(!valid) {
|
||||
if (!valid)
|
||||
memset(char_name, 0, sizeof(char_name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1446,6 +1440,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
|
||||
SetRacialLanguages( &pp ); // bUsh
|
||||
SetRaceStartingSkills( &pp ); // bUsh
|
||||
SetClassStartingSkills( &pp ); // bUsh
|
||||
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
|
||||
@@ -2034,3 +2029,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;
|
||||
|
||||
@@ -93,6 +93,7 @@ ClientListEntry::~ClientListEntry() {
|
||||
Camp(); // updates zoneserver's numplayers
|
||||
client_list.RemoveCLEReferances(this);
|
||||
}
|
||||
tell_queue.clear();
|
||||
}
|
||||
|
||||
void ClientListEntry::SetChar(uint32 iCharID, const char* iCharName) {
|
||||
@@ -233,6 +234,7 @@ void ClientListEntry::ClearVars(bool iAll) {
|
||||
pLFG = 0;
|
||||
gm = 0;
|
||||
pClientVersion = 0;
|
||||
tell_queue.clear();
|
||||
}
|
||||
|
||||
void ClientListEntry::Camp(ZoneServer* iZS) {
|
||||
@@ -295,3 +297,21 @@ bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientListEntry::ProcessTellQueue()
|
||||
{
|
||||
if (!Server())
|
||||
return;
|
||||
|
||||
ServerPacket *pack;
|
||||
auto it = tell_queue.begin();
|
||||
while (it != tell_queue.end()) {
|
||||
pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1);
|
||||
memcpy(pack->pBuffer, *it, pack->size);
|
||||
pack->Deflate();
|
||||
Server()->SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
it = tell_queue.erase(it);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "../common/md5.h"
|
||||
//#include "../common/eq_packet_structs.h"
|
||||
#include "../common/servertalk.h"
|
||||
#include "../common/rulesys.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
#define CLE_Status_Never -1
|
||||
@@ -80,6 +82,11 @@ public:
|
||||
inline const char* GetLFGComments() const { return pLFGComments; }
|
||||
inline uint8 GetClientVersion() { return pClientVersion; }
|
||||
|
||||
inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); }
|
||||
inline bool TellQueueEmpty() const { return tell_queue.empty(); }
|
||||
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
|
||||
void ProcessTellQueue();
|
||||
|
||||
private:
|
||||
void ClearVars(bool iAll = false);
|
||||
|
||||
@@ -120,6 +127,9 @@ private:
|
||||
uint8 pLFGToLevel;
|
||||
bool pLFGMatchFilter;
|
||||
char pLFGComments[64];
|
||||
|
||||
// Tell Queue -- really a vector :D
|
||||
std::vector<ServerChannelMessage_Struct *> tell_queue;
|
||||
};
|
||||
|
||||
#endif /*CLIENTENTRY_H_*/
|
||||
|
||||
+48
-35
@@ -437,45 +437,48 @@ bool ZoneServer::Process() {
|
||||
Console* con = 0;
|
||||
con = console_list.FindByAccountName(&scm->deliverto[1]);
|
||||
if (((!con) || (!con->SendChannelMessage(scm))) && (!scm->noreply))
|
||||
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
|
||||
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to);
|
||||
break;
|
||||
}
|
||||
ClientListEntry* cle = client_list.FindCharacter(scm->deliverto);
|
||||
if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) {
|
||||
if (!scm->noreply)
|
||||
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
|
||||
}
|
||||
else if (cle->Online() == CLE_Status_Zoning) {
|
||||
if (!scm->noreply)
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm * timeinfo;
|
||||
time ( &rawtime );
|
||||
timeinfo = localtime ( &rawtime );
|
||||
char *telldate=asctime(timeinfo);
|
||||
|
||||
std::string query = StringFormat("SELECT name FROM character_ WHERE name = '%s'",scm->deliverto);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
break;
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
|
||||
break;
|
||||
}
|
||||
|
||||
query = StringFormat("INSERT INTO tellque "
|
||||
"(Date, Receiver, Sender, Message) "
|
||||
"VALUES('%s', '%s', '%s', '%s')",
|
||||
telldate, scm->deliverto, scm->from, scm->message);
|
||||
results = database.QueryDatabase(query);
|
||||
if (results.Success())
|
||||
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to);
|
||||
else
|
||||
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
|
||||
|
||||
if (cle == 0 || cle->Online() < CLE_Status_Zoning ||
|
||||
(cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) {
|
||||
if (!scm->noreply) {
|
||||
ClientListEntry* sender = client_list.FindCharacter(scm->from);
|
||||
if (!sender)
|
||||
break;
|
||||
scm->noreply = true;
|
||||
scm->queued = 3; // offline
|
||||
strcpy(scm->deliverto, scm->from);
|
||||
// ideally this would be trimming off the message too, oh well
|
||||
sender->Server()->SendPacket(pack);
|
||||
}
|
||||
} else if (cle->Online() == CLE_Status_Zoning) {
|
||||
if (!scm->noreply) {
|
||||
ClientListEntry* sender = client_list.FindCharacter(scm->from);
|
||||
if (cle->TellQueueFull()) {
|
||||
if (!sender)
|
||||
break;
|
||||
scm->noreply = true;
|
||||
scm->queued = 2; // queue full
|
||||
strcpy(scm->deliverto, scm->from);
|
||||
sender->Server()->SendPacket(pack);
|
||||
} else {
|
||||
size_t struct_size = sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1;
|
||||
ServerChannelMessage_Struct *temp = (ServerChannelMessage_Struct *) new uchar[struct_size];
|
||||
memset(temp, 0, struct_size); // just in case, was seeing some corrupt messages, but it shouldn't happen
|
||||
memcpy(temp, scm, struct_size);
|
||||
temp->noreply = true;
|
||||
cle->PushToTellQueue(temp); // deallocation is handled in processing or deconstructor
|
||||
|
||||
if (!sender)
|
||||
break;
|
||||
scm->noreply = true;
|
||||
scm->queued = 1; // queued
|
||||
strcpy(scm->deliverto, scm->from);
|
||||
sender->Server()->SendPacket(pack);
|
||||
}
|
||||
}
|
||||
// zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
|
||||
}
|
||||
else if (cle->Server() == 0) {
|
||||
if (!scm->noreply)
|
||||
@@ -1319,6 +1322,16 @@ bool ZoneServer::Process() {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_RequestTellQueue:
|
||||
{
|
||||
ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer;
|
||||
ClientListEntry *cle = client_list.FindCharacter(rtq->name);
|
||||
if (!cle || cle->TellQueueEmpty())
|
||||
break;
|
||||
|
||||
cle->ProcessTellQueue();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
zlog(WORLD__ZONE_ERR,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size);
|
||||
|
||||
+144
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -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;
|
||||
|
||||
|
||||
+50
-42
@@ -3134,6 +3134,14 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::Tell_StringID(uint32 string_id, const char *who, const char *message)
|
||||
{
|
||||
char string_id_str[10];
|
||||
snprintf(string_id_str, 10, "%d", string_id);
|
||||
|
||||
Message_StringID(MT_TellEcho, TELL_QUEUED_MESSAGE, who, string_id_str, message);
|
||||
}
|
||||
|
||||
void Client::SetTint(int16 in_slot, uint32 color) {
|
||||
Color_Struct new_color;
|
||||
new_color.color = color;
|
||||
@@ -4002,54 +4010,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)
|
||||
@@ -8346,3 +8338,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;
|
||||
}
|
||||
|
||||
+6
-3
@@ -222,6 +222,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);
|
||||
@@ -258,6 +260,7 @@ public:
|
||||
const char *message5 = nullptr, const char *message6 = nullptr,
|
||||
const char *message7 = nullptr, const char *message8 = nullptr,
|
||||
const char *message9 = nullptr);
|
||||
void Tell_StringID(uint32 string_id, const char *who, const char *message);
|
||||
void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice);
|
||||
void SendTraderItem(uint32 item_id,uint16 quantity);
|
||||
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
|
||||
@@ -1194,9 +1197,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; }
|
||||
|
||||
@@ -9574,11 +9574,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:
|
||||
@@ -9828,6 +9834,8 @@ void Client::CompleteConnect() {
|
||||
}
|
||||
|
||||
entity_list.RefreshClientXTargets(this);
|
||||
|
||||
worldserver.RequestTellQueue(GetName());
|
||||
}
|
||||
|
||||
void Client::Handle_OP_KeyRing(const EQApplicationPacket *app)
|
||||
|
||||
+81
-96
@@ -333,8 +333,6 @@ int command_init(void) {
|
||||
command_add("guilds",nullptr,0,command_guild) ||
|
||||
command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) ||
|
||||
command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) ||
|
||||
command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) ||
|
||||
command_add("viewmessages",nullptr,0,command_viewmessage) ||
|
||||
command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) ||
|
||||
command_add("randomfeatures","- Temporarily randomizes the Facial Features of your target",80,command_randomfeatures) ||
|
||||
command_add("rf",nullptr,80,command_randomfeatures) ||
|
||||
@@ -5294,53 +5292,6 @@ void command_manaburn(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
void command_viewmessage(Client *c, const Seperator *sep)
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
if(sep->arg[1][0]==0)
|
||||
{
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where receiver='%s'",c->GetName()), errbuf, &result))
|
||||
{
|
||||
if (mysql_num_rows(result)>0)
|
||||
{
|
||||
c->Message(0,"You have messages waiting for you to view.");
|
||||
c->Message(0,"Type #Viewmessage <Message ID> to view the message.");
|
||||
c->Message(0," ID , Message Sent Date, Message Sender");
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
c->Message(0,"ID: %s Sent Date: %s Sender: %s ",row[0],row[1],row[3]);
|
||||
}
|
||||
else
|
||||
c->Message(0,"You have no new messages");
|
||||
mysql_free_result(result);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where id=%s",sep->argplus[1]), errbuf, &result))
|
||||
{
|
||||
if (mysql_num_rows(result)==1)
|
||||
{
|
||||
row = mysql_fetch_row(result);
|
||||
mysql_free_result(result);
|
||||
if (strcasecmp((const char *) c->GetName(), (const char *) row[2]) == 0)
|
||||
{
|
||||
c->Message(15,"ID: %s,Sent Date: %s,Sender: %s,Message: %s",row[0],row[1],row[3],row[4]);
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "Delete from tellque where id=%s",row[0]), errbuf);
|
||||
}
|
||||
else
|
||||
c->Message(13,"Invalid Message Number, check the number and try again.");
|
||||
}
|
||||
else
|
||||
c->Message(13,"Invalid Message Number, check the number and try again.");
|
||||
}
|
||||
safe_delete_array(query);
|
||||
}
|
||||
}
|
||||
|
||||
void command_doanim(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (!sep->IsNumber(1))
|
||||
@@ -6307,36 +6258,49 @@ void command_stun(Client *c, const Seperator *sep)
|
||||
c->Message(0, "Usage: #stun [duration]");
|
||||
}
|
||||
|
||||
|
||||
void command_ban(Client *c, const Seperator *sep)
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if(sep->arg[1][0] == 0)
|
||||
if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0)
|
||||
{
|
||||
c->Message(0, "Usage: #ban [charname]");
|
||||
c->Message(0, "Usage: #ban <charname> <message>");
|
||||
}
|
||||
else
|
||||
{
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "SELECT account_id from character_ where name = '%s'", sep->arg[1]), errbuf, &result);
|
||||
if(query)
|
||||
{
|
||||
safe_delete_array(query);
|
||||
auto account_id = database.GetAccountIDByChar(sep->arg[1]);
|
||||
|
||||
std::string message;
|
||||
int i = 2;
|
||||
while(1) {
|
||||
if(sep->arg[i][0] == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(message.length() > 0) {
|
||||
message.push_back(' ');
|
||||
}
|
||||
|
||||
message += sep->arg[i];
|
||||
++i;
|
||||
}
|
||||
|
||||
if(mysql_num_rows(result))
|
||||
{
|
||||
row = mysql_fetch_row(result);
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2 where id = %i", atoi(row[0])), errbuf, 0);
|
||||
c->Message(13,"Account number %i with the character %s has been banned.", atoi(row[0]), sep->arg[1]);
|
||||
if(message.length() == 0) {
|
||||
c->Message(0, "Usage: #ban <charname> <message>");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_FlagUpdate, 6);
|
||||
*((uint32*) pack->pBuffer) = atoi(row[0]);
|
||||
*((int16*) &pack->pBuffer[4]) = -2;
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
if(account_id > 0)
|
||||
{
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2, ban_reason = '%s' where id = %i", EscapeString(message).c_str(), account_id), errbuf, 0);
|
||||
c->Message(13, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str());
|
||||
|
||||
ServerPacket pack(ServerOP_FlagUpdate, 6);
|
||||
*((uint32*)&pack.pBuffer[0]) = account_id;
|
||||
*((int16*)&pack.pBuffer[4]) = -2;
|
||||
worldserver.SendPacket(&pack);
|
||||
|
||||
Client *client = nullptr;
|
||||
client = entity_list.GetClientByName(sep->arg[1]);
|
||||
@@ -6346,25 +6310,20 @@ void command_ban(Client *c, const Seperator *sep)
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
|
||||
ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer;
|
||||
ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
|
||||
ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack.pBuffer;
|
||||
strcpy(skp->adminname, c->GetName());
|
||||
strcpy(skp->name, sep->arg[1]);
|
||||
skp->adminrank = c->Admin();
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
worldserver.SendPacket(&pack);
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Message(13,"Character does not exist.");
|
||||
}
|
||||
if(query)
|
||||
{
|
||||
safe_delete_array(query);
|
||||
c->Message(13, "Character does not exist.");
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6374,7 +6333,7 @@ void command_suspend(Client *c, const Seperator *sep)
|
||||
char *query = nullptr;
|
||||
|
||||
if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0))
|
||||
c->Message(0, "Usage: #suspend <charname> <days> (Specify 0 days to lift the suspension immediately)");
|
||||
c->Message(0, "Usage: #suspend <charname> <days>(Specify 0 days to lift the suspension immediately) <message>");
|
||||
else
|
||||
{
|
||||
int Duration = atoi(sep->arg[2]);
|
||||
@@ -6382,22 +6341,40 @@ void command_suspend(Client *c, const Seperator *sep)
|
||||
if(Duration < 0)
|
||||
Duration = 0;
|
||||
|
||||
char *EscName = new char[strlen(sep->arg[1]) * 2 + 1];
|
||||
std::string message;
|
||||
if(Duration > 0) {
|
||||
int i = 3;
|
||||
while(1) {
|
||||
if(sep->arg[i][0] == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
database.DoEscapeString(EscName, sep->arg[1], strlen(sep->arg[1]));
|
||||
if(message.length() > 0) {
|
||||
message.push_back(' ');
|
||||
}
|
||||
|
||||
message += sep->arg[i];
|
||||
++i;
|
||||
}
|
||||
|
||||
if(message.length() == 0) {
|
||||
c->Message(0, "Usage: #suspend <charname> <days>(Specify 0 days to lift the suspension immediately) <message>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int AccountID;
|
||||
|
||||
if((AccountID = database.GetAccountIDByChar(EscName)) > 0)
|
||||
if((AccountID = database.GetAccountIDByChar(sep->arg[1])) > 0)
|
||||
{
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY)"
|
||||
" WHERE `id` = %i", Duration, AccountID), errbuf, 0);
|
||||
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), "
|
||||
"suspend_reason = '%s' WHERE `id` = %i", Duration, EscapeString(message).c_str(), AccountID), errbuf, 0);
|
||||
|
||||
if(Duration)
|
||||
c->Message(13,"Account number %i with the character %s has been temporarily suspended for %i day(s).", AccountID, sep->arg[1],
|
||||
Duration);
|
||||
c->Message(13, "Account number %i with the character %s has been temporarily suspended for %i day(s) with the message: \"%s\"", AccountID, sep->arg[1],
|
||||
Duration, message.c_str());
|
||||
else
|
||||
c->Message(13,"Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]);
|
||||
c->Message(13, "Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]);
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
@@ -6407,22 +6384,20 @@ void command_suspend(Client *c, const Seperator *sep)
|
||||
BannedClient->WorldKick();
|
||||
else
|
||||
{
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
|
||||
ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*) pack->pBuffer;
|
||||
ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
|
||||
ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*)pack.pBuffer;
|
||||
|
||||
strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname));
|
||||
strn0cpy(sks->name, sep->arg[1], sizeof(sks->name));
|
||||
sks->adminrank = c->Admin();
|
||||
|
||||
worldserver.SendPacket(pack);
|
||||
|
||||
safe_delete(pack);
|
||||
worldserver.SendPacket(&pack);
|
||||
}
|
||||
|
||||
} else
|
||||
c->Message(13,"Character does not exist.");
|
||||
|
||||
safe_delete_array(EscName);
|
||||
}
|
||||
else {
|
||||
c->Message(13, "Character does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6658,6 +6633,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");
|
||||
@@ -7159,6 +7135,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];
|
||||
|
||||
@@ -217,7 +217,6 @@ void command_guild(Client *c, const Seperator *sep);
|
||||
bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value);
|
||||
void command_zonestatus(Client *c, const Seperator *sep);
|
||||
void command_manaburn(Client *c, const Seperator *sep);
|
||||
void command_viewmessage(Client *c, const Seperator *sep);
|
||||
void command_doanim(Client *c, const Seperator *sep);
|
||||
void command_randomfeatures(Client *c, const Seperator *sep);
|
||||
void command_face(Client *c, const Seperator *sep);
|
||||
|
||||
+22
-43
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+7
-15
@@ -903,24 +903,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;
|
||||
|
||||
+13
-154
@@ -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;
|
||||
@@ -1939,157 +1940,6 @@ void Mob::Kill() {
|
||||
Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
|
||||
}
|
||||
|
||||
void Mob::SetAttackTimer() {
|
||||
float PermaHaste;
|
||||
if (GetHaste())
|
||||
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);
|
||||
@@ -4525,6 +4375,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();
|
||||
@@ -5065,7 +4924,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, '^');
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
|
||||
+8
-8
@@ -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:
|
||||
|
||||
+6
-7
@@ -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));
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
+133
-26
@@ -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()) {
|
||||
@@ -5492,28 +5598,29 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id)
|
||||
}
|
||||
|
||||
//for some stupid reason SK procs return theirs one base off...
|
||||
uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) {
|
||||
uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index)
|
||||
{
|
||||
if (!RuleB(Spells, SHDProcIDOffByOne)) // UF+ spell files
|
||||
return spells[spell_id].base[effect_index];
|
||||
|
||||
// We should actually just be checking if the mob is SHD, but to not force
|
||||
// custom servers to create new spells, we will still do this
|
||||
bool sk = false;
|
||||
bool other = false;
|
||||
for(int x = 0; x < 16; x++)
|
||||
{
|
||||
if(x == 4){
|
||||
if(spells[spell_id].classes[4] < 255)
|
||||
for (int x = 0; x < 16; x++) {
|
||||
if (x == 4) {
|
||||
if (spells[spell_id].classes[4] < 255)
|
||||
sk = true;
|
||||
}
|
||||
else{
|
||||
if(spells[spell_id].classes[x] < 255)
|
||||
} else {
|
||||
if (spells[spell_id].classes[x] < 255)
|
||||
other = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(sk && !other)
|
||||
{
|
||||
return(spells[spell_id].base[effect_index] + 1);
|
||||
}
|
||||
else{
|
||||
return(spells[spell_id].base[effect_index]);
|
||||
}
|
||||
if (sk && !other)
|
||||
return spells[spell_id].base[effect_index] + 1;
|
||||
else
|
||||
return spells[spell_id].base[effect_index];
|
||||
}
|
||||
|
||||
bool Mob::TryDivineSave()
|
||||
|
||||
@@ -249,6 +249,8 @@
|
||||
#define PLAYER_CHARMED 1461 //You lose control of yourself!
|
||||
#define TRADER_BUSY 1468 //That Trader is currently with a customer. Please wait until their transaction is finished.
|
||||
#define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction.
|
||||
#define QUEUED_TELL 2458 //[queued]
|
||||
#define QUEUE_TELL_FULL 2459 //[zoing and queue is full]
|
||||
#define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...'
|
||||
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
|
||||
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
|
||||
@@ -269,6 +271,8 @@
|
||||
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
|
||||
#define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away.
|
||||
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
|
||||
#define TELL_QUEUED_MESSAGE 5045 //You told %1 '%T2. %3'
|
||||
#define TOLD_NOT_ONLINE 5046 //%1 is not online at this time.
|
||||
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
|
||||
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
|
||||
#define GAIN_RAIDEXP 5085 //You gained raid experience!
|
||||
|
||||
+83
-133
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+15
-28
@@ -567,9 +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 sz = GetSize();
|
||||
newz += sz >= 0.0f ? sz / 2.0f : 0.0f;
|
||||
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);
|
||||
|
||||
@@ -698,9 +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 sz = GetSize();
|
||||
newz += sz >= 0.0f ? sz / 2.0f : 0.0f;
|
||||
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);
|
||||
|
||||
@@ -825,9 +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 sz = GetSize();
|
||||
newz += sz >= 0.0f ? sz / 2.0f : 0.0f;
|
||||
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);
|
||||
|
||||
@@ -1013,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) {
|
||||
|
||||
+31
-8
@@ -168,18 +168,24 @@ void WorldServer::Process() {
|
||||
break;
|
||||
}
|
||||
case ServerOP_ChannelMessage: {
|
||||
if (!ZoneLoaded) break;
|
||||
if (!ZoneLoaded)
|
||||
break;
|
||||
ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer;
|
||||
if (scm->deliverto[0] == 0) {
|
||||
entity_list.ChannelMessageFromWorld(scm->from, scm->to, scm->chan_num, scm->guilddbid, scm->language, scm->message);
|
||||
}
|
||||
else {
|
||||
Client* client;
|
||||
client = entity_list.GetClientByName(scm->deliverto);
|
||||
if (client != 0) {
|
||||
} else {
|
||||
Client* client = entity_list.GetClientByName(scm->deliverto);
|
||||
if (client) {
|
||||
if (client->Connected()) {
|
||||
client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message);
|
||||
if (!scm->noreply && scm->chan_num!=2) { //dont echo on group chat
|
||||
if (scm->queued == 1) // tell was queued
|
||||
client->Tell_StringID(QUEUED_TELL, scm->to, scm->message);
|
||||
else if (scm->queued == 2) // tell queue was full
|
||||
client->Tell_StringID(QUEUE_TELL_FULL, scm->to, scm->message);
|
||||
else if (scm->queued == 3) // person was offline
|
||||
client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE, scm->to);
|
||||
else // normal stuff
|
||||
client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message);
|
||||
if (!scm->noreply && scm->chan_num != 2) { //dont echo on group chat
|
||||
// if it's a tell, echo back so it shows up
|
||||
scm->noreply = true;
|
||||
scm->chan_num = 14;
|
||||
@@ -1856,6 +1862,7 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu
|
||||
scm->chan_num = chan_num;
|
||||
scm->guilddbid = guilddbid;
|
||||
scm->language = language;
|
||||
scm->queued = 0;
|
||||
strcpy(scm->message, buffer);
|
||||
|
||||
pack->Deflate();
|
||||
@@ -2180,3 +2187,19 @@ void WorldServer::HandleLFPMatches(ServerPacket *pack) {
|
||||
safe_delete(outapp);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldServer::RequestTellQueue(const char *who)
|
||||
{
|
||||
if (!who)
|
||||
return;
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_RequestTellQueue, sizeof(ServerRequestTellQueue_Struct));
|
||||
ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer;
|
||||
|
||||
strn0cpy(rtq->name, who, sizeof(rtq->name));
|
||||
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
void HandleLFGMatches(ServerPacket *pack);
|
||||
void HandleLFPMatches(ServerPacket *pack);
|
||||
|
||||
void RequestTellQueue(const char *who);
|
||||
|
||||
private:
|
||||
virtual void OnConnected();
|
||||
|
||||
|
||||
+1573
-2271
File diff suppressed because it is too large
Load Diff
+1
-9
@@ -246,11 +246,7 @@ public:
|
||||
/*
|
||||
* General Character Related Stuff
|
||||
*/
|
||||
void StoreCharacterLookup(uint32 char_id);
|
||||
bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
|
||||
uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
|
||||
bool GetAccountInfoForLogin(uint32 account_id, int16* admin = 0, char* account_name = 0,
|
||||
uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = 0);
|
||||
void StoreCharacterLookup(uint32 char_id);
|
||||
bool GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin = 0, char* account_name = 0,
|
||||
uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = nullptr,
|
||||
uint32* account_creation = 0);
|
||||
@@ -258,10 +254,6 @@ public:
|
||||
PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0,
|
||||
uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_= 0, uint8 *level = 0, bool *LFP = 0,
|
||||
bool *LFG = 0, uint8 *NumXTargets = 0, uint8* firstlogon = 0);
|
||||
bool GetCharacterInfoForLogin(const char* name, uint32* character_id = 0, char* current_zone = 0,
|
||||
PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0,
|
||||
uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_ = 0, uint8 *level = 0, bool *LFP = 0,
|
||||
bool *LFG = 0, uint8 *NumXTargets = 0, uint8* firstlogon = 0);
|
||||
void SaveBuffs(Client *c);
|
||||
void LoadBuffs(Client *c);
|
||||
void LoadPetInfo(Client *c);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user