mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 18:52:22 +00:00
Merge remote-tracking branch 'upstream/master'
Conflicts: changelog.txt world/worlddb.cpp
This commit is contained in:
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user