Merge from master

This commit is contained in:
akkadius
2014-09-17 20:16:37 -05:00
31 changed files with 2373 additions and 2861 deletions
+144 -2
View File
@@ -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
View File
@@ -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;
+42 -42
View File
@@ -3949,54 +3949,38 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const
void Client::KeyRingLoad()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
query = new char[256];
sprintf(query, "SELECT item_id FROM keyring WHERE char_id='%i' ORDER BY item_id",character_id);
if (database.RunQuery(query, strlen(query), errbuf, &result))
{
safe_delete_array(query);
while(0 != (row = mysql_fetch_row(result))){
keyring.push_back(atoi(row[0]));
}
mysql_free_result(result);
}else {
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT item_id FROM keyring "
"WHERE char_id = '%i' ORDER BY item_id", character_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << results.ErrorMessage() << std::endl;
return;
}
}
for (auto row = results.begin(); row != results.end(); ++row)
keyring.push_back(atoi(row[0]));
}
void Client::KeyRingAdd(uint32 item_id)
{
if(0==item_id)return;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
query = new char[256];
bool bFound = KeyRingCheck(item_id);
if(!bFound){
sprintf(query, "INSERT INTO keyring(char_id,item_id) VALUES(%i,%i)",character_id,item_id);
if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) {
Message(4,"Added to keyring.");
if(0==item_id)
return;
/* QS: PlayerLogKeyringAddition */
if (RuleB(QueryServ, PlayerLogKeyringAddition)){
std::string event_desc = StringFormat("itemid:%i in zoneid:%i instid:%i", item_id, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Keyring_Addition, this->CharacterID(), event_desc);
}
safe_delete_array(query);
}
else {
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return;
}
keyring.push_back(item_id);
}
bool found = KeyRingCheck(item_id);
if (found)
return;
std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << results.ErrorMessage() << std::endl;
return;
}
Message(4,"Added to keyring.");
keyring.push_back(item_id);
}
bool Client::KeyRingCheck(uint32 item_id)
@@ -8282,3 +8266,19 @@ void Client::ShowNumHits()
return;
}
float Client::GetQuiverHaste()
{
float quiver_haste = 0;
for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) {
const ItemInst *pi = GetInv().GetItem(r);
if (!pi)
continue;
if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) {
float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv));
quiver_haste = std::max(temp_wr, quiver_haste);
}
}
if (quiver_haste > 0)
quiver_haste = 1.0f / (1.0f + static_cast<float>(quiver_haste) / 100.0f);
return quiver_haste;
}
+5 -3
View File
@@ -218,6 +218,8 @@ public:
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
virtual inline bool IsBerserk() { return berserk; }
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
virtual void SetAttackTimer();
float GetQuiverHaste();
void AI_Init();
void AI_Start(uint32 iMoveDelay = 0);
@@ -1193,9 +1195,9 @@ public:
int32 mod_client_xp(int32 in_exp, NPC *npc);
uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level);
int mod_client_haste_cap(int cap);
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
int mod_food_value(const Item_Struct *item, int change);
int mod_drink_value(const Item_Struct *item, int change);
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
int mod_food_value(const Item_Struct *item, int change);
int mod_drink_value(const Item_Struct *item, int change);
void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; }
bool GetEngagedRaidTarget() const { return EngagedRaidTarget; }
+8 -2
View File
@@ -6781,7 +6781,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
tobe_auged = user_inv.GetItem(slot_id);
auged_with = user_inv.GetItem(MainCursor);
if(tobe_auged && auged_with)
if (tobe_auged && auged_with)
{
if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) &&
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
@@ -9432,11 +9432,17 @@ void Client::CompleteConnect() {
//reapply some buffs
uint32 buff_count = GetMaxTotalSlots();
for (uint32 j1 = 0; j1 < buff_count; j1++) {
if (buffs[j1].spellid >(uint32)SPDAT_RECORDS)
if (!IsValidSpell(buffs[j1].spellid))
continue;
const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid];
int NimbusEffect = GetNimbusEffect(buffs[j1].spellid);
if(NimbusEffect) {
if(!IsNimbusEffectActive(NimbusEffect))
SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true);
}
for (int x1 = 0; x1 < EFFECT_COUNT; x1++) {
switch (spell.effectid[x1]) {
case SE_IllusionCopy:
+10
View File
@@ -6555,6 +6555,7 @@ void command_npcedit(Client *c, const Seperator *sep)
c->Message(0, "#npcedit qglobal - Sets an NPC's quest global flag");
c->Message(0, "#npcedit limit - Sets an NPC's spawn limit counter");
c->Message(0, "#npcedit Attackspeed - Sets an NPC's attack speed modifier");
c->Message(0, "#npcedit Attackdelay - Sets an NPC's attack delay");
c->Message(0, "#npcedit findable - Sets an NPC's findable flag");
c->Message(0, "#npcedit wep1 - Sets an NPC's primary weapon model");
c->Message(0, "#npcedit wep2 - Sets an NPC's secondary weapon model");
@@ -7056,6 +7057,15 @@ void command_npcedit(Client *c, const Seperator *sep)
c->LogSQL(query);
safe_delete_array(query);
}
else if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 )
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
c->Message(15,"NPCID %u now has attack_delay set to %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2]));
database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_delay=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf);
c->LogSQL(query);
safe_delete_array(query);
}
else if ( strcasecmp( sep->arg[1], "findable" ) == 0 )
{
char errbuf[MYSQL_ERRMSG_SIZE];
+10 -6
View File
@@ -356,9 +356,11 @@ void Client::GoFish()
inst = m_inv.GetItem(MainCursor);
}
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
if(inst) {
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
}
}
}
else
@@ -470,9 +472,11 @@ void Client::ForageItem(bool guarantee) {
inst = m_inv.GetItem(MainCursor);
}
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
if(inst) {
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
}
}
int ChanceSecondForage = aabonuses.ForageAdditionalItems + itembonuses.ForageAdditionalItems + spellbonuses.ForageAdditionalItems;
+12
View File
@@ -561,6 +561,18 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender)
}
}
/* This may seem pointless but the case above does not cover the following situation:
* Group has Leader a, member b, member c
* b and c are out of zone
* a disconnects/quits
* b or c zone back in and disconnects/quits
* a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b)
* Ultimately we should think up a better solution to this.
*/
if(oldmember == GetLeader()) {
SetLeader(nullptr);
}
ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct));
ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer;
gl->gid = GetID();
+22 -43
View File
@@ -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
View File
@@ -850,24 +850,16 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32
return false;
}
const char *Query="INSERT INTO `guild_bank` (`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')";
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor), errbuf))
{
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("INSERT INTO `guild_bank` "
"(`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')",
GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor);
auto results = database.QueryDatabase(query);
if(!results.Success()) {
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
const Item_Struct *Item = database.GetItem(ItemID);
GuildBankItemUpdate_Struct gbius;
+13 -156
View File
@@ -174,8 +174,9 @@ Mob::Mob(const char* in_name,
drakkin_heritage = in_drakkin_heritage;
drakkin_tattoo = in_drakkin_tattoo;
drakkin_details = in_drakkin_details;
attack_speed= 0;
slow_mitigation= 0;
attack_speed = 0;
attack_delay = 0;
slow_mitigation = 0;
findable = false;
trackable = true;
has_shieldequiped = false;
@@ -1940,159 +1941,6 @@ void Mob::Kill() {
Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
}
void Mob::SetAttackTimer() {
float PermaHaste;
if(GetHaste() > 0)
PermaHaste = 1 / (1 + (float)GetHaste()/100);
else if(GetHaste() < 0)
PermaHaste = 1 * (1 - (float)GetHaste()/100);
else
PermaHaste = 1.0f;
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer* TimerToUse = nullptr;
const Item_Struct* PrimaryWeapon = nullptr;
for (int i=MainRange; i<=MainSecondary; i++) {
//pick a timer
if (i == MainPrimary)
TimerToUse = &attack_timer;
else if (i == MainRange)
TimerToUse = &ranged_timer;
else if(i == MainSecondary)
TimerToUse = &attack_dw_timer;
else //invalid slot (hands will always hit this)
continue;
const Item_Struct* ItemToUse = nullptr;
//find our item
if (IsClient()) {
ItemInst* ci = CastToClient()->GetInv().GetItem(i);
if (ci)
ItemToUse = ci->GetItem();
} else if(IsNPC())
{
//The code before here was fundementally flawed because equipment[]
//isn't the same as PC inventory and also:
//NPCs don't use weapon speed to dictate how fast they hit anyway.
ItemToUse = nullptr;
}
//special offhand stuff
if(i == MainSecondary) {
//if we have a 2H weapon in our main hand, no dual
if(PrimaryWeapon != nullptr) {
if( PrimaryWeapon->ItemClass == ItemClassCommon
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
attack_dw_timer.Disable();
continue;
}
}
//clients must have the skill to use it...
if(IsClient()) {
//if we cant dual wield, skip it
if (!CanThisClassDualWield()) {
attack_dw_timer.Disable();
continue;
}
} else {
//NPCs get it for free at 13
if(GetLevel() < 13) {
attack_dw_timer.Disable();
continue;
}
}
}
//see if we have a valid weapon
if(ItemToUse != nullptr) {
//check type and damage/delay
if(ItemToUse->ItemClass != ItemClassCommon
|| ItemToUse->Damage == 0
|| ItemToUse->Delay == 0) {
//no weapon
ItemToUse = nullptr;
}
// Check to see if skill is valid
else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) {
//no weapon
ItemToUse = nullptr;
}
}
int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands;
if (DelayMod < -99)
DelayMod = -99;
//if we have no weapon..
if (ItemToUse == nullptr) {
//above checks ensure ranged weapons do not fall into here
// Work out if we're a monk
if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) {
//we are a monk, use special delay
int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
// 1200 seemed too much, with delay 10 weapons available
if(speed < RuleI(Combat, MinHastedDelay)) //lower bound
speed = RuleI(Combat, MinHastedDelay);
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic
} else {
//not a monk... using fist, regular delay
int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound
speed = RuleI(Combat, MinHastedDelay);
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36
}
} else {
//we have a weapon, use its delay
// Convert weapon delay to timer resolution (milliseconds)
//delay * 100
int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
if(speed < RuleI(Combat, MinHastedDelay))
speed = RuleI(Combat, MinHastedDelay);
if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing))
{
if(IsClient())
{
float max_quiver = 0;
for(int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++)
{
const ItemInst *pi = CastToClient()->GetInv().GetItem(r);
if(!pi)
continue;
if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver)
{
float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) );
if(temp_wr > max_quiver)
{
max_quiver = temp_wr;
}
}
}
if(max_quiver > 0)
{
float quiver_haste = 1 / (1 + max_quiver / 100);
speed *= quiver_haste;
}
}
}
TimerToUse->SetAtTrigger(speed, true);
}
if(i == MainPrimary)
PrimaryWeapon = ItemToUse;
}
}
bool Mob::CanThisClassDualWield(void) const {
if(!IsClient()) {
return(GetSkill(SkillDualWield) > 0);
@@ -4528,6 +4376,15 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID)
void Mob::RemoveNimbusEffect(int effectid)
{
if (effectid == nimbus_effect1)
nimbus_effect1 = 0;
else if (effectid == nimbus_effect2)
nimbus_effect2 = 0;
else if (effectid == nimbus_effect3)
nimbus_effect3 = 0;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct));
RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer;
rne->spawnid = GetID();
@@ -5068,7 +4925,7 @@ void Mob::ClearSpecialAbilities() {
}
}
void Mob::ProcessSpecialAbilities(const std::string str) {
void Mob::ProcessSpecialAbilities(const std::string &str) {
ClearSpecialAbilities();
std::vector<std::string> sp = SplitString(str, '^');
+3 -1
View File
@@ -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;
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+5 -7
View File
@@ -1413,10 +1413,8 @@ void NPC::DoClassAttacks(Mob *target) {
return;
float HasteModifier = 0;
if(GetHaste() > 0)
if (GetHaste())
HasteModifier = 10000 / (100 + GetHaste());
else if(GetHaste() < 0)
HasteModifier = (100 - GetHaste());
else
HasteModifier = 100;
@@ -1464,7 +1462,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = KickReuseTime * 1000;
reuse = (KickReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
did_attack = true;
}
@@ -1484,7 +1482,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = BashReuseTime * 1000;
reuse = (BashReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
did_attack = true;
}
@@ -1537,7 +1535,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = KickReuseTime * 1000;
reuse = (KickReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
did_attack = true;
}
@@ -1562,7 +1560,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = BashReuseTime * 1000;
reuse = (BashReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
did_attack = true;
}
+117 -11
View File
@@ -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()) {
+7 -9
View File
@@ -4501,13 +4501,12 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
}
else
{
resist_chance -= roll;
if(resist_chance < 1)
{
resist_chance = 1;
}
int partial_modifier = ((150 * (roll - resist_chance)) / resist_chance);
int partial_modifier = ((150 * (resist_chance - roll)) / resist_chance);
if(IsNPC())
{
@@ -4535,17 +4534,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
}
}
if(partial_modifier < 0)
if(partial_modifier <= 0)
{
return 100;
}
else if(partial_modifier >= 100)
{
return 0;
}
if(partial_modifier > 100)
{
return 100;
}
return partial_modifier;
return (100.0f - partial_modifier);
}
}
}
+83 -133
View File
@@ -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 -22
View File
@@ -567,7 +567,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
{
Map::Vertex dest(x_pos, y_pos, z_pos);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
@@ -696,7 +696,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
{
Map::Vertex dest(x_pos, y_pos, z_pos);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f;
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
@@ -821,7 +821,7 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
{
Map::Vertex dest(x_pos, y_pos, z_pos);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
@@ -1007,26 +1007,19 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
}
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
char *query = 0;
char errbuff[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
int res = 0;
if (RunQuery(query, MakeAnyLenString(&query,
"SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i",
zoneid),errbuff,&result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
res = atoi( row[0] );
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query, errbuff);
safe_delete_array(query);
}
return(res);
std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
uint8 ZoneDatabase::GetGridType2(uint32 grid, uint16 zoneid) {
+1572 -2212
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -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