Merge remote-tracking branch 'origin/master' into Development

This commit is contained in:
RicardoCampos
2014-10-21 22:51:53 +01:00
170 changed files with 45699 additions and 42904 deletions
-2
View File
@@ -119,7 +119,6 @@ SET(zone_sources
zone_logsys.cpp
zone_config.cpp
zonedb.cpp
zonedbasync.cpp
zoning.cpp
)
@@ -207,7 +206,6 @@ SET(zone_headers
zone.h
zone_config.h
zonedb.h
zonedbasync.h
zonedump.h
)
+207 -47
View File
@@ -18,6 +18,8 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net)
// Test 1
#include <iostream>
#include "../common/debug.h"
#include "aa.h"
#include "mob.h"
@@ -300,7 +302,7 @@ void Client::ActivateAA(aaID activate){
return;
}
} else {
if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) {
if (!CastSpell(caa->spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) {
//Reset on failed cast
SendAATimer(AATimerID, 0, 0xFFFFFF);
Message_StringID(15,ABILITY_FAILED);
@@ -316,13 +318,14 @@ void Client::ActivateAA(aaID activate){
}
}
// Check if AA is expendable
if (aas_send[activate - activate_val]->special_category == 7)
{
if (aas_send[activate - activate_val]->special_category == 7) {
// Add the AA cost to the extended profile to track overall total
m_epp.expended_aa += aas_send[activate]->cost;
SetAA(activate, 0);
Save();
SaveAA(); /* Save Character AA */
SendAA(activate);
SendAATable();
}
@@ -525,7 +528,7 @@ void Client::HandleAAAction(aaID activate) {
//cast the spell, if we have one
if(IsValidSpell(spell_id)) {
int aatid = GetAATimerID(activate);
if(!CastSpell(spell_id, target_id , 10, -1, -1, 0, -1, pTimerAAStart + aatid , CalcAAReuseTimer(caa), 1)) {
if (!CastSpell(spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, pTimerAAStart + aatid, CalcAAReuseTimer(caa), 1)) {
SendAATimer(aatid, 0, 0xFFFFFF);
Message_StringID(15,ABILITY_FAILED);
p_timers.Clear(&database, pTimerAAStart + aatid);
@@ -1035,8 +1038,7 @@ void Client::BuyAA(AA_Action* action)
uint32 real_cost;
std::map<uint32, AALevelCost_Struct>::iterator RequiredLevel = AARequiredLevelAndCost.find(action->ability);
if(RequiredLevel != AARequiredLevelAndCost.end())
{
if(RequiredLevel != AARequiredLevelAndCost.end()) {
real_cost = RequiredLevel->second.Cost;
}
else
@@ -1049,7 +1051,11 @@ void Client::BuyAA(AA_Action* action)
m_pp.aapoints -= real_cost;
Save();
/* Do Player Profile rank calculations and set player profile */
SaveAA();
/* Save to Database to avoid having to write the whole AA array to the profile, only write changes*/
// database.SaveCharacterAA(this->CharacterID(), aa2->id, (cur_level + 1));
if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u))
&& ((aa2->max_level == (cur_level + 1)) && aa2->sof_next_id)){
SendAA(aa2->id);
@@ -1060,8 +1066,10 @@ void Client::BuyAA(AA_Action* action)
SendAATable();
//we are building these messages ourself instead of using the stringID to work around patch discrepencies
//these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2
/*
We are building these messages ourself instead of using the stringID to work around patch discrepencies
these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2
*/
/* Initial purchase of an AA ability */
if (cur_level < 1){
@@ -1084,8 +1092,6 @@ void Client::BuyAA(AA_Action* action)
}
}
SendAAStats();
CalcBonuses();
@@ -1514,11 +1520,15 @@ bool ZoneDatabase::LoadAAEffects2() {
return true;
}
void Client::ResetAA(){
RefundAA();
uint32 i;
for(i=0;i<MAX_PP_AA_ARRAY;i++){
aa[i]->AA = 0;
aa[i]->value = 0;
m_pp.aa_array[MAX_PP_AA_ARRAY].AA = 0;
m_pp.aa_array[MAX_PP_AA_ARRAY].value = 0;
}
std::map<uint32,uint8>::iterator itr;
for(itr=aa_points.begin();itr!=aa_points.end();++itr)
aa_points[itr->first] = 0;
@@ -1530,10 +1540,51 @@ void Client::ResetAA(){
m_pp.raid_leadership_points = 0;
m_pp.group_leadership_exp = 0;
m_pp.raid_leadership_exp = 0;
database.DeleteCharacterAAs(this->CharacterID());
SaveAA();
SendAATable();
database.DeleteCharacterLeadershipAAs(this->CharacterID());
Kick();
}
int Client::GroupLeadershipAAHealthEnhancement()
{
if (IsRaidGrouped()) {
int bonus = 0;
Raid *raid = GetRaid();
if (!raid)
return 0;
uint32 group_id = raid->GetGroup(this);
if (group_id < 12 && raid->GroupCount(group_id) >= 3) {
switch (raid->GetLeadershipAA(groupAAHealthEnhancement, group_id)) {
case 1:
bonus = 30;
break;
case 2:
bonus = 60;
break;
case 3:
bonus = 100;
break;
}
}
if (raid->RaidCount() >= 18) {
switch (raid->GetLeadershipAA(raidAAHealthEnhancement)) {
case 1:
bonus += 30;
break;
case 2:
bonus += 60;
break;
case 3:
bonus += 100;
break;
}
}
return bonus;
}
Group *g = GetGroup();
if(!g || (g->GroupCount() < 3))
@@ -1556,6 +1607,41 @@ int Client::GroupLeadershipAAHealthEnhancement()
int Client::GroupLeadershipAAManaEnhancement()
{
if (IsRaidGrouped()) {
int bonus = 0;
Raid *raid = GetRaid();
if (!raid)
return 0;
uint32 group_id = raid->GetGroup(this);
if (group_id < 12 && raid->GroupCount(group_id) >= 3) {
switch (raid->GetLeadershipAA(groupAAManaEnhancement, group_id)) {
case 1:
bonus = 30;
break;
case 2:
bonus = 60;
break;
case 3:
bonus = 100;
break;
}
}
if (raid->RaidCount() >= 18) {
switch (raid->GetLeadershipAA(raidAAManaEnhancement)) {
case 1:
bonus += 30;
break;
case 2:
bonus += 60;
break;
case 3:
bonus += 100;
break;
}
}
return bonus;
}
Group *g = GetGroup();
if(!g || (g->GroupCount() < 3))
@@ -1578,6 +1664,41 @@ int Client::GroupLeadershipAAManaEnhancement()
int Client::GroupLeadershipAAHealthRegeneration()
{
if (IsRaidGrouped()) {
int bonus = 0;
Raid *raid = GetRaid();
if (!raid)
return 0;
uint32 group_id = raid->GetGroup(this);
if (group_id < 12 && raid->GroupCount(group_id) >= 3) {
switch (raid->GetLeadershipAA(groupAAHealthRegeneration, group_id)) {
case 1:
bonus = 4;
break;
case 2:
bonus = 6;
break;
case 3:
bonus = 8;
break;
}
}
if (raid->RaidCount() >= 18) {
switch (raid->GetLeadershipAA(raidAAHealthRegeneration)) {
case 1:
bonus += 4;
break;
case 2:
bonus += 6;
break;
case 3:
bonus += 8;
break;
}
}
return bonus;
}
Group *g = GetGroup();
if(!g || (g->GroupCount() < 3))
@@ -1600,6 +1721,53 @@ int Client::GroupLeadershipAAHealthRegeneration()
int Client::GroupLeadershipAAOffenseEnhancement()
{
if (IsRaidGrouped()) {
int bonus = 0;
Raid *raid = GetRaid();
if (!raid)
return 0;
uint32 group_id = raid->GetGroup(this);
if (group_id < 12 && raid->GroupCount(group_id) >= 3) {
switch (raid->GetLeadershipAA(groupAAOffenseEnhancement, group_id)) {
case 1:
bonus = 10;
break;
case 2:
bonus = 19;
break;
case 3:
bonus = 28;
break;
case 4:
bonus = 34;
break;
case 5:
bonus = 40;
break;
}
}
if (raid->RaidCount() >= 18) {
switch (raid->GetLeadershipAA(raidAAOffenseEnhancement)) {
case 1:
bonus += 10;
break;
case 2:
bonus += 19;
break;
case 3:
bonus += 28;
break;
case 4:
bonus += 34;
break;
case 5:
bonus += 40;
break;
}
}
return bonus;
}
Group *g = GetGroup();
if(!g || (g->GroupCount() < 3))
@@ -1625,29 +1793,26 @@ int Client::GroupLeadershipAAOffenseEnhancement()
void Client::InspectBuffs(Client* Inspector, int Rank)
{
if(!Inspector || (Rank == 0)) return;
// At some point the removed the restriction of being a group member for this to work
// not sure when, but the way it's coded now, it wouldn't work with mobs.
if (!Inspector || Rank == 0)
return;
EQApplicationPacket *outapp = new EQApplicationPacket(OP_InspectBuffs, sizeof(InspectBuffs_Struct));
InspectBuffs_Struct *ib = (InspectBuffs_Struct *)outapp->pBuffer;
Inspector->Message_StringID(0, CURRENT_SPELL_EFFECTS, GetName());
uint32 buff_count = GetMaxTotalSlots();
for (uint32 i = 0; i < buff_count; ++i)
{
if (buffs[i].spellid != SPELL_UNKNOWN)
{
if(Rank == 1)
Inspector->Message(0, "%s", spells[buffs[i].spellid].name);
else
{
if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent)
Inspector->Message(0, "%s (Permanent)", spells[buffs[i].spellid].name);
else {
char *TempString = nullptr;
MakeAnyLenString(&TempString, "%.1f", static_cast<float>(buffs[i].ticsremaining) / 10.0f);
Inspector->Message_StringID(0, BUFF_MINUTES_REMAINING, spells[buffs[i].spellid].name, TempString);
safe_delete_array(TempString);
}
}
}
uint32 packet_index = 0;
for (uint32 i = 0; i < buff_count; i++) {
if (buffs[i].spellid == SPELL_UNKNOWN)
continue;
ib->spell_id[packet_index] = buffs[i].spellid;
if (Rank > 1)
ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buffdurationformula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining;
packet_index++;
}
Inspector->FastQueuePacket(&outapp);
}
//this really need to be renamed to LoadAAActions()
@@ -1735,19 +1900,15 @@ void ZoneDatabase::FillAAEffects(SendAA_Struct* aa_struct){
if(!aa_struct)
return;
std::string query = StringFormat("SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
int index = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
aa_struct->abilities[index].skill_id=atoi(row[0]);
aa_struct->abilities[index].base1=atoi(row[1]);
aa_struct->abilities[index].base2=atoi(row[2]);
aa_struct->abilities[index].slot=atoi(row[3]);
auto it = aa_effects.find(aa_struct->id);
if (it != aa_effects.end()) {
for (int slot = 0; slot < aa_struct->total_abilities; slot++) {
// aa_effects is a map of a map, so the slot reference does not start at 0
aa_struct->abilities[slot].skill_id = it->second[slot + 1].skill_id;
aa_struct->abilities[slot].base1 = it->second[slot + 1].base1;
aa_struct->abilities[slot].base2 = it->second[slot + 1].base2;
aa_struct->abilities[slot].slot = it->second[slot + 1].slot;
}
}
}
@@ -1818,8 +1979,7 @@ void ZoneDatabase::LoadAAs(SendAA_Struct **load){
}
AALevelCost_Struct aalcs;
for (auto row = results.begin(); row != results.end(); ++row)
{
for (auto row = results.begin(); row != results.end(); ++row) {
aalcs.Level = atoi(row[1]);
aalcs.Cost = atoi(row[2]);
AARequiredLevelAndCost[atoi(row[0])] = aalcs;
+72 -38
View File
@@ -3315,7 +3315,10 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
if(damage < 1)
return 0;
//Regular runes absorb spell damage (except dots) - Confirmed on live.
if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0)
damage = RuneAbsorb(damage, SE_Rune);
if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0)
damage = RuneAbsorb(damage, SE_AbsorbMagicAtt);
@@ -3331,10 +3334,13 @@ int32 Mob::ReduceAllDamage(int32 damage)
if(damage <= 0)
return damage;
if(spellbonuses.ManaAbsorbPercentDamage[0] && (GetMana() > damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100)) {
damage -= (damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100);
SetMana(GetMana() - damage);
TryTriggerOnValueAmount(false, true);
if(spellbonuses.ManaAbsorbPercentDamage[0]) {
int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100;
if (GetMana() >= mana_reduced){
damage -= mana_reduced;
SetMana(GetMana() - mana_reduced);
TryTriggerOnValueAmount(false, true);
}
}
CheckNumHitsRemaining(NUMHIT_IncomingDamage);
@@ -4197,24 +4203,26 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
}
#endif //BOTS
float critChance = 0.0f;
bool IsBerskerSPA = false;
//1: Try Slay Undead
if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire){
if (defender && (defender->GetBodyType() == BT_Undead ||
defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) {
int16 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0];
if (SlayRateBonus) {
critChance += (float(SlayRateBonus)/100.0f);
critChance /= 100.0f;
if(MakeRandomFloat(0, 1) < critChance){
float slayChance = static_cast<float>(SlayRateBonus) / 10000.0f;
if (MakeRandomFloat(0, 1) < slayChance) {
int16 SlayDmgBonus = aabonuses.SlayUndead[1] + itembonuses.SlayUndead[1] + spellbonuses.SlayUndead[1];
damage = (damage*SlayDmgBonus*2.25)/100;
entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s cleanses %s target!(%d)", GetCleanName(), this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its", damage);
damage = (damage * SlayDmgBonus * 2.25) / 100;
if (GetGender() == 1) // female
entity_list.FilteredMessageClose_StringID(this, false, 200,
MT_CritMelee, FilterMeleeCrits, FEMALE_SLAYUNDEAD,
GetCleanName(), itoa(damage));
else // males and neuter I guess
entity_list.FilteredMessageClose_StringID(this, false, 200,
MT_CritMelee, FilterMeleeCrits, MALE_SLAYUNDEAD,
GetCleanName(), itoa(damage));
return;
}
}
@@ -4804,6 +4812,26 @@ void Mob::CommonBreakInvisible()
improved_hidden = false;
}
/* Dev quotes:
* Old formula
* Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 100) * Original Delay)
* New formula
* Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 1000) * (Original Delay / (haste mod *.01f))
* Base Delay 20 25 30 37
* Haste 2.25 2.25 2.25 2.25
* HHE (old) -17 -17 -17 -17
* Final Delay 5.488888889 6.861111111 8.233333333 10.15444444
*
* Base Delay 20 25 30 37
* Haste 2.25 2.25 2.25 2.25
* HHE (new) -383 -383 -383 -383
* Final Delay 5.484444444 6.855555556 8.226666667 10.14622222
*
* Difference -0.004444444 -0.005555556 -0.006666667 -0.008222222
*
* These times are in 10th of a second
*/
void Mob::SetAttackTimer()
{
attack_timer.SetAtTrigger(4000, true);
@@ -4811,7 +4839,7 @@ void Mob::SetAttackTimer()
void Client::SetAttackTimer()
{
float PermaHaste = GetPermaHaste();
float haste_mod = GetHaste() * 0.01f;
//default value for attack timer in case they have
//an invalid weapon equipped:
@@ -4876,28 +4904,30 @@ void Client::SetAttackTimer()
}
}
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
int hhe = itembonuses.HundredHands + spellbonuses.HundredHands;
int speed = 0;
int delay = 36;
float quiver_haste = 0.0f;
//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);
if (GetClass() == MONK || GetClass() == BEASTLORD)
delay = GetMonkHandToHandDelay();
} 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;
}
delay = ItemToUse->Delay;
if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing)
quiver_haste = GetQuiverHaste();
}
if (RuleB(Spells, Jun182014HundredHandsRevamp))
speed = static_cast<int>(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100);
else
speed = static_cast<int>(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100);
// this is probably wrong
if (quiver_haste > 0)
speed *= quiver_haste;
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
if (i == MainPrimary)
@@ -4907,13 +4937,24 @@ void Client::SetAttackTimer()
void NPC::SetAttackTimer()
{
float PermaHaste = GetPermaHaste();
float haste_mod = GetHaste() * 0.01f;
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer *TimerToUse = nullptr;
int hhe = itembonuses.HundredHands + spellbonuses.HundredHands;
// 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 = 0;
if (RuleB(Spells, Jun182014HundredHandsRevamp))
speed = static_cast<int>(((attack_delay / haste_mod) + ((hhe / 1000.0f) * (attack_delay / haste_mod))) * 100);
else
speed = static_cast<int>(((attack_delay / haste_mod) + ((hhe / 100.0f) * attack_delay)) * 100);
for (int i = MainRange; i <= MainSecondary; i++) {
//pick a timer
@@ -4935,13 +4976,6 @@ void NPC::SetAttackTimer()
}
}
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);
}
}
+17 -4
View File
@@ -104,6 +104,8 @@ void Client::CalcBonuses()
CalcMaxMana();
CalcMaxEndurance();
SetAttackTimer();
rooted = FindType(SE_Root);
XPRate = 100 + spellbonuses.XPRateMod;
@@ -174,8 +176,6 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
if(newbon->EnduranceRegen > CalcEnduranceRegenCap())
newbon->EnduranceRegen = CalcEnduranceRegenCap();
SetAttackTimer();
}
void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) {
@@ -1546,6 +1546,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_AttackSpeed4:
{
// These don't generate the IMMUNE_ATKSPEED message and the icon shows up
// but have no effect on the mobs attack speed
if (GetSpecialAbility(UNSLOWABLE))
break;
if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow)
effect_value = effect_value * -1;
@@ -2467,7 +2472,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
}
break;
}
case SE_ManaAbsorbPercentDamage:
{
if (newbon->ManaAbsorbPercentDamage[0] < effect_value){
@@ -2488,7 +2493,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ShieldBlock:
newbon->ShieldBlock += effect_value;
break;
case SE_ShieldEquipHateMod:
newbon->ShieldEquipHateMod += effect_value;
break;
@@ -2502,6 +2507,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->BlockBehind += effect_value;
break;
case SE_Blind:
newbon->IsBlind = true;
break;
case SE_Fear:
newbon->IsFeared = true;
break;
@@ -4081,6 +4090,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
itembonuses.BlockBehind = effect_value;
break;
case SE_Blind:
spellbonuses.IsBlind = false;
break;
case SE_Fear:
spellbonuses.IsFeared = false;
break;
+891 -1300
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -311,11 +311,11 @@ public:
virtual float GetAOERange(uint16 spell_id);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0);
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr);
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF);
// Bot Action Command Methods
bool MesmerizeTarget(Mob* target);
+313 -417
View File
File diff suppressed because it is too large Load Diff
+20 -13
View File
@@ -37,6 +37,7 @@ class Client;
#include "../common/item_struct.h"
#include "../common/clientversions.h"
#include "common.h"
#include "zonedb.h"
#include "errno.h"
#include "mob.h"
@@ -102,11 +103,6 @@ enum { //scribing argument to MemorizeSpell
memSpellSpellbar = 3
};
#define USE_ITEM_SPELL_SLOT 10
#define POTION_BELT_SPELL_SLOT 11
#define DISCIPLINE_SPELL_SLOT 10
#define ABILITY_SPELL_SLOT 9
//Modes for the zoning state of the client.
typedef enum {
ZoneToSafeCoords, // Always send ZonePlayerToBind_Struct to client: Succor/Evac
@@ -240,8 +236,6 @@ public:
bool KeyRingCheck(uint32 item_id);
void KeyRingList();
virtual bool IsClient() const { return true; }
virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw);
bool FinishConnState2(DBAsyncWork* dbaw);
void CompleteConnect();
bool TryStacking(ItemInst* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true);
void SendTraderPacket(Client* trader, uint32 Unknown72 = 51);
@@ -260,6 +254,8 @@ 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 SendColoredText(uint32 color, std::string 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);
@@ -315,6 +311,10 @@ public:
bool Save(uint8 iCommitNow); // 0 = delayed, 1=async now, 2=sync now
void SaveBackup();
/* New PP Save Functions */
bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); }
bool SaveAA();
inline bool ClientDataLoaded() const { return client_data_loaded; }
inline bool Connected() const { return (client_state == CLIENT_CONNECTED); }
inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); }
@@ -559,6 +559,9 @@ public:
void SendLeadershipEXPUpdate();
bool IsLeadershipEXPOn();
inline int GetLeadershipAA(int AAID) { return m_pp.leader_abilities.ranks[AAID]; }
inline LeadershipAA_Struct &GetLeadershipAA() { return m_pp.leader_abilities; }
inline GroupLeadershipAA_Struct &GetGroupLeadershipAA() { return m_pp.leader_abilities.group; }
inline RaidLeadershipAA_Struct &GetRaidLeadershipAA() { return m_pp.leader_abilities.raid; }
int GroupLeadershipAAHealthEnhancement();
int GroupLeadershipAAManaEnhancement();
int GroupLeadershipAAHealthRegeneration();
@@ -585,6 +588,7 @@ public:
bool CheckLoreConflict(const Item_Struct* item);
void ChangeLastName(const char* in_lastname);
void GetGroupAAs(GroupLeadershipAA_Struct *into) const;
void GetRaidAAs(RaidLeadershipAA_Struct *into) const;
void ClearGroupAAs();
void UpdateGroupAAs(int32 points, uint32 type);
void SacrificeConfirm(Client* caster);
@@ -657,12 +661,12 @@ public:
void OnDisconnect(bool hard_disconnect);
uint16 GetSkillPoints() {return m_pp.points;}
void SetSkillPoints(int inp) {m_pp.points = inp;}
uint16 GetSkillPoints() { return m_pp.points;}
void SetSkillPoints(int inp) { m_pp.points = inp;}
void IncreaseSkill(int skill_id, int value = 1) { if (skill_id <= HIGHEST_SKILL) { m_pp.skills[skill_id] += value; } }
void IncreaseLanguageSkill(int skill_id, int value = 1);
virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0)? m_pp.skills[skill_id]*(100 + itembonuses.skillmod[skill_id])/100 : m_pp.skills[skill_id]); } return 0; }
virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0) ? m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100 : m_pp.skills[skill_id]); } return 0; }
uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; }
bool HasSkill(SkillUseTypes skill_id) const;
bool CanHaveSkill(SkillUseTypes skill_id) const;
@@ -681,7 +685,7 @@ public:
inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_);
void TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid);
void TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid);
void SendTradeskillDetails(uint32 recipe_id);
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill);
@@ -785,6 +789,7 @@ public:
int16 acmod();
// Item methods
void EVENT_ITEM_ScriptStopReturn();
uint32 NukeItem(uint32 itemnum, uint8 where_to_check =
(invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor));
void SetTint(int16 slot_id, uint32 color);
@@ -895,7 +900,8 @@ public:
//This is used to later set the buff duration of the spell, in slot to duration.
//Doesn't appear to work directly after the client recieves an action packet.
void SendBuffDurationPacket(uint16 spell_id, int duration, int inlevel);
void SendBuffDurationPacket(Buffs_Struct &buff);
void SendBuffNumHitPacket(Buffs_Struct &buff, int slot);
void ProcessInspectRequest(Client* requestee, Client* requester);
bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); }
@@ -1154,6 +1160,7 @@ public:
const char* GetClassPlural(Client* client);
void SendWebLink(const char* website);
void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg);
void SendSpellAnim(uint16 targetid, uint16 spell_id);
void DuplicateLoreMessage(uint32 ItemID);
void GarbleMessage(char *, uint8);
@@ -1449,7 +1456,7 @@ private:
unsigned int RestRegenHP;
unsigned int RestRegenMana;
unsigned int RestRegenEndurance;
bool EngagedRaidTarget;
uint32 SavedRaidRestTimer;
+2 -3
View File
@@ -33,14 +33,12 @@ void ClientLogs::subscribe(EQEMuLog::LogIDs id, Client *c) {
if(c == nullptr)
return;
//make sure they arnt allready subscribed.
std::vector<Client *>::iterator cur,end;
cur = entries[id].begin();
end = entries[id].end();
for(; cur != end; ++cur) {
if(*cur == c) {
printf("%s was allready subscribed to %d\n", c->GetName(), id);
printf("%s was already subscribed to %d\n", c->GetName(), id);
return;
}
}
@@ -100,6 +98,7 @@ void ClientLogs::msg(EQEMuLog::LogIDs id, const char *buf) {
for(; cur != end; ++cur) {
if(!(*cur)->InZone())
continue;
(*cur)->Message(CLIENT_LOG_CHANNEL, buf);
}
}
+40 -15
View File
@@ -1341,12 +1341,45 @@ int16 Client::CalcCHA() {
return(CHA);
}
int Client::CalcHaste() {
int h = spellbonuses.haste + spellbonuses.hastetype2;
int Client::CalcHaste()
{
/* Tests: (based on results in newer char window)
* 68 v1 + 46 item + 25 over + 35 inhib = 204%
* 46 item + 5 v2 + 25 over + 35 inhib = 65%
* 68 v1 + 46 item + 5 v2 + 25 over + 35 inhib = 209%
* 75% slow + 35 inhib = 25%
* 35 inhib = 65%
* 75% slow = 25%
* Conclusions:
* the bigger effect in slow v. inhib wins
* slow negates all other hastes
* inhib will only negate all other hastes if you don't have v1 (ex. VQ)
*/
// slow beats all! Besides a better inhibit
if (spellbonuses.haste < 0) {
if (-spellbonuses.haste <= spellbonuses.inhibitmelee)
Haste = 100 - spellbonuses.inhibitmelee;
else
Haste = 100 + spellbonuses.haste;
return Haste;
}
// No haste and inhibit, kills all other hastes
if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee) {
Haste = 100 - spellbonuses.inhibitmelee;
return Haste;
}
int h = 0;
int cap = 0;
int overhaste = 0;
int level = GetLevel();
// we know we have a haste spell and not slowed, no extra inhibit melee checks needed
if (spellbonuses.haste)
h += spellbonuses.haste - spellbonuses.inhibitmelee;
if (spellbonuses.hastetype2 && level > 49) // type 2 is capped at 10% and only available to 50+
h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2;
// 26+ no cap, 1-25 10
if (level > 25) // 26+
h += itembonuses.haste;
@@ -1368,24 +1401,16 @@ int Client::CalcHaste() {
// 51+ 25 (despite there being higher spells...), 1-50 10
if (level > 50) // 51+
overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
else // 1-50
overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
h += overhaste;
h += ExtraHaste; //GM granted haste.
h = mod_client_haste(h);
if (spellbonuses.inhibitmelee) {
if (h >= 0)
h -= spellbonuses.inhibitmelee;
else
h -= ((100 + h) * spellbonuses.inhibitmelee / 100);
}
Haste = h;
return(Haste);
Haste = 100 + h;
return Haste;
}
//The AA multipliers are set to be 5, but were 2 on WR
+12173 -12049
View File
File diff suppressed because it is too large Load Diff
+281 -278
View File
@@ -1,292 +1,295 @@
void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app);
void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app);
void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app);
void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app);
void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app);
void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app);
void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app);
// connecting opcode handlers
void Handle_Connect_0x3e33(const EQApplicationPacket *app);
void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientError(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app);
void Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app);
void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app);
void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app);
void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app);
void Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app);
void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app);
void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app);
void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app);
void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app);
void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app);
void Handle_Connect_OP_TGB(const EQApplicationPacket *app);
void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app);
void Handle_Connect_OP_WearChange(const EQApplicationPacket *app);
void Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app);
void Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app);
void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app);
void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app);
void Handle_Connect_OP_WearChange(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientError(const EQApplicationPacket *app);
void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app);
void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app);
void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app);
void Handle_Connect_OP_TGB(const EQApplicationPacket *app);
void Handle_OP_ClientUpdate(const EQApplicationPacket *app);
void Handle_OP_AutoAttack(const EQApplicationPacket *app);
void Handle_OP_AutoAttack2(const EQApplicationPacket *app);
void Handle_OP_Consent(const EQApplicationPacket *app);
void Handle_OP_ConsentDeny(const EQApplicationPacket *app);
void Handle_OP_TargetMouse(const EQApplicationPacket *app);
void Handle_OP_TargetCommand(const EQApplicationPacket *app);
void Handle_OP_Shielding(const EQApplicationPacket *app);
void Handle_OP_Jump(const EQApplicationPacket *app);
void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureRequest(const EQApplicationPacket *app);
void Handle_OP_LDoNButton(const EQApplicationPacket *app);
void Handle_OP_LeaveAdventure(const EQApplicationPacket *app);
void Handle_OP_Consume(const EQApplicationPacket *app);
void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app);
void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app);
void Handle_OP_Consider(const EQApplicationPacket *app);
void Handle_OP_Begging(const EQApplicationPacket *app);
void Handle_OP_TestBuff(const EQApplicationPacket *app);
void Handle_OP_Surname(const EQApplicationPacket *app);
void Handle_OP_ClearSurname(const EQApplicationPacket *app);
void Handle_OP_YellForHelp(const EQApplicationPacket *app);
void Handle_OP_Assist(const EQApplicationPacket *app);
void Handle_OP_AssistGroup(const EQApplicationPacket *app);
void Handle_OP_GMTraining(const EQApplicationPacket *app);
void Handle_OP_GMEndTraining(const EQApplicationPacket *app);
void Handle_OP_GMTrainSkill(const EQApplicationPacket *app);
void Handle_OP_DuelResponse(const EQApplicationPacket *app);
void Handle_OP_DuelResponse2(const EQApplicationPacket *app);
void Handle_OP_RequestDuel(const EQApplicationPacket *app);
void Handle_OP_SpawnAppearance(const EQApplicationPacket *app);
void Handle_OP_BazaarInspect(const EQApplicationPacket *app);
void Handle_OP_Death(const EQApplicationPacket *app);
void Handle_OP_MoveCoin(const EQApplicationPacket *app);
void Handle_OP_ItemLinkClick(const EQApplicationPacket *app);
void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app);
void Handle_OP_MoveItem(const EQApplicationPacket *app);
void Handle_OP_Camp(const EQApplicationPacket *app);
void Handle_OP_Logout(const EQApplicationPacket *app);
void Handle_OP_SenseHeading(const EQApplicationPacket *app);
void Handle_OP_LDoNOpen(const EQApplicationPacket *app);
void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app);
void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app);
void Handle_OP_LDoNInspect(const EQApplicationPacket *app);
void Handle_OP_LDoNPickLock(const EQApplicationPacket *app);
void Handle_OP_FeignDeath(const EQApplicationPacket *app);
void Handle_OP_Sneak(const EQApplicationPacket *app);
void Handle_OP_Hide(const EQApplicationPacket *app);
void Handle_OP_ChannelMessage(const EQApplicationPacket *app);
void Handle_OP_WearChange(const EQApplicationPacket *app);
void Handle_OP_ZoneChange(const EQApplicationPacket *app);
void Handle_OP_DeleteSpawn(const EQApplicationPacket *app);
void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app);
void Handle_OP_Save(const EQApplicationPacket *app);
void Handle_OP_WhoAllRequest(const EQApplicationPacket *app);
void Handle_OP_GMZoneRequest(const EQApplicationPacket *app);
void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app);
void Handle_OP_EndLootRequest(const EQApplicationPacket *app);
void Handle_OP_LootRequest(const EQApplicationPacket *app);
void Handle_OP_Dye(const EQApplicationPacket *app);
void Handle_OP_LootItem(const EQApplicationPacket *app);
void Handle_OP_GuildDelete(const EQApplicationPacket *app);
void Handle_OP_GuildPublicNote(const EQApplicationPacket *app);
void Handle_OP_GetGuildsList(const EQApplicationPacket *app);
void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app);
void Handle_OP_GuildPeace(const EQApplicationPacket *app);
void Handle_OP_GuildWar(const EQApplicationPacket *app);
void Handle_OP_GuildLeader(const EQApplicationPacket *app);
void Handle_OP_GuildDemote(const EQApplicationPacket *app);
void Handle_OP_GuildPromote(const EQApplicationPacket *app);
void Handle_OP_GuildInvite(const EQApplicationPacket *app);
void Handle_OP_GuildRemove(const EQApplicationPacket *app);
void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app);
void Handle_OP_GuildManageBanker(const EQApplicationPacket *app);
void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app);
void Handle_OP_ManaChange(const EQApplicationPacket *app);
void Handle_OP_MemorizeSpell(const EQApplicationPacket *app);
void Handle_OP_SwapSpell(const EQApplicationPacket *app);
void Handle_OP_CastSpell(const EQApplicationPacket *app);
void Handle_OP_DeleteItem(const EQApplicationPacket *app);
void Handle_OP_CombatAbility(const EQApplicationPacket *app);
void Handle_OP_Taunt(const EQApplicationPacket *app);
void Handle_OP_InstillDoubt(const EQApplicationPacket *app);
void Handle_OP_RezzAnswer(const EQApplicationPacket *app);
void Handle_OP_GMSummon(const EQApplicationPacket *app);
void Handle_OP_TradeRequest(const EQApplicationPacket *app);
void Handle_OP_TradeRequestAck(const EQApplicationPacket *app);
void Handle_OP_CancelTrade(const EQApplicationPacket *app);
void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app);
void Handle_OP_BoardBoat(const EQApplicationPacket *app);
void Handle_OP_LeaveBoat(const EQApplicationPacket *app);
void Handle_OP_RandomReq(const EQApplicationPacket *app);
void Handle_OP_Buff(const EQApplicationPacket *app);
void Handle_OP_GMHideMe(const EQApplicationPacket *app);
void Handle_OP_GMNameChange(const EQApplicationPacket *app);
void Handle_OP_GMKill(const EQApplicationPacket *app);
void Handle_OP_GMLastName(const EQApplicationPacket *app);
void Handle_OP_GMToggle(const EQApplicationPacket *app);
void Handle_OP_LFGCommand(const EQApplicationPacket *app);
void Handle_OP_GMGoto(const EQApplicationPacket *app);
void Handle_OP_TraderShop(const EQApplicationPacket *app);
void Handle_OP_ShopRequest(const EQApplicationPacket *app);
void Handle_OP_BazaarSearch(const EQApplicationPacket *app);
void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app);
void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app);
void Handle_OP_ShopEnd(const EQApplicationPacket *app);
// void Handle_OP_CloseContainer(const EQApplicationPacket *app);
void Handle_OP_ClickObjectAction(const EQApplicationPacket *app);
void Handle_OP_ClickObject(const EQApplicationPacket *app);
void Handle_OP_RecipesFavorite(const EQApplicationPacket *app);
void Handle_OP_RecipesSearch(const EQApplicationPacket *app);
void Handle_OP_RecipeDetails(const EQApplicationPacket *app);
void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app);
void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app);
void Handle_OP_ItemName(const EQApplicationPacket *app);
void Handle_OP_AugmentItem(const EQApplicationPacket *app);
void Handle_OP_ClickDoor(const EQApplicationPacket *app);
void Handle_OP_CreateObject(const EQApplicationPacket *app);
void Handle_OP_FaceChange(const EQApplicationPacket *app);
void Handle_OP_GroupInvite(const EQApplicationPacket *app);
void Handle_OP_GroupInvite2(const EQApplicationPacket *app);
void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app);
void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app);
void Handle_OP_GroupFollow(const EQApplicationPacket *app);
void Handle_OP_GroupFollow2(const EQApplicationPacket *app);
void Handle_OP_GroupDisband(const EQApplicationPacket *app);
void Handle_OP_GroupDelete(const EQApplicationPacket *app);
void Handle_OP_GMEmoteZone(const EQApplicationPacket *app);
void Handle_OP_InspectRequest(const EQApplicationPacket *app);
void Handle_OP_InspectAnswer(const EQApplicationPacket *app);
void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app);
void Handle_OP_Medding(const EQApplicationPacket *app);
void Handle_OP_DeleteSpell(const EQApplicationPacket *app);
void Handle_OP_PetitionBug(const EQApplicationPacket *app);
void Handle_OP_Bug(const EQApplicationPacket *app);
void Handle_OP_Petition(const EQApplicationPacket *app);
void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app);
void Handle_OP_PetitionResolve(const EQApplicationPacket *app);
void Handle_OP_PetitionDelete(const EQApplicationPacket *app);
void Handle_OP_PetCommands(const EQApplicationPacket *app);
void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app);
void Handle_OP_PetitionQue(const EQApplicationPacket *app);
void Handle_OP_PDeletePetition(const EQApplicationPacket *app);
void Handle_OP_PetitionCheckout(const EQApplicationPacket *app);
void Handle_OP_PetitionRefresh(const EQApplicationPacket *app);
void Handle_OP_ReadBook(const EQApplicationPacket *app);
void Handle_OP_Emote(const EQApplicationPacket *app);
void Handle_OP_Animation(const EQApplicationPacket *app);
void Handle_OP_SetServerFilter(const EQApplicationPacket *app);
void Handle_OP_GMDelCorpse(const EQApplicationPacket *app);
void Handle_OP_GMKick(const EQApplicationPacket *app);
void Handle_OP_GMServers(const EQApplicationPacket *app);
void Handle_OP_Illusion(const EQApplicationPacket *app);
void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app);
void Handle_OP_Fishing(const EQApplicationPacket *app);
void Handle_OP_Forage(const EQApplicationPacket *app);
void Handle_OP_Mend(const EQApplicationPacket *app);
void Handle_OP_EnvDamage(const EQApplicationPacket *app);
void Handle_OP_Damage(const EQApplicationPacket *app);
void Handle_OP_AAAction(const EQApplicationPacket *app);
void Handle_OP_TraderBuy(const EQApplicationPacket *app);
void Handle_OP_Trader(const EQApplicationPacket *app);
void Handle_OP_GMFind(const EQApplicationPacket *app);
void Handle_OP_PickPocket(const EQApplicationPacket *app);
void Handle_OP_Bind_Wound(const EQApplicationPacket *app);
void Handle_OP_TrackTarget(const EQApplicationPacket *app);
void Handle_OP_Track(const EQApplicationPacket *app);
void Handle_OP_TrackUnknown(const EQApplicationPacket *app);
void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app);
// connected opcode handlers
void Handle_0x0193(const EQApplicationPacket *app);
void Handle_0x01e7(const EQApplicationPacket *app);
void Handle_OP_ClientError(const EQApplicationPacket *app);
void Handle_OP_ReloadUI(const EQApplicationPacket *app);
void Handle_OP_TGB(const EQApplicationPacket *app);
void Handle_OP_Split(const EQApplicationPacket *app);
void Handle_OP_SenseTraps(const EQApplicationPacket *app);
void Handle_OP_DisarmTraps(const EQApplicationPacket *app);
void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app);
void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app);
void Handle_OP_TributeItem(const EQApplicationPacket *app);
void Handle_OP_TributeMoney(const EQApplicationPacket *app);
void Handle_OP_SelectTribute(const EQApplicationPacket *app);
void Handle_OP_TributeUpdate(const EQApplicationPacket *app);
void Handle_OP_TributeToggle(const EQApplicationPacket *app);
void Handle_OP_TributeNPC(const EQApplicationPacket *app);
void Handle_OP_ConfirmDelete(const EQApplicationPacket *app);
void Handle_OP_CrashDump(const EQApplicationPacket *app);
void Handle_OP_ControlBoat(const EQApplicationPacket *app);
void Handle_OP_DumpName(const EQApplicationPacket *app);
void Handle_OP_SetRunMode(const EQApplicationPacket *app);
void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app);
void Handle_OP_Heartbeat(const EQApplicationPacket *app);
void Handle_OP_SafePoint(const EQApplicationPacket *app);
void Handle_OP_FindPersonRequest(const EQApplicationPacket *app);
void Handle_OP_BankerChange(const EQApplicationPacket *app);
void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app);
void Handle_OP_SetTitle(const EQApplicationPacket *app);
void Handle_OP_RequestTitles(const EQApplicationPacket *app);
void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app);
void Handle_OP_Ignore(const EQApplicationPacket *app);
void Handle_OP_LoadSpellSet(const EQApplicationPacket *app);
void Handle_OP_AutoFire(const EQApplicationPacket *app);
void Handle_OP_Rewind(const EQApplicationPacket *app);
void Handle_OP_RaidCommand(const EQApplicationPacket *app);
void Handle_OP_Translocate(const EQApplicationPacket *app);
void Handle_OP_Sacrifice(const EQApplicationPacket *app);
void Handle_OP_AAAction(const EQApplicationPacket *app);
void Handle_OP_AcceptNewTask(const EQApplicationPacket *app);
void Handle_OP_CancelTask(const EQApplicationPacket *app);
void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app);
void Handle_OP_KeyRing(const EQApplicationPacket *app);
void Handle_OP_FriendsWho(const EQApplicationPacket *app);
void Handle_OP_Bandolier(const EQApplicationPacket *app);
void Handle_OP_PopupResponse(const EQApplicationPacket *app);
void Handle_OP_PotionBelt(const EQApplicationPacket *app);
void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app);
void Handle_OP_LFPCommand(const EQApplicationPacket *app);
void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app);
void Handle_OP_Barter(const EQApplicationPacket *app);
void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app);
void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app);
void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app);
void Handle_OP_DelegateAbility(const EQApplicationPacket *app);
void Handle_OP_ApplyPoison(const EQApplicationPacket *app);
void Handle_OP_AugmentInfo(const EQApplicationPacket *app);
void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app);
void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app);
void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app);
void Handle_OP_RespawnWindow(const EQApplicationPacket *app);
void Handle_OP_GroupUpdate(const EQApplicationPacket *app);
void Handle_OP_SetStartCity(const EQApplicationPacket *app);
void Handle_OP_Report(const EQApplicationPacket *app);
void Handle_OP_VetClaimRequest(const EQApplicationPacket *app);
void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app);
void Handle_OP_GuildBank(const EQApplicationPacket *app);
void Handle_OP_GroupRoles(const EQApplicationPacket *app);
void Handle_OP_HideCorpse(const EQApplicationPacket *app);
void Handle_OP_TradeBusy(const EQApplicationPacket *app);
void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app);
void Handle_OP_GuildStatus(const EQApplicationPacket *app);
void Handle_OP_BlockedBuffs(const EQApplicationPacket *app);
void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app);
void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app);
void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app);
void Handle_OP_CorpseDrag(const EQApplicationPacket *app);
void Handle_OP_CorpseDrop(const EQApplicationPacket *app);
void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app);
void Handle_OP_GuildCreate(const EQApplicationPacket *app);
void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app);
void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app);
void Handle_OP_AdventureRequest(const EQApplicationPacket *app);
void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app);
void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app);
void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app);
void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app);
void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app);
void Handle_OP_AltCurrencySell(const EQApplicationPacket *app);
void Handle_OP_CrystalReclaim(const EQApplicationPacket *app);
void Handle_OP_CrystalCreate(const EQApplicationPacket *app);
void Handle_OP_LFGuild(const EQApplicationPacket *app);
void Handle_OP_XTargetRequest(const EQApplicationPacket *app);
void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app);
void Handle_OP_ItemPreview(const EQApplicationPacket *app);
void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app);
void Handle_OP_MercenaryHire(const EQApplicationPacket *app);
void Handle_OP_MercenaryCommand(const EQApplicationPacket *app);
void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app);
void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app);
void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app);
void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app);
void Handle_OP_OpenInventory(const EQApplicationPacket *app);
void Handle_OP_OpenContainer(const EQApplicationPacket *app);
void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app);
void Handle_OP_Animation(const EQApplicationPacket *app);
void Handle_OP_ApplyPoison(const EQApplicationPacket *app);
void Handle_OP_Assist(const EQApplicationPacket *app);
void Handle_OP_AssistGroup(const EQApplicationPacket *app);
void Handle_OP_AugmentInfo(const EQApplicationPacket *app);
void Handle_OP_AugmentItem(const EQApplicationPacket *app);
void Handle_OP_AutoAttack(const EQApplicationPacket *app);
void Handle_OP_AutoAttack2(const EQApplicationPacket *app);
void Handle_OP_AutoFire(const EQApplicationPacket *app);
void Handle_OP_Bandolier(const EQApplicationPacket *app);
void Handle_OP_BankerChange(const EQApplicationPacket *app);
void Handle_OP_Barter(const EQApplicationPacket *app);
void Handle_OP_BazaarInspect(const EQApplicationPacket *app);
void Handle_OP_BazaarSearch(const EQApplicationPacket *app);
void Handle_OP_Begging(const EQApplicationPacket *app);
void Handle_OP_Bind_Wound(const EQApplicationPacket *app);
void Handle_OP_BlockedBuffs(const EQApplicationPacket *app);
void Handle_OP_BoardBoat(const EQApplicationPacket *app);
void Handle_OP_Buff(const EQApplicationPacket *app);
void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app);
void Handle_OP_Bug(const EQApplicationPacket *app);
void Handle_OP_Camp(const EQApplicationPacket *app);
void Handle_OP_CancelTask(const EQApplicationPacket *app);
void Handle_OP_CancelTrade(const EQApplicationPacket *app);
void Handle_OP_CastSpell(const EQApplicationPacket *app);
void Handle_OP_ChannelMessage(const EQApplicationPacket *app);
void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app);
void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app);
void Handle_OP_ClearSurname(const EQApplicationPacket *app);
void Handle_OP_ClickDoor(const EQApplicationPacket *app);
void Handle_OP_ClickObject(const EQApplicationPacket *app);
void Handle_OP_ClickObjectAction(const EQApplicationPacket *app);
void Handle_OP_ClientError(const EQApplicationPacket *app);
void Handle_OP_ClientTimeStamp(const EQApplicationPacket *app);
void Handle_OP_ClientUpdate(const EQApplicationPacket *app);
// void Handle_OP_CloseContainer(const EQApplicationPacket *app);
void Handle_OP_CombatAbility(const EQApplicationPacket *app);
void Handle_OP_ConfirmDelete(const EQApplicationPacket *app);
void Handle_OP_Consent(const EQApplicationPacket *app);
void Handle_OP_ConsentDeny(const EQApplicationPacket *app);
void Handle_OP_Consider(const EQApplicationPacket *app);
void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app);
void Handle_OP_Consume(const EQApplicationPacket *app);
void Handle_OP_ControlBoat(const EQApplicationPacket *app);
void Handle_OP_CorpseDrag(const EQApplicationPacket *app);
void Handle_OP_CorpseDrop(const EQApplicationPacket *app);
void Handle_OP_CrashDump(const EQApplicationPacket *app);
void Handle_OP_CreateObject(const EQApplicationPacket *app);
void Handle_OP_CrystalCreate(const EQApplicationPacket *app);
void Handle_OP_CrystalReclaim(const EQApplicationPacket *app);
void Handle_OP_Damage(const EQApplicationPacket *app);
void Handle_OP_Death(const EQApplicationPacket *app);
void Handle_OP_DelegateAbility(const EQApplicationPacket *app);
void Handle_OP_DeleteItem(const EQApplicationPacket *app);
void Handle_OP_DeleteSpawn(const EQApplicationPacket *app);
void Handle_OP_DeleteSpell(const EQApplicationPacket *app);
void Handle_OP_DisarmTraps(const EQApplicationPacket *app);
void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app);
void Handle_OP_DuelResponse(const EQApplicationPacket *app);
void Handle_OP_DuelResponse2(const EQApplicationPacket *app);
void Handle_OP_DumpName(const EQApplicationPacket *app);
void Handle_OP_Dye(const EQApplicationPacket *app);
void Handle_OP_Emote(const EQApplicationPacket *app);
void Handle_OP_EndLootRequest(const EQApplicationPacket *app);
void Handle_OP_EnvDamage(const EQApplicationPacket *app);
void Handle_OP_FaceChange(const EQApplicationPacket *app);
void Handle_OP_FeignDeath(const EQApplicationPacket *app);
void Handle_OP_FindPersonRequest(const EQApplicationPacket *app);
void Handle_OP_Fishing(const EQApplicationPacket *app);
void Handle_OP_Forage(const EQApplicationPacket *app);
void Handle_OP_FriendsWho(const EQApplicationPacket *app);
void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app);
void Handle_OP_GetGuildsList(const EQApplicationPacket *app);
void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app);
void Handle_OP_GMDelCorpse(const EQApplicationPacket *app);
void Handle_OP_GMEmoteZone(const EQApplicationPacket *app);
void Handle_OP_GMEndTraining(const EQApplicationPacket *app);
void Handle_OP_GMFind(const EQApplicationPacket *app);
void Handle_OP_GMGoto(const EQApplicationPacket *app);
void Handle_OP_GMHideMe(const EQApplicationPacket *app);
void Handle_OP_GMKick(const EQApplicationPacket *app);
void Handle_OP_GMKill(const EQApplicationPacket *app);
void Handle_OP_GMLastName(const EQApplicationPacket *app);
void Handle_OP_GMNameChange(const EQApplicationPacket *app);
void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app);
void Handle_OP_GMServers(const EQApplicationPacket *app);
void Handle_OP_GMSummon(const EQApplicationPacket *app);
void Handle_OP_GMToggle(const EQApplicationPacket *app);
void Handle_OP_GMTraining(const EQApplicationPacket *app);
void Handle_OP_GMTrainSkill(const EQApplicationPacket *app);
void Handle_OP_GMZoneRequest(const EQApplicationPacket *app);
void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app);
void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app);
void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app);
void Handle_OP_GroupDelete(const EQApplicationPacket *app);
void Handle_OP_GroupDisband(const EQApplicationPacket *app);
void Handle_OP_GroupFollow(const EQApplicationPacket *app);
void Handle_OP_GroupFollow2(const EQApplicationPacket *app);
void Handle_OP_GroupInvite(const EQApplicationPacket *app);
void Handle_OP_GroupInvite2(const EQApplicationPacket *app);
void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app);
void Handle_OP_GroupMentor(const EQApplicationPacket *app);
void Handle_OP_GroupRoles(const EQApplicationPacket *app);
void Handle_OP_GroupUpdate(const EQApplicationPacket *app);
void Handle_OP_GuildBank(const EQApplicationPacket *app);
void Handle_OP_GuildCreate(const EQApplicationPacket *app);
void Handle_OP_GuildDelete(const EQApplicationPacket *app);
void Handle_OP_GuildDemote(const EQApplicationPacket *app);
void Handle_OP_GuildInvite(const EQApplicationPacket *app);
void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app);
void Handle_OP_GuildLeader(const EQApplicationPacket *app);
void Handle_OP_GuildManageBanker(const EQApplicationPacket *app);
void Handle_OP_GuildPeace(const EQApplicationPacket *app);
void Handle_OP_GuildPromote(const EQApplicationPacket *app);
void Handle_OP_GuildPublicNote(const EQApplicationPacket *app);
void Handle_OP_GuildRemove(const EQApplicationPacket *app);
void Handle_OP_GuildStatus(const EQApplicationPacket *app);
void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app);
void Handle_OP_GuildWar(const EQApplicationPacket *app);
void Handle_OP_Heartbeat(const EQApplicationPacket *app);
void Handle_OP_Hide(const EQApplicationPacket *app);
void Handle_OP_HideCorpse(const EQApplicationPacket *app);
void Handle_OP_Ignore(const EQApplicationPacket *app);
void Handle_OP_Illusion(const EQApplicationPacket *app);
void Handle_OP_InspectAnswer(const EQApplicationPacket *app);
void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app);
void Handle_OP_InspectRequest(const EQApplicationPacket *app);
void Handle_OP_InstillDoubt(const EQApplicationPacket *app);
void Handle_OP_ItemLinkClick(const EQApplicationPacket *app);
void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app);
void Handle_OP_ItemName(const EQApplicationPacket *app);
void Handle_OP_ItemPreview(const EQApplicationPacket *app);
void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app);
void Handle_OP_Jump(const EQApplicationPacket *app);
void Handle_OP_KeyRing(const EQApplicationPacket *app);
void Handle_OP_LDoNButton(const EQApplicationPacket *app);
void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app);
void Handle_OP_LDoNInspect(const EQApplicationPacket *app);
void Handle_OP_LDoNOpen(const EQApplicationPacket *app);
void Handle_OP_LDoNPickLock(const EQApplicationPacket *app);
void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app);
void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app);
void Handle_OP_LeaveAdventure(const EQApplicationPacket *app);
void Handle_OP_LeaveBoat(const EQApplicationPacket *app);
void Handle_OP_LFGCommand(const EQApplicationPacket *app);
void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app);
void Handle_OP_LFGuild(const EQApplicationPacket *app);
void Handle_OP_LFPCommand(const EQApplicationPacket *app);
void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app);
void Handle_OP_LoadSpellSet(const EQApplicationPacket *app);
void Handle_OP_Logout(const EQApplicationPacket *app);
void Handle_OP_LootItem(const EQApplicationPacket *app);
void Handle_OP_LootRequest(const EQApplicationPacket *app);
void Handle_OP_ManaChange(const EQApplicationPacket *app);
void Handle_OP_Medding(const EQApplicationPacket *app);
void Handle_OP_MemorizeSpell(const EQApplicationPacket *app);
void Handle_OP_Mend(const EQApplicationPacket *app);
void Handle_OP_MercenaryCommand(const EQApplicationPacket *app);
void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app);
void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app);
void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app);
void Handle_OP_MercenaryHire(const EQApplicationPacket *app);
void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app);
void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app);
void Handle_OP_MoveCoin(const EQApplicationPacket *app);
void Handle_OP_MoveItem(const EQApplicationPacket *app);
void Handle_OP_OpenContainer(const EQApplicationPacket *app);
void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app);
void Handle_OP_OpenInventory(const EQApplicationPacket *app);
void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app);
void Handle_OP_PDeletePetition(const EQApplicationPacket *app);
void Handle_OP_PetCommands(const EQApplicationPacket *app);
void Handle_OP_Petition(const EQApplicationPacket *app);
void Handle_OP_PetitionBug(const EQApplicationPacket *app);
void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app);
void Handle_OP_PetitionCheckout(const EQApplicationPacket *app);
void Handle_OP_PetitionDelete(const EQApplicationPacket *app);
void Handle_OP_PetitionQue(const EQApplicationPacket *app);
void Handle_OP_PetitionRefresh(const EQApplicationPacket *app);
void Handle_OP_PetitionResolve(const EQApplicationPacket *app);
void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app);
void Handle_OP_PickPocket(const EQApplicationPacket *app);
void Handle_OP_PopupResponse(const EQApplicationPacket *app);
void Handle_OP_PotionBelt(const EQApplicationPacket *app);
void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app);
void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app);
void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app);
void Handle_OP_RaidCommand(const EQApplicationPacket *app);
void Handle_OP_RandomReq(const EQApplicationPacket *app);
void Handle_OP_ReadBook(const EQApplicationPacket *app);
void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app);
void Handle_OP_RecipeDetails(const EQApplicationPacket *app);
void Handle_OP_RecipesFavorite(const EQApplicationPacket *app);
void Handle_OP_RecipesSearch(const EQApplicationPacket *app);
void Handle_OP_ReloadUI(const EQApplicationPacket *app);
void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app);
void Handle_OP_Report(const EQApplicationPacket *app);
void Handle_OP_RequestDuel(const EQApplicationPacket *app);
void Handle_OP_RequestTitles(const EQApplicationPacket *app);
void Handle_OP_RespawnWindow(const EQApplicationPacket *app);
void Handle_OP_Rewind(const EQApplicationPacket *app);
void Handle_OP_RezzAnswer(const EQApplicationPacket *app);
void Handle_OP_Sacrifice(const EQApplicationPacket *app);
void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app);
void Handle_OP_SafePoint(const EQApplicationPacket *app);
void Handle_OP_Save(const EQApplicationPacket *app);
void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app);
void Handle_OP_SelectTribute(const EQApplicationPacket *app);
void Handle_OP_SenseHeading(const EQApplicationPacket *app);
void Handle_OP_SenseTraps(const EQApplicationPacket *app);
void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app);
void Handle_OP_SetRunMode(const EQApplicationPacket *app);
void Handle_OP_SetServerFilter(const EQApplicationPacket *app);
void Handle_OP_SetStartCity(const EQApplicationPacket *app);
void Handle_OP_SetTitle(const EQApplicationPacket *app);
void Handle_OP_Shielding(const EQApplicationPacket *app);
void Handle_OP_ShopEnd(const EQApplicationPacket *app);
void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app);
void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app);
void Handle_OP_ShopRequest(const EQApplicationPacket *app);
void Handle_OP_Sneak(const EQApplicationPacket *app);
void Handle_OP_SpawnAppearance(const EQApplicationPacket *app);
void Handle_OP_Split(const EQApplicationPacket *app);
void Handle_OP_Surname(const EQApplicationPacket *app);
void Handle_OP_SwapSpell(const EQApplicationPacket *app);
void Handle_OP_TargetCommand(const EQApplicationPacket *app);
void Handle_OP_TargetMouse(const EQApplicationPacket *app);
void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app);
void Handle_OP_Taunt(const EQApplicationPacket *app);
void Handle_OP_TestBuff(const EQApplicationPacket *app);
void Handle_OP_TGB(const EQApplicationPacket *app);
void Handle_OP_Track(const EQApplicationPacket *app);
void Handle_OP_TrackTarget(const EQApplicationPacket *app);
void Handle_OP_TrackUnknown(const EQApplicationPacket *app);
void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app);
void Handle_OP_TradeBusy(const EQApplicationPacket *app);
void Handle_OP_Trader(const EQApplicationPacket *app);
void Handle_OP_TraderBuy(const EQApplicationPacket *app);
void Handle_OP_TradeRequest(const EQApplicationPacket *app);
void Handle_OP_TradeRequestAck(const EQApplicationPacket *app);
void Handle_OP_TraderShop(const EQApplicationPacket *app);
void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app);
void Handle_OP_Translocate(const EQApplicationPacket *app);
void Handle_OP_TributeItem(const EQApplicationPacket *app);
void Handle_OP_TributeMoney(const EQApplicationPacket *app);
void Handle_OP_TributeNPC(const EQApplicationPacket *app);
void Handle_OP_TributeToggle(const EQApplicationPacket *app);
void Handle_OP_TributeUpdate(const EQApplicationPacket *app);
void Handle_OP_VetClaimRequest(const EQApplicationPacket *app);
void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app);
void Handle_OP_WearChange(const EQApplicationPacket *app);
void Handle_OP_WhoAllRequest(const EQApplicationPacket *app);
void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app);
void Handle_OP_XTargetRequest(const EQApplicationPacket *app);
void Handle_OP_YellForHelp(const EQApplicationPacket *app);
void Handle_OP_ZoneChange(const EQApplicationPacket *app);
+56 -47
View File
@@ -967,93 +967,96 @@ void Client::BulkSendInventoryItems()
void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
const Item_Struct* handyitem = nullptr;
uint32 numItemSlots=80; //The max number of items passed in the transaction.
uint32 numItemSlots = 80; //The max number of items passed in the transaction.
const Item_Struct *item;
std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
std::list<MerchantList>::const_iterator itr;
Mob* merch = entity_list.GetMobByNpcTypeID(npcid);
if(merlist.size()==0){ //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded
if (merlist.size() == 0) { //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded
zone->LoadNewMerchantData(merchant_id);
merlist = zone->merchanttable[merchant_id];
if(merlist.size()==0)
if (merlist.size() == 0)
return;
}
std::list<TempMerchantList> tmp_merlist = zone->tmpmerchanttable[npcid];
std::list<TempMerchantList>::iterator tmp_itr;
uint32 i=1;
uint32 i = 1;
uint8 handychance = 0;
for (itr = merlist.begin(); itr != merlist.end() && i < numItemSlots; ++itr) {
for (itr = merlist.begin(); itr != merlist.end() && i <= numItemSlots; ++itr) {
MerchantList ml = *itr;
if (merch->CastToNPC()->GetMerchantProbability() > ml.probability)
continue;
if(GetLevel() < ml.level_required)
if (GetLevel() < ml.level_required)
continue;
if (!(ml.classes_required & (1 << (GetClass() - 1))))
continue;
int32 fac = merch ? merch->GetPrimaryFaction() : 0;
if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required)
if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required)
continue;
handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1 );
handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1);
item = database.GetItem(ml.item);
if(item) {
if(handychance==0)
handyitem=item;
if (item) {
if (handychance == 0)
handyitem = item;
else
handychance--;
int charges=1;
if(item->ItemClass==ItemClassCommon)
charges=item->MaxCharges;
int charges = 1;
if (item->ItemClass == ItemClassCommon)
charges = item->MaxCharges;
ItemInst* inst = database.CreateItem(item, charges);
if (inst) {
if (RuleB(Merchant, UsePriceMod)){
inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(merch,false)));
if (RuleB(Merchant, UsePriceMod)) {
inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false)));
}
else
inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate));
inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate));
inst->SetMerchantSlot(ml.slot);
inst->SetMerchantCount(-1); //unlimited
if(charges > 0)
if (charges > 0)
inst->SetCharges(charges);
else
inst->SetCharges(1);
SendItemPacket(ml.slot-1, inst, ItemPacketMerchant);
SendItemPacket(ml.slot - 1, inst, ItemPacketMerchant);
safe_delete(inst);
}
}
// Account for merchant lists with gaps.
if(ml.slot >= i)
if (ml.slot >= i) {
if (ml.slot > i)
LogFile->write(EQEMuLog::Debug, "(WARNING) Merchantlist contains gap at slot %d. Merchant: %d, NPC: %d", i, merchant_id, npcid);
i = ml.slot + 1;
}
}
std::list<TempMerchantList> origtmp_merlist = zone->tmpmerchanttable[npcid];
tmp_merlist.clear();
for(tmp_itr = origtmp_merlist.begin();tmp_itr != origtmp_merlist.end() && i<numItemSlots;++tmp_itr){
for (tmp_itr = origtmp_merlist.begin(); tmp_itr != origtmp_merlist.end() && i <= numItemSlots; ++tmp_itr) {
TempMerchantList ml = *tmp_itr;
item=database.GetItem(ml.item);
ml.slot=i;
item = database.GetItem(ml.item);
ml.slot = i;
if (item) {
if(handychance==0)
handyitem=item;
if (handychance == 0)
handyitem = item;
else
handychance--;
int charges=1;
int charges = 1;
//if(item->ItemClass==ItemClassCommon && (int16)ml.charges <= item->MaxCharges)
// charges=ml.charges;
//else
charges = item->MaxCharges;
ItemInst* inst = database.CreateItem(item, charges);
if (inst) {
if (RuleB(Merchant, UsePriceMod)){
inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(merch,false)));
if (RuleB(Merchant, UsePriceMod)) {
inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false)));
}
else
inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate));
inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate));
inst->SetMerchantSlot(ml.slot);
inst->SetMerchantCount(ml.charges);
if(charges > 0)
@@ -1069,32 +1072,32 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
}
//this resets the slot
zone->tmpmerchanttable[npcid] = tmp_merlist;
if(merch != nullptr && handyitem){
char handy_id[8]={0};
int greeting=MakeRandomInt(0, 4);
int greet_id=0;
switch(greeting){
if (merch != nullptr && handyitem) {
char handy_id[8] = { 0 };
int greeting = MakeRandomInt(0, 4);
int greet_id = 0;
switch (greeting) {
case 1:
greet_id=MERCHANT_GREETING;
greet_id = MERCHANT_GREETING;
break;
case 2:
greet_id=MERCHANT_HANDY_ITEM1;
greet_id = MERCHANT_HANDY_ITEM1;
break;
case 3:
greet_id=MERCHANT_HANDY_ITEM2;
greet_id = MERCHANT_HANDY_ITEM2;
break;
case 4:
greet_id=MERCHANT_HANDY_ITEM3;
greet_id = MERCHANT_HANDY_ITEM3;
break;
default:
greet_id=MERCHANT_HANDY_ITEM4;
greet_id = MERCHANT_HANDY_ITEM4;
}
sprintf(handy_id,"%i",greet_id);
sprintf(handy_id, "%i", greet_id);
if(greet_id!=MERCHANT_GREETING)
Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName(),handyitem->Name);
if (greet_id != MERCHANT_GREETING)
Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName(), handyitem->Name);
else
Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName());
Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName());
merch->CastToNPC()->FaceTarget(this->CastToMob());
}
@@ -1547,7 +1550,12 @@ void Client::OPMoveCoin(const EQApplicationPacket* app)
if (from_bucket == &m_pp.platinum_shared)
amount_to_add = 0 - amount_to_take;
database.SetSharedPlatinum(AccountID(),amount_to_add);
database.SetSharedPlatinum(AccountID(),amount_to_add);
}
}
else{
if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared){
this->Message(13, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE");
}
}
}
@@ -1580,7 +1588,7 @@ void Client::OPMoveCoin(const EQApplicationPacket* app)
safe_delete(outapp);
}
Save();
SaveCurrency();
}
void Client::OPGMTraining(const EQApplicationPacket *app)
@@ -1715,6 +1723,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
}
uint16 skilllevel = GetRawSkill(skill);
if(skilllevel == 0) {
//this is a new skill..
uint16 t_level = SkillTrainLevel(skill, GetClass());
@@ -1724,7 +1733,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
}
SetSkill(skill, t_level);
} else {
} else {
switch(skill) {
case SkillBrewing:
case SkillMakePoison:
+2542 -3104
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -125,7 +125,6 @@ void command_worldshutdown(Client *c, const Seperator *sep);
void command_sendzonespawns(Client *c, const Seperator *sep);
void command_zsave(Client *c, const Seperator *sep);
void command_dbspawn2(Client *c, const Seperator *sep);
void command_copychar(Client *c, const Seperator *sep);
void command_shutdown(Client *c, const Seperator *sep);
void command_delacct(Client *c, const Seperator *sep);
void command_setpass(Client *c, const Seperator *sep);
@@ -151,7 +150,6 @@ void command_texture(Client *c, const Seperator *sep);
void command_npctypespawn(Client *c, const Seperator *sep);
void command_heal(Client *c, const Seperator *sep);
void command_appearance(Client *c, const Seperator *sep);
void command_charbackup(Client *c, const Seperator *sep);
void command_nukeitem(Client *c, const Seperator *sep);
void command_peekinv(Client *c, const Seperator *sep);
void command_findnpctype(Client *c, const Seperator *sep);
@@ -217,7 +215,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);
+5
View File
@@ -17,6 +17,10 @@
#define _NPCPET(x) (x && x->IsNPC() && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsNPC())
#define _BECOMENPCPET(x) (x && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsClient() && x->CastToMob()->GetOwner()->CastToClient()->IsBecomeNPC())
#define USE_ITEM_SPELL_SLOT 10
#define POTION_BELT_SPELL_SLOT 11
#define DISCIPLINE_SPELL_SLOT 10
#define ABILITY_SPELL_SLOT 9
//LOS Parameters:
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from
@@ -290,6 +294,7 @@ struct StatBonuses {
int16 ResistFearChance; //i
bool Fearless; //i
bool IsFeared; //i
bool IsBlind; //i
int16 StunResist; //i
int16 MeleeSkillCheck; //i
uint8 MeleeSkillCheckSkill;
+107 -9
View File
@@ -97,6 +97,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna
for (unsigned int i=0; i < dbpcs->itemcount; i++) {
tmp = new ServerLootItem_Struct;
memcpy(tmp, &dbpcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct));
tmp->equipSlot = CorpseToServerSlot(tmp->equipSlot); // temp hack until corpse blobs are removed
itemlist.push_back(tmp);
}
@@ -147,6 +148,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna
for (unsigned int i=0; i < dbpc->itemcount; i++) {
tmp = new ServerLootItem_Struct;
memcpy(tmp, &dbpc->items[i], sizeof(player_lootitem::ServerLootItem_Struct));
tmp->equipSlot = CorpseToServerSlot(tmp->equipSlot); // temp hack until corpse blobs are removed
itemlist.push_back(tmp);
}
@@ -600,6 +602,7 @@ bool Corpse::Save() {
end = itemlist.end();
for(; cur != end; ++cur) {
ServerLootItem_Struct* item = *cur;
item->equipSlot = ServerToCorpseSlot(item->equipSlot); // temp hack until corpse blobs are removed
memcpy((char*) &dbpc->items[x++], (char*) item, sizeof(player_lootitem::ServerLootItem_Struct));
}
@@ -971,8 +974,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
}
RemoveCash();
Save();
client->Save();
Save();
}
outapp->priority = 6;
@@ -2064,15 +2066,111 @@ void Corpse::LoadPlayerCorpseDecayTime(uint32 dbid){
}
/*
uint32 Corpse::ServerToCorpseSlot(int16 server_slot) {
// reserved
}
** Corpse slot translations are needed until corpse database blobs are converted
**
** To account for the addition of MainPowerSource, MainGeneral9 and MainGeneral10 into
** the contiguous possessions slot enumeration, the following designations will be used:
**
** Designatiom Server Corpse Offset
** --------------------------------------------------
** MainCharm 0 0 0
** ... ... ... 0
** MainWaist 20 20 0
** MainPowerSource 21 9999 +9978
** MainAmmo 22 21 -1
**
** MainGeneral1 23 22 -1
** ... ... ... -1
** MainGeneral8 30 29 -1
** MainGeneral9 31 9997 +9966
** MainGeneral10 32 9998 +9966
**
** MainCursor 33 30 -3
**
** MainGeneral1_1 251 251 0
** ... ... ... 0
** MainGeneral8_10 330 330 0
** MainGeneral9_1 331 341 +10
** ... ... ... +10
** MainGeneral10_10 350 360 +10
**
** MainCursor_1 351 331 -20
** ... ... ... -20
** MainCursor_10 360 340 -20
**
** (Not all slot designations are valid to all clients..see <client>##_constants.h files for valid slot enumerations)
*/
/*
int16 Corpse::CorpseToServerSlot(uint32 corpse_slot) {
// reserved
int16 Corpse::ServerToCorpseSlot(int16 server_slot)
{
return server_slot; // temporary return
/*
switch (server_slot)
{
case MainPowerSource:
return 9999;
case MainGeneral9:
return 9997;
case MainGeneral10:
return 9998;
case MainCursor:
return 30;
case MainAmmo:
case MainGeneral1:
case MainGeneral2:
case MainGeneral3:
case MainGeneral4:
case MainGeneral5:
case MainGeneral6:
case MainGeneral7:
case MainGeneral8:
return server_slot - 1;
default:
if (server_slot >= EmuConstants::CURSOR_BAG_BEGIN && server_slot <= EmuConstants::CURSOR_BAG_END)
return server_slot - 20;
else if (server_slot >= EmuConstants::GENERAL_BAGS_END - 19 && server_slot <= EmuConstants::GENERAL_BAGS_END)
return server_slot + 10;
else
return server_slot;
}
*/
}
int16 Corpse::CorpseToServerSlot(int16 corpse_slot)
{
return corpse_slot; // temporary return
/*
switch (corpse_slot)
{
case 9999:
return MainPowerSource;
case 9997:
return MainGeneral9;
case 9998:
return MainGeneral10;
case 30:
return MainCursor;
case 21: // old SLOT_AMMO
case 22: // old PERSONAL_BEGIN
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
case 29: // old PERSONAL_END
return corpse_slot + 1;
default:
if (corpse_slot >= 331 && corpse_slot <= 340)
return corpse_slot + 20;
else if (corpse_slot >= 341 && corpse_slot <= 360)
return corpse_slot - 10;
else
return corpse_slot;
}
*/
}
*/
/*
void Corpse::CastRezz(uint16 spellid, Mob* Caster){
+2 -2
View File
@@ -112,8 +112,8 @@ public:
inline int GetRezzExp() { return rezzexp; }
// these are a temporary work-around until corpse inventory is removed from the database blob
//static uint32 ServerToCorpseSlot(int16 server_slot); // encode
//static int16 CorpseToServerSlot(uint32 corpse_slot); // decode
static int16 ServerToCorpseSlot(int16 server_slot); // encode
static int16 CorpseToServerSlot(int16 corpse_slot); // decode
protected:
std::list<uint32> MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot);
+3 -6
View File
@@ -56,7 +56,7 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
value -= target->GetFcDamageAmtIncoming(this, spell_id)/spells[spell_id].buffduration;
}
value += dmg*SpellFocusDMG/100;
value += dmg*GetSpellFocusDMG()/100;
if (AI_HasSpellsEffects()){
int16 chance = 0;
@@ -275,7 +275,7 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
//Scale all NPC spell healing via SetSpellFocusHeal(value)
value += value*SpellFocusHeal/100;
value += value*GetSpellFocusHeal()/100;
if (target) {
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
@@ -606,6 +606,7 @@ bool Client::TrainDiscipline(uint32 itemid) {
return(false);
} else if(m_pp.disciplines.values[r] == 0) {
m_pp.disciplines.values[r] = spell_id;
database.SaveCharacterDisc(this->CharacterID(), r, spell_id);
SendDisciplineUpdate();
Message(0, "You have learned a new discipline!");
return(true);
@@ -616,13 +617,9 @@ bool Client::TrainDiscipline(uint32 itemid) {
}
void Client::SendDisciplineUpdate() {
//this dosent seem to work right now
EQApplicationPacket app(OP_DisciplineUpdate, sizeof(Disciplines_Struct));
Disciplines_Struct *d = (Disciplines_Struct*)app.pBuffer;
//dunno why I dont just send the one from m_pp
memcpy(d, &m_pp.disciplines, sizeof(m_pp.disciplines));
QueuePacket(&app);
}
+38 -1
View File
@@ -3402,6 +3402,41 @@ XS(XS__qs_player_event)
XSRETURN_EMPTY;
}
XS(XS__crosszonesetentityvariablebynpctypeid);
XS(XS__crosszonesetentityvariablebynpctypeid)
{
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: crosszonesetentityvariablebynpctypeid(npctype_id, id, m_var)");
if (items == 3) {
uint32 npctype_id = (uint32)SvIV(ST(0));
const char *id = (const char *)SvPV_nolen(ST(1));
const char *m_var = (const char *)SvPV_nolen(ST(2));
quest_manager.CrossZoneSetEntityVariableByNPCTypeID(npctype_id, id, m_var);
}
XSRETURN_EMPTY;
}
XS(XS__crosszonesignalnpcbynpctypeid);
XS(XS__crosszonesignalnpcbynpctypeid)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: crosszonesignalnpcbynpctypeid(npctype_id, data)");
if (items == 2) {
uint32 npctype_id = (uint32)SvIV(ST(0));
uint32 data = (uint32)SvIV(ST(1));
quest_manager.CrossZoneSignalNPCByNPCTypeID(npctype_id, data);
}
XSRETURN_EMPTY;
}
/*
This is the callback perl will look for to setup the
quest package's XSUBs
@@ -3624,7 +3659,9 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "disablerecipe"), XS__disablerecipe, file);
newXS(strcpy(buf, "clear_npctype_cache"), XS__clear_npctype_cache, file);
newXS(strcpy(buf, "qs_send_query"), XS__qs_send_query, file);
newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file);
newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file);
newXS(strcpy(buf, "crosszonesetentityvariablebynpctypeid"), XS__crosszonesetentityvariablebynpctypeid, file);
newXS(strcpy(buf, "crosszonesignalnpcbynpctypeid"), XS__crosszonesignalnpcbynpctypeid, file);
XSRETURN_YES;
}
+61 -39
View File
@@ -40,7 +40,6 @@
#include "../common/spdat.h"
#include "../common/features.h"
#include "string_ids.h"
#include "../common/dbasync.h"
#include "guild_mgr.h"
#include "raids.h"
#include "quest_parser_collection.h"
@@ -57,7 +56,6 @@ extern WorldServer worldserver;
extern NetConnection net;
extern uint32 numclients;
extern PetitionList petition_list;
extern DBAsync *dbasync;
extern char errorname[32];
extern uint16 adverrornum;
@@ -65,12 +63,11 @@ extern uint16 adverrornum;
Entity::Entity()
{
id = 0;
pDBAsyncWorkID = 0;
}
Entity::~Entity()
{
dbasync->CancelWork(pDBAsyncWorkID);
}
Client *Entity::CastToClient()
@@ -493,23 +490,36 @@ void EntityList::MobProcess()
#endif
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (!it->second) {
uint16 id = it->first;
Mob *mob = it->second;
size_t sz = mob_list.size();
bool p_val = mob->Process();
size_t a_sz = mob_list.size();
if(a_sz > sz) {
//increased size can potentially screw with iterators so reset it to current value
//if buckets are re-orderered we may skip a process here and there but since
//process happens so often it shouldn't matter much
it = mob_list.find(id);
++it;
} else {
++it;
continue;
}
if (!it->second->Process()) {
Mob *mob = it->second;
uint16 tempid = it->first;
++it; // we don't erase here because the destructor will
if (mob->IsNPC()) {
entity_list.RemoveNPC(mob->CastToNPC()->GetID());
} else if (mob->IsMerc()) {
entity_list.RemoveMerc(mob->CastToMerc()->GetID());
if(!p_val) {
if(mob->IsNPC()) {
entity_list.RemoveNPC(id);
}
else if(mob->IsMerc()) {
entity_list.RemoveMerc(id);
#ifdef BOTS
} else if (mob->IsBot()) {
entity_list.RemoveBot(mob->CastToBot()->GetID());
}
else if(mob->IsBot()) {
entity_list.RemoveBot(id);
#endif
} else {
}
else {
#ifdef _WINDOWS
struct in_addr in;
in.s_addr = mob->CastToClient()->GetIP();
@@ -517,20 +527,19 @@ void EntityList::MobProcess()
#endif
zone->StartShutdownTimer();
Group *g = GetGroupByMob(mob);
if (g) {
if(g) {
LogFile->write(EQEMuLog::Error, "About to delete a client still in a group.");
g->DelMember(mob);
}
Raid *r = entity_list.GetRaidByClient(mob->CastToClient());
if (r) {
if(r) {
LogFile->write(EQEMuLog::Error, "About to delete a client still in a raid.");
r->MemberZoned(mob->CastToClient());
}
entity_list.RemoveClient(mob->GetID());
entity_list.RemoveClient(id);
}
entity_list.RemoveMob(tempid);
} else {
++it;
entity_list.RemoveMob(id);
}
}
}
@@ -1342,7 +1351,7 @@ void EntityList::RefreshClientXTargets(Client *c)
}
void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *app,
bool iSendToSender, Mob *SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits)
bool iSendToSender, Mob *SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits, bool inspect_buffs)
{
auto it = client_list.begin();
while (it != client_list.end()) {
@@ -1356,8 +1365,7 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap
Mob *TargetsTarget = nullptr;
if (Target)
TargetsTarget = Target->GetTarget();
TargetsTarget = Target->GetTarget();
bool Send = false;
@@ -1369,11 +1377,30 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap
Send = true;
if (c != sender) {
if (Target == sender)
Send = true;
else if (HoTT)
if (TargetsTarget == sender)
if (Target == sender) {
if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA
if (c->IsRaidGrouped()) {
Raid *raid = c->GetRaid();
if (!raid)
continue;
uint32 gid = raid->GetGroup(c);
if (gid > 11 || raid->GroupCount(gid) < 3)
continue;
if (raid->GetLeadershipAA(groupAAInspectBuffs, gid))
Send = true;
} else {
Group *group = c->GetGroup();
if (!group || group->GroupCount() < 3)
continue;
if (group->GetLeadershipAA(groupAAInspectBuffs))
Send = true;
}
} else {
Send = true;
}
} else if (HoTT && TargetsTarget == sender) {
Send = true;
}
}
if (Send && (c->GetClientVersionBit() & ClientVersionBits))
@@ -4111,15 +4138,10 @@ void EntityList::UnMarkNPC(uint16 ID)
// each group to remove the dead mobs entity ID from the groups list of NPCs marked via the
// Group Leadership AA Mark NPC ability.
//
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second) {
Group *g = nullptr;
g = it->second->GetGroup();
if (g)
g->UnMarkNPC(ID);
}
auto it = group_list.begin();
while (it != group_list.end()) {
if (*it)
(*it)->UnMarkNPC(ID);
++it;
}
}
+2 -4
View File
@@ -28,7 +28,6 @@
#include "zonedb.h"
#include "zonedump.h"
#include "zonedbasync.h"
#include "qglobals.h"
class EQApplicationPacket;
@@ -53,7 +52,7 @@ class Bot;
class BotRaids;
#endif
extern EntityList entity_list;
extern EntityList entity_list;
class Entity
{
@@ -100,7 +99,6 @@ public:
inline const uint16& GetID() const { return id; }
virtual const char* GetName() { return ""; }
virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { pDBAsyncWorkID = 0; }
bool CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk=1);
#ifdef BOTS
@@ -299,7 +297,7 @@ public:
void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0);
void QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID);
void QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, Mob* SkipThisMob = 0, bool ackreq = true,
bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF);
bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF, bool inspect_buffs = false);
void QueueClientsByXTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true);
void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app);
+51 -28
View File
@@ -51,14 +51,7 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level)
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
/* Set a timestamp in an entity variable for plugin check_handin.pl in return_items
This will stopgap players from items being returned if global_npc.pl has a catch all return_items
*/
struct timeval read_time;
char buffer[50];
gettimeofday(&read_time, 0);
sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec);
this->SetEntityVariable("Stop_Return", buffer);
this->EVENT_ITEM_ScriptStopReturn();
uint32 add_exp = in_add_exp;
@@ -140,30 +133,60 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
}
}
if(IsLeadershipEXPOn() && ((conlevel == CON_BLUE) || (conlevel == CON_WHITE) || (conlevel == CON_YELLOW) || (conlevel == CON_RED))) {
if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) {
add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);
if(GetGroup())
{
if((m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()))
&& (RuleI(Character, KillsPerGroupLeadershipAA) > 0))
{
AddLeadershipEXP(GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA), 0);
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
}
else
if (GetGroup()) {
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
Client *mentoree = GetGroup()->GetMentoree();
if (GetGroup()->GetMentorPercent() && mentoree &&
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f);
exp -= mentor_exp;
mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
}
if (exp > 0) { // possible if you mentor 100% to the other client
AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
}
} else {
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
}
else
{
if((m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel()))
&& (RuleI(Character, KillsPerRaidLeadershipAA) > 0))
{
AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
}
else
Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
} else {
Raid *raid = GetRaid();
// Raid leaders CAN NOT gain group AA XP, other group leaders can though!
if (raid->IsLeader(this)) {
if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())
&& RuleI(Character, KillsPerRaidLeadershipAA) > 0) {
AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
} else {
Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
}
} else {
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
uint32 group_id = raid->GetGroup(this);
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
Client *mentoree = raid->GetMentoree(group_id);
if (raid->GetMentorPercent(group_id) && mentoree &&
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f);
exp -= mentor_exp;
mentoree->AddLeadershipEXP(mentor_exp, 0);
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
}
if (exp > 0) {
AddLeadershipEXP(exp, 0);
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
}
} else {
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
}
}
}
}
+18 -31
View File
@@ -31,12 +31,10 @@
#define snprintf _snprintf
#endif
extern Zone* zone;
#define FEAR_PATHING_DEBUG
//this is called whenever we are damaged to process possible fleeing
void Mob::CheckFlee() {
//if were allready fleeing, dont need to check more...
@@ -55,7 +53,7 @@ void Mob::CheckFlee() {
float ratio = GetHPRatio();
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(ratio >= fleeratio)
return;
@@ -101,12 +99,13 @@ void Mob::CheckFlee() {
}
}
void Mob::ProcessFlee() {
void Mob::ProcessFlee()
{
//Stop fleeing if effect is applied after they start to run.
//When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee.
if(flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && !spellbonuses.IsFeared){
if (flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) &&
!spellbonuses.IsFeared && !spellbonuses.IsBlind) {
curfp = false;
return;
}
@@ -114,40 +113,42 @@ void Mob::ProcessFlee() {
//see if we are still dying, if so, do nothing
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(GetHPRatio() < fleeratio)
if (GetHPRatio() < fleeratio)
return;
//we are not dying anymore... see what we do next
flee_mode = false;
//see if we are legitimately feared now
if(!spellbonuses.IsFeared) {
//not feared... were done...
//see if we are legitimately feared or blind now
if (!spellbonuses.IsFeared && !spellbonuses.IsBlind) {
//not feared or blind... were done...
curfp = false;
return;
}
}
float Mob::GetFearSpeed() {
if(flee_mode) {
float Mob::GetFearSpeed()
{
if (flee_mode) {
//we know ratio < FLEE_HP_RATIO
float speed = GetBaseRunspeed();
float ratio = GetHPRatio();
float multiplier = RuleR(Combat, FleeMultiplier);
if(GetSnaredAmount() > 40)
if (GetSnaredAmount() > 40)
multiplier = multiplier / 6.0f;
speed = speed * ratio * multiplier / 100;
//NPC will eventually stop. Snares speeds this up.
if(speed < 0.09)
if (speed < 0.09)
speed = 0.0001f;
return(speed);
return speed;
}
return(GetRunspeed());
// fear and blind use their normal run speed
return GetRunspeed();
}
void Mob::CalculateNewFearpoint()
@@ -209,17 +210,3 @@ void Mob::CalculateNewFearpoint()
}
}
+112 -108
View File
@@ -46,6 +46,7 @@ Group::Group(uint32 gid)
: GroupIDConsumer(gid)
{
leader = nullptr;
mentoree = nullptr;
memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS);
AssistTargetID = 0;
TankTargetID = 0;
@@ -81,6 +82,7 @@ Group::Group(Mob* leader)
TankTargetID = 0;
PullerTargetID = 0;
memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct));
mentoree = nullptr;
uint32 i;
for(i=0;i<MAX_GROUP_MEMBERS;i++)
{
@@ -195,7 +197,7 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu
if (members[i] != nullptr && members[i]->IsClient()) { // If Group Member is Client
Client *c = members[i]->CastToClient();
//I could not get MoneyOnCorpse to work, so we use this
c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true);
c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true);
c->Message(2, msg.c_str());
}
}
@@ -467,6 +469,11 @@ bool Group::UpdatePlayer(Mob* update){
return true;
}
}
// mentoree isn't set, the name has a length and the name is ours! update the pointer
if (update->IsClient() && !mentoree && mentoree_name.length() && !mentoree_name.compare(update->GetName()))
mentoree = update->CastToClient();
return false;
}
@@ -500,6 +507,9 @@ void Group::MemberZoned(Mob* removemob) {
if(removemob->IsClient() && HasRole(removemob, RolePuller))
SetGroupPullerTarget(0);
if (removemob->IsClient() && removemob == mentoree)
mentoree = nullptr;
}
bool Group::DelMemberOOZ(const char *Name) {
@@ -528,6 +538,8 @@ bool Group::DelMemberOOZ(const char *Name) {
}
ClearAllNPCMarks();
}
if (Name == mentoree_name)
ClearGroupMentor();
return true;
}
}
@@ -642,6 +654,9 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender)
UnDelegatePuller(oldmember->GetName());
}
if (oldmember->GetName() == mentoree_name)
ClearGroupMentor();
if(oldmember->IsClient())
SendMarkedNPCsToMember(oldmember->CastToClient(), true);
@@ -969,31 +984,28 @@ void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float
}
bool Group::LearnMembers() {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT name FROM group_id WHERE groupid=%lu", (unsigned long)GetID()),
errbuf,&result)){
safe_delete_array(query);
if(mysql_num_rows(result) < 1) { //could prolly be 2
mysql_free_result(result);
LogFile->write(EQEMuLog::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), errbuf);
return(false);
}
int i = 0;
while((row = mysql_fetch_row(result))) {
if(!row[0])
continue;
members[i] = nullptr;
strn0cpy(membername[i], row[0], 64);
std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %lu", (unsigned long)GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
return false;
i++;
}
mysql_free_result(result);
if (results.RowCount() == 0) {
LogFile->write(EQEMuLog::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str());
return false;
}
int memberIndex = 0;
for(auto row = results.begin(); row != results.end(); ++row) {
if(!row[0])
continue;
members[memberIndex] = nullptr;
strn0cpy(membername[memberIndex], row[0], 64);
memberIndex++;
}
return(true);
return true;
}
void Group::VerifyGroup() {
@@ -1077,7 +1089,7 @@ void Group::HealGroup(uint32 heal_amt, Mob* caster, int32 range)
if (!range)
range = 200;
float distance;
float range2 = range*range;
@@ -1114,10 +1126,10 @@ void Group::BalanceHP(int32 penalty, int32 range, Mob* caster, int32 limit)
return;
if (!range)
range = 200;
range = 200;
int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0;
float distance;
float range2 = range*range;
@@ -1164,16 +1176,16 @@ void Group::BalanceMana(int32 penalty, int32 range, Mob* caster, int32 limit)
return;
if (!range)
range = 200;
range = 200;
float distance;
float range2 = range*range;
int manataken = 0, numMem = 0, manataken_tmp = 0;
unsigned int gi = 0;
for(; gi < MAX_GROUP_MEMBERS; gi++)
{
if(members[gi] && (members[gi]->GetMaxMana() > 0)){
if(members[gi] && (members[gi]->GetMaxMana() > 0)){
distance = caster->DistNoRoot(*members[gi]);
if(distance <= range2){
@@ -1341,15 +1353,12 @@ void Group::DelegateMainTank(const char *NewMainTankName, uint8 toggle)
}
if(updateDB) {
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = nullptr;
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='%s' WHERE gid=%i LIMIT 1",
MainTankName.c_str(), GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to set group main tank: %s\n", errbuff);
safe_delete_array(Query);
std::string query = StringFormat("UPDATE group_leaders SET maintank = '%s' WHERE gid = %i LIMIT 1",
MainTankName.c_str(), GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to set group main tank: %s\n", results.ErrorMessage().c_str());
}
}
@@ -1390,15 +1399,13 @@ void Group::DelegateMainAssist(const char *NewMainAssistName, uint8 toggle)
}
if(updateDB) {
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = nullptr;
std::string query = StringFormat("UPDATE group_leaders SET assist = '%s' WHERE gid = %i LIMIT 1",
MainAssistName.c_str(), GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to set group main assist: %s\n", results.ErrorMessage().c_str());
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='%s' WHERE gid=%i LIMIT 1",
MainAssistName.c_str(), GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to set group main assist: %s\n", errbuff);
safe_delete_array(Query);
}
}
@@ -1439,15 +1446,13 @@ void Group::DelegatePuller(const char *NewPullerName, uint8 toggle)
}
if(updateDB) {
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = nullptr;
std::string query = StringFormat("UPDATE group_leaders SET puller = '%s' WHERE gid = %i LIMIT 1",
PullerName.c_str(), GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to set group main puller: %s\n", results.ErrorMessage().c_str());
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='%s' WHERE gid=%i LIMIT 1",
PullerName.c_str(), GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to set group main puller: %s\n", errbuff);
safe_delete_array(Query);
}
}
@@ -1593,15 +1598,11 @@ void Group::UnDelegateMainTank(const char *OldMainTankName, uint8 toggle)
// informing them of the change and update the group_leaders table.
//
if(OldMainTankName == MainTankName) {
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = 0;
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='' WHERE gid=%i LIMIT 1",
GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to clear group main tank: %s\n", errbuff);
safe_delete_array(Query);
std::string query = StringFormat("UPDATE group_leaders SET maintank = '' WHERE gid = %i LIMIT 1", GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to clear group main tank: %s\n", results.ErrorMessage().c_str());
if(!toggle) {
for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) {
@@ -1647,15 +1648,10 @@ void Group::UnDelegateMainAssist(const char *OldMainAssistName, uint8 toggle)
safe_delete(outapp);
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = 0;
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='' WHERE gid=%i LIMIT 1",
GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to clear group main assist: %s\n", errbuff);
safe_delete_array(Query);
std::string query = StringFormat("UPDATE group_leaders SET assist = '' WHERE gid = %i LIMIT 1", GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to clear group main assist: %s\n", results.ErrorMessage().c_str());
if(!toggle)
{
@@ -1679,15 +1675,11 @@ void Group::UnDelegatePuller(const char *OldPullerName, uint8 toggle)
// informing them of the change and update the group_leaders table.
//
if(OldPullerName == PullerName) {
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = 0;
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='' WHERE gid=%i LIMIT 1",
GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to clear group main puller: %s\n", errbuff);
safe_delete_array(Query);
std::string query = StringFormat("UPDATE group_leaders SET puller = '' WHERE gid = %i LIMIT 1", GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to clear group main puller: %s\n", results.ErrorMessage().c_str());
if(!toggle) {
for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) {
@@ -1759,6 +1751,31 @@ void Group::SetGroupPullerTarget(Mob *m)
}
}
void Group::SetGroupMentor(int percent, char *name)
{
mentoree_name = name;
mentor_percent = percent;
Client *client = entity_list.GetClientByName(name);
mentoree = client ? client : nullptr;
std::string query = StringFormat("UPDATE group_leaders SET mentoree = '%s', mentor_percent = %i WHERE gid = %i LIMIT 1",
mentoree_name.c_str(), mentor_percent, GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to set group mentor: %s\n", results.ErrorMessage().c_str());
}
void Group::ClearGroupMentor()
{
mentoree_name.clear();
mentor_percent = 0;
mentoree = nullptr;
std::string query = StringFormat("UPDATE group_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i LIMIT 1", GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to clear group mentor: %s\n", results.ErrorMessage().c_str());
}
void Group::NotifyAssistTarget(Client *c)
{
// Send a packet to the specified client notifying them of the group target selected by the Main Assist.
@@ -1822,16 +1839,11 @@ void Group::DelegateMarkNPC(const char *NewNPCMarkerName)
if(members[i] && members[i]->IsClient())
NotifyMarkNPC(members[i]->CastToClient());
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = 0;
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='%s' WHERE gid=%i LIMIT 1",
NewNPCMarkerName, GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to set group mark npc: %s\n", errbuff);
safe_delete_array(Query);
std::string query = StringFormat("UPDATE group_leaders SET marknpc = '%s' WHERE gid = %i LIMIT 1",
NewNPCMarkerName, GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to set group mark npc: %s\n", results.ErrorMessage().c_str());
}
void Group::NotifyMarkNPC(Client *c)
@@ -1908,37 +1920,29 @@ void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName)
NPCMarkerName.clear();
char errbuff[MYSQL_ERRMSG_SIZE];
char *Query = 0;
std::string query = StringFormat("UPDATE group_leaders SET marknpc = '' WHERE gid = %i LIMIT 1", GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to clear group marknpc: %s\n", results.ErrorMessage().c_str());
if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='' WHERE gid=%i LIMIT 1",
GetID()), errbuff))
LogFile->write(EQEMuLog::Error, "Unable to clear group marknpc: %s\n", errbuff);
safe_delete_array(Query);
}
void Group::SaveGroupLeaderAA()
{
// Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table.
// This is done so that group members not in the same zone as the Leader still have access to this information.
char *queryBuffer = new char[sizeof(GroupLeadershipAA_Struct) * 2 + 1];
database.DoEscapeString(queryBuffer, (char*)&LeaderAbilities, sizeof(GroupLeadershipAA_Struct));
char *Query = new char[200 + sizeof(GroupLeadershipAA_Struct)*2];
std::string query = "UPDATE group_leaders SET leadershipaa = '";
query += queryBuffer;
query += StringFormat("' WHERE gid = %i LIMIT 1", GetID());
safe_delete_array(queryBuffer);
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str());
char *End = Query;
End += sprintf(End, "UPDATE group_leaders SET leadershipaa='");
End += database.DoEscapeString(End, (char*)&LeaderAbilities, sizeof(GroupLeadershipAA_Struct));
End += sprintf(End,"' WHERE gid=%i LIMIT 1", GetID());
char errbuff[MYSQL_ERRMSG_SIZE];
if (!database.RunQuery(Query, End - Query, errbuff))
LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", errbuff);
safe_delete_array(Query);
}
void Group::UnMarkNPC(uint16 ID)
+8
View File
@@ -132,6 +132,11 @@ public:
const char *GetClientNameByIndex(uint8 index);
void UpdateXTargetMarkedNPC(uint32 Number, Mob *m);
void SetGroupMentor(int percent, char *name);
void ClearGroupMentor();
inline int GetMentorPercent() { return mentor_percent; }
inline Client *GetMentoree() { return mentoree; }
Mob* members[MAX_GROUP_MEMBERS];
char membername[MAX_GROUP_MEMBERS][64];
uint8 MemberRoles[MAX_GROUP_MEMBERS];
@@ -151,6 +156,9 @@ private:
uint16 PullerTargetID;
uint16 MarkedNPCs[MAX_MARKED_NPCS];
std::string mentoree_name;
Client *mentoree;
int mentor_percent;
};
#endif
-53
View File
@@ -24,59 +24,6 @@
#include "client.h"
#include "entity.h"
/*
CREATE TABLE guilds (
id MEDIUMINT UNSIGNED NOT NULL,
name VARCHAR(32) NOT NULL,
leader int NOT NULL,
minstatus SMALLINT NOT NULL,
tribute INT UNSIGNED NOT NULL,
motd TEXT NOT NULL DEFAULT '',
PRIMARY KEY(id),
UNIQUE KEY(name),
UNIQUE KEY(leader)
);
CREATE TABLE guild_ranks (
guild_id MEDIUMINT UNSIGNED NOT NULL,
rank TINYINT UNSIGNED NOT NULL,
title VARCHAR(128) NOT NULL,
can_hear TINYINT UNSIGNED NOT NULL,
can_speak TINYINT UNSIGNED NOT NULL,
can_invite TINYINT UNSIGNED NOT NULL,
can_remove TINYINT UNSIGNED NOT NULL,
can_promote TINYINT UNSIGNED NOT NULL,
can_demote TINYINT UNSIGNED NOT NULL,
can_motd TINYINT UNSIGNED NOT NULL,
can_warpeace TINYINT UNSIGNED NOT NULL,
PRIMARY KEY(guild_id,rank)
);
# guild1 < guild2 by definition.
CREATE TABLE guild_relations (
guild1 MEDIUMINT UNSIGNED NOT NULL,
guild2 MEDIUMINT UNSIGNED NOT NULL,
relation TINYINT NOT NULL,
PRIMARY KEY(guild1, guild1)
);
CREATE TABLE guild_members (
char_id INT NOT NULL,
guild_id MEDIUMINT UNSIGNED NOT NULL,
rank TINYINT UNSIGNED NOT NULL,
tribute_enable TINYINT UNSIGNED NOT NULL DEFAULT 0,
total_tribute INT UNSIGNED NOT NULL DEFAULT 0,
last_tribute INT UNSIGNED NOT NULL DEFAULT 0,
banker TINYINT UNSIGNED NOT NULL DEFAULT 0,
public_note TEXT NOT NULL DEFAULT '',
PRIMARY KEY(char_id)
);
*/
ZoneGuildManager guild_mgr;
GuildBankManager *GuildBanks;
+10 -20
View File
@@ -200,14 +200,7 @@ bool Client::CheckLoreConflict(const Item_Struct* item) {
}
bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) {
/* Set a timestamp in an entity variable for plugin check_handin.pl in return_items
This will stopgap players from items being returned if global_npc.pl has a catch all return_items
*/
struct timeval read_time;
char buffer[50];
gettimeofday(&read_time, 0);
sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec);
this->SetEntityVariable("Recieved_Item", buffer);
this->EVENT_ITEM_ScriptStopReturn();
// TODO: update calling methods and script apis to handle a failure return
@@ -1877,7 +1870,9 @@ void Client::DyeArmor(DyeStruct* dye){
uint8 slot2=SlotConvert(i);
ItemInst* inst = this->m_inv.GetItem(slot2);
if(inst){
inst->SetColor((dye->dye[i].rgb.red*65536)+(dye->dye[i].rgb.green*256)+(dye->dye[i].rgb.blue));
uint32 armor_color = (dye->dye[i].rgb.red * 65536) + (dye->dye[i].rgb.green * 256) + (dye->dye[i].rgb.blue);
inst->SetColor(armor_color);
database.SaveCharacterMaterialColor(this->CharacterID(), i, armor_color);
database.SaveInventory(CharacterID(),inst,slot2);
if(dye->dye[i].rgb.use_tint)
m_pp.item_tint[i].rgb.use_tint = 0xFF;
@@ -1898,7 +1893,7 @@ void Client::DyeArmor(DyeStruct* dye){
EQApplicationPacket* outapp=new EQApplicationPacket(OP_Dye,0);
QueuePacket(outapp);
safe_delete(outapp);
Save();
}
/*bool Client::DecreaseByItemType(uint32 type, uint8 amt) {
@@ -2417,10 +2412,8 @@ void Client::CreateBandolier(const EQApplicationPacket *app) {
_log(INVENTORY__BANDOLIER, "Char: %s Creating Bandolier Set %i, Set Name: %s", GetName(), bs->number, bs->name);
strcpy(m_pp.bandoliers[bs->number].name, bs->name);
const ItemInst* InvItem;
const Item_Struct *BaseItem;
const ItemInst* InvItem;
const Item_Struct *BaseItem;
int16 WeaponSlot;
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
@@ -2431,6 +2424,7 @@ void Client::CreateBandolier(const EQApplicationPacket *app) {
_log(INVENTORY__BANDOLIER, "Char: %s adding item %s to slot %i", GetName(),BaseItem->Name, WeaponSlot);
m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = BaseItem->ID;
m_pp.bandoliers[bs->number].items[BandolierSlot].icon = BaseItem->Icon;
database.SaveCharacterBandolier(this->CharacterID(), bs->number, BandolierSlot, m_pp.bandoliers[bs->number].items[BandolierSlot].item_id, m_pp.bandoliers[bs->number].items[BandolierSlot].icon, bs->name);
}
else {
_log(INVENTORY__BANDOLIER, "Char: %s no item in slot %i", GetName(), WeaponSlot);
@@ -2438,21 +2432,17 @@ void Client::CreateBandolier(const EQApplicationPacket *app) {
m_pp.bandoliers[bs->number].items[BandolierSlot].icon = 0;
}
}
Save();
}
void Client::RemoveBandolier(const EQApplicationPacket *app) {
// Delete bandolier with the specified number
BandolierDelete_Struct *bds = (BandolierDelete_Struct*)app->pBuffer;
_log(INVENTORY__BANDOLIER, "Char: %s removing set", GetName(), bds->number);
memset(m_pp.bandoliers[bds->number].name, 0, 32);
for(int i = bandolierMainHand; i <= bandolierAmmo; i++) {
m_pp.bandoliers[bds->number].items[i].item_id = 0;
m_pp.bandoliers[bds->number].items[i].icon = 0;
m_pp.bandoliers[bds->number].items[i].icon = 0;
}
Save();
database.DeleteCharacterBandolier(this->CharacterID(), bds->number);
}
void Client::SetBandolier(const EQApplicationPacket *app) {
+6
View File
@@ -1239,6 +1239,11 @@ void Lua_Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in
self->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg);
}
void Lua_Client::SendColoredText(uint32 type, std::string msg) {
Lua_Safe_Call_Void();
self->SendColoredText(type, msg);
}
void Lua_Client::PlayMP3(std::string file)
{
Lua_Safe_Call_Void();
@@ -1492,6 +1497,7 @@ luabind::scope lua_register_client() {
.def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst)
.def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption)
.def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage)
.def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText)
.def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3);
}
+1
View File
@@ -275,6 +275,7 @@ public:
void SetThirst(int in_thirst);
void SetConsumption(int in_hunger, int in_thirst);
void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg);
void SendColoredText(uint32 type, std::string msg);
void PlayMP3(std::string file);
};
+13 -1
View File
@@ -407,8 +407,18 @@ void Lua_NPC::SetSpellFocusHeal(int focus) {
self->SetSpellFocusHeal(focus);
}
float Lua_NPC::GetSlowMitigation() {
int Lua_NPC::GetSpellFocusDMG() {
Lua_Safe_Call_Int();
return self->GetSpellFocusDMG();
}
int Lua_NPC::GetSpellFocusHeal() {
Lua_Safe_Call_Int();
return self->GetSpellFocusHeal();
}
float Lua_NPC::GetSlowMitigation() {
Lua_Safe_Call_Real();
return self->GetSlowMitigation();
}
@@ -535,6 +545,8 @@ luabind::scope lua_register_npc() {
.def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell)
.def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG)
.def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal)
.def("GetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusDMG)
.def("GetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusHeal)
.def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation)
.def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed)
.def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating)
+2
View File
@@ -107,6 +107,8 @@ public:
void RemoveAISpell(int spell_id);
void SetSpellFocusDMG(int focus);
void SetSpellFocusHeal(int focus);
int GetSpellFocusDMG();
int GetSpellFocusHeal();
float GetSlowMitigation();
float GetAttackSpeed();
int GetAccuracyRating();
+44 -6
View File
@@ -14,14 +14,31 @@ Lua_Packet::Lua_Packet(int opcode, int size) {
owned_ = true;
}
Lua_Packet::Lua_Packet(int opcode, int size, bool raw) {
if(raw) {
SetLuaPtrData(new EQApplicationPacket(OP_Unknown, size));
owned_ = true;
EQApplicationPacket *self = reinterpret_cast<EQApplicationPacket*>(d_);
self->SetOpcodeBypass(opcode);
} else {
SetLuaPtrData(new EQApplicationPacket(static_cast<EmuOpcode>(opcode), size));
owned_ = true;
}
}
Lua_Packet& Lua_Packet::operator=(const Lua_Packet& o) {
if(o.owned_) {
owned_ = true;
EQApplicationPacket *app = reinterpret_cast<EQApplicationPacket*>(o.d_);
if(app)
if(app) {
d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size);
else
EQApplicationPacket *self = reinterpret_cast<EQApplicationPacket*>(d_);
self->SetOpcodeBypass(app->GetOpcodeBypass());
} else {
d_ = nullptr;
}
} else {
owned_ = false;
d_ = o.d_;
@@ -33,10 +50,14 @@ Lua_Packet::Lua_Packet(const Lua_Packet& o) {
if(o.owned_) {
owned_ = true;
EQApplicationPacket *app = reinterpret_cast<EQApplicationPacket*>(o.d_);
if(app)
if(app) {
d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size);
else
EQApplicationPacket *self = reinterpret_cast<EQApplicationPacket*>(d_);
self->SetOpcodeBypass(app->GetOpcodeBypass());
} else {
d_ = nullptr;
}
} else {
owned_ = false;
d_ = o.d_;
@@ -54,6 +75,16 @@ int Lua_Packet::GetOpcode() {
}
void Lua_Packet::SetOpcode(int op) {
Lua_Safe_Call_Void();
self->SetOpcodeBypass(static_cast<uint16>(op));
}
int Lua_Packet::GetRawOpcode() {
Lua_Safe_Call_Int();
return static_cast<int>(self->GetOpcodeBypass());
}
void Lua_Packet::SetRawOpcode(int op) {
Lua_Safe_Call_Void();
self->SetOpcode(static_cast<EmuOpcode>(op));
}
@@ -244,11 +275,14 @@ luabind::scope lua_register_packet() {
return luabind::class_<Lua_Packet>("Packet")
.def(luabind::constructor<>())
.def(luabind::constructor<int,int>())
.def(luabind::constructor<int,int,bool>())
.property("null", &Lua_Packet::Null)
.property("valid", &Lua_Packet::Valid)
.def("GetSize", &Lua_Packet::GetSize)
.def("GetOpcode", &Lua_Packet::GetOpcode)
.def("SetOpcode", &Lua_Packet::SetOpcode)
.def("GetRawOpcode", &Lua_Packet::GetRawOpcode)
.def("SetRawOpcode", &Lua_Packet::SetRawOpcode)
.def("WriteInt8", &Lua_Packet::WriteInt8)
.def("WriteInt16", &Lua_Packet::WriteInt16)
.def("WriteInt32", &Lua_Packet::WriteInt32)
@@ -267,6 +301,7 @@ luabind::scope lua_register_packet() {
.def("ReadFixedLengthString", &Lua_Packet::ReadFixedLengthString);
}
//TODO: Reorder these to match emu_oplist.h again
luabind::scope lua_register_packet_opcodes() {
return luabind::class_<Opcodes>("Opcode")
.enum_("constants")
@@ -412,7 +447,7 @@ luabind::scope lua_register_packet_opcodes() {
luabind::value("YellForHelp", static_cast<int>(OP_YellForHelp)),
luabind::value("SafePoint", static_cast<int>(OP_SafePoint)),
luabind::value("Buff", static_cast<int>(OP_Buff)),
luabind::value("BuffFadeMsg", static_cast<int>(OP_BuffFadeMsg)),
luabind::value("ColoredText", static_cast<int>(OP_ColoredText)),
luabind::value("SpecialMesg", static_cast<int>(OP_SpecialMesg)),
luabind::value("Consent", static_cast<int>(OP_Consent)),
luabind::value("ConsentResponse", static_cast<int>(OP_ConsentResponse)),
@@ -809,7 +844,10 @@ luabind::scope lua_register_packet_opcodes() {
luabind::value("MercenaryDismiss", static_cast<int>(OP_MercenaryDismiss)),
luabind::value("MercenaryTimerRequest", static_cast<int>(OP_MercenaryTimerRequest)),
luabind::value("OpenInventory", static_cast<int>(OP_OpenInventory)),
luabind::value("OpenContainer", static_cast<int>(OP_OpenContainer))
luabind::value("OpenContainer", static_cast<int>(OP_OpenContainer)),
luabind::value("Marquee", static_cast<int>(OP_Marquee)),
luabind::value("ClientTimeStamp", static_cast<int>(OP_ClientTimeStamp)),
luabind::value("GuildPromote", static_cast<int>(OP_GuildPromote))
];
}
+3
View File
@@ -21,6 +21,7 @@ public:
Lua_Packet() : Lua_Ptr(nullptr), owned_(false) { }
Lua_Packet(EQApplicationPacket *d) : Lua_Ptr(d), owned_(false) { }
Lua_Packet(int opcode, int size);
Lua_Packet(int opcode, int size, bool raw);
Lua_Packet& operator=(const Lua_Packet& o);
Lua_Packet(const Lua_Packet& o);
virtual ~Lua_Packet() { if(owned_) { EQApplicationPacket *ptr = GetLuaPtrData(); if(ptr) { delete ptr; } } }
@@ -28,6 +29,8 @@ public:
int GetSize();
int GetOpcode();
void SetOpcode(int op);
int GetRawOpcode();
void SetRawOpcode(int op);
void WriteInt8(int offset, int value);
void WriteInt16(int offset, int value);
void WriteInt32(int offset, int value);
+60
View File
@@ -419,6 +419,56 @@ bool Lua_Spell::GetAllowRest() {
return self->AllowRest;
}
bool Lua_Spell::GetInCombat() {
Lua_Safe_Call_Bool();
return self->InCombat;
}
bool Lua_Spell::GetOutOfCombat() {
Lua_Safe_Call_Bool();
return self->OutofCombat;
}
int Lua_Spell::GetAEMaxTargets() {
Lua_Safe_Call_Int();
return self->aemaxtargets;
}
int Lua_Spell::GetMaxTargets() {
Lua_Safe_Call_Int();
return self->maxtargets;
}
bool Lua_Spell::GetPersistDeath() {
Lua_Safe_Call_Bool();
return self->persistdeath;
}
float Lua_Spell::GetMinDist() {
Lua_Safe_Call_Real();
return self->min_dist;
}
float Lua_Spell::GetMinDistMod() {
Lua_Safe_Call_Real();
return self->min_dist_mod;
}
float Lua_Spell::GetMaxDist() {
Lua_Safe_Call_Real();
return self->max_dist;
}
float Lua_Spell::GetMaxDistMod() {
Lua_Safe_Call_Real();
return self->max_dist_mod;
}
float Lua_Spell::GetMinRange() {
Lua_Safe_Call_Real();
return self->min_range;
}
int Lua_Spell::GetDamageShieldType() {
Lua_Safe_Call_Int();
return self->DamageShieldType;
@@ -501,6 +551,16 @@ luabind::scope lua_register_spell() {
.def("PowerfulFlag", &Lua_Spell::GetPowerfulFlag)
.def("CastRestriction", &Lua_Spell::GetCastRestriction)
.def("AllowRest", &Lua_Spell::GetAllowRest)
.def("InCombat", &Lua_Spell::GetInCombat)
.def("OutOfCombat", &Lua_Spell::GetOutOfCombat)
.def("AEMaxTargets", &Lua_Spell::GetAEMaxTargets)
.def("MaxTargets", &Lua_Spell::GetMaxTargets)
.def("PersistDeath", &Lua_Spell::GetPersistDeath)
.def("MinDist", &Lua_Spell::GetMinDist)
.def("MinDistMod", &Lua_Spell::GetMinDistMod)
.def("MaxDist", &Lua_Spell::GetMaxDist)
.def("MaxDistMod", &Lua_Spell::GetMaxDistMod)
.def("MinRange", &Lua_Spell::GetMinRange)
.def("DamageShieldType", &Lua_Spell::GetDamageShieldType);
}
+10
View File
@@ -96,6 +96,16 @@ public:
int GetPowerfulFlag();
int GetCastRestriction();
bool GetAllowRest();
bool GetInCombat();
bool GetOutOfCombat();
int GetAEMaxTargets();
int GetMaxTargets();
bool GetPersistDeath();
float GetMinDist();
float GetMinDistMod();
float GetMaxDist();
float GetMaxDistMod();
float GetMinRange();
int GetDamageShieldType();
};
+2 -8
View File
@@ -4619,13 +4619,7 @@ void Merc::DoClassAttacks(Mob *target) {
if(!ca_time)
return;
float HasteModifier = 0;
if(GetHaste() > 0)
HasteModifier = 10000 / (100 + GetHaste());
else if(GetHaste() < 0)
HasteModifier = (100 - GetHaste());
else
HasteModifier = 100;
float HasteModifier = GetHaste() * 0.01f;
int level = GetLevel();
int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will
@@ -4689,7 +4683,7 @@ void Merc::DoClassAttacks(Mob *target) {
}
}
classattack_timer.Start(reuse*HasteModifier/100);
classattack_timer.Start(reuse / HasteModifier);
}
bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
+153 -48
View File
@@ -1932,6 +1932,7 @@ void Mob::SetZone(uint32 zone_id, uint32 instance_id)
{
CastToClient()->GetPP().zone_id = zone_id;
CastToClient()->GetPP().zoneInstance = instance_id;
CastToClient()->Save();
}
Save();
}
@@ -2734,12 +2735,29 @@ uint32 Mob::GetZoneID() const {
return(zone->GetZoneID());
}
int Mob::GetHaste() {
int h = spellbonuses.haste + spellbonuses.hastetype2;
int Mob::GetHaste()
{
// See notes in Client::CalcHaste
// Need to check if the effect of inhibit melee differs for NPCs
if (spellbonuses.haste < 0) {
if (-spellbonuses.haste <= spellbonuses.inhibitmelee)
return 100 - spellbonuses.inhibitmelee;
else
return 100 + spellbonuses.haste;
}
if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee)
return 100 - spellbonuses.inhibitmelee;
int h = 0;
int cap = 0;
int overhaste = 0;
int level = GetLevel();
if (spellbonuses.haste)
h += spellbonuses.haste - spellbonuses.inhibitmelee;
if (spellbonuses.hastetype2 && level > 49)
h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2;
// 26+ no cap, 1-25 10
if (level > 25) // 26+
h += itembonuses.haste;
@@ -2759,21 +2777,13 @@ int Mob::GetHaste() {
// 51+ 25 (despite there being higher spells...), 1-50 10
if (level > 50) // 51+
overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
else // 1-50
overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
h += overhaste;
h += ExtraHaste; //GM granted haste.
if (spellbonuses.inhibitmelee) {
if (h >= 0)
h -= spellbonuses.inhibitmelee;
else
h -= ((100 + h) * spellbonuses.inhibitmelee / 100);
}
return(h);
return 100 + h;
}
void Mob::SetTarget(Mob* mob) {
@@ -3030,12 +3040,11 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
}
}
void Mob::TrySpellTrigger(Mob *target, uint32 spell_id)
bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect)
{
if(target == nullptr || !IsValidSpell(spell_id))
{
return;
}
if(!target || !IsValidSpell(spell_id))
return false;
int spell_trig = 0;
// Count all the percentage chances to trigger for all effects
for(int i = 0; i < EFFECT_COUNT; i++)
@@ -3054,8 +3063,10 @@ void Mob::TrySpellTrigger(Mob *target, uint32 spell_id)
if(MakeRandomInt(0, trig_chance) <= spells[spell_id].base[i])
{
// If we trigger an effect then its over.
SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff);
break;
if (IsValidSpell(spells[spell_id].base2[i])){
SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
return true;
}
}
else
{
@@ -3069,37 +3080,15 @@ void Mob::TrySpellTrigger(Mob *target, uint32 spell_id)
// if the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well.
else
{
for(int i = 0; i < EFFECT_COUNT; i++)
if(MakeRandomInt(0, 100) <= spells[spell_id].base[effect])
{
if (spells[spell_id].effectid[i] == SE_SpellTrigger)
{
if(MakeRandomInt(0, 100) <= spells[spell_id].base[i])
{
SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff);
}
}
}
}
}
void Mob::TryApplyEffect(Mob *target, uint32 spell_id)
{
if(target == nullptr || !IsValidSpell(spell_id))
{
return;
}
for(int i = 0; i < EFFECT_COUNT; i++)
{
if (spells[spell_id].effectid[i] == SE_ApplyEffect)
{
if(MakeRandomInt(0, 100) <= spells[spell_id].base[i])
{
if(target)
SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff);
if (IsValidSpell(spells[spell_id].base2[effect])){
SpellFinished(spells[spell_id].base2[effect], target, 10, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff);
return true; //Only trigger once of these per spell effect.
}
}
}
return false;
}
void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet)
@@ -5013,3 +5002,119 @@ float Mob::HeadingAngleToMob(Mob *other)
return (90.0 - angle + 270.0) * 511.5 * 0.0027777778;
}
int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot)
{
if (!IsValidSpell(spell_id))
return 0;
if (!identifier)
return 0;
int32 stat = 0;
if (slot > 0)
slot = slot - 1;
std::string id = identifier;
for(int i = 0; i < id.length(); ++i)
{
id[i] = tolower(id[i]);
}
if (slot < 16){
if (id == "classes") {stat = spells[spell_id].classes[slot]; }
else if (id == "dieties") {stat = spells[spell_id].deities[slot];}
}
if (slot < 12){
if (id == "base") {stat = spells[spell_id].base[slot];}
else if (id == "base2") {stat = spells[spell_id].base2[slot];}
else if (id == "max") {stat = spells[spell_id].max[slot];}
else if (id == "formula") {spells[spell_id].formula[slot];}
else if (id == "effectid") {spells[spell_id].effectid[slot];}
}
if (slot < 4){
if (id == "components") { spells[spell_id].components[slot];}
else if (id == "component_counts") {spells[spell_id].component_counts[slot];}
else if (id == "NoexpendReagent") {spells[spell_id].NoexpendReagent[slot];}
}
if (id == "range") {stat = spells[spell_id].range; }
else if (id == "aoerange") {stat = spells[spell_id].aoerange;}
else if (id == "pushback") {stat = spells[spell_id].pushback;}
else if (id == "pushup") {stat = spells[spell_id].pushup;}
else if (id == "cast_time") {stat = spells[spell_id].cast_time;}
else if (id == "recovery_time") {stat = spells[spell_id].recovery_time;}
else if (id == "recast_time") {stat = spells[spell_id].recast_time;}
else if (id == "buffdurationformula") {stat = spells[spell_id].buffdurationformula;}
else if (id == "buffduration") {stat = spells[spell_id].buffduration;}
else if (id == "AEDuration") {stat = spells[spell_id].AEDuration;}
else if (id == "mana") {stat = spells[spell_id].mana;}
//else if (id == "LightType") {stat = spells[spell_id].LightType;} - Not implemented
else if (id == "goodEffect") {stat = spells[spell_id].goodEffect;}
else if (id == "Activated") {stat = spells[spell_id].Activated;}
else if (id == "resisttype") {stat = spells[spell_id].resisttype;}
else if (id == "targettype") {stat = spells[spell_id].targettype;}
else if (id == "basedeiff") {stat = spells[spell_id].basediff;}
else if (id == "skill") {stat = spells[spell_id].skill;}
else if (id == "zonetype") {stat = spells[spell_id].zonetype;}
else if (id == "EnvironmentType") {stat = spells[spell_id].EnvironmentType;}
else if (id == "TimeOfDay") {stat = spells[spell_id].TimeOfDay;}
else if (id == "CastingAnim") {stat = spells[spell_id].CastingAnim;}
else if (id == "SpellAffectIndex") {stat = spells[spell_id].SpellAffectIndex; }
else if (id == "disallow_sit") {stat = spells[spell_id].disallow_sit; }
//else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented
else if (id == "uninterruptable") {stat = spells[spell_id].uninterruptable; }
else if (id == "ResistDiff") {stat = spells[spell_id].ResistDiff; }
else if (id == "dot_stacking_exemp") {stat = spells[spell_id].dot_stacking_exempt; }
else if (id == "RecourseLink") {stat = spells[spell_id].RecourseLink; }
else if (id == "no_partial_resist") {stat = spells[spell_id].no_partial_resist; }
else if (id == "short_buff_box") {stat = spells[spell_id].short_buff_box; }
else if (id == "descnum") {stat = spells[spell_id].descnum; }
else if (id == "effectdescnum") {stat = spells[spell_id].effectdescnum; }
else if (id == "npc_no_los") {stat = spells[spell_id].npc_no_los; }
else if (id == "reflectable") {stat = spells[spell_id].reflectable; }
else if (id == "bonushate") {stat = spells[spell_id].bonushate; }
else if (id == "EndurCost") {stat = spells[spell_id].EndurCost; }
else if (id == "EndurTimerIndex") {stat = spells[spell_id].EndurTimerIndex; }
else if (id == "IsDisciplineBuf") {stat = spells[spell_id].IsDisciplineBuff; }
else if (id == "HateAdded") {stat = spells[spell_id].HateAdded; }
else if (id == "EndurUpkeep") {stat = spells[spell_id].EndurUpkeep; }
else if (id == "numhitstype") {stat = spells[spell_id].numhitstype; }
else if (id == "numhits") {stat = spells[spell_id].numhits; }
else if (id == "pvpresistbase") {stat = spells[spell_id].pvpresistbase; }
else if (id == "pvpresistcalc") {stat = spells[spell_id].pvpresistcalc; }
else if (id == "pvpresistcap") {stat = spells[spell_id].pvpresistcap; }
else if (id == "spell_category") {stat = spells[spell_id].spell_category; }
else if (id == "can_mgb") {stat = spells[spell_id].can_mgb; }
else if (id == "dispel_flag") {stat = spells[spell_id].dispel_flag; }
else if (id == "MinResist") {stat = spells[spell_id].MinResist; }
else if (id == "MaxResist") {stat = spells[spell_id].MaxResist; }
else if (id == "viral_targets") {stat = spells[spell_id].viral_targets; }
else if (id == "viral_timer") {stat = spells[spell_id].viral_timer; }
else if (id == "NimbusEffect") {stat = spells[spell_id].NimbusEffect; }
else if (id == "directional_start") {stat = spells[spell_id].directional_start; }
else if (id == "directional_end") {stat = spells[spell_id].directional_end; }
else if (id == "not_extendable") {stat = spells[spell_id].not_extendable; }
else if (id == "suspendable") {stat = spells[spell_id].suspendable; }
else if (id == "viral_range") {stat = spells[spell_id].viral_range; }
else if (id == "spellgroup") {stat = spells[spell_id].spellgroup; }
else if (id == "rank") {stat = spells[spell_id].rank; }
else if (id == "powerful_flag") {stat = spells[spell_id].powerful_flag; }
else if (id == "CastRestriction") {stat = spells[spell_id].CastRestriction; }
else if (id == "AllowRest") {stat = spells[spell_id].AllowRest; }
else if (id == "InCombat") {stat = spells[spell_id].InCombat; }
else if (id == "OutofCombat") {stat = spells[spell_id].OutofCombat; }
else if (id == "aemaxtargets") {stat = spells[spell_id].aemaxtargets; }
else if (id == "maxtargets") {stat = spells[spell_id].maxtargets; }
else if (id == "persistdeath") {stat = spells[spell_id].persistdeath; }
else if (id == "min_dist") {stat = spells[spell_id].min_dist; }
else if (id == "min_dist_mod") {stat = spells[spell_id].min_dist_mod; }
else if (id == "max_dist") {stat = spells[spell_id].max_dist; }
else if (id == "min_range") {stat = spells[spell_id].min_range; }
else if (id == "DamageShieldType") {stat = spells[spell_id].DamageShieldType; }
return stat;
}
+5 -5
View File
@@ -205,7 +205,7 @@ public:
void SendSpellBarEnable(uint16 spellid);
void ZeroCastingVars();
virtual void SpellProcess();
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1,
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1,
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, uint32 type = 0, int16 *resist_adjust = nullptr);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1,
@@ -526,7 +526,7 @@ public:
//More stuff to sort:
virtual bool IsRaidTarget() { return false; };
virtual bool IsRaidTarget() const { return false; };
virtual bool IsAttackAllowed(Mob *target, bool isSpellAttack = false);
bool IsTargeted() const { return (targeted > 0); }
inline void IsTargeted(int in_tar) { targeted += in_tar; if(targeted < 0) targeted = 0;}
@@ -579,8 +579,7 @@ public:
void DoBuffWearOffEffect(uint32 index);
void TryTriggerOnCast(uint32 spell_id, bool aa_trigger);
void TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger);
void TrySpellTrigger(Mob *target, uint32 spell_id);
void TryApplyEffect(Mob *target, uint32 spell_id);
bool TrySpellTrigger(Mob *target, uint32 spell_id, int effect);
void TryTriggerOnValueAmount(bool IsHP = false, bool IsMana = false, bool IsEndur = false, bool IsPet = false);
void TryTwincast(Mob *caster, Mob *target, uint32 spell_id);
void TrySympatheticProc(Mob *target, uint32 spell_id);
@@ -621,6 +620,7 @@ public:
void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr);
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };
int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0);
void ModSkillDmgTaken(SkillUseTypes skill_num, int value);
int16 GetModSkillDmgTaken(const SkillUseTypes skill_num);
@@ -684,7 +684,6 @@ 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);
@@ -769,6 +768,7 @@ public:
inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); }
void ProcessFlee();
void CheckFlee();
inline bool IsBlind() { return spellbonuses.IsBlind; }
inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);}
float CalculateHeadingToTarget(float in_x, float in_y);
+4 -2
View File
@@ -1040,7 +1040,7 @@ void Mob::AI_Process() {
//
if(RuleB(Combat, EnableFearPathing)){
if(curfp) {
if(IsRooted()) {
if(IsRooted() || (IsBlind() && CombatRange(hate_list.GetClosest(this)))) {
//make sure everybody knows were not moving, for appearance sake
if(IsMoving())
{
@@ -1087,7 +1087,9 @@ void Mob::AI_Process() {
if (engaged)
{
if (IsRooted())
// we are prevented from getting here if we are blind and don't have a target in range
// from above, so no extra blind checks needed
if (IsRooted() || IsBlind())
SetTarget(hate_list.GetClosest(this));
else
{
-10
View File
@@ -97,8 +97,6 @@ extern Zone* zone;
EQStreamFactory eqsf(ZoneStream);
npcDecayTimes_Struct npcCorpseDecayTimes[100];
TitleManager title_manager;
DBAsyncFinishedQueue MTdbafq;
DBAsync *dbasync = nullptr;
QueryServ *QServ = 0;
TaskManager *taskmanager = 0;
QuestParserCollection *parse = 0;
@@ -168,8 +166,6 @@ int main(int argc, char** argv) {
_log(ZONE__INIT_ERR, "Cannot continue without a database connection.");
return 1;
}
dbasync = new DBAsync(&database);
dbasync->AddFQ(&MTdbafq);
guild_mgr.SetDatabase(&database);
GuildBanks = nullptr;
@@ -444,10 +440,6 @@ int main(int argc, char** argv) {
}
}
DBAsyncWork* dbaw = 0;
while ((dbaw = MTdbafq.Pop())) {
DispatchFinishedDBAsync(dbaw);
}
if (InterserverTimer.Check()) {
InterserverTimer.Start();
database.ping();
@@ -507,8 +499,6 @@ int main(int argc, char** argv) {
//Fix for Linux world server problem.
eqsf.Close();
worldserver.Disconnect();
dbasync->CommitWrites();
dbasync->StopThread();
safe_delete(taskmanager);
command_deinit();
safe_delete(parse);
+333 -221
View File
@@ -965,240 +965,352 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z,
}
}
uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
uint32 tmp = 0;
uint32 tmp2 = 0;
uint32 ZoneDatabase::CreateNewNPCCommand(const char* zone, uint32 zone_version,Client *client, NPC* spawn, uint32 extra) {
uint32 npc_type_id = 0;
if (extra && client && client->GetZoneID())
{
// Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000)
int starting_npc_id = client->GetZoneID() * 1000;
std::string query = StringFormat("SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i",
starting_npc_id, starting_npc_id + 1000);
auto results = QueryDatabase(query);
if (results.Success()) {
if (results.RowCount() != 0)
{
auto row = results.begin();
npc_type_id = atoi(row[0]) + 1;
// Prevent the npc_type id from exceeding the range for this zone
if (npc_type_id >= (starting_npc_id + 1000))
npc_type_id = 0;
}
else // No npc_type IDs set in this range yet
npc_type_id = starting_npc_id;
}
}
char tmpstr[64];
EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr)));
std::string query;
if (npc_type_id)
{
query = StringFormat("INSERT INTO npc_types (id, name, level, race, class, hp, gender, "
"texture, helmtexture, size, loottable_id, merchant_id, face, "
"runspeed, prim_melee_type, sec_melee_type) "
"VALUES(%i, \"%s\" , %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)",
npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
npc_type_id = results.LastInsertedID();
}
else
{
query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, "
"texture, helmtexture, size, loottable_id, merchant_id, face, "
"runspeed, prim_melee_type, sec_melee_type) "
"VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)",
tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
npc_type_id = results.LastInsertedID();
}
if(client)
client->LogSQL(query.c_str());
query = StringFormat("INSERT INTO spawngroup (id, name) VALUES(%i, '%s-%s')", 0, zone, spawn->GetName());
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
uint32 spawngroupid = results.LastInsertedID();
if(client)
client->LogSQL(query.c_str());
query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) "
"VALUES('%s', %u, %f, %f, %f, %i, %f, %i)",
zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200,
spawn->GetHeading(), spawngroupid);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
if(client)
client->LogSQL(query.c_str());
query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)",
spawngroupid, npc_type_id, 100);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
if(client)
client->LogSQL(query.c_str());
return true;
}
uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) {
uint32 last_insert_id = 0;
std::string query = StringFormat("INSERT INTO spawngroup (name) VALUES('%s%s%i')",
zone, spawn->GetName(), Timer::GetCurrentTime());
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
last_insert_id = results.LastInsertedID();
if(client)
client->LogSQL(query.c_str());
uint32 respawntime = 0;
uint32 spawnid = 0;
if (respawnTime)
respawntime = respawnTime;
else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0)
respawntime = spawn->respawn2->RespawnTimer();
else
respawntime = 1200;
query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) "
"VALUES('%s', %u, %f, %f, %f, %i, %f, %i)",
zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime,
spawn->GetHeading(), last_insert_id);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
spawnid = results.LastInsertedID();
if(client)
client->LogSQL(query.c_str());
query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)",
last_insert_id, spawn->GetNPCTypeID(), 100);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if(client)
client->LogSQL(query.c_str());
return spawnid;
}
uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) {
std::string query = StringFormat("UPDATE npc_types SET name = \"%s\", level = %i, race = %i, class = %i, "
"hp = %i, gender = %i, texture = %i, helmtexture = %i, size = %i, "
"loottable_id = %i, merchant_id = %i, face = %i, WHERE id = %i",
spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
spawn->MerchantType, spawn->GetNPCTypeID());
auto results = QueryDatabase(query);
if (!results.Success() && client)
client->LogSQL(query.c_str());
return results.Success() == true? 1: 0;
}
uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn) {
uint32 id = 0;
uint32 spawngroupID = 0;
std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE "
"zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2());
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
if (results.RowCount() == 0)
return 0;
auto row = results.begin();
if (row[0])
id = atoi(row[0]);
if (row[1])
spawngroupID = atoi(row[1]);
query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id);
results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID);
results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID);
results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
return 1;
}
uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn) {
uint32 id = 0;
uint32 spawngroupID = 0;
std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE zone = '%s' "
"AND version = %u AND spawngroupID = %i",
zone, zone_version, spawn->GetSp2());
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
if (results.RowCount() == 0)
return 0;
auto row = results.begin();
if (row[0])
id = atoi(row[0]);
if (row[1])
spawngroupID = atoi(row[1]);
query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id);
results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID);
results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID);
results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
query = StringFormat("DELETE FROM npc_types WHERE id = '%i'", spawn->GetNPCTypeID());
results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
return 1;
}
uint32 ZoneDatabase::AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) {
uint32 last_insert_id = 0;
std::string query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) "
"VALUES('%s', %u, %f, %f, %f, %i, %f, %i)",
zone, zone_version, client->GetX(), client->GetY(), client->GetZ(),
120, client->GetHeading(), spawnGroupID);
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
if(client)
client->LogSQL(query.c_str());
return 1;
}
uint32 ZoneDatabase::AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) {
uint32 npc_type_id;
char numberlessName[64];
EntityList::RemoveNumbers(strn0cpy(numberlessName, spawn->GetName(), sizeof(numberlessName)));
std::string query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, "
"texture, helmtexture, size, loottable_id, merchant_id, face, "
"runspeed, prim_melee_type, sec_melee_type) "
"VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)",
numberlessName, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28);
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
npc_type_id = results.LastInsertedID();
if(client)
client->LogSQL(query.c_str());
if(client)
client->Message(0, "%s npc_type ID %i created successfully!", numberlessName, npc_type_id);
return 1;
}
uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) {
switch (command) {
case 0: { // Create a new NPC and add all spawn related data
uint32 npc_type_id = 0;
uint32 spawngroupid;
if (extra && c && c->GetZoneID())
{
// Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000)
int starting_npc_id = c->GetZoneID() * 1000;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) {
row = mysql_fetch_row(result);
if(row)
{
if (row[0])
{
npc_type_id = atoi(row[0]) + 1;
// Prevent the npc_type id from exceeding the range for this zone
if (npc_type_id >= (starting_npc_id + 1000))
{
npc_type_id = 0;
}
}
else
{
// row[0] is nullptr - No npc_type IDs set in this range yet
npc_type_id = starting_npc_id;
}
}
safe_delete_array(query);
mysql_free_result(result);
}
}
char tmpstr[64];
EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr)));
if (npc_type_id)
{
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
}
else
{
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
}
if(c) c->LogSQL(query);
safe_delete_array(query);
snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName());
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
return true;
break;
return CreateNewNPCCommand(zone, zone_version, c, spawn, extra);
}
case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID
tmp2 = spawn->GetNPCTypeID();
char tmpstr[64];
snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime());
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
uint32 respawntime = 0;
uint32 spawnid = 0;
if (extra)
respawntime = extra;
else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0)
respawntime = spawn->respawn2->RespawnTimer();
else
respawntime = 1200;
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) {
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
return spawnid;
break;
return AddNewNPCSpawnGroupCommand(zone, zone_version, c, spawn, extra);
}
case 2: { // Update npc_type appearance and other data on targeted spawn
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) {
if(c) c->LogSQL(query);
safe_delete_array(query);
return true;
}
else {
safe_delete_array(query);
return false;
}
break;
return UpdateNPCTypeAppearance(c, spawn);
}
case 3: { // delete spawn from spawning, but leave in npc_types table
if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) {
safe_delete_array(query);
return 0;
}
safe_delete_array(query);
row = mysql_fetch_row(result);
if (row == nullptr) return false;
if (row[0]) tmp = atoi(row[0]);
if (row[1]) tmp2 = atoi(row[1]);
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
return true;
break;
return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn);
}
case 4: { //delete spawn from DB (including npc_type)
if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) {
safe_delete_array(query);
return(0);
}
safe_delete_array(query);
row = mysql_fetch_row(result);
if (row == nullptr) return false;
if (row[0]) tmp = atoi(row[0]);
if (row[1]) tmp2 = atoi(row[1]);
mysql_free_result(result);
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
return true;
break;
return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn);
}
case 5: { // add a spawn from spawngroup
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, c->GetX(), c->GetY(), c->GetZ(), 120, c->GetHeading(), extra), errbuf, 0, 0, &tmp)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
return true;
break;
}
return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra);
}
case 6: { // add npc_type
uint32 npc_type_id;
char tmpstr[64];
EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr)));
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) {
safe_delete(query);
return false;
}
if(c) c->LogSQL(query);
safe_delete_array(query);
if(c) c->Message(0, "%s npc_type ID %i created successfully!", tmpstr, npc_type_id);
return true;
break;
return AddNPCTypes(zone, zone_version, c, spawn, extra);
}
}
return false;
@@ -1743,7 +1855,7 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
else
ns->spawn.IsMercenary = 0;
}
//Not recommended if using above (However, this will work better on older clients).
if (RuleB(Pets, UnTargetableSwarmPet)) {
if(GetOwnerID() || GetSwarmOwner()) {
+8 -4
View File
@@ -136,10 +136,6 @@ public:
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;}
inline void SetSpellFocusHeal(int32 NewSpellFocusHeal) {SpellFocusHeal = NewSpellFocusHeal;}
int32 SpellFocusDMG;
int32 SpellFocusHeal;
virtual void SetTarget(Mob* mob);
virtual uint16 GetSkill(SkillUseTypes skill_num) const { if (skill_num <= HIGHEST_SKILL) { return skills[skill_num]; } return 0; }
@@ -387,6 +383,12 @@ public:
inline void SetHealScale(float amt) { healscale = amt; }
inline float GetHealScale() { return healscale; }
inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;}
inline int32 GetSpellFocusDMG() const { return SpellFocusDMG;}
inline void SetSpellFocusHeal(int32 NewSpellFocusHeal) {SpellFocusHeal = NewSpellFocusHeal;}
inline int32 GetSpellFocusHeal() const {return SpellFocusHeal;}
uint32 GetSpawnKillCount();
int GetScore();
@@ -452,6 +454,8 @@ protected:
uint32 npc_mana;
float spellscale;
float healscale;
int32 SpellFocusDMG;
int32 SpellFocusHeal;
//pet crap:
uint16 pet_spell_id;
+85
View File
@@ -5072,6 +5072,35 @@ XS(XS_Client_UpdateTaskActivity)
XSRETURN_EMPTY;
}
XS(XS_Client_GetTaskActivityDoneCount); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_GetTaskActivityDoneCount)
{
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: Client::GetTaskActivityDoneCount(THIS, TaskID, ActivityID)");
{
Client * THIS;
int RETVAL;
int TaskID = (int)SvIV(ST(1));
int ActivityID = (int)SvIV(ST(2));
dXSTARG;
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *, tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Client");
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetTaskActivityDoneCountFromTaskID(TaskID, ActivityID);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Client_AssignTask); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_AssignTask)
{
@@ -5991,6 +6020,58 @@ XS(XS_Client_SendMarqueeMessage)
XSRETURN_EMPTY;
}
XS(XS_Client_SendColoredText); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SendColoredText)
{
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: Client::SendColoredText(color, message)");
{
Client * THIS;
uint32 color = (uint32)SvUV(ST(1));
std::string msg = (std::string)SvPV_nolen(ST(2));
dXSTARG;
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Client");
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->SendColoredText(color, msg);
}
XSRETURN_EMPTY;
}
XS(XS_Client_SendSpellAnim); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SendSpellAnim)
{
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: SendSpellAnim(uint16 spell_id, uint32 seq)");
{
Client * THIS;
uint16 targetid = (uint16)SvUV(ST(1));
uint16 spell_id = (uint16)SvUV(ST(2));
dXSTARG;
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Client");
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->SendSpellAnim(targetid,spell_id);
}
XSRETURN_EMPTY;
}
#ifdef __cplusplus
extern "C"
#endif
@@ -6199,6 +6280,7 @@ XS(boot_Client)
newXSproto(strcpy(buf, "IsTaskCompleted"), XS_Client_IsTaskCompleted, file, "$$");
newXSproto(strcpy(buf, "IsTaskActive"), XS_Client_IsTaskActive, file, "$$");
newXSproto(strcpy(buf, "IsTaskActivityActive"), XS_Client_IsTaskActivityActive, file, "$$$");
newXSproto(strcpy(buf, "GetTaskActivityDoneCount"), XS_Client_GetTaskActivityDoneCount, file, "$$$");
newXSproto(strcpy(buf, "GetCorpseCount"), XS_Client_GetCorpseCount, file, "$");
newXSproto(strcpy(buf, "GetCorpseID"), XS_Client_GetCorpseID, file, "$$");
newXSproto(strcpy(buf, "GetCorpseItemAt"), XS_Client_GetCorpseItemAt, file, "$$$");
@@ -6229,6 +6311,9 @@ XS(boot_Client)
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$");
newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$");
newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$");
newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$");
XSRETURN_YES;
}
+34
View File
@@ -8137,6 +8137,39 @@ XS(XS_Mob_GetFlurryChance)
XSRETURN(1);
}
XS(XS_Mob_GetSpellStat); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetSpellStat)
{
dXSARGS;
if (items < 3 || items > 4)
Perl_croak(aTHX_ "Usage: Mob::GetSpellStat(THIS, itemid, stat, slot)");
{
Mob * THIS;
int32 RETVAL;
uint32 spellid = (uint32)SvUV(ST(1));
Const_char * stat = (Const_char *)SvPV_nolen(ST(2));
uint8 slot = (uint8)SvUV(ST(3));
dXSTARG;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items > 4) { slot = 0; }
RETVAL = THIS->GetSpellStat(spellid, stat, slot);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
#ifdef __cplusplus
extern "C"
#endif
@@ -8436,6 +8469,7 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "IsMeleeDisabled"), XS_Mob_IsMeleeDisabled, file, "$$");
newXSproto(strcpy(buf, "SetFlurryChance"), XS_Mob_SetFlurryChance, file, "$$");
newXSproto(strcpy(buf, "GetFlurryChance"), XS_Mob_GetFlurryChance, file, "$");
newXSproto(strcpy(buf, "GetSpellStat"), XS_Mob_GetSpellStat, file, "$$$$");
XSRETURN_YES;
}
+56 -2
View File
@@ -1991,6 +1991,32 @@ XS(XS_NPC_SetSpellFocusDMG)
XSRETURN_EMPTY;
}
XS(XS_NPC_GetSpellFocusDMG); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_GetSpellFocusDMG)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusDMG(THIS)");
{
NPC * THIS;
int32 RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetSpellFocusDMG();
XSprePUSH; PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
XS(XS_NPC_SetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_SetSpellFocusHeal)
{
@@ -2015,6 +2041,32 @@ XS(XS_NPC_SetSpellFocusHeal)
XSRETURN_EMPTY;
}
XS(XS_NPC_GetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_GetSpellFocusHeal)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusHeal(THIS)");
{
NPC * THIS;
int32 RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetSpellFocusHeal();
XSprePUSH; PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
XS(XS_NPC_GetSlowMitigation); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_GetSlowMitigation)
{
@@ -2286,8 +2338,10 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "RemoveAISpell"), XS_NPC_RemoveSpellFromNPCList, file, "$$");
newXSproto(strcpy(buf, "SetSpellFocusDMG"), XS_NPC_SetSpellFocusDMG, file, "$$");
newXSproto(strcpy(buf, "SetSpellFocusHeal"), XS_NPC_SetSpellFocusHeal, file, "$$");
newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetAttackSpeed, file, "$");
newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetSlowMitigation, file, "$");
newXSproto(strcpy(buf, "GetSpellFocusDMG"), XS_NPC_GetSpellFocusDMG, file, "$");
newXSproto(strcpy(buf, "GetSpellFocusHeal"), XS_NPC_GetSpellFocusHeal, file, "$");
newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetSlowMitigation, file, "$");
newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetAttackSpeed, file, "$");
newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$");
newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$");
newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$");
+166 -189
View File
@@ -172,7 +172,7 @@ void QuestManager::EndQuest() {
cur = QTimerList.erase(cur);
else
++cur;
}
}
run.owner->Depop();
}
quests_running_.pop();
@@ -447,7 +447,7 @@ void QuestManager::settimer(const char *timer_name, int seconds) {
end = QTimerList.end();
while (cur != end) {
if(cur->mob && cur->mob == owner && cur->name == timer_name)
if(cur->mob && cur->mob == owner && cur->name == timer_name)
{
cur->Timer_.Enable();
cur->Timer_.Start(seconds * 1000, false);
@@ -471,7 +471,7 @@ void QuestManager::settimerMS(const char *timer_name, int milliseconds) {
end = QTimerList.end();
while (cur != end) {
if(cur->mob && cur->mob == owner && cur->name == timer_name)
if(cur->mob && cur->mob == owner && cur->name == timer_name)
{
cur->Timer_.Enable();
cur->Timer_.Start(milliseconds, false);
@@ -946,7 +946,7 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
uint16 count;
uint16 curspell;
uint16 Char_ID = initiator->CharacterID();
uint32 Char_ID = initiator->CharacterID();
bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals);
bool SpellGlobalCheckResult = 0;
@@ -960,7 +960,7 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
spells[curspell].skill != 52 &&
( !RuleB(Spells, UseCHAScribeHack) || spells[curspell].effectid[EFFECT_COUNT - 1] != 10 )
)
{
{
if(IsDiscipline(curspell)){
//we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little
for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) {
@@ -974,18 +974,20 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
SpellGlobalCheckResult = initiator->SpellGlobalCheck(curspell, Char_ID);
if (SpellGlobalCheckResult) {
initiator->GetPP().disciplines.values[r] = curspell;
database.SaveCharacterDisc(Char_ID, r, curspell);
initiator->SendDisciplineUpdate();
initiator->Message(0, "You have learned a new discipline!");
count++; //success counter
}
break; //continue the 1st loop
break; //continue the 1st loop
}
else {
initiator->GetPP().disciplines.values[r] = curspell;
initiator->SendDisciplineUpdate();
initiator->Message(0, "You have learned a new discipline!");
count++; //success counter
break; //continue the 1st loop
initiator->GetPP().disciplines.values[r] = curspell;
database.SaveCharacterDisc(Char_ID, r, curspell);
initiator->SendDisciplineUpdate();
initiator->Message(0, "You have learned a new discipline!");
count++; //success counter
break; //continue the 1st loop
}
} //if we get to this point, there's already a discipline in this slot, so we skip it
}
@@ -1308,7 +1310,7 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti
if (initiator && initiator->IsClient()){ // some events like waypoint and spawn don't have a player involved
qgCharid=initiator->CharacterID();
}
}
else {
qgCharid=-qgNpcid; // make char id negative npc id as a fudge
}
@@ -1335,81 +1337,69 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti
/* Inserts global variable into quest_globals table */
int QuestManager::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varname, const char *varvalue, int duration) {
char *query = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
// Make duration string either "unix_timestamp(now()) + xxx" or "NULL"
std::stringstream duration_ss;
if (duration == INT_MAX) {
duration_ss << "NULL";
}
else {
duration_ss << "unix_timestamp(now()) + " << duration;
}
std::string durationText = (duration == INT_MAX)? "NULL": StringFormat("unix_timestamp(now()) + %i", duration);
/*
NOTE: this should be escaping the contents of arglist
npcwise a malicious script can arbitrarily alter the DB
*/
uint32 last_id = 0;
if (!database.RunQuery(query, MakeAnyLenString(&query,
"REPLACE INTO quest_globals (charid, npcid, zoneid, name, value, expdate)"
"VALUES (%i, %i, %i, '%s', '%s', %s)",
charid, npcid, zoneid, varname, varvalue, duration_ss.str().c_str()
), errbuf, nullptr, nullptr, &last_id))
{
std::cerr << "setglobal error inserting " << varname << " : " << errbuf << std::endl;
}
safe_delete_array(query);
if(zone) {
/* Delete existing qglobal data and update zone processes */
ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct));
ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer;
qgd->npc_id = npcid;
qgd->char_id = charid;
qgd->zone_id = zoneid;
qgd->from_zone_id = zone->GetZoneID();
qgd->from_instance_id = zone->GetInstanceID();
strcpy(qgd->name, varname);
std::string query = StringFormat("REPLACE INTO quest_globals "
"(charid, npcid, zoneid, name, value, expdate)"
"VALUES (%i, %i, %i, '%s', '%s', %s)",
charid, npcid, zoneid, varname, varvalue, durationText.c_str());
auto results = database.QueryDatabase(query);
if (!results.Success())
std::cerr << "setglobal error inserting " << varname << " : " << results.ErrorMessage() << std::endl;
entity_list.DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id);
zone->DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id);
if(!zone)
return 0;
worldserver.SendPacket(pack);
safe_delete(pack);
/* Delete existing qglobal data and update zone processes */
ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct));
ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer;
qgd->npc_id = npcid;
qgd->char_id = charid;
qgd->zone_id = zoneid;
qgd->from_zone_id = zone->GetZoneID();
qgd->from_instance_id = zone->GetInstanceID();
strcpy(qgd->name, varname);
/* Create new qglobal data and update zone processes */
pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct));
ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer;
qgu->npc_id = npcid;
qgu->char_id = charid;
qgu->zone_id = zoneid;
if(duration == INT_MAX) {
qgu->expdate = 0xFFFFFFFF;
}
else {
qgu->expdate = Timer::GetTimeSeconds() + duration;
}
strcpy((char*)qgu->name, varname);
strn0cpy((char*)qgu->value, varvalue, 128);
qgu->id = last_id;
qgu->from_zone_id = zone->GetZoneID();
qgu->from_instance_id = zone->GetInstanceID();
entity_list.DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id);
zone->DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id);
QGlobal temp;
temp.npc_id = npcid;
temp.char_id = charid;
temp.zone_id = zoneid;
temp.expdate = qgu->expdate;
temp.name.assign(qgu->name);
temp.value.assign(qgu->value);
entity_list.UpdateQGlobal(qgu->id, temp);
zone->UpdateQGlobal(qgu->id, temp);
worldserver.SendPacket(pack);
safe_delete(pack);
worldserver.SendPacket(pack);
safe_delete(pack);
}
/* Create new qglobal data and update zone processes */
pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct));
ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer;
qgu->npc_id = npcid;
qgu->char_id = charid;
qgu->zone_id = zoneid;
qgu->expdate = (duration == INT_MAX)? 0xFFFFFFFF: Timer::GetTimeSeconds() + duration;
strcpy((char*)qgu->name, varname);
strn0cpy((char*)qgu->value, varvalue, 128);
qgu->id = results.LastInsertedID();
qgu->from_zone_id = zone->GetZoneID();
qgu->from_instance_id = zone->GetInstanceID();
QGlobal temp;
temp.npc_id = npcid;
temp.char_id = charid;
temp.zone_id = zoneid;
temp.expdate = qgu->expdate;
temp.name.assign(qgu->name);
temp.value.assign(qgu->value);
entity_list.UpdateQGlobal(qgu->id, temp);
zone->UpdateQGlobal(qgu->id, temp);
worldserver.SendPacket(pack);
safe_delete(pack);
return 0;
}
@@ -1420,22 +1410,14 @@ void QuestManager::targlobal(const char *varname, const char *value, const char
void QuestManager::delglobal(const char *varname) {
QuestManagerCurrentQuestVars();
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
int qgZoneid=zone->GetZoneID();
int qgCharid=0;
int qgNpcid=owner->GetNPCTypeID();
if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved
{
qgCharid=initiator->CharacterID();
}
else {
if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved
qgCharid=initiator->CharacterID();
else
qgCharid=-qgNpcid; // make char id negative npc id as a fudge
}
/* QS: PlayerLogQGlobalUpdate */
if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){
@@ -1443,31 +1425,32 @@ void QuestManager::delglobal(const char *varname) {
QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc);
}
if (!database.RunQuery(query,
MakeAnyLenString(&query,
"DELETE FROM quest_globals WHERE name='%s'"
" && (npcid=0 || npcid=%i) && (charid=0 || charid=%i) && (zoneid=%i || zoneid=0)",
varname,qgNpcid,qgCharid,qgZoneid),errbuf))
{
std::cerr << "delglobal error deleting " << varname << " : " << errbuf << std::endl;
}
safe_delete_array(query);
std::string query = StringFormat("DELETE FROM quest_globals "
"WHERE name = '%s' "
"&& (npcid=0 || npcid=%i) "
"&& (charid=0 || charid=%i) "
"&& (zoneid=%i || zoneid=0)",
varname, qgNpcid, qgCharid, qgZoneid);
auto results = database.QueryDatabase(query);
if (!results.Success())
std::cerr << "delglobal error deleting " << varname << " : " << results.ErrorMessage() << std::endl;
if(zone) {
ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct));
ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer;
if(!zone)
return;
qgu->npc_id = qgNpcid;
qgu->char_id = qgCharid;
qgu->zone_id = qgZoneid;
strcpy(qgu->name, varname);
ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct));
ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer;
entity_list.DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id);
zone->DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id);
qgu->npc_id = qgNpcid;
qgu->char_id = qgCharid;
qgu->zone_id = qgZoneid;
strcpy(qgu->name, varname);
worldserver.SendPacket(pack);
safe_delete(pack);
}
entity_list.DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id);
zone->DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id);
worldserver.SendPacket(pack);
safe_delete(pack);
}
// Converts duration string to duration value (in seconds)
@@ -1688,11 +1671,6 @@ void QuestManager::showgrid(int grid) {
if(initiator == nullptr)
return;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
FindPerson_Point pt;
std::vector<FindPerson_Point> pts;
@@ -1702,22 +1680,25 @@ void QuestManager::showgrid(int grid) {
pts.push_back(pt);
// Retrieve all waypoints for this grid
if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result)) {
while((row = mysql_fetch_row(result))) {
pt.x = atof(row[0]);
pt.y = atof(row[1]);
pt.z = atof(row[2]);
pts.push_back(pt);
}
mysql_free_result(result);
initiator->SendPathPacket(pts);
}
else // DB query error!
{
LogFile->write(EQEMuLog::Quest, "Error loading grid %d for showgrid(): %s", grid, errbuf);
std::string query = StringFormat("SELECT `x`,`y`,`z` FROM grid_entries "
"WHERE `gridid` = %i AND `zoneid` = %i "
"ORDER BY `number`", grid, zone->GetZoneID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Quest, "Error loading grid %d for showgrid(): %s", grid, results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
}
for(auto row = results.begin(); row != results.end(); ++row) {
pt.x = atof(row[0]);
pt.y = atof(row[1]);
pt.z = atof(row[2]);
pts.push_back(pt);
}
initiator->SendPathPacket(pts);
}
//change the value of a spawn condition
@@ -2293,19 +2274,19 @@ bool QuestManager::istaskappropriate(int task) {
}
void QuestManager::clearspawntimers() {
if(zone) {
//TODO: Dec 19, 2008, replace with code updated for current spawn timers.
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
while (iterator.MoreElements())
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE id=%lu AND "
"instance_id=%lu",(unsigned long)iterator.GetData()->GetID(), (unsigned long)zone->GetInstanceID()), errbuf);
safe_delete_array(query);
iterator.Advance();
}
if(!zone)
return;
//TODO: Dec 19, 2008, replace with code updated for current spawn timers.
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
while (iterator.MoreElements()) {
std::string query = StringFormat("DELETE FROM respawn_times "
"WHERE id = %lu AND instance_id = %lu",
(unsigned long)iterator.GetData()->GetID(),
(unsigned long)zone->GetInstanceID());
auto results = database.QueryDatabase(query);
iterator.Advance();
}
}
@@ -2682,11 +2663,6 @@ void QuestManager::FlagInstanceByRaidLeader(uint32 zone, int16 version)
const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkName) {
QuestManagerCurrentQuestVars();
const char *ERR_MYSQLERROR = "Error in saylink phrase queries";
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
int sayid = 0;
int sz = strlen(Phrase);
@@ -2694,70 +2670,50 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam
database.DoEscapeString(escaped_string, Phrase, sz);
// Query for an existing phrase and id in the saylink table
if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string),errbuf,&result))
{
if (mysql_num_rows(result) >= 1)
{
while((row = mysql_fetch_row(result)))
{
std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string);
auto results = database.QueryDatabase(query);
if (results.Success()) {
if (results.RowCount() >= 1) {
for (auto row = results.begin();row != results.end(); ++row)
sayid = atoi(row[0]);
}
mysql_free_result(result);
}
else // Add a new saylink entry to the database and query it again for the new sayid number
{
safe_delete_array(query);
database.RunQuery(query,MakeAnyLenString(&query,"INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string),errbuf);
safe_delete_array(query);
if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `id` FROM saylink WHERE `phrase` = '%s'", escaped_string),errbuf,&result))
{
if (mysql_num_rows(result) >= 1)
{
while((row = mysql_fetch_row(result)))
{
sayid = atoi(row[0]);
}
mysql_free_result(result);
} else { // Add a new saylink entry to the database and query it again for the new sayid number
std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string);
results = database.QueryDatabase(insert_query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
} else {
results = database.QueryDatabase(query);
if (results.Success()) {
if (results.RowCount() >= 1)
for(auto row = results.begin(); row != results.end(); ++row)
sayid = atoi(row[0]);
} else {
LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
}
}
else
{
LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf);
}
safe_delete_array(query);
}
}
safe_delete_array(query);
safe_delete_array(escaped_string);
if(silent)
if (silent)
sayid = sayid + 750000;
else
sayid = sayid + 500000;
//Create the say link as an item link hash
char linktext[250];
//Create the say link as an item link hash
char linktext[250];
if(initiator)
{
if (initiator) {
if (initiator->GetClientVersion() >= EQClientRoF)
{
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12);
}
else if (initiator->GetClientVersion() >= EQClientSoF)
{
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"00000000000000000000000000000000000000000000",LinkName,0x12);
}
else
{
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"000000000000000000000000000000000000000",LinkName,0x12);
}
}
else { // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones.
} else { // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones.
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12);
}
strcpy(Phrase,linktext);
return Phrase;
@@ -2949,6 +2905,15 @@ const char* QuestManager::GetZoneLongName(const char *zone) {
return ln.c_str();
}
void QuestManager::CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 data){
ServerPacket* pack = new ServerPacket(ServerOP_CZSignalNPC, sizeof(CZNPCSignal_Struct));
CZNPCSignal_Struct* CZSN = (CZNPCSignal_Struct*)pack->pBuffer;
CZSN->npctype_id = npctype_id;
CZSN->data = data;
worldserver.SendPacket(pack);
safe_delete(pack);
}
void QuestManager::CrossZoneSignalPlayerByCharID(int charid, uint32 data){
ServerPacket* pack = new ServerPacket(ServerOP_CZSignalClient, sizeof(CZClientSignal_Struct));
CZClientSignal_Struct* CZSC = (CZClientSignal_Struct*) pack->pBuffer;
@@ -2966,7 +2931,7 @@ void QuestManager::CrossZoneSignalPlayerByName(const char *CharName, uint32 data
CZSC->data = data;
worldserver.SendPacket(pack);
safe_delete(pack);
}
}
void QuestManager::CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message){
uint32 message_len = strlen(CharName) + 1;
@@ -2976,6 +2941,18 @@ void QuestManager::CrossZoneMessagePlayerByName(uint32 Type, const char *CharNam
CZSC->Type = Type;
strn0cpy(CZSC->CharName, CharName, 64);
strn0cpy(CZSC->Message, Message, 512);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void QuestManager::CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var){
uint32 message_len = strlen(id) + 1;
uint32 message_len2 = strlen(m_var) + 1;
ServerPacket* pack = new ServerPacket(ServerOP_CZSetEntityVariableByNPCTypeID, sizeof(CZSetEntVarByNPCTypeID_Struct) + message_len + message_len2);
CZSetEntVarByNPCTypeID_Struct* CZSNBYNID = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer;
CZSNBYNID->npctype_id = npctype_id;
strn0cpy(CZSNBYNID->id, id, 256);
strn0cpy(CZSNBYNID->m_var, m_var, 256);
worldserver.SendPacket(pack);
safe_delete(pack);
}
+3 -1
View File
@@ -239,8 +239,10 @@ public:
uint16 CreateDoor( const char* model, float x, float y, float z, float heading, uint8 opentype, uint16 size);
int32 GetZoneID(const char *zone);
const char *GetZoneLongName(const char *zone);
void CrossZoneSignalPlayerByCharID(int charid, uint32 data);
void CrossZoneSignalPlayerByCharID(int charid, uint32 data);
void CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 data);
void CrossZoneSignalPlayerByName(const char *CharName, uint32 data);
void CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var);
void CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message);
bool EnableRecipe(uint32 recipe_id);
bool DisableRecipe(uint32 recipe_id);
+339 -193
View File
@@ -29,6 +29,12 @@ Raid::Raid(uint32 raidID)
: GroupIDConsumer(raidID)
{
memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS));
memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct));
memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS);
for (int i = 0; i < MAX_RAID_GROUPS; i++) {
group_mentor[i].mentor_percent = 0;
group_mentor[i].mentoree = nullptr;
}
leader = nullptr;
memset(leadername, 0, 64);
locked = false;
@@ -39,6 +45,12 @@ Raid::Raid(Client* nLeader)
: GroupIDConsumer()
{
memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS));
memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct));
memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS);
for (int i = 0; i < MAX_RAID_GROUPS; i++) {
group_mentor[i].mentor_percent = 0;
group_mentor[i].mentoree = nullptr;
}
leader = nLeader;
memset(leadername, 0, 64);
strn0cpy(leadername, nLeader->GetName(), 64);
@@ -74,21 +86,32 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo
if(!c)
return;
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO raid_members SET raidid=%lu, charid=%lu, groupid=%lu, _class=%d, level=%d, name='%s', isgroupleader=%d, israidleader=%d, islooter=%d", (unsigned long)GetID(), (unsigned long)c->CharacterID(), (unsigned long)group, c->GetClass(), c->GetLevel(), c->GetName(), groupleader, rleader, looter ),errbuf,&result)){
mysql_free_result(result);
}
std::string query = StringFormat("INSERT INTO raid_members SET raidid = %lu, charid = %lu, "
"groupid = %lu, _class = %d, level = %d, name = '%s', "
"isgroupleader = %d, israidleader = %d, islooter = %d",
(unsigned long)GetID(), (unsigned long)c->CharacterID(),
(unsigned long)group, c->GetClass(), c->GetLevel(),
c->GetName(), groupleader, rleader, looter);
auto results = database.QueryDatabase(query);
safe_delete_array(query);
LearnMembers();
VerifyRaid();
if (rleader) {
database.SetRaidGroupLeaderInfo(RAID_GROUPLESS, GetID());
UpdateRaidAAs();
}
if (group != RAID_GROUPLESS && groupleader) {
database.SetRaidGroupLeaderInfo(group, GetID());
UpdateGroupAAs(group);
}
if(group < 12)
GroupUpdate(group);
else // get raid AAs, GroupUpdate will handles it otherwise
SendGroupLeadershipAA(c, RAID_GROUPLESS);
SendRaidAddAll(c->GetName());
c->SetRaidGrouped(true);
SendRaidMOTD(c);
ServerPacket *pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
@@ -100,32 +123,26 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo
safe_delete(pack);
}
void Raid::RemoveMember(const char *c)
void Raid::RemoveMember(const char *characterName)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "DELETE FROM raid_members where name='%s'", c ),errbuf,&result)){
mysql_free_result(result);
}
std::string query = StringFormat("DELETE FROM raid_members where name='%s'", characterName);
auto results = database.QueryDatabase(query);
Client *m = entity_list.GetClientByName(c);
safe_delete_array(query);
Client *client = entity_list.GetClientByName(characterName);
disbandCheck = true;
SendRaidRemoveAll(c);
SendRaidDisband(m);
SendRaidRemoveAll(characterName);
SendRaidDisband(client);
LearnMembers();
VerifyRaid();
if(m){
m->SetRaidGrouped(false);
}
if(client)
client->SetRaidGrouped(false);
ServerPacket *pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
rga->instance_id = zone->GetInstanceID();
strn0cpy(rga->playername, c, 64);
strn0cpy(rga->playername, characterName, 64);
rga->zoneid = zone->GetZoneID();
worldserver.SendPacket(pack);
safe_delete(pack);
@@ -133,14 +150,9 @@ void Raid::RemoveMember(const char *c)
void Raid::DisbandRaid()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "DELETE FROM raid_members WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){
mysql_free_result(result);
}
std::string query = StringFormat("DELETE FROM raid_members WHERE raidid = %lu", (unsigned long)GetID());
auto results = database.QueryDatabase(query);
safe_delete_array(query);
LearnMembers();
VerifyRaid();
SendRaidDisbandAll();
@@ -159,14 +171,10 @@ void Raid::DisbandRaid()
void Raid::MoveMember(const char *name, uint32 newGroup)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET groupid=%lu WHERE name='%s'", (unsigned long)newGroup, name),errbuf,&result)){
mysql_free_result(result);
}
std::string query = StringFormat("UPDATE raid_members SET groupid = %lu WHERE name = '%s'",
(unsigned long)newGroup, name);
auto results = database.QueryDatabase(query);
safe_delete_array(query);
LearnMembers();
VerifyRaid();
SendRaidMoveAll(name);
@@ -183,21 +191,13 @@ void Raid::MoveMember(const char *name, uint32 newGroup)
void Raid::SetGroupLeader(const char *who, bool glFlag)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET isgroupleader=%lu WHERE name='%s'", (unsigned long)glFlag, who),errbuf,&result)){
mysql_free_result(result);
}
std::string query = StringFormat("UPDATE raid_members SET isgroupleader = %lu WHERE name = '%s'",
(unsigned long)glFlag, who);
auto results = database.QueryDatabase(query);
safe_delete_array(query);
LearnMembers();
VerifyRaid();
//if(glFlag == true){ //we're setting the flag
//this->SendMakeGroupLeaderPacket(who);
//}
ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupLeader, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
@@ -208,27 +208,29 @@ void Raid::SetGroupLeader(const char *who, bool glFlag)
safe_delete(pack);
}
Client *Raid::GetGroupLeader(uint32 group_id)
{
if (group_id == RAID_GROUPLESS)
return nullptr;
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
if (members[i].member && members[i].IsGroupLeader && members[i].GroupNumber == group_id)
return members[i].member;
return nullptr;
}
void Raid::SetRaidLeader(const char *wasLead, const char *name)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (!database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET israidleader=0 WHERE name='%s'", wasLead),errbuf,&result)){
printf("Set Raid Leader error: %s\n", errbuf);
}
else
mysql_free_result(result);
std::string query = StringFormat("UPDATE raid_members SET israidleader = 0 WHERE name = '%s'", wasLead);
auto results = database.QueryDatabase(query);
if (!results.Success())
printf("Set Raid Leader error: %s\n", results.ErrorMessage().c_str());
safe_delete_array(query);
query = 0;
if (!database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET israidleader=1 WHERE name='%s'", name),errbuf,&result)){
printf("Set Raid Leader error: %s\n", errbuf);
}
else
mysql_free_result(result);
safe_delete_array(query);
query = StringFormat("UPDATE raid_members SET israidleader = 1 WHERE name = '%s'", name);
results = database.QueryDatabase(query);
if (!results.Success())
printf("Set Raid Leader error: %s\n", results.ErrorMessage().c_str());
strn0cpy(leadername, name, 64);
@@ -250,6 +252,58 @@ void Raid::SetRaidLeader(const char *wasLead, const char *name)
safe_delete(pack);
}
void Raid::SaveGroupLeaderAA(uint32 gid)
{
char *queryBuffer = new char[sizeof(GroupLeadershipAA_Struct) * 2 + 1];
database.DoEscapeString(queryBuffer, (char*)&group_aa[gid], sizeof(GroupLeadershipAA_Struct));
std::string query = "UPDATE raid_leaders SET leadershipaa = '";
query += queryBuffer;
query += StringFormat("' WHERE gid = %lu AND rid = %lu LIMIT 1", gid, GetID());
safe_delete_array(queryBuffer);
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str());
}
void Raid::SaveRaidLeaderAA()
{
char *queryBuffer = new char[sizeof(RaidLeadershipAA_Struct) * 2 + 1];
database.DoEscapeString(queryBuffer, (char*)&raid_aa, sizeof(RaidLeadershipAA_Struct));
std::string query = "UPDATE raid_leaders SET leadershipaa = '";
query += queryBuffer;
query += StringFormat("' WHERE gid = %lu AND rid = %lu LIMIT 1", RAID_GROUPLESS, GetID());
safe_delete_array(queryBuffer);
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str());
}
void Raid::UpdateGroupAAs(uint32 gid)
{
Client *gl = GetGroupLeader(gid);
if (gl)
gl->GetGroupAAs(&group_aa[gid]);
else
memset(&group_aa[gid], 0, sizeof(GroupLeadershipAA_Struct));
SaveGroupLeaderAA(gid);
}
void Raid::UpdateRaidAAs()
{
Client *rl = GetLeader();
if (rl)
rl->GetRaidAAs(&raid_aa);
else
memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct));
SaveRaidLeaderAA();
}
bool Raid::IsGroupLeader(const char *who)
{
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
@@ -264,14 +318,10 @@ bool Raid::IsGroupLeader(const char *who)
void Raid::UpdateLevel(const char *name, int newLevel)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET level=%lu WHERE name='%s'", (unsigned long)newLevel, name),errbuf,&result)){
mysql_free_result(result);
}
std::string query = StringFormat("UPDATE raid_members SET level = %lu WHERE name = '%s'",
(unsigned long)newLevel, name);
auto results = database.QueryDatabase(query);
safe_delete_array(query);
LearnMembers();
VerifyRaid();
}
@@ -514,7 +564,7 @@ void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster, int32
int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0;
int gi = 0;
float distance;
float range2 = range*range;
@@ -567,7 +617,7 @@ void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster, int3
if (!range)
range = 200;
float distance;
float range2 = range*range;
@@ -775,27 +825,16 @@ void Raid::TeleportRaid(Mob* sender, uint32 zoneID, uint16 instance_id, float x,
void Raid::ChangeLootType(uint32 type)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_details SET loottype=%lu WHERE raidid=%lu", (unsigned long)type, (unsigned long)GetID()),errbuf,&result)){
mysql_free_result(result);
}
safe_delete_array(query);
std::string query = StringFormat("UPDATE raid_details SET loottype = %lu WHERE raidid = %lu",
(unsigned long)type, (unsigned long)GetID());
auto results = database.QueryDatabase(query);
LootType = type;
}
void Raid::AddRaidLooter(const char* looter)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET islooter=1 WHERE name='%s'", looter),errbuf,&result)){
mysql_free_result(result);
}
safe_delete_array(query);
std::string query = StringFormat("UPDATE raid_members SET islooter = 1 WHERE name = '%s'", looter);
auto results = database.QueryDatabase(query);
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
@@ -812,36 +851,19 @@ void Raid::AddRaidLooter(const char* looter)
rga->instance_id = zone->GetInstanceID();
worldserver.SendPacket(pack);
safe_delete(pack);
/* For reference only at this time. This code adds a looter to the Raid Options Window.
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct));
RaidGeneral_Struct *rgs = (RaidGeneral_Struct*)outapp->pBuffer;
rgs->action = 33;
strcpy(rgs->leader_name, looter);
QueuePacket(outapp);
safe_delete(outapp); */
}
void Raid::RemoveRaidLooter(const char* looter)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET islooter=0 WHERE name='%s'", looter),errbuf,&result)){
mysql_free_result(result);
}
safe_delete_array(query);
std::string query = StringFormat("UPDATE raid_members SET islooter = 0 WHERE name = '%s'", looter);
auto results = database.QueryDatabase(query);
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
if(strcmp(looter, members[x].membername) == 0)
{
if(strcmp(looter, members[x].membername) == 0) {
members[x].IsLooter = 0;
break;
}
}
ServerPacket *pack = new ServerPacket(ServerOP_DetailsChange, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
@@ -849,15 +871,6 @@ void Raid::RemoveRaidLooter(const char* looter)
rga->instance_id = zone->GetInstanceID();
worldserver.SendPacket(pack);
safe_delete(pack);
/* For reference only at this time. This code removes a looter from the Raid Options Window.
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct));
RaidGeneral_Struct *rgs = (RaidGeneral_Struct*)outapp->pBuffer;
rgs->action = 34;
strcpy(rgs->leader_name, looter);
QueuePacket(outapp);
safe_delete(outapp); */
}
bool Raid::IsRaidMember(const char *name){
@@ -1089,12 +1102,12 @@ void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req)
void Raid::SendMakeLeaderPacket(const char *who) //30
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct));
RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer;
rg->action = raidMakeLeader;
strn0cpy(rg->leader_name, who, 64);
strn0cpy(rg->player_name, who, 64);
rg->parameter = 0;
memcpy(&rg->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct));
QueuePacket(outapp);
safe_delete(outapp);
}
@@ -1104,12 +1117,12 @@ void Raid::SendMakeLeaderPacketTo(const char *who, Client *to)
if(!to)
return;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct));
RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer;
rg->action = raidMakeLeader;
strn0cpy(rg->leader_name, who, 64);
strn0cpy(rg->player_name, who, 64);
rg->parameter = 0;
memcpy(&rg->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct));
to->QueuePacket(outapp);
safe_delete(outapp);
}
@@ -1167,6 +1180,7 @@ void Raid::SendGroupUpdate(Client *to)
strn0cpy(gu->leadersname, to->GetName(), 64);
}
strn0cpy(gu->yourname, to->GetName(), 64);
memcpy(&gu->leader_aas, &group_aa[grp], sizeof(GroupLeadershipAA_Struct));
to->FastQueuePacket(&outapp);
}
@@ -1179,8 +1193,10 @@ void Raid::GroupUpdate(uint32 gid, bool initial)
{
if(strlen(members[x].membername) > 0){
if(members[x].GroupNumber == gid){
if(members[x].member)
if(members[x].member) {
SendGroupUpdate(members[x].member);
SendGroupLeadershipAA(members[x].member, gid);
}
}
}
}
@@ -1279,16 +1295,78 @@ void Raid::SendRaidGroupRemove(const char *who, uint32 gid)
safe_delete(pack);
}
void Raid::SendRaidMOTD(Client *c)
{
if (!c || motd.empty())
return;
size_t size = motd.size() + 1;
EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidMOTD_Struct) + size);
RaidMOTD_Struct *rmotd = (RaidMOTD_Struct *)outapp->pBuffer;
rmotd->general.action = raidSetMotd;
strn0cpy(rmotd->general.player_name, c->GetName(), 64);
strn0cpy(rmotd->motd, motd.c_str(), size);
c->FastQueuePacket(&outapp);
}
void Raid::SendRaidMOTD()
{
if (motd.empty())
return;
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
if (members[i].member)
SendRaidMOTD(members[i].member);
}
void Raid::SendRaidMOTDToWorld()
{
if (motd.empty())
return;
size_t size = motd.size() + 1;
ServerPacket *pack = new ServerPacket(ServerOP_RaidMOTD, sizeof(ServerRaidMOTD_Struct) + size);
ServerRaidMOTD_Struct *smotd = (ServerRaidMOTD_Struct *)pack->pBuffer;
smotd->rid = GetID();
strn0cpy(smotd->motd, motd.c_str(), size);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Raid::SendGroupLeadershipAA(Client *c, uint32 gid)
{
EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
RaidLeadershipUpdate_Struct *rlaa = (RaidLeadershipUpdate_Struct *)outapp->pBuffer;
rlaa->action = raidSetLeaderAbilities;
strn0cpy(rlaa->leader_name, c->GetName(), 64);
strn0cpy(rlaa->player_name, c->GetName(), 64);
if (gid != RAID_GROUPLESS)
memcpy(&rlaa->group, &group_aa[gid], sizeof(GroupLeadershipAA_Struct));
memcpy(&rlaa->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct));
c->QueuePacket(outapp);
safe_delete(outapp);
}
void Raid::SendGroupLeadershipAA(uint32 gid)
{
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
if (members[i].member && members[i].GroupNumber == gid)
SendGroupLeadershipAA(members[i].member, gid);
}
void Raid::SendAllRaidLeadershipAA()
{
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
if (members[i].member)
SendGroupLeadershipAA(members[i].member, members[i].GroupNumber);
}
void Raid::LockRaid(bool lockFlag)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_details SET locked=%d WHERE raidid=%lu", lockFlag, (unsigned long)GetID()),errbuf,&result)){
mysql_free_result(result);
}
std::string query = StringFormat("UPDATE raid_details SET locked = %d WHERE raidid = %lu",
lockFlag, (unsigned long)GetID());
auto results = database.QueryDatabase(query);
safe_delete_array(query);
locked = lockFlag;
if(lockFlag)
SendRaidLock();
@@ -1307,74 +1385,79 @@ void Raid::LockRaid(bool lockFlag)
void Raid::SetRaidDetails()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO raid_details SET raidid=%lu, loottype=4, locked=0", (unsigned long)GetID()),errbuf,&result)){
mysql_free_result(result);
}
safe_delete_array(query);
std::string query = StringFormat("INSERT INTO raid_details SET raidid = %lu, loottype = 4, locked = 0, motd = ''",
(unsigned long)GetID());
auto results = database.QueryDatabase(query);
}
void Raid::GetRaidDetails()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT locked, loottype FROM raid_details WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){
safe_delete_array(query);
if(mysql_num_rows(result) < 1) {
mysql_free_result(result);
LogFile->write(EQEMuLog::Error, "Error getting raid details for raid %lu: %s", (unsigned long)GetID(), errbuf);
return;
}
row = mysql_fetch_row(result);
if(row){
locked = atoi(row[0]);
LootType = atoi(row[1]);
}
mysql_free_result(result);
}
std::string query = StringFormat("SELECT locked, loottype, motd FROM raid_details WHERE raidid = %lu",
(unsigned long)GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
return;
if (results.RowCount() == 0) {
LogFile->write(EQEMuLog::Error, "Error getting raid details for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str());
return;
}
auto row = results.begin();
locked = atoi(row[0]);
LootType = atoi(row[1]);
motd = std::string(row[2]);
}
void Raid::SaveRaidMOTD()
{
std::string query = StringFormat("UPDATE raid_details SET motd = '%s' WHERE raidid = %lu",
EscapeString(motd).c_str(), (unsigned long)GetID());
auto results = database.QueryDatabase(query);
}
bool Raid::LearnMembers()
{
memset(members, 0, (sizeof(RaidMember)*MAX_RAID_MEMBERS));
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT name, groupid, _class, level, isgroupleader, israidleader, islooter FROM raid_members WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){
safe_delete_array(query);
if(mysql_num_rows(result) < 1) {
mysql_free_result(result);
LogFile->write(EQEMuLog::Error, "Error getting raid members for raid %lu: %s", (unsigned long)GetID(), errbuf);
disbandCheck = true;
return(false);
}
int i = 0;
while((row = mysql_fetch_row(result))) {
if(!row[0])
continue;
members[i].member = nullptr;
strn0cpy(members[i].membername, row[0], 64);
int GroupNum = atoi(row[1]);
if(GroupNum > 11)
members[i].GroupNumber = 0xFFFFFFFF;
else
members[i].GroupNumber = GroupNum;
members[i]._class = atoi(row[2]);
members[i].level = atoi(row[3]);
members[i].IsGroupLeader = atoi(row[4]);
members[i].IsRaidLeader = atoi(row[5]);
members[i].IsLooter = atoi(row[6]);
i++;
}
mysql_free_result(result);
}
return(true);
std::string query = StringFormat("SELECT name, groupid, _class, level, "
"isgroupleader, israidleader, islooter "
"FROM raid_members WHERE raidid = %lu",
(unsigned long)GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
return false;
if(results.RowCount() == 0) {
LogFile->write(EQEMuLog::Error, "Error getting raid members for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str());
disbandCheck = true;
return false;
}
int index = 0;
for(auto row = results.begin(); row != results.end(); ++row) {
if(!row[0])
continue;
members[index].member = nullptr;
strn0cpy(members[index].membername, row[0], 64);
int groupNum = atoi(row[1]);
if(groupNum > 11)
members[index].GroupNumber = 0xFFFFFFFF;
else
members[index].GroupNumber = groupNum;
members[index]._class = atoi(row[2]);
members[index].level = atoi(row[3]);
members[index].IsGroupLeader = atoi(row[4]);
members[index].IsRaidLeader = atoi(row[5]);
members[index].IsLooter = atoi(row[6]);
++index;
}
return true;
}
void Raid::VerifyRaid()
@@ -1407,13 +1490,19 @@ void Raid::MemberZoned(Client *c)
if(!c)
return;
// Raid::GetGroup() goes over the members as well, this way we go over once
uint32 gid = RAID_GROUPLESS;
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
if(members[x].member == c)
{
members[x].member = nullptr;
gid = members[x].GroupNumber;
}
}
if (gid < 12 && group_mentor[gid].mentoree == c)
group_mentor[gid].mentoree = nullptr;
}
void Raid::SendHPPacketsTo(Client *c)
@@ -1518,3 +1607,60 @@ void Raid::RaidMessage_StringID(Mob* sender, uint32 type, uint32 string_id, cons
}
}
void Raid::LoadLeadership()
{
database.GetRaidLeadershipInfo(GetID(), nullptr, nullptr, nullptr, nullptr, &raid_aa);
char mentor_name[64];
for (uint32 group_id = 0; group_id < MAX_RAID_GROUPS; group_id++) {
database.GetGroupLeadershipInfo(group_id, GetID(), nullptr, nullptr, nullptr, nullptr,
mentor_name, &group_mentor[group_id].mentor_percent, &group_aa[group_id]);
if (strlen(mentor_name)) {
group_mentor[group_id].name = mentor_name;
mentor_name[0] = '\0';
}
}
}
void Raid::SetGroupMentor(uint32 group_id, int percent, char *name)
{
if (group_id > 11)
return;
group_mentor[group_id].name = name;
group_mentor[group_id].mentor_percent = percent;
Client *client = entity_list.GetClientByName(name);
group_mentor[group_id].mentoree = client ? client : nullptr;
std::string query = StringFormat("UPDATE raid_leaders SET mentoree = '%s', mentor_percent = %i WHERE gid = %i AND rid = %i LIMIT 1",
name, percent, group_id, GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to set raid group mentor: %s\n", results.ErrorMessage().c_str());
}
void Raid::ClearGroupMentor(uint32 group_id)
{
if (group_id > 11)
return;
group_mentor[group_id].name.clear();
group_mentor[group_id].mentor_percent = 0;
group_mentor[group_id].mentoree = nullptr;
std::string query = StringFormat("UPDATE raid_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i AND rid = %i LIMIT 1",
group_id, GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Unable to clear raid group mentor: %s\n", results.ErrorMessage().c_str());
}
// there isn't a nice place to add this in another function, unlike groups
// so we do it here instead
void Raid::CheckGroupMentor(uint32 group_id, Client *c)
{
if (!c || group_id > 11)
return;
if (group_mentor[group_id].name == c->GetName())
group_mentor[group_id].mentoree = c;
}
+44 -3
View File
@@ -38,13 +38,13 @@ enum { //raid packet types:
raidMembers = 6, //len 395+, details + members list
raidNoAssignLeadership = 7,
raidCreate = 8, //len 72
raidUnknown = 9,
raidUnknown = 9, // unused?
raidNoRaid = 10, //parameter=0
raidChangeLootType = 11,
raidStringID = 12,
raidChangeGroupLeader = 13, //136 raid leader, new group leader, group_id?
raidBecomeGroupLeader = 14, //472
raidUnknown2 = 15,
raidSetLeaderAbilities = 14, //472
raidSetLeaderData = 15, // 14,15 SoE names, not sure on difference, 14 packet has 0x100 bytes 15 0x214 in addition to raid general
raidChangeGroup = 16, //?? len 136 old leader, new leader, 0 (preceeded with a remove2)
raidLock = 17, //len 136 leader?, leader, 0
raidUnlock = 18, //len 136 leader?, leader, 0
@@ -79,6 +79,7 @@ enum { //raid command types
#define MAX_RAID_GROUPS 12
#define MAX_RAID_MEMBERS 72
const uint32 RAID_GROUPLESS = 0xFFFFFFFF;
struct RaidMember{
char membername[64];
@@ -91,6 +92,12 @@ struct RaidMember{
bool IsLooter;
};
struct GroupMentor {
std::string name;
Client *mentoree;
int mentor_percent;
};
class Raid : public GroupIDConsumer {
public:
Raid(Client *nLeader);
@@ -111,6 +118,7 @@ public:
void DisbandRaid();
void MoveMember(const char *name, uint32 newGroup);
void SetGroupLeader(const char *who, bool glFlag = true);
Client *GetGroupLeader(uint32 group_id);
void RemoveGroupLeader(const char *who);
bool IsGroupLeader(const char *who);
bool IsRaidMember(const char *name);
@@ -130,6 +138,8 @@ public:
void AddRaidLooter(const char* looter);
void RemoveRaidLooter(const char* looter);
inline void SetRaidMOTD(std::string in_motd) { motd = in_motd; };
//util func
//keeps me from having to keep iterating through the list
//when I want lots of data from the same entry
@@ -160,6 +170,7 @@ public:
//also learns raid structure based on db.
void SetRaidDetails();
void GetRaidDetails();
void SaveRaidMOTD();
bool LearnMembers();
void VerifyRaid();
void MemberZoned(Client *c);
@@ -194,9 +205,34 @@ public:
void SendMakeGroupLeaderPacketAll();
void SendMakeGroupLeaderPacket(const char *who); //13
void SendMakeGroupLeaderPacketTo(const char *who, Client *to);
void SendRaidMOTD(Client *c);
void SendRaidMOTD();
void SendRaidMOTDToWorld();
void QueuePacket(const EQApplicationPacket *app, bool ack_req = true);
// Leadership
void UpdateGroupAAs(uint32 gid);
void SaveGroupLeaderAA(uint32 gid);
void UpdateRaidAAs();
void SaveRaidLeaderAA();
void SendGroupLeadershipAA(Client *c, uint32 gid);
void SendGroupLeadershipAA(uint32 gid);
void SendAllRaidLeadershipAA();
void LoadLeadership();
inline int GetLeadershipAA(int AAID, uint32 gid = 0)
{ if (AAID >= 16) return raid_aa.ranks[AAID - 16]; else return group_aa[gid].ranks[AAID]; }
inline void SetGroupAAs(uint32 gid, GroupLeadershipAA_Struct *glaa)
{ memcpy(&group_aa[gid], glaa, sizeof(GroupLeadershipAA_Struct)); }
inline void SetRaidAAs(RaidLeadershipAA_Struct *rlaa)
{ memcpy(&raid_aa, rlaa, sizeof(RaidLeadershipAA_Struct)); }
void SetGroupMentor(uint32 group_id, int percent, char *name);
void ClearGroupMentor(uint32 group_id);
void CheckGroupMentor(uint32 group_id, Client *c); // this just checks if we should be fixing the pointer in group mentor struct on zone
inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; }
inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; }
RaidMember members[MAX_RAID_MEMBERS];
char leadername[64];
protected:
@@ -206,6 +242,11 @@ protected:
uint32 LootType;
bool disbandCheck;
bool forceDisband;
std::string motd;
RaidLeadershipAA_Struct raid_aa;
GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS];
GroupMentor group_mentor[MAX_RAID_GROUPS];
};
+40 -41
View File
@@ -26,6 +26,7 @@
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/rulesys.h"
#include "../common/string_util.h"
int Mob::GetKickDamage() {
@@ -346,15 +347,23 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction;
//Live AA - Technique of Master Wu
uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
if (bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0, 99))) {
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]);
// always 1/4 of the double attack chance, 25% at rank 5 (100/4)
if ((bDoubleSpecialAttack / 4) > MakeRandomInt(0, 99))
MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]);
int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
if (wuchance) {
if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) {
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
int extra = 1;
// always 1/4 of the double attack chance, 25% at rank 5 (100/4)
if (wuchance / 4 > MakeRandomInt(0, 99))
extra++;
// They didn't add a string ID for this.
std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra);
// live uses 400 here -- not sure if it's the best for all clients though
SendColoredText(400, msg);
while (extra) {
MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]);
extra--;
}
}
}
if(ReuseTime < 100) {
@@ -1412,11 +1421,7 @@ void NPC::DoClassAttacks(Mob *target) {
if(!ca_time)
return;
float HasteModifier = 0;
if (GetHaste())
HasteModifier = 10000 / (100 + GetHaste());
else
HasteModifier = 100;
float HasteModifier = GetHaste() * 0.01f;
int level = GetLevel();
int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will
@@ -1568,7 +1573,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
classattack_timer.Start(reuse*HasteModifier/100);
classattack_timer.Start(reuse / HasteModifier);
}
void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
@@ -1592,20 +1597,13 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
int ReuseTime = 0;
int ClientHaste = GetHaste();
int HasteMod = 0;
float HasteMod = GetHaste() * 0.01f;
if(ClientHaste >= 0){
HasteMod = (10000/(100+ClientHaste)); //+100% haste = 2x as many attacks
}
else{
HasteMod = (100-ClientHaste); //-100% haste = 1/2 as many attacks
}
int32 dmg = 0;
uint16 skill_to_use = -1;
if (skill == -1){
if (skill == -1){
switch(GetClass()){
case WARRIOR:
case RANGER:
@@ -1677,8 +1675,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
}
ReuseTime = BashReuseTime-1;
ReuseTime = (ReuseTime*HasteMod)/100;
ReuseTime = (BashReuseTime - 1) / HasteMod;
DoSpecialAttackDamage(ca_target, SkillBash, dmg, 1,-1,ReuseTime);
@@ -1705,8 +1702,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
if (min_dmg > max_dmg)
max_dmg = min_dmg;
ReuseTime = FrenzyReuseTime-1;
ReuseTime = (ReuseTime*HasteMod)/100;
ReuseTime = (FrenzyReuseTime - 1) / HasteMod;
//Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit.
while(AtkRounds > 0) {
@@ -1756,18 +1752,21 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
return;
//Live AA - Technique of Master Wu
uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
if( bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0,100))) {
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]);
int TripleChance = 25;
if (bDoubleSpecialAttack > 100)
TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100;
if(TripleChance > MakeRandomInt(0,100)) {
MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]);
int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
if (wuchance) {
if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) {
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
int extra = 1;
if (wuchance / 4 > MakeRandomInt(0, 99))
extra++;
// They didn't add a string ID for this.
std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra);
// live uses 400 here -- not sure if it's the best for all clients though
SendColoredText(400, msg);
while (extra) {
MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0, 4)]);
extra--;
}
}
}
}
@@ -1781,7 +1780,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
TryBackstab(ca_target,ReuseTime);
}
ReuseTime = (ReuseTime*HasteMod)/100;
ReuseTime = ReuseTime / HasteMod;
if(ReuseTime > 0 && !IsRiposte){
p_timers.Start(pTimerCombatAbility, ReuseTime);
}
+101 -70
View File
@@ -188,6 +188,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if (!IsPowerDistModSpell(spell_id))
SetSpellPowerDistanceMod(0);
bool SE_SpellTrigger_HasCast = false;
// iterate through the effects in the spell
for (i = 0; i < EFFECT_COUNT; i++)
@@ -1265,10 +1267,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value);
#endif
if (spells[spell_id].base[i] == 1)
// this should catch the cures
if (BeneficialSpell(spell_id) && spells[spell_id].buffduration == 0)
BuffFadeByEffect(SE_Blind);
// handled by client
// TODO: blind flag?
else if (!IsClient())
CalculateNewFearpoint();
break;
}
@@ -2729,6 +2732,25 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
Message(10, "The power of your next illusion spell will flow to your grouped target in your place.");
break;
}
case SE_ApplyEffect: {
if (caster && IsValidSpell(spells[spell_id].base2[i])){
if(MakeRandomInt(0, 100) <= spells[spell_id].base[i])
caster->SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
}
break;
}
case SE_SpellTrigger: {
if (!SE_SpellTrigger_HasCast) {
if (caster && caster->TrySpellTrigger(this, spell_id, i))
SE_SpellTrigger_HasCast = true;
}
break;
}
// Handled Elsewhere
case SE_ImmuneFleeing:
@@ -2839,8 +2861,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_HealRate:
case SE_SkillDamageTaken:
case SE_FcSpellVulnerability:
case SE_SpellTrigger:
case SE_ApplyEffect:
case SE_FcTwincast:
case SE_DelayDeath:
case SE_CastOnFadeEffect:
@@ -3185,55 +3205,55 @@ snare has both of them negative, yet their range should work the same:
case 124: // check sign
result = ubase;
if (caster_level > 50)
result += caster_level - 50;
result += updownsign * (caster_level - 50);
break;
case 125: // check sign
result = ubase;
if (caster_level > 50)
result += 2 * (caster_level - 50);
result += updownsign * 2 * (caster_level - 50);
break;
case 126: // check sign
result = ubase;
if (caster_level > 50)
result += 3 * (caster_level - 50);
result += updownsign * 3 * (caster_level - 50);
break;
case 127: // check sign
result = ubase;
if (caster_level > 50)
result += 4 * (caster_level - 50);
result += updownsign * 4 * (caster_level - 50);
break;
case 128: // check sign
result = ubase;
if (caster_level > 50)
result += 5 * (caster_level - 50);
result += updownsign * 5 * (caster_level - 50);
break;
case 129: // check sign
result = ubase;
if (caster_level > 50)
result += 10 * (caster_level - 50);
result += updownsign * 10 * (caster_level - 50);
break;
case 130: // check sign
result = ubase;
if (caster_level > 50)
result += 15 * (caster_level - 50);
result += updownsign * 15 * (caster_level - 50);
break;
case 131: // check sign
result = ubase;
if (caster_level > 50)
result += 20 * (caster_level - 50);
result += updownsign * 20 * (caster_level - 50);
break;
case 132: // check sign
result = ubase;
if (caster_level > 50)
result += 25 * (caster_level - 50);
result += updownsign * 25 * (caster_level - 50);
break;
case 137: // used in berserker AA desperation
@@ -3375,7 +3395,10 @@ void Mob::BuffProcess()
{
if(buffs[buffs_i].UpdateClient == true)
{
CastToClient()->SendBuffDurationPacket(buffs[buffs_i].spellid, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel);
CastToClient()->SendBuffDurationPacket(buffs[buffs_i]);
// Hack to get UF to play nicer, RoF seems fine without it
if (CastToClient()->GetClientVersion() == EQClientUnderfoot && buffs[buffs_i].numhits > 0)
CastToClient()->SendBuffNumHitPacket(buffs[buffs_i], buffs_i);
buffs[buffs_i].UpdateClient = false;
}
}
@@ -3976,6 +3999,11 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
break;
}
case SE_Blind:
if (curfp && !FindType(SE_Fear))
curfp = false;
break;
case SE_Fear:
{
if(RuleB(Combat, EnableFearPathing)){
@@ -4140,6 +4168,12 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
safe_delete(outapp);
}
if (IsNPC()) {
EQApplicationPacket *outapp = MakeBuffsPacket();
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater, true);
safe_delete(outapp);
}
if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater)
{
EQApplicationPacket *outapp = MakeBuffsPacket(false);
@@ -5534,92 +5568,85 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id)
uint32 buff_max = GetMaxTotalSlots();
//Spell specific procs [Type 7,10,11]
if (IsValidSpell(spell_id)){
for(uint32 d = 0; d < buff_max; d++) {
if((buffs[d].spellid == spell_id) && (buffs[d].numhits > 0) && (spells[buffs[d].spellid].numhitstype == type)){
if(--buffs[d].numhits == 0) {
if (IsValidSpell(spell_id)) {
for (uint32 d = 0; d < buff_max; d++) {
if (buffs[d].spellid == spell_id && buffs[d].numhits > 0 &&
spells[buffs[d].spellid].numhitstype == type) {
if (--buffs[d].numhits == 0) {
CastOnNumHitFade(buffs[d].spellid);
if(!TryFadeEffect(d))
if (!TryFadeEffect(d))
BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d);
}
}
}
}
else if (type == 7){
if (buff_slot > 0){
if(--buffs[buff_slot].numhits == 0) {
} else if (type == 7) {
if (buff_slot > 0) {
if (--buffs[buff_slot].numhits == 0) {
CastOnNumHitFade(buffs[buff_slot].spellid);
if(!TryFadeEffect(buff_slot))
if (!TryFadeEffect(buff_slot))
BuffFadeBySlot(buff_slot , true);
}
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[buff_slot], buff_slot);
}
else {
for(int d = 0; d < buff_max; d++) {
if(!m_spellHitsLeft[d])
} else {
for (int d = 0; d < buff_max; d++) {
if (!m_spellHitsLeft[d])
continue;
if ((IsValidSpell(buffs[d].spellid)) && (m_spellHitsLeft[d] == buffs[d].spellid)) {
if(--buffs[d].numhits == 0) {
if (IsValidSpell(buffs[d].spellid) && m_spellHitsLeft[d] == buffs[d].spellid) {
if (--buffs[d].numhits == 0) {
CastOnNumHitFade(buffs[d].spellid);
m_spellHitsLeft[d] = 0;
if(!TryFadeEffect(d))
if (!TryFadeEffect(d))
BuffFadeBySlot(d, true);
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d);
}
}
}
}
}
else{
for(uint32 d = 0; d < buff_max; d++) {
if((IsValidSpell(buffs[d].spellid)) && (buffs[d].numhits > 0) && (spells[buffs[d].spellid].numhitstype == type)){
if(--buffs[d].numhits == 0) {
} else {
for (uint32 d = 0; d < buff_max; d++) {
if (IsValidSpell(buffs[d].spellid) && buffs[d].numhits > 0 &&
spells[buffs[d].spellid].numhitstype == type) {
if (--buffs[d].numhits == 0) {
CastOnNumHitFade(buffs[d].spellid);
if(!TryFadeEffect(d)){
if (!TryFadeEffect(d))
BuffFadeBySlot(d, true);
}
} else if (IsClient()) { // still have numhits and client, update
CastToClient()->SendBuffNumHitPacket(buffs[d], d);
}
}
}
}
}
//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()
@@ -6453,8 +6480,12 @@ void Mob::ResourceTap(int32 damage, uint16 spellid){
if (spells[spellid].max[i] && (damage > spells[spellid].max[i]))
damage = spells[spellid].max[i];
if (spells[spellid].base2[i] == 0) //HP Tap
SetHP((GetHP()+ damage));
if (spells[spellid].base2[i] == 0){ //HP Tap
if (damage > 0)
HealDamage(damage);
else
Damage(this, -damage,0, SkillEvocation,false);
}
if (spells[spellid].base2[i] == 1) //Mana Tap
SetMana(GetMana() + damage);
+72 -112
View File
@@ -2215,6 +2215,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot);
if(itm && itm->GetItem()->RecastDelay > 0){
CastToClient()->GetPTimers().Start((pTimerItemStart + itm->GetItem()->RecastType), itm->GetItem()->RecastDelay);
EQApplicationPacket *outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct));
ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer;
ird->recast_delay = itm->GetItem()->RecastDelay;
ird->recast_type = itm->GetItem()->RecastType;
CastToClient()->QueuePacket(outapp);
safe_delete(outapp);
}
}
@@ -3122,6 +3128,12 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
safe_delete(outapp);
}
if (IsNPC()) {
EQApplicationPacket *outapp = MakeBuffsPacket();
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater, true);
safe_delete(outapp);
}
// recalculate bonuses since we stripped/added buffs
CalcBonuses();
@@ -3606,108 +3618,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
spell_effectiveness = 100;
}
// Recourse means there is a spell linked to that spell in that the recourse spell will
// be automatically casted on the casters group or the caster only depending on Targettype
// this is for things like dark empathy, shadow vortex
int recourse_spell=0;
recourse_spell = spells[spell_id].RecourseLink;
if(recourse_spell)
{
if(spells[recourse_spell].targettype == ST_Group || spells[recourse_spell].targettype == ST_GroupTeleport)
{
if(IsGrouped())
{
Group *g = entity_list.GetGroupByMob(this);
if(g)
g->CastGroupSpell(this, recourse_spell);
else{
SpellOnTarget(recourse_spell, this);
#ifdef GROUP_BUFF_PETS
if (GetPet())
SpellOnTarget(recourse_spell, GetPet());
#endif
}
}
else if(IsRaidGrouped() && IsClient())
{
Raid *r = entity_list.GetRaidByClient(CastToClient());
uint32 gid = 0xFFFFFFFF;
if(r)
gid = r->GetGroup(GetName());
else
gid = 13; // Forces ungrouped spell casting
if(gid < 12)
{
r->CastGroupSpell(this, recourse_spell, gid);
}
else{
SpellOnTarget(recourse_spell, this);
#ifdef GROUP_BUFF_PETS
if (GetPet())
SpellOnTarget(recourse_spell, GetPet());
#endif
}
}
else if(HasOwner())
{
if(GetOwner()->IsGrouped())
{
Group *g = entity_list.GetGroupByMob(GetOwner());
if(g)
g->CastGroupSpell(this, recourse_spell);
else{
SpellOnTarget(recourse_spell, GetOwner());
SpellOnTarget(recourse_spell, this);
}
}
else if(GetOwner()->IsRaidGrouped() && GetOwner()->IsClient())
{
Raid *r = entity_list.GetRaidByClient(GetOwner()->CastToClient());
uint32 gid = 0xFFFFFFFF;
if(r)
gid = r->GetGroup(GetOwner()->GetName());
else
gid = 13; // Forces ungrouped spell casting
if(gid < 12)
{
r->CastGroupSpell(this, recourse_spell, gid);
}
else
{
SpellOnTarget(recourse_spell, GetOwner());
SpellOnTarget(recourse_spell, this);
}
}
else
{
SpellOnTarget(recourse_spell, GetOwner());
SpellOnTarget(recourse_spell, this);
}
}
else
{
SpellOnTarget(recourse_spell, this);
#ifdef GROUP_BUFF_PETS
if (GetPet())
SpellOnTarget(recourse_spell, GetPet());
#endif
}
}
else
{
SpellOnTarget(recourse_spell, this);
}
}
if(spelltar->spellbonuses.SpellDamageShield && IsDetrimentalSpell(spell_id))
spelltar->DamageShield(this, true);
TrySpellTrigger(spelltar, spell_id);
TryApplyEffect(spelltar, spell_id);
if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) {
int32 aggro_amount = CheckAggroAmount(spell_id, isproc);
mlog(SPELLS__CASTING, "Spell %d cast on %s generated %d hate", spell_id, spelltar->GetName(), aggro_amount);
@@ -3746,7 +3659,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
return false;
}
if (IsValidSpell(spells[spell_id].RecourseLink))
SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
if (IsDetrimentalSpell(spell_id)) {
CheckNumHitsRemaining(NUMHIT_OutgoingSpells);
@@ -4817,8 +4732,8 @@ void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message)
if(send_message)
{
const char *fadetext = spells[spell_id].spell_fades;
outapp = new EQApplicationPacket(OP_BuffFadeMsg, sizeof(BuffFadeMsg_Struct) + strlen(fadetext));
BuffFadeMsg_Struct *bfm = (BuffFadeMsg_Struct *) outapp->pBuffer;
outapp = new EQApplicationPacket(OP_ColoredText, sizeof(ColoredText_Struct) + strlen(fadetext));
ColoredText_Struct *bfm = (ColoredText_Struct *) outapp->pBuffer;
bfm->color = MT_Spells;
memcpy(bfm->msg, fadetext, strlen(fadetext));
QueuePacket(outapp);
@@ -4841,6 +4756,8 @@ void Client::MemSpell(uint16 spell_id, int slot, bool update_client)
m_pp.mem_spells[slot] = spell_id;
mlog(CLIENT__SPELLS, "Spell %d memorized into slot %d", spell_id, slot);
database.SaveCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot);
if(update_client)
{
MemorizeSpell(slot, spell_id, memSpellMemorize);
@@ -4855,6 +4772,8 @@ void Client::UnmemSpell(int slot, bool update_client)
mlog(CLIENT__SPELLS, "Spell %d forgotten from slot %d", m_pp.mem_spells[slot], slot);
m_pp.mem_spells[slot] = 0xFFFFFFFF;
database.DeleteCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot);
if(update_client)
{
MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget);
@@ -4882,6 +4801,7 @@ void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client)
}
m_pp.spell_book[slot] = spell_id;
database.SaveCharacterSpell(this->CharacterID(), spell_id, slot);
mlog(CLIENT__SPELLS, "Spell %d scribed into spell book slot %d", spell_id, slot);
if(update_client)
@@ -4897,7 +4817,8 @@ void Client::UnscribeSpell(int slot, bool update_client)
mlog(CLIENT__SPELLS, "Spell %d erased from spell book slot %d", m_pp.spell_book[slot], slot);
m_pp.spell_book[slot] = 0xFFFFFFFF;
database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot);
if(update_client)
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct));
@@ -4925,8 +4846,9 @@ void Client::UntrainDisc(int slot, bool update_client)
if(slot >= MAX_PP_DISCIPLINES || slot < 0)
return;
mlog(CLIENT__SPELLS, "Discipline %d untrained from slot %d", m_pp.disciplines.values[slot], slot);
mlog(CLIENT__SPELLS, "Discipline %d untrained from slot %d", m_pp.disciplines.values[slot], slot);
m_pp.disciplines.values[slot] = 0;
database.DeleteCharacterDisc(this->CharacterID(), slot);
if(update_client)
{
@@ -5241,20 +5163,40 @@ void Mob::_StopSong()
//Thus I use this in the buff process to update the correct duration once after casting
//this allows AAs and focus effects that increase buff duration to work correctly, but could probably
//be used for other things as well
void Client::SendBuffDurationPacket(uint16 spell_id, int duration, int inlevel)
void Client::SendBuffDurationPacket(Buffs_Struct &buff)
{
EQApplicationPacket* outapp;
outapp = new EQApplicationPacket(OP_Buff, sizeof(SpellBuffFade_Struct));
SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) outapp->pBuffer;
sbf->entityid = GetID();
sbf->slot=2;
sbf->spellid=spell_id;
sbf->slotid=0;
sbf->effect = inlevel > 0 ? inlevel : GetLevel();
sbf->level = inlevel > 0 ? inlevel : GetLevel();
sbf->slot = 2;
sbf->spellid = buff.spellid;
sbf->slotid = 0;
sbf->effect = buff.casterlevel > 0 ? buff.casterlevel : GetLevel();
sbf->level = buff.casterlevel > 0 ? buff.casterlevel : GetLevel();
sbf->bufffade = 0;
sbf->duration = duration;
sbf->duration = buff.ticsremaining;
sbf->num_hits = buff.numhits;
FastQueuePacket(&outapp);
}
void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot)
{
// UF+ use this packet
if (GetClientVersion() < EQClientUnderfoot)
return;
EQApplicationPacket *outapp;
outapp = new EQApplicationPacket(OP_BuffCreate, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct));
BuffIcon_Struct *bi = (BuffIcon_Struct *)outapp->pBuffer;
bi->entity_id = GetID();
bi->count = 1;
bi->all_buffs = 0;
bi->entries[0].buff_slot = slot;
bi->entries[0].spell_id = buff.spellid;
bi->entries[0].tics_remaining = buff.ticsremaining;
bi->entries[0].num_hits = buff.numhits;
FastQueuePacket(&outapp);
}
@@ -5329,6 +5271,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer;
buff->entity_id = GetID();
buff->count = count;
buff->all_buffs = 1;
uint32 index = 0;
for(unsigned int i = 0; i < buff_count; ++i)
@@ -5338,6 +5281,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
buff->entries[index].buff_slot = i;
buff->entries[index].spell_id = buffs[i].spellid;
buff->entries[index].tics_remaining = buffs[i].ticsremaining;
buff->entries[index].num_hits = buffs[i].numhits;
++index;
}
}
@@ -5355,7 +5299,7 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration)
buffs[i].ticsremaining = newDuration;
if(IsClient())
{
CastToClient()->SendBuffDurationPacket(buffs[i].spellid, buffs[i].ticsremaining, buffs[i].casterlevel);
CastToClient()->SendBuffDurationPacket(buffs[i]);
}
}
}
@@ -5406,3 +5350,19 @@ void NPC::UninitializeBuffSlots()
safe_delete_array(buffs);
}
void Client::SendSpellAnim(uint16 targetid, uint16 spell_id)
{
if (!targetid || !IsValidSpell(spell_id))
return;
EQApplicationPacket app(OP_Action, sizeof(Action_Struct));
Action_Struct* a = (Action_Struct*)app.pBuffer;
a->target = targetid;
a->source = this->GetID();
a->type = 231;
a->spell = spell_id;
a->sequence = 231;
app.priority = 1;
entity_list.QueueCloseClients(this, &app);
}
+7
View File
@@ -163,6 +163,8 @@
#define CANNOT_WAKE 555 //%1 tells you, 'I am unable to wake %2, master.'
#define GUILD_NAME_IN_USE 711 //You cannot create a guild with that name, that guild already exists on this server.
#define GM_GAINXP 1002 //[GM] You have gained %1 AXP and %2 EXP (%3).
#define MALE_SLAYUNDEAD 1007 //%1's holy blade cleanses his target!(%2)
#define FEMALE_SLAYUNDEAD 1008 //%1's holy blade cleanses her target!(%2)
#define FINISHING_BLOW 1009 //%1 scores a Finishing Blow!!
#define ASSASSINATES 1016 //%1 ASSASSINATES their victim!!
#define CRIPPLING_BLOW 1021 //%1 lands a Crippling Blow!(%2)
@@ -249,6 +251,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,8 +273,11 @@
#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 ALREADY_IN_RAID 5060 //%1 is already in a raid.
#define GAIN_RAIDEXP 5085 //You gained raid experience!
#define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there.
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
+617 -818
View File
File diff suppressed because it is too large Load Diff
+287 -409
View File
@@ -142,7 +142,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme
// Adding augment
if (in_augment->augment_slot == -1)
{
if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) &&
if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) &&
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
{
tobe_auged->PutAugment(slot, *auged_with);
@@ -424,38 +424,28 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
return;
}
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
uint32 qlen = 0;
uint8 qcount = 0;
//pull the list of components
qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount "
" FROM tradeskill_recipe_entries AS tre "
" WHERE tre.componentcount > 0 AND tre.recipe_id=%u", rac->recipe_id);
if (!database.RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query, errbuf);
safe_delete_array(query);
//pull the list of components
std::string query = StringFormat("SELECT tre.item_id, tre.componentcount "
"FROM tradeskill_recipe_entries AS tre "
"WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
rac->recipe_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount < 1) {
if(results.RowCount() < 1) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: no components returned");
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
if(qcount > 10) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", qcount);
if(results.RowCount() > 10) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", results.RowCount());
user->QueuePacket(outapp);
safe_delete(outapp);
return;
@@ -466,17 +456,15 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
uint8 counts[10];
memset(counts, 0, sizeof(counts));
//search for all the items in their inventory
Inventory& user_inv = user->GetInv();
uint8 count = 0;
uint8 needcount = 0;
uint8 r,k;
std::list<int> MissingItems;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
uint8 needItemIndex = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++needItemIndex) {
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
@@ -491,10 +479,9 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
MissingItems.push_back(item);
//dont start deleting anything until we have found it all.
items[r] = item;
counts[r] = num;
items[needItemIndex] = item;
counts[needItemIndex] = num;
}
mysql_free_result(result);
//make sure we found it all...
if(count != needcount)
@@ -520,12 +507,12 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
//remove all the items from the players inventory, with updates...
int16 slot;
for(r = 0; r < qcount; r++) {
for(uint8 r = 0; r < results.RowCount(); r++) {
if(items[r] == 0 || counts[r] == 0)
continue; //skip empties, could prolly break here
//we have to loop here to delete 1 at a time in case its in multiple stacks.
for(k = 0; k < counts[r]; k++) {
for(uint8 k = 0; k < counts[r]; k++) {
slot = user_inv.HasItem(items[r], 1, invWherePersonal);
if (slot == INVALID_INDEX) {
//WTF... I just checked this above, but just to be sure...
@@ -539,19 +526,14 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
const ItemInst* inst = user_inv.GetItem(slot);
if (inst && !inst->IsStackable())
{
user->DeleteItemInInventory(slot, 0, true);
}
else
{
user->DeleteItemInInventory(slot, 1, true);
}
}
}
//otherwise, we found it all...
outp->reply_code = 0x00000000; //success for finding it...
user->QueuePacket(outapp);
safe_delete(outapp);
@@ -654,34 +636,26 @@ SkillUseTypes Object::TypeToSkill(uint32 type)
return TradeskillUnknown;
}
void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid) {
void Client::TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid) {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
if (!database.RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query, errbuf);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
uint8 qcount = 0;
if(results.RowCount() < 1)
return; //search gave no results... not an error
qcount = mysql_num_rows(result);
if(qcount < 1) {
//search gave no results... not an error
return;
}
if(mysql_num_fields(result) != 6) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query);
if(results.ColumnCount() != 6) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query.c_str());
return;
}
uint8 r;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
for(auto row = results.begin(); row != results.end(); ++row) {
if(row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || row[5] == nullptr)
continue;
uint32 recipe = (uint32)atoi(row[0]);
const char *name = row[1];
uint32 trivial = (uint32) atoi(row[2]);
@@ -691,14 +665,10 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi
// Skip the recipes that exceed the threshold in skill difference
// Recipes that have either been made before or were
// explicitly learned are excempt from that limit
if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)) {
if (((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff)
&& row[4] == nullptr)
{
if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)
&& ((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff)
&& row[4] == nullptr)
continue;
}
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct));
RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer;
@@ -711,39 +681,30 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi
strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name));
FastQueuePacket(&outapp);
}
mysql_free_result(result);
}
void Client::SendTradeskillDetails(uint32 recipe_id) {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
uint32 qlen = 0;
uint8 qcount = 0;
//pull the list of components
qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount,i.icon,i.Name "
" FROM tradeskill_recipe_entries AS tre "
" LEFT JOIN items AS i ON tre.item_id = i.id "
" WHERE tre.componentcount > 0 AND tre.recipe_id=%u", recipe_id);
if (!database.RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query, errbuf);
safe_delete_array(query);
//pull the list of components
std::string query = StringFormat("SELECT tre.item_id,tre.componentcount,i.icon,i.Name "
"FROM tradeskill_recipe_entries AS tre "
"LEFT JOIN items AS i ON tre.item_id = i.id "
"WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
recipe_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount < 1) {
if(results.RowCount() < 1) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: no components returned");
return;
}
if(qcount > 10) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", qcount);
if(results.RowCount() > 10) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", results.RowCount());
return;
}
@@ -773,20 +734,18 @@ void Client::SendTradeskillDetails(uint32 recipe_id) {
uint32 len;
uint32 datalen = 0;
uint8 count = 0;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
for(auto row = results.begin(); row != results.end(); ++row) {
//watch for references to items which are not in the
//items table, which the left join will make nullptr...
if(row[2] == nullptr || row[3] == nullptr) {
if(row[2] == nullptr || row[3] == nullptr)
continue;
}
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
uint32 icon = (uint32) atoi(row[2]);
const char *name = row[3];
len = strlen(name);
if(len > 63)
@@ -816,7 +775,6 @@ void Client::SendTradeskillDetails(uint32 recipe_id) {
}
}
mysql_free_result(result);
//now move the item data over top of the FFFFs
uint8 dist = sizeof(uint32) * (10 - count);
@@ -1094,7 +1052,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
++itr;
}
return(true);
}
}
/* Tradeskill Fail */
else {
success_modifier = 2; // Halves the chance
@@ -1172,7 +1130,7 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
chance_stage2 = 12.5 - (.08 * (current_raw_skill - 175));
}
}
chance_stage2 = mod_tradeskill_skillup(chance_stage2);
if (chance_stage2 > MakeRandomFloat(0, 99)) {
@@ -1191,249 +1149,212 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id,
uint32 char_id, DBTradeskillRecipe_Struct *spec)
{
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
char buf2[4096];
uint32 sum = 0;
uint32 count = 0;
uint32 qcount = 0;
uint32 qlen = 0;
// make where clause segment for container(s)
char containers[30];
if (some_id == 0) {
// world combiner so no item number
snprintf(containers,29, "= %u", c_type);
} else {
// container in inventory
snprintf(containers,29, "in (%u,%u)", c_type, some_id);
}
buf2[0] = '\0';
std::string containers;// make where clause segment for container(s)
if (some_id == 0)
containers = StringFormat("= %u", c_type); // world combiner so no item number
else
containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory
//Could prolly watch for stacks in this loop and handle them properly...
//just increment sum and count accordingly
bool first = true;
uint8 i;
char *pos = buf2;
for (i = 0; i < 10; i++) { // <watch> TODO: need to determine if this is bound to world/item container size
std::string buf2;
uint32 count = 0;
uint32 sum = 0;
for (uint8 i = 0; i < 10; i++) { // <watch> TODO: need to determine if this is bound to world/item container size
const ItemInst* inst = container->GetItem(i);
if (inst) {
const Item_Struct* item = GetItem(inst->GetItem()->ID);
if (item) {
if(first) {
pos += snprintf(pos, 19, "%d", item->ID);
first = false;
} else {
pos += snprintf(pos, 19, ",%d", item->ID);
}
sum += item->ID;
count++;
}
}
}
*pos = '\0';
if (!inst)
continue;
if(count < 1) {
return(false); //no items == no recipe
const Item_Struct* item = GetItem(inst->GetItem()->ID);
if (!item)
continue;
if(first) {
buf2 += StringFormat("%d", item->ID);
first = false;
} else
buf2 += StringFormat(",%d", item->ID);
sum += item->ID;
count++;
}
qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id "
" FROM tradeskill_recipe_entries AS tre"
" INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) "
" WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount>0 )"
" OR ( tre.item_id %s AND tre.iscontainer=1 ))"
" GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
" AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum);
if(count == 0)
return false; //no items == no recipe
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query);
safe_delete_array(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf);
return(false);
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount > 1) {
//multiple recipes, partial match... do an extra query to get it exact.
//this happens when combining components for a smaller recipe
//which is completely contained within another recipe
first = true;
pos = buf2;
for (i = 0; i < qcount; i++) {
row = mysql_fetch_row(result);
uint32 recipeid = (uint32)atoi(row[0]);
if(first) {
pos += snprintf(pos, 19, "%u", recipeid);
first = false;
} else {
pos += snprintf(pos, 19, ",%u", recipeid);
}
//length limit on buf2
if(i == 214) { //Maximum number of recipe matches (19 * 215 = 4096)
LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", i + 1, qcount);
break;
}
}
qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id"
" FROM tradeskill_recipe_entries AS tre"
" WHERE tre.recipe_id IN (%s)"
" GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
" AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum);
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query);
safe_delete_array(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
return(false);
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
std::string query = StringFormat("SELECT tre.recipe_id "
"FROM tradeskill_recipe_entries AS tre "
"INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) "
"WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount > 0) "
"OR ( tre.item_id %s AND tre.iscontainer=1 ))"
"GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
"AND sum(tre.item_id * tre.componentcount) = %u",
buf2.c_str(), containers.c_str(), count, sum);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str());
return false;
}
if(qcount < 1)
return(false);
if (results.RowCount() > 1) {
//multiple recipes, partial match... do an extra query to get it exact.
//this happens when combining components for a smaller recipe
//which is completely contained within another recipe
first = true;
uint32 index = 0;
buf2 = "";
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
uint32 recipeid = (uint32)atoi(row[0]);
if(first) {
buf2 += StringFormat("%u", recipeid);
first = false;
} else
buf2 += StringFormat(",%u", recipeid);
if(qcount > 1)
{
//length limit on buf2
if(index == 214) { //Maximum number of recipe matches (19 * 215 = 4096)
LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", index + 1, results.RowCount());
break;
}
}
query = StringFormat("SELECT tre.recipe_id "
"FROM tradeskill_recipe_entries AS tre "
"WHERE tre.recipe_id IN (%s) "
"GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
"AND sum(tre.item_id * tre.componentcount) = %u", buf2.c_str(), count, sum);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
return false;
}
}
if (results.RowCount() < 1)
return false;
if(results.RowCount() > 1) {
//The recipe is not unique, so we need to compare the container were using.
uint32 containerId = 0;
if(some_id) { //Standard container
if(some_id) //Standard container
containerId = some_id;
}
else if(c_type) { //World container
else if(c_type)//World container
containerId = c_type;
}
else { //Invalid container
return(false);
else //Invalid container
return false;
query = StringFormat("SELECT tre.recipe_id "
"FROM tradeskill_recipe_entries AS tre "
"WHERE tre.recipe_id IN (%s) "
"AND tre.item_id = %u;", buf2.c_str(), containerId);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
return false;
}
qlen = MakeAnyLenString(&query,"SELECT tre.recipe_id FROM tradeskill_recipe_entries as tre WHERE tre.recipe_id IN (%s)"
" AND tre.item_id = %u;",buf2,containerId);
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query);
safe_delete_array(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
return(false);
}
safe_delete_array(query);
uint32 resultRowTotal = mysql_num_rows(result);
if(resultRowTotal == 0) { //Recipe contents matched more than 1 recipe, but not in this container
if(results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container
LogFile->write(EQEMuLog::Error, "Combine error: Incorrect container is being used!");
return(false);
}
if(resultRowTotal > 1) { //Recipe contents matched more than 1 recipe in this container
LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", resultRowTotal, containerId);
return false;
}
if (results.RowCount() > 1) //Recipe contents matched more than 1 recipe in this container
LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", results.RowCount(), containerId);
}
row = mysql_fetch_row(result);
auto row = results.begin();
uint32 recipe_id = (uint32)atoi(row[0]);
mysql_free_result(result);
//Right here we verify that we actually have ALL of the tradeskill components..
//instead of part which is possible with experimentation.
//This is here because something's up with the query above.. it needs to be rethought out
bool has_components = true;
char TSerrbuf[MYSQL_ERRMSG_SIZE];
char *TSquery = 0;
MYSQL_RES *TSresult;
MYSQL_ROW TSrow;
if (RunQuery(TSquery, MakeAnyLenString(&TSquery, "SELECT item_id, componentcount from tradeskill_recipe_entries where recipe_id=%i AND componentcount > 0", recipe_id), TSerrbuf, &TSresult)) {
while((TSrow = mysql_fetch_row(TSresult))!=nullptr) {
int ccnt = 0;
for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) {
const ItemInst* inst = container->GetItem(x);
if(inst){
const Item_Struct* item = GetItem(inst->GetItem()->ID);
if (item) {
if(item->ID == atoi(TSrow[0])){
ccnt++;
}
}
}
}
if(ccnt != atoi(TSrow[1]))
has_components = false;
}
mysql_free_result(TSresult);
} else {
LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", TSquery, TSerrbuf);
}
safe_delete_array(TSquery);
if(has_components == false){
query = StringFormat("SELECT item_id, componentcount "
"FROM tradeskill_recipe_entries "
"WHERE recipe_id = %i AND componentcount > 0",
recipe_id);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
}
return false;
}
if (results.RowCount() == 0)
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
return(GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec));
for (auto row = results.begin(); row != results.end(); ++row) {
int ccnt = 0;
for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) {
const ItemInst* inst = container->GetItem(x);
if(!inst)
continue;
const Item_Struct* item = GetItem(inst->GetItem()->ID);
if (!item)
continue;
if(item->ID == atoi(row[0]))
ccnt++;
}
if(ccnt != atoi(row[1]))
return false;
}
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
}
bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id,
uint32 char_id, DBTradeskillRecipe_Struct *spec)
{
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
uint32 qcount = 0;
uint32 qlen;
// make where clause segment for container(s)
char containers[30];
if (some_id == 0) {
// world combiner so no item number
snprintf(containers,29, "= %u", c_type);
} else {
// container in inventory
snprintf(containers,29, "in (%u,%u)", c_type, some_id);
std::string containers;
if (some_id == 0)
containers = StringFormat("= %u", c_type); // world combiner so no item number
else
containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory
std::string query = StringFormat("SELECT tr.id, tr.tradeskill, tr.skillneeded, "
"tr.trivial, tr.nofail, tr.replace_container, "
"tr.name, tr.must_learn, tr.quest, crl.madecount "
"FROM tradeskill_recipe AS tr "
"INNER JOIN tradeskill_recipe_entries AS tre "
"ON tr.id = tre.recipe_id "
"LEFT JOIN (SELECT recipe_id, madecount "
"FROM char_recipe_list WHERE char_id = %u) AS crl "
"ON tr.id = crl.recipe_id "
"WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled "
"GROUP BY tr.id",
char_id, (unsigned long)recipe_id, containers.c_str());
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
return false;
}
qlen = MakeAnyLenString(&query, "SELECT tr.id, tr.tradeskill, tr.skillneeded,"
" tr.trivial, tr.nofail, tr.replace_container, tr.name, tr.must_learn, tr.quest, crl.madecount"
" FROM tradeskill_recipe AS tr inner join tradeskill_recipe_entries as tre"
" ON tr.id = tre.recipe_id"
" LEFT JOIN (SELECT recipe_id, madecount from char_recipe_list WHERE char_id = %u) AS crl "
" ON tr.id = crl.recipe_id "
" WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled "
" GROUP BY tr.id", char_id, (unsigned long)recipe_id, containers);
if(results.RowCount() != 1)
return false;//just not found i guess..
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query);
safe_delete_array(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
return(false);
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount != 1) {
//just not found i guess..
return(false);
}
row = mysql_fetch_row(result);
spec->tradeskill = (SkillUseTypes)atoi(row[1]);
spec->skill_needed = (int16)atoi(row[2]);
spec->trivial = (uint16)atoi(row[3]);
spec->nofail = atoi(row[4]) ? true : false;
auto row = results.begin();
spec->tradeskill = (SkillUseTypes)atoi(row[1]);
spec->skill_needed = (int16)atoi(row[2]);
spec->trivial = (uint16)atoi(row[3]);
spec->nofail = atoi(row[4]) ? true : false;
spec->replace_container = atoi(row[5]) ? true : false;
spec->name = row[6];
spec->must_learn = (uint8)atoi(row[7]);
spec->quest = atoi(row[8]) ? true : false;
if (row[9] == nullptr) {
spec->has_learnt = false;
spec->madecount = 0;
@@ -1442,141 +1363,109 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id
spec->madecount = (uint32)atoul(row[9]);
}
spec->recipe_id = recipe_id;
mysql_free_result(result);
//Pull the on-success items...
qlen = MakeAnyLenString(&query, "SELECT item_id,successcount FROM tradeskill_recipe_entries"
" WHERE successcount>0 AND recipe_id=%u", recipe_id);
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query, errbuf);
safe_delete_array(query);
return(false);
query = StringFormat("SELECT item_id,successcount FROM tradeskill_recipe_entries "
"WHERE successcount > 0 AND recipe_id = %u", recipe_id);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount < 1) {
if(results.RowCount() < 1) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned");
return(false);
return false;
}
uint8 r;
spec->onsuccess.clear();
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
for(auto row = results.begin(); row != results.end(); ++row) {
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
spec->onsuccess.push_back(std::pair<uint32,uint8>(item, num));
}
mysql_free_result(result);
spec->onfail.clear();
//Pull the on-fail items...
qlen = MakeAnyLenString(&query, "SELECT item_id,failcount FROM tradeskill_recipe_entries"
" WHERE failcount>0 AND recipe_id=%u", recipe_id);
spec->onfail.clear();
if (RunQuery(query, qlen, errbuf, &result)) {
qcount = mysql_num_rows(result);
uint8 r;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
query = StringFormat("SELECT item_id, failcount FROM tradeskill_recipe_entries "
"WHERE failcount > 0 AND recipe_id = %u", recipe_id);
results = QueryDatabase(query);
if (results.Success())
for(auto row = results.begin(); row != results.end(); ++row) {
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
spec->onfail.push_back(std::pair<uint32,uint8>(item, num));
}
mysql_free_result(result);
}
spec->salvage.clear();
// Don't bother with the query if TS is nofail
if (spec->nofail)
return true;
// Pull the salvage list
qlen = MakeAnyLenString(&query, "SELECT item_id,salvagecount FROM tradeskill_recipe_entries WHERE salvagecount>0 AND recipe_id=%u", recipe_id);
spec->salvage.clear();
// Don't bother with the query if TS is nofail
if (!spec->nofail && RunQuery(query, qlen, errbuf, &result)) {
qcount = mysql_num_rows(result);
uint8 r;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
query = StringFormat("SELECT item_id, salvagecount "
"FROM tradeskill_recipe_entries "
"WHERE salvagecount > 0 AND recipe_id = %u", recipe_id);
results = QueryDatabase(query);
if (results.Success())
for(auto row = results.begin(); row != results.begin(); ++row) {
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8)atoi(row[1]);
spec->salvage.push_back(std::pair<uint32,uint8>(item, num));
}
mysql_free_result(result);
}
safe_delete_array(query);
return(true);
return true;
}
void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount)
void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madeCount)
{
char *query = 0;
uint32 qlen;
char errbuf[MYSQL_ERRMSG_SIZE];
qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list "
" SET recipe_id = %u, char_id = %u, madecount = %u "
" ON DUPLICATE KEY UPDATE madecount = %u;"
, recipe_id, char_id, madecount, madecount);
if (!RunQuery(query, qlen, errbuf)) {
LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query, errbuf);
}
safe_delete_array(query);
std::string query = StringFormat("INSERT INTO char_recipe_list "
"SET recipe_id = %u, char_id = %u, madecount = %u "
"ON DUPLICATE KEY UPDATE madecount = %u;",
recipe_id, char_id, madeCount, madeCount);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
void Client::LearnRecipe(uint32 recipeID)
{
char *query = 0;
uint32 qlen;
uint32 qcount = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
qlen = MakeAnyLenString(&query, "SELECT tr.name, crl.madecount "
" FROM tradeskill_recipe as tr "
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl "
" ON tr.id = crl.recipe_id "
" WHERE tr.id = %u ;", CharacterID(), recipeID);
if (!database.RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("SELECT tr.name, crl.madecount "
"FROM tradeskill_recipe AS tr "
"LEFT JOIN (SELECT recipe_id, madecount "
"FROM char_recipe_list WHERE char_id = %u) AS crl "
"ON tr.id = crl.recipe_id "
"WHERE tr.id = %u ;", CharacterID(), recipeID);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
qcount = mysql_num_rows(result);
if (qcount != 1) {
LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, qcount);
mysql_free_result(result);
safe_delete_array(query);
if (results.RowCount() != 1) {
LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, results.RowCount());
return;
}
safe_delete_array(query);
row = mysql_fetch_row(result);
auto row = results.begin();
if (row != nullptr && row[0] != nullptr) {
// Only give Learn message if character doesn't know the recipe
if (row[1] == nullptr) {
Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]);
// Actually learn the recipe now
qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list "
" SET recipe_id = %u, char_id = %u, madecount = 0 "
" ON DUPLICATE KEY UPDATE madecount = madecount;"
, recipeID, CharacterID());
if (row[0] == nullptr)
return;
if (!database.RunQuery(query, qlen, errbuf)) {
LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query, errbuf);
}
safe_delete_array(query);
}
}
// Only give Learn message if character doesn't know the recipe
if (row[1] != nullptr)
return;
mysql_free_result(result);
Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]);
// Actually learn the recipe now
query = StringFormat("INSERT INTO char_recipe_list "
"SET recipe_id = %u, char_id = %u, madecount = 0 "
"ON DUPLICATE KEY UPDATE madecount = madecount;",
recipeID, CharacterID());
results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
@@ -1622,33 +1511,22 @@ bool Client::CanIncreaseTradeskill(SkillUseTypes tradeskill) {
bool ZoneDatabase::EnableRecipe(uint32 recipe_id)
{
char *query = 0;
uint32 qlen;
char errbuf[MYSQL_ERRMSG_SIZE];
uint32 affected_rows = 0;
std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 1 "
"WHERE id = %u;", recipe_id);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 1 WHERE id = %u;", recipe_id);
if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) {
LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query, errbuf);
}
safe_delete_array(query);
return (affected_rows > 0);
return results.RowsAffected() > 0;
}
bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
{
char *query = 0;
uint32 qlen;
char errbuf[MYSQL_ERRMSG_SIZE];
uint32 affected_rows = 0;
std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 0 "
"WHERE id = %u;", recipe_id);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 0 WHERE id = %u;", recipe_id);
if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) {
LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query, errbuf);
}
safe_delete_array(query);
return (affected_rows > 0);
return results.RowsAffected() > 0;
}
+329 -404
View File
@@ -133,7 +133,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) {
client->Kick();
return;
}
SendItemData(inst, trade_slot_id);
_log(TRADING__HOLDER, "%s added item '%s' to trade slot %i", owner->GetName(), inst->GetItem()->Name, trade_slot_id);
@@ -451,13 +451,13 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
mlog(TRADING__CLIENT, "Finishing trade with client %s", other->GetName());
this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true);
// step 0: pre-processing
// QS code
if (RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) {
qs_audit = (QSPlayerLogTrade_Struct*)event_entry;
qs_log = true;
if (finalizer) {
qs_audit->char2_id = this->character_id;
@@ -506,7 +506,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
detail->aug_5 = inst->GetAugmentItemID(5);
event_details->push_back(detail);
if (finalizer)
qs_audit->char2_count += detail->charges;
else
@@ -886,7 +886,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
if (baginst) {
const Item_Struct* bagitem = baginst->GetItem();
if (bagitem && (GetGM() || (bagitem->NoDrop != 0 && baginst->IsInstNoDrop() == false))) {
tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist,
tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist,
baginst->GetCharges(), 1, 127, true, true);
}
else if (RuleB(NPC, ReturnNonQuestNoDropItems)) {
@@ -895,8 +895,8 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
}
}
}
tradingWith->CastToNPC()->AddLootDrop(item, &tradingWith->CastToNPC()->itemlist,
tradingWith->CastToNPC()->AddLootDrop(item, &tradingWith->CastToNPC()->itemlist,
inst->GetCharges(), 1, 127, true, true);
}
// Return NO DROP and Attuned items being handed into a non-quest NPC if the rule is true
@@ -913,14 +913,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
if(!tradingWith->IsMoving())
tradingWith->FaceTarget(this);
/* Set a timestamp in an entity variable for plugin check_handin.pl in return_items
This will stopgap players from items being returned if global_npc.pl has a catch all return_items
*/
struct timeval read_time;
char buffer[50];
gettimeofday(&read_time, 0);
sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec);
this->SetEntityVariable("Stop_Return", buffer);
this->EVENT_ITEM_ScriptStopReturn();
}
}
@@ -938,12 +931,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
parse->AddVar(temp1, temp2);
snprintf(temp1, 100, "platinum.%d", tradingWith->GetNPCTypeID());
snprintf(temp2, 100, "%u", trade->pp);
parse->AddVar(temp1, temp2);
parse->AddVar(temp1, temp2);
if(tradingWith->GetAppearance() != eaDead) {
tradingWith->FaceTarget(this);
}
ItemInst *insts[4] = { 0 };
for(int i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_NPC_END; ++i) {
insts[i - EmuConstants::TRADE_BEGIN] = m_inv.PopItem(i);
@@ -1475,18 +1468,16 @@ void Client::TradeRequestFailed(const EQApplicationPacket* app) {
}
static void BazaarAuditTrail(const char *Seller, const char *Buyer, const char *ItemName, int Quantity, int TotalCost, int TranType) {
static void BazaarAuditTrail(const char *seller, const char *buyer, const char *itemName, int quantity, int totalCost, int tranType) {
const char *AuditQuery="INSERT INTO `trader_audit` (`time`, `seller`, `buyer`, `itemname`, `quantity`, `totalcost`, `trantype`) "
"VALUES (NOW(), '%s', '%s', '%s', %i, %i, %i)";
std::string query = StringFormat("INSERT INTO `trader_audit` "
"(`time`, `seller`, `buyer`, `itemname`, `quantity`, `totalcost`, `trantype`) "
"VALUES (NOW(), '%s', '%s', '%s', %i, %i, %i)",
seller, buyer, itemName, quantity, totalCost, tranType);
auto results = database.QueryDatabase(query);
if(!results.Success())
_log(TRADING__CLIENT, "Audit write error: %s : %s", query.c_str(), results.ErrorMessage().c_str());
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
if(!database.RunQuery(query, MakeAnyLenString(&query, AuditQuery, Seller, Buyer, ItemName, Quantity, TotalCost, TranType), errbuf))
_log(TRADING__CLIENT, "Audit write error: %s : %s", query, errbuf);
safe_delete_array(query);
}
@@ -1624,365 +1615,326 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
}
void Client::SendBazaarWelcome(){
void Client::SendBazaarWelcome()
{
const std::string query = "SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader";
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() == 1){
auto row = results.begin();
char errbuf[MYSQL_ERRMSG_SIZE];
EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct));
char* query = 0;
memset(outapp->pBuffer,0,outapp->size);
MYSQL_RES *result;
BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer;
MYSQL_ROW row;
bws->Beginning.Action = BazaarWelcome;
if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct char_id),count(char_id) from trader"),errbuf,&result)){
if(mysql_num_rows(result)==1){
bws->Traders = atoi(row[0]);
bws->Items = atoi(row[1]);
row = mysql_fetch_row(result);
QueuePacket(outapp);
EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct));
memset(outapp->pBuffer,0,outapp->size);
BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer;
bws->Beginning.Action = BazaarWelcome;
bws->Items = atoi(row[1]);
bws->Traders = atoi(row[0]);
QueuePacket(outapp);
safe_delete(outapp);
}
mysql_free_result(result);
safe_delete(outapp);
}
safe_delete_array(query);
if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct charid) from buyer"),errbuf,&result)){
if(mysql_num_rows(result)==1) {
const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer";
results = database.QueryDatabase(buyerCountQuery);
if (!results.Success() || results.RowCount() != 1)
return;
row = mysql_fetch_row(result);
Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them,"
" or use /buyer to set up your own Buy Lines.", atoi(row[0]));
}
mysql_free_result(result);
}
safe_delete_array(query);
auto row = results.begin();
Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, "
"or use /buyer to set up your own Buy Lines.", atoi(row[0]));
}
void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint32 ItemStat, uint32 Slot, uint32 Type,
char Name[64], uint32 MinPrice, uint32 MaxPrice) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* Query = 0;
std::string Search, Values;
MYSQL_RES *Result;
MYSQL_ROW Row;
char Tmp[100] = {0};
std::string searchValues = " COUNT(item_id), trader.*, items.name ";
std::string searchCriteria = " WHERE trader.item_id = items.id ";
Values.append("count(item_id),trader.*,items.name");
if(TraderID > 0) {
Client* trader = entity_list.GetClientByID(TraderID);
Search.append("where trader.item_id=items.id");
if(trader)
searchCriteria.append(StringFormat(" AND trader.char_id = %i", trader->CharacterID()));
}
if(TraderID > 0){
Client* Trader = entity_list.GetClientByID(TraderID);
if(MinPrice != 0)
searchCriteria.append(StringFormat(" AND trader.item_cost >= %i", MinPrice));
if(Trader){
sprintf(Tmp," and trader.char_id=%i",Trader->CharacterID());
Search.append(Tmp);
}
if(MaxPrice != 0)
searchCriteria.append(StringFormat(" AND trader.item_cost <= %i", MaxPrice));
if(strlen(Name) > 0) {
char *safeName = RemoveApostrophes(Name);
searchCriteria.append(StringFormat(" AND items.name LIKE '%%%s%%'", safeName));
safe_delete_array(safeName);
}
std::string SearchrResults;
if(MinPrice != 0){
sprintf(Tmp, " and trader.item_cost>=%i", MinPrice);
Search.append(Tmp);
}
if(MaxPrice != 0){
sprintf(Tmp, " and trader.item_cost<=%i", MaxPrice);
Search.append(Tmp);
}
if(strlen(Name) > 0){
char *SafeName = RemoveApostrophes(Name);
sprintf(Tmp, " and items.name like '%%%s%%'", SafeName);
safe_delete_array(SafeName);
Search.append(Tmp);
}
if(Class_ != 0xFFFFFFFF){
sprintf(Tmp, " and mid(reverse(bin(items.classes)),%i,1)=1", Class_);
Search.append(Tmp);
}
if(Race!=0xFFFFFFFF){
sprintf(Tmp, " and mid(reverse(bin(items.races)),%i,1)=1", Race);
Search.append(Tmp);
}
if(Slot!=0xFFFFFFFF){
sprintf(Tmp, " and mid(reverse(bin(items.slots)),%i,1)=1", Slot + 1);
Search.append(Tmp);
}
if(Type!=0xFFFFFFFF){
if(Class_ != 0xFFFFFFFF)
searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.classes)), %i, 1) = 1", Class_));
switch(Type){
if(Race != 0xFFFFFFFF)
searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.races)), %i, 1) = 1", Race));
case 0:
// 1H Slashing
Search.append(" and items.itemtype=0 and damage>0");
break;
case 31:
Search.append(" and items.itemclass=2");
break;
case 46:
Search.append(" and items.spellid>0 and items.spellid<65000");
break;
case 47:
Search.append(" and items.spellid=998");
break;
case 48:
Search.append(" and items.spellid>=1298 and items.spellid<=1307");
break;
case 49:
Search.append(" and items.focuseffect>0");
break;
default:
sprintf(Tmp, " and items.itemtype=%i", Type);
Search.append(Tmp);
}
}
if(Slot != 0xFFFFFFFF)
searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.slots)), %i, 1) = 1", Slot + 1));
switch(Type){
case 0xFFFFFFFF:
break;
case 0:
// 1H Slashing
searchCriteria.append(" AND items.itemtype = 0 AND damage > 0");
break;
case 31:
searchCriteria.append(" AND items.itemclass = 2");
break;
case 46:
searchCriteria.append(" AND items.spellid > 0 AND items.spellid < 65000");
break;
case 47:
searchCriteria.append(" AND items.spellid = 998");
break;
case 48:
searchCriteria.append(" AND items.spellid >= 1298 AND items.spellid <= 1307");
break;
case 49:
searchCriteria.append(" AND items.focuseffect > 0");
break;
default:
searchCriteria.append(StringFormat(" AND items.itemtype = %i", Type));
}
switch(ItemStat) {
case STAT_AC:
Search.append(" and items.ac>0");
Values.append(",items.ac");
searchCriteria.append(" AND items.ac > 0");
searchValues.append(", items.ac");
break;
case STAT_AGI:
Search.append(" and items.aagi>0");
Values.append(",items.aagi");
searchCriteria.append(" AND items.aagi > 0");
searchValues.append(", items.aagi");
break;
case STAT_CHA:
Search.append(" and items.acha>0");
Values.append(",items.acha");
searchCriteria.append(" AND items.acha > 0");
searchValues.append(", items.acha");
break;
case STAT_DEX:
Search.append(" and items.adex>0");
Values.append(",items.adex");
searchCriteria.append(" AND items.adex > 0");
searchValues.append(", items.adex");
break;
case STAT_INT:
Search.append(" and items.aint>0");
Values.append(",items.aint");
searchCriteria.append(" AND items.aint > 0");
searchValues.append(", items.aint");
break;
case STAT_STA:
Search.append(" and items.asta>0");
Values.append(",items.asta");
searchCriteria.append(" AND items.asta > 0");
searchValues.append(", items.asta");
break;
case STAT_STR:
Search.append(" and items.astr>0");
Values.append(",items.astr");
searchCriteria.append(" AND items.astr > 0");
searchValues.append(", items.astr");
break;
case STAT_WIS:
Search.append(" and items.awis>0");
Values.append(",items.awis");
searchCriteria.append(" AND items.awis > 0");
searchValues.append(", items.awis");
break;
case STAT_COLD:
Search.append(" and items.cr>0");
Values.append(",items.cr");
searchCriteria.append(" AND items.cr > 0");
searchValues.append(", items.cr");
break;
case STAT_DISEASE:
Search.append(" and items.dr>0");
Values.append(",items.dr");
searchCriteria.append(" AND items.dr > 0");
searchValues.append(", items.dr");
break;
case STAT_FIRE:
Search.append(" and items.fr>0");
Values.append(",items.fr");
searchCriteria.append(" AND items.fr > 0");
searchValues.append(", items.fr");
break;
case STAT_MAGIC:
Values.append(",items.mr");
Search.append(" and items.mr>0");
searchCriteria.append(" AND items.mr > 0");
searchValues.append(", items.mr");
break;
case STAT_POISON:
Search.append(" and items.pr>0");
Values.append(",items.pr");
searchCriteria.append(" AND items.pr > 0");
searchValues.append(", items.pr");
break;
case STAT_HP:
Search.append(" and items.hp>0");
Values.append(",items.hp");
searchCriteria.append(" AND items.hp > 0");
searchValues.append(", items.hp");
break;
case STAT_MANA:
Search.append(" and items.mana>0");
Values.append(",items.mana");
searchCriteria.append(" AND items.mana > 0");
searchValues.append(", items.mana");
break;
case STAT_ENDURANCE:
Search.append(" and items.endur>0");
Values.append(",items.endur");
searchCriteria.append(" AND items.endur > 0");
searchValues.append(", items.endur");
break;
case STAT_ATTACK:
Search.append(" and items.attack>0");
Values.append(",items.attack");
searchCriteria.append(" AND items.attack > 0");
searchValues.append(", items.attack");
break;
case STAT_HP_REGEN:
Search.append(" and items.regen>0");
Values.append(",items.regen");
searchCriteria.append(" AND items.regen > 0");
searchValues.append(", items.regen");
break;
case STAT_MANA_REGEN:
Search.append(" and items.manaregen>0");
Values.append(",items.manaregen");
searchCriteria.append(" AND items.manaregen > 0");
searchValues.append(", items.manaregen");
break;
case STAT_HASTE:
Search.append(" and items.haste>0");
Values.append(",items.haste");
searchCriteria.append(" AND items.haste > 0");
searchValues.append(", items.haste");
break;
case STAT_DAMAGE_SHIELD:
Search.append(" and items.damageshield>00");
Values.append(",items.damageshield");
searchCriteria.append(" AND items.damageshield > 0");
searchValues.append(", items.damageshield");
break;
default:
Values.append(",0");
searchValues.append(", 0");
break;
}
Values.append(",sum(charges), items.stackable ");
std::string query = StringFormat("SELECT %s, SUM(charges), items.stackable "
"FROM trader, items %s GROUP BY items.id, charges, char_id LIMIT %i",
searchValues.c_str(), searchCriteria.c_str(), RuleI(Bazaar, MaxSearchResults));
auto results = database.QueryDatabase(query);
if (!results.Success()) {
_log(TRADING__CLIENT, "Failed to retrieve Bazaar Search!! %s %s\n", query.c_str(), results.ErrorMessage().c_str());
return;
}
if (database.RunQuery(Query,MakeAnyLenString(&Query, "select %s from trader,items %s group by items.id,charges,char_id limit %i",
Values.c_str(),Search.c_str(), RuleI(Bazaar, MaxSearchResults)),errbuf,&Result)){
_log(TRADING__CLIENT, "SRCH: %s", query.c_str());
_log(TRADING__CLIENT, "SRCH: %s", Query);
safe_delete_array(Query);
int Size = 0;
uint32 ID = 0;
int Size = 0;
uint32 ID = 0;
if(mysql_num_rows(Result) == static_cast<unsigned long>(RuleI(Bazaar, MaxSearchResults)))
if (results.RowCount() == static_cast<unsigned long>(RuleI(Bazaar, MaxSearchResults)))
Message(15, "Your search reached the limit of %i results. Please narrow your search down by selecting more options.",
RuleI(Bazaar, MaxSearchResults));
if(mysql_num_rows(Result) == 0){
EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer;
brds->TraderID = ID;
brds->Type = BazaarSearchDone;
brds->Unknown008 = 0xFFFFFFFF;
brds->Unknown012 = 0xFFFFFFFF;
brds->Unknown016 = 0xFFFFFFFF;
this->QueuePacket(outapp2);
_pkt(TRADING__PACKETS,outapp2);
safe_delete(outapp2);
mysql_free_result(Result);
return;
}
Size = mysql_num_rows(Result) * sizeof(BazaarSearchResults_Struct);
uchar *buffer = new uchar[Size];
uchar *bufptr = buffer;
memset(buffer, 0, Size);
int Action = BazaarSearchResults;
uint32 Cost = 0;
int32 SerialNumber = 0;
char Name[64] = {0};
int Count = 0;
uint32 StatValue=0;
while ((Row = mysql_fetch_row(Result))) {
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action);
Count = atoi(Row[0]);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count);
SerialNumber = atoi(Row[3]);
VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber);
Client* Trader2=entity_list.GetClientByCharID(atoi(Row[1]));
if(Trader2){
ID = Trader2->GetID();
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID);
}
else{
_log(TRADING__CLIENT, "Unable to find trader: %i\n",atoi(Row[1]));
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
}
Cost = atoi(Row[5]);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost);
StatValue = atoi(Row[8]);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue);
bool Stackable = atoi(Row[10]);
if(Stackable) {
int Charges = atoi(Row[9]);
sprintf(Name, "%s(%i)", Row[7], Charges);
}
else
sprintf(Name,"%s(%i)",Row[7], Count);
memcpy(bufptr,&Name, strlen(Name));
bufptr += 64;
// Extra fields for SoD+
//
if(Trader2)
sprintf(Name, "%s", Trader2->GetName());
else
sprintf(Name, "Unknown");
memcpy(bufptr,&Name, strlen(Name));
bufptr += 64;
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(Row[1])); // ItemID
}
mysql_free_result(Result);
EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, Size);
memcpy(outapp->pBuffer, buffer, Size);
this->QueuePacket(outapp);
_pkt(TRADING__PACKETS,outapp);
safe_delete(outapp);
safe_delete_array(buffer);
if(results.RowCount() == 0) {
EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer;
brds->TraderID = ID;
brds->Type = BazaarSearchDone;
brds->Unknown008 = 0xFFFFFFFF;
brds->Unknown012 = 0xFFFFFFFF;
brds->Unknown016 = 0xFFFFFFFF;
this->QueuePacket(outapp2);
_pkt(TRADING__PACKETS,outapp2);
safe_delete(outapp2);
}
else{
_log(TRADING__CLIENT, "Failed to retrieve Bazaar Search!! %s %s\n", Query, errbuf);
safe_delete_array(Query);
return;
}
Size = results.RowCount() * sizeof(BazaarSearchResults_Struct);
uchar *buffer = new uchar[Size];
uchar *bufptr = buffer;
memset(buffer, 0, Size);
int Action = BazaarSearchResults;
uint32 Cost = 0;
int32 SerialNumber = 0;
char temp_buffer[64] = {0};
int Count = 0;
uint32 StatValue=0;
for (auto row = results.begin(); row != results.end(); ++row) {
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action);
Count = atoi(row[0]);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count);
SerialNumber = atoi(row[3]);
VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber);
Client* Trader2=entity_list.GetClientByCharID(atoi(row[1]));
if(Trader2){
ID = Trader2->GetID();
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID);
}
else{
_log(TRADING__CLIENT, "Unable to find trader: %i\n",atoi(row[1]));
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
}
Cost = atoi(row[5]);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost);
StatValue = atoi(row[8]);
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue);
bool Stackable = atoi(row[10]);
if(Stackable) {
int Charges = atoi(row[9]);
sprintf(temp_buffer, "%s(%i)", row[7], Charges);
}
else
sprintf(temp_buffer,"%s(%i)",row[7], Count);
memcpy(bufptr,&temp_buffer, strlen(temp_buffer));
bufptr += 64;
// Extra fields for SoD+
//
if(Trader2)
sprintf(temp_buffer, "%s", Trader2->GetName());
else
sprintf(temp_buffer, "Unknown");
memcpy(bufptr,&temp_buffer, strlen(temp_buffer));
bufptr += 64;
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(row[1])); // ItemID
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, Size);
memcpy(outapp->pBuffer, buffer, Size);
this->QueuePacket(outapp);
_pkt(TRADING__PACKETS,outapp);
safe_delete(outapp);
safe_delete_array(buffer);
EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer;
brds->TraderID = ID;
brds->Type = BazaarSearchDone;
brds->Unknown008 = 0xFFFFFFFF;
brds->Unknown012 = 0xFFFFFFFF;
brds->Unknown016 = 0xFFFFFFFF;
this->QueuePacket(outapp2);
_pkt(TRADING__PACKETS,outapp2);
safe_delete(outapp2);
}
static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Struct* gis, uint32 ItemID) {
@@ -2304,103 +2256,92 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) {
}
void Client::SendBuyerResults(char* SearchString, uint32 SearchID) {
void Client::SendBuyerResults(char* searchString, uint32 searchID) {
// This method is called when a potential seller in the /barter window searches for matching buyers
//
_log(TRADING__BARTER, "Client::SendBuyerResults %s\n", SearchString);
_log(TRADING__BARTER, "Client::SendBuyerResults %s\n", searchString);
char errbuf[MYSQL_ERRMSG_SIZE];
char* Query = 0;
char ItemName[64];
std::string Search, Values;
MYSQL_RES *Result;
MYSQL_ROW Row;
char* escSearchString = new char[strlen(searchString) * 2 + 1];
database.DoEscapeString(escSearchString, searchString, strlen(searchString));
char*EscSearchString = new char[strlen(SearchString) * 2 + 1];
database.DoEscapeString(EscSearchString, SearchString, strlen(SearchString));
std::string query = StringFormat("SELECT * FROM buyer WHERE itemname LIKE '%%%s%%' ORDER BY charid LIMIT %i",
escSearchString, RuleI(Bazaar, MaxBarterSearchResults));
safe_delete_array(escSearchString);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
_log(TRADING__CLIENT, "Failed to retrieve Barter Search!! %s %s\n", query.c_str(), results.ErrorMessage().c_str());
return;
}
if (database.RunQuery(Query,MakeAnyLenString(&Query, "select * from buyer where itemname like '%%%s%%' order by charid limit %i",
EscSearchString, RuleI(Bazaar, MaxBarterSearchResults)), errbuf, &Result)) {
int numberOfRows = results.RowCount();
int NumberOfRows = mysql_num_rows(Result);
if(numberOfRows == RuleI(Bazaar, MaxBarterSearchResults))
Message(15, "Your search found too many results; some are not displayed.");
else if(strlen(searchString) == 0)
Message(10, "There are %i Buy Lines.", numberOfRows);
else
Message(10, "There are %i Buy Lines that match the search string '%s'.", numberOfRows, searchString);
if(NumberOfRows == RuleI(Bazaar, MaxBarterSearchResults))
Message(15, "Your search found too many results; some are not displayed.");
else {
if(strlen(SearchString) == 0)
Message(10, "There are %i Buy Lines.", NumberOfRows);
else
Message(10, "There are %i Buy Lines that match the search string '%s'.",
NumberOfRows, SearchString);
if(numberOfRows == 0)
return;
uint32 lastCharID = 0;
Client *buyer = nullptr;
for (auto row = results.begin(); row != results.end(); ++row) {
char itemName[64];
uint32 charID = atoi(row[0]);
uint32 buySlot = atoi(row[1]);
uint32 itemID = atoi(row[2]);
strcpy(itemName, row[3]);
uint32 quantity = atoi(row[4]);
uint32 price = atoi(row[5]);
// Each item in the search results is sent as a single fixed length packet, although the position of
// the fields varies due to the use of variable length strings. The reason the packet is so big, is
// to allow item compensation, e.g. a buyer could offer to buy a Blade Of Carnage for 10000pp plus
// other items in exchange. Item compensation is not currently supported in EQEmu.
//
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 940);
char *buf = (char *)outapp->pBuffer;
const Item_Struct* item = database.GetItem(itemID);
if(!item)
continue;
// Save having to scan the client list when dealing with multiple buylines for the same Character.
if(charID != lastCharID) {
buyer = entity_list.GetClientByCharID(charID);
lastCharID = charID;
}
if(NumberOfRows == 0) {
mysql_free_result(Result);
safe_delete_array(Query);
return;
}
if(!buyer)
continue;
uint32 LastCharID = 0;
Client *Buyer = nullptr;
VARSTRUCT_ENCODE_TYPE(uint32, buf, Barter_BuyerSearchResults); // Command
VARSTRUCT_ENCODE_TYPE(uint32, buf, searchID); // Match up results with the request
VARSTRUCT_ENCODE_TYPE(uint32, buf, buySlot); // Slot in this Buyer's list
VARSTRUCT_ENCODE_TYPE(uint8, buf, 0x01); // Unknown - probably a flag field
VARSTRUCT_ENCODE_TYPE(uint32, buf, itemID); // ItemID
VARSTRUCT_ENCODE_STRING(buf, itemName); // Itemname
VARSTRUCT_ENCODE_TYPE(uint32, buf, item->Icon); // Icon
VARSTRUCT_ENCODE_TYPE(uint32, buf, quantity); // Quantity
VARSTRUCT_ENCODE_TYPE(uint8, buf, 0x01); // Unknown - probably a flag field
VARSTRUCT_ENCODE_TYPE(uint32, buf, price); // Price
VARSTRUCT_ENCODE_TYPE(uint32, buf, buyer->GetID()); // Entity ID
VARSTRUCT_ENCODE_TYPE(uint32, buf, 0); // Flag for + Items , probably ItemCount
VARSTRUCT_ENCODE_STRING(buf, buyer->GetName()); // Seller Name
while ((Row = mysql_fetch_row(Result))) {
_pkt(TRADING__BARTER, outapp);
uint32 CharID = atoi(Row[0]);
uint32 BuySlot = atoi(Row[1]);
uint32 ItemID = atoi(Row[2]);
strcpy(ItemName, Row[3]);
uint32 Quantity = atoi(Row[4]);
uint32 Price = atoi(Row[5]);
QueuePacket(outapp);
safe_delete(outapp);
}
// Each item in the search results is sent as a single fixed length packet, although the position of
// the fields varies due to the use of variable length strings. The reason the packet is so big, is
// to allow item compensation, e.g. a buyer could offer to buy a Blade Of Carnage for 10000pp plus
// other items in exchange. Item compensation is not currently supported in EQEmu.
//
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 940);
char *Buf = (char *)outapp->pBuffer;
const Item_Struct* item = database.GetItem(ItemID);
if(!item) continue;
// Save having to scan the client list when dealing with multiple buylines for the same Character.
if(CharID != LastCharID) {
Buyer = entity_list.GetClientByCharID(CharID);
LastCharID = CharID;
}
if(!Buyer) continue;
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerSearchResults); // Command
VARSTRUCT_ENCODE_TYPE(uint32, Buf, SearchID); // Match up results with the request
VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); // Slot in this Buyer's list
VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); // Unknown - probably a flag field
VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); // ItemID
VARSTRUCT_ENCODE_STRING(Buf, ItemName); // Itemname
VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); // Icon
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); // Quantity
VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); // Unknown - probably a flag field
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); // Price
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); // Entity ID
VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); // Flag for + Items , probably ItemCount
VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName()); // Seller Name
_pkt(TRADING__BARTER, outapp);
QueuePacket(outapp);
safe_delete(outapp);
}
mysql_free_result(Result);
}
else{
_log(TRADING__CLIENT, "Failed to retrieve Barter Search!! %s %s\n", Query, errbuf);
}
safe_delete_array(Query);
safe_delete_array(EscSearchString);
}
void Client::ShowBuyLines(const EQApplicationPacket *app) {
@@ -2443,60 +2384,44 @@ void Client::ShowBuyLines(const EQApplicationPacket *app) {
safe_delete(outapp);
char errbuf[MYSQL_ERRMSG_SIZE];
char* Query = 0;
char ItemName[64];
std::string Search, Values;
MYSQL_RES *Result;
MYSQL_ROW Row;
std::string query = StringFormat("SELECT * FROM buyer WHERE charid = %i", Buyer->CharacterID());
auto results = database.QueryDatabase(query);
if (!results.Success() || results.RowCount() == 0)
return;
if (database.RunQuery(Query,MakeAnyLenString(&Query, "select * from buyer where charid = %i",
Buyer->CharacterID()),errbuf,&Result)){
for (auto row = results.begin(); row != results.end(); ++row) {
char ItemName[64];
uint32 BuySlot = atoi(row[1]);
uint32 ItemID = atoi(row[2]);
strcpy(ItemName, row[3]);
uint32 Quantity = atoi(row[4]);
uint32 Price = atoi(row[5]);
if(mysql_num_rows(Result) == 0) {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 936);
safe_delete_array(Query);
char *Buf = (char *)outapp->pBuffer;
mysql_free_result(Result);
const Item_Struct* item = database.GetItem(ItemID);
return;
}
if(!item)
continue;
while ((Row = mysql_fetch_row(Result))) {
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerInspectWindow);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot);
VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag
VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID);
VARSTRUCT_ENCODE_STRING(Buf, ItemName);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity);
VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID());
VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0);
VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName());
uint32 BuySlot = atoi(Row[1]);
uint32 ItemID = atoi(Row[2]);
strcpy(ItemName, Row[3]);
uint32 Quantity = atoi(Row[4]);
uint32 Price = atoi(Row[5]);
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 936);
char *Buf = (char *)outapp->pBuffer;
const Item_Struct* item = database.GetItem(ItemID);
if(!item) continue;
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerInspectWindow);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot);
VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag
VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID);
VARSTRUCT_ENCODE_STRING(Buf, ItemName);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity);
VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price);
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID());
VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0);
VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName());
_pkt(TRADING__BARTER, outapp);
QueuePacket(outapp);
}
mysql_free_result(Result);
}
safe_delete_array(Query);
_pkt(TRADING__BARTER, outapp);
QueuePacket(outapp);
}
}
void Client::SellToBuyer(const EQApplicationPacket *app) {
+6 -4
View File
@@ -254,8 +254,10 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) {
return(0);
}
//make sure they have enough of them
//and remove it from inventory
/*
Make sure they have enough of them
and remove it from inventory
*/
if(inst->IsStackable()) {
if(inst->GetCharges() < (int32)quantity) //dont have enough....
return(0);
@@ -267,7 +269,7 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) {
pts *= quantity;
//add the tribute value in points
/* Add the tribute value in points */
AddTributePoints(pts);
return(pts);
}
@@ -279,7 +281,7 @@ int32 Client::TributeMoney(uint32 platinum) {
return(0);
}
//add the tribute value in points
/* Add the tribute value in points */
AddTributePoints(platinum);
return(platinum);
}
+9 -6
View File
@@ -1084,14 +1084,14 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
// how much it's allowed to be off by
#define _GASSIGN_TOLERANCE 1.0
if(results.RowCount() == 0) // try a fuzzy match if that didn't find it
if (results.RowCount() == 0) // try a fuzzy match if that didn't find it
{
query = StringFormat("SELECT id,x,y FROM spawn2 WHERE zone='%s' AND "
"ABS( ABS(x) - ABS(%f) ) < %f AND "
"ABS( ABS(y) - ABS(%f) ) < %f",
zone->GetShortName(), x, _GASSIGN_TOLERANCE, y, _GASSIGN_TOLERANCE);
results = QueryDatabase(query);
if(!results.Success()) {
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error querying fuzzy spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return;
}
@@ -1100,12 +1100,13 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
matches = results.RowCount();
}
if (matches == 0) {
if (matches == 0)
{
client->Message(0, "ERROR: Unable to assign grid - can't find it in spawn2");
return;
}
if(matches == 1)
if(matches > 1)
{
client->Message(0, "ERROR: Unable to assign grid - multiple spawn2 rows match");
return;
@@ -1125,7 +1126,8 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
return;
}
if (results.RowsAffected() != 1) {
if (results.RowsAffected() != 1)
{
client->Message(0, "ERROR: found spawn2 id %d but the update query failed", spawn2id);
return;
}
@@ -1133,7 +1135,8 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid)
if(client)
client->LogSQL(query.c_str());
if(!fuzzy) {
if (!fuzzy)
{
client->Message(0, "Grid assign: spawn2 id = %d updated - exact match", spawn2id);
return;
}
+66 -10
View File
@@ -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;
@@ -322,7 +328,7 @@ void WorldServer::Process() {
entity->CastToMob()->SetZone(ztz->requested_zone_id, ztz->requested_instance_id);
if(ztz->ignorerestrictions == 3)
if(ztz->ignorerestrictions == 3)
entity->CastToClient()->GoToSafeCoords(ztz->requested_zone_id, ztz->requested_instance_id);
}
@@ -995,9 +1001,11 @@ void WorldServer::Process() {
char AssistName[64];
char PullerName[64];
char NPCMarkerName[64];
char mentoree_name[64];
int mentor_percent;
GroupLeadershipAA_Struct GLAA;
memset(ln, 0, 64);
strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA));
strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA));
Client *lc = entity_list.GetClientByName(ln);
if(lc)
group->SetLeader(lc);
@@ -1007,6 +1015,7 @@ void WorldServer::Process() {
group->SetPuller(PullerName);
group->SetNPCMarker(NPCMarkerName);
group->SetGroupAAs(&GLAA);
group->SetGroupMentor(mentor_percent, mentoree_name);
}
}
@@ -1366,6 +1375,18 @@ void WorldServer::Process() {
break;
}
case ServerOP_RaidMOTD: {
ServerRaidMOTD_Struct *rmotd = (ServerRaidMOTD_Struct *)pack->pBuffer;
if (!zone)
break;
Raid *r = entity_list.GetRaidByID(rmotd->rid);
if (!r)
break;
r->SetRaidMOTD(std::string(rmotd->motd));
r->SendRaidMOTD();
break;
}
case ServerOP_SpawnPlayerCorpse: {
SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer;
Corpse* NewCorpse = database.LoadPlayerCorpse(s->player_corpse_id);
@@ -1777,6 +1798,24 @@ void WorldServer::Process() {
break;
}
case ServerOP_CZSetEntityVariableByNPCTypeID:
{
CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer;
NPC* n = entity_list.GetNPCByNPCTypeID(CZM->npctype_id);
if (n != 0) {
n->SetEntityVariable(CZM->id, CZM->m_var);
}
break;
}
case ServerOP_CZSignalNPC:
{
CZNPCSignal_Struct* CZCN = (CZNPCSignal_Struct*)pack->pBuffer;
NPC* n = entity_list.GetNPCByNPCTypeID(CZCN->npctype_id);
if (n != 0) {
n->SignalNPC(CZCN->data);
}
break;
}
case ServerOP_CZSignalClient:
{
CZClientSignal_Struct* CZCS = (CZClientSignal_Struct*) pack->pBuffer;
@@ -1856,6 +1895,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 +2220,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;
}
+2
View File
@@ -57,6 +57,8 @@ public:
void HandleLFGMatches(ServerPacket *pack);
void HandleLFPMatches(ServerPacket *pack);
void RequestTellQueue(const char *who);
private:
virtual void OnConnected();
+78 -143
View File
@@ -77,8 +77,6 @@ extern bool staticzone;
Zone* zone = 0;
volatile bool ZoneLoaded = false;
extern QuestParserCollection* parse;
extern DBAsyncFinishedQueue MTdbafq;
extern DBAsync *dbasync;
bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) {
const char* zonename = database.GetZoneName(iZoneID);
@@ -105,7 +103,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) {
}
zone->zonemap = Map::LoadMapFile(zone->map_name);
zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name);
zone->pathing = PathManager::LoadPathFile(zone->map_name);
zone->pathing = PathManager::LoadPathFile(zone->map_name);
char tmp[10];
if (database.GetVariable("loglevel",tmp, 9)) {
@@ -309,41 +307,40 @@ bool Zone::LoadGroundSpawns() {
return(true);
}
int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold){
int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold) {
int freeslot = 0;
std::list<MerchantList> merlist = merchanttable[merchantid];
std::list<MerchantList>::const_iterator itr;
uint32 i = 1;
for (itr = merlist.begin(); itr != merlist.end(); ++itr) {
MerchantList ml = *itr;
if(ml.item == item)
if (ml.item == item)
return 0;
// Account for merchant lists with gaps in them.
if(ml.slot >= i)
if (ml.slot >= i)
i = ml.slot + 1;
}
std::list<TempMerchantList> tmp_merlist = tmpmerchanttable[npcid];
std::list<TempMerchantList>::const_iterator tmp_itr;
bool update_charges = false;
TempMerchantList ml;
while(freeslot == 0 && !update_charges){
while (freeslot == 0 && !update_charges) {
freeslot = i;
for (tmp_itr = tmp_merlist.begin(); tmp_itr != tmp_merlist.end(); ++tmp_itr) {
ml = *tmp_itr;
if(ml.item == item){
if (ml.item == item) {
update_charges = true;
freeslot = 0;
break;
}
if((ml.slot == i) || (ml.origslot==i)) {
freeslot=0;
if ((ml.slot == i) || (ml.origslot == i)) {
freeslot = 0;
}
}
i++;
}
if(update_charges){
if (update_charges) {
tmp_merlist.clear();
std::list<TempMerchantList> oldtmp_merlist = tmpmerchanttable[npcid];
for (tmp_itr = oldtmp_merlist.begin(); tmp_itr != oldtmp_merlist.end(); ++tmp_itr) {
@@ -351,27 +348,27 @@ int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charg
if(ml2.item != item)
tmp_merlist.push_back(ml2);
}
if(sold)
if (sold)
ml.charges = ml.charges + charges;
else
ml.charges = charges;
if(!ml.origslot)
if (!ml.origslot)
ml.origslot = ml.slot;
if(charges>0){
if (charges > 0) {
database.SaveMerchantTemp(npcid, ml.origslot, item, ml.charges);
tmp_merlist.push_back(ml);
}
else{
database.DeleteMerchantTemp(npcid,ml.origslot);
else {
database.DeleteMerchantTemp(npcid, ml.origslot);
}
tmpmerchanttable[npcid] = tmp_merlist;
if(sold)
if (sold)
return ml.slot;
}
if(freeslot){
if(charges<0) //sanity check only, shouldnt happen
if (freeslot) {
if (charges < 0) //sanity check only, shouldnt happen
charges = 0x7FFF;
database.SaveMerchantTemp(npcid, freeslot, item, charges);
tmp_merlist = tmpmerchanttable[npcid];
@@ -393,48 +390,42 @@ uint32 Zone::GetTempMerchantQuantity(uint32 NPCID, uint32 Slot) {
std::list<TempMerchantList>::const_iterator Iterator;
for (Iterator = TmpMerchantList.begin(); Iterator != TmpMerchantList.end(); ++Iterator)
if((*Iterator).slot == Slot)
if ((*Iterator).slot == Slot)
return (*Iterator).charges;
return 0;
}
void Zone::LoadTempMerchantData(){
void Zone::LoadTempMerchantData() {
LogFile->write(EQEMuLog::Status, "Loading Temporary Merchant Lists...");
char* query = 0;
uint32_breakdown workpt;
workpt.b4() = DBA_b4_Zone;
workpt.w2_3() = 0;
workpt.b1() = DBA_b1_Zone_MerchantListsTemp;
DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read);
dbaw->AddQuery(1, &query, MakeAnyLenString(&query,
"select ml.npcid,ml.slot,ml.itemid,ml.charges "
"from "
" merchantlist_temp ml, "
" spawnentry se, "
" spawn2 s2 "
"where "
" ml.npcid=se.npcid "
" and se.spawngroupid=s2.spawngroupid "
" and s2.zone='%s' and s2.version=%u", GetShortName(), GetInstanceVersion()));
if (!(pQueuedMerchantsWorkID = dbasync->AddWork(&dbaw))) {
safe_delete(dbaw);
LogFile->write(EQEMuLog::Error, "dbasync->AddWork() failed adding merchant list query");
std::string query = StringFormat(
"SELECT "
"ml.npcid, "
"ml.slot, "
"ml.charges, "
"ml.itemid "
"FROM "
"merchantlist_temp ml, "
"spawnentry se, "
"spawn2 s2 "
"WHERE "
"ml.npcid = se.npcid "
"AND se.spawngroupid = s2.spawngroupid "
"AND s2.zone = '%s' AND s2.version = %i "
"ORDER BY ml.slot ", GetShortName(), GetInstanceVersion());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadTempMerchantData query '%s' %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
}
void Zone::LoadTempMerchantData_result(MYSQL_RES* result) {
MYSQL_ROW row;
std::map<uint32,std::list<TempMerchantList> >::iterator cur;
std::map<uint32, std::list<TempMerchantList> >::iterator cur;
uint32 npcid = 0;
while((row = mysql_fetch_row(result))) {
for (auto row = results.begin(); row != results.end(); ++row) {
TempMerchantList ml;
ml.npcid = atoul(row[0]);
if(npcid != ml.npcid){
if (npcid != ml.npcid){
cur = tmpmerchanttable.find(ml.npcid);
if(cur == tmpmerchanttable.end()) {
if (cur == tmpmerchanttable.end()) {
std::list<TempMerchantList> empty;
tmpmerchanttable[ml.npcid] = empty;
cur = tmpmerchanttable.find(ml.npcid);
@@ -442,19 +433,19 @@ void Zone::LoadTempMerchantData_result(MYSQL_RES* result) {
npcid = ml.npcid;
}
ml.slot = atoul(row[1]);
ml.item = atoul(row[2]);
ml.charges = atoul(row[3]);
ml.charges = atoul(row[2]);
ml.item = atoul(row[3]);
ml.origslot = ml.slot;
cur->second.push_back(ml);
}
pQueuedMerchantsWorkID = 0;
}
//there should prolly be a temp counterpart of this...
void Zone::LoadNewMerchantData(uint32 merchantid){
void Zone::LoadNewMerchantData(uint32 merchantid) {
std::list<MerchantList> merlist;
std::string query = StringFormat("SELECT item, slot, faction_required, level_required, alt_currency_cost, "
"classes_required FROM merchantlist WHERE merchantid=%d", merchantid);
"classes_required FROM merchantlist WHERE merchantid=%d ORDER BY slot", merchantid);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadNewMerchantData query '%s' %s", query.c_str(), results.ErrorMessage().c_str());
@@ -476,16 +467,39 @@ void Zone::LoadNewMerchantData(uint32 merchantid){
merchanttable[merchantid] = merlist;
}
void Zone::LoadMerchantData_result(MYSQL_RES* result) {
MYSQL_ROW row;
std::map<uint32,std::list<MerchantList> >::iterator cur;
void Zone::GetMerchantDataForZoneLoad() {
LogFile->write(EQEMuLog::Status, "Loading Merchant Lists...");
std::string query = StringFormat(
"SELECT "
"ml.merchantid, "
"ml.slot, "
"ml.item, "
"ml.faction_required, "
"ml.level_required, "
"ml.alt_currency_cost, "
"ml.classes_required, "
"ml.probability "
"FROM "
"merchantlist AS ml, "
"npc_types AS nt, "
"spawnentry AS se, "
"spawn2 AS s2 "
"WHERE nt.merchant_id = ml.merchantid AND nt.id = se.npcid "
"AND se.spawngroupid = s2.spawngroupid AND s2.zone = '%s' AND s2.version = %i "
"ORDER BY ml.slot ", GetShortName(), GetInstanceVersion());
auto results = database.QueryDatabase(query);
std::map<uint32, std::list<MerchantList> >::iterator cur;
uint32 npcid = 0;
while((row = mysql_fetch_row(result))) {
if (results.RowCount() == 0) {
LogFile->write(EQEMuLog::Error, "Error in loading Merchant Data for zone");
return;
}
for (auto row = results.begin(); row != results.end(); ++row) {
MerchantList ml;
ml.id = atoul(row[0]);
if(npcid != ml.id){
if (npcid != ml.id) {
cur = merchanttable.find(ml.id);
if(cur == merchanttable.end()) {
if (cur == merchanttable.end()) {
std::list<MerchantList> empty;
merchanttable[ml.id] = empty;
cur = merchanttable.find(ml.id);
@@ -495,15 +509,15 @@ void Zone::LoadMerchantData_result(MYSQL_RES* result) {
std::list<MerchantList>::iterator iter = cur->second.begin();
bool found = false;
while(iter != cur->second.end()) {
if((*iter).item == ml.id) {
while (iter != cur->second.end()) {
if ((*iter).item == ml.id) {
found = true;
break;
}
++iter;
}
if(found) {
if (found) {
continue;
}
@@ -516,28 +530,7 @@ void Zone::LoadMerchantData_result(MYSQL_RES* result) {
ml.probability = atoul(row[7]);
cur->second.push_back(ml);
}
}
void Zone::GetMerchantDataForZoneLoad(){
LogFile->write(EQEMuLog::Status, "Loading Merchant Lists...");
char* query = 0;
uint32_breakdown workpt;
workpt.b4() = DBA_b4_Zone;
workpt.w2_3() = 0;
workpt.b1() = DBA_b1_Zone_MerchantLists;
DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read);
dbaw->AddQuery(1, &query, MakeAnyLenString(&query,
"select ml.merchantid,ml.slot,ml.item,ml.faction_required,ml.level_required,ml.alt_currency_cost,ml.classes_required,ml.probability "
"from merchantlist ml, npc_types nt, spawnentry se, spawn2 s2 "
"where nt.merchant_id=ml.merchantid and nt.id=se.npcid "
"and se.spawngroupid=s2.spawngroupid and s2.zone='%s' and s2.version=%u "
//"group by ml.merchantid,slot order by merchantid,slot asc" //this made the query use a temp table/filesort (very slow)... so we handle unsorted data on our end.
, GetShortName(), GetInstanceVersion()));
if (!(pQueuedMerchantsWorkID = dbasync->AddWork(&dbaw))) {
safe_delete(dbaw);
LogFile->write(EQEMuLog::Error,"dbasync->AddWork() failed adding merchant list query");
return;
}
}
void Zone::LoadMercTemplates(){
@@ -665,61 +658,6 @@ void Zone::LoadMercSpells(){
}
void Zone::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) {
// LogFile->write(EQEMuLog::Debug, "Zone work complete...");
switch (workpt_b1) {
case DBA_b1_Zone_MerchantLists: {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES* result = 0;
DBAsyncQuery* dbaq = dbaw->PopAnswer();
if(dbaq == nullptr) {
LogFile->write(EQEMuLog::Error, "nullptr answer provided for async merchant list load.");
break;
}
if(!dbaq->GetAnswer(errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unable to get results for merchant lists");
break;
}
if(dbaq->QPT() != 1) {
LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Invalid query part for merchant lists");
break;
}
LoadMerchantData_result(result);
pQueuedMerchantsWorkID = 0;
break;
}
case DBA_b1_Zone_MerchantListsTemp: {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES* result = 0;
DBAsyncQuery* dbaq = dbaw->PopAnswer();
if(dbaq == nullptr) {
LogFile->write(EQEMuLog::Error, "nullptr answer provided for async temp merchant list load.");
break;
}
if(!dbaq->GetAnswer(errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unable to get results for temp merchant lists");
break;
}
if(dbaq->QPT() != 1) {
LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Invalid query part for temp merchant lists");
break;
}
LoadTempMerchantData_result(result);
pQueuedMerchantsWorkID = 0;
break;
}
default: {
LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unknown workpt_b1");
break;
}
}
}
bool Zone::IsLoaded() {
return ZoneLoaded;
}
@@ -772,7 +710,6 @@ void Zone::Shutdown(bool quite)
zone->ResetAuth();
safe_delete(zone);
dbasync->CommitWrites();
entity_list.ClearAreas();
parse->ReloadQuests(true);
UpdateWindowTitle();
@@ -911,8 +848,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
}
Zone::~Zone() {
if(pQueuedMerchantsWorkID != 0)
dbasync->CancelWork(pQueuedMerchantsWorkID);
spawn2_list.Clear();
safe_delete(zonemap);
safe_delete(watermap);
-3
View File
@@ -166,7 +166,6 @@ public:
void SetStaticZone(bool sz) { staticzone = sz; }
inline bool IsStaticZone() { return staticzone; }
inline void GotCurTime(bool time) { gottime = time; }
void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw);
void SpawnConditionChanged(const SpawnCondition &c, int16 old_value);
@@ -174,8 +173,6 @@ public:
void LoadNewMerchantData(uint32 merchantid);
void LoadTempMerchantData();
uint32 GetTempMerchantQuantity(uint32 NPCID, uint32 Slot);
void LoadTempMerchantData_result(MYSQL_RES* result);
void LoadMerchantData_result(MYSQL_RES* result);
int SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold=false);
void LoadMercTemplates();
void LoadMercSpells();
+1008 -240
View File
File diff suppressed because it is too large Load Diff
+84 -96
View File
@@ -6,6 +6,8 @@
#include "../common/loottable.h"
#include "zonedump.h"
#include "../common/faction.h"
#include <limits>
//#include "doors.h"
struct wplist {
@@ -212,9 +214,7 @@ public:
ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port);
virtual ~ZoneDatabase();
/*
* Objects and World Containers
*/
/* Objects and World Containers */
void LoadWorldContainer(uint32 parentid, ItemInst* container);
void SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container);
void DeleteWorldContainer(uint32 parent_id,uint32 zone_id);
@@ -223,10 +223,7 @@ public:
void DeleteObject(uint32 id);
Ground_Spawns* LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs);
/*
* Traders
*/
/* Traders */
void SaveTraderItem(uint32 char_id,uint32 itemid,uint32 uniqueid, int32 charges,uint32 itemcost,uint8 slot);
void UpdateTraderItemCharges(int char_id, uint32 ItemInstID, int32 charges);
void UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice);
@@ -236,38 +233,67 @@ public:
Trader_Struct* LoadTraderItem(uint32 char_id);
TraderCharges_Struct* LoadTraderItemWithCharges(uint32 char_id);
// Buyer/Barter
//
/* Buyer/Barter */
void AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char *ItemName, uint32 Quantity, uint32 Price);
void RemoveBuyLine(uint32 CharID, uint32 BuySlot);
void DeleteBuyLines(uint32 CharID);
void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity);
/*
* General Character Related Stuff
*/
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);
bool GetCharacterInfoForLogin_result(MYSQL_RES* result, 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);
/* General Character Related Stuff */
bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
void SaveBuffs(Client *c);
void LoadBuffs(Client *c);
void LoadPetInfo(Client *c);
void SavePetInfo(Client *c);
void RemoveTempFactions(Client *c);
/*
* Character Inventory
*/
/* Character Data Loaders */
bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list);
bool LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);
bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp);
/* Character Data Saves */
bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home);
bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);
bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level);
bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color);
bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value);
bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value);
bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id);
bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name);
bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon);
bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp);
/* Character Data Deletes */
bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id);
bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id);
bool DeleteCharacterLeadershipAAs(uint32 character_id);
bool DeleteCharacterAAs(uint32 character_id);
bool DeleteCharacterDye(uint32 character_id);
/* Character Inventory */
bool NoRentExpired(const char* name);
/*
* Corpses
*/
/* Corpses */
bool GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes);
uint32 CreatePlayerCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading);
bool CreatePlayerCorpseBackup(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading);
@@ -293,21 +319,15 @@ public:
uint32 GetPlayerCorpseItemAt(uint32 corpse_id, uint16 slotid);
uint32 GetPlayerCorpseTimeLeft(uint8 corpse, uint8 type);
/*
* Faction
*/
/* Faction */
bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0);
bool GetFactionData(FactionMods* fd, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id); //rembrant, needed for factions Dec, 16 2001
bool GetFactionName(int32 faction_id, char* name, uint32 buflen); // rembrant, needed for factions Dec, 16 2001
bool GetFactionIdsForNPC(uint32 nfl_id, std::list<struct NPCFaction*> *faction_list, int32* primary_faction = 0); // neotokyo: improve faction handling
bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // rembrant, needed for factions Dec, 16 2001
bool LoadFactionData();
bool LoadFactionValues(uint32 char_id, faction_map & val_list);
bool LoadFactionValues_result(MYSQL_RES* result, faction_map & val_list);
/*
* AAs
*/
/* AAs */
bool LoadAAEffects();
bool LoadAAEffects2();
bool LoadSwarmSpells();
@@ -319,9 +339,7 @@ public:
uint32 CountAAEffects();
void FillAAEffects(SendAA_Struct* aa_struct);
/*
* Zone related
*/
/* Zone related */
bool GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename);
bool SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd);
bool LoadStaticZonePoints(LinkedList<ZonePoint*>* zone_point_list,const char* zonename, uint32 version);
@@ -329,9 +347,7 @@ public:
uint8 GetUseCFGSafeCoords();
int getZoneShutDownDelay(uint32 zoneID, uint32 version);
/*
* Spawns and Spawn Points
*/
/* Spawns and Spawn Points */
bool LoadSpawnGroups(const char* zone_name, uint16 version, SpawnGroupList* spawn_group_list);
bool LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_group_list);
bool PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spawn2_list, int16 version, uint32 repopdelay = 0);
@@ -341,9 +357,7 @@ public:
uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id);
void UpdateSpawn2Status(uint32 id, uint8 new_status);
/*
* Grids/Paths
*/
/* Grids/Paths */
uint32 GetFreeGrid(uint16 zoneid);
void DeleteGrid(Client *c, uint32 sg2, uint32 grid_num, bool grid_too,uint16 zoneid);
void DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num,uint16 zoneid);
@@ -359,11 +373,16 @@ public:
int GetHighestGrid(uint32 zoneid);
int GetHighestWaypoint(uint32 zoneid, uint32 gridid);
/*
* NPCs
*/
/* NPCs */
const NPCType* GetNPCType(uint32 id);
uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete
uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra);
uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime);
uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn);
uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn);
uint32 AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID);
uint32 AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID);
uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn);
bool SetSpecialAttkFlag(uint8 id, const char* flag);
bool GetPetEntry(const char *pet_type, PetRecord *into);
bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into);
@@ -375,9 +394,7 @@ public:
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
/*
* Mercs
*/
/* Mercs */
const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel);
void LoadMercEquipment(Merc *merc);
void SaveMercBuffs(Merc *merc);
@@ -389,9 +406,7 @@ public:
//void LoadMercTypesForMercMerchant(NPC *merchant);
//void LoadMercsForMercMerchant(NPC *merchant);
/*
* Petitions
*/
/* Petitions */
void UpdateBug(BugStruct* bug);
void UpdateBug(PetitionBug_Struct* bug);
void DeletePetitionFromDB(Petition* wpet);
@@ -399,16 +414,11 @@ public:
void InsertPetitionToDB(Petition* wpet);
void RefreshPetitionsFromDB();
/*
* Merchants
*/
/* Merchants */
void SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges);
void DeleteMerchantTemp(uint32 npcid, uint32 slot);
/*
* Tradeskills
*/
/* Tradeskills */
bool GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec);
bool GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec);
uint32 GetZoneForage(uint32 ZoneID, uint8 skill); /* for foraging */
@@ -417,14 +427,10 @@ public:
bool EnableRecipe(uint32 recipe_id);
bool DisableRecipe(uint32 recipe_id);
/*
* Tribute
*/
/* Tribute */
bool LoadTributes();
/*
* Doors
*/
/* Doors */
bool DoorIsOpen(uint8 door_id,const char* zone_name);
void SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name);
bool LoadDoors(int32 iDoorCount, Door *into, const char *zone_name, int16 version);
@@ -437,64 +443,46 @@ public:
int32 GetDoorsDBCountPlusOne(const char *zone_name, int16 version);
void InsertDoor(uint32 did, uint16 ddoorid, const char* ddoor_name, float dxpos, float dypos, float dzpos, float dheading, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize);
/*
* Blocked Spells
*/
/* Blocked Spells */
int32 GetBlockedSpellsCount(uint32 zoneid);
bool LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid);
/*
* Traps
*/
/* Traps */
bool LoadTraps(const char* zonename, int16 version);
char* GetTrapMessage(uint32 trap_id);
/*
* Time
*/
/* Time */
uint32 GetZoneTZ(uint32 zoneid, uint32 version);
bool SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz);
/*
* Group
*/
/* Group */
void RefreshGroupFromDB(Client *c);
uint8 GroupCount(uint32 groupid);
/*
* Raid
*/
/* Raid */
uint8 RaidGroupCount(uint32 raidid, uint32 groupid);
/*
* Instancing
*/
/* Instancing */
void ListAllInstances(Client* c, uint32 charid);
/*
* QGlobals
*/
/* QGlobals */
void QGlobalPurge();
/*
* Alternate Currency
*/
/* Alternate Currency */
void LoadAltCurrencyValues(uint32 char_id, std::map<uint32, uint32> &currency);
void UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value);
/*
* Misc stuff.
* PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD
* REALLY HAS NO BETTER SECTION
* Misc stuff.
* PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD
* REALLY HAS NO BETTER SECTION
*/
bool logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname,const char* target, const char* descriptiontype, const char* description,int event_nid);
void GetEventLogs(const char* name,char* target,uint32 account_id=0,uint8 eventid=0,char* detail=0,char* timestamp=0, CharacterEventLog_Struct* cel=0);
uint32 GetKarma(uint32 acct_id);
void UpdateKarma(uint32 acct_id, uint32 amount);
/*
* Things which really dont belong here...
*/
/* Things which really dont belong here... */
int16 CommandRequirement(const char* commandname);
protected:
-127
View File
@@ -1,127 +0,0 @@
#include "../common/debug.h"
#include <iostream>
#include "entity.h"
#include "masterentity.h"
#include "../common/string_util.h"
#include "../common/breakdowns.h"
#include <stdlib.h>
extern EntityList entity_list;
void DispatchFinishedDBAsync(DBAsyncWork* dbaw) {
uint32_breakdown workpt;
workpt = dbaw->WPT();
switch (workpt.b4()) {
/* case DBA_b4_Main: {
switch (workpt.i24_1()) {
case DBA_i24_1_Main_LoadVariables: {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES* result;
DBAsyncQuery* dbaq = dbaw->PopAnswer();
if (dbaq->GetAnswer(errbuf, result))
database.LoadVariables_result(result);
else
std::cout << "Async DB.LoadVariables() failed: '" << errbuf << "'" << std::endl;
break;
}
default: {
std::cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4" << std::endl;
break;
}
}
}*/
case DBA_b4_Zone: {
if(zone == nullptr)
break;
zone->DBAWComplete(workpt.b1(), dbaw);
break;
}
case DBA_b4_Entity: {
Entity* entity = entity_list.GetID(workpt.w2_3());
if (!entity)
break;
entity->DBAWComplete(workpt.b1(), dbaw);
break;
}
default: {
std::cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4: " << (int) workpt.b4() << ", workpt=" << workpt << std::endl;
break;
}
}
safe_delete(dbaw);
}
#define MAX_TO_DELETE 10
#define MAX_BACKUPS 5
bool DBAsyncCB_CharacterBackup(DBAsyncWork* iWork) { // return true means delete data
char errbuf[MYSQL_ERRMSG_SIZE] = "dbaq == 0";
MYSQL_RES* result = 0;
MYSQL_ROW row;
char* query = 0;
uint32 i;
uint8 ToDeleteIndex = 0;
uint32 ToDelete[MAX_TO_DELETE];
memset(ToDelete, 0, sizeof(ToDelete));
uint32 BackupAges[MAX_BACKUPS]; // must be sorted, highest value in lowest index
memset(BackupAges, 0, sizeof(BackupAges));
bool FoundBackup[MAX_BACKUPS];
memset(FoundBackup, 0, sizeof(FoundBackup));
BackupAges[0] = 86400;
BackupAges[1] = 3600;
DBAsyncQuery* dbaq = iWork->PopAnswer();
if (dbaq && dbaq->GetAnswer(errbuf, &result)) {
while ((row = mysql_fetch_row(result))) {
for (i=0; i<MAX_BACKUPS; i++) {
if (BackupAges[i] == 0 || (uint32)atoi(row[1]) > BackupAges[i])
i = MAX_BACKUPS;
else if (!FoundBackup[i]) {
FoundBackup[i] = true;
break;
}
}
if (i >= MAX_BACKUPS)
ToDelete[ToDeleteIndex++] = atoi(row[0]);
if (ToDeleteIndex >= MAX_TO_DELETE)
break;
}
if (ToDelete[0]) {
uint32 len = 0, size = 0;
AppendAnyLenString(&query, &size, &len, "Delete from character_backup where id=%u", ToDelete[0]);
for (uint8 i=1; i<ToDeleteIndex; i++) {
AppendAnyLenString(&query, &size, &len, " or id=%u", ToDelete[i]);
}
if (!database.RunQuery(query, len, errbuf)) {
LogFile->write(EQEMuLog::Error, "Error in DBAsyncCB_CharacterBackup query2 '%s' %s", query, errbuf);
safe_delete_array(query);
return true;
}
safe_delete_array(query);
}
bool needtoinsert = false;
for (i=0; i<MAX_BACKUPS; i++) {
if (BackupAges[i] != 0 && !FoundBackup[i])
needtoinsert = true;
}
if (needtoinsert) {
if (!database.RunQuery(query, MakeAnyLenString(&query,
"Insert Delayed into character_backup (charid, account_id, name, profile, level, class, x, y, z, zoneid) "
"select id, account_id, name, profile, level, class, x, y, z, zoneid "
"from character_ where id=%u", iWork->WPT()), errbuf)) {
std::cout << "Error in DBAsyncCB_CharacterBackup query3 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return true;
}
safe_delete_array(query);
}
}
else {
// std::cout << "Error in DBAsyncCB_CharacterBackup query1 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return true;
}
return true;
}
-22
View File
@@ -1,22 +0,0 @@
#ifndef ZONEDBASYNC_H
#define ZONEDBASYNC_H
#include "../common/dbasync.h"
void DispatchFinishedDBAsync(DBAsyncWork* iDBAW);
bool DBAsyncCB_CharacterBackup(DBAsyncWork* iWork);
#define DBA_b4_Main 1
#define DBA_b4_Worldserver 2
#define DBA_b4_Zone 3
#define DBA_b4_Entity 4
#define DBA_b1_Entity_SeeQPT 0
#define DBA_b1_Entity_Client_InfoForLogin 1
#define DBA_b1_Entity_Client_Save 2
#define DBA_b1_Entity_Client_Backup 3
#define DBA_b1_Entity_Corpse_Backup 4
#define DBA_b1_Zone_MerchantLists 5
#define DBA_b1_Zone_MerchantListsTemp 6
#endif
+2 -1
View File
@@ -354,7 +354,7 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc
m_pp.zoneInstance = instance_id;
//Force a save so its waiting for them when they zone
Save(2);
Save(2);
if (zone_id == zone->GetZoneID() && instance_id == zone->GetInstanceID()) {
// No need to ask worldserver if we're zoning to ourselves (most
@@ -704,6 +704,7 @@ void Client::SetBindPoint(int to_zone, float new_x, float new_y, float new_z) {
m_pp.binds[0].y = new_y;
m_pp.binds[0].z = new_z;
}
database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[0].zoneId, 0, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0, 0);
}
void Client::GoToBind(uint8 bindnum) {