Merge remote-tracking branch 'upstream/master'

Conflicts:
	changelog.txt
	world/worlddb.cpp
This commit is contained in:
Paul Coene
2015-02-23 08:13:09 -05:00
109 changed files with 4914 additions and 3112 deletions
+3 -3
View File
@@ -525,7 +525,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
NPCType *made_npc = nullptr;
const NPCType *npc_type = database.GetNPCType(pet.npc_id);
const NPCType *npc_type = database.LoadNPCTypesData(pet.npc_id);
if(npc_type == nullptr) {
//log write
Log.Out(Logs::General, Logs::Error, "Unknown npc type for swarm pet spell id: %d", spell_id);
@@ -622,7 +622,7 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
NPCType *made_npc = nullptr;
const NPCType *npc_type = database.GetNPCType(typesid);
const NPCType *npc_type = database.LoadNPCTypesData(typesid);
if(npc_type == nullptr) {
//log write
Log.Out(Logs::General, Logs::Error, "Unknown npc type for swarm pet type id: %d", typesid);
@@ -715,7 +715,7 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
return;
//assuming we have pets in our table; we take the first pet as a base type.
const NPCType *base_type = database.GetNPCType(500);
const NPCType *base_type = database.LoadNPCTypesData(500);
NPCType *make_npc = new NPCType;
memcpy(make_npc, base_type, sizeof(NPCType));
+66 -54
View File
@@ -362,13 +362,40 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
//garunteed hit
bool ghit = false;
if((attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500)
if((attacker->aabonuses.MeleeSkillCheck + attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500)
ghit = true;
bool InFront = false;
if (attacker->InFrontMob(this, attacker->GetX(), attacker->GetY()))
InFront = true;
/*
This special ability adds a negative modifer to the defenders riposte/block/parry/chance
therefore reducing the defenders chance to successfully avoid the melee attack. At present
time this is the only way to fine tune counter these mods on players. This may
ultimately end up being more useful as fields in npc_types.
*/
int counter_all = 0;
int counter_riposte = 0;
int counter_block = 0;
int counter_parry = 0;
int counter_dodge = 0;
if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)){
counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0);
counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE,1);
counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2);
counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3);
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
}
//////////////////////////////////////////////////////////
// make enrage same as riposte
/////////////////////////////////////////////////////////
if (IsEnraged() && other->InFrontMob(this, other->GetX(), other->GetY())) {
if (IsEnraged() && InFront) {
damage = -3;
Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack.");
}
@@ -377,9 +404,10 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
// riposte
/////////////////////////////////////////////////////////
float riposte_chance = 0.0f;
if (CanRiposte && damage > 0 && CanThisClassRiposte() && other->InFrontMob(this, other->GetX(), other->GetY()))
if (CanRiposte && damage > 0 && CanThisClassRiposte() && InFront)
{
riposte_chance = (100.0f + (float)defender->aabonuses.RiposteChance + (float)defender->spellbonuses.RiposteChance + (float)defender->itembonuses.RiposteChance) / 100.0f;
riposte_chance = (100.0f + static_cast<float>(aabonuses.RiposteChance + spellbonuses.RiposteChance +
itembonuses.RiposteChance - counter_riposte - counter_all)) / 100.0f;
skill = GetSkill(SkillRiposte);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillRiposte, other, -10);
@@ -398,28 +426,19 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
///////////////////////////////////////////////////////
bool bBlockFromRear = false;
bool bShieldBlockFromRear = false;
if (this->IsClient()) {
int aaChance = 0;
// a successful roll on this does not mean a successful block is forthcoming. only that a chance to block
// from a direction other than the rear is granted.
// a successful roll on this does not mean a successful block is forthcoming. only that a chance to block
// from a direction other than the rear is granted.
int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind;
//Live AA - HightenedAwareness
int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind;
if (BlockBehindChance && zone->random.Roll(BlockBehindChance)) {
bBlockFromRear = true;
if (spellbonuses.BlockBehind || itembonuses.BlockBehind)
bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind.
}
}
if (BlockBehindChance && zone->random.Roll(BlockBehindChance))
bBlockFromRear = true;
float block_chance = 0.0f;
if (damage > 0 && CanThisClassBlock() && (other->InFrontMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) {
block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f;
if (damage > 0 && CanThisClassBlock() && (InFront || bBlockFromRear)) {
block_chance = (100.0f + static_cast<float>(aabonuses.IncreaseBlockChance + spellbonuses.IncreaseBlockChance +
itembonuses.IncreaseBlockChance - counter_block - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillBlock);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillBlock, other, -10);
@@ -435,32 +454,20 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
RollTable[1] = RollTable[0];
}
if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock)
&& (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) {
float bonusShieldBlock = 0.0f;
bonusShieldBlock = static_cast<float>(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock);
RollTable[1] += bonusShieldBlock;
}
if(IsClient() && damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock)
&& (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) {
if(CastToClient()->m_inv.GetItem(MainPrimary)) {
float bonusStaffBlock = 0.0f;
if (CastToClient()->m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt){
bonusStaffBlock = static_cast<float>(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock);
RollTable[1] += bonusStaffBlock;
}
}
}
//Try Shield Block OR TwoHandBluntBlockCheck
if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) && (InFront || bBlockFromRear))
RollTable[1] += static_cast<float>(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock - counter_block - counter_all);
else if(damage > 0 && HasTwoHandBluntEquiped() && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) && (InFront || bBlockFromRear))
RollTable[1] += static_cast<float>(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock - counter_block - counter_all);
//////////////////////////////////////////////////////
// parry
//////////////////////////////////////////////////////
float parry_chance = 0.0f;
if (damage > 0 && CanThisClassParry() && other->InFrontMob(this, other->GetX(), other->GetY()))
{
parry_chance = (100.0f + (float)defender->spellbonuses.ParryChance + (float)defender->itembonuses.ParryChance) / 100.0f;
if (damage > 0 && CanThisClassParry() && InFront){
parry_chance = (100.0f + static_cast<float>(aabonuses.ParryChance + itembonuses.ParryChance +
itembonuses.ParryChance - counter_parry - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillParry);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillParry, other, -10);
@@ -481,9 +488,11 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
// dodge
////////////////////////////////////////////////////////
float dodge_chance = 0.0f;
if (damage > 0 && CanThisClassDodge() && other->InFrontMob(this, other->GetX(), other->GetY()))
{
dodge_chance = (100.0f + (float)defender->spellbonuses.DodgeChance + (float)defender->itembonuses.DodgeChance) / 100.0f;
if (damage > 0 && CanThisClassDodge() && InFront){
dodge_chance = (100.0f + static_cast<float>(aabonuses.DodgeChance + spellbonuses.DodgeChance +
itembonuses.DodgeChance - counter_dodge - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillDodge);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10);
@@ -1595,10 +1604,12 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
//this generates a lot of 'updates' to the client that the client does not need
BuffFadeNonPersistDeath();
if((GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover))
UnmemSpellAll(true);
else
UnmemSpellAll(false);
if (RuleB(Character, UnmemSpellsOnDeath)) {
if((GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover))
UnmemSpellAll(true);
else
UnmemSpellAll(false);
}
if((RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) || RuleB(Character, LeaveNakedCorpses))
{
@@ -2404,8 +2415,8 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/)
{
assert(other != nullptr);
if(!other)
return;
if (other == this)
return;
@@ -3530,6 +3541,10 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
Log.Out(Logs::Detail, Logs::Combat, "Melee Damage reduced to %d", damage);
damage = ReduceAllDamage(damage);
TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker);
if (skill_used)
CheckNumHitsRemaining(NumHit::IncomingHitSuccess);
} else {
int32 origdmg = damage;
damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker);
@@ -3545,9 +3560,6 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker);
}
if (skill_used)
CheckNumHitsRemaining(NumHit::IncomingHitSuccess);
if(IsClient() && CastToClient()->sneaking){
CastToClient()->sneaking = false;
SendAppearancePacket(AT_Sneak, 0);
+91 -28
View File
@@ -139,7 +139,8 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
// Clear item faction mods
ClearItemFactionBonuses();
ShieldEquiped(false);
SetShieldEquiped(false);
SetTwoHandBluntEquiped(false);
unsigned int i;
//should not include 21 (SLOT_AMMO)
@@ -149,9 +150,12 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
continue;
AddItemBonuses(inst, newbon);
//Check if item is secondary slot is a 'shield'. Required for multiple spelll effects.
if (i == MainSecondary && (m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield))
ShieldEquiped(true);
//These are given special flags due to how often they are checked for various spell effects.
const Item_Struct *item = inst->GetItem();
if (i == MainSecondary && (item && item->ItemType == ItemTypeShield))
SetShieldEquiped(true);
else if (i == MainPrimary && (item && item->ItemType == ItemType2HBlunt))
SetTwoHandBluntEquiped(true);
}
//Power Source Slot
@@ -169,6 +173,17 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
continue;
AddItemBonuses(inst, newbon, false, true);
}
//Optional ability to have worn effects calculate as an addititive bonus instead of highest value
if (RuleI(Spells, AdditiveBonusWornType) && RuleI(Spells, AdditiveBonusWornType) != ET_WornEffect){
for (i = MainCharm; i < MainAmmo; i++) {
const ItemInst* inst = m_inv[i];
if(inst == 0)
continue;
AdditiveWornBonuses(inst, newbon);
}
}
// Caps
if(newbon->HPRegen > CalcHPRegenCap())
newbon->HPRegen = CalcHPRegenCap();
@@ -410,12 +425,12 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
else
newbon->DSMitigation += item->DSMitigation;
}
if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true);
if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);
}
if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true);
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0);
}
switch(item->BardType)
@@ -537,6 +552,45 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
}
void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug) {
/*
Powerful Non-live like option allows developers to add worn effects on items that
can stack with other worn effects of the same spell effect type, instead of only taking the highest value.
Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus.
To enable use RuleI(Spells, AdditiveBonusWornType)
Setting value = 2 Will force all live items to automatically be calculated additivily
Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation
which will also stack with regular (worntype 2) effects. [Ie set rule = 3 and item worntype = 3]
*/
if(!inst || !inst->IsType(ItemClassCommon))
return;
if(inst->GetAugmentType()==0 && isAug == true)
return;
const Item_Struct *item = inst->GetItem();
if(!inst->IsEquipable(GetBaseRace(),GetClass()))
return;
if(GetLevel() < item->ReqLevel)
return;
if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType))
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects
if (!isAug)
{
int i;
for (i = 0; i < EmuConstants::ITEM_COMMON_SIZE; i++) {
AdditiveWornBonuses(inst->GetAugment(i),newbon,true);
}
}
}
void Client::CalcEdibleBonuses(StatBonuses* newbon) {
uint32 i;
@@ -639,7 +693,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
uint8 focus = IsFocusEffect(0, 0, true,effect);
if (focus)
{
newbon->FocusEffects[focus] = effect;
newbon->FocusEffects[focus] = static_cast<uint8>(effect);
continue;
}
@@ -1393,7 +1447,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++) {
if(buffs[i].spellid != SPELL_UNKNOWN){
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false, buffs[i].ticsremaining,i);
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i);
if (buffs[i].numhits > 0)
Numhits(true);
@@ -1416,10 +1470,11 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
}
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot,
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
{
int i, effect_value, base2, max, effectid;
bool AdditiveWornBonus = false;
Mob *caster = nullptr;
if(!IsAISpellEffect && !IsValidSpell(spell_id))
@@ -1439,10 +1494,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
uint8 focus = IsFocusEffect(spell_id, i);
if (focus)
{
new_bonus->FocusEffects[focus] = spells[spell_id].effectid[i];
if (WornType){
if (RuleB(Spells, UseAdditiveFocusFromWornSlot))
new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i];
}
else
new_bonus->FocusEffects[focus] = static_cast<uint8>(spells[spell_id].effectid[i]);
continue;
}
if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType))
AdditiveWornBonus = true;
effectid = spells[spell_id].effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
@@ -1808,7 +1872,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_CriticalHitChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus) {
if (AdditiveWornBonus) {
if(base2 == -1)
new_bonus->CriticalHitChance[HIGHEST_SKILL+1] += effect_value;
else
@@ -1834,7 +1898,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_CrippBlowChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->CrippBlowChance += effect_value;
else if((effect_value < 0) && (new_bonus->CrippBlowChance > effect_value))
@@ -1848,7 +1912,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_AvoidMeleeChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->AvoidMeleeChanceEffect += effect_value;
else if((effect_value < 0) && (new_bonus->AvoidMeleeChanceEffect > effect_value))
@@ -1861,7 +1925,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_RiposteChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->RiposteChance += effect_value;
else if((effect_value < 0) && (new_bonus->RiposteChance > effect_value))
@@ -1874,7 +1938,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DodgeChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->DodgeChance += effect_value;
else if((effect_value < 0) && (new_bonus->DodgeChance > effect_value))
@@ -1887,7 +1951,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ParryChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->ParryChance += effect_value;
else if((effect_value < 0) && (new_bonus->ParryChance > effect_value))
@@ -1900,7 +1964,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DualWieldChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->DualWieldChance += effect_value;
else if((effect_value < 0) && (new_bonus->DualWieldChance > effect_value))
@@ -1914,7 +1978,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DoubleAttackChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->DoubleAttackChance += effect_value;
else if((effect_value < 0) && (new_bonus->DoubleAttackChance > effect_value))
@@ -1928,7 +1992,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_TripleAttackChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->TripleAttackChance += effect_value;
else if((effect_value < 0) && (new_bonus->TripleAttackChance > effect_value))
@@ -1941,7 +2005,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_MeleeLifetap:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->MeleeLifetap += spells[spell_id].base[i];
else if((effect_value < 0) && (new_bonus->MeleeLifetap > effect_value))
@@ -1990,7 +2054,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_HundredHands:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->HundredHands += effect_value;
if (effect_value > 0 && effect_value > new_bonus->HundredHands)
@@ -2012,7 +2076,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_HitChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus){
if (AdditiveWornBonus){
if(base2 == -1)
new_bonus->HitChanceEffect[HIGHEST_SKILL+1] += effect_value;
else
@@ -2079,7 +2143,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ProcChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->ProcChanceSPA += effect_value;
else if((effect_value < 0) && (new_bonus->ProcChanceSPA > effect_value))
@@ -2117,7 +2181,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DivineSave:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus) {
if (AdditiveWornBonus) {
new_bonus->DivineSaveChance[0] += effect_value;
new_bonus->DivineSaveChance[1] = 0;
}
@@ -2126,7 +2190,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
{
new_bonus->DivineSaveChance[0] = effect_value;
new_bonus->DivineSaveChance[1] = base2;
//SetDeathSaveChance(true);
}
break;
}
@@ -3046,12 +3109,12 @@ void NPC::CalcItemBonuses(StatBonuses *newbon)
newbon->ProcChance += cur->CombatEffects;
}
if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon);
ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type);
}
if (RuleB(Spells, NPC_UseFocusFromItems)){
if (cur->Focus.Effect>0 && (cur->Focus.Type == ET_Focus)){ // focus effects
ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon, 0, true);
ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon);
}
}
+13 -13
View File
@@ -4395,24 +4395,24 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
item = inst->GetItem();
if (item != 0)
{
ns->spawn.equipment[i].material = item->Material;
ns->spawn.equipment[i].elitematerial = item->EliteMaterial;
ns->spawn.equipment[i].heroforgemodel = item->HerosForgeModel;
ns->spawn.equipment[i].Material = item->Material;
ns->spawn.equipment[i].EliteMaterial = item->EliteMaterial;
ns->spawn.equipment[i].HeroForgeModel = item->HerosForgeModel;
if (armor_tint[i])
{
ns->spawn.colors[i].color = armor_tint[i];
ns->spawn.colors[i].Color = armor_tint[i];
}
else
{
ns->spawn.colors[i].color = item->Color;
ns->spawn.colors[i].Color = item->Color;
}
}
else
{
if (armor_tint[i])
{
ns->spawn.colors[i].color = armor_tint[i];
ns->spawn.colors[i].Color = armor_tint[i];
}
}
}
@@ -4426,9 +4426,9 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
{
if(strlen(item->IDFile) > 2)
{
ns->spawn.equipment[MaterialPrimary].material = atoi(&item->IDFile[2]);
ns->spawn.equipment[MaterialPrimary].Material = atoi(&item->IDFile[2]);
}
ns->spawn.colors[MaterialPrimary].color = GetEquipmentColor(MaterialPrimary);
ns->spawn.colors[MaterialPrimary].Color = GetEquipmentColor(MaterialPrimary);
}
}
@@ -4440,9 +4440,9 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
{
if(strlen(item->IDFile) > 2)
{
ns->spawn.equipment[MaterialSecondary].material = atoi(&item->IDFile[2]);
ns->spawn.equipment[MaterialSecondary].Material = atoi(&item->IDFile[2]);
}
ns->spawn.colors[MaterialSecondary].color = GetEquipmentColor(MaterialSecondary);
ns->spawn.colors[MaterialSecondary].Color = GetEquipmentColor(MaterialSecondary);
}
}
}
@@ -5061,7 +5061,7 @@ void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32
wc->spawn_id = GetID();
wc->material = material;
wc->color.color = color;
wc->color.Color = color;
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
@@ -10959,7 +10959,7 @@ void Bot::CalcItemBonuses()
}
}
if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses);
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type);
}
}
}
@@ -11043,7 +11043,7 @@ void Bot::CalcItemBonuses()
}
}
if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses);
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type);
}
}
}
+116 -45
View File
@@ -141,6 +141,10 @@ Client::Client(EQStreamInterface* ieqs)
merc_timer(RuleI(Mercs, UpkeepIntervalMS)),
ItemTickTimer(10000),
ItemQuestTimer(500),
anon_toggle_timer(250),
afk_toggle_timer(250),
helm_toggle_timer(250),
light_update_timer(600),
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
@@ -161,6 +165,7 @@ Client::Client(EQStreamInterface* ieqs)
Trader=false;
Buyer = false;
CustomerID = 0;
TraderID = 0;
TrackingID = 0;
WID = 0;
account_id = 0;
@@ -245,7 +250,7 @@ Client::Client(EQStreamInterface* ieqs)
AttemptedMessages = 0;
TotalKarma = 0;
m_ClientVersion = ClientVersion::Unknown;
ClientVersionBit = 0;
m_ClientVersionBit = 0;
AggroCount = 0;
RestRegenHP = 0;
RestRegenMana = 0;
@@ -409,6 +414,69 @@ Client::~Client() {
UninitializeBuffSlots();
}
void Client::SendZoneInPackets()
{
//////////////////////////////////////////////////////
// Spawn Appearance Packet
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
sa->type = AT_SpawnID; // Is 0x10 used to set the player id?
sa->parameter = GetID(); // Four bytes for this parameter...
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
// Inform the world about the client
outapp = new EQApplicationPacket();
CreateSpawnPacket(outapp);
outapp->priority = 6;
if (!GetHideMe()) entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
if (GetPVP()) //force a PVP update until we fix the spawn struct
SendAppearancePacket(AT_PVP, GetPVP(), true, false);
//Send AA Exp packet:
if (GetLevel() >= 51)
SendAAStats();
// Send exp packets
outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1);
uint32 tmpxp2 = GetEXPForLevel(GetLevel());
// Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc)
if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) {
float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2);
eu->exp = (uint32)(330.0f * tmpxp);
outapp->priority = 6;
QueuePacket(outapp);
}
safe_delete(outapp);
SendAATimers();
outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct));
ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer;
strcpy(zonesendname->name, m_pp.name);
strcpy(zonesendname->name2, m_pp.name);
zonesendname->unknown0 = 0x0A;
QueuePacket(outapp);
safe_delete(outapp);
if (IsInAGuild()) {
SendGuildMembers();
SendGuildURL();
SendGuildChannel();
SendGuildLFGuildStatus();
}
SendLFGuildStatus();
//No idea why live sends this if even were not in a guild
SendGuildMOTD();
}
void Client::SendLogoutPackets() {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_CancelTrade, sizeof(CancelTrade_Struct));
@@ -2471,11 +2539,13 @@ void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32
bool Client::BindWound(Mob* bindmob, bool start, bool fail){
EQApplicationPacket* outapp = 0;
if(!fail) {
if(!fail)
{
outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct));
BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer;
// Start bind
if(!bindwound_timer.Enabled()) {
if(!bindwound_timer.Enabled())
{
//make sure we actually have a bandage... and consume it.
int16 bslot = m_inv.HasItemByUse(ItemTypeBandage, 1, invWhereWorn|invWherePersonal);
if (bslot == INVALID_INDEX) {
@@ -2521,7 +2591,9 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){
; // Binding self
}
}
} else {
}
else if (bindwound_timer.Check()) // Did the timer finish?
{
// finish bind
// disable complete timer
bindwound_timer.Disable();
@@ -2967,7 +3039,7 @@ void Client::Tell_StringID(uint32 string_id, const char *who, const char *messag
void Client::SetTint(int16 in_slot, uint32 color) {
Color_Struct new_color;
new_color.color = color;
new_color.Color = color;
SetTint(in_slot, new_color);
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color);
}
@@ -2978,8 +3050,8 @@ void Client::SetTint(int16 in_slot, Color_Struct& color) {
uint8 matslot = Inventory::CalcMaterialFromSlot(in_slot);
if (matslot != _MaterialInvalid)
{
m_pp.item_tint[matslot].color = color.color;
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color);
m_pp.item_tint[matslot].Color = color.Color;
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.Color);
}
}
@@ -5290,35 +5362,35 @@ void Client::SendRewards()
FastQueuePacket(&vetapp);
}
bool Client::TryReward(uint32 claim_id) {
//Make sure we have an open spot
//Make sure we have it in our acct and count > 0
//Make sure the entry was found
//If we meet all the criteria:
//Decrement our count by 1 if it > 1 delete if it == 1
//Create our item in bag if necessary at the free inv slot
//save
bool Client::TryReward(uint32 claim_id)
{
// Make sure we have an open spot
// Make sure we have it in our acct and count > 0
// Make sure the entry was found
// If we meet all the criteria:
// Decrement our count by 1 if it > 1 delete if it == 1
// Create our item in bag if necessary at the free inv slot
// save
uint32 free_slot = 0xFFFFFFFF;
for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) {
for (int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) {
ItemInst *item = GetInv().GetItem(i);
if(!item) {
if (!item) {
free_slot = i;
break;
}
}
if(free_slot == 0xFFFFFFFF)
if (free_slot == 0xFFFFFFFF)
return false;
char errbuf[MYSQL_ERRMSG_SIZE];
std::string query = StringFormat("SELECT amount FROM account_rewards "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
if (!results.Success())
return false;
}
if (results.RowCount() == 0)
return false;
@@ -5326,58 +5398,57 @@ bool Client::TryReward(uint32 claim_id) {
auto row = results.begin();
uint32 amt = atoi(row[0]);
if(amt == 0)
if (amt == 0)
return false;
std::list<InternalVeteranReward>::iterator iter = zone->VeteranRewards.begin();
for (; iter != zone->VeteranRewards.end(); ++row)
if((*iter).claim_id == claim_id)
break;
auto iter = std::find_if(zone->VeteranRewards.begin(), zone->VeteranRewards.end(),
[claim_id](const InternalVeteranReward &a) { return a.claim_id == claim_id; });
if(iter == zone->VeteranRewards.end())
if (iter == zone->VeteranRewards.end())
return false;
if(amt == 1) {
if (amt == 1) {
query = StringFormat("DELETE FROM account_rewards "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
}
else {
} else {
query = StringFormat("UPDATE account_rewards SET amount = (amount-1) "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
}
InternalVeteranReward ivr = (*iter);
auto &ivr = (*iter);
ItemInst *claim = database.CreateItem(ivr.items[0].item_id, ivr.items[0].charges);
if(!claim) {
if (!claim) {
Save();
return true;
}
bool lore_conflict = CheckLoreConflict(claim->GetItem());
for(int y = 1; y < 8; y++)
if(ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) {
for (int y = 1; y < 8; y++)
if (ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) {
ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges);
if(item_temp) {
if(CheckLoreConflict(item_temp->GetItem())) {
if (item_temp) {
if (CheckLoreConflict(item_temp->GetItem())) {
lore_conflict = true;
DuplicateLoreMessage(ivr.items[y].item_id);
}
claim->PutItem(y-1, *item_temp);
claim->PutItem(y - 1, *item_temp);
safe_delete(item_temp);
}
}
if(lore_conflict) {
if (lore_conflict) {
safe_delete(claim);
return true;
}
PutItemInInventory(free_slot, *claim);
SendItemPacket(free_slot, claim, ItemPacketTrade);
safe_delete(claim);
Save();
return true;
@@ -6173,7 +6244,7 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid
NPCType *made_npc = nullptr;
const NPCType *npc_type = database.GetNPCType(pet.npc_id);
const NPCType *npc_type = database.LoadNPCTypesData(pet.npc_id);
if(npc_type == nullptr) {
Log.Out(Logs::General, Logs::Error, "Unknown npc type for doppelganger spell id: %d", spell_id);
Message(0,"Unable to find pet!");
@@ -7396,7 +7467,7 @@ void Client::SendClearMercInfo()
void Client::DuplicateLoreMessage(uint32 ItemID)
{
if(!(ClientVersionBit & BIT_RoFAndLater))
if (!(m_ClientVersionBit & BIT_RoFAndLater))
{
Message_StringID(0, PICK_LORE);
return;
+13 -5
View File
@@ -266,10 +266,11 @@ public:
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);
uint32 FindTraderItemSerialNumber(int32 ItemID);
ItemInst* FindTraderItemBySerialNumber(int32 SerialNumber);
void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot);
void NukeTraderItem(uint16 slot,int16 charges,uint16 quantity,Client* customer,uint16 traderslot, int uniqueid);
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges);
void NukeTraderItem(uint16 slot, int16 charges, uint16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0);
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0);
void TradeRequestFailed(const EQApplicationPacket* app);
void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app);
void TraderUpdate(uint16 slot_id,uint32 trader_id);
@@ -1021,7 +1022,7 @@ public:
inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); }
inline const ClientVersion GetClientVersion() const { return m_ClientVersion; }
inline const uint32 GetClientVersionBit() const { return ClientVersionBit; }
inline const uint32 GetClientVersionBit() const { return m_ClientVersionBit; }
inline void SetClientVersion(ClientVersion in) { m_ClientVersion = in; }
/** Adventure Stuff **/
@@ -1139,7 +1140,7 @@ public:
void HandleLFGuildResponse(ServerPacket *pack);
void SendLFGuildStatus();
void SendGuildLFGuildStatus();
inline bool XTargettingAvailable() const { return ((ClientVersionBit & BIT_UFAndLater) && RuleB(Character, EnableXTargetting)); }
inline bool XTargettingAvailable() const { return ((m_ClientVersionBit & BIT_UFAndLater) && RuleB(Character, EnableXTargetting)); }
inline uint8 GetMaxXTargets() const { return MaxXTargets; }
void SetMaxXTargets(uint8 NewMax);
bool IsXTarget(const Mob *m) const;
@@ -1257,6 +1258,7 @@ protected:
friend class Mob;
void CalcItemBonuses(StatBonuses* newbon);
void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false);
void AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false);
int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat);
void CalcEdibleBonuses(StatBonuses* newbon);
void CalcAABonuses(StatBonuses* newbon);
@@ -1387,6 +1389,7 @@ private:
uint16 BoatID;
uint16 TrackingID;
uint16 CustomerID;
uint16 TraderID;
uint32 account_creation;
uint8 firstlogon;
uint32 mercid; // current merc
@@ -1414,6 +1417,7 @@ private:
bool CanBeInZone();
void SendLogoutPackets();
void SendZoneInPackets();
bool AddPacket(const EQApplicationPacket *, bool);
bool AddPacket(EQApplicationPacket**, bool);
bool SendAllPackets();
@@ -1464,6 +1468,10 @@ private:
Timer TrackingTimer;
Timer RespawnFromHoverTimer;
Timer merc_timer;
Timer anon_toggle_timer;
Timer afk_toggle_timer;
Timer helm_toggle_timer;
Timer light_update_timer;
glm::vec3 m_Proximity;
@@ -1509,7 +1517,7 @@ private:
uint32 AttemptedMessages;
ClientVersion m_ClientVersion;
uint32 ClientVersionBit;
uint32 m_ClientVersionBit;
int XPRate;
+273 -275
View File
@@ -398,12 +398,18 @@ void ClearMappedOpcode(EmuOpcode op)
// client methods
int Client::HandlePacket(const EQApplicationPacket *app)
{
if(Log.log_settings[Logs::LogCategory::Netcode].log_to_console > 0) {
if (Log.log_settings[Logs::LogCategory::Netcode].is_category_enabled == 1) {
char buffer[64];
app->build_header_dump(buffer);
Log.Out(Logs::Detail, Logs::Client_Server_Packet, "Dispatch opcode: %s", buffer);
}
if (Log.log_settings[Logs::Client_Server_Packet].is_category_enabled == 1)
Log.Out(Logs::General, Logs::Client_Server_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size());
if (Log.log_settings[Logs::Client_Server_Packet_With_Dump].is_category_enabled == 1)
Log.Out(Logs::General, Logs::Client_Server_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size(), DumpPacketToString(app).c_str());
EmuOpcode opcode = app->GetOpcode();
if (opcode == OP_AckPacket) {
return true;
@@ -445,23 +451,16 @@ int Client::HandlePacket(const EQApplicationPacket *app)
case CLIENT_CONNECTED: {
ClientPacketProc p;
p = ConnectedOpcodes[opcode];
if(p == nullptr) {
if(p == nullptr) {
std::vector<EQEmu::Any> args;
args.push_back(const_cast<EQApplicationPacket*>(app));
parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 0, &args);
char buffer[64];
Log.Out(Logs::Detail, Logs::Client_Server_Packet, "Unhandled incoming opcode: %s - 0x%04x", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode());
if (Log.log_settings[Logs::Client_Server_Packet].log_to_console > 0){
if (Log.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1){
char buffer[64];
app->build_header_dump(buffer);
if (app->size < 1000)
DumpPacket(app, app->size);
else{
std::cout << "Dump limited to 1000 characters:\n";
DumpPacket(app, 1000);
}
Log.Out(Logs::General, Logs::Client_Server_Packet_Unhandled, "%s %s", buffer, DumpPacketToString(app).c_str());
}
break;
}
@@ -1090,76 +1089,15 @@ void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app)
{
//////////////////////////////////////////////////////
// Spawn Appearance Packet
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
sa->type = AT_SpawnID; // Is 0x10 used to set the player id?
sa->parameter = GetID(); // Four bytes for this parameter...
outapp->priority = 6;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendExpZonein, 0);
QueuePacket(outapp);
safe_delete(outapp);
// Inform the world about the client
outapp = new EQApplicationPacket();
CreateSpawnPacket(outapp);
outapp->priority = 6;
if (!GetHideMe()) entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
if (GetPVP()) //force a PVP update until we fix the spawn struct
SendAppearancePacket(AT_PVP, GetPVP(), true, false);
//Send AA Exp packet:
if (GetLevel() >= 51)
SendAAStats();
// Send exp packets
outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1);
uint32 tmpxp2 = GetEXPForLevel(GetLevel());
// Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc)
if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) {
float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2);
eu->exp = (uint32)(330.0f * tmpxp);
outapp->priority = 6;
QueuePacket(outapp);
// SoF+ Gets Zone-In packets after sending OP_WorldObjectsSent
if (GetClientVersion() < ClientVersion::SoF)
{
SendZoneInPackets();
}
safe_delete(outapp);
SendAATimers();
outapp = new EQApplicationPacket(OP_SendExpZonein, 0);
QueuePacket(outapp);
safe_delete(outapp);
outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct));
ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer;
strcpy(zonesendname->name, m_pp.name);
strcpy(zonesendname->name2, m_pp.name);
zonesendname->unknown0 = 0x0A;
QueuePacket(outapp);
safe_delete(outapp);
/* this is actually the guild MOTD
outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2));
ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer;
strcpy(zonesendname2->name,m_pp.name);
QueuePacket(outapp);
safe_delete(outapp);*/
if (IsInAGuild()) {
SendGuildMembers();
SendGuildURL();
SendGuildChannel();
SendGuildLFGuildStatus();
}
SendLFGuildStatus();
//No idea why live sends this if even were not in a guild
SendGuildMOTD();
return;
}
@@ -1217,72 +1155,13 @@ void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app)
{
//This is a copy of SendExpZonein created for SoF+ due to packet order change
//////////////////////////////////////////////////////
// Spawn Appearance Packet
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
sa->type = AT_SpawnID; // Is 0x10 used to set the player id?
sa->parameter = GetID(); // Four bytes for this parameter...
outapp->priority = 6;
// New for SoF+
EQApplicationPacket* outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0);
QueuePacket(outapp);
safe_delete(outapp);
// Inform the world about the client
outapp = new EQApplicationPacket();
CreateSpawnPacket(outapp);
outapp->priority = 6;
if (!GetHideMe()) entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
if (GetPVP()) //force a PVP update until we fix the spawn struct
SendAppearancePacket(AT_PVP, GetPVP(), true, false);
//Send AA Exp packet:
if (GetLevel() >= 51)
SendAAStats();
// Send exp packets
outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1);
uint32 tmpxp2 = GetEXPForLevel(GetLevel());
// Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc)
if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) {
float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2);
eu->exp = (uint32)(330.0f * tmpxp);
outapp->priority = 6;
QueuePacket(outapp);
}
safe_delete(outapp);
SendAATimers();
// New for Secrets of Faydwer - Used in Place of OP_SendExpZonein
outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0);
QueuePacket(outapp);
safe_delete(outapp);
outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct));
ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer;
strcpy(zonesendname->name, m_pp.name);
strcpy(zonesendname->name2, m_pp.name);
zonesendname->unknown0 = 0x0A;
QueuePacket(outapp);
safe_delete(outapp);
if (IsInAGuild()) {
SendGuildMembers();
SendGuildURL();
SendGuildChannel();
SendGuildLFGuildStatus();
}
SendLFGuildStatus();
//No idea why live sends this if even were not in a guild
SendGuildMOTD();
// Packet order changed for SoF+, so below is sent here instead of OP_SendExpLogin
SendZoneInPackets();
if (RuleB(Mercs, AllowMercs))
{
@@ -1312,8 +1191,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
conn_state = ReceivedZoneEntry;
SetClientVersion(Connection()->GetClientVersion());
if (m_ClientVersion != ClientVersion::Unknown)
ClientVersionBit = 1 << (static_cast<unsigned int>(m_ClientVersion) - 1);
m_ClientVersionBit = ClientBitFromVersion(Connection()->GetClientVersion());
bool siv = m_inv.SetInventoryVersion(m_ClientVersion);
Log.Out(Logs::General, Logs::None, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), ClientVersionName(m_ClientVersion), m_ClientVersion);
@@ -1428,9 +1306,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Set item material tint */
for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++)
{
if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255)
if (m_pp.item_tint[i].RGB.UseTint == 1 || m_pp.item_tint[i].RGB.UseTint == 255)
{
m_pp.item_tint[i].rgb.use_tint = 0xFF;
m_pp.item_tint[i].RGB.UseTint = 0xFF;
}
}
@@ -1839,9 +1717,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (loaditems) { /* Dont load if a length error occurs */
BulkSendInventoryItems();
/* Send stuff on the cursor which isnt sent in bulk */
for (auto iter = m_inv.cursor_begin(); iter != m_inv.cursor_end(); ++iter) {
for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) {
/* First item cursor is sent in bulk inventory packet */
if (iter == m_inv.cursor_begin())
if (iter == m_inv.cursor_cbegin())
continue;
const ItemInst *inst = *iter;
SendItemPacket(MainCursor, inst, ItemPacketSummonItem);
@@ -1857,7 +1735,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
safe_delete(outapp);
}
if (ClientVersionBit & BIT_UFAndLater) {
if (m_ClientVersionBit & BIT_UFAndLater) {
outapp = new EQApplicationPacket(OP_XTargetResponse, 8);
outapp->WriteUInt32(GetMaxXTargets());
outapp->WriteUInt32(0);
@@ -3292,7 +3170,6 @@ void Client::Handle_OP_AutoFire(const EQApplicationPacket *app)
void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
{
// Although there are three different structs for OP_Bandolier, they are all the same size.
//
if (app->size != sizeof(BandolierCreate_Struct)) {
@@ -3304,19 +3181,20 @@ void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer;
switch (bs->action) {
case BandolierCreate:
switch (bs->Action)
{
case bandolierCreate:
CreateBandolier(app);
break;
case BandolierRemove:
case bandolierRemove:
RemoveBandolier(app);
break;
case BandolierSet:
case bandolierSet:
SetBandolier(app);
break;
default:
Log.Out(Logs::General, Logs::None, "Uknown Bandolier action %i", bs->action);
Log.Out(Logs::General, Logs::None, "Unknown Bandolier action %i", bs->Action);
break;
}
}
@@ -5517,7 +5395,7 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
if (damage < 0)
damage = 31337;
if (admin >= minStatusToAvoidFalling && GetGM()){
if (admin >= minStatusToAvoidFalling && GetGM()) {
Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype);
SetHP(GetHP() - 1);//needed or else the client wont acknowledge
return;
@@ -5527,11 +5405,11 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
SetHP(GetHP() - 1);//needed or else the client wont acknowledge
return;
}
else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184){
else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) {
// Hard coded tutorial and load zones for no fall damage
return;
}
else{
else {
SetHP(GetHP() - (damage * RuleR(Character, EnvironmentDamageMulipliter)));
/* EVENT_ENVIRONMENTAL_DAMAGE */
@@ -7361,6 +7239,16 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app)
if (gc->guildeqid == 0)
gc->guildeqid = GuildID();
// Convert Membership Level between RoF and previous clients.
if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF)
{
gc->officer = 0;
}
if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF)
{
gc->officer = 8;
}
Log.Out(Logs::Detail, Logs::Guilds, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size);
client->SetPendingGuildInvitation(true);
client->QueuePacket(app);
@@ -7395,6 +7283,8 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app)
GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer;
uint32 guildrank = gj->response;
if (GetClientVersion() >= ClientVersion::RoF)
{
if (gj->response > 9)
@@ -7423,9 +7313,25 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app)
Log.Out(Logs::Detail, Logs::Guilds, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s",
gj->guildeqid, gj->response, gj->inviter, gj->newmember);
//ok, the invite is also used for changing rank as well.
Mob* inviter = entity_list.GetMob(gj->inviter);
if (inviter && inviter->IsClient())
{
Client* client = inviter->CastToClient();
// Convert Membership Level between RoF and previous clients.
if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF)
{
guildrank = 0;
}
if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF)
{
guildrank = 8;
}
}
//we dont really care a lot about what this packet means, as long as
//it has been authorized with the guild manager
if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) {
if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, guildrank)) {
worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName());
Message(13, "Invalid invite response packet!");
return;
@@ -7453,7 +7359,7 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app)
//change guild and rank
uint32 guildrank = gj->response;
guildrank = gj->response;
if (GetClientVersion() >= ClientVersion::RoF)
{
@@ -9863,6 +9769,9 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
return;
}
if (mypet->GetPetType() == petTargetLock && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST))
return;
if (mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy))
return;
@@ -10532,16 +10441,16 @@ void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app)
if (mptbs->Action == 0) {
const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID);
if (BaseItem) {
m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID;
m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon;
strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name));
database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon);
m_pp.potionbelt.Items[mptbs->SlotNumber].ID = BaseItem->ID;
m_pp.potionbelt.Items[mptbs->SlotNumber].Icon = BaseItem->Icon;
strn0cpy(m_pp.potionbelt.Items[mptbs->SlotNumber].Name, BaseItem->Name, sizeof(BaseItem->Name));
database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.Items[mptbs->SlotNumber].ID, m_pp.potionbelt.Items[mptbs->SlotNumber].Icon);
}
}
else {
m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0;
m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0;
strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1);
m_pp.potionbelt.Items[mptbs->SlotNumber].ID = 0;
m_pp.potionbelt.Items[mptbs->SlotNumber].Icon = 0;
m_pp.potionbelt.Items[mptbs->SlotNumber].Name[0] = '\0';
}
}
@@ -12042,12 +11951,6 @@ void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app)
{
EQApplicationPacket empty(OP_ShopEndConfirm);
QueuePacket(&empty);
//EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2);
//outapp->pBuffer[0] = 0x0a;
//outapp->pBuffer[1] = 0x66;
//QueuePacket(outapp);
//safe_delete(outapp);
//Save();
return;
}
@@ -12143,6 +12046,10 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
mp->quantity = prevcharges;
}
// Item's stackable, but the quantity they want to buy exceeds the max stackable quantity.
if (item->Stackable && mp->quantity > item->StackSize)
mp->quantity = item->StackSize;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct));
Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer;
mpo->quantity = mp->quantity;
@@ -12169,6 +12076,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
mpo->price = SinglePrice;
else
mpo->price = SinglePrice * mp->quantity;
if (mpo->price < 0)
{
safe_delete(outapp);
@@ -12638,6 +12546,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
else if (sa->type == AT_Anim) {
if (IsAIControlled())
return;
if (sa->parameter == ANIM_STAND) {
SetAppearance(eaStanding);
playeraction = 0;
@@ -12671,15 +12580,6 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
SetFeigned(false);
}
// This is from old code
// I have no clue what it's for
/*
else if (sa->parameter == 0x05) {
// Illusion
std::cout << "Illusion packet recv'd:" << std::endl;
DumpPacket(app);
}
*/
else {
std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl;
return;
@@ -12688,6 +12588,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
entity_list.QueueClients(this, app, true);
}
else if (sa->type == AT_Anon) {
if(!anon_toggle_timer.Check()) {
return;
}
// For Anon/Roleplay
if (sa->parameter == 1) { // Anon
m_pp.anon = 1;
@@ -12709,13 +12613,18 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
return;
}
else if (sa->type == AT_AFK) {
this->AFK = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
if(afk_toggle_timer.Check()) {
AFK = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
}
}
else if (sa->type == AT_Split) {
m_pp.autosplit = (sa->parameter == 1);
}
else if (sa->type == AT_Sneak) {
if(sneaking == 0)
return;
if (sa->parameter != 0)
{
if (!HasSkill(SkillSneak))
@@ -12727,7 +12636,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
}
return;
}
this->sneaking = 0;
sneaking = 0;
entity_list.QueueClients(this, app, true);
}
else if (sa->type == AT_Size)
@@ -12739,7 +12648,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
}
else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield)
{
entity_list.QueueClients(this, app, false);
//don't do anything with this
}
else if (sa->type == AT_Levitate)
{
@@ -12748,8 +12657,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
}
else if (sa->type == AT_ShowHelm)
{
m_pp.showhelm = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
if(helm_toggle_timer.Check()) {
m_pp.showhelm = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
}
}
else {
std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec
@@ -13360,7 +13271,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
/*
if (GetClientVersion() >= EQClientRoF)
max_items = 200;
max_items = 200;
*/
//Show Items
@@ -13372,20 +13283,25 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
{
case BazaarTrader_EndTraderMode: {
Trader_EndTrader();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Trader Session");
break;
}
case BazaarTrader_EndTransaction: {
Client* c = entity_list.GetClientByID(sis->TraderID);
if (c)
{
c->WithCustomer(0);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Transaction");
}
else
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Null Client Pointer");
break;
}
case BazaarTrader_ShowItems: {
Trader_ShowItems();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Show Trader Items");
break;
}
default: {
@@ -13408,6 +13324,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
{
GetItems_Struct* gis = GetTraderItems();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Start Trader Mode");
// Verify there are no NODROP or items with a zero price
bool TradeItemsValid = true;
@@ -13454,7 +13371,8 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
safe_delete(gis);
this->Trader_StartTrader();
// This refreshes the Trader window to display the End Trader button
if (GetClientVersion() >= ClientVersion::RoF)
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct));
@@ -13464,15 +13382,6 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
safe_delete(outapp);
}
}
else if (app->size == sizeof(TraderStatus_Struct))
{
TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer;
if (tss->Code == BazaarTrader_ShowItems)
{
Trader_ShowItems();
}
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n",
ints->Code);
@@ -13480,9 +13389,35 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
Log.Out(Logs::General, Logs::Error, "Unknown TraderStruct code of: %i\n", ints->Code);
}
}
else if (app->size == sizeof(TraderStatus_Struct))
{
TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Trader Status Code: %d", tss->Code);
switch (tss->Code)
{
case BazaarTrader_EndTraderMode: {
Trader_EndTrader();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Trader Session");
break;
}
case BazaarTrader_ShowItems: {
Trader_ShowItems();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Show Trader Items");
break;
}
default: {
Log.Out(Logs::Detail, Logs::Trading, "Unhandled action code in OP_Trader ShowItems_Struct");
break;
}
}
}
else if (app->size == sizeof(TraderPriceUpdate_Struct))
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Trader Price Update");
HandleTraderPriceUpdate(app);
}
else {
@@ -13501,23 +13436,22 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
//
// Client has elected to buy an item from a Trader
//
if (app->size != sizeof(TraderBuy_Struct)) {
Log.Out(Logs::General, Logs::Error, "Wrong size: OP_TraderBuy, size=%i, expected %i", app->size, sizeof(TraderBuy_Struct));
return;
}
if (app->size == sizeof(TraderBuy_Struct)){
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){
BuyTraderItem(tbs, Trader, app);
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
}
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){
BuyTraderItem(tbs, Trader, app);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Buy Trader Item ");
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Struct size mismatch");
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
}
return;
}
@@ -13579,51 +13513,124 @@ void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app)
void Client::Handle_OP_TraderShop(const EQApplicationPacket *app)
{
// Bazaar Trader:
//
// This is when a potential purchaser right clicks on this client who is in Trader mode to
// browse their goods.
//
TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer;
if (app->size == sizeof(TraderClick_Struct))
{
TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer;
if (app->size != sizeof(TraderClick_Struct)) {
Log.Out(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: TraderClick_Struct TraderID %d, Code %d, Unknown008 %d, Approval %d",
tcs->TraderID, tcs->Code, tcs->Unknown008, tcs->Approval);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch");
if (tcs->Code == BazaarWelcome)
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info");
SendBazaarWelcome();
}
else
{
// This is when a potential purchaser right clicks on this client who is in Trader mode to
// browse their goods.
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct));
TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer;
Client* Trader = entity_list.GetClientByID(tcs->TraderID);
if (Trader)
{
outtcs->Approval = Trader->WithCustomer(GetID());
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Shop Request (%s) to (%s) with Approval: %d", GetCleanName(), Trader->GetCleanName(), outtcs->Approval);
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)"
" returned a nullptr pointer");
return;
}
outtcs->TraderID = tcs->TraderID;
outtcs->Unknown008 = 0x3f800000;
QueuePacket(outapp);
if (outtcs->Approval) {
this->BulkSendTraderInventory(Trader->CharacterID());
Trader->Trader_CustomerBrowsing(this);
TraderID = tcs->TraderID;
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Inventory Sent");
}
else
{
Message_StringID(clientMessageYellow, TRADER_BUSY);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Busy");
}
safe_delete(outapp);
return;
}
return;
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct));
TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer;
Client* Customer = entity_list.GetClientByID(tcs->TraderID);
if (Customer)
outtcs->Approval = Customer->WithCustomer(GetID());
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)"
" returned a nullptr pointer");
return;
else if (app->size == sizeof(BazaarWelcome_Struct))
{
// RoF+
// Client requested Bazaar Welcome Info (Trader and Item Total Counts)
SendBazaarWelcome();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info");
}
else if (app->size == sizeof(TraderBuy_Struct))
{
// RoF+
// Customer has purchased an item from the Trader
outtcs->TraderID = tcs->TraderID;
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
outtcs->Unknown008 = 0x3f800000;
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID))
{
BuyTraderItem(tbs, Trader, app);
Log.Out(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s",
tbs->Action, tbs->Price, tbs->TraderID, tbs->ItemID, tbs->Quantity, tbs->ItemName);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "OP_TraderShop: Null Client Pointer");
}
}
else if (app->size == 4)
{
// RoF+
// Customer has closed the trade window
uint32 Command = *((uint32 *)app->pBuffer);
QueuePacket(outapp);
if (outtcs->Approval) {
this->BulkSendTraderInventory(Customer->CharacterID());
Customer->Trader_CustomerBrowsing(this);
if (Command == 4)
{
Client* c = entity_list.GetClientByID(TraderID);
TraderID = 0;
if (c)
{
c->WithCustomer(0);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Transaction - Code %d", Command);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Null Client Pointer for Trader - Code %d", Command);
}
EQApplicationPacket empty(OP_ShopEndConfirm);
QueuePacket(&empty);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Unhandled Code %d", Command);
}
}
else
Message_StringID(clientMessageYellow, TRADER_BUSY);
safe_delete(outapp);
return;
{
Log.Out(Logs::Detail, Logs::Trading, "Unknown size for OP_TraderShop: %i\n", app->size);
Log.Out(Logs::General, Logs::Error, "Unknown size for OP_TraderShop: %i\n", app->size);
DumpPacket(app);
return;
}
}
void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app)
@@ -13786,41 +13793,32 @@ void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app)
void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app)
{
if (app->size < sizeof(VeteranClaimRequest))
{
Log.Out(Logs::General, Logs::None, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size, sizeof(VeteranClaimRequest));
if (app->size < sizeof(VeteranClaim)) {
Log.Out(Logs::General, Logs::None,
"OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size,
sizeof(VeteranClaim));
DumpPacket(app);
return;
}
VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer;
VeteranClaim *vcr = (VeteranClaim *)app->pBuffer;
if (vcr->claim_id == 0xFFFFFFFF) //request update packet
{
if (vcr->claim_id == 0xFFFFFFFF) { // request update packet
SendRewards();
return;
}
else //try to claim something!
{
if (!TryReward(vcr->claim_id))
{
Message(13, "Your claim has been rejected.");
EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply));
VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer;
strcpy(cr->name, GetName());
cr->claim_id = vcr->claim_id;
cr->reject_field = -1;
FastQueuePacket(&vetapp);
}
else
{
EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply));
VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer;
strcpy(cr->name, GetName());
cr->claim_id = vcr->claim_id;
cr->reject_field = 0;
FastQueuePacket(&vetapp);
}
}
// try to claim something!
EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaim));
VeteranClaim *cr = (VeteranClaim *)vetapp->pBuffer;
strcpy(cr->name, GetName());
cr->claim_id = vcr->claim_id;
if (!TryReward(vcr->claim_id))
cr->action = 1;
else
cr->action = 0;
FastQueuePacket(&vetapp);
}
void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app)
+11 -3
View File
@@ -239,7 +239,8 @@ bool Client::Process() {
if(IsAIControlled())
AI_Process();
if (bindwound_timer.Check() && bindwound_target != 0) {
// Don't reset the bindwound timer so we can check it in BindWound as well.
if (bindwound_timer.Check(false) && bindwound_target != 0) {
BindWound(bindwound_target, false);
}
@@ -260,6 +261,13 @@ bool Client::Process() {
}
}
if(light_update_timer.Check()) {
UpdateEquipLightValue();
if(UpdateActiveLightValue()) {
SendAppearancePacket(AT_Light, GetActiveLightValue());
}
}
bool may_use_attacks = false;
/*
Things which prevent us from attacking:
@@ -960,7 +968,7 @@ 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.
if (ClientVersionBit & BIT_RoFAndLater) { // RoF+ can send 200 items
if (m_ClientVersionBit & BIT_RoFAndLater) { // RoF+ can send 200 items
numItemSlots = 200;
}
const Item_Struct *item;
@@ -1886,7 +1894,7 @@ void Client::DoHPRegen() {
}
void Client::DoManaRegen() {
if (GetMana() >= max_mana)
if (GetMana() >= max_mana && spellbonuses.ManaRegen >= 0)
return;
SetMana(GetMana() + CalcManaRegen() + RestRegenMana);
+56 -29
View File
@@ -2461,7 +2461,7 @@ void command_npctypespawn(Client *c, const Seperator *sep)
{
if (sep->IsNumber(1)) {
const NPCType* tmp = 0;
if ((tmp = database.GetNPCType(atoi(sep->arg[1])))) {
if ((tmp = database.LoadNPCTypesData(atoi(sep->arg[1])))) {
//tmp->fixedZ = 1;
NPC* npc = new NPC(tmp, 0, c->GetPosition(), FlyMode3);
if (npc && sep->IsNumber(2))
@@ -2618,7 +2618,7 @@ void command_peekinv(Client *c, const Seperator *sep)
}
else {
int cursorDepth = 0;
for (auto it = targetClient->GetInv().cursor_begin(); (it != targetClient->GetInv().cursor_end()); ++it, ++cursorDepth) {
for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++cursorDepth) {
inst_main = *it;
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
linker.SetItemInst(inst_main);
@@ -2923,7 +2923,7 @@ void command_viewnpctype(Client *c, const Seperator *sep)
else
{
uint32 npctypeid=atoi(sep->arg[1]);
const NPCType* npct = database.GetNPCType(npctypeid);
const NPCType* npct = database.LoadNPCTypesData(npctypeid);
if (npct) {
c->Message(0, " NPCType Info, ");
c->Message(0, " NPCTypeID: %u", npct->npc_id);
@@ -4284,31 +4284,49 @@ void command_goto(Client *c, const Seperator *sep)
void command_iteminfo(Client *c, const Seperator *sep)
{
const ItemInst* inst = c->GetInv()[MainCursor];
if (!inst)
c->Message(13, "Error: You need an item on your cursor for this command");
else {
const Item_Struct* item = inst->GetItem();
c->Message(0, "ID: %i Name: %s", item->ID, item->Name);
c->Message(0, " Lore: %s ND: %i NS: %i Type: %i", (item->LoreFlag) ? "true":"false", item->NoDrop, item->NoRent, item->ItemClass);
c->Message(0, " IDF: %s Size: %i Weight: %i icon_id: %i Price: %i", item->IDFile, item->Size, item->Weight, item->Icon, item->Price);
if (c->Admin() >= 200)
c->Message(0, "MinStatus: %i", item->MinStatus);
if (item->ItemClass==ItemClassBook)
c->Message(0, " This item is a Book: %s", item->Filename);
else if (item->ItemClass==ItemClassContainer)
c->Message(0, " This item is a container with %i slots", item->BagSlots);
else {
c->Message(0, " equipableSlots: %u equipable Classes: %u", item->Slots, item->Classes);
c->Message(0, " Magic: %i SpellID: %i Proc Level: %i DBCharges: %i CurCharges: %i", item->Magic, item->Click.Effect, item->Click.Level, item->MaxCharges, inst->GetCharges());
c->Message(0, " EffectType: 0x%02x CastTime: %.2f", (uint8) item->Click.Type, (double) item->CastTime/1000);
c->Message(0, " Material: 0x%02x Color: 0x%08x Skill: %i", item->Material, item->Color, item->ItemType);
c->Message(0, " Required level: %i Required skill: %i Recommended level:%i", item->ReqLevel, item->RecSkill, item->RecLevel);
c->Message(0, " Skill mod: %i percent: %i", item->SkillModType, item->SkillModValue);
c->Message(0, " BaneRace: %i BaneBody: %i BaneDMG: %i", item->BaneDmgRace, item->BaneDmgBody, item->BaneDmgAmt);
}
auto inst = c->GetInv()[MainCursor];
if (!inst) { c->Message(13, "Error: You need an item on your cursor for this command"); }
auto item = inst->GetItem();
if (!item) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer");
c->Message(13, "Error: This item has no data reference");
}
Client::TextLink linker;
linker.SetLinkType(linker.linkItemInst);
linker.SetItemInst(inst);
auto item_link = linker.GenerateLink();
c->Message(0, "*** Item Info for [%s] ***", item_link.c_str());
c->Message(0, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass);
c->Message(0, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon);
c->Message(0, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice);
c->Message(0, ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", item->Material, item->Color, inst->GetColor(), item->Light);
c->Message(0, ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", (item->LoreFlag ? "TRUE" : "FALSE"), item->LoreGroup, item->Lore);
c->Message(0, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u",
item->NoDrop, item->NoRent, (uint8)item->NoPet, (uint8)item->NoTransfer, item->FVNoDrop);
if (item->ItemClass == ItemClassBook) {
c->Message(0, "*** This item is a Book (filename:'%s') ***", item->Filename);
}
else if (item->ItemClass == ItemClassContainer) {
c->Message(0, "*** This item is a Container (%u slots) ***", item->BagSlots);
}
else {
c->Message(0, "*** This item is Common ***");
c->Message(0, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots);
c->Message(0, ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", item->RecSkill, item->ReqLevel, item->RecLevel);
c->Message(0, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue);
c->Message(0, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i",
item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt);
c->Message(0, ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u",
(item->Magic ? "TRUE" : "FALSE"), item->Click.Effect, item->Click.Level, inst->GetCharges(), item->MaxCharges);
c->Message(0, ">> EffectType: 0x%02X, CastTime: %.2f", (uint8)item->Click.Type, ((double)item->CastTime / 1000));
}
if (c->Admin() >= 200)
c->Message(0, ">> MinStatus: %u", item->MinStatus);
}
void command_uptime(Client *c, const Seperator *sep)
@@ -6669,7 +6687,7 @@ void command_qglobal(Client *c, const Seperator *sep) {
}
if(!strcasecmp(sep->arg[1], "view")) {
const NPCType *type = database.GetNPCType(target->GetNPCTypeID());
const NPCType *type = database.LoadNPCTypesData(target->GetNPCTypeID());
if(!type)
c->Message(15, "Invalid NPC type.");
else if(type->qglobal)
@@ -7002,7 +7020,7 @@ void Client::Undye() {
database.SaveInventory(CharacterID(), inst, slot2);
}
m_pp.item_tint[cur_slot].color = 0;
m_pp.item_tint[cur_slot].Color = 0;
SendWearChange(cur_slot);
}
@@ -10605,6 +10623,15 @@ void command_logs(Client *c, const Seperator *sep){
c->Message(15, "Your Log Settings have been applied");
c->Message(15, "Output Method: %s :: Debug Level: %i - Category: %s", sep->arg[2], atoi(sep->arg[4]), Logs::LogCategoryName[atoi(sep->arg[3])]);
}
/* We use a general 'is_category_enabled' now, let's update when we update any output settings
This is used in hot places of code to check if its enabled in any way before triggering logs
*/
if (sep->arg[4] > 0){
Log.log_settings[atoi(sep->arg[3])].is_category_enabled = 1;
}
else{
Log.log_settings[atoi(sep->arg[3])].is_category_enabled = 0;
}
}
}
else {
+5 -3
View File
@@ -136,7 +136,8 @@ enum {
ALLOW_TO_TANK = 41,
IGNORE_ROOT_AGGRO_RULES = 42,
CASTING_RESIST_DIFF = 43,
MAX_SPECIAL_ATTACK = 44
COUNTER_AVOID_DAMAGE = 44,
MAX_SPECIAL_ATTACK = 45
};
typedef enum { //fear states
@@ -350,7 +351,8 @@ struct StatBonuses {
int32 CharmBreakChance; // chance to break charm
int32 SongRange; // increases range of beneficial bard songs
uint32 HPToManaConvert; // Uses HP to cast spells at specific conversion
uint32 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
uint8 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot
bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses)
int32 SkillDamageAmount2[HIGHEST_SKILL+2]; // Adds skill specific damage
uint32 NegateAttacks[3]; // 0 = bool HasEffect 1 = Buff Slot 2 = Max damage absorbed per hit
@@ -505,7 +507,7 @@ typedef enum {
petOther,
petCharmed,
petNPCFollow,
petHatelist //remain active as long something is on the hatelist. Don't listen to any commands
petTargetLock //remain active as long something is on the hatelist. Don't listen to any commands
} PetType;
typedef enum {
+14 -18
View File
@@ -113,15 +113,15 @@ Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std:
pc->Lock();
/* Load Item Tints */
pc->item_tint[0].color = pcs->item_tint[0].color;
pc->item_tint[1].color = pcs->item_tint[1].color;
pc->item_tint[2].color = pcs->item_tint[2].color;
pc->item_tint[3].color = pcs->item_tint[3].color;
pc->item_tint[4].color = pcs->item_tint[4].color;
pc->item_tint[5].color = pcs->item_tint[5].color;
pc->item_tint[6].color = pcs->item_tint[6].color;
pc->item_tint[7].color = pcs->item_tint[7].color;
pc->item_tint[8].color = pcs->item_tint[8].color;
pc->item_tint[0].Color = pcs->item_tint[0].Color;
pc->item_tint[1].Color = pcs->item_tint[1].Color;
pc->item_tint[2].Color = pcs->item_tint[2].Color;
pc->item_tint[3].Color = pcs->item_tint[3].Color;
pc->item_tint[4].Color = pcs->item_tint[4].Color;
pc->item_tint[5].Color = pcs->item_tint[5].Color;
pc->item_tint[6].Color = pcs->item_tint[6].Color;
pc->item_tint[7].Color = pcs->item_tint[7].Color;
pc->item_tint[8].Color = pcs->item_tint[8].Color;
/* Load Physical Appearance */
pc->haircolor = pcs->haircolor;
@@ -361,8 +361,8 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob (
database.QueryDatabase(ss.str().c_str());
}
auto start = client->GetInv().cursor_begin();
auto finish = client->GetInv().cursor_end();
auto start = client->GetInv().cursor_cbegin();
auto finish = client->GetInv().cursor_cend();
database.SaveCursor(client->CharacterID(), start, finish);
client->CalcBonuses();
@@ -1271,11 +1271,7 @@ void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
Mob::FillSpawnStruct(ns, ForWho);
ns->spawn.max_hp = 120;
if (IsPlayerCorpse())
ns->spawn.NPC = 3;
else
ns->spawn.NPC = 2;
ns->spawn.NPC = 2;
UpdateActiveLightValue();
ns->spawn.light = active_light;
@@ -1417,8 +1413,8 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const {
item = database.GetItem(GetEquipment(material_slot));
if(item != NO_ITEM) {
return item_tint[material_slot].rgb.use_tint ?
item_tint[material_slot].color :
return item_tint[material_slot].RGB.UseTint ?
item_tint[material_slot].Color :
item->Color;
}
+16 -14
View File
@@ -41,14 +41,14 @@ extern EntityList entity_list;
extern WorldServer worldserver;
Doors::Doors(const Door* door) :
close_timer(5000),
m_Position(door->pos_x, door->pos_y, door->pos_z, door->heading),
m_Destination(door->dest_x, door->dest_y, door->dest_z, door->dest_heading)
close_timer(5000),
m_Position(door->pos_x, door->pos_y, door->pos_z, door->heading),
m_Destination(door->dest_x, door->dest_y, door->dest_z, door->dest_heading)
{
db_id = door->db_id;
door_id = door->door_id;
strn0cpy(zone_name,door->zone_name,32);
strn0cpy(door_name,door->door_name,32);
strn0cpy(zone_name, door->zone_name, 32);
strn0cpy(door_name, door->door_name, 32);
incline = door->incline;
opentype = door->opentype;
guild_id = door->guild_id;
@@ -57,7 +57,7 @@ Doors::Doors(const Door* door) :
nokeyring = door->nokeyring;
trigger_door = door->trigger_door;
trigger_type = door->trigger_type;
triggered=false;
triggered = false;
door_param = door->door_param;
size = door->size;
invert_state = door->invert_state;
@@ -65,7 +65,7 @@ Doors::Doors(const Door* door) :
close_timer.Disable();
strn0cpy(dest_zone,door->dest_zone,16);
strn0cpy(dest_zone, door->dest_zone, 16);
dest_instance_id = door->dest_instance_id;
is_ldon_door = door->is_ldon_door;
@@ -73,14 +73,14 @@ Doors::Doors(const Door* door) :
}
Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uint16 dsize) :
close_timer(5000),
m_Position(position),
m_Destination(glm::vec4())
close_timer(5000),
m_Position(position),
m_Destination(glm::vec4())
{
db_id = database.GetDoorsCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion());
door_id = database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion());
strn0cpy(zone_name,zone->GetShortName(),32);
strn0cpy(door_name,dmodel,32);
strn0cpy(zone_name, zone->GetShortName(), 32);
strn0cpy(door_name, dmodel, 32);
incline = 0;
opentype = dopentype;
guild_id = 0;
@@ -89,7 +89,7 @@ Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uin
nokeyring = 0;
trigger_door = 0;
trigger_type = 0;
triggered=false;
triggered = false;
door_param = 0;
size = dsize;
invert_state = 0;
@@ -97,7 +97,7 @@ Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uin
close_timer.Disable();
strn0cpy(dest_zone,"NONE",32);
strn0cpy(dest_zone, "NONE", 32);
dest_instance_id = 0;
is_ldon_door = 0;
@@ -655,6 +655,8 @@ bool ZoneDatabase::LoadDoors(int32 iDoorCount, Door *into, const char *zone_name
into[rowIndex].db_id = atoi(row[0]);
into[rowIndex].door_id = atoi(row[1]);
Log.Out(Logs::Detail, Logs::Doors, "Door Load: db id: %u, door_id %u", into[rowIndex].db_id, into[rowIndex].door_id);
strn0cpy(into[rowIndex].zone_name,row[2],32);
strn0cpy(into[rowIndex].door_name,row[3],32);
+2 -2
View File
@@ -749,9 +749,9 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
if (bad) {
if (!caster->IsAttackAllowed(curmob, true))
continue;
if (center && !center->CheckLosFN(curmob))
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
continue;
if (!center && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize()))
if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize()))
continue;
} else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
// This does not check faction for beneficial AE buffs..only agro and attackable.
+1 -1
View File
@@ -270,7 +270,7 @@ void Client::GoFish()
//check for add NPC
if(npc_chance > 0 && npc_id) {
if(npc_chance < zone->random.Int(0, 99)) {
const NPCType* tmp = database.GetNPCType(npc_id);
const NPCType* tmp = database.LoadNPCTypesData(npc_id);
if(tmp != nullptr) {
auto positionNPC = GetPosition();
positionNPC.x = positionNPC.x + 3;
+2 -2
View File
@@ -72,8 +72,8 @@ public:
uint32 GetTotalGroupDamage(Mob* other);
void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr);
inline void SetLeader(Mob* newleader){ leader=newleader; };
inline Mob* GetLeader(){ return leader; };
char* GetLeaderName() { return membername[0]; };
inline Mob* GetLeader() { return leader; };
const char* GetLeaderName() { return membername[0]; };
void SendHPPacketsTo(Mob* newmember);
void SendHPPacketsFrom(Mob* newmember);
bool UpdatePlayer(Mob* update);
+8 -8
View File
@@ -219,17 +219,17 @@ bool HateList::RemoveEntFromHateList(Mob *in_entity)
{
if ((*iterator)->entity_on_hatelist == in_entity)
{
if (in_entity)
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "0", 0);
is_found = true;
if (in_entity && in_entity->IsClient())
in_entity->CastToClient()->DecrementAggroCount();
delete (*iterator);
iterator = list.erase(iterator);
if (in_entity)
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "0", 0);
}
else
++iterator;
@@ -282,14 +282,14 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
return nullptr;
Mob* top_hate = nullptr;
int32 hate = -1;
int64 hate = -1;
if (center == nullptr)
return nullptr;
if (RuleB(Aggro, SmartAggroList)){
Mob* top_client_type_in_range = nullptr;
int32 hate_client_type_in_range = -1;
int64 hate_client_type_in_range = -1;
int skipped_count = 0;
auto iterator = list.begin();
@@ -337,7 +337,7 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
continue;
}
int32 current_hate = cur->stored_hate_amount;
int64 current_hate = cur->stored_hate_amount;
if (cur->entity_on_hatelist->IsClient()){
@@ -459,13 +459,13 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
Mob *HateList::GetEntWithMostHateOnList(){
Mob* top = nullptr;
int32 hate = -1;
int64 hate = -1;
auto iterator = list.begin();
while (iterator != list.end())
{
struct_HateList *cur = (*iterator);
if (cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate))
if (cur && cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate))
{
top = cur->entity_on_hatelist;
hate = cur->stored_hate_amount;
+127 -85
View File
@@ -177,16 +177,16 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) {
}
bool Client::CheckLoreConflict(const Item_Struct* item) {
if (!item)
return false;
if (!(item->LoreFlag))
return false;
bool Client::CheckLoreConflict(const Item_Struct* item)
{
if (!item) { return false; }
if (!item->LoreFlag) { return false; }
if (item->LoreGroup == 0) { return false; }
if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result
if (item->LoreGroup == 0xFFFFFFFF) // Standard lore items; look everywhere except the shared bank, return the result
return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != INVALID_INDEX);
//If the item has a lore group, we check for other items with the same group and return the result
// If the item has a lore group, we check for other items with the same group and return the result
return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != INVALID_INDEX);
}
@@ -619,7 +619,7 @@ void Client::DropItem(int16 slot_id)
// Save client inventory change to database
if (slot_id == MainCursor) {
SendCursorBuffer();
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(CharacterID(), s, e);
} else {
database.SaveInventory(CharacterID(), nullptr, slot_id);
@@ -680,20 +680,37 @@ int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) {
return INVALID_ID;
}
void Client::SendCursorBuffer() {
void Client::SendCursorBuffer()
{
// Temporary work-around for the RoF+ Client Buffer
// Instead of dealing with client moving items in cursor buffer,
// we can just send the next item in the cursor buffer to the cursor.
if (GetClientVersion() >= ClientVersion::RoF)
{
if (!GetInv().CursorEmpty())
{
const ItemInst* inst = GetInv().GetCursorItem();
if (inst)
{
SendItemPacket(MainCursor, inst, ItemPacketSummonItem);
}
}
if (GetClientVersion() < ClientVersion::RoF) { return; }
if (GetInv().CursorEmpty()) { return; }
auto test_inst = GetInv().GetCursorItem();
if (test_inst == nullptr) { return; }
auto test_item = test_inst->GetItem();
if (test_item == nullptr) { return; }
bool lore_pass = true;
if (test_item->LoreGroup == 0xFFFFFFFF) {
lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
else if (test_item->LoreGroup != 0) {
lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
if (!lore_pass) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor",
GetName(), test_item->Name, test_item->ID);
Message_StringID(MT_LootMessages, 290);
parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0);
DeleteItemInInventory(MainCursor);
SendCursorBuffer();
}
else {
SendItemPacket(MainCursor, test_inst, ItemPacketSummonItem);
}
}
@@ -772,7 +789,7 @@ void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_upd
const ItemInst* inst = nullptr;
if (slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
if(update_db)
database.SaveCursor(character_id, s, e);
}
@@ -826,7 +843,7 @@ bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update)
SendItemPacket(MainCursor, &inst, ItemPacketSummonItem);
}
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
return database.SaveCursor(CharacterID(), s, e);
}
@@ -851,7 +868,7 @@ bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client
}
if (slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
return database.SaveCursor(this->CharacterID(), s, e);
}
else {
@@ -870,7 +887,7 @@ void Client::PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootI
SendLootItemInPacket(&inst, slot_id);
if (slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
else {
@@ -1009,7 +1026,7 @@ void Client::MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type)
from.SetCharges(from.GetCharges() - charges_to_move);
SendLootItemInPacket(tmp_inst, to_slot);
if (to_slot == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
else {
@@ -1320,10 +1337,33 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
return false;
}
// This could be expounded upon at some point to let the server know that
// the client has moved a buffered cursor item onto the active cursor -U
if (move_in->from_slot == move_in->to_slot) { // Item summon, no further processing needed
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
if (GetClientVersion() >= ClientVersion::RoF) { return true; } // Can't do RoF+
if (move_in->to_slot == MainCursor) {
auto test_inst = m_inv.GetItem(MainCursor);
if (test_inst == nullptr) { return true; }
auto test_item = test_inst->GetItem();
if (test_item == nullptr) { return true; }
if (!test_item->LoreFlag) { return true; }
bool lore_pass = true;
if (test_item->LoreGroup == 0xFFFFFFFF) {
lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
else if (test_item->LoreGroup != 0) {
lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
if (!lore_pass) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor",
GetName(), test_item->Name, test_item->ID);
Message_StringID(MT_LootMessages, 290);
parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0);
DeleteItemInInventory(MainCursor, 0, true);
}
}
return true;
}
@@ -1567,7 +1607,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
{
SendCursorBuffer();
}
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(character_id, s, e);
}
else
@@ -1726,7 +1766,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
{
SendCursorBuffer();
}
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(character_id, s, e);
}
else {
@@ -1734,7 +1774,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
}
if (dst_slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(character_id, s, e);
}
else {
@@ -1944,9 +1984,9 @@ void Client::QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call) {
void Client::DyeArmor(DyeStruct* dye){
int16 slot=0;
for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_TINT_END; i++) {
if (m_pp.item_tint[i].rgb.blue != dye->dye[i].rgb.blue ||
m_pp.item_tint[i].rgb.red != dye->dye[i].rgb.red ||
m_pp.item_tint[i].rgb.green != dye->dye[i].rgb.green
if (m_pp.item_tint[i].RGB.Blue != dye->dye[i].RGB.Blue ||
m_pp.item_tint[i].RGB.Red != dye->dye[i].RGB.Red ||
m_pp.item_tint[i].RGB.Green != dye->dye[i].RGB.Green
) {
slot = m_inv.HasItem(32557, 1, invWherePersonal);
if (slot != INVALID_INDEX){
@@ -1954,18 +1994,18 @@ void Client::DyeArmor(DyeStruct* dye){
uint8 slot2=SlotConvert(i);
ItemInst* inst = this->m_inv.GetItem(slot2);
if(inst){
uint32 armor_color = ((uint32)dye->dye[i].rgb.red << 16) | ((uint32)dye->dye[i].rgb.green << 8) | ((uint32)dye->dye[i].rgb.blue);
uint32 armor_color = ((uint32)dye->dye[i].RGB.Red << 16) | ((uint32)dye->dye[i].RGB.Green << 8) | ((uint32)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;
if(dye->dye[i].RGB.UseTint)
m_pp.item_tint[i].RGB.UseTint = 0xFF;
else
m_pp.item_tint[i].rgb.use_tint=0x00;
m_pp.item_tint[i].RGB.UseTint=0x00;
}
m_pp.item_tint[i].rgb.blue=dye->dye[i].rgb.blue;
m_pp.item_tint[i].rgb.red=dye->dye[i].rgb.red;
m_pp.item_tint[i].rgb.green=dye->dye[i].rgb.green;
m_pp.item_tint[i].RGB.Blue=dye->dye[i].RGB.Blue;
m_pp.item_tint[i].RGB.Red=dye->dye[i].RGB.Red;
m_pp.item_tint[i].RGB.Green=dye->dye[i].RGB.Green;
SendWearChange(i);
}
else{
@@ -2170,7 +2210,7 @@ void Client::RemoveNoRent(bool client_update)
}
local.clear();
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
}
@@ -2298,7 +2338,7 @@ void Client::RemoveDuplicateLore(bool client_update)
}
local_2.clear();
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
}
@@ -2380,7 +2420,7 @@ uint32 Client::GetEquipmentColor(uint8 material_slot) const
const Item_Struct *item = database.GetItem(GetEquipment(material_slot));
if(item != nullptr)
return ((m_pp.item_tint[material_slot].rgb.use_tint) ? m_pp.item_tint[material_slot].color : item->Color);
return ((m_pp.item_tint[material_slot].RGB.UseTint) ? m_pp.item_tint[material_slot].Color : item->Color);
return 0;
}
@@ -2436,97 +2476,99 @@ EQApplicationPacket* Client::ReturnItemPacket(int16 slot_id, const ItemInst* ins
return outapp;
}
static int16 BandolierSlotToWeaponSlot(int BandolierSlot) {
switch(BandolierSlot) {
case bandolierMainHand:
return MainPrimary;
case bandolierOffHand:
return MainSecondary;
case bandolierRange:
return MainRange;
default:
return MainAmmo;
static int16 BandolierSlotToWeaponSlot(int BandolierSlot)
{
switch (BandolierSlot)
{
case bandolierPrimary:
return MainPrimary;
case bandolierSecondary:
return MainSecondary;
case bandolierRange:
return MainRange;
default:
return MainAmmo;
}
}
void Client::CreateBandolier(const EQApplicationPacket *app) {
void Client::CreateBandolier(const EQApplicationPacket *app)
{
// Store bandolier set with the number and name passed by the client, along with the items that are currently
// in the players weapon slots.
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s Creating Bandolier Set %i, Set Name: %s", GetName(), bs->number, bs->name);
strcpy(m_pp.bandoliers[bs->number].name, bs->name);
Log.Out(Logs::Detail, Logs::Inventory, "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 = nullptr;
const Item_Struct *BaseItem = nullptr;
int16 WeaponSlot;
int16 WeaponSlot = 0;
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
for(int BandolierSlot = bandolierPrimary; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
InvItem = GetInv()[WeaponSlot];
if(InvItem) {
BaseItem = InvItem->GetItem();
Log.Out(Logs::Detail, Logs::Inventory, "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);
m_pp.bandoliers[bs->Number].Items[BandolierSlot].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].ID, m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon, bs->Name);
}
else {
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s no item in slot %i", GetName(), WeaponSlot);
m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = 0;
m_pp.bandoliers[bs->number].items[BandolierSlot].icon = 0;
m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID = 0;
m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon = 0;
}
}
}
void Client::RemoveBandolier(const EQApplicationPacket *app) {
void Client::RemoveBandolier(const EQApplicationPacket *app)
{
BandolierDelete_Struct *bds = (BandolierDelete_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Inventory, "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;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s removing set", GetName(), bds->Number);
memset(m_pp.bandoliers[bds->Number].Name, 0, 32);
for(int i = bandolierPrimary; i <= bandolierAmmo; i++) {
m_pp.bandoliers[bds->Number].Items[i].ID = 0;
m_pp.bandoliers[bds->Number].Items[i].Icon = 0;
}
database.DeleteCharacterBandolier(this->CharacterID(), bds->number);
database.DeleteCharacterBandolier(this->CharacterID(), bds->Number);
}
void Client::SetBandolier(const EQApplicationPacket *app) {
void Client::SetBandolier(const EQApplicationPacket *app)
{
// Swap the weapons in the given bandolier set into the character's weapon slots and return
// any items currently in the weapon slots to inventory.
BandolierSet_Struct *bss = (BandolierSet_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s activating set %i", GetName(), bss->number);
int16 slot;
int16 WeaponSlot;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s activating set %i", GetName(), bss->Number);
int16 slot = 0;
int16 WeaponSlot = 0;
ItemInst *BandolierItems[4]; // Temporary holding area for the weapons we pull out of their inventory
// First we pull the items for this bandolier set out of their inventory, this makes space to put the
// currently equipped items back.
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
for(int BandolierSlot = bandolierPrimary; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
// If this bandolier set has an item in this position
if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) {
if(m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID) {
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
// Check if the player has the item specified in the bandolier set on them.
//
slot = m_inv.HasItem(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id, 1,
slot = m_inv.HasItem(m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID, 1,
invWhereWorn|invWherePersonal);
// removed 'invWhereCursor' argument from above and implemented slots 30, 331-340 checks here
if (slot == INVALID_INDEX) {
if (m_inv.GetItem(MainCursor)) {
if (m_inv.GetItem(MainCursor)->GetItem()->ID == m_pp.bandoliers[bss->number].items[BandolierSlot].item_id &&
if (m_inv.GetItem(MainCursor)->GetItem()->ID == m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID &&
m_inv.GetItem(MainCursor)->GetCharges() >= 1) { // '> 0' the same, but this matches Inventory::_HasItem conditional check
slot = MainCursor;
}
else if (m_inv.GetItem(MainCursor)->GetItem()->ItemClass == 1) {
for(int16 CursorBagSlot = EmuConstants::CURSOR_BAG_BEGIN; CursorBagSlot <= EmuConstants::CURSOR_BAG_END; CursorBagSlot++) {
if (m_inv.GetItem(CursorBagSlot)) {
if (m_inv.GetItem(CursorBagSlot)->GetItem()->ID == m_pp.bandoliers[bss->number].items[BandolierSlot].item_id &&
if (m_inv.GetItem(CursorBagSlot)->GetItem()->ID == m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID &&
m_inv.GetItem(CursorBagSlot)->GetCharges() >= 1) { // ditto
slot = CursorBagSlot;
break;
@@ -2590,14 +2632,14 @@ void Client::SetBandolier(const EQApplicationPacket *app) {
// Now we move the required weapons into the character weapon slots, and return any items we are replacing
// back to inventory.
//
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
for(int BandolierSlot = bandolierPrimary; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
// Find the inventory slot corresponding to this bandolier slot
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
// if there is an item in this Bandolier slot ?
if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) {
if(m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID) {
// if the player has this item in their inventory, and it is not already where it needs to be
if(BandolierItems[BandolierSlot]) {
// Pull the item that we are going to replace
@@ -2826,9 +2868,9 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool
}
int limbo = 0;
for (auto cursor_itr = m_inv.cursor_begin(); cursor_itr != m_inv.cursor_end(); ++cursor_itr, ++limbo) {
for (auto cursor_itr = m_inv.cursor_cbegin(); cursor_itr != m_inv.cursor_cend(); ++cursor_itr, ++limbo) {
// m_inv.cursor_begin() is referenced as MainCursor in MapPossessions above
if (cursor_itr == m_inv.cursor_begin())
if (cursor_itr == m_inv.cursor_cbegin())
continue;
instmap[8000 + limbo] = *cursor_itr;
+100 -90
View File
@@ -19,6 +19,7 @@
#include "../common/global_define.h"
#include "../common/loottable.h"
#include "../common/misc_functions.h"
#include "../common/data_verification.h"
#include "client.h"
#include "entity.h"
@@ -35,7 +36,7 @@
// Queries the loottable: adds item & coin to the npc
void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) {
const LootTable_Struct* lts = 0;
const LootTable_Struct* lts = nullptr;
*copper = 0;
*silver = 0;
*gold = 0;
@@ -45,44 +46,39 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
if (!lts)
return;
// do coin
if (lts->mincash > lts->maxcash) {
std::cerr << "Error in loottable #" << loottable_id << ": mincash > maxcash" << std::endl;
uint32 min_cash = lts->mincash;
uint32 max_cash = lts->maxcash;
if(min_cash > max_cash) {
uint32 t = min_cash;
min_cash = max_cash;
max_cash = t;
}
else if (lts->maxcash != 0) {
uint32 cash = 0;
if (lts->mincash == lts->maxcash)
cash = lts->mincash;
else
cash = zone->random.Int(lts->mincash, lts->maxcash);
if (cash != 0) {
if (lts->avgcoin != 0) {
//this is some crazy ass stuff... and makes very little sense... dont use it, k?
uint32 mincoin = (uint32) (lts->avgcoin * 0.75 + 1);
uint32 maxcoin = (uint32) (lts->avgcoin * 1.25 + 1);
*copper = zone->random.Int(mincoin, maxcoin);
*silver = zone->random.Int(mincoin, maxcoin);
*gold = zone->random.Int(mincoin, maxcoin);
if(*copper > cash) { *copper = cash; }
cash -= *copper;
if(*silver>(cash/10)) { *silver = (cash/10); }
cash -= *silver*10;
if(*gold > (cash/100)) { *gold = (cash/100); }
cash -= *gold*100;
}
if (cash < 0) {
cash = 0;
}
*plat = cash / 1000;
cash -= *plat * 1000;
uint32 gold2 = cash / 100;
cash -= gold2 * 100;
uint32 silver2 = cash / 10;
cash -= silver2 * 10;
*gold += gold2;
*silver += silver2;
*copper += cash;
uint32 cash = 0;
if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
if(avg_cash_roll < upper_chance) {
cash = zone->random.Int(lts->avgcoin, max_cash);
} else {
cash = zone->random.Int(min_cash, lts->avgcoin);
}
} else {
cash = zone->random.Int(min_cash, max_cash);
}
if(cash != 0) {
*plat = cash / 1000;
cash -= *plat * 1000;
*gold = cash / 100;
cash -= *gold * 100;
*silver = cash / 10;
cash -= *silver * 10;
*copper = cash;
}
// Do items
@@ -97,11 +93,11 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
float drop_chance = 0.0f;
if(ltchance > 0.0 && ltchance < 100.0) {
drop_chance = zone->random.Real(0.0, 100.0);
drop_chance = (float)zone->random.Real(0.0, 100.0);
}
if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance < ltchance)) {
AddLootDropToNPC(npc,lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop);
if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance <= ltchance)) {
AddLootDropToNPC(npc, lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop);
}
}
}
@@ -114,63 +110,76 @@ void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* iteml
if (!lds) {
return;
}
if(lds->NumEntries == 0) //nothing possible to add
if(lds->NumEntries == 0)
return;
// Too long a list needs to be limited.
if(lds->NumEntries > 99 && droplimit < 1)
droplimit = lds->NumEntries/100;
uint8 limit = 0;
// Start at a random point in itemlist.
uint32 item = zone->random.Int(0, lds->NumEntries-1);
// Main loop.
for (uint32 i=0; i<lds->NumEntries;)
{
//Force the itemlist back to beginning.
if (item > (lds->NumEntries-1))
item = 0;
uint8 charges = lds->Entries[item].multiplier;
uint8 pickedcharges = 0;
// Loop to check multipliers.
for (uint32 x=1; x<=charges; x++)
{
// Actual roll.
float thischance = 0.0;
thischance = lds->Entries[item].chance;
float drop_chance = 0.0;
if(thischance != 100.0)
drop_chance = zone->random.Real(0.0, 100.0);
#if EQDEBUG>=11
Log.Out(Logs::General, Logs::None, "Drop chance for npc: %s, this chance:%f, drop roll:%f", npc->GetName(), thischance, drop_chance);
#endif
if (thischance == 100.0 || drop_chance < thischance)
{
uint32 itemid = lds->Entries[item].item_id;
const Item_Struct* dbitem = GetItem(itemid);
npc->AddLootDrop(dbitem, itemlist, lds->Entries[item].item_charges, lds->Entries[item].minlevel, lds->Entries[item].maxlevel, lds->Entries[item].equip_item, false);
pickedcharges++;
if(droplimit == 0 && mindrop == 0) {
for(uint32 i = 0; i < lds->NumEntries; ++i) {
int charges = lds->Entries[i].multiplier;
for(int j = 0; j < charges; ++j) {
if(zone->random.Real(0.0, 100.0) <= lds->Entries[i].chance) {
const Item_Struct* dbitem = GetItem(lds->Entries[i].item_id);
npc->AddLootDrop(dbitem, itemlist, lds->Entries[i].item_charges, lds->Entries[i].minlevel,
lds->Entries[i].maxlevel, lds->Entries[i].equip_item > 0 ? true : false, false);
}
}
}
// Items with multipliers only count as 1 towards the limit.
if(pickedcharges > 0)
limit++;
return;
}
// If true, limit reached.
if(limit >= droplimit && droplimit > 0)
break;
if(lds->NumEntries > 100 && droplimit == 0) {
droplimit = 10;
}
item++;
i++;
if(droplimit < mindrop) {
droplimit = mindrop;
}
// We didn't reach our minimium, run loop again.
if(i == lds->NumEntries){
if(limit < mindrop){
i = 0;
float roll_t = 0.0f;
bool active_item_list = false;
for(uint32 i = 0; i < lds->NumEntries; ++i) {
const Item_Struct* db_item = GetItem(lds->Entries[i].item_id);
if(db_item) {
roll_t += lds->Entries[i].chance;
active_item_list = true;
}
}
roll_t = EQEmu::ClampLower(roll_t, 100.0f);
if(!active_item_list) {
return;
}
mindrop = EQEmu::ClampLower(mindrop, (uint8)1);
int item_count = zone->random.Int(mindrop, droplimit);
for(int i = 0; i < item_count; ++i) {
float roll = (float)zone->random.Real(0.0, roll_t);
for(uint32 j = 0; j < lds->NumEntries; ++j) {
const Item_Struct* db_item = GetItem(lds->Entries[j].item_id);
if(db_item) {
if(roll < lds->Entries[j].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
int charges = (int)lds->Entries[i].multiplier;
charges = EQEmu::ClampLower(charges, 1);
for(int k = 1; k < charges; ++k) {
float c_roll = (float)zone->random.Real(0.0, 100.0);
if(c_roll <= lds->Entries[i].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
}
}
j = lds->NumEntries;
break;
}
else {
roll -= lds->Entries[j].chance;
}
}
}
} // We either ran out of items or reached our limit.
@@ -215,6 +224,7 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge
item->attuned = 0;
item->min_level = minlevel;
item->max_level = maxlevel;
if (equipit) {
uint8 eslot = 0xFF;
char newid[20];
+15 -2
View File
@@ -17,6 +17,7 @@
#include "questmgr.h"
#include "qglobals.h"
#include "../common/timer.h"
#include "../common/eqemu_logsys.h"
struct Events { };
struct Factions { };
@@ -1221,7 +1222,6 @@ std::string lua_get_encounter() {
return quest_manager.GetEncounter();
}
void lua_map_opcodes() {
MapOpcodes();
}
@@ -1249,6 +1249,17 @@ double lua_clock() {
return static_cast<double>(t) / 1000.0;
}
void lua_debug(std::string message) {
Log.Out(Logs::General, Logs::QuestDebug, message);
}
void lua_debug(std::string message, int level) {
if (level < Logs::General || level > Logs::Detail)
return;
Log.Out(static_cast<Logs::DebugLevel>(level), Logs::QuestDebug, message);
}
#define LuaCreateNPCParse(name, c_type, default_value) do { \
cur = table[#name]; \
if(luabind::type(cur) != LUA_TNIL) { \
@@ -1582,7 +1593,9 @@ luabind::scope lua_register_general() {
luabind::def("disable_recipe", &lua_disable_recipe),
luabind::def("clear_npctype_cache", &lua_clear_npctype_cache),
luabind::def("clock", &lua_clock),
luabind::def("create_npc", &lua_create_npc)
luabind::def("create_npc", &lua_create_npc),
luabind::def("debug", (void(*)(std::string))&lua_debug),
luabind::def("debug", (void(*)(std::string, int))&lua_debug)
];
}
+2 -2
View File
@@ -35,14 +35,13 @@
#include "zone.h"
#include "lua_parser.h"
const char *LuaEvents[_LargestEventID] = {
const char *LuaEvents[_LargestEventID] = {
"event_say",
"event_trade",
"event_death",
"event_spawn",
"event_attack",
"event_combat",
"event_environmental_damage",
"event_aggro",
"event_slay",
"event_npc_slay",
@@ -68,6 +67,7 @@ const char *LuaEvents[_LargestEventID] = {
"event_aggro_say",
"event_player_pickup",
"event_popup_response",
"event_environmental_damage",
"event_proximity_say",
"event_cast",
"event_cast_begin",
+2 -2
View File
@@ -449,11 +449,11 @@ void Merc::AddItemBonuses(const Item_Struct *item, StatBonuses* newbon) {
newbon->DSMitigation += item->DSMitigation;
}
if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true);
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);
}
if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true);
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0);
}
switch(item->BardType)
+37 -11
View File
@@ -170,6 +170,7 @@ Mob::Mob(const char* in_name,
findable = false;
trackable = true;
has_shieldequiped = false;
has_twohandbluntequiped = false;
has_numhits = false;
has_MGB = false;
has_ProjectIllusion = false;
@@ -300,6 +301,7 @@ Mob::Mob(const char* in_name,
focused = false;
_IsTempPet = false;
pet_owner_client = false;
pet_targetlock_id = 0;
attacked_count = 0;
mezzed = false;
@@ -959,10 +961,10 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
// Only Player Races Wear Armor
if (Mob::IsPlayerRace(race) || i > 6)
{
ns->spawn.equipment[i].material = GetEquipmentMaterial(i);
ns->spawn.equipment[i].elitematerial = IsEliteMaterialItem(i);
ns->spawn.equipment[i].heroforgemodel = GetHerosForgeModel(i);
ns->spawn.colors[i].color = GetEquipmentColor(i);
ns->spawn.equipment[i].Material = GetEquipmentMaterial(i);
ns->spawn.equipment[i].EliteMaterial = IsEliteMaterialItem(i);
ns->spawn.equipment[i].HeroForgeModel = GetHerosForgeModel(i);
ns->spawn.colors[i].Color = GetEquipmentColor(i);
}
}
@@ -1981,9 +1983,10 @@ void Mob::TempName(const char *newname)
strn0cpy(temp_name, GetCleanName(), 64);
}
// Remove Numbers before making name unique
EntityList::RemoveNumbers(temp_name);
// Make the new name unique and set it
strn0cpy(temp_name, entity_list.MakeNameUnique(temp_name), 64);
entity_list.MakeNameUnique(temp_name);
// Send the new name to all clients
EQApplicationPacket* outapp = new EQApplicationPacket(OP_MobRename, sizeof(MobRename_Struct));
@@ -2559,7 +2562,7 @@ void Mob::SendWearChange(uint8 material_slot)
wc->material = GetEquipmentMaterial(material_slot);
wc->elite_material = IsEliteMaterialItem(material_slot);
wc->hero_forge_model = GetHerosForgeModel(material_slot);
wc->color.color = GetEquipmentColor(material_slot);
wc->color.Color = GetEquipmentColor(material_slot);
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
@@ -2574,9 +2577,9 @@ void Mob::SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model, uin
wc->spawn_id = this->GetID();
wc->material = texture;
if (this->IsClient())
wc->color.color = GetEquipmentColor(slot);
wc->color.Color = GetEquipmentColor(slot);
else
wc->color.color = this->GetArmorTint(slot);
wc->color.Color = this->GetArmorTint(slot);
wc->wear_slot_id = slot;
wc->unknown06 = unknown06;
@@ -2604,7 +2607,7 @@ void Mob::SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uin
wc->spawn_id = this->GetID();
wc->material = GetEquipmentMaterial(material_slot);
wc->hero_forge_model = GetHerosForgeModel(material_slot);
wc->color.color = color;
wc->color.Color = color;
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
@@ -2621,7 +2624,7 @@ void Mob::WearChange(uint8 material_slot, uint16 texture, uint32 color, uint32 h
wc->spawn_id = this->GetID();
wc->material = texture;
wc->hero_forge_model = hero_forge_model;
wc->color.color = color;
wc->color.Color = color;
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
@@ -5362,3 +5365,26 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot)
return stat;
}
bool Mob::CanClassEquipItem(uint32 item_id)
{
const Item_Struct* itm = nullptr;
itm = database.GetItem(item_id);
if (!itm)
return false;
if(itm->Classes == 65535 )
return true;
if (GetClass() > 16)
return false;
int bitmask = 1;
bitmask = bitmask << (GetClass() - 1);
if(!(itm->Classes & bitmask))
return false;
else
return true;
}
+11 -3
View File
@@ -194,7 +194,7 @@ public:
bool IsBeneficialAllowed(Mob *target);
virtual int GetCasterLevel(uint16 spell_id);
void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0,
bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1,
uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1,
bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0);
void NegateSpellsBonuses(uint16 spell_id);
virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false);
@@ -308,13 +308,16 @@ public:
void SetTargetable(bool on);
bool IsTargetable() const { return m_targetable; }
bool HasShieldEquiped() const { return has_shieldequiped; }
inline void ShieldEquiped(bool val) { has_shieldequiped = val; }
inline void SetShieldEquiped(bool val) { has_shieldequiped = val; }
bool HasTwoHandBluntEquiped() const { return has_twohandbluntequiped; }
inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; }
virtual uint16 GetSkill(SkillUseTypes skill_num) const { return 0; }
virtual uint32 GetEquipment(uint8 material_slot) const { return(0); }
virtual int32 GetEquipmentMaterial(uint8 material_slot) const;
virtual int32 GetHerosForgeModel(uint8 material_slot) const;
virtual uint32 GetEquipmentColor(uint8 material_slot) const;
virtual uint32 IsEliteMaterialItem(uint8 material_slot) const;
bool CanClassEquipItem(uint32 item_id);
bool AffectedBySpellExcludingSlot(int slot, int effect);
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill) = 0;
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill,
@@ -621,7 +624,7 @@ public:
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
bool ImprovedTaunt();
bool TryRootFadeByDamage(int buffslot, Mob* attacker);
int16 GetSlowMitigation() const {return slow_mitigation;}
float GetSlowMitigation() const { return slow_mitigation; }
void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr);
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };
@@ -675,6 +678,9 @@ public:
bool IsFamiliar() const { return(typeofpet == petFamiliar); }
bool IsAnimation() const { return(typeofpet == petAnimation); }
bool IsCharmed() const { return(typeofpet == petCharmed); }
bool IsTargetLockPet() const { return(typeofpet == petTargetLock); }
inline uint32 GetPetTargetLockID() { return pet_targetlock_id; };
inline void SetPetTargetLockID(uint32 value) { pet_targetlock_id = value; };
void SetOwnerID(uint16 NewOwnerID);
inline uint16 GetOwnerID() const { return ownerid; }
inline virtual bool HasOwner() { if(GetOwnerID()==0){return false;} return( entity_list.GetMob(GetOwnerID()) != 0); }
@@ -1146,6 +1152,7 @@ protected:
uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids
bool offhand;
bool has_shieldequiped;
bool has_twohandbluntequiped;
bool has_numhits;
bool has_MGB;
bool has_ProjectIllusion;
@@ -1244,6 +1251,7 @@ protected:
bool _IsTempPet;
int16 count_TempPet;
bool pet_owner_client; //Flags regular and pets as belonging to a client
uint32 pet_targetlock_id;
EGNode *_egnode; //the EG node we are in
glm::vec3 m_TargetLocation;
+1 -1
View File
@@ -2559,7 +2559,7 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon)
for(int i=0; i < AIspellsEffects.size(); i++)
{
ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1,
ApplySpellsBonuses(0, 0, newbon, 0, 0, 0,-1,
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
}
+33
View File
@@ -2394,6 +2394,29 @@ void NPC::DoQuestPause(Mob *other) {
}
void NPC::ChangeLastName(const char* in_lastname)
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct));
GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer;
strcpy(gmn->name, GetName());
strcpy(gmn->gmname, GetName());
strcpy(gmn->lastname, in_lastname);
gmn->unknown[0]=1;
gmn->unknown[1]=1;
gmn->unknown[2]=1;
gmn->unknown[3]=1;
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
void NPC::ClearLastName()
{
std::string WT;
WT = '\0'; //Clear Last Name
ChangeLastName( WT.c_str());
}
void NPC::DepopSwarmPets()
{
if (GetSwarmInfo()) {
@@ -2419,4 +2442,14 @@ void NPC::DepopSwarmPets()
}
}
}
if (IsPet() && GetPetType() == petTargetLock && GetPetTargetLockID()){
Mob *targMob = entity_list.GetMob(GetPetTargetLockID());
if(!targMob || (targMob && targMob->IsCorpse())){
Kill();
return;
}
}
}
+4 -1
View File
@@ -253,7 +253,7 @@ public:
uint32 GetMaxDMG() const {return max_dmg;}
uint32 GetMinDMG() const {return min_dmg;}
int16 GetSlowMitigation() const {return slow_mitigation;}
float GetSlowMitigation() const { return slow_mitigation; }
float GetAttackSpeed() const {return attack_speed;}
uint8 GetAttackDelay() const {return attack_delay;}
bool IsAnimal() const { return(bodytype == BT_Animal); }
@@ -361,6 +361,9 @@ public:
const bool IsUnderwaterOnly() const { return NPCTypedata->underwater; }
const char* GetRawNPCTypeName() const { return NPCTypedata->name; }
void ChangeLastName(const char* in_lastname);
void ClearLastName();
bool GetDepop() { return p_depop; }
void NPCSlotTexture(uint8 slot, uint16 texture); // Sets new material values for slots
+2 -2
View File
@@ -342,8 +342,8 @@ XS(XS_Group_GetLeaderName)
if (items != 1)
Perl_croak(aTHX_ "Usage: Group::GetLeaderName(THIS)");
{
Group * THIS;
char * RETVAL;
Group * THIS;
const char * RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "Group")) {
+28
View File
@@ -8367,6 +8367,33 @@ XS(XS_Mob_ProcessSpecialAbilities)
XSRETURN_EMPTY;
}
XS(XS_Mob_CanClassEquipItem); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_CanClassEquipItem)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::CanClassEquipItem(THIS, item_id)");
{
Mob * THIS;
bool RETVAL;
uint32 item_id = (uint32)SvUV(ST(1));
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.");
RETVAL = THIS->CanClassEquipItem(item_id);
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
}
XSRETURN(1);
}
#ifdef __cplusplus
extern "C"
#endif
@@ -8675,6 +8702,7 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "SetSpecialAbilityParam"), XS_Mob_SetSpecialAbilityParam, file, "$$$$");
newXSproto(strcpy(buf, "ClearSpecialAbilities"), XS_Mob_ClearSpecialAbilities, file, "$");
newXSproto(strcpy(buf, "ProcessSpecialAbilities"), XS_Mob_ProcessSpecialAbilities, file, "$$");
newXSproto(strcpy(buf, "CanClassEquipItem"), XS_Mob_CanClassEquipItem, file, "$$");
XSRETURN_YES;
}
+92 -4
View File
@@ -795,6 +795,42 @@ XS(XS_NPC_IsOnHatelist)
XSRETURN(1);
}
XS(XS_NPC_RemoveFromHateList); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_RemoveFromHateList)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: NPC::RemoveFromHateList(THIS, ent)");
{
NPC * THIS;
Mob* ent;
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.");
if (sv_derived_from(ST(1), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(1)));
ent = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "ent is not of type Mob");
if(ent == nullptr)
Perl_croak(aTHX_ "ent is nullptr, avoiding crash.");
THIS->RemoveFromHateList(ent);
}
XSRETURN_EMPTY;
}
XS(XS_NPC_SetNPCFactionID); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_SetNPCFactionID)
{
@@ -2076,7 +2112,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)");
{
NPC * THIS;
int16 RETVAL;
float RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "NPC")) {
@@ -2089,7 +2125,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetSlowMitigation();
XSprePUSH; PUSHn((UV)RETVAL);
XSprePUSH; PUSHn((double)RETVAL);
}
XSRETURN(1);
}
@@ -2343,7 +2379,7 @@ XS(XS_NPC_AddRangedProc) {
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->AddDefensiveProc(spell_id,chance);
THIS->AddRangedProc(spell_id,chance);
}
XSRETURN_EMPTY;
}
@@ -2368,7 +2404,56 @@ XS(XS_NPC_AddDefensiveProc) {
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->AddProcToWeapon(spell_id, true, chance);
THIS->AddDefensiveProc(spell_id,chance);
}
XSRETURN_EMPTY;
}
XS(XS_NPC_ChangeLastName); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_ChangeLastName)
{
dXSARGS;
if (items < 1 || items > 2)
Perl_croak(aTHX_ "Usage: Mob::ChangeLastName(THIS, name)");
{
NPC * THIS;
char * name = nullptr;
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.");
if (items > 1) { name = (char *)SvPV_nolen(ST(1)); }
THIS->ChangeLastName(name);
}
XSRETURN_EMPTY;
}
XS(XS_NPC_ClearLastName); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_ClearLastName)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mob::ClearLastName(THIS)");
{
NPC * THIS;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
THIS->ClearLastName();
}
XSRETURN_EMPTY;
}
@@ -2420,6 +2505,7 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "GetPrimaryFaction"), XS_NPC_GetPrimaryFaction, file, "$");
newXSproto(strcpy(buf, "GetNPCHate"), XS_NPC_GetNPCHate, file, "$$");
newXSproto(strcpy(buf, "IsOnHatelist"), XS_NPC_IsOnHatelist, file, "$$");
newXSproto(strcpy(buf, "RemoveFromHateList"), XS_NPC_RemoveFromHateList, file, "$$");
newXSproto(strcpy(buf, "SetNPCFactionID"), XS_NPC_SetNPCFactionID, file, "$$");
newXSproto(strcpy(buf, "GetMaxDMG"), XS_NPC_GetMaxDMG, file, "$");
newXSproto(strcpy(buf, "GetMinDMG"), XS_NPC_GetMinDMG, file, "$");
@@ -2480,6 +2566,8 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$");
newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$");
newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$");
newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$");
newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$");
XSRETURN_YES;
}
+16 -2
View File
@@ -248,7 +248,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
}
//find the NPC data for the specified NPC type
const NPCType *base = database.GetNPCType(record.npc_type);
const NPCType *base = database.LoadNPCTypesData(record.npc_type);
if(base == nullptr) {
Message(13, "Unable to load NPC data for pet %s", pettype);
Log.Out(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type);
@@ -384,7 +384,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
monsterid = 567;
// give the summoned pet the attributes of the monster we found
const NPCType* monster = database.GetNPCType(monsterid);
const NPCType* monster = database.LoadNPCTypesData(monsterid);
if(monster) {
npc_type->race = monster->race;
npc_type->size = monster->size;
@@ -426,6 +426,20 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
entity_list.AddNPC(npc, true, true);
SetPetID(npc->GetID());
// We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet
if (record.petcontrol == petTargetLock)
{
Mob* target = GetTarget();
if (target){
npc->AddToHateList(target, 1);
npc->SetPetTargetLockID(target->GetID());
npc->SetSpecialAbility(IMMUNE_AGGRO, 1);
}
else
npc->Kill(); //On live casts spell 892 Unsummon (Kayen - Too limiting to use that for emu since pet can have more than 20k HP)
}
}
/* This is why the pets ghost - pets were being spawned too far away from its npc owner and some
into walls or objects (+10), this sometimes creates the "ghost" effect. I changed to +2 (as close as I
+1 -1
View File
@@ -483,7 +483,7 @@ QuestInterface *QuestParserCollection::GetQIByNPCQuest(uint32 npcid, std::string
}
//second look for /quests/zone/npcname.ext (precedence)
const NPCType *npc_type = database.GetNPCType(npcid);
const NPCType *npc_type = database.LoadNPCTypesData(npcid);
if(!npc_type) {
return nullptr;
}
+6 -6
View File
@@ -203,7 +203,7 @@ void QuestManager::write(const char *file, const char *str) {
Mob* QuestManager::spawn2(int npc_type, int grid, int unused, const glm::vec4& position) {
const NPCType* tmp = 0;
if (tmp = database.GetNPCType(npc_type))
if (tmp = database.LoadNPCTypesData(npc_type))
{
NPC* npc = new NPC(tmp, nullptr, position, FlyMode3);
npc->AddLootTable();
@@ -225,7 +225,7 @@ Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::v
}
const NPCType* tmp = 0;
if (tmp = database.GetNPCType(npc_type))
if (tmp = database.LoadNPCTypesData(npc_type))
{
NPC* npc = new NPC(tmp, nullptr, position, FlyMode3);
npc->AddLootTable();
@@ -275,7 +275,7 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
return nullptr;
}
const NPCType* tmp = database.GetNPCType(npcid);
const NPCType* tmp = database.LoadNPCTypesData(npcid);
if(!tmp)
{
return nullptr;
@@ -297,7 +297,7 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
}
}
database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0);
database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), 0);
found_spawn->SetCurrentNPCID(npcid);
auto position = glm::vec4(found_spawn->GetX(), found_spawn->GetY(), found_spawn->GetZ(), found_spawn->GetHeading());
@@ -1574,7 +1574,7 @@ void QuestManager::respawn(int npcTypeID, int grid) {
quests_running_.push(e);
const NPCType* npcType = nullptr;
if ((npcType = database.GetNPCType(npcTypeID)))
if ((npcType = database.LoadNPCTypesData(npcTypeID)))
{
owner = new NPC(npcType, nullptr, owner->GetPosition(), FlyMode3);
owner->CastToNPC()->AddLootTable();
@@ -2388,7 +2388,7 @@ void QuestManager::UpdateSpawnTimer(uint32 id, uint32 newTime)
{
bool found = false;
database.UpdateSpawn2Timeleft(id, 0, (newTime/1000));
database.UpdateRespawnTime(id, 0, (newTime/1000));
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
while (iterator.MoreElements())
+86 -24
View File
@@ -183,7 +183,7 @@ bool Spawn2::Process() {
}
//try to find our NPC type.
const NPCType* tmp = database.GetNPCType(npcid);
const NPCType* tmp = database.LoadNPCTypesData(npcid);
if (tmp == nullptr) {
Log.Out(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", spawn2_id, spawngroup_id_, npcid);
Reset(); //try again later
@@ -214,9 +214,6 @@ bool Spawn2::Process() {
if(IsDespawned)
return true;
if(spawn2_id)
database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0);
currentnpcid = npcid;
NPC* npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3);
@@ -348,7 +345,7 @@ void Spawn2::DeathReset(bool realdeath)
//if we have a valid spawn id
if(spawn2_id)
{
database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), (cur/1000));
database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), (cur/1000));
Log.Out(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset by death, repop in %d ms", spawn2_id, timer.GetRemainingTime());
//store it to database too
}
@@ -356,28 +353,95 @@ void Spawn2::DeathReset(bool realdeath)
bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spawn2_list, int16 version, uint32 repopdelay) {
std::unordered_map<uint32, uint32> spawn_times;
timeval tv;
gettimeofday(&tv, nullptr);
/* Bulk Load NPC Types Data into the cache */
database.LoadNPCTypesData(0, true);
std::string spawn_query = StringFormat(
"SELECT "
"respawn_times.id, "
"respawn_times.`start`, "
"respawn_times.duration "
"FROM "
"respawn_times "
"WHERE instance_id = %u",
zone->GetInstanceID()
);
auto results = QueryDatabase(spawn_query);
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 start_duration = atoi(row[1]) > 0 ? atoi(row[1]) : 0;
uint32 end_duration = atoi(row[2]) > 0 ? atoi(row[2]) : 0;
/* Our current time was expired */
if ((start_duration + end_duration) <= tv.tv_sec) {
spawn_times[atoi(row[0])] = 0;
}
/* We still have time left on this timer */
else {
spawn_times[atoi(row[0])] = ((start_duration + end_duration) - tv.tv_sec) * 1000;
}
}
const char *zone_name = database.GetZoneName(zoneid);
std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, "
"respawntime, variance, pathgrid, _condition, "
"cond_value, enabled, animation FROM spawn2 "
"WHERE zone = '%s' AND version = %u",
zone_name, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::string query = StringFormat(
"SELECT "
"id, "
"spawngroupID, "
"x, "
"y, "
"z, "
"heading, "
"respawntime, "
"variance, "
"pathgrid, "
"_condition, "
"cond_value, "
"enabled, "
"animation "
"FROM "
"spawn2 "
"WHERE zone = '%s' AND version = %u",
zone_name,
version
);
results = QueryDatabase(query);
if (!results.Success()) {
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
Spawn2* newSpawn = 0;
for (auto row = results.begin(); row != results.end(); ++row) {
bool perl_enabled = atoi(row[11]) == 1? true: false;
uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000);
newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]),
atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]),
atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12]));
uint32 spawn_time_left = 0;
Spawn2* new_spawn = 0;
bool perl_enabled = atoi(row[11]) == 1 ? true : false;
spawn2_list.Insert(newSpawn);
}
if (spawn_times.count(atoi(row[0])) != 0)
spawn_time_left = spawn_times[atoi(row[0])];
new_spawn = new Spawn2( //
atoi(row[0]), // uint32 in_spawn2_id
atoi(row[1]), // uint32 spawngroup_id
atof(row[2]), // float in_x
atof(row[3]), // float in_y
atof(row[4]), // float in_z
atof(row[5]), // float in_heading
atoi(row[6]), // uint32 respawn
atoi(row[7]), // uint32 variance
spawn_time_left, // uint32 timeleft
atoi(row[8]), // uint32 grid
atoi(row[9]), // uint16 in_cond_id
atoi(row[10]), // int16 in_min_value
perl_enabled, // bool in_enabled
(EmuAppearance)atoi(row[12]) // EmuAppearance anim
);
spawn2_list.Insert(new_spawn);
}
return true;
}
@@ -427,8 +491,6 @@ bool ZoneDatabase::CreateSpawn2(Client *client, uint32 spawngroup, const char* z
if (results.RowsAffected() != 1)
return false;
if(client)
return true;
}
+1 -2
View File
@@ -1186,7 +1186,6 @@ float Mob::GetRangeDistTargetSizeMod(Mob* other)
void NPC::RangedAttack(Mob* other)
{
if (!other)
return;
//make sure the attack and ranged timers are up
@@ -1306,7 +1305,7 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha
if (TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, skillInUse);
else
else if (TotalDmg < -4)
TotalDmg = -5;
if (TotalDmg > 0)
+6 -1
View File
@@ -5527,7 +5527,12 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) {
//Summon Spells that require reagents are typically imbue type spells, enchant metal, sacrifice and shouldn't be affected
//by reagent conservation for obvious reasons.
return realTotal + realTotal2 + realTotal3;
//Non-Live like feature to allow for an additive focus bonus to be applied from foci that are placed in worn slot. (No limit checks)
int16 worneffect_bonus = 0;
if (RuleB(Spells, UseAdditiveFocusFromWornSlot))
worneffect_bonus = itembonuses.FocusEffectsWorn[type];
return realTotal + realTotal2 + realTotal3 + worneffect_bonus;
}
int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) {
+1 -1
View File
@@ -3742,7 +3742,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
return false;
}
if (IsValidSpell(spells[spell_id].RecourseLink))
if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id)
SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
if (IsDetrimentalSpell(spell_id)) {
+10 -1
View File
@@ -2872,7 +2872,16 @@ int ClientTaskState::GetTaskActivityDoneCountFromTaskID(int TaskID, int Activity
break;
}
}
return ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount;
if (ActiveTaskIndex == -1)
return 0;
if (ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount){
return ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount;
}
else{
return 0;
}
}
int ClientTaskState::GetTaskStartTime(int index) {
+173 -80
View File
@@ -1098,7 +1098,15 @@ void Client::Trader_EndTrader() {
for(int i = 0; i < 80; i++) {
if(gis->Items[i] != 0) {
tdis->ItemID = gis->SerialNumber[i];
if (Customer->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ use Item IDs for now
tdis->ItemID = gis->Items[i];
}
else
{
tdis->ItemID = gis->SerialNumber[i];
}
Customer->QueuePacket(outapp);
}
@@ -1138,7 +1146,6 @@ void Client::Trader_EndTrader() {
QueuePacket(outapp);
safe_delete(outapp);
WithCustomer(0);
@@ -1221,6 +1228,29 @@ void Client::BulkSendTraderInventory(uint32 char_id) {
safe_delete(TraderItems);
}
uint32 Client::FindTraderItemSerialNumber(int32 ItemID) {
ItemInst* item = nullptr;
uint16 SlotID = 0;
for (int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; i++){
item = this->GetInv().GetItem(i);
if (item && item->GetItem()->ID == 17899){ //Traders Satchel
for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; x++) {
// we already have the parent bag and a contents iterator..why not just iterate the bag!??
SlotID = Inventory::CalcSlotId(i, x);
item = this->GetInv().GetItem(SlotID);
if (item) {
if (item->GetID() == ItemID)
return item->GetSerialNumber();
}
}
}
}
Log.Out(Logs::Detail, Logs::Trading, "Client::FindTraderItemSerialNumber Couldn't find item! Item ID %i", ItemID);
return 0;
}
ItemInst* Client::FindTraderItemBySerialNumber(int32 SerialNumber){
ItemInst* item = nullptr;
@@ -1288,9 +1318,9 @@ uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){
item = this->GetInv().GetItem(SlotID);
if(item && item->GetSerialNumber() == SerialNumber &&
(item->GetCharges() >= Quantity || (item->GetCharges() <= 0 && Quantity == 1))){
if (item && item->GetSerialNumber() == SerialNumber &&
(item->GetCharges() >= Quantity || (item->GetCharges() <= 0 && Quantity == 1)))
{
return SlotID;
}
}
@@ -1302,21 +1332,34 @@ uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){
return 0;
}
void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Customer,uint16 TraderSlot, int SerialNumber) {
void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Customer,uint16 TraderSlot, int32 SerialNumber, int32 itemid) {
if(!Customer)
return;
if(!Customer) return;
Log.Out(Logs::Detail, Logs::Trading, "NukeTraderItem(Slot %i, Charges %i, Quantity %i", Slot, Charges, Quantity);
if(Quantity < Charges) {
if(Quantity < Charges)
{
Customer->SendSingleTraderItem(this->CharacterID(), SerialNumber);
m_inv.DeleteItem(Slot, Quantity);
}
else {
else
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderDelItem,sizeof(TraderDelItem_Struct));
TraderDelItem_Struct* tdis = (TraderDelItem_Struct*)outapp->pBuffer;
tdis->Unknown000 = 0;
tdis->TraderID = Customer->GetID();
tdis->ItemID = SerialNumber;
if (Customer->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ use Item IDs for now
tdis->ItemID = itemid;
}
else
{
tdis->ItemID = SerialNumber;
}
tdis->Unknown012 = 0;
@@ -1324,7 +1367,6 @@ void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Cu
safe_delete(outapp);
m_inv.DeleteItem(Slot);
}
// This updates the trader. Removes it from his trading bags.
//
@@ -1375,49 +1417,61 @@ void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client*
int16 Charges=0;
uint16 SlotID = FindTraderItem(SerialNumber, Quantity);
if(SlotID > 0){
if(SlotID > 0) {
item = this->GetInv().GetItem(SlotID);
if(item) {
Charges = this->GetInv().GetItem(SlotID)->GetCharges();
Stackable = item->IsStackable();
if(!Stackable)
Quantity = (Charges > 0) ? Charges : 1;
Log.Out(Logs::Detail, Logs::Trading, "FindAndNuke %s, Charges %i, Quantity %i", item->GetItem()->Name, Charges, Quantity);
if (!item)
{
Log.Out(Logs::Detail, Logs::Trading, "Could not find Item: %i on Trader: %s", SerialNumber, Quantity, this->GetName());
return;
}
if(item && (Charges <= Quantity || (Charges <= 0 && Quantity==1) || !Stackable)){
Charges = this->GetInv().GetItem(SlotID)->GetCharges();
Stackable = item->IsStackable();
if (!Stackable)
Quantity = (Charges > 0) ? Charges : 1;
Log.Out(Logs::Detail, Logs::Trading, "FindAndNuke %s, Charges %i, Quantity %i", item->GetItem()->Name, Charges, Quantity);
if (Charges <= Quantity || (Charges <= 0 && Quantity==1) || !Stackable)
{
this->DeleteItemInInventory(SlotID, Quantity);
TraderCharges_Struct* GetSlot = database.LoadTraderItemWithCharges(this->CharacterID());
TraderCharges_Struct* TraderItems = database.LoadTraderItemWithCharges(this->CharacterID());
uint8 Count = 0;
bool TestSlot = true;
for(int y = 0;y < 80;y++){
for(int i = 0;i < 80;i++){
if(TestSlot && GetSlot->SerialNumber[y] == SerialNumber){
database.DeleteTraderItem(this->CharacterID(),y);
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, GetSlot->SerialNumber[y]);
if(TestSlot && TraderItems->SerialNumber[i] == SerialNumber)
{
database.DeleteTraderItem(this->CharacterID(),i);
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, TraderItems->SerialNumber[i], TraderItems->ItemID[i]);
TestSlot=false;
}
else if(GetSlot->ItemID[y] > 0)
else if (TraderItems->ItemID[i] > 0)
{
Count++;
}
}
if(Count == 0)
if (Count == 0)
{
Trader_EndTrader();
}
return;
}
else if(item) {
else
{
database.UpdateTraderItemCharges(this->CharacterID(), item->GetSerialNumber(), Charges-Quantity);
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, item->GetSerialNumber());
NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, item->GetSerialNumber(), item->GetID());
return;
@@ -1427,22 +1481,38 @@ void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client*
Quantity,this->GetName());
}
void Client::ReturnTraderReq(const EQApplicationPacket* app, int16 TraderItemCharges){
void Client::ReturnTraderReq(const EQApplicationPacket* app, int16 TraderItemCharges, uint32 itemid){
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderBuy, sizeof(TraderBuy_Struct));
EQApplicationPacket* outapp = nullptr;
if (GetClientVersion() >= ClientVersion::RoF)
{
outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderBuy_Struct));
}
else
{
outapp = new EQApplicationPacket(OP_TraderBuy, sizeof(TraderBuy_Struct));
}
TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer;
memcpy(outtbs, tbs, app->size);
outtbs->Price = (tbs->Price * static_cast<uint32>(TraderItemCharges));
if (GetClientVersion() >= ClientVersion::RoF)
{
// Convert Serial Number back to Item ID for RoF+
outtbs->ItemID = itemid;
}
else
{
// RoF+ requires individual price, but older clients require total price
outtbs->Price = (tbs->Price * static_cast<uint32>(TraderItemCharges));
}
outtbs->Quantity = TraderItemCharges;
// This should probably be trader ID, not customer ID as it is below.
outtbs->TraderID = this->GetID();
outtbs->AlreadySold = 0;
QueuePacket(outapp);
@@ -1479,9 +1549,7 @@ static void BazaarAuditTrail(const char *seller, const char *buyer, const char *
database.QueryDatabase(query);
}
void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicationPacket* app){
void Client::BuyTraderItem(TraderBuy_Struct* tbs, Client* Trader, const EQApplicationPacket* app){
if(!Trader) return;
@@ -1490,13 +1558,23 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
return;
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader,sizeof(TraderBuy_Struct));
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderBuy_Struct));
TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer;
outtbs->ItemID = tbs->ItemID;
const ItemInst* BuyItem = Trader->FindTraderItemBySerialNumber(tbs->ItemID);
const ItemInst* BuyItem = nullptr;
uint32 ItemID = 0;
if (GetClientVersion() >= ClientVersion::RoF)
{
// Convert Item ID to Serial Number for RoF+
ItemID = tbs->ItemID;
tbs->ItemID = Trader->FindTraderItemSerialNumber(tbs->ItemID);
}
BuyItem = Trader->FindTraderItemBySerialNumber(tbs->ItemID);
if(!BuyItem) {
Log.Out(Logs::Detail, Logs::Trading, "Unable to find item on trader.");
@@ -1509,15 +1587,15 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
BuyItem->GetItem()->Name, BuyItem->IsStackable(), tbs->Quantity, BuyItem->GetCharges());
// If the item is not stackable, then we can only be buying one of them.
if(!BuyItem->IsStackable())
outtbs->Quantity = tbs->Quantity;
outtbs->Quantity = 1; // normally you can't send more than 1 here
else {
// Stackable items, arrows, diamonds, etc
int ItemCharges = BuyItem->GetCharges();
int32 ItemCharges = BuyItem->GetCharges();
// ItemCharges for stackables should not be <= 0
if(ItemCharges <= 0)
outtbs->Quantity = 1;
// If the purchaser requested more than is in the stack, just sell them how many are actually in the stack.
else if(ItemCharges < (int16)tbs->Quantity)
else if(static_cast<uint32>(ItemCharges) < tbs->Quantity)
outtbs->Quantity = ItemCharges;
else
outtbs->Quantity = tbs->Quantity;
@@ -1546,12 +1624,10 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
return;
}
ReturnTraderReq(app, outtbs->Quantity);
ReturnTraderReq(app, outtbs->Quantity, ItemID);
outtbs->TraderID = this->GetID();
outtbs->Action = BazaarBuyItem;
strn0cpy(outtbs->ItemName, BuyItem->GetItem()->Name, 64);
int TraderSlot = 0;
@@ -1561,55 +1637,50 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
else
SendTraderItem(BuyItem->GetItem()->ID, BuyItem->GetCharges());
EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_MoneyUpdate,sizeof(MoneyUpdate_Struct));
MoneyUpdate_Struct* mus= (MoneyUpdate_Struct*)outapp2->pBuffer;
// This cannot overflow assuming MAX_TRANSACTION_VALUE, checked above, is the default of 2000000000
uint32 TotalCost = tbs->Price * outtbs->Quantity;
outtbs->Price = TotalCost;
if (Trader->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ uses individual item price where older clients use total price
outtbs->Price = tbs->Price;
}
else
{
outtbs->Price = TotalCost;
}
this->TakeMoneyFromPP(TotalCost);
mus->platinum = TotalCost / 1000;
Log.Out(Logs::Detail, Logs::Trading, "Customer Paid: %d in Copper", TotalCost);
TotalCost -= (mus->platinum * 1000);
uint32 platinum = TotalCost / 1000;
TotalCost -= (platinum * 1000);
uint32 gold = TotalCost / 100;
TotalCost -= (gold * 100);
uint32 silver = TotalCost / 10;
TotalCost -= (silver * 10);
uint32 copper = TotalCost;
mus->gold = TotalCost / 100;
Trader->AddMoneyToPP(copper, silver, gold, platinum, true);
TotalCost -= (mus->gold * 100);
mus->silver = TotalCost / 10;
TotalCost -= (mus->silver * 10);
mus->copper = TotalCost;
Trader->AddMoneyToPP(mus->copper,mus->silver,mus->gold,mus->platinum,false);
mus->platinum = Trader->GetPlatinum();
mus->gold = Trader->GetGold();
mus->silver = Trader->GetSilver();
mus->copper = Trader->GetCopper();
Log.Out(Logs::Detail, Logs::Trading, "Trader Received: %d Platinum, %d Gold, %d Silver, %d Copper", platinum, gold, silver, copper);
TraderSlot = Trader->FindTraderItem(tbs->ItemID, outtbs->Quantity);
Trader->QueuePacket(outapp2);
if(RuleB(Bazaar, AuditTrail))
BazaarAuditTrail(Trader->GetName(), GetName(), BuyItem->GetItem()->Name, outtbs->Quantity, outtbs->Price, 0);
Trader->FindAndNukeTraderItem(tbs->ItemID, outtbs->Quantity, this, 0);
if (ItemID > 0 && Trader->GetClientVersion() >= ClientVersion::RoF)
{
// Convert Serial Number back to ItemID for RoF+
outtbs->ItemID = ItemID;
}
Trader->QueuePacket(outapp);
safe_delete(outapp);
safe_delete(outapp2);
}
void Client::SendBazaarWelcome()
@@ -1619,7 +1690,15 @@ void Client::SendBazaarWelcome()
if (results.Success() && results.RowCount() == 1){
auto row = results.begin();
EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct));
EQApplicationPacket* outapp = nullptr;
if (GetClientVersion() >= ClientVersion::RoF)
{
outapp = new EQApplicationPacket(OP_TraderShop, sizeof(BazaarWelcome_Struct));
}
else
{
outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct));
}
memset(outapp->pBuffer,0,outapp->size);
@@ -1630,6 +1709,11 @@ void Client::SendBazaarWelcome()
bws->Traders = atoi(row[0]);
bws->Items = atoi(row[1]);
if (GetClientVersion() >= ClientVersion::RoF)
{
bws->Unknown012 = GetID();
}
QueuePacket(outapp);
safe_delete(outapp);
@@ -2000,6 +2084,15 @@ static void UpdateTraderCustomerPriceChanged(uint32 CustomerID, TraderCharges_St
for(int i = 0; i < 80; i++) {
if(gis->ItemID[i] == ItemID) {
if (Customer->GetClientVersion() >= ClientVersion::RoF)
{
// RoF+ use Item IDs for now
tdis->ItemID = gis->ItemID[i];
}
else
{
tdis->ItemID = gis->SerialNumber[i];
}
tdis->ItemID = gis->SerialNumber[i];
Log.Out(Logs::Detail, Logs::Trading, "Telling customer to remove item %i with %i charges and S/N %i",
ItemID, Charges, gis->SerialNumber[i]);
+3 -3
View File
@@ -142,7 +142,7 @@ void Trap::Trigger(Mob* trigger)
for (i = 0; i < effectvalue2; i++)
{
if ((tmp = database.GetNPCType(effectvalue)))
if ((tmp = database.LoadNPCTypesData(effectvalue)))
{
auto randomOffset = glm::vec4(zone->random.Int(-5, 5),zone->random.Int(-5, 5),zone->random.Int(-5, 5), zone->random.Int(0, 249));
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
@@ -165,7 +165,7 @@ void Trap::Trigger(Mob* trigger)
for (i = 0; i < effectvalue2; i++)
{
if ((tmp = database.GetNPCType(effectvalue)))
if ((tmp = database.LoadNPCTypesData(effectvalue)))
{
auto randomOffset = glm::vec4(zone->random.Int(-2, 2), zone->random.Int(-2, 2), zone->random.Int(-2, 2), zone->random.Int(0, 249));
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
@@ -294,7 +294,7 @@ void Trap::CreateHiddenTrigger()
if(hiddenTrigger)
return;
const NPCType *base_type = database.GetNPCType(500);
const NPCType *base_type = database.LoadNPCTypesData(500);
NPCType *make_npc = new NPCType;
memcpy(make_npc, base_type, sizeof(NPCType));
make_npc->max_hp = 100000;
+8 -6
View File
@@ -735,6 +735,7 @@ void Zone::LoadZoneDoors(const char* zone, int16 version)
for(r = 0; r < count; r++, d++) {
Doors* newdoor = new Doors(d);
entity_list.AddDoor(newdoor);
Log.Out(Logs::Detail, Logs::Doors, "Door Add to Entity List, index: %u db id: %u, door_id %u", r, dlist[r].db_id, dlist[r].door_id);
}
delete[] dlist;
}
@@ -931,6 +932,9 @@ bool Zone::Init(bool iStaticZone) {
Log.Out(Logs::General, Logs::Error, "Loading World Objects failed. continuing.");
}
Log.Out(Logs::General, Logs::Status, "Flushing old respawn timers...");
database.QueryDatabase("DELETE FROM `respawn_times` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())");
//load up the zone's doors (prints inside)
zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion());
zone->LoadBlockedSpells(zone->GetZoneID());
@@ -1423,14 +1427,12 @@ bool Zone::Depop(bool StartSpawnTimer) {
std::map<uint32,NPCType *>::iterator itr;
entity_list.Depop(StartSpawnTimer);
#ifdef DEPOP_INVALIDATES_NPC_TYPES_CACHE
// Refresh npctable, getting current info from database.
while(npctable.size()) {
itr=npctable.begin();
/* Refresh npctable (cache), getting current info from database. */
while(npctable.size()) {
itr = npctable.begin();
delete itr->second;
npctable.erase(itr);
}
#endif
return true;
}
@@ -2162,7 +2164,7 @@ void Zone::DoAdventureActions()
{
if(ds->assa_count >= RuleI(Adventure, NumberKillsForBossSpawn))
{
const NPCType* tmp = database.GetNPCType(ds->data_id);
const NPCType* tmp = database.LoadNPCTypesData(ds->data_id);
if(tmp)
{
NPC* npc = new NPC(tmp, nullptr, glm::vec4(ds->assa_x, ds->assa_y, ds->assa_z, ds->assa_h), FlyMode3);
+20 -11
View File
@@ -23,6 +23,7 @@
#include "../common/rulesys.h"
#include "../common/types.h"
#include "../common/random.h"
#include "../common/string_util.h"
#include "qglobals.h"
#include "spawn2.h"
#include "spawngroup.h"
@@ -67,16 +68,6 @@ struct item_tick_struct {
std::string qglobal;
};
// static uint32 gmsay_log_message_colors[EQEmuLogSys::MaxLogID] = {
// 15, // "Status", - Yellow
// 15, // "Normal", - Yellow
// 3, // "Error", - Red
// 14, // "Debug", - Light Green
// 4, // "Quest",
// 5, // "Command",
// 3 // "Crash"
// };
class Client;
class Map;
class Mob;
@@ -266,7 +257,25 @@ public:
// random object that provides random values for the zone
EQEmu::Random random;
static void GMSayHookCallBackProcess(uint16 log_category, const std::string& message){ entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message.c_str()); }
static void GMSayHookCallBackProcess(uint16 log_category, std::string message){
/* Cut messages down to 4000 max to prevent client crash */
if (!message.empty())
message = message.substr(0, 4000);
/* Replace Occurrences of % or MessageStatus will crash */
find_replace(message, std::string("%"), std::string("."));
if (message.find("\n") != std::string::npos){
auto message_split = SplitString(message, '\n');
entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message_split[0].c_str());
for (size_t iter = 1; iter < message_split.size(); ++iter) {
entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "--- %s", message_split[iter].c_str());
}
}
else{
entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message.c_str());
}
}
//MODDING HOOKS
void mod_init();
+456 -304
View File
@@ -97,118 +97,178 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct
*map_filename = new char[100];
zone_data->zone_id = zoneid;
std::string query = StringFormat("SELECT ztype, fog_red, fog_green, fog_blue, fog_minclip, fog_maxclip, " // 5
"fog_red2, fog_green2, fog_blue2, fog_minclip2, fog_maxclip2, " // 5
"fog_red3, fog_green3, fog_blue3, fog_minclip3, fog_maxclip3, " // 5
"fog_red4, fog_green4, fog_blue4, fog_minclip4, fog_maxclip4, " // 5
"fog_density, sky, zone_exp_multiplier, safe_x, safe_y, safe_z, underworld, " // 7
"minclip, maxclip, time_type, canbind, cancombat, canlevitate, " // 6
"castoutdoor, hotzone, ruleset, suspendbuffs, map_file_name, short_name, " // 6
"rain_chance1, rain_chance2, rain_chance3, rain_chance4, " // 4
"rain_duration1, rain_duration2, rain_duration3, rain_duration4, " // 4
"snow_chance1, snow_chance2, snow_chance3, snow_chance4, " // 4
"snow_duration1, snow_duration2, snow_duration3, snow_duration4 " // 4
"FROM zone WHERE zoneidnumber = %i AND version = %i", zoneid, instance_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
strcpy(*map_filename, "default");
std::string query = StringFormat(
"SELECT "
"ztype, " // 0
"fog_red, " // 1
"fog_green, " // 2
"fog_blue, " // 3
"fog_minclip, " // 4
"fog_maxclip, " // 5
"fog_red2, " // 6
"fog_green2, " // 7
"fog_blue2, " // 8
"fog_minclip2, " // 9
"fog_maxclip2, " // 10
"fog_red3, " // 11
"fog_green3, " // 12
"fog_blue3, " // 13
"fog_minclip3, " // 14
"fog_maxclip3, " // 15
"fog_red4, " // 16
"fog_green4, " // 17
"fog_blue4, " // 18
"fog_minclip4, " // 19
"fog_maxclip4, " // 20
"fog_density, " // 21
"sky, " // 22
"zone_exp_multiplier, " // 23
"safe_x, " // 24
"safe_y, " // 25
"safe_z, " // 26
"underworld, " // 27
"minclip, " // 28
"maxclip, " // 29
"time_type, " // 30
"canbind, " // 31
"cancombat, " // 32
"canlevitate, " // 33
"castoutdoor, " // 34
"hotzone, " // 35
"ruleset, " // 36
"suspendbuffs, " // 37
"map_file_name, " // 38
"short_name, " // 39
"rain_chance1, " // 40
"rain_chance2, " // 41
"rain_chance3, " // 42
"rain_chance4, " // 43
"rain_duration1, " // 44
"rain_duration2, " // 45
"rain_duration3, " // 46
"rain_duration4, " // 47
"snow_chance1, " // 48
"snow_chance2, " // 49
"snow_chance3, " // 50
"snow_chance4, " // 51
"snow_duration1, " // 52
"snow_duration2, " // 53
"snow_duration3, " // 54
"snow_duration4, " // 55
"gravity " // 56
"FROM zone WHERE zoneidnumber = %i AND version = %i",
zoneid, instance_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
strcpy(*map_filename, "default");
return false;
}
}
if (results.RowCount() == 0) {
strcpy(*map_filename, "default");
return false;
}
strcpy(*map_filename, "default");
return false;
}
auto row = results.begin();
auto row = results.begin();
memset(zone_data, 0, sizeof(NewZone_Struct));
zone_data->ztype = atoi(row[0]);
zone_type = zone_data->ztype;
memset(zone_data, 0, sizeof(NewZone_Struct));
zone_data->ztype = atoi(row[0]);
zone_type = zone_data->ztype;
int index;
for(index = 0; index < 4; index++) {
zone_data->fog_red[index]=atoi(row[1 + index * 5]);
zone_data->fog_green[index]=atoi(row[2 + index * 5]);
zone_data->fog_blue[index]=atoi(row[3 + index * 5]);
zone_data->fog_minclip[index]=atof(row[4 + index * 5]);
zone_data->fog_maxclip[index]=atof(row[5 + index * 5]);
}
int index;
for (index = 0; index < 4; index++) {
zone_data->fog_red[index] = atoi(row[1 + index * 5]);
zone_data->fog_green[index] = atoi(row[2 + index * 5]);
zone_data->fog_blue[index] = atoi(row[3 + index * 5]);
zone_data->fog_minclip[index] = atof(row[4 + index * 5]);
zone_data->fog_maxclip[index] = atof(row[5 + index * 5]);
}
zone_data->fog_density = atof(row[21]);
zone_data->sky=atoi(row[22]);
zone_data->zone_exp_multiplier=atof(row[23]);
zone_data->safe_x=atof(row[24]);
zone_data->safe_y=atof(row[25]);
zone_data->safe_z=atof(row[26]);
zone_data->underworld=atof(row[27]);
zone_data->minclip=atof(row[28]);
zone_data->maxclip=atof(row[29]);
zone_data->time_type=atoi(row[30]);
zone_data->fog_density = atof(row[21]);
zone_data->sky = atoi(row[22]);
zone_data->zone_exp_multiplier = atof(row[23]);
zone_data->safe_x = atof(row[24]);
zone_data->safe_y = atof(row[25]);
zone_data->safe_z = atof(row[26]);
zone_data->underworld = atof(row[27]);
zone_data->minclip = atof(row[28]);
zone_data->maxclip = atof(row[29]);
zone_data->time_type = atoi(row[30]);
//not in the DB yet:
zone_data->gravity = 0.4;
allow_mercs = true;
//not in the DB yet:
zone_data->gravity = atof(row[56]);
Log.Out(Logs::General, Logs::Debug, "Zone Gravity is %f", zone_data->gravity);
allow_mercs = true;
int bindable = 0;
bindable = atoi(row[31]);
int bindable = 0;
bindable = atoi(row[31]);
can_bind = bindable == 0? false: true;
is_city = bindable == 2? true: false;
can_combat = atoi(row[32]) == 0? false: true;
can_levitate = atoi(row[33]) == 0? false: true;
can_castoutdoor = atoi(row[34]) == 0? false: true;
is_hotzone = atoi(row[35]) == 0? false: true;
can_bind = bindable == 0 ? false : true;
is_city = bindable == 2 ? true : false;
can_combat = atoi(row[32]) == 0 ? false : true;
can_levitate = atoi(row[33]) == 0 ? false : true;
can_castoutdoor = atoi(row[34]) == 0 ? false : true;
is_hotzone = atoi(row[35]) == 0 ? false : true;
ruleset = atoi(row[36]);
zone_data->SuspendBuffs = atoi(row[37]);
ruleset = atoi(row[36]);
zone_data->SuspendBuffs = atoi(row[37]);
char *file = row[38];
if(file)
strcpy(*map_filename, file);
else
strcpy(*map_filename, row[39]);
char *file = row[38];
if (file)
strcpy(*map_filename, file);
else
strcpy(*map_filename, row[39]);
for(index = 0; index < 4; index++)
zone_data->rain_chance[index]=atoi(row[40 + index]);
for (index = 0; index < 4; index++)
zone_data->rain_chance[index] = atoi(row[40 + index]);
for(index = 0; index < 4; index++)
zone_data->rain_duration[index]=atoi(row[44 + index]);
for (index = 0; index < 4; index++)
zone_data->rain_duration[index] = atoi(row[44 + index]);
for(index = 0; index < 4; index++)
zone_data->snow_chance[index]=atoi(row[48 + index]);
for (index = 0; index < 4; index++)
zone_data->snow_chance[index] = atoi(row[48 + index]);
for(index = 0; index < 4; index++)
zone_data->snow_duration[index]=atof(row[52 + index]);
for (index = 0; index < 4; index++)
zone_data->snow_duration[index] = atof(row[52 + index]);
return true;
}
//updates or clears the respawn time in the database for the current spawn id
void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 timeleft)
void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint32 time_left)
{
timeval tv;
gettimeofday(&tv, nullptr);
uint32 cur = tv.tv_sec;
uint32 current_time = tv.tv_sec;
//if we pass timeleft as 0 that means we clear from respawn time
//otherwise we update with a REPLACE INTO
if(timeleft == 0) {
std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu "
"AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id);
auto results = QueryDatabase(query);
if (!results.Success())
/* If we pass timeleft as 0 that means we clear from respawn time
otherwise we update with a REPLACE INTO
*/
if(time_left == 0) {
std::string query = StringFormat("DELETE FROM `respawn_times` WHERE `id` = %u AND `instance_id` = %u", spawn2_id, instance_id);
QueryDatabase(query);
return;
}
std::string query = StringFormat("REPLACE INTO respawn_times (id, start, duration, instance_id) "
"VALUES (%lu, %lu, %lu, %lu)",
(unsigned long)id, (unsigned long)cur,
(unsigned long)timeleft, (unsigned long)instance_id);
auto results = QueryDatabase(query);
if (!results.Success())
std::string query = StringFormat(
"REPLACE INTO `respawn_times` "
"(id, "
"start, "
"duration, "
"instance_id) "
"VALUES "
"(%u, "
"%u, "
"%u, "
"%u)",
spawn2_id,
current_time,
time_left,
instance_id
);
QueryDatabase(query);
return;
}
@@ -1107,20 +1167,25 @@ bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile
for (auto row = results.begin(); row != results.end(); ++row) {
r = 0;
i = atoi(row[r]); /* Slot */ r++;
pp->item_tint[i].rgb.blue = atoi(row[r]); r++;
pp->item_tint[i].rgb.green = atoi(row[r]); r++;
pp->item_tint[i].rgb.red = atoi(row[r]); r++;
pp->item_tint[i].rgb.use_tint = atoi(row[r]);
pp->item_tint[i].RGB.Blue = atoi(row[r]); r++;
pp->item_tint[i].RGB.Green = atoi(row[r]); r++;
pp->item_tint[i].RGB.Red = atoi(row[r]); r++;
pp->item_tint[i].RGB.UseTint = atoi(row[r]);
}
return true;
}
bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp){
std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT 16", character_id);
bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp)
{
std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT %u",
character_id, EmuConstants::BANDOLIERS_SIZE);
auto results = database.QueryDatabase(query); int i = 0; int r = 0; int si = 0;
for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){
for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){
pp->bandoliers[i].items[si].icon = 0;
for (i = 0; i < EmuConstants::BANDOLIERS_SIZE; i++) {
pp->bandoliers[i].Name[0] = '\0';
for (int si = 0; si < EmuConstants::BANDOLIER_ITEM_COUNT; si++) {
pp->bandoliers[i].Items[si].ID = 0;
pp->bandoliers[i].Items[si].Icon = 0;
pp->bandoliers[i].Items[si].Name[0] = '\0';
}
}
@@ -1128,10 +1193,21 @@ bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Str
r = 0;
i = atoi(row[r]); /* Bandolier ID */ r++;
si = atoi(row[r]); /* Bandolier Slot */ r++;
pp->bandoliers[i].items[si].item_id = atoi(row[r]); r++;
pp->bandoliers[i].items[si].icon = atoi(row[r]); r++;
strcpy(pp->bandoliers[i].name, row[r]); r++;
si++;
const Item_Struct* item_data = database.GetItem(atoi(row[r]));
if (item_data) {
pp->bandoliers[i].Items[si].ID = item_data->ID; r++;
pp->bandoliers[i].Items[si].Icon = atoi(row[r]); r++; // Must use db value in case an Ornamentation is assigned
strncpy(pp->bandoliers[i].Items[si].Name, item_data->Name, 64);
}
else {
pp->bandoliers[i].Items[si].ID = 0; r++;
pp->bandoliers[i].Items[si].Icon = 0; r++;
pp->bandoliers[i].Items[si].Name[0] = '\0';
}
strcpy(pp->bandoliers[i].Name, row[r]); r++;
si++; // What is this for!?
}
return true;
}
@@ -1155,26 +1231,27 @@ bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struc
return true;
}
bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp){
std::string query = StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT 4", character_id);
auto results = database.QueryDatabase(query); int i = 0;
for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){
pp->potionbelt.items[i].icon = 0;
pp->potionbelt.items[i].item_id = 0;
strncpy(pp->potionbelt.items[i].item_name, "\0", 1);
bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct *pp)
{
std::string query =
StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT %u",
character_id, EmuConstants::POTION_BELT_ITEM_COUNT);
auto results = database.QueryDatabase(query);
int i = 0;
for (i = 0; i < EmuConstants::POTION_BELT_ITEM_COUNT; i++) {
pp->potionbelt.Items[i].Icon = 0;
pp->potionbelt.Items[i].ID = 0;
pp->potionbelt.Items[i].Name[0] = '\0';
}
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]); /* Potion belt slot number */
uint32 item_id = atoi(row[1]);
const Item_Struct *item = database.GetItem(item_id);
if(!item)
i = atoi(row[0]);
const Item_Struct *item_data = database.GetItem(atoi(row[1]));
if (!item_data)
continue;
pp->potionbelt.items[i].item_id = item_id;
pp->potionbelt.items[i].icon = atoi(row[2]);
strncpy(pp->potionbelt.items[i].item_name, item->Name, 64);
pp->potionbelt.Items[i].ID = item_data->ID;
pp->potionbelt.Items[i].Icon = atoi(row[2]);
strncpy(pp->potionbelt.Items[i].Name, item_data->Name, 64);
}
return true;
@@ -1268,7 +1345,8 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc
return true;
}
bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name){
bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name)
{
char bandolier_name_esc[64];
DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name));
std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc);
@@ -1277,7 +1355,8 @@ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_i
return true;
}
bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon) {
bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon)
{
std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon);
auto results = QueryDatabase(query);
return true;
@@ -1711,166 +1790,240 @@ bool ZoneDatabase::NoRentExpired(const char* name){
return (seconds>1800);
}
/* Searches npctable for matching id, and returns the item if found,
* or nullptr otherwise. If id passed is 0, loads all npc_types for
* the current zone, returning the last item added.
*/
const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
const NPCType *npc=nullptr;
const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load /*= false*/)
{
const NPCType *npc = nullptr;
// If NPC is already in tree, return it.
auto itr = zone->npctable.find(id);
/* If there is a cached NPC entry, load it */
auto itr = zone->npctable.find(npc_type_id);
if(itr != zone->npctable.end())
return itr->second;
// Otherwise, get NPCs from database.
std::string where_condition = "";
if (bulk_load){
Log.Out(Logs::General, Logs::Debug, "Performing bulk NPC Types load");
where_condition = StringFormat(
"INNER JOIN spawnentry ON npc_types.id = spawnentry.npcID "
"INNER JOIN spawn2 ON spawnentry.spawngroupID = spawn2.spawngroupID "
"WHERE spawn2.zone = '%s' and spawn2.version = %u GROUP BY npc_types.id", zone->GetShortName(), zone->GetInstanceVersion());
}
else{
where_condition = StringFormat("WHERE id = %u", npc_type_id);
}
// If id is 0, load all npc_types for the current zone,
// according to spawn2.
std::string query = StringFormat("SELECT npc_types.id, npc_types.name, npc_types.level, npc_types.race, "
"npc_types.class, npc_types.hp, npc_types.mana, npc_types.gender, "
"npc_types.texture, npc_types.helmtexture, npc_types.herosforgemodel, npc_types.size, "
"npc_types.loottable_id, npc_types.merchant_id, npc_types.alt_currency_id, "
"npc_types.adventure_template_id, npc_types.trap_template, npc_types.attack_speed, "
"npc_types.STR, npc_types.STA, npc_types.DEX, npc_types.AGI, npc_types._INT, "
"npc_types.WIS, npc_types.CHA, npc_types.MR, npc_types.CR, npc_types.DR, "
"npc_types.FR, npc_types.PR, npc_types.Corrup, npc_types.PhR, "
"npc_types.mindmg, npc_types.maxdmg, npc_types.attack_count, npc_types.special_abilities, "
"npc_types.npc_spells_id, npc_types.npc_spells_effects_id, npc_types.d_melee_texture1, "
"npc_types.d_melee_texture2, npc_types.ammo_idfile, npc_types.prim_melee_type, "
"npc_types.sec_melee_type, npc_types.ranged_type, npc_types.runspeed, npc_types.findable, "
"npc_types.trackable, npc_types.hp_regen_rate, npc_types.mana_regen_rate, "
"npc_types.aggroradius, npc_types.assistradius, npc_types.bodytype, npc_types.npc_faction_id, "
"npc_types.face, npc_types.luclin_hairstyle, npc_types.luclin_haircolor, "
"npc_types.luclin_eyecolor, npc_types.luclin_eyecolor2, npc_types.luclin_beardcolor,"
"npc_types.luclin_beard, npc_types.drakkin_heritage, npc_types.drakkin_tattoo, "
"npc_types.drakkin_details, npc_types.armortint_id, "
"npc_types.armortint_red, npc_types.armortint_green, npc_types.armortint_blue, "
"npc_types.see_invis, npc_types.see_invis_undead, npc_types.lastname, "
"npc_types.qglobal, npc_types.AC, npc_types.npc_aggro, npc_types.spawn_limit, "
"npc_types.see_hide, npc_types.see_improved_hide, npc_types.ATK, npc_types.Accuracy, "
"npc_types.Avoidance, npc_types.slow_mitigation, npc_types.maxlevel, npc_types.scalerate, "
"npc_types.private_corpse, npc_types.unique_spawn_by_name, npc_types.underwater, "
"npc_types.emoteid, npc_types.spellscale, npc_types.healscale, npc_types.no_target_hotkey, "
"npc_types.raid_target, npc_types.attack_delay, npc_types.light FROM npc_types WHERE id = %d", id);
std::string query = StringFormat("SELECT "
"npc_types.id, "
"npc_types.name, "
"npc_types.level, "
"npc_types.race, "
"npc_types.class, "
"npc_types.hp, "
"npc_types.mana, "
"npc_types.gender, "
"npc_types.texture, "
"npc_types.helmtexture, "
"npc_types.herosforgemodel, "
"npc_types.size, "
"npc_types.loottable_id, "
"npc_types.merchant_id, "
"npc_types.alt_currency_id, "
"npc_types.adventure_template_id, "
"npc_types.trap_template, "
"npc_types.attack_speed, "
"npc_types.STR, "
"npc_types.STA, "
"npc_types.DEX, "
"npc_types.AGI, "
"npc_types._INT, "
"npc_types.WIS, "
"npc_types.CHA, "
"npc_types.MR, "
"npc_types.CR, "
"npc_types.DR, "
"npc_types.FR, "
"npc_types.PR, "
"npc_types.Corrup, "
"npc_types.PhR, "
"npc_types.mindmg, "
"npc_types.maxdmg, "
"npc_types.attack_count, "
"npc_types.special_abilities, "
"npc_types.npc_spells_id, "
"npc_types.npc_spells_effects_id, "
"npc_types.d_melee_texture1, "
"npc_types.d_melee_texture2, "
"npc_types.ammo_idfile, "
"npc_types.prim_melee_type, "
"npc_types.sec_melee_type, "
"npc_types.ranged_type, "
"npc_types.runspeed, "
"npc_types.findable, "
"npc_types.trackable, "
"npc_types.hp_regen_rate, "
"npc_types.mana_regen_rate, "
"npc_types.aggroradius, "
"npc_types.assistradius, "
"npc_types.bodytype, "
"npc_types.npc_faction_id, "
"npc_types.face, "
"npc_types.luclin_hairstyle, "
"npc_types.luclin_haircolor, "
"npc_types.luclin_eyecolor, "
"npc_types.luclin_eyecolor2, "
"npc_types.luclin_beardcolor, "
"npc_types.luclin_beard, "
"npc_types.drakkin_heritage, "
"npc_types.drakkin_tattoo, "
"npc_types.drakkin_details, "
"npc_types.armortint_id, "
"npc_types.armortint_red, "
"npc_types.armortint_green, "
"npc_types.armortint_blue, "
"npc_types.see_invis, "
"npc_types.see_invis_undead, "
"npc_types.lastname, "
"npc_types.qglobal, "
"npc_types.AC, "
"npc_types.npc_aggro, "
"npc_types.spawn_limit, "
"npc_types.see_hide, "
"npc_types.see_improved_hide, "
"npc_types.ATK, "
"npc_types.Accuracy, "
"npc_types.Avoidance, "
"npc_types.slow_mitigation, "
"npc_types.maxlevel, "
"npc_types.scalerate, "
"npc_types.private_corpse, "
"npc_types.unique_spawn_by_name, "
"npc_types.underwater, "
"npc_types.emoteid, "
"npc_types.spellscale, "
"npc_types.healscale, "
"npc_types.no_target_hotkey, "
"npc_types.raid_target, "
"npc_types.attack_delay, "
"npc_types.light "
"FROM npc_types %s",
where_condition.c_str()
);
auto results = QueryDatabase(query);
if (!results.Success()) {
return nullptr;
}
for (auto row = results.begin(); row != results.end(); ++row) {
NPCType *tmpNPCType;
tmpNPCType = new NPCType;
memset (tmpNPCType, 0, sizeof *tmpNPCType);
NPCType *temp_npctype_data;
temp_npctype_data = new NPCType;
memset (temp_npctype_data, 0, sizeof *temp_npctype_data);
tmpNPCType->npc_id = atoi(row[0]);
temp_npctype_data->npc_id = atoi(row[0]);
strn0cpy(tmpNPCType->name, row[1], 50);
strn0cpy(temp_npctype_data->name, row[1], 50);
tmpNPCType->level = atoi(row[2]);
tmpNPCType->race = atoi(row[3]);
tmpNPCType->class_ = atoi(row[4]);
tmpNPCType->max_hp = atoi(row[5]);
tmpNPCType->cur_hp = tmpNPCType->max_hp;
tmpNPCType->Mana = atoi(row[6]);
tmpNPCType->gender = atoi(row[7]);
tmpNPCType->texture = atoi(row[8]);
tmpNPCType->helmtexture = atoi(row[9]);
tmpNPCType->herosforgemodel = atoul(row[10]);
tmpNPCType->size = atof(row[11]);
tmpNPCType->loottable_id = atoi(row[12]);
tmpNPCType->merchanttype = atoi(row[13]);
tmpNPCType->alt_currency_type = atoi(row[14]);
tmpNPCType->adventure_template = atoi(row[15]);
tmpNPCType->trap_template = atoi(row[16]);
tmpNPCType->attack_speed = atof(row[17]);
tmpNPCType->STR = atoi(row[18]);
tmpNPCType->STA = atoi(row[19]);
tmpNPCType->DEX = atoi(row[20]);
tmpNPCType->AGI = atoi(row[21]);
tmpNPCType->INT = atoi(row[22]);
tmpNPCType->WIS = atoi(row[23]);
tmpNPCType->CHA = atoi(row[24]);
tmpNPCType->MR = atoi(row[25]);
tmpNPCType->CR = atoi(row[26]);
tmpNPCType->DR = atoi(row[27]);
tmpNPCType->FR = atoi(row[28]);
tmpNPCType->PR = atoi(row[29]);
tmpNPCType->Corrup = atoi(row[30]);
tmpNPCType->PhR = atoi(row[31]);
tmpNPCType->min_dmg = atoi(row[32]);
tmpNPCType->max_dmg = atoi(row[33]);
tmpNPCType->attack_count = atoi(row[34]);
temp_npctype_data->level = atoi(row[2]);
temp_npctype_data->race = atoi(row[3]);
temp_npctype_data->class_ = atoi(row[4]);
temp_npctype_data->max_hp = atoi(row[5]);
temp_npctype_data->cur_hp = temp_npctype_data->max_hp;
temp_npctype_data->Mana = atoi(row[6]);
temp_npctype_data->gender = atoi(row[7]);
temp_npctype_data->texture = atoi(row[8]);
temp_npctype_data->helmtexture = atoi(row[9]);
temp_npctype_data->herosforgemodel = atoul(row[10]);
temp_npctype_data->size = atof(row[11]);
temp_npctype_data->loottable_id = atoi(row[12]);
temp_npctype_data->merchanttype = atoi(row[13]);
temp_npctype_data->alt_currency_type = atoi(row[14]);
temp_npctype_data->adventure_template = atoi(row[15]);
temp_npctype_data->trap_template = atoi(row[16]);
temp_npctype_data->attack_speed = atof(row[17]);
temp_npctype_data->STR = atoi(row[18]);
temp_npctype_data->STA = atoi(row[19]);
temp_npctype_data->DEX = atoi(row[20]);
temp_npctype_data->AGI = atoi(row[21]);
temp_npctype_data->INT = atoi(row[22]);
temp_npctype_data->WIS = atoi(row[23]);
temp_npctype_data->CHA = atoi(row[24]);
temp_npctype_data->MR = atoi(row[25]);
temp_npctype_data->CR = atoi(row[26]);
temp_npctype_data->DR = atoi(row[27]);
temp_npctype_data->FR = atoi(row[28]);
temp_npctype_data->PR = atoi(row[29]);
temp_npctype_data->Corrup = atoi(row[30]);
temp_npctype_data->PhR = atoi(row[31]);
temp_npctype_data->min_dmg = atoi(row[32]);
temp_npctype_data->max_dmg = atoi(row[33]);
temp_npctype_data->attack_count = atoi(row[34]);
if (row[35] != nullptr)
strn0cpy(tmpNPCType->special_abilities, row[35], 512);
strn0cpy(temp_npctype_data->special_abilities, row[35], 512);
else
tmpNPCType->special_abilities[0] = '\0';
temp_npctype_data->special_abilities[0] = '\0';
tmpNPCType->npc_spells_id = atoi(row[36]);
tmpNPCType->npc_spells_effects_id = atoi(row[37]);
tmpNPCType->d_melee_texture1 = atoi(row[38]);
tmpNPCType->d_melee_texture2 = atoi(row[39]);
strn0cpy(tmpNPCType->ammo_idfile, row[40], 30);
tmpNPCType->prim_melee_type = atoi(row[41]);
tmpNPCType->sec_melee_type = atoi(row[42]);
tmpNPCType->ranged_type = atoi(row[43]);
tmpNPCType->runspeed= atof(row[44]);
tmpNPCType->findable = atoi(row[45]) == 0? false : true;
tmpNPCType->trackable = atoi(row[46]) == 0? false : true;
tmpNPCType->hp_regen = atoi(row[47]);
tmpNPCType->mana_regen = atoi(row[48]);
temp_npctype_data->npc_spells_id = atoi(row[36]);
temp_npctype_data->npc_spells_effects_id = atoi(row[37]);
temp_npctype_data->d_melee_texture1 = atoi(row[38]);
temp_npctype_data->d_melee_texture2 = atoi(row[39]);
strn0cpy(temp_npctype_data->ammo_idfile, row[40], 30);
temp_npctype_data->prim_melee_type = atoi(row[41]);
temp_npctype_data->sec_melee_type = atoi(row[42]);
temp_npctype_data->ranged_type = atoi(row[43]);
temp_npctype_data->runspeed= atof(row[44]);
temp_npctype_data->findable = atoi(row[45]) == 0? false : true;
temp_npctype_data->trackable = atoi(row[46]) == 0? false : true;
temp_npctype_data->hp_regen = atoi(row[47]);
temp_npctype_data->mana_regen = atoi(row[48]);
// set default value for aggroradius
tmpNPCType->aggroradius = (int32)atoi(row[49]);
if (tmpNPCType->aggroradius <= 0)
tmpNPCType->aggroradius = 70;
temp_npctype_data->aggroradius = (int32)atoi(row[49]);
if (temp_npctype_data->aggroradius <= 0)
temp_npctype_data->aggroradius = 70;
tmpNPCType->assistradius = (int32)atoi(row[50]);
if (tmpNPCType->assistradius <= 0)
tmpNPCType->assistradius = tmpNPCType->aggroradius;
temp_npctype_data->assistradius = (int32)atoi(row[50]);
if (temp_npctype_data->assistradius <= 0)
temp_npctype_data->assistradius = temp_npctype_data->aggroradius;
if (row[51] && strlen(row[51]))
tmpNPCType->bodytype = (uint8)atoi(row[51]);
temp_npctype_data->bodytype = (uint8)atoi(row[51]);
else
tmpNPCType->bodytype = 0;
temp_npctype_data->bodytype = 0;
tmpNPCType->npc_faction_id = atoi(row[52]);
temp_npctype_data->npc_faction_id = atoi(row[52]);
tmpNPCType->luclinface = atoi(row[53]);
tmpNPCType->hairstyle = atoi(row[54]);
tmpNPCType->haircolor = atoi(row[55]);
tmpNPCType->eyecolor1 = atoi(row[56]);
tmpNPCType->eyecolor2 = atoi(row[57]);
tmpNPCType->beardcolor = atoi(row[58]);
tmpNPCType->beard = atoi(row[59]);
tmpNPCType->drakkin_heritage = atoi(row[60]);
tmpNPCType->drakkin_tattoo = atoi(row[61]);
tmpNPCType->drakkin_details = atoi(row[62]);
temp_npctype_data->luclinface = atoi(row[53]);
temp_npctype_data->hairstyle = atoi(row[54]);
temp_npctype_data->haircolor = atoi(row[55]);
temp_npctype_data->eyecolor1 = atoi(row[56]);
temp_npctype_data->eyecolor2 = atoi(row[57]);
temp_npctype_data->beardcolor = atoi(row[58]);
temp_npctype_data->beard = atoi(row[59]);
temp_npctype_data->drakkin_heritage = atoi(row[60]);
temp_npctype_data->drakkin_tattoo = atoi(row[61]);
temp_npctype_data->drakkin_details = atoi(row[62]);
uint32 armor_tint_id = atoi(row[63]);
tmpNPCType->armor_tint[0] = (atoi(row[64]) & 0xFF) << 16;
tmpNPCType->armor_tint[0] |= (atoi(row[65]) & 0xFF) << 8;
tmpNPCType->armor_tint[0] |= (atoi(row[66]) & 0xFF);
tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0;
temp_npctype_data->armor_tint[0] = (atoi(row[64]) & 0xFF) << 16;
temp_npctype_data->armor_tint[0] |= (atoi(row[65]) & 0xFF) << 8;
temp_npctype_data->armor_tint[0] |= (atoi(row[66]) & 0xFF);
temp_npctype_data->armor_tint[0] |= (temp_npctype_data->armor_tint[0]) ? (0xFF << 24) : 0;
if (armor_tint_id != 0)
{
std::string armortint_query = StringFormat("SELECT red1h, grn1h, blu1h, "
"red2c, grn2c, blu2c, "
"red3a, grn3a, blu3a, "
"red4b, grn4b, blu4b, "
"red5g, grn5g, blu5g, "
"red6l, grn6l, blu6l, "
"red7f, grn7f, blu7f, "
"red8x, grn8x, blu8x, "
"red9x, grn9x, blu9x "
"FROM npc_types_tint WHERE id = %d",
armor_tint_id);
if (armor_tint_id != 0) {
std::string armortint_query = StringFormat(
"SELECT red1h, grn1h, blu1h, "
"red2c, grn2c, blu2c, "
"red3a, grn3a, blu3a, "
"red4b, grn4b, blu4b, "
"red5g, grn5g, blu5g, "
"red6l, grn6l, blu6l, "
"red7f, grn7f, blu7f, "
"red8x, grn8x, blu8x, "
"red9x, grn9x, blu9x "
"FROM npc_types_tint WHERE id = %d",
armor_tint_id);
auto armortint_results = QueryDatabase(armortint_query);
if (!armortint_results.Success() || armortint_results.RowCount() == 0)
armor_tint_id = 0;
@@ -1878,60 +2031,59 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
auto armorTint_row = armortint_results.begin();
for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) {
tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16;
tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8;
tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]);
tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0;
temp_npctype_data->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16;
temp_npctype_data->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8;
temp_npctype_data->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]);
temp_npctype_data->armor_tint[index] |= (temp_npctype_data->armor_tint[index]) ? (0xFF << 24) : 0;
}
}
}
// Try loading npc_types tint fields if armor tint is 0 or query failed to get results
if (armor_tint_id == 0)
{
for (int index = MaterialChest; index < _MaterialCount; index++)
{
tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0];
if (armor_tint_id == 0) {
for (int index = MaterialChest; index < _MaterialCount; index++) {
temp_npctype_data->armor_tint[index] = temp_npctype_data->armor_tint[0];
}
}
tmpNPCType->see_invis = atoi(row[67]);
tmpNPCType->see_invis_undead = atoi(row[68]) == 0? false: true; // Set see_invis_undead flag
if (row[69] != nullptr)
strn0cpy(tmpNPCType->lastname, row[69], 32);
temp_npctype_data->see_invis = atoi(row[67]);
temp_npctype_data->see_invis_undead = atoi(row[68]) == 0? false: true; // Set see_invis_undead flag
tmpNPCType->qglobal = atoi(row[70]) == 0? false: true; // qglobal
tmpNPCType->AC = atoi(row[71]);
tmpNPCType->npc_aggro = atoi(row[72]) == 0? false: true;
tmpNPCType->spawn_limit = atoi(row[73]);
tmpNPCType->see_hide = atoi(row[74]) == 0? false: true;
tmpNPCType->see_improved_hide = atoi(row[75]) == 0? false: true;
tmpNPCType->ATK = atoi(row[76]);
tmpNPCType->accuracy_rating = atoi(row[77]);
tmpNPCType->avoidance_rating = atoi(row[78]);
tmpNPCType->slow_mitigation = atoi(row[79]);
tmpNPCType->maxlevel = atoi(row[80]);
tmpNPCType->scalerate = atoi(row[81]);
tmpNPCType->private_corpse = atoi(row[82]) == 1 ? true: false;
tmpNPCType->unique_spawn_by_name = atoi(row[83]) == 1 ? true: false;
tmpNPCType->underwater = atoi(row[84]) == 1 ? true: false;
tmpNPCType->emoteid = atoi(row[85]);
tmpNPCType->spellscale = atoi(row[86]);
tmpNPCType->healscale = atoi(row[87]);
tmpNPCType->no_target_hotkey = atoi(row[88]) == 1 ? true: false;
tmpNPCType->raid_target = atoi(row[89]) == 0 ? false: true;
tmpNPCType->attack_delay = atoi(row[90]);
tmpNPCType->light = (atoi(row[91]) & 0x0F);
if (row[69] != nullptr)
strn0cpy(temp_npctype_data->lastname, row[69], 32);
temp_npctype_data->qglobal = atoi(row[70]) == 0? false: true; // qglobal
temp_npctype_data->AC = atoi(row[71]);
temp_npctype_data->npc_aggro = atoi(row[72]) == 0? false: true;
temp_npctype_data->spawn_limit = atoi(row[73]);
temp_npctype_data->see_hide = atoi(row[74]) == 0? false: true;
temp_npctype_data->see_improved_hide = atoi(row[75]) == 0? false: true;
temp_npctype_data->ATK = atoi(row[76]);
temp_npctype_data->accuracy_rating = atoi(row[77]);
temp_npctype_data->avoidance_rating = atoi(row[78]);
temp_npctype_data->slow_mitigation = atoi(row[79]);
temp_npctype_data->maxlevel = atoi(row[80]);
temp_npctype_data->scalerate = atoi(row[81]);
temp_npctype_data->private_corpse = atoi(row[82]) == 1 ? true: false;
temp_npctype_data->unique_spawn_by_name = atoi(row[83]) == 1 ? true: false;
temp_npctype_data->underwater = atoi(row[84]) == 1 ? true: false;
temp_npctype_data->emoteid = atoi(row[85]);
temp_npctype_data->spellscale = atoi(row[86]);
temp_npctype_data->healscale = atoi(row[87]);
temp_npctype_data->no_target_hotkey = atoi(row[88]) == 1 ? true: false;
temp_npctype_data->raid_target = atoi(row[89]) == 0 ? false: true;
temp_npctype_data->attack_delay = atoi(row[90]);
temp_npctype_data->light = (atoi(row[91]) & 0x0F);
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.
if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) {
std::cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << std::endl;
delete tmpNPCType;
if (zone->npctable.find(temp_npctype_data->npc_id) != zone->npctable.end()) {
std::cerr << "Error loading duplicate NPC " << temp_npctype_data->npc_id << std::endl;
delete temp_npctype_data;
return nullptr;
}
zone->npctable[tmpNPCType->npc_id]=tmpNPCType;
npc = tmpNPCType;
zone->npctable[temp_npctype_data->npc_id] = temp_npctype_data;
npc = temp_npctype_data;
}
return npc;
@@ -3248,7 +3400,7 @@ bool ZoneDatabase::LoadFactionData()
auto row = results.begin();
max_faction = atoi(row[0]);
max_faction = row[0] ? atoi(row[0]) : 0;
faction_array = new Faction*[max_faction+1];
for(unsigned int index=0; index<max_faction; index++)
faction_array[index] = nullptr;
@@ -3398,9 +3550,9 @@ uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const c
dbpc->plat, dbpc->haircolor, dbpc->beardcolor, dbpc->eyecolor1,
dbpc->eyecolor2, dbpc->hairstyle, dbpc->face, dbpc->beard,
dbpc->drakkin_heritage, dbpc->drakkin_tattoo, dbpc->drakkin_details,
dbpc->item_tint[0].color, dbpc->item_tint[1].color, dbpc->item_tint[2].color,
dbpc->item_tint[3].color, dbpc->item_tint[4].color, dbpc->item_tint[5].color,
dbpc->item_tint[6].color, dbpc->item_tint[7].color, dbpc->item_tint[8].color,
dbpc->item_tint[0].Color, dbpc->item_tint[1].Color, dbpc->item_tint[2].Color,
dbpc->item_tint[3].Color, dbpc->item_tint[4].Color, dbpc->item_tint[5].Color,
dbpc->item_tint[6].Color, dbpc->item_tint[7].Color, dbpc->item_tint[8].Color,
db_id);
auto results = QueryDatabase(query);
@@ -3491,15 +3643,15 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui
dbpc->drakkin_heritage,
dbpc->drakkin_tattoo,
dbpc->drakkin_details,
dbpc->item_tint[0].color,
dbpc->item_tint[1].color,
dbpc->item_tint[2].color,
dbpc->item_tint[3].color,
dbpc->item_tint[4].color,
dbpc->item_tint[5].color,
dbpc->item_tint[6].color,
dbpc->item_tint[7].color,
dbpc->item_tint[8].color
dbpc->item_tint[0].Color,
dbpc->item_tint[1].Color,
dbpc->item_tint[2].Color,
dbpc->item_tint[3].Color,
dbpc->item_tint[4].Color,
dbpc->item_tint[5].Color,
dbpc->item_tint[6].Color,
dbpc->item_tint[7].Color,
dbpc->item_tint[8].Color
);
auto results = QueryDatabase(query);
uint32 last_insert_id = results.LastInsertedID();
@@ -3671,15 +3823,15 @@ bool ZoneDatabase::LoadCharacterCorpseData(uint32 corpse_id, PlayerCorpse_Struct
pcs->drakkin_heritage = atoul(row[i++]); // drakkin_heritage,
pcs->drakkin_tattoo = atoul(row[i++]); // drakkin_tattoo,
pcs->drakkin_details = atoul(row[i++]); // drakkin_details,
pcs->item_tint[0].color = atoul(row[i++]); // wc_1,
pcs->item_tint[1].color = atoul(row[i++]); // wc_2,
pcs->item_tint[2].color = atoul(row[i++]); // wc_3,
pcs->item_tint[3].color = atoul(row[i++]); // wc_4,
pcs->item_tint[4].color = atoul(row[i++]); // wc_5,
pcs->item_tint[5].color = atoul(row[i++]); // wc_6,
pcs->item_tint[6].color = atoul(row[i++]); // wc_7,
pcs->item_tint[7].color = atoul(row[i++]); // wc_8,
pcs->item_tint[8].color = atoul(row[i++]); // wc_9
pcs->item_tint[0].Color = atoul(row[i++]); // wc_1,
pcs->item_tint[1].Color = atoul(row[i++]); // wc_2,
pcs->item_tint[2].Color = atoul(row[i++]); // wc_3,
pcs->item_tint[3].Color = atoul(row[i++]); // wc_4,
pcs->item_tint[4].Color = atoul(row[i++]); // wc_5,
pcs->item_tint[5].Color = atoul(row[i++]); // wc_6,
pcs->item_tint[6].Color = atoul(row[i++]); // wc_7,
pcs->item_tint[7].Color = atoul(row[i++]); // wc_8,
pcs->item_tint[8].Color = atoul(row[i++]); // wc_9
}
query = StringFormat(
"SELECT \n"
+2 -2
View File
@@ -365,7 +365,7 @@ public:
bool PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spawn2_list, int16 version, uint32 repopdelay = 0);
Spawn2* LoadSpawn2(LinkedList<Spawn2*> &spawn2_list, uint32 spawn2id, uint32 timeleft);
bool CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, const glm::vec4& position, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value);
void UpdateSpawn2Timeleft(uint32 id, uint16 instance_id,uint32 timeleft);
void UpdateRespawnTime(uint32 id, uint16 instance_id,uint32 timeleft);
uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id);
void UpdateSpawn2Status(uint32 id, uint8 new_status);
@@ -405,7 +405,7 @@ public:
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
const NPCType* GetNPCType(uint32 id);
const NPCType* LoadNPCTypesData(uint32 id, bool bulk_load = false);
/* Mercs */
const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel);
+1 -1
View File
@@ -182,7 +182,7 @@ struct PlayerCorpse_Struct {
struct Door {
uint32 db_id;
uint8 door_id;
char zone_name[16];
char zone_name[32];
char door_name[32];
float pos_x;
float pos_y;
+7 -3
View File
@@ -587,10 +587,14 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
// If we are SoF and later and are respawning from hover, we want the real zone ID, else zero to use the old hack.
//
if((GetClientVersionBit() & BIT_SoFAndLater) && (!RuleB(Character, RespawnFromHover) || !IsHoveringForRespawn()))
gmg->bind_zone_id = 0;
else
if(zone->GetZoneID() == zoneID) {
if((GetClientVersionBit() & BIT_SoFAndLater) && (!RuleB(Character, RespawnFromHover) || !IsHoveringForRespawn()))
gmg->bind_zone_id = 0;
else
gmg->bind_zone_id = zoneID;
} else {
gmg->bind_zone_id = zoneID;
}
gmg->x = x;
gmg->y = y;