Merge from master, probably wont compile but will fix that soon

This commit is contained in:
KimLS
2015-06-21 01:38:19 -07:00
125 changed files with 7658 additions and 8436 deletions
+4
View File
@@ -20,6 +20,7 @@ SET(zone_sources
embparser_api.cpp
embperl.cpp
embxs.cpp
encounter.cpp
entity.cpp
exp.cpp
fearpath.cpp
@@ -35,6 +36,7 @@ SET(zone_sources
lua_corpse.cpp
lua_client.cpp
lua_door.cpp
lua_encounter.cpp
lua_entity.cpp
lua_entity_list.cpp
lua_general.cpp
@@ -137,6 +139,7 @@ SET(zone_headers
embparser.h
embperl.h
embxs.h
encounter.h
entity.h
errmsg.h
event_codes.h
@@ -148,6 +151,7 @@ SET(zone_headers
lua_bit.h
lua_client.h
lua_corpse.h
lua_encounter.h
lua_entity.h
lua_entity_list.h
lua_general.h
+21 -14
View File
@@ -1104,9 +1104,9 @@ void Client::SendAATable() {
uint32 i;
for(i=0;i < MAX_PP_AA_ARRAY;i++){
aa2->aa_list[i].aa_skill = aa[i]->AA;
aa2->aa_list[i].aa_value = aa[i]->value;
aa2->aa_list[i].unknown08 = 0;
aa2->aa_list[i].AA = aa[i]->value ? aa[i]->AA : 0; // bit of a hack to prevent expendables punching a hole
aa2->aa_list[i].value = aa[i]->value;
aa2->aa_list[i].charges = aa[i]->charges;
}
QueuePacket(outapp);
safe_delete(outapp);
@@ -1309,11 +1309,9 @@ void Client::SendAA(uint32 id, int seq) {
SendAA_Struct* saa_next = nullptr;
saa_next = zone->FindAA(saa->sof_next_id);
// hard-coding values like this is dangerous and makes adding/updating clients a nightmare...
if (saa_next &&
(((GetClientVersionBit() == 4) && (saa_next->clientver > 4))
|| ((GetClientVersionBit() == 8) && (saa_next->clientver > 5))
|| ((GetClientVersionBit() == 16) && (saa_next->clientver > 6)))){
// this check should work as long as we continue to just add the clients and just increase
// each number ....
if (saa_next && static_cast<int>(GetClientVersion()) < saa_next->clientver - 1) {
saa->next_id=0xFFFFFFFF;
}
}
@@ -1397,26 +1395,33 @@ uint32 Client::GetAA(uint32 aa_id) const {
bool Client::SetAA(uint32 aa_id, uint32 new_value) {
aa_points[aa_id] = new_value;
uint32 cur;
auto sendaa = zone->FindAA(aa_id); // this is a bit hacky
uint32 charges = sendaa->special_category == 7 && new_value ? 1 : 0;
for(cur=0;cur < MAX_PP_AA_ARRAY;cur++){
if((aa[cur]->value > 1) && ((aa[cur]->AA - aa[cur]->value + 1)== aa_id)){
aa[cur]->value = new_value;
if(new_value > 0)
aa[cur]->AA++;
else
aa[cur]->AA = 0;
aa[cur]->charges = charges;
return true;
}
else if((aa[cur]->value == 1) && (aa[cur]->AA == aa_id)){
aa[cur]->value = new_value;
if(new_value > 0)
aa[cur]->AA++;
else
aa[cur]->AA = 0;
aa[cur]->charges = charges;
return true;
}
// hack to prevent expendable exploit, we should probably be reshuffling the array to fix the hole
else if(aa[cur]->value == 0 && new_value == 1 && aa[cur]->AA == aa_id) {
aa[cur]->value = new_value;
aa[cur]->charges = charges;
return true;
}
else if(aa[cur]->AA==0){ //end of list
aa[cur]->AA = aa_id;
aa[cur]->value = new_value;
aa[cur]->charges = charges;
return true;
}
}
@@ -1487,8 +1492,10 @@ void Client::ResetAA(){
for (i=0; i < MAX_PP_AA_ARRAY; i++) {
aa[i]->AA = 0;
aa[i]->value = 0;
m_pp.aa_array[MAX_PP_AA_ARRAY].AA = 0;
m_pp.aa_array[MAX_PP_AA_ARRAY].value = 0;
aa[i]->charges = 0;
m_pp.aa_array[i].AA = 0;
m_pp.aa_array[i].value = 0;
m_pp.aa_array[i].charges= 0;
}
std::map<uint32,uint8>::iterator itr;
+40 -13
View File
@@ -982,14 +982,24 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
return 0;
}
else{
if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){
dmg = GetMonkHandToHandDamage();
if (hate) *hate += dmg;
bool MagicGloves=false;
if (IsClient()) {
ItemInst *gloves=CastToClient()->GetInv().GetItem(MainHands);
if (gloves != nullptr) {
MagicGloves = gloves->GetItem()->Magic;
}
}
if((GetClass() == MONK || GetClass() == BEASTLORD)) {
if(MagicGloves || GetLevel() >= 30){
dmg = GetMonkHandToHandDamage();
if (hate) *hate += dmg;
}
}
else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ //pets wouldn't actually use this but...
dmg = 1; //it gives us an idea if we can hit
}
else if(GetSpecialAbility(SPECATK_MAGICAL)){
else if(MagicGloves || GetSpecialAbility(SPECATK_MAGICAL)){
dmg = 1;
}
else
@@ -2134,6 +2144,10 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
if(give_exp && give_exp->IsClient())
give_exp_client = give_exp->CastToClient();
//do faction hits even if we are a merchant, so long as a player killed us
if (give_exp_client && !RuleB(NPC, EnableMeritBasedFaction))
hate_list.DoFactionHits(GetNPCFactionID());
bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE);
if (give_exp_client && !IsCorpse())
{
@@ -2277,10 +2291,6 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
}
}
//do faction hits even if we are a merchant, so long as a player killed us
if(give_exp_client && !RuleB(NPC, EnableMeritBasedFaction))
hate_list.DoFactionHits(GetNPCFactionID());
if (!HasOwner() && !IsMerc() && class_ != MERCHANT && class_ != ADVENTUREMERCHANT && !GetSwarmInfo()
&& MerchantType == 0 && killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) ||
(killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient())))
@@ -3683,7 +3693,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
//send an HP update if we are hurt
if(GetHP() < GetMaxHP())
SendHPUpdate();
SendHPUpdate(!iBuffTic); // the OP_Damage actually updates the client in these cases, so we skill them
} //end `if damage was done`
//send damage packet...
@@ -3700,6 +3710,23 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
a->type = SkillDamageTypes[skill_used]; // was 0x1c
a->damage = damage;
a->spellid = spell_id;
a->meleepush_xy = attacker->GetHeading() * 2.0f;
if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
a->force = EQEmu::GetSkillMeleePushForce(skill_used);
// update NPC stuff
auto new_pos = glm::vec3(m_Position.x + (a->force * std::sin(a->meleepush_xy) + m_Delta.x),
m_Position.y + (a->force * std::cos(a->meleepush_xy) + m_Delta.y), m_Position.z);
if (zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable.
if (IsNPC()) {
// Is this adequate?
Teleport(new_pos);
SendPosUpdate();
}
} else {
a->force = 0.0f; // we couldn't move there, so lets not
}
}
//Note: if players can become pets, they will not receive damage messages of their own
//this was done to simplify the code here (since we can only effectively skip one mob on queue)
@@ -3968,7 +3995,7 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) {
}
// Innate + aug procs from weapons
// TODO: powersource procs
// TODO: powersource procs -- powersource procs are on invis augs, so shouldn't need anything extra
TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand);
// Procs from Buffs and AA both melee and range
TrySpellProc(weapon_g, weapon_g->GetItem(), on, hand);
@@ -4054,7 +4081,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const ItemData *weapon, Mob *on, u
}
}
}
// TODO: Powersource procs
// TODO: Powersource procs -- powersource procs are from augs so shouldn't need anything extra
return;
}
@@ -4116,7 +4143,7 @@ void Mob::TrySpellProc(const ItemInst *inst, const ItemData *weapon, Mob *on, ui
outapp->priority = 3;
entity_list.QueueCloseClients(this, outapp, false, 200, 0, true);
safe_delete(outapp);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0,
SpellProcs[i].base_spellID);
} else {
@@ -4222,7 +4249,7 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
}
#ifdef BOTS
if (this->IsPet() && this->GetOwner()->IsBot()) {
if (this->IsPet() && this->GetOwner() && this->GetOwner()->IsBot()) {
this->TryPetCriticalHit(defender,skill,damage);
return;
}
+31 -30
View File
@@ -428,7 +428,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
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, item->Worn.Type);
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);
}
if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects
@@ -559,7 +559,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool
/*
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.
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
@@ -579,7 +579,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool
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
@@ -691,7 +691,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
continue;
Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName());
uint8 focus = IsFocusEffect(0, 0, true,effect);
if (focus)
{
@@ -1007,7 +1007,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_BlockBehind:
newbon->BlockBehind += base1;
break;
case SE_StrikeThrough:
case SE_StrikeThrough2:
newbon->StrikeThrough += base1;
@@ -1313,7 +1313,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_Vampirism:
newbon->Vampirism += base1;
break;
break;
case SE_FrenziedDevastation:
newbon->FrenziedDevastation += base2;
@@ -1416,7 +1416,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
}
case SE_SkillProcSuccess:{
for(int e = 0; e < MAX_SKILL_PROCS; e++)
{
if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == aaid)
@@ -1449,7 +1449,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, 0, buffs[i].ticsremaining,i);
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod);
if (buffs[i].numhits > 0)
Numhits(true);
@@ -1472,8 +1472,9 @@ 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, uint8 WornType, uint32 ticsremaining, int buffslot,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId,
uint8 WornType, int32 ticsremaining, int buffslot, int instrument_mod,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
{
int i, effect_value, base2, max, effectid;
bool AdditiveWornBonus = false;
@@ -1509,9 +1510,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType))
AdditiveWornBonus = true;
effectid = spells[spell_id].effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, caster, ticsremaining);
base2 = spells[spell_id].base2[i];
max = spells[spell_id].max[i];
}
@@ -1620,10 +1621,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) {
effect_value -= ((effect_value * GetSlowMitigation()/100));
if (effect_value > new_bonus->inhibitmelee)
if (effect_value > new_bonus->inhibitmelee)
new_bonus->inhibitmelee = effect_value;
}
break;
}
@@ -1839,7 +1840,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
new_bonus->DamageShieldType = GetDamageShieldType(spell_id, max);
else
new_bonus->DamageShieldType = GetDamageShieldType(spell_id);
break;
}
@@ -2020,7 +2021,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_Vampirism:
new_bonus->Vampirism += effect_value;
break;
break;
case SE_AllInstrumentMod:
{
@@ -2263,7 +2264,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_CriticalSpellChance:
{
new_bonus->CriticalSpellChance += effect_value;
if (base2 > new_bonus->SpellCritDmgIncNoStack)
new_bonus->SpellCritDmgIncNoStack = base2;
break;
@@ -2473,7 +2474,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_NegateAttacks:
{
if (!new_bonus->NegateAttacks[0] ||
if (!new_bonus->NegateAttacks[0] ||
((new_bonus->NegateAttacks[0] && new_bonus->NegateAttacks[2]) && (new_bonus->NegateAttacks[2] < max))){
new_bonus->NegateAttacks[0] = 1;
new_bonus->NegateAttacks[1] = buffslot;
@@ -2493,7 +2494,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
break;
}
case SE_MeleeThresholdGuard:
{
if (new_bonus->MeleeThresholdGuard[0] < effect_value){
@@ -2860,17 +2861,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
new_bonus->NegateIfCombat = true;
break;
case SE_Screech:
case SE_Screech:
new_bonus->Screech = effect_value;
break;
case SE_AlterNPCLevel:
if (IsNPC()){
if (!new_bonus->AlterNPCLevel
|| ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value))
if (!new_bonus->AlterNPCLevel
|| ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value))
|| ((effect_value > 0) && (new_bonus->AlterNPCLevel < effect_value))) {
int tmp_lv = GetOrigLevel() + effect_value;
if (tmp_lv < 1)
tmp_lv = 1;
@@ -2908,7 +2909,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
new_bonus->BerserkSPA = true;
break;
case SE_Metabolism:
new_bonus->Metabolism += effect_value;
break;
@@ -3009,7 +3010,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
}
case SE_SkillProc:{
for(int e = 0; e < MAX_SKILL_PROCS; e++)
{
if(new_bonus->SkillProc[e] && new_bonus->SkillProc[e] == spell_id)
@@ -3024,7 +3025,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
}
case SE_SkillProcSuccess:{
for(int e = 0; e < MAX_SKILL_PROCS; e++)
{
if(new_bonus->SkillProcSuccess[e] && new_bonus->SkillProcSuccess[e] == spell_id)
@@ -3040,9 +3041,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
if (IsAISpellEffect) {
//Non-Focused Effect to modify incoming spell damage by resist type.
case SE_FcSpellVulnerability:
case SE_FcSpellVulnerability:
ModVulnerability(base2, effect_value);
break;
}
@@ -4394,7 +4395,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
aabonuses.SlayUndead[0] = effect_value;
aabonuses.SlayUndead[1] = effect_value;
break;
case SE_DoubleRangedAttack:
spellbonuses.DoubleRangedAttack = effect_value;
aabonuses.DoubleRangedAttack = effect_value;
@@ -4414,7 +4415,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
aabonuses.ShieldEquipDmgMod[1] = effect_value;
itembonuses.ShieldEquipDmgMod[0] = effect_value;
itembonuses.ShieldEquipDmgMod[1] = effect_value;
break;
break;
case SE_TriggerMeleeThreshold:
spellbonuses.TriggerMeleeThreshold = false;
+3091 -5714
View File
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -60,6 +60,7 @@ enum SpellTypeIndex {
};
class Bot : public NPC {
friend class Mob;
public:
// Class enums
enum BotfocusType { //focus types
@@ -154,7 +155,7 @@ public:
// Class Methods
bool IsValidRaceClassCombo();
bool IsValidName();
bool IsBotNameAvailable(std::string* errorMessage);
static bool IsBotNameAvailable(char *botName, std::string* errorMessage);
bool DeleteBot(std::string* errorMessage);
void Spawn(Client* botCharacterOwner, std::string* errorMessage);
virtual void SetLevel(uint8 in_level, bool command = false);
@@ -190,7 +191,9 @@ public:
bool CanDoSpecialAttack(Mob *other);
virtual int32 CheckAggroAmount(uint16 spellid);
virtual void CalcBonuses();
void CalcItemBonuses();
void CalcItemBonuses(StatBonuses* newbon);
void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false);
int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat);
virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr);
virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther);
inline virtual bool IsPet() { return false; }
@@ -310,7 +313,7 @@ public:
virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration);
virtual float GetAOERange(uint16 spell_id);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0);
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
@@ -464,6 +467,7 @@ public:
uint32 GetHealRotationNextHealTime() { return _healRotationNextHeal; }
uint32 GetHealRotationTimer () { return _healRotationTimer; }
bool GetBardUseOutOfCombatSongs() { return _bardUseOutOfCombatSongs;}
bool GetShowHelm() { return _showhelm; }
inline virtual int32 GetAC() const { return AC; }
inline virtual int32 GetSTR() const { return STR; }
inline virtual int32 GetSTA() const { return STA; }
@@ -547,6 +551,7 @@ public:
void SetHealRotationTimer( uint32 timer ) { _healRotationTimer = timer; }
void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; }
void SetBardUseOutOfCombatSongs(bool useOutOfCombatSongs) { _bardUseOutOfCombatSongs = useOutOfCombatSongs;}
void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
// Class Destructors
virtual ~Bot();
@@ -619,6 +624,7 @@ private:
std::map<uint32, BotAA> botAAs;
InspectMessage_Struct _botInspectMessage;
bool _bardUseOutOfCombatSongs;
bool _showhelm;
// Private "base stats" Members
int32 _baseMR;
+84 -27
View File
@@ -116,7 +116,7 @@ Client::Client(EQStreamInterface* ieqs)
),
//these must be listed in the order they appear in client.h
position_timer(250),
hpupdate_timer(1800),
hpupdate_timer(2000),
camp_timer(29000),
process_timer(100),
stamina_timer(40000),
@@ -208,6 +208,7 @@ Client::Client(EQStreamInterface* ieqs)
npclevel = 0;
pQueuedSaveWorkID = 0;
position_timer_counter = 0;
position_update_same_count = 0;
fishing_timer.Disable();
shield_timer.Disable();
dead_timer.Disable();
@@ -549,17 +550,22 @@ bool Client::SaveAA(){
}
}
m_pp.aapoints_spent = spentpoints + m_epp.expended_aa;
int highest = 0;
for (int a = 0; a < MAX_PP_AA_ARRAY; a++) {
if (aa[a]->AA > 0 && aa[a]->value){
if (aa[a]->AA > 0) { // those with value 0 will be cleaned up on next load
if (first_entry != 1){
rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)"
" VALUES (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value);
rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value, charges)"
" VALUES (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges);
first_entry = 1;
} else {
rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges);
}
rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value);
highest = a;
}
}
auto results = database.QueryDatabase(rquery);
/* This is another part of the hack to clean up holes left by expendable AAs */
rquery = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `slot` > %d", character_id, highest);
return true;
}
@@ -1051,12 +1057,12 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
if(quest_manager.ProximitySayInUse())
entity_list.ProcessProximitySay(message, this, language);
if (GetTarget() != 0 && GetTarget()->IsNPC()) {
if (GetTarget() != 0 && GetTarget()->IsNPC() &&
!IsInvisible(GetTarget())) {
if(!GetTarget()->CastToNPC()->IsEngaged()) {
CheckLDoNHail(GetTarget());
CheckEmoteHail(GetTarget(), message);
if(DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= 200) {
NPC *tar = GetTarget()->CastToNPC();
parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language);
@@ -2540,12 +2546,12 @@ 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);
@@ -2592,9 +2598,9 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){
; // Binding self
}
}
}
}
else if (bindwound_timer.Check()) // Did the timer finish?
{
{
// finish bind
// disable complete timer
bindwound_timer.Disable();
@@ -4991,7 +4997,7 @@ void Client::SetShadowStepExemption(bool v)
if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__,
@@ -5048,7 +5054,7 @@ void Client::SetKnockBackExemption(bool v)
if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
@@ -5105,7 +5111,7 @@ void Client::SetPortExemption(bool v)
if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
@@ -5709,8 +5715,8 @@ void Client::ProcessInspectRequest(Client* requestee, Client* requester) {
else if (inst && inst->GetOrnamentationIcon())
{
insr->itemicons[L] = inst->GetOrnamentationIcon();
}
else
}
else
{
insr->itemicons[L] = item->Icon;
}
@@ -7491,6 +7497,10 @@ void Client::GarbleMessage(char *message, uint8 variance)
const char delimiter = 0x12;
int delimiter_count = 0;
// Don't garble # commands
if (message[0] == '#')
return;
for (size_t i = 0; i < strlen(message); i++) {
// Client expects hex values inside of a text link body
if (message[i] == delimiter) {
@@ -7500,7 +7510,7 @@ void Client::GarbleMessage(char *message, uint8 variance)
}
uint8 chance = (uint8)zone->random.Int(0, 115); // variation just over worst possible scrambling
if (isalpha(message[i]) && (chance <= variance)) {
if (isalpha((unsigned char)message[i]) && (chance <= variance)) {
uint8 rand_char = (uint8)zone->random.Int(0,51); // choose a random character from the alpha list
message[i] = alpha_list[rand_char];
}
@@ -7584,7 +7594,7 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra
}
//Sets the characters faction standing with the specified NPC.
void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity)
void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest)
{
int32 faction_id[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int32 npc_value[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -7608,9 +7618,18 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui
// Find out starting faction for this faction
// It needs to be used to adj max and min personal
// The range is still the same, 1200-3000(4200), but adjusted for base
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
faction_id[i]);
if (quest)
{
//The ole switcheroo
if (npc_value[i] > 0)
npc_value[i] = -abs(npc_value[i]);
else if (npc_value[i] < 0)
npc_value[i] = abs(npc_value[i]);
}
// Adjust the amount you can go up or down so the resulting range
// is PERSONAL_MAX - PERSONAL_MIN
//
@@ -7649,7 +7668,7 @@ void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class
// Find out starting faction for this faction
// It needs to be used to adj max and min personal
// The range is still the same, 1200-3000(4200), but adjusted for base
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
faction_id);
// Adjust the amount you can go up or down so the resulting range
@@ -7835,14 +7854,14 @@ void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_
char name[50];
int32 faction_value;
// If we're dropping from MAX or raising from MIN or repairing,
// If we're dropping from MAX or raising from MIN or repairing,
// we should base the message on the new updated value so we don't show
// a min MAX message
//
// If we're changing any other place, we use the value before the
// hit. For example, if we go from 1199 to 1200 which is the MAX
// we still want to say faction got better this time around.
if ( (faction_before_hit >= this_faction_max) ||
(faction_before_hit <= this_faction_min))
faction_value = totalvalue;
@@ -8378,10 +8397,10 @@ std::string Client::TextLink::GenerateLink()
m_Link.clear();
m_LinkBody.clear();
m_LinkText.clear();
generate_body();
generate_text();
if ((m_LinkBody.length() == EmuConstants::TEXT_LINK_BODY_LENGTH) && (m_LinkText.length() > 0)) {
m_Link.push_back(0x12);
m_Link.append(m_LinkBody);
@@ -8420,7 +8439,7 @@ void Client::TextLink::generate_body()
{
/*
Current server mask: EQClientRoF2
RoF2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X" (56)
RoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (55)
SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (50)
@@ -8428,7 +8447,6 @@ void Client::TextLink::generate_body()
*/
memset(&m_LinkBodyStruct, 0, sizeof(TextLinkBody_Struct));
const ItemData* item_data = nullptr;
switch (m_LinkType) {
@@ -8475,7 +8493,7 @@ void Client::TextLink::generate_body()
default:
break;
}
if (m_ProxyItemID != NOT_USED) {
m_LinkBodyStruct.item_id = m_ProxyItemID;
}
@@ -8580,3 +8598,42 @@ bool Client::TextLink::GenerateLinkBody(std::string& textLinkBody, const TextLin
if (textLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; }
return true;
}
void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction) {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct));
memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer));
QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer;
qr->mob_id = target->GetID(); // Entity ID for the from mob name
qr->target_id = GetID(); // The Client ID (this)
qr->copper = copper;
qr->silver = silver;
qr->gold = gold;
qr->platinum = platinum;
qr->item_id = itemid;
qr->exp_reward = exp;
if (copper > 0 || silver > 0 || gold > 0 || platinum > 0)
AddMoneyToPP(copper, silver, gold, platinum, false);
if (itemid > 0)
SummonItem(itemid, 0, 0, 0, 0, 0, 0, false, MainPowerSource);
if (faction)
{
if (target->IsNPC())
{
int32 nfl_id = target->CastToNPC()->GetNPCFactionID();
SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true);
qr->faction = target->CastToNPC()->GetPrimaryFaction();
qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow.
}
}
if (exp > 0)
AddEXP(exp);
QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
safe_delete(outapp);
}
+10 -1
View File
@@ -559,6 +559,7 @@ public:
void SendCrystalCounts();
void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false);
uint32 CalcEXP(uint8 conlevel = 0xFF);
void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false);
void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0);
void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp);
@@ -613,7 +614,7 @@ public:
void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_before_hit, int32 totalvalue, uint8 temp, int32 this_faction_min, int32 this_faction_max);
void UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction_id, int32 *current_value, int32 temp, int32 this_faction_min, int32 this_faction_max);
void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity);
void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest = false);
void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp);
int32 GetRawItemAC();
uint16 GetCombinedAC_TEST();
@@ -930,6 +931,7 @@ public:
bool DecreaseByID(uint32 type, uint8 amt);
uint8 SlotConvert2(uint8 slot); //Maybe not needed.
void Escape(); //AA Escape
void DisenchantSummonedBags(bool client_update = true);
void RemoveNoRent(bool client_update = true);
void RemoveDuplicateLore(bool client_update = true);
void MoveSlotNotAllowed(bool client_update = true);
@@ -1154,6 +1156,7 @@ public:
inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); }
void DragCorpses();
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
inline void ResetPositionTimer() { position_timer_counter = 0; }
void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
@@ -1279,6 +1282,9 @@ public:
virtual int32 Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false);
void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false);
void ResetHPUpdateTimer() { hpupdate_timer.Start(); }
protected:
friend class Mob;
void CalcItemBonuses(StatBonuses* newbon);
@@ -1466,6 +1472,9 @@ private:
Timer position_timer;
uint8 position_timer_counter;
// this is used to try to cut back on position update reflections
int position_update_same_count;
PTimerList p_timers; //persistent timers
Timer hpupdate_timer;
Timer camp_timer;
+72 -86
View File
@@ -1974,101 +1974,87 @@ int32 Client::CalcATK()
uint32 Mob::GetInstrumentMod(uint16 spell_id) const
{
if (GetClass() != BARD) {
if (GetClass() != BARD || spells[spell_id].IsDisciplineBuff) // Puretone is Singing but doesn't get any mod
return 10;
}
uint32 effectmod = 10;
int effectmodcap = RuleI(Character, BaseInstrumentSoftCap);
//this should never use spell modifiers...
//if a spell grants better modifers, they are copied into the item mods
//because the spells are supposed to act just like having the intrument.
//item mods are in 10ths of percent increases
// this should never use spell modifiers...
// if a spell grants better modifers, they are copied into the item mods
// because the spells are supposed to act just like having the intrument.
// item mods are in 10ths of percent increases
// clickies (Symphony of Battle) that have a song skill don't get AA bonus for some reason
// but clickies that are songs (selo's on Composers Greaves) do get AA mod as well
switch (spells[spell_id].skill) {
case SkillPercussionInstruments:
if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) {
effectmod = 10;
}
else if (GetSkill(SkillPercussionInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.percussionMod > spellbonuses.percussionMod) {
effectmod = itembonuses.percussionMod;
}
else {
effectmod = spellbonuses.percussionMod;
}
effectmod += aabonuses.percussionMod;
break;
case SkillStringedInstruments:
if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) {
effectmod = 10;
}
else if (GetSkill(SkillStringedInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.stringedMod > spellbonuses.stringedMod) {
effectmod = itembonuses.stringedMod;
}
else {
effectmod = spellbonuses.stringedMod;
}
effectmod += aabonuses.stringedMod;
break;
case SkillWindInstruments:
if (itembonuses.windMod == 0 && spellbonuses.windMod == 0) {
effectmod = 10;
}
else if (GetSkill(SkillWindInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.windMod > spellbonuses.windMod) {
effectmod = itembonuses.windMod;
}
else {
effectmod = spellbonuses.windMod;
}
effectmod += aabonuses.windMod;
break;
case SkillBrassInstruments:
if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) {
effectmod = 10;
}
else if (GetSkill(SkillBrassInstruments) == 0) {
effectmod = 10;
}
else if (itembonuses.brassMod > spellbonuses.brassMod) {
effectmod = itembonuses.brassMod;
}
else {
effectmod = spellbonuses.brassMod;
}
effectmod += aabonuses.brassMod;
break;
case SkillSinging:
if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) {
effectmod = 10;
}
else if (itembonuses.singingMod > spellbonuses.singingMod) {
effectmod = itembonuses.singingMod;
}
else {
effectmod = spellbonuses.singingMod;
}
effectmod += aabonuses.singingMod + spellbonuses.Amplification;
break;
default:
case SkillPercussionInstruments:
if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0)
effectmod = 10;
break;
else if (GetSkill(SkillPercussionInstruments) == 0)
effectmod = 10;
else if (itembonuses.percussionMod > spellbonuses.percussionMod)
effectmod = itembonuses.percussionMod;
else
effectmod = spellbonuses.percussionMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.percussionMod;
break;
case SkillStringedInstruments:
if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0)
effectmod = 10;
else if (GetSkill(SkillStringedInstruments) == 0)
effectmod = 10;
else if (itembonuses.stringedMod > spellbonuses.stringedMod)
effectmod = itembonuses.stringedMod;
else
effectmod = spellbonuses.stringedMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.stringedMod;
break;
case SkillWindInstruments:
if (itembonuses.windMod == 0 && spellbonuses.windMod == 0)
effectmod = 10;
else if (GetSkill(SkillWindInstruments) == 0)
effectmod = 10;
else if (itembonuses.windMod > spellbonuses.windMod)
effectmod = itembonuses.windMod;
else
effectmod = spellbonuses.windMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.windMod;
break;
case SkillBrassInstruments:
if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0)
effectmod = 10;
else if (GetSkill(SkillBrassInstruments) == 0)
effectmod = 10;
else if (itembonuses.brassMod > spellbonuses.brassMod)
effectmod = itembonuses.brassMod;
else
effectmod = spellbonuses.brassMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.brassMod;
break;
case SkillSinging:
if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0)
effectmod = 10;
else if (itembonuses.singingMod > spellbonuses.singingMod)
effectmod = itembonuses.singingMod;
else
effectmod = spellbonuses.singingMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.singingMod + spellbonuses.Amplification;
break;
default:
effectmod = 10;
return effectmod;
}
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
if (effectmod < 10) {
if (effectmod < 10)
effectmod = 10;
}
if (effectmod > effectmodcap) {
if (effectmod > effectmodcap)
effectmod = effectmodcap;
}
Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n",
GetName(), spell_id, effectmod, effectmodcap);
Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id,
effectmod, effectmodcap);
return effectmod;
}
+84 -25
View File
@@ -306,6 +306,8 @@ void MapOpcodes()
ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh;
ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve;
ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout;
ConnectedOpcodes[OP_PlayerStateAdd] = &Client::Handle_OP_PlayerStateAdd;
ConnectedOpcodes[OP_PlayerStateRemove] = &Client::Handle_OP_PlayerStateRemove;
ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket;
ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse;
ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt;
@@ -703,7 +705,7 @@ void Client::CompleteConnect()
case SE_AddMeleeProc:
case SE_WeaponProc:
{
AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid);
AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid, buffs[j1].casterlevel);
break;
}
case SE_DefensiveProc:
@@ -1444,22 +1446,32 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; }
/* Initialize AA's : Move to function eventually */
for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ aa[a] = &m_pp.aa_array[a]; }
for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++)
aa[a] = &m_pp.aa_array[a];
query = StringFormat(
"SELECT "
"slot, "
"aa_id, "
"aa_value "
"aa_value, "
"charges "
"FROM "
"`character_alternate_abilities` "
"WHERE `id` = %u ORDER BY `slot`", this->CharacterID());
results = database.QueryDatabase(query); i = 0;
int offset = 0; // offset to fix the hole from expendables
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
i = atoi(row[0]) - offset;
m_pp.aa_array[i].AA = atoi(row[1]);
m_pp.aa_array[i].value = atoi(row[2]);
aa[i]->AA = atoi(row[1]);
aa[i]->value = atoi(row[2]);
m_pp.aa_array[i].charges = atoi(row[3]);
/* A used expendable could cause there to be a "hole" in the array, this is very bad. Bad things like keeping your expendable after use.
We could do a few things, one of them being reshuffling when the hole is created or defer the fixing until a later point, like during load!
Or just never making a hole in the array and just have hacks every where. Fixing the hole at load really just keeps 1 hack in Client::SendAATable
and keeping this offset that will cause the next AA to be pushed back over the hole. We also need to clean up on save so we don't have multiple
entries for a single AA.
*/
if (m_pp.aa_array[i].value == 0)
offset++;
}
for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){
uint32 id = aa[a]->AA;
@@ -1498,7 +1510,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
for (int i = 0; i < max_slots; i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) {
m_pp.buffs[i].spellid = buffs[i].spellid;
m_pp.buffs[i].bard_modifier = 10;
m_pp.buffs[i].bard_modifier = buffs[i].instrument_mod;
m_pp.buffs[i].slotid = 2;
m_pp.buffs[i].player_id = 0x2211;
m_pp.buffs[i].level = buffs[i].casterlevel;
@@ -1707,7 +1719,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Time of Day packet */
outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct));
TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer;
zone->zone_time.getEQTimeOfDay(time(0), tod);
zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod);
outapp->priority = 6;
FastQueuePacket(&outapp);
@@ -4129,7 +4141,7 @@ void Client::Handle_OP_ClickObject(const EQApplicationPacket *app)
char buf[10];
snprintf(buf, 9, "%u", click_object->drop_id);
buf[9] = '\0';
parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args);
parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, GetID(), &args);
}
// Observed in RoF after OP_ClickObjectAction:
@@ -4267,7 +4279,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
if((cur_time - m_TimeSinceLastPositionCheck) > 0)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
@@ -4335,7 +4347,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
if((cur_time - m_TimeSinceLastPositionCheck) > 2500)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
@@ -4448,9 +4460,20 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
// Outgoing client packet
float tmpheading = EQ19toFloat(ppu->heading);
/* The clients send an update at best every 1.3 seconds
* We want to avoid reflecting these updates to other clients as much as possible
* The client also sends an update every 280 ms while turning, if we prevent
* sending these by checking if the location is the same too aggressively, clients end up spinning
* so keep a count of how many packets are the same within a tolerance and stop when we get there */
if (!FCMP(ppu->y_pos, m_Position.y) || !FCMP(ppu->x_pos, m_Position.x) || !FCMP(tmpheading, m_Position.w) || ppu->animation != animation)
bool pos_same = FCMP(ppu->y_pos, m_Position.y) && FCMP(ppu->x_pos, m_Position.x) && FCMP(tmpheading, m_Position.w) && ppu->animation == animation;
if (!pos_same || (pos_same && position_update_same_count < 6))
{
if (pos_same)
position_update_same_count++;
else
position_update_same_count = 0;
m_Position.x = ppu->x_pos;
m_Position.y = ppu->y_pos;
m_Position.z = ppu->z_pos;
@@ -6355,15 +6378,25 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app)
Bot::ProcessBotGroupDisband(this, std::string());
}
else {
Mob* tempMember = entity_list.GetMob(gd->name2);
if (tempMember) {
if (tempMember->IsBot())
Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName()));
Mob* tempMember = entity_list.GetMob(gd->name1); //Name1 is the target you are disbanding
if (tempMember && tempMember->IsBot()) {
tempMember->CastToBot()->RemoveBotFromGroup(tempMember->CastToBot(), group);
if (LFP)
{
// If we are looking for players, update to show we are on our own now.
UpdateLFP();
}
return; //No need to continue from here we were removing a bot from party
}
}
}
}
group = GetGroup();
if (!group) //We must recheck this here.. incase the final bot disbanded the party..otherwise we crash
return;
#endif
if (group->GroupCount() < 3)
{
group->DisbandGroup();
@@ -9705,6 +9738,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
char val1[20] = { 0 };
PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer;
Mob* mypet = this->GetPet();
Mob *target = entity_list.GetMob(pet->target);
if (!mypet || pet->command == PET_LEADER)
{
@@ -9752,22 +9786,22 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
switch (PetCommand)
{
case PET_ATTACK: {
if (!GetTarget())
if (!target)
break;
if (GetTarget()->IsMezzed()) {
Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName());
if (target->IsMezzed()) {
Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), target->GetCleanName());
break;
}
if (mypet->IsFeared())
break; //prevent pet from attacking stuff while feared
if (!mypet->IsAttackAllowed(GetTarget())) {
if (!mypet->IsAttackAllowed(target)) {
mypet->Say_StringID(NOT_LEGAL_TARGET);
break;
}
if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) {
if (GetTarget() != this && DistanceSquaredNoZ(mypet->GetPosition(), GetTarget()->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
if (mypet->IsHeld()) {
if (!mypet->IsFocused()) {
mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack.
@@ -9775,12 +9809,12 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
mypet->SetPetOrder(SPO_Follow);
}
else {
mypet->SetTarget(GetTarget());
mypet->SetTarget(target);
}
}
zone->AddAggroMob();
mypet->AddToHateList(GetTarget(), 1);
Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName());
mypet->AddToHateList(target, 1);
Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), target->GetCleanName());
}
}
break;
@@ -10275,6 +10309,31 @@ void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app)
return;
}
void Client::Handle_OP_PlayerStateAdd(const EQApplicationPacket *app)
{
if (app->size != sizeof(PlayerState_Struct)) {
std::cout << "Wrong size: OP_PlayerStateAdd, size=" << app->size << ", expected " << sizeof(PlayerState_Struct) << std::endl;
return;
}
PlayerState_Struct *ps = (PlayerState_Struct *)app->pBuffer;
AddPlayerState(ps->state);
entity_list.QueueClients(this, app, true);
}
void Client::Handle_OP_PlayerStateRemove(const EQApplicationPacket *app)
{
if (app->size != sizeof(PlayerState_Struct)) {
std::cout << "Wrong size: OP_PlayerStateRemove, size=" << app->size << ", expected " << sizeof(PlayerState_Struct) << std::endl;
return;
}
PlayerState_Struct *ps = (PlayerState_Struct *)app->pBuffer;
RemovePlayerState(ps->state);
entity_list.QueueClients(this, app, true);
}
void Client::Handle_OP_PickPocket(const EQApplicationPacket *app)
{
if (app->size != sizeof(PickPocket_Struct))
@@ -12745,7 +12804,7 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
inspect_buffs = group->GetLeadershipAA(groupAAInspectBuffs);
}
}
if (nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) ||
if (GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs) || nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) ||
(nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) ||
(nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()))
nt->SendBuffsToClient(this);
+2
View File
@@ -218,6 +218,8 @@
void Handle_OP_PetitionRefresh(const EQApplicationPacket *app);
void Handle_OP_PetitionResolve(const EQApplicationPacket *app);
void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app);
void Handle_OP_PlayerStateAdd(const EQApplicationPacket *app);
void Handle_OP_PlayerStateRemove(const EQApplicationPacket *app);
void Handle_OP_PickPocket(const EQApplicationPacket *app);
void Handle_OP_PopupResponse(const EQApplicationPacket *app);
void Handle_OP_PotionBelt(const EQApplicationPacket *app);
+8 -8
View File
@@ -129,7 +129,9 @@ bool Client::Process() {
if(IsTracking() && (GetClientVersion() >= ClientVersion::SoD) && TrackingTimer.Check())
DoTracking();
if(hpupdate_timer.Check())
// SendHPUpdate calls hpupdate_timer.Start so it can delay this timer, so lets not reset with the check
// since the function will anyways
if(hpupdate_timer.Check(false))
SendHPUpdate();
if(mana_timer.Check())
@@ -197,10 +199,8 @@ bool Client::Process() {
instalog = true;
}
if (IsStunned() && stunned_timer.Check()) {
this->stunned = false;
this->stunned_timer.Disable();
}
if (IsStunned() && stunned_timer.Check())
Mob::UnStun();
if(!m_CheatDetectMoved)
{
@@ -262,7 +262,7 @@ bool Client::Process() {
}
if(light_update_timer.Check()) {
UpdateEquipmentLight();
if(UpdateActiveLight()) {
SendAppearancePacket(AT_Light, GetActiveLightType());
@@ -562,7 +562,7 @@ bool Client::Process() {
}
ProjectileAttack();
if(spellbonuses.GravityEffect == 1) {
if(gravity_timer.Check())
DoGravityEffect();
@@ -793,7 +793,7 @@ void Client::OnDisconnect(bool hard_disconnect) {
Mob *Other = trade->With();
if(Other)
{
Log.Out(Logs::Detail, Logs::Trading, "Client disconnected during a trade. Returning their items.");
Log.Out(Logs::Detail, Logs::Trading, "Client disconnected during a trade. Returning their items.");
FinishTrade(this);
if(Other->IsClient())
+27 -27
View File
@@ -1369,7 +1369,7 @@ void command_date(Client *c, const Seperator *sep)
else {
int h=0, m=0;
TimeOfDay_Struct eqTime;
zone->zone_time.getEQTimeOfDay( time(0), &eqTime);
zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime);
if(!sep->IsNumber(4))
h=eqTime.hour;
else
@@ -1402,7 +1402,7 @@ void command_timezone(Client *c, const Seperator *sep)
// Update all clients with new TZ.
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct));
TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer;
zone->zone_time.getEQTimeOfDay(time(0), tod);
zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod);
entity_list.QueueClients(c, outapp);
safe_delete(outapp);
}
@@ -1484,7 +1484,7 @@ void command_npcstats(Client *c, const Seperator *sep)
c->Message(0, "Current HP: %i Max HP: %i", c->GetTarget()->GetHP(), c->GetTarget()->GetMaxHP());
//c->Message(0, "Weapon Item Number: %s", c->GetTarget()->GetWeapNo());
c->Message(0, "Gender: %i Size: %f Bodytype: %d", c->GetTarget()->GetGender(), c->GetTarget()->GetSize(), c->GetTarget()->GetBodyType());
c->Message(0, "Runspeed: %f Walkspeed: %f", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed());
c->Message(0, "Runspeed: %i Walkspeed: %i", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed());
c->Message(0, "Spawn Group: %i Grid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid());
c->Message(0, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetEmoteID());
c->GetTarget()->CastToNPC()->QueryLoot(c);
@@ -4393,7 +4393,7 @@ void command_time(Client *c, const Seperator *sep)
else {
c->Message(13, "To set the Time: #time HH [MM]");
TimeOfDay_Struct eqTime;
zone->zone_time.getEQTimeOfDay( time(0), &eqTime);
zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime);
sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)",
((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12),
(eqTime.minute < 10) ? "0" : "",
@@ -8565,20 +8565,20 @@ void command_object(Client *c, const Seperator *sep)
od.object_type = atoi(row[7]);
icon = atoi(row[8]);
od.unknown008 = atoi(row[9]);
od.unknown010 = atoi(row[10]);
od.size = atoi(row[9]);
od.solidtype = atoi(row[10]);
od.unknown020 = atoi(row[11]);
switch (od.object_type) {
case 0: // Static Object
case staticType: // Static Object unlocked for changes
if (od.unknown008 == 0) // Unknown08 field is optional Size parameter for static objects
od.unknown008 = 100; // Static object default Size is 100%
if (od.size == 0) // Unknown08 field is optional Size parameter for static objects
od.size = 100; // Static object default Size is 100%
c->Message(0, "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, "
"size %u, solidtype %u, incline %u",
(od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z,
od.heading, od.object_name, od.unknown008, od.unknown010, od.unknown020);
od.heading, od.object_name, od.size, od.solidtype, od.unknown020);
break;
case OT_DROPPEDITEM: // Ground Spawn
@@ -8636,10 +8636,10 @@ void command_object(Client *c, const Seperator *sep)
switch (od.object_type) {
case 0: // Static Object
if ((sep->argnum - col) > 3) {
od.unknown008 = atoi(sep->arg[4 + col]); // Size specified
od.size = atoi(sep->arg[4 + col]); // Size specified
if ((sep->argnum - col) > 4) {
od.unknown010 = atoi(sep->arg[5 + col]); // SolidType specified
od.solidtype = atoi(sep->arg[5 + col]); // SolidType specified
if ((sep->argnum - col) > 5)
od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified
@@ -8938,16 +8938,16 @@ void command_object(Client *c, const Seperator *sep)
return;
}
od.unknown008 = atoi(sep->arg[4]);
od.size = atoi(sep->arg[4]);
o->SetObjectData(&od);
if (od.unknown008 == 0) // 0 == unspecified == 100%
od.unknown008 = 100;
if (od.size == 0) // 0 == unspecified == 100%
od.size = 100;
c->Message(0, "Static Object %u set to %u%% size. Size will take effect when you commit to the "
"database with '#object Save', after which the object will be unchangeable until "
"you unlock it again with '#object Edit' and zone out and back in.",
id, od.unknown008);
id, od.size);
} else if (strcmp(sep->arg[3], "solidtype") == 0) {
if (od.object_type != staticType) {
@@ -8962,13 +8962,13 @@ void command_object(Client *c, const Seperator *sep)
return;
}
od.unknown010 = atoi(sep->arg[4]);
od.solidtype = atoi(sep->arg[4]);
o->SetObjectData(&od);
c->Message(0, "Static Object %u set to SolidType %u. Change will take effect when you commit "
"to the database with '#object Save'. Support for this property is on a "
"per-model basis, mostly seen in smaller objects such as chests and tables.",
id, od.unknown010);
id, od.solidtype);
} else if (strcmp(sep->arg[3], "icon") == 0) {
if ((od.object_type < 2) || (od.object_type == staticType)) {
@@ -9255,24 +9255,24 @@ void command_object(Client *c, const Seperator *sep)
"unknown08 = %u, unknown10 = %u, unknown20 = %u "
"WHERE ID = %u",
zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z,
od.heading, od.object_name, od.object_type, icon, od.unknown008,
od.unknown010, od.unknown020, id);
od.heading, od.object_name, od.object_type, icon, od.size,
od.solidtype, od.unknown020, id);
else if (id == 0)
query = StringFormat("INSERT INTO object "
"(zoneid, version, xpos, ypos, zpos, heading, objectname, "
"type, icon, unknown08, unknown10, unknown20) "
"VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)",
zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z,
od.heading, od.object_name, od.object_type, icon, od.unknown008,
od.unknown010, od.unknown020);
od.heading, od.object_name, od.object_type, icon, od.size,
od.solidtype, od.unknown020);
else
query = StringFormat("INSERT INTO object "
"(id, zoneid, version, xpos, ypos, zpos, heading, objectname, "
"type, icon, unknown08, unknown10, unknown20) "
"VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)",
id, zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z,
od.heading, od.object_name, od.object_type, icon, od.unknown008,
od.unknown010, od.unknown020);
od.heading, od.object_name, od.object_type, icon, od.size,
od.solidtype, od.unknown020);
results = database.QueryDatabase(query);
if (!results.Success()) {
@@ -9330,12 +9330,12 @@ void command_object(Client *c, const Seperator *sep)
memcpy(door.dest_zone, "NONE", 5);
if ((door.size = od.unknown008) == 0) // unknown08 = optional size percentage
if ((door.size = od.size) == 0) // unknown08 = optional size percentage
door.size = 100;
switch (
door.opentype =
od.unknown010) // unknown10 = optional request_nonsolid (0 or 1 or experimental number)
od.solidtype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number)
{
case 0:
door.opentype = 31;
@@ -9592,8 +9592,8 @@ void command_object(Client *c, const Seperator *sep)
strn0cpy(od.object_name, row[4], sizeof(od.object_name));
od.object_type = atoi(row[5]);
icon = atoi(row[6]);
od.unknown008 = atoi(row[7]);
od.unknown010 = atoi(row[8]);
od.size = atoi(row[7]);
od.solidtype = atoi(row[8]);
od.unknown020 = atoi(row[9]);
if (od.object_type == 0)
+17 -2
View File
@@ -173,6 +173,18 @@ enum class NumHit { // Numhits type
OffensiveSpellProcs = 11 // Offensive buff procs
};
enum class PlayerState : uint32 {
None = 0,
Open = 1,
WeaponSheathed = 2,
Aggressive = 4,
ForcedAggressive = 8,
InstrumentEquipped = 16,
Stunned = 32,
PrimaryWeaponEquipped = 64,
SecondaryWeaponEquipped = 128
};
//this is our internal representation of the BUFF struct, can put whatever we want in it
struct Buffs_Struct {
uint16 spellid;
@@ -190,6 +202,7 @@ struct Buffs_Struct {
int32 caston_z;
int32 ExtraDIChance;
int16 RootBreakChance; //Not saved to dbase
uint32 instrument_mod;
bool persistant_buff;
bool client; //True if the caster is a client
bool UpdateClient;
@@ -438,7 +451,7 @@ struct StatBonuses {
int32 ShieldEquipHateMod; // Hate mod when shield equiped.
int32 ShieldEquipDmgMod[2]; // Damage mod when shield equiped. 0 = damage modifier 1 = Unknown
bool TriggerOnValueAmount; // Triggers off various different conditions, bool to check if client has effect.
int8 StunBashChance; // chance to stun with bash.
int8 StunBashChance; // chance to stun with bash.
int8 IncreaseChanceMemwipe; // increases chance to memory wipe
int8 CriticalMend; // chance critical monk mend
int32 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy
@@ -455,6 +468,7 @@ typedef struct
uint16 spellID;
uint16 chance;
uint16 base_spellID;
int level_override;
} tProc;
struct Shielders_Struct {
@@ -507,7 +521,8 @@ typedef enum {
petOther,
petCharmed,
petNPCFollow,
petTargetLock //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
petNone = 0xFF // not a pet
} PetType;
typedef enum {
+24 -19
View File
@@ -89,7 +89,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (IsClient() && GetClass() == WIZARD)
ratio += RuleI(Spells, WizCritRatio); //Default is zero
if (Critical){
value = value_BaseEffect*ratio/100;
@@ -172,7 +172,7 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100;
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) +
GetFocusEffect(focusFcDamageAmt, spell_id);
@@ -219,11 +219,11 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s
total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time;
if (total_cast_time > 0 && total_cast_time <= 2500)
extra_spell_amt = extra_spell_amt*25/100;
else if (total_cast_time > 2500 && total_cast_time < 7000)
extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000;
else
extra_spell_amt = extra_spell_amt * total_cast_time / 7000;
extra_spell_amt = extra_spell_amt*25/100;
else if (total_cast_time > 2500 && total_cast_time < 7000)
extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000;
else
extra_spell_amt = extra_spell_amt * total_cast_time / 7000;
if(extra_spell_amt*2 < base_spell_dmg)
return 0;
@@ -281,7 +281,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if (Critical) {
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
OTHER_CRIT_HEAL, GetName(), itoa(value));
if (IsClient())
Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value));
}
@@ -421,16 +421,21 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
int tic_inc = 0;
tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id);
// Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1
// However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync
if (IsClient() && !(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
IsFearSpell(spell_id) ||
IsCharmSpell(spell_id) ||
IsMezSpell(spell_id) ||
IsBlindSpell(spell_id))
tic_inc += 1;
// unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now
// a level 53 bard reported getting 2 tics
// bard DOTs do get this extra tick, but beneficial long bard songs don't? (invul, crescendo)
if ((IsShortDurationBuff(spell_id) || IsDetrimentalSpell(spell_id)) && IsBardSong(spell_id) &&
spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60)
tic_inc++;
float focused = ((duration * increase) / 100.0f) + tic_inc;
int ifocused = static_cast<int>(focused);
return (((duration * increase) / 100) + tic_inc);
// 7.6 is rounded to 7, 8.6 is rounded to 9
// 6 is 6, etc
if (FCMP(focused, ifocused) || ifocused % 2) // equal or odd
return ifocused;
else // even and not equal round to odd
return ifocused + 1;
}
int32 Client::GetActSpellCasttime(uint16 spell_id, int32 casttime)
@@ -771,7 +776,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
}
} else {
if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets)
if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
if (!spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
@@ -859,7 +864,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff
if (!center->CheckLosFN(curmob))
continue;
} else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
// See notes in AESpell() above for more info.
// See notes in AESpell() above for more info.
if (caster->IsAttackAllowed(curmob, true))
continue;
if (caster->CheckAggro(curmob))
+12 -1
View File
@@ -1072,7 +1072,7 @@ void PerlembParser::ExportZoneVariables(std::string &package_name) {
ExportVar(package_name.c_str(), "instanceid", zone->GetInstanceID());
ExportVar(package_name.c_str(), "instanceversion", zone->GetInstanceVersion());
TimeOfDay_Struct eqTime;
zone->zone_time.getEQTimeOfDay( time(0), &eqTime);
zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime);
ExportVar(package_name.c_str(), "zonehour", eqTime.hour - 1);
ExportVar(package_name.c_str(), "zonemin", eqTime.minute);
ExportVar(package_name.c_str(), "zonetime", (eqTime.hour - 1) * 100 + eqTime.minute);
@@ -1277,6 +1277,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
case EVENT_PLAYER_PICKUP:{
ExportVar(package_name.c_str(), "picked_up_id", data);
ExportVar(package_name.c_str(), "picked_up_entity_id", extradata);
break;
}
@@ -1320,6 +1321,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
ExportVar(package_name.c_str(), "itemid", objid);
ExportVar(package_name.c_str(), "itemname", iteminst->GetItem()->Name);
ExportVar(package_name.c_str(), "slotid", extradata);
ExportVar(package_name.c_str(), "spell_id", iteminst->GetItem()->Click.Effect);
break;
}
@@ -1367,6 +1369,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
case EVENT_CLICK_OBJECT: {
ExportVar(package_name.c_str(), "objectid", data);
ExportVar(package_name.c_str(), "clicker_id", extradata);
break;
}
@@ -1397,6 +1400,14 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]);
break;
}
case EVENT_DROP_ITEM: {
ExportVar(package_name.c_str(), "quantity", iteminst->IsStackable() ? iteminst->GetCharges() : 1);
ExportVar(package_name.c_str(), "itemname", iteminst->GetItem()->Name);
ExportVar(package_name.c_str(), "itemid", iteminst->GetItem()->ID);
ExportVar(package_name.c_str(), "spell_id", iteminst->GetItem()->Click.Effect);
ExportVar(package_name.c_str(), "slotid", extradata);
break;
}
default: {
break;
+66 -5
View File
@@ -1182,13 +1182,26 @@ XS(XS__settime);
XS(XS__settime)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: settime(new_hour, new_min)");
if (items < 2)
Perl_croak(aTHX_ "Usage: settime(new_hour, new_min, [update_world = true])");
int new_hour = (int)SvIV(ST(0));
int new_min = (int)SvIV(ST(1));
if (items == 2){
int new_hour = (int)SvIV(ST(0));
int new_min = (int)SvIV(ST(1));
quest_manager.settime(new_hour, new_min, true);
}
else if (items == 3){
int new_hour = (int)SvIV(ST(0));
int new_min = (int)SvIV(ST(1));
quest_manager.settime(new_hour, new_min);
int update_world = (int)SvIV(ST(2));
if (update_world == 1){
quest_manager.settime(new_hour, new_min, true);
}
else{
quest_manager.settime(new_hour, new_min, false);
}
}
XSRETURN_EMPTY;
}
@@ -1917,6 +1930,52 @@ XS(XS__repopzone)
XSRETURN_EMPTY;
}
XS(XS__ConnectNodeToNode);
XS(XS__ConnectNodeToNode)
{
dXSARGS;
if (items != 4)
Perl_croak(aTHX_ "Usage: ConnectNodeToNode(node1, node2, teleport, doorid)");
int node1 = (int)SvIV(ST(0));
int node2 = (int)SvIV(ST(1));
int teleport = (int)SvIV(ST(2));
int doorid = (int)SvIV(ST(3));
quest_manager.ConnectNodeToNode(node1, node2, teleport, doorid);
XSRETURN_EMPTY;
}
XS(XS__AddNode);
XS(XS__AddNode)
{
dXSARGS;
//void QuestManager::AddNode(float x, float y, float z, float best_z, int32 requested_id);
if (items < 3 || items > 5)
Perl_croak(aTHX_ "Usage: AddNode(x, y, z, [best_z], [requested_id])");
int x = (int)SvIV(ST(0));
int y = (int)SvIV(ST(1));
int z = (int)SvIV(ST(2));
int best_z = 0;
int requested_id = 0;
if (items == 4)
{
best_z = (int)SvIV(ST(3));
}
else if (items == 5)
{
best_z = (int)SvIV(ST(3));
requested_id = (int)SvIV(ST(4));
}
quest_manager.AddNode(x, y, z, best_z, requested_id);
XSRETURN_EMPTY;
}
XS(XS__npcrace);
XS(XS__npcrace)
{
@@ -3699,6 +3758,8 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "reloadzonestaticdata"), XS__reloadzonestaticdata, file);
newXS(strcpy(buf, "removetitle"), XS__removetitle, file);
newXS(strcpy(buf, "repopzone"), XS__repopzone, file);
newXS(strcpy(buf, "ConnectNodeToNode"), XS__ConnectNodeToNode, file);
newXS(strcpy(buf, "AddNode"), XS__AddNode, file);
newXS(strcpy(buf, "resettaskactivity"), XS__resettaskactivity, file);
newXS(strcpy(buf, "respawn"), XS__respawn, file);
newXS(strcpy(buf, "resume"), XS__resume, file);
+53
View File
@@ -0,0 +1,53 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef _WINDOWS
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#include "../common/races.h"
#include "encounter.h"
#include "entity.h"
#include "mob.h"
class Zone;
Encounter::Encounter(const char* enc_name)
:Mob
(
nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, glm::vec4(0,0,0,0), 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
)
{
encounter_name[0] = 0;
strn0cpy(encounter_name, enc_name, 64);
remove_me = false;
}
Encounter::~Encounter()
{
}
bool Encounter::Process() {
if (remove_me) return false;
return true;
}
+62
View File
@@ -0,0 +1,62 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef ENCOUNTER_H
#define ENCOUNTER_H
#include "mob.h"
#include "../common/types.h"
#include "../common/timer.h"
class Group;
class Raid;
struct ExtraAttackOptions;
class Encounter : public Mob
{
public:
Encounter(const char* enc_name);
~Encounter();
//abstract virtual function implementations required by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill) { return true; }
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) { return; }
virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr) {
return false;
}
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return false; }
virtual Raid* GetRaid() { return 0; }
virtual Group* GetGroup() { return 0; }
bool IsEncounter() const { return true; }
const char* GetEncounterName() const { return encounter_name; }
bool Process();
virtual void Depop(bool not_used = true) { remove_me = true; }
protected:
bool remove_me;
char encounter_name[64];
private:
};
#endif
+92 -8
View File
@@ -79,7 +79,7 @@ Client *Entity::CastToClient()
}
#ifdef _EQDEBUG
if (!IsClient()) {
Log.Out(Logs::General, Logs::Error, "CastToClient error (not client)");
Log.Out(Logs::General, Logs::Error, "CastToClient error (not client)");
return 0;
}
#endif
@@ -173,6 +173,11 @@ Beacon *Entity::CastToBeacon()
return static_cast<Beacon *>(this);
}
Encounter *Entity::CastToEncounter()
{
return static_cast<Encounter *>(this);
}
const Client *Entity::CastToClient() const
{
if (this == 0x00) {
@@ -263,6 +268,11 @@ const Beacon* Entity::CastToBeacon() const
return static_cast<const Beacon *>(this);
}
const Encounter* Entity::CastToEncounter() const
{
return static_cast<const Encounter *>(this);
}
#ifdef BOTS
Bot *Entity::CastToBot()
{
@@ -533,6 +543,21 @@ void EntityList::BeaconProcess()
}
}
void EntityList::EncounterProcess()
{
auto it = encounter_list.begin();
while (it != encounter_list.end()) {
if (!it->second->Process()) {
safe_delete(it->second);
free_ids.push(it->first);
it = encounter_list.erase(it);
}
else {
++it;
}
}
}
void EntityList::AddGroup(Group *group)
{
if (group == nullptr) //this seems to be happening somehow...
@@ -540,7 +565,7 @@ void EntityList::AddGroup(Group *group)
uint32 gid = worldserver.NextGroupID();
if (gid == 0) {
Log.Out(Logs::General, Logs::Error,
Log.Out(Logs::General, Logs::Error,
"Unable to get new group ID from world server. group is going to be broken.");
return;
}
@@ -569,7 +594,7 @@ void EntityList::AddRaid(Raid *raid)
uint32 gid = worldserver.NextGroupID();
if (gid == 0) {
Log.Out(Logs::General, Logs::Error,
Log.Out(Logs::General, Logs::Error,
"Unable to get new group ID from world server. group is going to be broken.");
return;
}
@@ -618,6 +643,8 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
EQApplicationPacket *app = new EQApplicationPacket;
npc->CreateSpawnPacket(app, npc);
QueueClients(npc, app);
npc->SendArmorAppearance();
npc->SetAppearance(npc->GetGuardPointAnim(),false);
safe_delete(app);
} else {
NewSpawn_Struct *ns = new NewSpawn_Struct;
@@ -706,6 +733,12 @@ void EntityList::AddBeacon(Beacon *beacon)
beacon_list.insert(std::pair<uint16, Beacon *>(beacon->GetID(), beacon));
}
void EntityList::AddEncounter(Encounter *encounter)
{
encounter->SetID(GetFreeID());
encounter_list.insert(std::pair<uint16, Encounter *>(encounter->GetID(), encounter));
}
void EntityList::AddToSpawnQueue(uint16 entityid, NewSpawn_Struct **ns)
{
uint32 count;
@@ -726,10 +759,23 @@ void EntityList::CheckSpawnQueue()
EQApplicationPacket *outapp = 0;
iterator.Reset();
NewSpawn_Struct *ns;
while(iterator.MoreElements()) {
outapp = new EQApplicationPacket;
Mob::CreateSpawnPacket(outapp, iterator.GetData());
ns = iterator.GetData();
Mob::CreateSpawnPacket(outapp, ns);
QueueClients(0, outapp);
auto it = npc_list.find(ns->spawn.spawnId);
if (it == npc_list.end()) {
// We must of despawned, hope that's the reason!
Log.Out(Logs::General, Logs::Error, "Error in EntityList::CheckSpawnQueue: Unable to find NPC for spawnId '%u'", ns->spawn.spawnId);
}
else {
NPC *pnpc = it->second;
pnpc->SendArmorAppearance();
pnpc->SetAppearance(pnpc->GetGuardPointAnim(), false);
}
safe_delete(outapp);
iterator.RemoveCurrent();
}
@@ -920,6 +966,11 @@ Entity *EntityList::GetEntityBeacon(uint16 id)
return beacon_list.count(id) ? beacon_list.at(id) : nullptr;
}
Entity *EntityList::GetEntityEncounter(uint16 id)
{
return encounter_list.count(id) ? encounter_list.at(id) : nullptr;
}
Entity *EntityList::GetID(uint16 get_id)
{
Entity *ent = 0;
@@ -935,6 +986,8 @@ Entity *EntityList::GetID(uint16 get_id)
return ent;
else if ((ent=entity_list.GetEntityBeacon(get_id)) != 0)
return ent;
else if ((ent = entity_list.GetEntityEncounter(get_id)) != 0)
return ent;
else
return 0;
}
@@ -1149,19 +1202,39 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
NewSpawn_Struct ns;
Mob *spawn;
uint32 maxspawns = 100;
EQApplicationPacket *app;
if (maxspawns > mob_list.size())
maxspawns = mob_list.size();
BulkZoneSpawnPacket *bzsp = new BulkZoneSpawnPacket(client, maxspawns);
int32 race=-1;
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
spawn = it->second;
if (spawn && spawn->InZone()) {
if (spawn->IsClient() && (spawn->CastToClient()->GMHideMe(client) ||
spawn->CastToClient()->IsHoveringForRespawn()))
continue;
memset(&ns, 0, sizeof(NewSpawn_Struct));
spawn->FillSpawnStruct(&ns, client);
bzsp->AddSpawn(&ns);
race = spawn->GetRace();
// Illusion races on PCs don't work as a mass spawn
// But they will work as an add_spawn AFTER CLIENT_CONNECTED.
if (spawn->IsClient() && (race == MINOR_ILL_OBJ || race == TREE)) {
app = new EQApplicationPacket;
spawn->CreateSpawnPacket(app);
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
safe_delete(app);
}
else {
memset(&ns, 0, sizeof(NewSpawn_Struct));
spawn->FillSpawnStruct(&ns, client);
bzsp->AddSpawn(&ns);
}
// Despite being sent in the OP_ZoneSpawns packet, the client
// does not display worn armor correctly so display it.
spawn->SendArmorAppearance(client);
}
}
safe_delete(bzsp);
@@ -1355,7 +1428,9 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap
if (c != sender) {
if (Target == sender) {
if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA
if (c->IsRaidGrouped()) {
if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) {
Send = true;
} else if (c->IsRaidGrouped()) {
Raid *raid = c->GetRaid();
if (!raid)
continue;
@@ -3389,6 +3464,15 @@ bool EntityList::IsMobInZone(Mob *who)
}
++it;
}
auto enc_it = encounter_list.begin();
while (enc_it != encounter_list.end()) {
if (enc_it->second == who) {
return true;
}
++enc_it;
}
return false;
}
+8
View File
@@ -30,6 +30,7 @@
#include "position.h"
#include "zonedump.h"
class Encounter;
class Beacon;
class Client;
class Corpse;
@@ -77,6 +78,7 @@ public:
virtual bool IsDoor() const { return false; }
virtual bool IsTrap() const { return false; }
virtual bool IsBeacon() const { return false; }
virtual bool IsEncounter() const { return false; }
virtual bool Process() { return false; }
virtual bool Save() { return true; }
@@ -91,6 +93,7 @@ public:
Doors *CastToDoors();
Trap *CastToTrap();
Beacon *CastToBeacon();
Encounter *CastToEncounter();
const Client *CastToClient() const;
const NPC *CastToNPC() const;
@@ -101,6 +104,7 @@ public:
const Doors *CastToDoors() const;
const Trap *CastToTrap() const;
const Beacon *CastToBeacon() const;
const Encounter *CastToEncounter() const;
inline const uint16& GetID() const { return id; }
inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; }
@@ -203,6 +207,7 @@ public:
void MobProcess();
void TrapProcess();
void BeaconProcess();
void EncounterProcess();
void ProcessMove(Client *c, const glm::vec3& location);
void ProcessMove(NPC *n, float x, float y, float z);
void AddArea(int id, int type, float min_x, float max_x, float min_y, float max_y, float min_z, float max_z);
@@ -228,6 +233,7 @@ public:
void AddDoor(Doors* door);
void AddTrap(Trap* trap);
void AddBeacon(Beacon *beacon);
void AddEncounter(Encounter *encounter);
void AddProximity(NPC *proximity_for);
void Clear();
bool RemoveMob(uint16 delete_id);
@@ -266,6 +272,7 @@ public:
Entity *GetEntityCorpse(uint16 id);
Entity *GetEntityTrap(uint16 id);
Entity *GetEntityBeacon(uint16 id);
Entity *GetEntityEncounter(uint16 id);
Entity *GetEntityMob(const char *name);
Entity *GetEntityCorpse(const char *name);
@@ -448,6 +455,7 @@ private:
std::unordered_map<uint16, Doors *> door_list;
std::unordered_map<uint16, Trap *> trap_list;
std::unordered_map<uint16, Beacon *> beacon_list;
std::unordered_map<uint16, Encounter *> encounter_list;
std::list<NPC *> proximity_list;
std::list<Group *> group_list;
std::list<Raid *> raid_list;
+124 -13
View File
@@ -59,6 +59,97 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level)
return 10;
}
uint32 Client::CalcEXP(uint8 conlevel) {
uint32 in_add_exp = EXP_FORMULA;
if((XPRate != 0))
in_add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));
float totalmod = 1.0;
float zemmod = 1.0;
//get modifiers
if(RuleR(Character, ExpMultiplier) >= 0){
totalmod *= RuleR(Character, ExpMultiplier);
}
if(zone->newzone_data.zone_exp_multiplier >= 0){
zemmod *= zone->newzone_data.zone_exp_multiplier;
}
if(RuleB(Character,UseRaceClassExpBonuses))
{
if(GetBaseRace() == HALFLING){
totalmod *= 1.05;
}
if(GetClass() == ROGUE || GetClass() == WARRIOR){
totalmod *= 1.05;
}
}
if(zone->IsHotzone())
{
totalmod += RuleR(Zone, HotZoneBonus);
}
in_add_exp = uint32(float(in_add_exp) * totalmod * zemmod);
if(RuleB(Character,UseXPConScaling))
{
if (conlevel != 0xFF) {
switch (conlevel)
{
case CON_GREEN:
in_add_exp = 0;
return 0;
case CON_LIGHTBLUE:
in_add_exp = in_add_exp * RuleI(Character, LightBlueModifier)/100;
break;
case CON_BLUE:
in_add_exp = in_add_exp * RuleI(Character, BlueModifier)/100;
break;
case CON_WHITE:
in_add_exp = in_add_exp * RuleI(Character, WhiteModifier)/100;
break;
case CON_YELLOW:
in_add_exp = in_add_exp * RuleI(Character, YellowModifier)/100;
break;
case CON_RED:
in_add_exp = in_add_exp * RuleI(Character, RedModifier)/100;
break;
}
}
}
float aatotalmod = 1.0;
if(zone->newzone_data.zone_exp_multiplier >= 0){
aatotalmod *= zone->newzone_data.zone_exp_multiplier;
}
if(RuleB(Character,UseRaceClassExpBonuses))
{
if(GetBaseRace() == HALFLING){
aatotalmod *= 1.05;
}
if(GetClass() == ROGUE || GetClass() == WARRIOR){
aatotalmod *= 1.05;
}
}
if(RuleB(Zone, LevelBasedEXPMods)){
if(zone->level_exp_mod[GetLevel()].ExpMod){
in_add_exp *= zone->level_exp_mod[GetLevel()].ExpMod;
}
}
return in_add_exp;
}
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
ItemScriptStopReturn();
@@ -78,7 +169,7 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
//figure out how much of this goes to AAs
add_aaxp = add_exp * m_epp.perAA / 100;
//take that ammount away from regular exp
//take that amount away from regular exp
add_exp -= add_aaxp;
float totalmod = 1.0;
@@ -247,12 +338,22 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
Message(13, "Error in Client::SetEXP. EXP not set.");
return; // Must be invalid class/race
}
uint32 i = 0;
uint32 membercount = 0;
if(GetGroup())
{
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (GetGroup()->members[i] != nullptr) {
membercount++;
}
}
}
if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) {
if (isrezzexp)
this->Message_StringID(MT_Experience, REZ_REGAIN);
else{
if(this->IsGrouped())
if(membercount > 1)
this->Message_StringID(MT_Experience, GAIN_GROUPXP);
else if(IsRaidGrouped())
Message_StringID(MT_Experience, GAIN_RAIDEXP);
@@ -268,12 +369,17 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
//this ammount of exp (once these loops complete)
uint16 check_level = GetLevel()+1;
//see if we gained any levels
bool level_increase = true;
int8 level_count = 0;
while (set_exp >= GetEXPForLevel(check_level)) {
check_level++;
if (check_level > 127) { //hard level cap
check_level = 127;
break;
}
level_count++;
if(GetMercID())
UpdateMercLevel();
}
@@ -284,6 +390,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
check_level = 2;
break;
}
level_increase = false;
if(GetMercID())
UpdateMercLevel();
}
@@ -364,17 +471,21 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
if ((GetLevel() != check_level) && !(check_level >= maxlevel)) {
char val1[20]={0};
if (GetLevel() == check_level-1){
Message_StringID(MT_Experience, GAIN_LEVEL,ConvertArray(check_level,val1));
SendLevelAppearance();
/* Message(15, "You have gained a level! Welcome to level %i!", check_level); */
}
if (GetLevel() == check_level){
Message_StringID(MT_Experience, LOSE_LEVEL,ConvertArray(check_level,val1));
/* Message(15, "You lost a level! You are now level %i!", check_level); */
if (level_increase)
{
if (level_count == 1)
Message_StringID(MT_Experience, GAIN_LEVEL, ConvertArray(check_level, val1));
else
Message(15, "Welcome to level %i!", check_level);
if (check_level == RuleI(Character, DeathItemLossLevel))
Message_StringID(15, CORPSE_ITEM_LOST);
if (check_level == RuleI(Character, DeathExpLossLevel))
Message_StringID(15, CORPSE_EXP_LOST);
}
else
Message(15, "Welcome to level %i!", check_level);
Message_StringID(MT_Experience, LOSE_LEVEL, ConvertArray(check_level, val1));
#ifdef BOTS
uint8 myoldlevel = GetLevel();
@@ -594,8 +705,8 @@ void Group::SplitExp(uint32 exp, Mob* other) {
groupmod = 2.16;
else
groupmod = 1.0;
groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier)));
if(membercount > 1 && membercount <= 6)
groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier)));
int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel());
if(conlevel == CON_GREEN)
-23
View File
@@ -123,29 +123,6 @@ void Mob::ProcessFlee()
}
}
float Mob::GetFearSpeed()
{
if (flee_mode) {
//we know ratio < FLEE_HP_RATIO
float speed = GetBaseRunspeed();
float ratio = GetHPRatio();
float multiplier = RuleR(Combat, FleeMultiplier);
if (GetSnaredAmount() > 40)
multiplier = multiplier / 6.0f;
speed = speed * ratio * multiplier / 100;
//NPC will eventually stop. Snares speeds this up.
if (speed < 0.09)
speed = 0.0001f;
return speed;
}
// fear and blind use their normal run speed
return GetRunspeed();
}
void Mob::CalculateNewFearpoint()
{
if(RuleB(Pathing, Fear) && zone->pathing)
+25 -16
View File
@@ -185,26 +185,35 @@ bool Client::CanFish() {
rodPosition.x = m_Position.x + RodLength * sin(HeadingDegrees * M_PI/180.0f);
rodPosition.y = m_Position.y + RodLength * cos(HeadingDegrees * M_PI/180.0f);
rodPosition.z = m_Position.z;
// Do BestZ to find where the line hanging from the rod intersects the water (if it is water).
// and go 1 unit into the water.
glm::vec3 dest;
dest.x = rodPosition.x;
dest.y = rodPosition.y;
dest.z = m_Position.z+10;
rodPosition.z = zone->zonemap->FindBestZ(dest, nullptr) + 4;
bool in_lava = zone->watermap->InLava(rodPosition);
bool in_water = zone->watermap->InWater(rodPosition) || zone->watermap->InVWater(rodPosition);
//Message(0, "Rod is at %4.3f, %4.3f, %4.3f, InWater says %d, InLava says %d", RodX, RodY, RodZ, in_water, in_lava);
if (in_lava) {
Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something?
float bestz = zone->zonemap->FindBestZ(rodPosition, nullptr);
float len = m_Position.z - bestz;
if(len > LineLength || len < 0.0f) {
Message_StringID(MT_Skills, FISHING_LAND);
return false;
}
if((!in_water) || (m_Position.z-rodPosition.z)>LineLength) { //Didn't hit the water OR the water is too far below us
Message_StringID(MT_Skills, FISHING_LAND); //Trying to catch land sharks perhaps?
return false;
float step_size = RuleR(Watermap, FishingLineStepSize);
for(float i = 0.0f; i < len; i += step_size) {
glm::vec3 dest(rodPosition.x, rodPosition.y, m_Position.z - i);
bool in_lava = zone->watermap->InLava(dest);
bool in_water = zone->watermap->InWater(dest) || zone->watermap->InVWater(dest);
if (in_lava) {
Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something?
return false;
}
if(in_water) {
return true;
}
}
Message_StringID(MT_Skills, FISHING_LAND);
return false;
}
return true;
}
+182 -1
View File
@@ -606,7 +606,7 @@ void Client::DropItem(int16 slot_id)
// Take control of item in client inventory
ItemInst *inst = m_inv.PopItem(slot_id);
if(inst) {
int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", 0);
int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", slot_id);
if(i != 0) {
safe_delete(inst);
}
@@ -2199,6 +2199,187 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) {
return true;
}
static bool IsSummonedBagID(uint32 item_id)
{
switch (item_id) {
case 17147: // "Spiritual Prismatic Pack"
case 17303: // "Spirit Pouch"
case 17304: // "Dimensional Pocket"
case 17305: // "Dimensional Hole"
case 17306: // "Glowing Backpack"
case 17307: // "Quiver of Marr"
case 17308: // "Bandoleer of Luclin"
case 17309: // "Pouch of Quellious"
case 17310: // "Phantom Satchel"
case 17510: // "Glowing Chest"
case 17900: // "Grandmaster's Satchel"
case 57260: // "Glowing Backpack"
case 57261: // "Pouch of Quellious"
case 57262: // "Phantom Satchel"
case 60224: // "Faded-Glyph Tablet"
case 95199: // "Beginner Artisan Satchel"
case 95200: // "Apprentice Artisan Satchel"
case 95201: // "Freshman Artisan Satchel"
case 95202: // "Journeyman Artisan Satchel"
case 95203: // "Expert Artisan Satchel"
case 95204: // "Master Artisan Satchel"
//case 96960: // "Artisan Satchel" - no 12-slot disenchanted bags
return true;
default:
return false;
}
}
static uint32 GetDisenchantedBagID(uint8 bag_slots)
{
switch (bag_slots) {
case 4:
return 77772; // "Small Disenchanted Backpack"
case 6:
return 77774; // "Disenchanted Backpack"
case 8:
return 77776; // "Large Disenchanted Backpack"
case 10:
return 77778; // "Huge Disenchanted Backpack"
default:
return 0; // no suitable conversions
}
}
static bool CopyBagContents(ItemInst* new_bag, const ItemInst* old_bag)
{
if (!new_bag || !old_bag) { return false; }
if (new_bag->GetItem()->BagSlots < old_bag->GetItem()->BagSlots) { return false; }
// pre-check for size comparisons
for (auto bag_slot = 0; bag_slot < old_bag->GetItem()->BagSlots; ++bag_slot) {
if (!old_bag->GetItem(bag_slot)) { continue; }
if (old_bag->GetItem(bag_slot)->GetItem()->Size > new_bag->GetItem()->BagSize) {
Log.Out(Logs::General, Logs::Inventory, "Copy Bag Contents: Failure due to %s is larger than size capacity of %s (%i > %i)",
old_bag->GetItem(bag_slot)->GetItem()->Name, new_bag->GetItem()->Name, old_bag->GetItem(bag_slot)->GetItem()->Size, new_bag->GetItem()->BagSize);
return false;
}
}
for (auto bag_slot = 0; bag_slot < old_bag->GetItem()->BagSlots; ++bag_slot) {
if (!old_bag->GetItem(bag_slot)) { continue; }
new_bag->PutItem(bag_slot, *(old_bag->GetItem(bag_slot)));
}
return true;
}
void Client::DisenchantSummonedBags(bool client_update)
{
for (auto slot_id = EmuConstants::GENERAL_BEGIN; slot_id <= EmuConstants::GENERAL_END; ++slot_id) {
auto inst = m_inv[slot_id];
if (!inst) { continue; }
if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; }
if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; }
if (inst->GetTotalItemCount() == 1) { continue; }
auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots);
if (!new_id) { continue; }
auto new_item = database.GetItem(new_id);
if (!new_item) { continue; }
auto new_inst = database.CreateBaseItem(new_item);
if (!new_inst) { continue; }
if (CopyBagContents(new_inst, inst)) {
Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id);
PutItemInInventory(slot_id, *new_inst, client_update);
}
safe_delete(new_inst);
}
for (auto slot_id = EmuConstants::BANK_BEGIN; slot_id <= EmuConstants::BANK_END; ++slot_id) {
auto inst = m_inv[slot_id];
if (!inst) { continue; }
if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; }
if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; }
if (inst->GetTotalItemCount() == 1) { continue; }
auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots);
if (!new_id) { continue; }
auto new_item = database.GetItem(new_id);
if (!new_item) { continue; }
auto new_inst = database.CreateBaseItem(new_item);
if (!new_inst) { continue; }
if (CopyBagContents(new_inst, inst)) {
Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id);
PutItemInInventory(slot_id, *new_inst, client_update);
}
safe_delete(new_inst);
}
for (auto slot_id = EmuConstants::SHARED_BANK_BEGIN; slot_id <= EmuConstants::SHARED_BANK_END; ++slot_id) {
auto inst = m_inv[slot_id];
if (!inst) { continue; }
if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; }
if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; }
if (inst->GetTotalItemCount() == 1) { continue; }
auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots);
if (!new_id) { continue; }
auto new_item = database.GetItem(new_id);
if (!new_item) { continue; }
auto new_inst = database.CreateBaseItem(new_item);
if (!new_inst) { continue; }
if (CopyBagContents(new_inst, inst)) {
Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id);
PutItemInInventory(slot_id, *new_inst, client_update);
}
safe_delete(new_inst);
}
while (!m_inv.CursorEmpty()) {
auto inst = m_inv[MainCursor];
if (!inst) { break; }
if (!IsSummonedBagID(inst->GetItem()->ID)) { break; }
if (inst->GetItem()->ItemClass != ItemClassContainer) { break; }
if (inst->GetTotalItemCount() == 1) { break; }
auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots);
if (!new_id) { break; }
auto new_item = database.GetItem(new_id);
if (!new_item) { break; }
auto new_inst = database.CreateBaseItem(new_item);
if (!new_inst) { break; }
if (CopyBagContents(new_inst, inst)) {
Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, MainCursor);
std::list<ItemInst*> local;
local.push_front(new_inst);
m_inv.PopItem(MainCursor);
safe_delete(inst);
while (!m_inv.CursorEmpty()) {
auto limbo_inst = m_inv.PopItem(MainCursor);
if (limbo_inst == nullptr) { continue; }
local.push_back(limbo_inst);
}
for (auto iter = local.begin(); iter != local.end(); ++iter) {
auto cur_inst = *iter;
if (cur_inst == nullptr) { continue; }
m_inv.PushCursor(*cur_inst);
safe_delete(cur_inst);
}
local.clear();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
else {
safe_delete(new_inst); // deletes disenchanted bag if not used
}
break;
}
}
void Client::RemoveNoRent(bool client_update)
{
for (auto slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; ++slot_id) {
+5 -1
View File
@@ -58,7 +58,7 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
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 {
@@ -332,6 +332,8 @@ void NPC::AddLootDrop(const ItemData *item2, ItemList* itemlist, int16 charges,
CastToMob()->AddProcToWeapon(item2->Proc.Effect, true);
eslot = MaterialPrimary;
if (item2->Damage > 0)
SendAddPlayerState(PlayerState::PrimaryWeaponEquipped);
}
else if (foundslot == MainSecondary
&& (GetOwner() != nullptr || (GetLevel() >= 13 && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) &&
@@ -342,6 +344,8 @@ void NPC::AddLootDrop(const ItemData *item2, ItemList* itemlist, int16 charges,
CastToMob()->AddProcToWeapon(item2->Proc.Effect, true);
eslot = MaterialSecondary;
if (item2->Damage > 0)
SendAddPlayerState(PlayerState::SecondaryWeaponEquipped);
}
else if (foundslot == MainHead) {
eslot = MaterialHead;
+49 -1
View File
@@ -1255,6 +1255,46 @@ void Lua_Client::PlayMP3(std::string file)
self->PlayMP3(file.c_str());
}
void Lua_Client::QuestReward(Lua_Mob target) {
Lua_Safe_Call_Void();
self->QuestReward(target);
}
void Lua_Client::QuestReward(Lua_Mob target, uint32 copper) {
Lua_Safe_Call_Void();
self->QuestReward(target, copper);
}
void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver) {
Lua_Safe_Call_Void();
self->QuestReward(target, copper, silver);
}
void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold) {
Lua_Safe_Call_Void();
self->QuestReward(target, copper, silver, gold);
}
void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum) {
Lua_Safe_Call_Void();
self->QuestReward(target, copper, silver, gold, platinum);
}
void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid) {
Lua_Safe_Call_Void();
self->QuestReward(target, copper, silver, gold, platinum, itemid);
}
void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp) {
Lua_Safe_Call_Void();
self->QuestReward(target, copper, silver, gold, platinum, itemid, exp);
}
void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction) {
Lua_Safe_Call_Void();
self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, faction);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@@ -1504,7 +1544,15 @@ luabind::scope lua_register_client() {
.def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption)
.def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage)
.def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText)
.def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3);
.def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward)
.def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32, bool))&Lua_Client::QuestReward);
}
luabind::scope lua_register_inventory_where() {
+8
View File
@@ -278,6 +278,14 @@ public:
void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg);
void SendColoredText(uint32 type, std::string msg);
void PlayMP3(std::string file);
void QuestReward(Lua_Mob target);
void QuestReward(Lua_Mob target, uint32 copper);
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver);
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold);
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum);
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid);
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp);
void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction);
};
#endif
+13
View File
@@ -0,0 +1,13 @@
#ifdef LUA_EQEMU
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include "lua_encounter.h"
#include "encounter.h"
luabind::scope lua_register_encounter() {
return luabind::class_<Lua_Encounter>("Encounter");
}
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef EQEMU_LUA_ENCOUNTER_H
#define EQEMU_LUA_ENCOUNTER_H
#ifdef LUA_EQEMU
#include "lua_ptr.h"
class Encounter;
namespace luabind {
struct scope;
class object;
}
luabind::scope lua_register_encounter();
class Lua_Encounter : public Lua_Ptr<Encounter>
{
typedef Encounter NativeType;
public:
Lua_Encounter() { SetLuaPtrData(nullptr); }
Lua_Encounter(Encounter *d) { SetLuaPtrData(reinterpret_cast<Encounter*>(d)); }
virtual ~Lua_Encounter() { }
operator Encounter*() {
return reinterpret_cast<Encounter*>(GetLuaPtrData());
}
};
#endif
#endif
+41 -9
View File
@@ -18,6 +18,8 @@
#include "qglobals.h"
#include "../common/timer.h"
#include "../common/eqemu_logsys.h"
#include "encounter.h"
#include "lua_encounter.h"
struct Events { };
struct Factions { };
@@ -34,6 +36,8 @@ struct lua_registered_event {
extern std::map<std::string, std::list<lua_registered_event>> lua_encounter_events_registered;
extern std::map<std::string, bool> lua_encounters_loaded;
extern std::map<std::string, Encounter *> lua_encounters;
extern void MapOpcodes();
extern void ClearMappedOpcode(EmuOpcode op);
@@ -42,19 +46,23 @@ void unregister_event(std::string package_name, std::string name, int evt);
void load_encounter(std::string name) {
if(lua_encounters_loaded.count(name) > 0)
return;
Encounter *enc = new Encounter(name.c_str());
entity_list.AddEncounter(enc);
lua_encounters[name] = enc;
lua_encounters_loaded[name] = true;
parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0);
parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, "", 0);
}
void load_encounter_with_data(std::string name, std::string info_str) {
if(lua_encounters_loaded.count(name) > 0)
return;
Encounter *enc = new Encounter(name.c_str());
entity_list.AddEncounter(enc);
lua_encounters[name] = enc;
lua_encounters_loaded[name] = true;
std::vector<EQEmu::Any> info_ptrs;
info_ptrs.push_back(&info_str);
parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0, &info_ptrs);
parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, "", 0, &info_ptrs);
}
void unload_encounter(std::string name) {
@@ -80,8 +88,10 @@ void unload_encounter(std::string name) {
}
}
lua_encounters[name]->Depop();
lua_encounters.erase(name);
lua_encounters_loaded.erase(name);
parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0);
parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, "", 0);
}
void unload_encounter_with_data(std::string name, std::string info_str) {
@@ -109,10 +119,12 @@ void unload_encounter_with_data(std::string name, std::string info_str) {
}
}
lua_encounters[name]->Depop();
lua_encounters.erase(name);
lua_encounters_loaded.erase(name);
std::vector<EQEmu::Any> info_ptrs;
info_ptrs.push_back(&info_str);
parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0, &info_ptrs);
parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, "", 0, &info_ptrs);
}
void register_event(std::string package_name, std::string name, int evt, luabind::adl::object func) {
@@ -285,6 +297,10 @@ void lua_set_timer(const char *timer, int time_ms, Lua_Mob mob) {
quest_manager.settimerMS(timer, time_ms, mob);
}
void lua_set_timer(const char *timer, int time_ms, Lua_Encounter enc) {
quest_manager.settimerMS(timer, time_ms, enc);
}
void lua_stop_timer(const char *timer) {
quest_manager.stoptimer(timer);
}
@@ -297,6 +313,10 @@ void lua_stop_timer(const char *timer, Lua_Mob mob) {
quest_manager.stoptimer(timer, mob);
}
void lua_stop_timer(const char *timer, Lua_Encounter enc) {
quest_manager.stoptimer(timer, enc);
}
void lua_stop_all_timers() {
quest_manager.stopalltimers();
}
@@ -309,6 +329,10 @@ void lua_stop_all_timers(Lua_Mob mob) {
quest_manager.stopalltimers(mob);
}
void lua_stop_all_timers(Lua_Encounter enc) {
quest_manager.stopalltimers(enc);
}
void lua_depop() {
quest_manager.depop(0);
}
@@ -386,7 +410,11 @@ void lua_create_guild(const char *name, const char *leader) {
}
void lua_set_time(int hour, int min) {
quest_manager.settime(hour, min);
quest_manager.settime(hour, min, true);
}
void lua_set_time(int hour, int min, bool update_world) {
quest_manager.settime(hour, min, update_world);
}
void lua_signal(int npc_id, int signal_id) {
@@ -979,7 +1007,7 @@ int lua_get_zone_weather() {
luabind::adl::object lua_get_zone_time(lua_State *L) {
TimeOfDay_Struct eqTime;
zone->zone_time.getEQTimeOfDay(time(0), &eqTime);
zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime);
luabind::adl::object ret = luabind::newtable(L);
ret["zone_hour"] = eqTime.hour - 1;
@@ -1442,12 +1470,15 @@ luabind::scope lua_register_general() {
luabind::def("set_timer", (void(*)(const char*, int))&lua_set_timer),
luabind::def("set_timer", (void(*)(const char*, int, Lua_ItemInst))&lua_set_timer),
luabind::def("set_timer", (void(*)(const char*, int, Lua_Mob))&lua_set_timer),
luabind::def("set_timer", (void(*)(const char*, int, Lua_Encounter))&lua_set_timer),
luabind::def("stop_timer", (void(*)(const char*))&lua_stop_timer),
luabind::def("stop_timer", (void(*)(const char*, Lua_ItemInst))&lua_stop_timer),
luabind::def("stop_timer", (void(*)(const char*, Lua_Mob))&lua_stop_timer),
luabind::def("stop_timer", (void(*)(const char*, Lua_Encounter))&lua_stop_timer),
luabind::def("stop_all_timers", (void(*)(void))&lua_stop_all_timers),
luabind::def("stop_all_timers", (void(*)(Lua_ItemInst))&lua_stop_all_timers),
luabind::def("stop_all_timers", (void(*)(Lua_Mob))&lua_stop_all_timers),
luabind::def("stop_all_timers", (void(*)(Lua_Encounter))&lua_stop_all_timers),
luabind::def("depop", (void(*)(void))&lua_depop),
luabind::def("depop", (void(*)(int))&lua_depop),
luabind::def("depop_with_timer", (void(*)(void))&lua_depop_with_timer),
@@ -1467,7 +1498,8 @@ luabind::scope lua_register_general() {
luabind::def("set_sky", &lua_set_sky),
luabind::def("set_guild", &lua_set_guild),
luabind::def("create_guild", &lua_create_guild),
luabind::def("set_time", &lua_set_time),
luabind::def("set_time", (void(*)(int, int))&lua_set_time),
luabind::def("set_time", (void(*)(int, int, bool))&lua_set_time),
luabind::def("signal", (void(*)(int,int))&lua_signal),
luabind::def("signal", (void(*)(int,int,int))&lua_signal),
luabind::def("set_global", &lua_set_global),
+12 -25
View File
@@ -1590,26 +1590,6 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
beard, aa_title, drakkin_heritage, drakkin_tattoo, drakkin_details, size);
}
void Lua_Mob::QuestReward(Lua_Client c) {
Lua_Safe_Call_Void();
self->QuestReward(c);
}
void Lua_Mob::QuestReward(Lua_Client c, uint32 silver) {
Lua_Safe_Call_Void();
self->QuestReward(c, silver);
}
void Lua_Mob::QuestReward(Lua_Client c, uint32 silver, uint32 gold) {
Lua_Safe_Call_Void();
self->QuestReward(c, silver, gold);
}
void Lua_Mob::QuestReward(Lua_Client c, uint32 silver, uint32 gold, uint32 platinum) {
Lua_Safe_Call_Void();
self->QuestReward(c, silver, gold, platinum);
}
void Lua_Mob::CameraEffect(uint32 duration, uint32 intensity) {
Lua_Safe_Call_Void();
self->CameraEffect(duration, intensity);
@@ -1651,6 +1631,11 @@ void Lua_Mob::TempName(const char *newname) {
self->TempName(newname);
}
std::string Lua_Mob::GetGlobal(const char *varname) {
Lua_Safe_Call_String();
return self->GetGlobal(varname);
}
void Lua_Mob::SetGlobal(const char *varname, const char *newvalue, int options, const char *duration) {
Lua_Safe_Call_Void();
self->SetGlobal(varname, newvalue, options, duration);
@@ -1866,6 +1851,10 @@ int Lua_Mob::CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite
return self->CanBuffStack(spell_id, caster_level, fail_if_overwrite);
}
void Lua_Mob::SetPseudoRoot(bool in) {
Lua_Safe_Call_Void();
self->SetPseudoRoot(in);
}
luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
@@ -2132,10 +2121,6 @@ luabind::scope lua_register_mob() {
.def("SetRace", (void(Lua_Mob::*)(int))&Lua_Mob::SetRace)
.def("SetGender", (void(Lua_Mob::*)(int))&Lua_Mob::SetGender)
.def("SendIllusionPacket", (void(Lua_Mob::*)(luabind::adl::object))&Lua_Mob::SendIllusionPacket)
.def("QuestReward", (void(Lua_Mob::*)(Lua_Client))&Lua_Mob::QuestReward)
.def("QuestReward", (void(Lua_Mob::*)(Lua_Client,uint32))&Lua_Mob::QuestReward)
.def("QuestReward", (void(Lua_Mob::*)(Lua_Client,uint32,uint32))&Lua_Mob::QuestReward)
.def("QuestReward", (void(Lua_Mob::*)(Lua_Client,uint32,uint32,uint32))&Lua_Mob::QuestReward)
.def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32))&Lua_Mob::CameraEffect)
.def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client))&Lua_Mob::CameraEffect)
.def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client,bool))&Lua_Mob::CameraEffect)
@@ -2144,6 +2129,7 @@ luabind::scope lua_register_mob() {
.def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32,bool,Lua_Client))&Lua_Mob::SendSpellEffect)
.def("TempName", (void(Lua_Mob::*)(void))&Lua_Mob::TempName)
.def("TempName", (void(Lua_Mob::*)(const char*))&Lua_Mob::TempName)
.def("GetGlobal", (std::string(Lua_Mob::*)(const char*))&Lua_Mob::GetGlobal)
.def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*))&Lua_Mob::SetGlobal)
.def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*,Lua_Mob))&Lua_Mob::SetGlobal)
.def("TarGlobal", (void(Lua_Mob::*)(const char*,const char*,const char*,int,int,int))&Lua_Mob::TarGlobal)
@@ -2186,7 +2172,8 @@ luabind::scope lua_register_mob() {
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot)
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot)
.def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack)
.def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack);
.def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack)
.def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot);
}
luabind::scope lua_register_special_abilities() {
+3 -5
View File
@@ -169,7 +169,7 @@ public:
bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost);
bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot);
bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer, int timer_duration);
bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer, int timer_duration,
bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer, int timer_duration,
int resist_adjust);
bool SpellFinished(int spell_id, Lua_Mob target);
bool SpellFinished(int spell_id, Lua_Mob target, int slot);
@@ -296,10 +296,6 @@ public:
void SetRace(int in);
void SetGender(int in);
void SendIllusionPacket(luabind::adl::object illusion);
void QuestReward(Lua_Client c);
void QuestReward(Lua_Client c, uint32 silver);
void QuestReward(Lua_Client c, uint32 silver, uint32 gold);
void QuestReward(Lua_Client c, uint32 silver, uint32 gold, uint32 platinum);
void CameraEffect(uint32 duration, uint32 intensity);
void CameraEffect(uint32 duration, uint32 intensity, Lua_Client c);
void CameraEffect(uint32 duration, uint32 intensity, Lua_Client c, bool global);
@@ -311,6 +307,7 @@ public:
uint32 unk020, bool perm_effect, Lua_Client c);
void TempName();
void TempName(const char *newname);
std::string GetGlobal(const char *varname);
void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration);
void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Lua_Mob other);
void TarGlobal(const char *varname, const char *value, const char *duration, int npc_id, int char_id, int zone_id);
@@ -355,6 +352,7 @@ public:
void BuffFadeBySlot(int slot, bool recalc_bonuses);
int CanBuffStack(int spell_id, int caster_level);
int CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite);
void SetPseudoRoot(bool in);
};
#endif
+3 -3
View File
@@ -18,7 +18,7 @@ Lua_Packet::Lua_Packet(int opcode, int size, bool raw) {
if(raw) {
SetLuaPtrData(new EQApplicationPacket(OP_Unknown, size));
owned_ = true;
EQApplicationPacket *self = reinterpret_cast<EQApplicationPacket*>(d_);
self->SetOpcodeBypass(opcode);
} else {
@@ -692,8 +692,8 @@ luabind::scope lua_register_packet_opcodes() {
luabind::value("VetClaimRequest", static_cast<int>(OP_VetClaimRequest)),
luabind::value("VetClaimReply", static_cast<int>(OP_VetClaimReply)),
luabind::value("WeaponEquip1", static_cast<int>(OP_WeaponEquip1)),
luabind::value("WeaponEquip2", static_cast<int>(OP_WeaponEquip2)),
luabind::value("WeaponUnequip2", static_cast<int>(OP_WeaponUnequip2)),
luabind::value("PlayerStateAdd", static_cast<int>(OP_PlayerStateAdd)),
luabind::value("PlayerStateRemove", static_cast<int>(OP_PlayerStateRemove)),
luabind::value("WorldLogout", static_cast<int>(OP_WorldLogout)),
luabind::value("SessionReady", static_cast<int>(OP_SessionReady)),
luabind::value("Login", static_cast<int>(OP_Login)),
+21 -9
View File
@@ -34,6 +34,7 @@
#include "questmgr.h"
#include "zone.h"
#include "lua_parser.h"
#include "lua_encounter.h"
const char *LuaEvents[_LargestEventID] = {
"event_say",
@@ -128,6 +129,7 @@ struct lua_registered_event {
std::map<std::string, std::list<lua_registered_event>> lua_encounter_events_registered;
std::map<std::string, bool> lua_encounters_loaded;
std::map<std::string, Encounter *> lua_encounters;
LuaParser::LuaParser() {
for(int i = 0; i < _LargestEventID; ++i) {
@@ -135,6 +137,7 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[i] = handle_player_null;
ItemArgumentDispatch[i] = handle_item_null;
SpellArgumentDispatch[i] = handle_spell_null;
EncounterArgumentDispatch[i] = handle_encounter_null;
}
NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say;
@@ -213,6 +216,10 @@ LuaParser::LuaParser() {
SpellArgumentDispatch[EVENT_SPELL_FADE] = handle_spell_fade;
SpellArgumentDispatch[EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE] = handle_translocate_finish;
EncounterArgumentDispatch[EVENT_TIMER] = handle_encounter_timer;
EncounterArgumentDispatch[EVENT_ENCOUNTER_LOAD] = handle_encounter_load;
EncounterArgumentDispatch[EVENT_ENCOUNTER_UNLOAD] = handle_encounter_unload;
L = nullptr;
}
@@ -575,7 +582,7 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc,
return 0;
}
int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, std::vector<EQEmu::Any> *extra_pointers) {
int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector<EQEmu::Any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
@@ -587,10 +594,10 @@ int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint
return 0;
}
return _EventEncounter(package_name, evt, encounter_name, extra_data, extra_pointers);
return _EventEncounter(package_name, evt, encounter_name, data, extra_data, extra_pointers);
}
int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data,
int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) {
const char *sub_name = LuaEvents[evt];
@@ -604,13 +611,12 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::
lua_pushstring(L, encounter_name.c_str());
lua_setfield(L, -2, "name");
if(extra_pointers) {
std::string *str = EQEmu::any_cast<std::string*>(extra_pointers->at(0));
lua_pushstring(L, str->c_str());
lua_setfield(L, -2, "data");
}
Encounter *enc = lua_encounters[encounter_name];
quest_manager.StartQuest(nullptr, nullptr, nullptr, encounter_name);
auto arg_function = EncounterArgumentDispatch[evt];
arg_function(this, L, enc, data, extra_data, extra_pointers);
quest_manager.StartQuest(enc, nullptr, nullptr, encounter_name);
if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
@@ -786,6 +792,11 @@ void LuaParser::ReloadQuests() {
lua_encounter_events_registered.clear();
lua_encounters_loaded.clear();
for (auto encounter : lua_encounters) {
encounter.second->Depop();
}
lua_encounters.clear();
if(L) {
lua_close(L);
}
@@ -968,6 +979,7 @@ void LuaParser::MapFunctions(lua_State *L) {
lua_register_client_version(),
lua_register_appearance(),
lua_register_entity(),
lua_register_encounter(),
lua_register_mob(),
lua_register_special_abilities(),
lua_register_npc(),
+4 -2
View File
@@ -39,7 +39,7 @@ public:
std::vector<EQEmu::Any> *extra_pointers);
virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data,
virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt);
@@ -82,7 +82,7 @@ private:
uint32 extra_data, std::vector<EQEmu::Any> *extra_pointers, luabind::adl::object *l_func = nullptr);
int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers, luabind::adl::object *l_func = nullptr);
int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data,
int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
void LoadScript(std::string filename, std::string package_name);
@@ -99,6 +99,8 @@ private:
PlayerArgumentHandler PlayerArgumentDispatch[_LargestEventID];
ItemArgumentHandler ItemArgumentDispatch[_LargestEventID];
SpellArgumentHandler SpellArgumentDispatch[_LargestEventID];
EncounterArgumentHandler EncounterArgumentDispatch[_LargestEventID];
};
#endif
+36
View File
@@ -22,6 +22,7 @@
#include "lua_door.h"
#include "lua_object.h"
#include "lua_packet.h"
#include "lua_encounter.h"
#include "zone.h"
#include "lua_parser_events.h"
@@ -704,4 +705,39 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl
std::vector<EQEmu::Any> *extra_pointers) {
}
void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) {
lua_pushstring(L, data.c_str());
lua_setfield(L, -2, "timer");
}
void handle_encounter_load(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) {
if (encounter) {
Lua_Encounter l_enc(encounter);
luabind::adl::object l_enc_o = luabind::adl::object(L, l_enc);
l_enc_o.push(L);
lua_setfield(L, -2, "encounter");
}
if (extra_pointers) {
std::string *str = EQEmu::any_cast<std::string*>(extra_pointers->at(0));
lua_pushstring(L, str->c_str());
lua_setfield(L, -2, "data");
}
}
void handle_encounter_unload(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) {
if (extra_pointers) {
std::string *str = EQEmu::any_cast<std::string*>(extra_pointers->at(0));
lua_pushstring(L, str->c_str());
lua_setfield(L, -2, "data");
}
}
void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) {
}
#endif
+12
View File
@@ -6,6 +6,7 @@ typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std::
typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector<EQEmu::Any>*);
typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, ItemInst*, Mob*, std::string, uint32, std::vector<EQEmu::Any>*);
typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector<EQEmu::Any>*);
typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<EQEmu::Any>*);
//NPC
void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data,
@@ -127,5 +128,16 @@ void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Cl
void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
//Encounter
void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
void handle_encounter_load(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
void handle_encounter_unload(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers);
#endif
#endif
+36 -1
View File
@@ -64,7 +64,7 @@ Map::~Map() {
float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const {
if (!imp)
return false;
return BEST_Z_INVALID;
glm::vec3 tmp;
if(!result)
@@ -93,6 +93,41 @@ float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const {
return BEST_Z_INVALID;
}
float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const {
// Unlike FindBestZ, this method finds the closest Z value above or below the specified point.
//
if (!imp)
return false;
float ClosestZ = BEST_Z_INVALID;
glm::vec3 tmp;
if (!result)
result = &tmp;
glm::vec3 from(start.x, start.y, start.z);
glm::vec3 to(start.x, start.y, BEST_Z_INVALID);
float hit_distance;
bool hit = false;
// first check is below us
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
if (hit) {
ClosestZ = result->z;
}
// Find nearest Z above us
to.z = -BEST_Z_INVALID;
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
if (hit) {
if (abs(from.z - result->z) < abs(ClosestZ - from.z))
return result->z;
}
return ClosestZ;
}
bool Map::LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const {
if(!imp)
return false;
+1
View File
@@ -34,6 +34,7 @@ public:
~Map();
float FindBestZ(glm::vec3 &start, glm::vec3 *result) const;
float FindClosestZ(glm::vec3 &start, glm::vec3 *result) const;
bool LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const;
bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const;
bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const;
+12 -13
View File
@@ -1236,10 +1236,7 @@ void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
bool Merc::Process()
{
if(IsStunned() && stunned_timer.Check())
{
this->stunned = false;
this->stunned_timer.Disable();
}
Mob::UnStun();
if (GetDepop())
{
@@ -1463,14 +1460,16 @@ void Merc::AI_Process() {
if(moved) {
moved = false;
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
}
return;
}
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
SendAddPlayerState(PlayerState::Aggressive);
bool atCombatRange = false;
float meleeDistance = GetMaxMeleeRangeToTarget(GetTarget());
@@ -1497,9 +1496,7 @@ void Merc::AI_Process() {
SetRunAnimSpeed(0);
if(moved) {
moved = false;
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
}
@@ -1684,6 +1681,9 @@ void Merc::AI_Process() {
confidence_timer.Disable();
_check_confidence = false;
if (m_PlayerState & static_cast<uint32>(PlayerState::Aggressive))
SendRemovePlayerState(PlayerState::Aggressive);
if(!check_target_timer.Enabled())
check_target_timer.Start(2000, false);
@@ -1707,7 +1707,7 @@ void Merc::AI_Process() {
if(follow)
{
float dist = DistanceSquared(m_Position, follow->GetPosition());
float speed = GetRunspeed();
int speed = GetRunspeed();
if(dist < GetFollowDistance() + 1000)
speed = GetWalkspeed();
@@ -1724,9 +1724,8 @@ void Merc::AI_Process() {
{
if(moved)
{
moved=false;
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
moved = false;
}
}
}
+364 -95
View File
@@ -26,6 +26,7 @@
#include <limits.h>
#include <math.h>
#include <sstream>
#include <algorithm>
#ifdef BOTS
#include "bot.h"
@@ -149,6 +150,29 @@ Mob::Mob(const char* in_name,
size = in_size;
base_size = size;
runspeed = in_runspeed;
// neotokyo: sanity check
if (runspeed < 0 || runspeed > 20)
runspeed = 1.25f;
base_runspeed = (int)((float)runspeed * 40.0f);
// clients
if (runspeed == 0.7f) {
base_runspeed = 28;
walkspeed = 0.3f;
base_walkspeed = 12;
fearspeed = 0.625f;
base_fearspeed = 25;
// npcs
} else {
base_walkspeed = base_runspeed * 100 / 265;
walkspeed = ((float)base_walkspeed) * 0.025f;
base_fearspeed = base_runspeed * 100 / 127;
fearspeed = ((float)base_fearspeed) * 0.025f;
}
current_speed = base_runspeed;
m_PlayerState = 0;
// sanity check
@@ -161,7 +185,7 @@ Mob::Mob(const char* in_name,
m_Light.Level.Spell = m_Light.Type.Spell = 0;
m_Light.Type.Active = m_Light.Type.Innate;
m_Light.Level.Active = m_Light.Level.Innate;
texture = in_texture;
helmtexture = in_helmtexture;
armtexture = in_armtexture;
@@ -240,15 +264,19 @@ Mob::Mob(const char* in_name,
PermaProcs[j].spellID = SPELL_UNKNOWN;
PermaProcs[j].chance = 0;
PermaProcs[j].base_spellID = SPELL_UNKNOWN;
PermaProcs[j].level_override = -1;
SpellProcs[j].spellID = SPELL_UNKNOWN;
SpellProcs[j].chance = 0;
SpellProcs[j].base_spellID = SPELL_UNKNOWN;
SpellProcs[j].level_override = -1;
DefensiveProcs[j].spellID = SPELL_UNKNOWN;
DefensiveProcs[j].chance = 0;
DefensiveProcs[j].base_spellID = SPELL_UNKNOWN;
DefensiveProcs[j].level_override = -1;
RangedProcs[j].spellID = SPELL_UNKNOWN;
RangedProcs[j].chance = 0;
RangedProcs[j].base_spellID = SPELL_UNKNOWN;
RangedProcs[j].level_override = -1;
}
for (i = 0; i < _MaterialCount; i++)
@@ -311,7 +339,7 @@ Mob::Mob(const char* in_name,
pLastChange = 0;
SetPetID(0);
SetOwnerID(0);
typeofpet = petCharmed; //default to charmed...
typeofpet = petNone; // default to not a pet
petpower = 0;
held = false;
nocast = false;
@@ -531,48 +559,32 @@ bool Mob::IsInvisible(Mob* other) const
return(false);
}
float Mob::_GetMovementSpeed(int mod) const
{
// List of movement speed modifiers, including AAs & spells:
// http://everquest.allakhazam.com/db/item.html?item=1721;page=1;howmany=50#m10822246245352
if (IsRooted())
return 0.0f;
int Mob::_GetWalkSpeed() const {
if (IsRooted() || IsStunned() || IsMezzed())
return 0;
else if (IsPseudoRooted())
return 0.00001f;
return 0;
float speed_mod = runspeed;
int aa_mod = 0;
int speed_mod = base_walkspeed;
int base_run = base_runspeed;
bool has_horse = false;
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
aa_mod += aabonuses.BaseMovementSpeed;
// These two cases ignore the cap, be wise in the DB for horses.
if (IsClient()) {
if (CastToClient()->GetGMSpeed()) {
speed_mod = 3.125f;
if (mod != 0)
speed_mod += speed_mod * static_cast<float>(mod) / 100.0f;
Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId());
if (horse) {
speed_mod = horse->GetBaseRunspeed();
return speed_mod;
} else {
Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId());
if (horse) {
speed_mod = horse->GetBaseRunspeed();
if (mod != 0)
speed_mod += speed_mod * static_cast<float>(mod) / 100.0f;
return speed_mod;
}
}
}
int aa_mod = 0;
int spell_mod = 0;
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
int movemod = 0;
float frunspeedcap = 0.0f;
runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
aa_mod += itembonuses.BaseMovementSpeed + spellbonuses.BaseMovementSpeed + aabonuses.BaseMovementSpeed;
spell_mod += spellbonuses.movementspeed + itembonuses.movementspeed;
// hard cap
if (runspeedcap > 225)
runspeedcap = 225;
if (spell_mod < 0)
movemod += spell_mod;
@@ -581,27 +593,189 @@ float Mob::_GetMovementSpeed(int mod) const
else
movemod = aa_mod;
// cap negative movemods from snares mostly
if (movemod < -85)
// hard cap
if (runspeedcap > 225)
runspeedcap = 225;
if(movemod < -85) //cap it at moving very very slow
movemod = -85;
if (movemod != 0)
speed_mod += speed_mod * static_cast<float>(movemod) / 100.0f;
if (!has_horse && movemod != 0)
speed_mod += (base_run * movemod / 100);
// runspeed caps
frunspeedcap = static_cast<float>(runspeedcap) / 100.0f;
if (IsClient() && speed_mod > frunspeedcap)
speed_mod = frunspeedcap;
if(speed_mod < 1)
return(0);
// apply final mod such as the -47 for walking
// use runspeed since it should stack with snares
// and if we get here, we know runspeed was the initial
// value before we applied movemod.
if (mod != 0)
speed_mod += runspeed * static_cast<float>(mod) / 100.0f;
//runspeed cap.
if(IsClient())
{
if(speed_mod > runspeedcap)
speed_mod = runspeedcap;
}
return speed_mod;
}
if (speed_mod <= 0.0f)
speed_mod = IsClient() ? 0.0001f : 0.0f;
int Mob::_GetRunSpeed() const {
if (IsRooted() || IsStunned() || IsMezzed() || IsPseudoRooted())
return 0;
int aa_mod = 0;
int speed_mod = base_runspeed;
int base_walk = base_walkspeed;
bool has_horse = false;
if (IsClient())
{
if(CastToClient()->GetGMSpeed())
{
speed_mod = 325;
}
else
{
Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId());
if(horse)
{
speed_mod = horse->GetBaseRunspeed();
base_walk = horse->GetBaseWalkspeed();
has_horse = true;
}
}
}
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
aa_mod = itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
int movemod = 0;
if(spell_mod < 0)
{
movemod += spell_mod;
}
else if(spell_mod > aa_mod)
{
movemod = spell_mod;
}
else
{
movemod = aa_mod;
}
if(movemod < -85) //cap it at moving very very slow
movemod = -85;
if (!has_horse && movemod != 0)
{
if (IsClient())
{
speed_mod += (speed_mod * movemod / 100);
} else {
if (movemod < 0) {
speed_mod += (50 * movemod / 100);
// basically stoped
if(speed_mod < 1)
{
return(0);
}
// moving slowly
if (speed_mod < 8)
return(8);
} else {
speed_mod += GetBaseWalkspeed();
if (movemod > 50)
speed_mod += 4;
if (movemod > 40)
speed_mod += 3;
}
}
}
if(speed_mod < 1)
{
return(0);
}
//runspeed cap.
if(IsClient())
{
if(speed_mod > runspeedcap)
speed_mod = runspeedcap;
}
return speed_mod;
}
int Mob::_GetFearSpeed() const {
if (IsRooted() || IsStunned() || IsMezzed())
return 0;
//float speed_mod = fearspeed;
int speed_mod = GetBaseFearSpeed();
// use a max of 1.75f in calcs.
int base_run = std::min(GetBaseRunspeed(), 70);
int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
int movemod = 0;
if(spell_mod < 0)
{
movemod += spell_mod;
}
if(movemod < -85) //cap it at moving very very slow
movemod = -85;
if (IsClient()) {
if (CastToClient()->IsRunning())
speed_mod = GetBaseRunspeed();
else
speed_mod = GetBaseWalkspeed();
if (movemod < 0)
return GetBaseWalkspeed();
speed_mod += (base_run * movemod / 100);
return speed_mod;
} else {
int hp_ratio = GetIntHPRatio();
// very large snares 50% or higher
if (movemod < -49)
{
if (hp_ratio < 25)
{
return (0);
}
if (hp_ratio < 50)
return (8);
else
return (12);
}
if (hp_ratio < 5) {
speed_mod = base_walkspeed / 3;
} else if (hp_ratio < 15) {
speed_mod = base_walkspeed / 2;
} else if (hp_ratio < 25) {
speed_mod = base_walkspeed + 1; // add the +1 so they do the run animation
} else if (hp_ratio < 50) {
speed_mod *= 82;
speed_mod /= 100;
}
if (movemod > 0) {
speed_mod += GetBaseWalkspeed();
if (movemod > 50)
speed_mod += 4;
if (movemod > 40)
speed_mod += 3;
return speed_mod;
}
else if (movemod < 0) {
speed_mod += (base_run * movemod / 100);
}
}
if (speed_mod < 1)
return (0);
if (speed_mod < 9)
return (8);
if (speed_mod < 13)
return (12);
return speed_mod;
}
@@ -740,7 +914,7 @@ void Mob::CreateSpawnPacket(EQApplicationPacket* app, Mob* ForWho) {
NewSpawn_Struct* ns = (NewSpawn_Struct*)app->pBuffer;
FillSpawnStruct(ns, ForWho);
if(strlen(ns->spawn.lastName) == 0)
if(strlen(ns->spawn.lastName) == 0)
{
switch(ns->spawn.class_)
{
@@ -916,6 +1090,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
ns->spawn.class_ = class_;
ns->spawn.gender = gender;
ns->spawn.level = level;
ns->spawn.PlayerState = m_PlayerState;
ns->spawn.deity = deity;
ns->spawn.animation = 0;
ns->spawn.findable = findable?1:0;
@@ -1092,7 +1267,7 @@ void Mob::CreateHPPacket(EQApplicationPacket* app)
}
// sends hp update of this mob to people who might care
void Mob::SendHPUpdate()
void Mob::SendHPUpdate(bool skip_self)
{
EQApplicationPacket hp_app;
Group *group;
@@ -1181,8 +1356,7 @@ void Mob::SendHPUpdate()
}
// send to self - we need the actual hps here
if(IsClient())
{
if(IsClient() && !skip_self) {
EQApplicationPacket* hp_app2 = new EQApplicationPacket(OP_HPUpdate,sizeof(SpawnHPUpdate_Struct));
SpawnHPUpdate_Struct* ds = (SpawnHPUpdate_Struct*)hp_app2->pBuffer;
ds->cur_hp = CastToClient()->GetHP() - itembonuses.HP;
@@ -1191,6 +1365,7 @@ void Mob::SendHPUpdate()
CastToClient()->QueuePacket(hp_app2);
safe_delete(hp_app2);
}
ResetHPUpdateTimer(); // delay the timer
}
// this one just warps the mob to the current location
@@ -1301,7 +1476,7 @@ void Mob::ShowStats(Client* client)
if(n->respawn2 != 0)
spawngroupid = n->respawn2->SpawnGroupID();
client->Message(0, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID());
client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %f Walkspeed: %f", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), n->GetRunspeed(), n->GetWalkspeed());
client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %u Walkspeed: %u", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), n->GetRunspeed(), n->GetWalkspeed());
n->QueryLoot(client);
}
if (IsAIControlled()) {
@@ -1788,7 +1963,7 @@ bool Mob::IsPlayerRace(uint16 in_race) {
uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) {
if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118) {
if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118 || in_race == 23) {
if (in_gender >= 2) {
// Male default for PC Races
return 0;
@@ -1909,22 +2084,6 @@ void Mob::SendTargetable(bool on, Client *specific_target) {
safe_delete(outapp);
}
void Mob::QuestReward(Client *c, uint32 silver, uint32 gold, uint32 platinum) {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct));
QuestReward_Struct* qr = (QuestReward_Struct*) outapp->pBuffer;
qr->from_mob = GetID(); // Entity ID for the from mob name
qr->silver = silver;
qr->gold = gold;
qr->platinum = platinum;
if(c)
c->QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
safe_delete(outapp);
}
void Mob::CameraEffect(uint32 duration, uint32 intensity, Client *c, bool global) {
@@ -2041,12 +2200,12 @@ const int32& Mob::SetMana(int32 amount)
void Mob::SetAppearance(EmuAppearance app, bool iIgnoreSelf) {
if (_appearance != app) {
_appearance = app;
SendAppearancePacket(AT_Anim, GetAppearanceValue(app), true, iIgnoreSelf);
if (this->IsClient() && this->IsAIControlled())
SendAppearancePacket(AT_Anim, ANIM_FREEZE, false, false);
}
if (_appearance == app)
return;
_appearance = app;
SendAppearancePacket(AT_Anim, GetAppearanceValue(app), true, iIgnoreSelf);
if (this->IsClient() && this->IsAIControlled())
SendAppearancePacket(AT_Anim, ANIM_FREEZE, false, false);
}
bool Mob::UpdateActiveLight()
@@ -2138,8 +2297,10 @@ void Mob::SetOwnerID(uint16 NewOwnerID) {
if (NewOwnerID == GetID() && NewOwnerID != 0) // ok, no charming yourself now =p
return;
ownerid = NewOwnerID;
if (ownerid == 0 && this->IsNPC() && this->GetPetType() != petCharmed)
this->Depop();
// if we're setting the owner ID to 0 and they're not either charmed or not-a-pet then
// they're a normal pet and should be despawned
if (ownerid == 0 && IsNPC() && GetPetType() != petCharmed && GetPetType() != petNone)
Depop();
}
// used in checking for behind (backstab) and checking in front (melee LoS)
@@ -2553,7 +2714,35 @@ uint32 NPC::GetEquipment(uint8 material_slot) const
return equipment[invslot];
}
void Mob::SendWearChange(uint8 material_slot)
void Mob::SendArmorAppearance(Client *one_client)
{
// one_client of 0 means sent to all clients
//
// Despite the fact that OP_NewSpawn and OP_ZoneSpawns include the
// armor being worn and its mats, the client doesn't update the display
// on arrival of these packets reliably.
//
// Send Wear changes if mob is a PC race and item is an armor slot.
// The other packets work for primary/secondary.
if (IsPlayerRace(race))
{
if (!IsClient())
{
const Item_Struct *item;
for (int i=0; i< 7 ; ++i)
{
item=database.GetItem(GetEquipment(i));
if (item != 0)
{
SendWearChange(i,one_client);
}
}
}
}
}
void Mob::SendWearChange(uint8 material_slot, Client *one_client)
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer;
@@ -2565,7 +2754,15 @@ void Mob::SendWearChange(uint8 material_slot)
wc->color.Color = GetEquipmentColor(material_slot);
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
if (!one_client)
{
entity_list.QueueClients(this, outapp);
}
else
{
one_client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
}
safe_delete(outapp);
}
@@ -2691,7 +2888,7 @@ int32 Mob::GetHerosForgeModel(uint8 material_slot) const
const ItemData *item;
item = database.GetItem(GetEquipment(material_slot));
int16 invslot = InventoryOld::CalcSlotFromMaterial(material_slot);
if (item != 0 && invslot != INVALID_INDEX)
{
if (IsClient())
@@ -2949,10 +3146,10 @@ uint32 Mob::GetLevelHP(uint8 tlevel)
}
int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) {
int32 cast_reducer = 0;
cast_reducer += GetFocusEffect(focusSpellHaste, spell_id);
if (level >= 60 && casttime > 1000)
{
casttime = casttime / 2;
@@ -2970,7 +3167,7 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) {
return casttime;
}
void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on) {
void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on, int level_override) {
// Changed proc targets to look up based on the spells goodEffect flag.
// This should work for the majority of weapons.
if(spell_id == SPELL_UNKNOWN || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
@@ -3009,14 +3206,14 @@ void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on) {
twinproc = true;
if (IsBeneficialSpell(spell_id)) {
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true);
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override);
if(twinproc)
SpellOnTarget(spell_id, this, false, false, 0, true);
SpellOnTarget(spell_id, this, false, false, 0, true, level_override);
}
else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
SpellFinished(spell_id, on, 10, 0, -1, spells[spell_id].ResistDiff, true);
SpellFinished(spell_id, on, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override);
if(twinproc)
SpellOnTarget(spell_id, on, false, false, 0, true);
SpellOnTarget(spell_id, on, false, false, 0, true, level_override);
}
return;
}
@@ -3566,7 +3763,7 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used)
// All skill dmg mod + Skill specific
skilldmg_mod += itembonuses.SkillDmgTaken[HIGHEST_SKILL+1] + spellbonuses.SkillDmgTaken[HIGHEST_SKILL+1] +
itembonuses.SkillDmgTaken[skill_used] + spellbonuses.SkillDmgTaken[skill_used];
skilldmg_mod += SkillDmgTaken_Mod[skill_used] + SkillDmgTaken_Mod[HIGHEST_SKILL+1];
@@ -4006,6 +4203,39 @@ int32 Mob::GetItemStat(uint32 itemid, const char *identifier)
return stat;
}
std::string Mob::GetGlobal(const char *varname) {
int qgCharid = 0;
int qgNpcid = 0;
if (this->IsNPC())
qgNpcid = this->GetNPCTypeID();
if (this->IsClient())
qgCharid = this->CastToClient()->CharacterID();
QGlobalCache *qglobals = nullptr;
std::list<QGlobal> globalMap;
if (this->IsClient())
qglobals = this->CastToClient()->GetQGlobals();
if (this->IsNPC())
qglobals = this->CastToNPC()->GetQGlobals();
if(qglobals)
QGlobalCache::Combine(globalMap, qglobals->GetBucket(), qgNpcid, qgCharid, zone->GetZoneID());
std::list<QGlobal>::iterator iter = globalMap.begin();
while(iter != globalMap.end()) {
if ((*iter).name.compare(varname) == 0)
return (*iter).value;
++iter;
}
return "Undefined";
}
void Mob::SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Mob *other) {
int qgZoneid = zone->GetZoneID();
@@ -5284,7 +5514,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot)
if (slot < 4){
if (id == "components") { return spells[spell_id].components[slot];}
else if (id == "component_counts") { return spells[spell_id].component_counts[slot];}
else if (id == "component_counts") { return spells[spell_id].component_counts[slot];}
else if (id == "NoexpendReagent") {return spells[spell_id].NoexpendReagent[slot];}
}
@@ -5362,7 +5592,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot)
else if (id == "max_dist") {return static_cast<int32>(spells[spell_id].max_dist); }
else if (id == "min_range") {return static_cast<int32>(spells[spell_id].min_range); }
else if (id == "DamageShieldType") {return spells[spell_id].DamageShieldType; }
return stat;
}
@@ -5382,9 +5612,48 @@ bool Mob::CanClassEquipItem(uint32 item_id)
int bitmask = 1;
bitmask = bitmask << (GetClass() - 1);
if(!(itm->Classes & bitmask))
return false;
else
return true;
}
void Mob::SendAddPlayerState(PlayerState new_state)
{
auto app = new EQApplicationPacket(OP_PlayerStateAdd, sizeof(PlayerState_Struct));
auto ps = (PlayerState_Struct *)app->pBuffer;
ps->spawn_id = GetID();
ps->state = static_cast<uint32>(new_state);
AddPlayerState(ps->state);
entity_list.QueueClients(nullptr, app);
safe_delete(app);
}
void Mob::SendRemovePlayerState(PlayerState old_state)
{
auto app = new EQApplicationPacket(OP_PlayerStateRemove, sizeof(PlayerState_Struct));
auto ps = (PlayerState_Struct *)app->pBuffer;
ps->spawn_id = GetID();
ps->state = static_cast<uint32>(old_state);
RemovePlayerState(ps->state);
entity_list.QueueClients(nullptr, app);
safe_delete(app);
}
void Mob::SetCurrentSpeed(int in){
if (current_speed != in)
{
current_speed = in;
tar_ndx = 20;
if (in == 0) {
SetRunAnimSpeed(0);
SetMoving(false);
SendPosition();
}
}
}
+46 -21
View File
@@ -172,7 +172,8 @@ public:
void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5,
Client *specific_target=nullptr);
void SendTargetable(bool on, Client *specific_target = nullptr);
virtual void SendWearChange(uint8 material_slot);
virtual void SendArmorAppearance(Client *one_client = nullptr);
virtual void SendWearChange(uint8 material_slot, Client *one_client = nullptr);
virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0,
uint32 unknown06 = 0, uint32 unknown18 = 0);
virtual void SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint);
@@ -200,7 +201,7 @@ public:
bool IsBeneficialAllowed(Mob *target);
virtual int GetCasterLevel(uint16 spell_id);
void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0,
uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1,
uint8 WornType = 0, int32 ticsremaining = 0, int buffslot = -1, int instrument_mod = 10,
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);
@@ -211,7 +212,8 @@ public:
virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration);
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false);
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false,
int level_override = -1);
int ResistPhysical(int level_diff, uint8 caster_level);
uint16 GetSpecializeSkillValue(uint16 spell_id) const;
void SendSpellBarDisable();
@@ -227,10 +229,10 @@ public:
void CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, uint16 mana_used,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0);
bool SpellFinished(uint16 spell_id, Mob *target, uint16 slot = 10, uint16 mana_used = 0,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false);
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false,
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center,
CastAction_type &CastAction);
virtual bool CheckFizzle(uint16 spell_id);
@@ -253,7 +255,7 @@ public:
//Buff
void BuffProcess();
virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0);
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
void BuffFadeBySpellID(uint16 spell_id);
void BuffFadeByEffect(int effectid, int skipslot = -1);
void BuffFadeAll();
@@ -364,6 +366,7 @@ public:
inline Mob* GetTarget() const { return target; }
virtual void SetTarget(Mob* mob);
virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); }
virtual inline float GetIntHPRatio() const { return max_hp == 0 ? 0 : (cur_hp/max_hp*100); }
inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; }
inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
@@ -441,9 +444,12 @@ public:
virtual void SetMoving(bool move) { moving = move; m_Delta = glm::vec4(); }
virtual void GoToBind(uint8 bindnum = 0) { }
virtual void Gate();
float GetWalkspeed() const { return(_GetMovementSpeed(-47)); }
float GetRunspeed() const { return(_GetMovementSpeed(0)); }
float GetBaseRunspeed() const { return runspeed; }
int GetWalkspeed() const { return(_GetWalkSpeed()); }
int GetRunspeed() const { return(_GetRunSpeed()); }
void SetCurrentSpeed(int in);
int GetBaseRunspeed() const { return base_runspeed; }
int GetBaseWalkspeed() const { return base_walkspeed; }
int GetBaseFearSpeed() const { return base_fearspeed; }
float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); }
bool IsRunning() const { return m_is_running; }
void SetRunning(bool val) { m_is_running = val; }
@@ -493,7 +499,6 @@ public:
inline bool CheckLastLosState() const { return last_los_check; }
//Quest
void QuestReward(Client *c = nullptr, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0);
void CameraEffect(uint32 duration, uint32 intensity, Client *c = nullptr, bool global = false);
inline bool GetQglobal() const { return qglobal; }
@@ -504,7 +509,8 @@ public:
static void CreateSpawnPacket(EQApplicationPacket* app, NewSpawn_Struct* ns);
virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
void CreateHPPacket(EQApplicationPacket* app);
void SendHPUpdate();
void SendHPUpdate(bool skip_self = false);
virtual void ResetHPUpdateTimer() {}; // does nothing
//Util
static uint32 RandomTimer(int min, int max);
@@ -533,7 +539,7 @@ public:
bool HasDefensiveProcs() const;
bool HasSkillProcs() const;
bool HasSkillProcSuccess() const;
bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN);
bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN, int level_override = -1);
bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false);
bool HasProcs() const;
bool IsCombatProc(uint16 spell_id);
@@ -802,9 +808,8 @@ public:
//old fear function
//void SetFeared(Mob *caster, uint32 duration, bool flee = false);
float GetFearSpeed();
bool IsFeared() { return curfp; } // This returns true if the mob is feared or fleeing due to low HP
//old fear: inline void StartFleeing() { SetFeared(GetHateTop(), FLEE_RUN_DURATION, true); }
int GetFearSpeed() { return _GetFearSpeed(); }
bool IsFeared() { return (spellbonuses.IsFeared || flee_mode); } // This returns true if the mob is feared or fleeing due to low HP
inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); }
void ProcessFlee();
void CheckFlee();
@@ -812,8 +817,8 @@ public:
inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);}
float CalculateHeadingToTarget(float in_x, float in_y);
bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false);
virtual bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true);
bool CalculateNewPosition(float x, float y, float z, int speed, bool checkZ = false, bool calcheading = true);
virtual bool CalculateNewPosition2(float x, float y, float z, int speed, bool checkZ = true, bool calcheading = true);
float CalculateDistance(float x, float y, float z);
float GetGroundZ(float new_x, float new_y, float z_offset=0.0);
void SendTo(float new_x, float new_y, float new_z);
@@ -859,7 +864,7 @@ public:
virtual uint32 GetAA(uint32 aa_id) const { return(0); }
uint32 GetInstrumentMod(uint16 spell_id) const;
int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, Mob *caster = nullptr, int ticsremaining = 0);
int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0);
int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0);
virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1);
uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; }
@@ -884,6 +889,8 @@ public:
Timer *GetSpecialAbilityTimer(int ability);
void ClearSpecialAbilities();
void ProcessSpecialAbilities(const std::string &str);
bool IsMoved() { return moved; }
void SetMoved(bool moveflag) { moved = moveflag; }
Shielders_Struct shielder[MAX_SHIELDERS];
Trade* trade;
@@ -906,6 +913,7 @@ public:
inline virtual bool IsBlockedBuff(int16 SpellID) { return false; }
inline virtual bool IsBlockedPetBuff(int16 SpellID) { return false; }
std::string GetGlobal(const char *varname);
void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Mob *other = nullptr);
void TarGlobal(const char *varname, const char *value, const char *duration, int npcid, int charid, int zoneid);
void DelGlobal(const char *varname);
@@ -956,7 +964,10 @@ protected:
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
float _GetMovementSpeed(int mod) const;
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ);
int _GetWalkSpeed() const;
int _GetRunSpeed() const;
int _GetFearSpeed() const;
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ);
virtual bool AI_EngagedCastCheck() { return(false); }
virtual bool AI_PursueCastCheck() { return(false); }
@@ -1030,6 +1041,13 @@ protected:
uint32 follow_dist;
bool no_target_hotkey;
uint32 m_PlayerState;
uint32 GetPlayerState() { return m_PlayerState; }
void AddPlayerState(uint32 new_state) { m_PlayerState |= new_state; }
void RemovePlayerState(uint32 old_state) { m_PlayerState &= ~old_state; }
void SendAddPlayerState(PlayerState new_state);
void SendRemovePlayerState(PlayerState old_state);
uint8 gender;
uint16 race;
uint8 base_gender;
@@ -1046,6 +1064,13 @@ protected:
float base_size;
float size;
float runspeed;
float walkspeed;
float fearspeed;
int base_runspeed;
int base_walkspeed;
int base_fearspeed;
int current_speed;
uint32 pLastChange;
bool held;
bool nocast;
@@ -1059,7 +1084,7 @@ protected:
void TryWeaponProc(const ItemInst* inst, const ItemData* weapon, Mob *on, uint16 hand = MainPrimary);
void TrySpellProc(const ItemInst* inst, const ItemData* weapon, Mob *on, uint16 hand = MainPrimary);
void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = MainPrimary);
void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on);
void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on, int level_override = -1);
virtual float GetProcChances(float ProcBonus, uint16 hand = MainPrimary);
virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand = MainPrimary, Mob *on = nullptr);
virtual float GetSpecialProcChances(uint16 hand);
+150 -157
View File
@@ -339,9 +339,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
//stop moving if were casting a spell and were not a bard...
if(!IsBardSong(AIspells[i].spellid)) {
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust));
@@ -698,9 +696,7 @@ void Client::AI_SpellCast()
{
if(!IsBardSong(spell_to_cast))
{
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return;
@@ -714,9 +710,7 @@ void Client::AI_SpellCast()
{
if(!IsBardSong(spell_to_cast))
{
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return;
@@ -772,16 +766,13 @@ void Client::AI_Process()
{
if(GetTarget())
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
SetCurrentSpeed(0);
}
//continue on to attack code, ensuring that we execute the engaged code
engaged = true;
} else {
if(AImovement_timer->Check()) {
animation = GetRunspeed() * 21;
//animation = GetFearSpeed() * 21;
// Check if we have reached the last fear point
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
(std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) {
@@ -839,16 +830,13 @@ void Client::AI_Process()
}
if (AImovement_timer->Check()) {
SetRunAnimSpeed(0);
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
}
if(IsMoving()) {
SetMoving(false);
moved=false;
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
tar_ndx =0;
}
if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) {
if(attack_timer.Check()) {
Attack(GetTarget(), MainPrimary);
@@ -944,28 +932,27 @@ void Client::AI_Process()
{
if(!IsRooted())
{
animation = 21 * GetRunspeed();
if(!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
else
if(AImovement_timer->Check())
{
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached);
if(!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
else
{
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached);
if(WaypointChanged)
tar_ndx = 20;
if(WaypointChanged)
tar_ndx = 20;
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
}
}
}
else if(IsMoving())
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
SetCurrentSpeed(0);
}
}
AI_SpellCast();
@@ -998,21 +985,23 @@ void Client::AI_Process()
return;
float dist = DistanceSquared(m_Position, owner->GetPosition());
if (dist >= 100)
if (dist >= 400)
{
float speed = dist >= 225 ? GetRunspeed() : GetWalkspeed();
animation = 21 * speed;
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
if(AImovement_timer->Check())
{
int speed = GetWalkspeed();
if (dist >= 5625)
speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
}
}
else
{
SetHeading(owner->GetHeading());
if(moved)
{
moved=false;
SetMoving(false);
SendPosition();
SetRunAnimSpeed(0);
SetCurrentSpeed(0);
moved = false;
}
}
}
@@ -1042,9 +1031,7 @@ void Mob::AI_Process() {
{
if(target)
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
moved=false;
}
//continue on to attack code, ensuring that we execute the engaged code
@@ -1058,7 +1045,9 @@ void Mob::AI_Process() {
CalculateNewFearpoint();
}
if(!RuleB(Pathing, Fear) || !zone->pathing)
{
CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true);
}
else
{
bool WaypointChanged, NodeReached;
@@ -1084,6 +1073,8 @@ void Mob::AI_Process() {
if (engaged)
{
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
SendAddPlayerState(PlayerState::Aggressive);
// we are prevented from getting here if we are blind and don't have a target in range
// from above, so no extra blind checks needed
if ((IsRooted() && !GetSpecialAbility(IGNORE_ROOT_AGGRO_RULES)) || IsBlind())
@@ -1114,7 +1105,7 @@ void Mob::AI_Process() {
}
#ifdef BOTS
if (IsPet() && GetOwner()->IsBot() && target == GetOwner())
if (IsPet() && GetOwner() && GetOwner()->IsBot() && target == GetOwner())
{
// this blocks all pet attacks against owner..bot pet test (copied above check)
RemoveFromHateList(this);
@@ -1154,15 +1145,21 @@ void Mob::AI_Process() {
{
if (AImovement_timer->Check())
{
SetRunAnimSpeed(0);
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
}
if(IsMoving())
{
SetMoving(false);
moved=false;
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SendPosition();
tar_ndx =0;
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
}
//casting checked above...
@@ -1367,7 +1364,7 @@ void Mob::AI_Process() {
CastToNPC()->DoClassAttacks(target);
}
AI_EngagedCastCheck();
} //end is within combat range
} //end is within combat rangepet
else {
//we cannot reach our target...
//underwater stuff only works with water maps in the zone!
@@ -1423,10 +1420,7 @@ void Mob::AI_Process() {
}
else if(IsMoving()) {
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
SetCurrentSpeed(0);
}
}
@@ -1435,6 +1429,8 @@ void Mob::AI_Process() {
}
else
{
if (m_PlayerState & static_cast<uint32>(PlayerState::Aggressive))
SendRemovePlayerState(PlayerState::Aggressive);
if(AIfeignremember_timer->Check()) {
// 6/14/06
// Improved Feign Death Memory
@@ -1477,7 +1473,6 @@ void Mob::AI_Process() {
}
else if (AImovement_timer->Check() && !IsRooted())
{
SetRunAnimSpeed(0);
if (IsPet())
{
// we're a pet, do as we're told
@@ -1496,18 +1491,18 @@ void Mob::AI_Process() {
float dist = DistanceSquared(m_Position, owner->GetPosition());
if (dist >= 400)
{
float speed = GetWalkspeed();
int speed = GetWalkspeed();
if (dist >= 5625)
speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
}
else
{
if(moved)
{
moved=false;
SetMoving(false);
SendPosition();
SetCurrentSpeed(0);
moved = false;
}
}
@@ -1553,19 +1548,15 @@ void Mob::AI_Process() {
if (dist2 >= followdist) // Default follow distance is 100
{
float speed = GetWalkspeed();
int speed = GetWalkspeed();
if (dist2 >= followdist + 150)
speed = GetRunspeed();
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
}
else
{
if(moved)
{
SendPosition();
moved=false;
SetMoving(false);
}
moved = false;
SetCurrentSpeed(0);
}
}
}
@@ -1662,92 +1653,39 @@ void NPC::AI_DoMovement() {
if (gridno > 0 || cur_wp==-2) {
if (movetimercompleted==true) { // time to pause at wp is over
int32 spawn_id = this->GetSpawnPointID();
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
Spawn2 *found_spawn = nullptr;
while(iterator.MoreElements())
{
Spawn2* cur = iterator.GetData();
iterator.Advance();
if(cur->GetID() == spawn_id)
{
found_spawn = cur;
break;
}
}
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(true); //depop and resart spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
}
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(false);//depop without spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
}
else {
movetimercompleted=false;
Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
//if we were under quest control (with no grid), we are done now..
if(cur_wp == -2) {
Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
roamer = false;
cur_wp = 0;
}
if(GetAppearance() != eaStanding)
SetAppearance(eaStanding, false);
entity_list.OpenDoorsNear(CastToNPC());
if(!DistractedFromGrid) {
//kick off event_waypoint depart
char temp[16];
sprintf(temp, "%d", cur_wp);
parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0);
//setup our next waypoint, if we are still on our normal grid
//remember that the quest event above could have done anything it wanted with our grid
if(gridno > 0) {
CastToNPC()->CalculateNewWaypoint();
}
}
else {
DistractedFromGrid = false;
}
}
AI_SetupNextWaypoint();
} // endif (movetimercompleted==true)
else if (!(AIwalking_timer->Enabled()))
{ // currently moving
bool doMove = true;
if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY())
{ // are we there yet? then stop
Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid());
SetWaypointPause();
if(GetAppearance() != eaStanding)
if (cur_wp_pause != 0) {
SetWaypointPause();
SetAppearance(eaStanding, false);
SetMoving(false);
if (m_CurrentWayPoint.w >= 0.0) {
SetHeading(m_CurrentWayPoint.w);
SetMoving(false);
if (m_CurrentWayPoint.w >= 0.0) {
SetHeading(m_CurrentWayPoint.w);
}
SendPosition();
}
SendPosition();
//kick off event_waypoint arrive
char temp[16];
sprintf(temp, "%d", cur_wp);
parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0);
// start moving directly to next waypoint if we're at a 0 pause waypoint and we didn't get quest halted.
if (!AIwalking_timer->Enabled())
AI_SetupNextWaypoint();
else
doMove = false;
// wipe feign memory since we reached our first waypoint
if(cur_wp == 1)
ClearFeignMemory();
}
else
{ // not at waypoint yet, so keep moving
if (doMove)
{ // not at waypoint yet or at 0 pause WP, so keep moving
if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0))
CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true);
else
@@ -1775,8 +1713,7 @@ void NPC::AI_DoMovement() {
SetGrid( 0 - GetGrid()); // revert to AI control
Log.Out(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid());
if(GetAppearance() != eaStanding)
SetAppearance(eaStanding, false);
SetAppearance(eaStanding, false);
CalculateNewWaypoint();
}
@@ -1812,29 +1749,86 @@ void NPC::AI_DoMovement() {
Log.Out(Logs::Detail, Logs::AI, "Reached guard point (%.3f,%.3f,%.3f)", m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z);
ClearFeignMemory();
moved=false;
SetMoving(false);
if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5*5 )
{
SetHeading(m_GuardPoint.w);
} else {
FaceTarget(GetTarget());
}
SendPosition();
SetCurrentSpeed(0);
SetAppearance(GetGuardPointAnim());
}
}
}
}
void NPC::AI_SetupNextWaypoint() {
int32 spawn_id = this->GetSpawnPointID();
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
Spawn2 *found_spawn = nullptr;
while (iterator.MoreElements())
{
Spawn2* cur = iterator.GetData();
iterator.Advance();
if (cur->GetID() == spawn_id)
{
found_spawn = cur;
break;
}
}
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(true); //depop and restart spawn timer
if (found_spawn)
found_spawn->SetNPCPointerNull();
}
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(false);//depop without spawn timer
if (found_spawn)
found_spawn->SetNPCPointerNull();
}
else {
movetimercompleted = false;
Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
//if we were under quest control (with no grid), we are done now..
if (cur_wp == -2) {
Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
roamer = false;
cur_wp = 0;
}
SetAppearance(eaStanding, false);
entity_list.OpenDoorsNear(CastToNPC());
if (!DistractedFromGrid) {
//kick off event_waypoint depart
char temp[16];
sprintf(temp, "%d", cur_wp);
parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0);
//setup our next waypoint, if we are still on our normal grid
//remember that the quest event above could have done anything it wanted with our grid
if (GetGrid() > 0) {
CastToNPC()->CalculateNewWaypoint();
}
}
else {
DistractedFromGrid = false;
}
}
}
// Note: Mob that caused this may not get added to the hate list until after this function call completes
void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
if (!IsAIControlled())
return;
if(GetAppearance() != eaStanding)
{
SetAppearance(eaStanding);
}
SetAppearance(eaStanding);
if (iYellForHelp) {
if(IsPet()) {
@@ -1881,9 +1875,10 @@ void Mob::AI_Event_NoLongerEngaged() {
pLastFightingDelayMoving += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving);
// So mobs don't keep running as a ghost until AIwalking_timer fires
// if they were moving prior to losing all hate
if(IsMoving()){
// except if we're a pet, then we might run into some issues with pets backing off when they should immediately be moving
if(!IsPet())
{
SetRunAnimSpeed(0);
SetMoving(false);
SendPosition();
}
ClearRampage();
@@ -2557,11 +2552,9 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon)
if (!AI_HasSpellsEffects())
return;
for(int i=0; i < AIspellsEffects.size(); i++)
{
ApplySpellsBonuses(0, 0, newbon, 0, 0, 0,-1,
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
}
for (int i = 0; i < AIspellsEffects.size(); i++)
ApplySpellsBonuses(0, 0, newbon, 0, 0, 0, -1, 10, true, AIspellsEffects[i].spelleffectid,
AIspellsEffects[i].base, AIspellsEffects[i].limit, AIspellsEffects[i].max);
return;
}
+1
View File
@@ -423,6 +423,7 @@ int main(int argc, char** argv) {
entity_list.Process();
entity_list.MobProcess();
entity_list.BeaconProcess();
entity_list.EncounterProcess();
if (zone) {
if(!zone->Process()) {
+10 -10
View File
@@ -115,7 +115,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
knightattack_timer(1000),
assist_timer(AIassistcheck_delay),
qglobal_purge_timer(30000),
sendhpupdate_timer(1000),
sendhpupdate_timer(2000),
enraged_timer(1000),
taunt_timer(TauntReuseTime * 1000),
m_SpawnPoint(position),
@@ -522,7 +522,7 @@ void NPC::QueryLoot(Client* to)
linker.SetItemData(item);
auto item_link = linker.GenerateLink();
to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", item_link.c_str(), item->ID, (*cur)->min_level, (*cur)->max_level);
}
@@ -569,8 +569,7 @@ bool NPC::Process()
{
if (IsStunned() && stunned_timer.Check())
{
this->stunned = false;
this->stunned_timer.Disable();
Mob::UnStun();
this->spun_timer.Disable();
}
@@ -651,7 +650,8 @@ bool NPC::Process()
}
}
if (sendhpupdate_timer.Check() && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) {
// we might actually want to reset in this check ... won't until issues arise at least :P
if (sendhpupdate_timer.Check(false) && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) {
if(!IsFullHP || cur_hp<max_hp){
SendHPUpdate();
}
@@ -724,7 +724,7 @@ void NPC::UpdateEquipmentLight()
{
m_Light.Type.Equipment = 0;
m_Light.Level.Equipment = 0;
for (int index = MAIN_BEGIN; index < EmuConstants::EQUIPMENT_SIZE; ++index) {
if (index == MainAmmo) { continue; }
@@ -1933,7 +1933,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
else if(id == "special_attacks") { NPCSpecialAttacks(val.c_str(), 0, 1); return; }
else if(id == "special_abilities") { ProcessSpecialAbilities(val.c_str()); return; }
else if(id == "attack_speed") { attack_speed = (float)atof(val.c_str()); CalcBonuses(); return; }
else if(id == "attack_delay") { attack_delay = atoi(val.c_str()); CalcBonuses(); return; }
else if(id == "attack_delay") { attack_delay = atoi(val.c_str()); CalcBonuses(); return; }
else if(id == "atk") { ATK = atoi(val.c_str()); return; }
else if(id == "accuracy") { accuracy_rating = atoi(val.c_str()); return; }
else if(id == "avoidance") { avoidance_rating = atoi(val.c_str()); return; }
@@ -2418,7 +2418,7 @@ void NPC::DoQuestPause(Mob *other) {
}
void NPC::ChangeLastName(const char* in_lastname)
void NPC::ChangeLastName(const char* in_lastname)
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct));
@@ -2468,9 +2468,9 @@ void NPC::DepopSwarmPets()
}
if (IsPet() && GetPetType() == petTargetLock && GetPetTargetLockID()){
Mob *targMob = entity_list.GetMob(GetPetTargetLockID());
if(!targMob || (targMob && targMob->IsCorpse())){
Kill();
return;
+2
View File
@@ -119,6 +119,7 @@ public:
virtual void AI_Start(uint32 iMoveDelay = 0);
virtual void AI_Stop();
void AI_DoMovement();
void AI_SetupNextWaypoint();
bool AI_AddNPCSpells(uint32 iDBSpellsID);
bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID);
virtual bool AI_EngagedCastCheck();
@@ -406,6 +407,7 @@ public:
void SetHeroForgeModel(uint32 model) { herosforgemodel = model; }
bool IsRaidTarget() const { return raid_target; };
void ResetHPUpdateTimer() { sendhpupdate_timer.Start(); }
protected:
+20 -5
View File
@@ -485,7 +485,22 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
buf[9] = '\0';
std::vector<EQEmu::Any> args;
args.push_back(m_inst);
parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, 0, &args);
if(parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, this->GetID(), &args))
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct));
memcpy(outapp->pBuffer, click_object, sizeof(ClickObject_Struct));
ClickObject_Struct* co = (ClickObject_Struct*)outapp->pBuffer;
co->drop_id = 0;
entity_list.QueueClients(nullptr, outapp, false);
safe_delete(outapp);
// No longer using a tradeskill object
sender->SetTradeskillObject(nullptr);
user = nullptr;
return true;
}
// Transfer item to client
sender->PutItemInInventory(MainCursor, *m_inst, false);
@@ -809,7 +824,7 @@ void Object::SetModelName(const char* modelname)
void Object::SetSize(uint16 size)
{
m_data.unknown008 = size;
m_data.size = size;
EQApplicationPacket* app = new EQApplicationPacket();
EQApplicationPacket* app2 = new EQApplicationPacket();
this->CreateDeSpawnPacket(app);
@@ -822,7 +837,7 @@ void Object::SetSize(uint16 size)
void Object::SetSolidType(uint16 solidtype)
{
m_data.unknown010 = solidtype;
m_data.solidtype = solidtype;
EQApplicationPacket* app = new EQApplicationPacket();
EQApplicationPacket* app2 = new EQApplicationPacket();
this->CreateDeSpawnPacket(app);
@@ -835,12 +850,12 @@ void Object::SetSolidType(uint16 solidtype)
uint16 Object::GetSize()
{
return m_data.unknown008;
return m_data.size;
}
uint16 Object::GetSolidType()
{
return m_data.unknown010;
return m_data.solidtype;
}
const char* Object::GetModelName()
+6 -3
View File
@@ -16,7 +16,7 @@
#define snprintf _snprintf
#endif
//#define PATHDEBUG
//#define PATHDEBUG
extern Zone *zone;
@@ -205,10 +205,10 @@ glm::vec3 PathManager::GetPathNodeCoordinates(int NodeNumber, bool BestZ)
}
std::deque<int> PathManager::FindRoute(int startID, int endID)
{
{
Log.Out(Logs::Detail, Logs::None, "FindRoute from node %i to %i", startID, endID);
memset(ClosedListFlag, 0, sizeof(int) * Head.PathNodeCount);
memset(ClosedListFlag, 0, sizeof(int) * Head.PathNodeCount);
std::deque<AStarNode> OpenList, ClosedList;
@@ -665,6 +665,9 @@ glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Wa
bool SameDestination = (To == PathingDestination);
if (Speed <= 0) // our speed is 0, we cant move so lets return the dest
return To; // this will also avoid the teleports cleanly
int NextNode;
if(To == From)
+80 -1
View File
@@ -6188,6 +6188,84 @@ XS(XS_Client_GetTargetRingZ)
XSRETURN(1);
}
XS(XS_Client_CalcEXP); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_CalcEXP)
{
dXSARGS;
if (items < 1 || items > 2)
Perl_croak(aTHX_ "Usage: CalcEXP(THIS, uint8 conlevel)");
{
Client * THIS;
uint8 conlevel = 0xFF;
uint32 RETVAL;
if(items == 2)
conlevel = (uint16)SvUV(ST(1));
dXSTARG;
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Client");
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
RETVAL = THIS->CalcEXP(conlevel);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Client_QuestReward); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_QuestReward)
{
dXSARGS;
if (items < 1 || items > 9)
Perl_croak(aTHX_ "Usage: Client::QuestReward(THIS, mob, copper, silver, gold, platinum, itemid, exp, faction)");
{
Client* THIS;
Mob * mob = nullptr;
int32 copper = 0;
int32 silver = 0;
int32 gold = 0;
int32 platinum = 0;
int32 itemid = 0;
int32 exp = 0;
bool faction = false;
if (sv_derived_from(ST(0), "THIS")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *, tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type client");
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items > 1) {
if (sv_derived_from(ST(1), "mob")) {
IV tmp = SvIV((SV*)SvRV(ST(1)));
mob = INT2PTR(Mob *, tmp);
}
else
Perl_croak(aTHX_ "mob is not of type Mob");
if (mob == nullptr)
Perl_croak(aTHX_ "mob is nullptr, avoiding crash.");
}
if (items > 2) { copper = (int32)SvIV(ST(2)); }
if (items > 3) { silver = (int32)SvIV(ST(3)); }
if (items > 4) { gold = (int32)SvIV(ST(4)); }
if (items > 5) { platinum = (int32)SvIV(ST(5)); }
if (items > 6) { itemid = (int32)SvIV(ST(6)); }
if (items > 7) { exp = (int32)SvIV(ST(7)); }
if (items > 8) { faction = (bool)SvIV(ST(8)); }
THIS->QuestReward(mob, copper, silver, gold, platinum, itemid, exp, faction);
}
XSRETURN_EMPTY;
}
#ifdef __cplusplus
extern "C"
#endif
@@ -6432,7 +6510,8 @@ XS(boot_Client)
newXSproto(strcpy(buf, "GetTargetRingX"), XS_Client_GetTargetRingX, file, "$$");
newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$");
newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$");
newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$");
newXSproto(strcpy(buf, "CalcEXP"), XS_Client_CalcEXP, file, "$");
XSRETURN_YES;
}
+32 -42
View File
@@ -7027,47 +7027,6 @@ XS(XS_Mob_SendAppearanceEffect)
XSRETURN_EMPTY;
}
XS(XS_Mob_QuestReward); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_QuestReward)
{
dXSARGS;
if (items < 1 || items > 5)
Perl_croak(aTHX_ "Usage: Mob::QuestReward(THIS, client, silver, gold, platinum)");
{
Mob * THIS;
Client* client = nullptr;
int32 silver = 0;
int32 gold = 0;
int32 platinum = 0;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items > 1) {
if (sv_derived_from(ST(1), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(1)));
client = INT2PTR(Client *,tmp);
}
else
Perl_croak(aTHX_ "client is not of type Client");
if(client == nullptr)
Perl_croak(aTHX_ "client is nullptr, avoiding crash.");
}
if (items > 2) { silver = (int32)SvIV(ST(2)); }
if (items > 3) { gold = (int32)SvIV(ST(3)); }
if (items > 4) { platinum = (int32)SvIV(ST(4)); }
THIS->QuestReward(client, silver, gold, platinum);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_SetFlyMode); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetFlyMode)
{
@@ -7362,6 +7321,37 @@ XS(XS_Mob_GetItemStat)
XSRETURN(1);
}
XS(XS_Mob_GetGlobal);
XS(XS_Mob_GetGlobal)
{
dXSARGS;
if (items < 2)
Perl_croak(aTHX_ "Usage: GetGlobal(THIS, varname)");
{
Mob* THIS;
Const_char* varname = (Const_char*)SvPV_nolen(ST(1));
std::string ret_val = "Undefined";
Const_char* RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *, tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (THIS->GetGlobal(varname) != "Undefined")
ret_val = THIS->GetGlobal(varname);
RETVAL = ret_val.c_str();
sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
}
XSRETURN(1);
}
XS(XS_Mob_SetGlobal);
XS(XS_Mob_SetGlobal)
{
@@ -8660,11 +8650,11 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "SendIllusion"), XS_Mob_SendIllusion, file, "$$;$$$$$$$$$$$$");
newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$");
newXSproto(strcpy(buf, "TypesTempPet"), XS_Mob_TypesTempPet, file, "$$;$$$$$");
newXSproto(strcpy(buf, "QuestReward"), XS_Mob_QuestReward, file, "$$;$$$");
newXSproto(strcpy(buf, "CameraEffect"), XS_Mob_CameraEffect, file, "$$;$$$");
newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$");
newXSproto(strcpy(buf, "TempName"), XS_Mob_TempName, file, "$:$");
newXSproto(strcpy(buf, "GetItemStat"), XS_Mob_GetItemStat, file, "$$$");
newXSproto(strcpy(buf, "GetGlobal"), XS_Mob_GetGlobal, file, "$$");
newXSproto(strcpy(buf, "SetGlobal"), XS_Mob_SetGlobal, file, "$$$$$;$");
newXSproto(strcpy(buf, "TarGlobal"), XS_Mob_TarGlobal, file, "$$$$$$$");
newXSproto(strcpy(buf, "DelGlobal"), XS_Mob_DelGlobal, file, "$$");
+34 -4
View File
@@ -28,6 +28,10 @@
#include "pets.h"
#include "zonedb.h"
#ifdef BOTS
#include "bot.h"
#endif
#ifndef WIN32
#include <stdlib.h>
#include "../common/unix.h"
@@ -231,6 +235,10 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);//Client only
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
}
#ifdef BOTS
else if (this->IsBot())
act_power = CastToBot()->GetBotFocusEffect(Bot::BotfocusPetPower, spell_id);
#endif
}
else if (petpower > 0)
act_power = petpower;
@@ -260,7 +268,11 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
memcpy(npc_type, base, sizeof(NPCType));
// If pet power is set to -1 in the DB, use stat scaling
if (this->IsClient() && record.petpower == -1)
if ((this->IsClient()
#ifdef BOTS
|| this->IsBot()
#endif
) && record.petpower == -1)
{
float scale_power = (float)act_power / 100.0f;
if(scale_power > 0)
@@ -268,10 +280,10 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
npc_type->max_hp *= (1 + scale_power);
npc_type->cur_hp = npc_type->max_hp;
npc_type->AC *= (1 + scale_power);
npc_type->level += 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power
npc_type->level += 1 + ((int)act_power / 25) > npc_type->level + RuleR(Pets, PetPowerLevelCap) ? RuleR(Pets, PetPowerLevelCap) : 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power
npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2)));
npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2)));
npc_type->size *= (1 + (scale_power / 2));
npc_type->size = npc_type->size * (1 + (scale_power / 2)) > npc_type->size * 3 ? npc_type->size * 3 : (1 + (scale_power / 2));
}
record.petpower = act_power;
}
@@ -426,7 +438,7 @@ 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)
{
@@ -457,6 +469,22 @@ Pet::Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 po
// Class should use npc constructor to set light properties
}
void Pet::SetTarget(Mob *mob)
{
if (mob == GetTarget())
return;
auto owner = GetOwner();
if (owner && owner->IsClient() && owner->CastToClient()->GetClientVersionBit() & BIT_UFAndLater) {
auto app = new EQApplicationPacket(OP_PetHoTT, sizeof(ClientTarget_Struct));
auto ct = (ClientTarget_Struct *)app->pBuffer;
ct->new_target = mob ? mob->GetID() : 0;
owner->CastToClient()->QueuePacket(app);
safe_delete(app);
}
NPC::SetTarget(mob);
}
bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) {
return GetPoweredPetEntry(pet_type, 0, into);
}
@@ -555,6 +583,7 @@ void NPC::GetPetState(SpellBuff_Struct *pet_buffs, uint32 *items, char *name) {
pet_buffs[i].level = buffs[i].casterlevel;
pet_buffs[i].effect = 10;
pet_buffs[i].counters = buffs[i].counters;
pet_buffs[i].bard_modifier = buffs[i].instrument_mod;
}
else {
pet_buffs[i].spellid = SPELL_UNKNOWN;
@@ -588,6 +617,7 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
buffs[i].casterid = 0;
buffs[i].counters = pet_buffs[i].counters;
buffs[i].numhits = spells[pet_buffs[i].spellid].numhits;
buffs[i].instrument_mod = pet_buffs[i].bard_modifier;
}
else {
buffs[i].spellid = SPELL_UNKNOWN;
+1
View File
@@ -39,6 +39,7 @@ struct NPCType;
class Pet : public NPC {
public:
Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power);
virtual void SetTarget(Mob *mob);
};
+1 -1
View File
@@ -41,7 +41,7 @@ public:
std::vector<EQEmu::Any> *extra_pointers) { return 0; }
virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) { return 0; }
virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data,
virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) { return 0; }
virtual bool HasQuestSub(uint32 npcid, QuestEventID evt) { return false; }
+3 -3
View File
@@ -434,14 +434,14 @@ int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client
return 0;
}
int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data,
int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers) {
auto iter = _encounter_quest_status.find(encounter_name);
if(iter != _encounter_quest_status.end()) {
//loaded or failed to load
if(iter->second != QuestFailedToLoad) {
std::map<uint32, QuestInterface*>::iterator qiter = _interfaces.find(iter->second);
return qiter->second->EventEncounter(evt, encounter_name, extra_data, extra_pointers);
return qiter->second->EventEncounter(evt, encounter_name, data, extra_data, extra_pointers);
}
} else {
std::string filename;
@@ -449,7 +449,7 @@ int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounte
if(qi) {
_encounter_quest_status[encounter_name] = qi->GetIdentifier();
qi->LoadEncounterScript(filename, encounter_name);
return qi->EventEncounter(evt, encounter_name, extra_data, extra_pointers);
return qi->EventEncounter(evt, encounter_name, data, extra_data, extra_pointers);
} else {
_encounter_quest_status[encounter_name] = QuestFailedToLoad;
}
+2 -1
View File
@@ -21,6 +21,7 @@
#include "../common/types.h"
#include "encounter.h"
#include "beacon.h"
#include "client.h"
#include "corpse.h"
@@ -71,7 +72,7 @@ public:
std::vector<EQEmu::Any> *extra_pointers = nullptr);
int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers = nullptr);
int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data,
int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data,
std::vector<EQEmu::Any> *extra_pointers = nullptr);
void GetErrors(std::list<std::string> &err);
+83 -7
View File
@@ -84,6 +84,8 @@ void QuestManager::Process() {
if(entity_list.IsMobInZone(cur->mob)) {
if(cur->mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0);
} else if (cur->mob->IsEncounter()) {
parse->EventEncounter(EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), cur->name, 0, nullptr);
} else {
//this is inheriently unsafe if we ever make it so more than npc/client start timers
parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0);
@@ -668,6 +670,79 @@ void QuestManager::repopzone() {
}
}
void QuestManager::ConnectNodeToNode(int node1, int node2, int teleport, int doorid) {
if (!node1 || !node2)
{
Log.Out(Logs::General, Logs::Quests, "QuestManager::ConnectNodeToNode called without node1 or node2. Probably syntax error in quest file.");
}
else
{
if (!teleport)
{
teleport = 0;
}
else if (teleport == 1 || teleport == -1)
{
teleport = -1;
}
if (!doorid)
{
doorid = 0;
}
if (!zone->pathing)
{
// if no pathing bits available, make them available.
zone->pathing = new PathManager();
}
if (zone->pathing)
{
zone->pathing->ConnectNodeToNode(node1, node2, teleport, doorid);
Log.Out(Logs::Moderate, Logs::Quests, "QuestManager::ConnectNodeToNode connecting node %i to node %i.", node1, node2);
}
}
}
void QuestManager::AddNode(float x, float y, float z, float best_z, int32 requested_id)
{
if (!x || !y || !z)
{
Log.Out(Logs::General, Logs::Quests, "QuestManager::AddNode called without x, y, z. Probably syntax error in quest file.");
}
if (!best_z || best_z == 0)
{
if (zone->zonemap)
{
glm::vec3 loc(x, y, z);
best_z = zone->zonemap->FindBestZ(loc, nullptr);
}
else
{
best_z = z;
}
}
if (!requested_id)
{
requested_id = 0;
}
if (!zone->pathing)
{
// if no pathing bits available, make them available.
zone->pathing = new PathManager();
}
if (zone->pathing)
{
zone->pathing->AddNode(x, y, z, best_z, requested_id);
Log.Out(Logs::Moderate, Logs::Quests, "QuestManager::AddNode adding node at (%i, %i, %i).", x, y, z);
}
}
void QuestManager::settarget(const char *type, int target_id) {
QuestManagerCurrentQuestVars();
if (!owner || !owner->IsNPC())
@@ -1219,9 +1294,10 @@ void QuestManager::CreateGuild(const char *guild_name, const char *leader) {
}
}
void QuestManager::settime(uint8 new_hour, uint8 new_min) {
void QuestManager::settime(uint8 new_hour, uint8 new_min, bool update_world /*= true*/)
{
if (zone)
zone->SetTime(new_hour + 1, new_min);
zone->SetTime(new_hour + 1, new_min, update_world);
}
void QuestManager::itemlink(int item_id) {
@@ -1997,6 +2073,11 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
return false;
}
if(Bot::IsBotNameAvailable((char*)name,&TempErrorMessage)) {
initiator->Message(0, "The name %s is already being used or is invalid. Please choose a different name.", (char*)name);
return false;
}
NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender);
Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator);
@@ -2012,11 +2093,6 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
return false;
}
if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) {
initiator->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName());
return false;
}
if(!TempErrorMessage.empty()) {
initiator->Message(13, "Database Error: %s", TempErrorMessage.c_str());
return false;
+3 -1
View File
@@ -87,6 +87,8 @@ public:
void depopall(int npc_type = 0);
void depopzone(bool StartSpawnTimer = true);
void repopzone();
void ConnectNodeToNode(int node1, int node2, int teleport, int doorid);
void AddNode(float x, float y, float z, float best_z, int32 requested_id);
void settarget(const char *type, int target_id);
void follow(int entity_id, int distance);
void sfollow();
@@ -124,7 +126,7 @@ public:
void setsky(uint8 new_sky);
void setguild(uint32 new_guild_id, uint8 new_rank);
void CreateGuild(const char *guild_name, const char *leader);
void settime(uint8 new_hour, uint8 new_min);
void settime(uint8 new_hour, uint8 new_min, bool update_world = true);
void itemlink(int item_id);
void signal(int npc_id, int wait_ms = 0);
void signalwith(int npc_id, int signal_id, int wait_ms = 0);
+5 -5
View File
@@ -624,7 +624,7 @@ void SpawnConditionManager::Process() {
//get our current time
TimeOfDay_Struct tod;
zone->zone_time.getEQTimeOfDay(&tod);
zone->zone_time.GetCurrentEQTimeOfDay(&tod);
//see if time is past our nearest event.
if(EQTime::IsTimeBefore(&next_event, &tod))
@@ -673,7 +673,7 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) {
}
TimeOfDay_Struct tod;
zone->zone_time.getEQTimeOfDay(&tod);
zone->zone_time.GetCurrentEQTimeOfDay(&tod);
if(event.strict && (event.next.hour != tod.hour || event.next.day != tod.day || event.next.month != tod.month || event.next.year != tod.year))
{
Log.Out(Logs::Detail, Logs::Spawns, "Event %d: Unable to execute. Condition is strict, and event time has already passed.", event.id);
@@ -871,7 +871,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
//better solution, and I just dont care thats much.
//get our current time
TimeOfDay_Struct tod;
zone->zone_time.getEQTimeOfDay(&tod);
zone->zone_time.GetCurrentEQTimeOfDay(&tod);
for(auto cur = spawn_events.begin(); cur != spawn_events.end(); ++cur) {
SpawnEvent &cevent = *cur;
@@ -1096,7 +1096,7 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool stri
if(reset_base) {
Log.Out(Logs::Detail, Logs::Spawns, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period);
//start with the time now
zone->zone_time.getEQTimeOfDay(&cevent.next);
zone->zone_time.GetCurrentEQTimeOfDay(&cevent.next);
//advance the next time by our period
EQTime::AddMinutes(cevent.period, &cevent.next);
} else {
@@ -1141,7 +1141,7 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool stri
if(reset_base) {
Log.Out(Logs::Detail, Logs::Spawns, "Spawn event %d is in zone %s. State set. Trigger time reset (period %d). Notifying world.", event_id, zone_short_name.c_str(), e.period);
//start with the time now
zone->zone_time.getEQTimeOfDay(&e.next);
zone->zone_time.GetCurrentEQTimeOfDay(&e.next);
//advance the next time by our period
EQTime::AddMinutes(e.period, &e.next);
} else {
+58 -49
View File
@@ -95,7 +95,7 @@ void Mob::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg)
}
}
void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime,
void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime,
bool HitChance, bool CanAvoid) {
//this really should go through the same code as normal melee damage to
//pick up all the special behavior there
@@ -119,8 +119,12 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
{
hate += item->GetItem()->AC;
}
const ItemData *itm = item->GetItem();
hate = hate * (100 + GetFuriousBash(itm->Focus.Effect)) / 100;
auto fbash = GetFuriousBash(itm->Focus.Effect);
hate = hate * (100 + fbash) / 100;
if (fbash)
Message_StringID(MT_Spells, GLOWS_RED, itm->Name);
}
}
}
@@ -181,6 +185,11 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
return;
CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer;
pTimerType timer = pTimerCombatAbility;
// RoF2+ Tiger Claw is unlinked from other monk skills, if they ever do that for other classes there will need
// to be more checks here
if (GetClientVersion() >= ClientVersion::RoF2 && ca_atk->m_skill == SkillTigerClaw)
timer = pTimerCombatAbility2;
/* Check to see if actually have skill */
if (!MaxSkill(static_cast<SkillUseTypes>(ca_atk->m_skill)))
@@ -218,7 +227,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
if(!CombatRange(GetTarget()))
return;
if(!p_timers.Expired(&database, pTimerCombatAbility, false)) {
if(!p_timers.Expired(&database, timer, false)) {
Message(13,"Ability recovery time not yet met.");
return;
}
@@ -268,7 +277,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
DoSpecialAttackDamage(GetTarget(), SkillBash, dmg, 1, ht, ReuseTime);
if(ReuseTime > 0)
{
p_timers.Start(pTimerCombatAbility, ReuseTime);
p_timers.Start(timer, ReuseTime);
}
}
return;
@@ -304,7 +313,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
}
if(ReuseTime > 0) {
p_timers.Start(pTimerCombatAbility, ReuseTime);
p_timers.Start(timer, ReuseTime);
}
return;
}
@@ -390,7 +399,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
ReuseTime = (ReuseTime*HasteMod)/100;
if(ReuseTime > 0){
p_timers.Start(pTimerCombatAbility, ReuseTime);
p_timers.Start(timer, ReuseTime);
}
}
@@ -558,7 +567,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
if (level > 54) {
// Check for double attack with main hand assuming maxed DA Skill (MS)
if(IsClient() && CastToClient()->CheckDoubleAttack(false))
if(IsClient() && CastToClient()->CheckDoubleAttack(false))
if(other->GetHP() > 0)
RogueBackstab(other,true, ReuseTime);
@@ -610,11 +619,11 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
if(primaryweapondamage > 0){
if(level > 25){
max_hit = (((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level-25)/3) + 1;
max_hit = (((((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level-25)/3) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100));
hate = 20 * backstab_dmg * GetSkill(SkillBackstab) / 355;
}
else{
max_hit = (((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1;;
max_hit = (((((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100));
hate = 20 * backstab_dmg * GetSkill(SkillBackstab) / 355;
}
@@ -650,10 +659,10 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
}
ndamage = mod_backstab_damage(ndamage);
uint32 Assassinate_Dmg = 0;
Assassinate_Dmg = TryAssassinate(other, SkillBackstab, ReuseTime);
if (Assassinate_Dmg) {
ndamage = Assassinate_Dmg;
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName());
@@ -797,14 +806,14 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) {
//EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow.
int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile;
if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){
if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){
DeleteItemInInventory(ammo_slot, 1, true);
Log.Out(Logs::Detail, Logs::Combat, "Consumed one arrow from slot %d", ammo_slot);
} else {
Log.Out(Logs::Detail, Logs::Combat, "Endless Quiver prevented ammo consumption.");
}
CheckIncreaseSkill(SkillArchery, GetTarget(), -15);
CheckIncreaseSkill(SkillArchery, GetTarget(), -15);
CommonBreakInvisible();
}
@@ -816,7 +825,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
(other->IsClient() && other->CastToClient()->dead)) ||
HasDied() ||
(!IsAttackAllowed(other)) ||
(other->GetInvul() ||
(other->GetInvul() ||
other->GetSpecialAbility(IMMUNE_MELEE))))
{
return;
@@ -841,12 +850,12 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
LaunchProjectile = true;
else{
/*
Item sync check on projectile landing.
Item sync check on projectile landing.
Weapon damage is already calculated so this only affects procs!
Ammo proc check will use database to find proc if you used up your last ammo.
If you change range item mid projectile flight, you loose your chance to proc from bow (Deal with it!).
*/
if (!RangeWeapon && !Ammo && range_id && ammo_id){
ProjectileImpact = true;
@@ -858,8 +867,8 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
_RangeWeapon = CastToClient()->m_inv[MainRange];
if (_RangeWeapon && _RangeWeapon->GetItem() && _RangeWeapon->GetItem()->ID == range_id)
RangeWeapon = _RangeWeapon;
RangeWeapon = _RangeWeapon;
_Ammo = CastToClient()->m_inv[AmmoSlot];
if (_Ammo && _Ammo->GetItem() && _Ammo->GetItem()->ID == ammo_id)
Ammo = _Ammo;
@@ -985,7 +994,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
other->AddToHateList(this, hate, 0, false);
other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery);
//Skill Proc Success
if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()){
if (ReuseTime)
@@ -1047,10 +1056,10 @@ bool Mob::TryProjectileAttack(Mob* other, const ItemData *item, SkillUseTypes sk
ProjectileAtk[slot].origin_x = GetX();
ProjectileAtk[slot].origin_y = GetY();
ProjectileAtk[slot].origin_z = GetZ();
if (RangeWeapon && RangeWeapon->GetItem())
ProjectileAtk[slot].ranged_id = RangeWeapon->GetItem()->ID;
if (Ammo && Ammo->GetItem())
ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID;
@@ -1085,7 +1094,7 @@ void Mob::ProjectileAttack()
disable = false;
Mob* target = entity_list.GetMobID(ProjectileAtk[i].target_id);
if (target && target->IsMoving()){ //Only recalculate hit increment if target moving
//Due to frequency that we need to check increment the targets position variables may not be updated even if moving. Do a simple check before calculating distance.
if (ProjectileAtk[i].tlast_x != target->GetX() || ProjectileAtk[i].tlast_y != target->GetY()){
@@ -1109,7 +1118,7 @@ void Mob::ProjectileAttack()
else
CastToNPC()->DoRangedAttackDmg(target, false, ProjectileAtk[i].wpn_dmg,0, static_cast<SkillUseTypes>(ProjectileAtk[i].skill));
}
else
{
if (ProjectileAtk[i].skill == SkillArchery)
@@ -1120,7 +1129,7 @@ void Mob::ProjectileAttack()
SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true);
}
}
ProjectileAtk[i].increment = 0;
ProjectileAtk[i].target_id = 0;
ProjectileAtk[i].wpn_dmg = 0;
@@ -1180,7 +1189,7 @@ float Mob::GetRangeDistTargetSizeMod(Mob* other)
mod = 42.0f + (5.8f * (tsize - 15.0f));
else
mod = 75.0f;
return (mod + 2.0f); //Add 2.0f as buffer to prevent any chance of failures, client enforce range check regardless.
}
@@ -1210,7 +1219,7 @@ void NPC::RangedAttack(Mob* other)
float min_range = static_cast<float>(RuleI(Combat, MinRangedAttackDist));
float max_range = 250; // needs to be longer than 200(most spells)
if (sa_max_range)
max_range = static_cast<float>(sa_max_range);
@@ -1242,12 +1251,12 @@ void NPC::RangedAttack(Mob* other)
}
void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 chance_mod, SkillUseTypes skill, float speed, const char *IDFile) {
if ((other == nullptr ||
(other->HasDied())) ||
HasDied() ||
if ((other == nullptr ||
(other->HasDied())) ||
HasDied() ||
(!IsAttackAllowed(other)) ||
(other->GetInvul() ||
(other->GetInvul() ||
other->GetSpecialAbility(IMMUNE_MELEE)))
{
return;
@@ -1268,7 +1277,7 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha
ammo = GetAmmoIDfile();
ProjectileAnimation(other, 0,false,speed,0,0,0,ammo,skillInUse);
if (RuleB(Combat, ProjectileDmgOnImpact))
{
TryProjectileAttack(other, nullptr, skillInUse, damage_mod, nullptr, nullptr, 0, speed);
@@ -1276,7 +1285,7 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha
}
}
if (!chance_mod)
if (!chance_mod)
chance_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2);
if (!other->CheckHitChance(this, skillInUse, MainRange, chance_mod))
@@ -1298,11 +1307,11 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha
if (!damage_mod)
damage_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3);//Damage modifier
TotalDmg += TotalDmg * damage_mod / 100;
TotalDmg += TotalDmg * damage_mod / 100;
other->AvoidDamage(this, TotalDmg, false);
other->MeleeMitigation(this, TotalDmg, MinDmg);
if (TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, skillInUse);
else if (TotalDmg < -4)
@@ -1432,18 +1441,18 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51
//consume ammo
DeleteItemInInventory(ammo_slot, 1, true);
CheckIncreaseSkill(SkillThrowing, GetTarget());
CheckIncreaseSkill(SkillThrowing, GetTarget());
CommonBreakInvisible();
}
void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemData* AmmoItem, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 range_id, int AmmoSlot, float speed)
{
if ((other == nullptr ||
((IsClient() && CastToClient()->dead) ||
(other->IsClient() && other->CastToClient()->dead)) ||
HasDied() ||
if ((other == nullptr ||
((IsClient() && CastToClient()->dead) ||
(other->IsClient() && other->CastToClient()->dead)) ||
HasDied() ||
(!IsAttackAllowed(other)) ||
(other->GetInvul() ||
(other->GetInvul() ||
other->GetSpecialAbility(IMMUNE_MELEE))))
{
return;
@@ -1511,7 +1520,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
return;
}
}
else
else
WDmg = weapon_damage;
if (focus) //From FcBaseEffects
@@ -1535,7 +1544,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
Log.Out(Logs::Detail, Logs::Combat, "Item DMG %d. Max Damage %d. Hit for damage %d", WDmg, MaxDmg, TotalDmg);
if (!Assassinate_Dmg)
other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks.
other->MeleeMitigation(this, TotalDmg, minDmg);
if(TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, SkillThrowing);
@@ -1620,7 +1629,7 @@ void Mob::SendItemAnimation(Mob *to, const ItemData *item, SkillUseTypes skillIn
safe_delete(outapp);
}
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) {
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) {
if (!to)
return;
@@ -2092,7 +2101,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
}
ReuseTime = ReuseTime / HasteMod;
if(ReuseTime > 0 && !IsRiposte){
if(ReuseTime > 0 && !IsRiposte){
p_timers.Start(pTimerCombatAbility, ReuseTime);
}
}
@@ -2270,7 +2279,7 @@ float Mob::GetSpecialProcChances(uint16 hand)
if (RuleB(Combat, AdjustSpecialProcPerMinute)) {
ProcChance = (static_cast<float>(weapon_speed) *
RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f);
RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f);
ProcBonus += static_cast<float>(mydex/35) + static_cast<float>(itembonuses.HeroicDEX / 25);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
@@ -2363,7 +2372,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
int Hand = MainPrimary;
if (hate == 0 && weapon_damage > 1) hate = weapon_damage;
if(weapon_damage > 0){
if(weapon_damage > 0){
if (focus) //From FcBaseEffects
weapon_damage += weapon_damage*focus/100;
@@ -2411,7 +2420,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
} else {
other->AvoidDamage(this, damage, CanRiposte);
other->MeleeMitigation(this, damage, min_hit);
if(damage > 0)
if(damage > 0)
CommonOutgoingHitSuccess(other, damage, skillinuse);
}
@@ -2449,7 +2458,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
if (CanSkillProc && HasSkillProcs())
TrySkillProc(other, skillinuse, ReuseTime);
if (CanSkillProc && (damage > 0) && HasSkillProcSuccess())
TrySkillProc(other, skillinuse, ReuseTime, true);
}
+487 -485
View File
File diff suppressed because it is too large Load Diff
+167 -118
View File
@@ -82,6 +82,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include <assert.h>
#include <math.h>
#include <algorithm>
#ifndef WIN32
#include <stdlib.h>
@@ -369,6 +370,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
// and a target wasn't provided, then it's us; unless TGB is on and this
// is a TGB compatible spell.
if((IsGroupSpell(spell_id) ||
spell.targettype == ST_AEClientV1 ||
spell.targettype == ST_Self ||
spell.targettype == ST_AECaster ||
spell.targettype == ST_Ring ||
@@ -392,7 +394,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
// we checked for spells not requiring targets above
if(target_id == 0) {
Log.Out(Logs::Detail, Logs::Spells, "Spell Error: no target. spell=%d\n", GetName(), spell_id);
Log.Out(Logs::Detail, Logs::Spells, "Spell Error: no target. spell=%d", spell_id);
if(IsClient()) {
//clients produce messages... npcs should not for this case
Message_StringID(13, SPELL_NEED_TAR);
@@ -488,6 +490,12 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
safe_delete(outapp);
outapp = nullptr;
if (IsClient() && slot == USE_ITEM_SPELL_SLOT &&item_slot != 0xFFFFFFFF) {
auto item = CastToClient()->GetInv().GetItem(item_slot);
if (item && item->GetItem())
Message_StringID(MT_Spells, BEGINS_TO_GLOW, item->GetItem()->Name);
}
if (!DoCastingChecks()) {
InterruptSpell();
return false;
@@ -1164,12 +1172,14 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
}
else if (!bard_song_mode)
{
int noexpend;
for(int t_count = 0; t_count < 4; t_count++) {
component = spells[spell_id].components[t_count];
if (component == -1)
noexpend = spells[spell_id].NoexpendReagent[t_count];
if (component == -1 || noexpend == component)
continue;
component_count = spells[spell_id].component_counts[t_count];
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Consuming %d of spell component item id %d", spell_id, component, component_count);
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Consuming %d of spell component item id %d", spell_id, component_count, component);
// Components found, Deleting
// now we go looking for and deleting the items one by one
for(int s = 0; s < component_count; s++)
@@ -1477,7 +1487,10 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
{
//invalid target
Log.Out(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (undead)", spell_id, mob_body);
Message_StringID(13,SPELL_NEED_TAR);
if(!spell_target)
Message_StringID(13,SPELL_NEED_TAR);
else
Message_StringID(13,CANNOT_AFFECT_NPC);
return false;
}
CastAction = SingleTarget;
@@ -1606,6 +1619,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
case ST_AEBard:
case ST_AECaster:
case ST_AEClientV1:
{
spell_target = nullptr;
ae_center = this;
@@ -1891,7 +1905,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
// we can't interrupt in this, or anything called from this!
// if you need to abort the casting, return false
bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 mana_used,
uint32 inventory_slot, int16 resist_adjust, bool isproc)
uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override)
{
//EQApplicationPacket *outapp = nullptr;
Mob *ae_center = nullptr;
@@ -1933,6 +1947,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
}
if (IsClient() && CastToClient()->GetGM()){
if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))){
Log.Out(Logs::Detail, Logs::Spells, "GM Cast Blocked Spell: %s (ID %i)", GetSpellName(spell_id), spell_id);
}
}
if
(
this->IsClient() &&
@@ -2048,14 +2068,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
return(false);
}
if (isproc) {
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true);
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true, level_override);
} else {
if (spells[spell_id].targettype == ST_TargetOptional){
if (!TrySpellProjectile(spell_target, spell_id))
return false;
}
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false, level_override)) {
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
// Prevent mana usage/timers being set for beneficial buffs
if(casting_spell_type == 1)
@@ -2149,7 +2169,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
// caster if they're not using TGB
// NOTE: this will always hit the caster, plus the target's group so
// it can affect up to 7 people if the targeted group is not our own
// Allow pets who cast group spells to affect the group.
if (spell_target->IsPetOwnerClient() && IsPetOwnerClient()){
Mob* owner = spell_target->GetOwner();
@@ -2157,7 +2177,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
if (owner)
spell_target = owner;
}
if(spell_target->IsGrouped())
{
Group *target_group = entity_list.GetGroupByMob(spell_target);
@@ -2252,7 +2272,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
//set our reuse timer on long ass reuse_time spells...
if(IsClient())
if(IsClient() && !isproc)
{
if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF)
{
@@ -2560,7 +2580,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
cd->source = action->source;
cd->type = DamageTypeSpell;
cd->spellid = action->spell;
cd->sequence = action->sequence;
cd->meleepush_xy = action->sequence;
cd->damage = 0;
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{
@@ -2612,84 +2632,85 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste
castlevel = caster_level_override;
int res = CalcBuffDuration_formula(castlevel, formula, duration);
if (caster == target && (target->aabonuses.IllusionPersistence || target->spellbonuses.IllusionPersistence ||
target->itembonuses.IllusionPersistence) &&
IsEffectInSpell(spell_id, SE_Illusion))
res = 10000; // ~16h override
res = mod_buff_duration(res, caster, target, spell_id);
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Casting level %d, formula %d, base_duration %d: result %d",
spell_id, castlevel, formula, duration, res);
return(res);
return res;
}
// the generic formula calculations
int CalcBuffDuration_formula(int level, int formula, int duration)
{
int i; // temp variable
int temp;
switch(formula)
{
case 0: // not a buff
return 0;
case 1:
i = (int)ceil(level / 2.0f);
return i < duration ? (i < 1 ? 1 : i) : duration;
case 2:
i = (int)ceil(duration / 5.0f * 3);
return i < duration ? (i < 1 ? 1 : i) : duration;
case 3:
i = level * 30;
return i < duration ? (i < 1 ? 1 : i) : duration;
case 4: // only used by 'LowerElement'
return ((duration != 0) ? duration : 50);
case 5:
i = duration;
// 0 value results in a 3 tick spell, else its between 1-3 ticks.
return i < 3 ? (i < 1 ? 3 : i) : 3;
case 6:
i = (int)ceil(level / 2.0f);
return i < duration ? (i < 1 ? 1 : i) : duration;
case 7:
i = level;
return (duration == 0) ? (i < 1 ? 1 : i) : duration;
case 8:
i = level + 10;
return i < duration ? (i < 1 ? 1 : i) : duration;
case 9:
i = level * 2 + 10;
return i < duration ? (i < 1 ? 1 : i) : duration;
case 10:
i = level * 3 + 10;
return i < duration ? (i < 1 ? 1 : i) : duration;
case 11:
return std::min((level + 3) * 30, duration);
case 12:
return duration;
case 15: // Don't know what the real formula for this should be. Used by Skinspikes potion.
return duration;
case 50: // lucy says this is unlimited?
return 72000; // 5 days
case 3600:
return duration ? duration : 3600;
default:
Log.Out(Logs::General, Logs::None, "CalcBuffDuration_formula: unknown formula %d", formula);
switch (formula) {
case 1:
temp = level > 3 ? level / 2 : 1;
break;
case 2:
temp = level > 3 ? level / 2 + 5 : 6;
break;
case 3:
temp = 30 * level;
break;
case 4: // only used by 'LowerElement'
temp = 50;
break;
case 5:
temp = 2;
break;
case 6:
temp = level / 2 + 2;
break;
case 7:
temp = level;
break;
case 8:
temp = level + 10;
break;
case 9:
temp = 2 * level + 10;
break;
case 10:
temp = 3 * level + 10;
break;
case 11:
temp = 30 * (level + 3);
break;
case 12:
temp = level > 7 ? level / 4 : 1;
break;
case 13:
temp = 4 * level + 10;
break;
case 14:
temp = 5 * (level + 2);
break;
case 15:
temp = 10 * (level + 10);
break;
case 50: // Permanent. Cancelled by casting/combat for perm invis, non-lev zones for lev, curing poison/curse
// counters, etc.
return -1;
case 51: // Permanent. Cancelled when out of range of aura.
return -4;
default:
// the client function has another bool parameter that if true returns -2 -- unsure
if (formula < 200)
return 0;
temp = formula;
break;
}
if (duration && duration < temp)
temp = duration;
return temp;
}
// helper function for AddBuff to determine stacking
@@ -3052,7 +3073,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
if (duration == 0) {
duration = CalcBuffDuration(caster, this, spell_id);
if (caster)
if (caster && duration > 0) // negatives are perma buffs
duration = caster->GetActSpellDuration(spell_id, duration);
}
@@ -3175,6 +3196,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
buffs[emptyslot].dot_rune = 0;
buffs[emptyslot].ExtraDIChance = 0;
buffs[emptyslot].RootBreakChance = 0;
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;
if (level_override > 0) {
buffs[emptyslot].UpdateClient = true;
@@ -3278,7 +3300,8 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite)
// and if you don't want effects just return false. interrupting here will
// break stuff
//
bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, bool isproc)
bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust,
bool isproc, int level_override)
{
// well we can't cast a spell on target without a target
@@ -3310,7 +3333,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
if(!IsValidSpell(spell_id))
return false;
uint16 caster_level = GetCasterLevel(spell_id);
uint16 caster_level = level_override > 0 ? level_override : GetCasterLevel(spell_id);
Log.Out(Logs::Detail, Logs::Spells, "Casting spell %d on %s with effective caster level %d", spell_id, spelltar->GetName(), caster_level);
@@ -3650,9 +3673,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
if(IsResistableSpell(spell_id))
{
if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id))
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true);
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust, true, false, false, level_override);
else
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust);
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust, false, false, false, level_override);
if(spell_effectiveness < 100)
{
@@ -3669,22 +3692,16 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
}
if(spelltar->IsAIControlled()){
if (spelltar->IsAIControlled()) {
int32 aggro = CheckAggroAmount(spell_id);
if(aggro > 0) {
if(!IsHarmonySpell(spell_id))
spelltar->AddToHateList(this, aggro);
else
if(!spelltar->PassCharismaCheck(this, spell_id))
spelltar->AddToHateList(this, aggro);
}
else{
uint32 newhate = spelltar->GetHateAmount(this) + aggro;
if (newhate < 1) {
spelltar->SetHateAmountOnEnt(this,1);
} else {
spelltar->SetHateAmountOnEnt(this,newhate);
}
if (aggro > 0) {
if (!IsHarmonySpell(spell_id))
spelltar->AddToHateList(this, aggro);
else if (!spelltar->PassCharismaCheck(this, spell_id))
spelltar->AddToHateList(this, aggro);
} else {
int newhate = spelltar->GetHateAmount(this) + aggro;
spelltar->SetHateAmountOnEnt(this, std::max(1, newhate));
}
}
@@ -3731,7 +3748,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
}
// cause the effects to the target
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness))
if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override))
{
// if SpellEffect returned false there's a problem applying the
// spell. It's most likely a buff that can't stack.
@@ -3830,7 +3847,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
cd->source = action->source;
cd->type = action->type;
cd->spellid = action->spell;
cd->sequence = action->sequence;
cd->meleepush_xy = action->sequence;
cd->damage = 0;
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{
@@ -4199,7 +4216,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
// pvp_resist_base
// pvp_resist_calc
// pvp_resist_cap
float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot)
float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot, int level_override)
{
if(!caster)
@@ -4342,18 +4359,19 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
int level_mod = 0;
//Adjust our resist chance based on level modifiers
int temp_level_diff = GetLevel() - caster->GetLevel();
uint8 caster_level = level_override > 0 ? level_override : caster->GetLevel();
int temp_level_diff = GetLevel() - caster_level;
//Physical Resists are calclated using their own formula derived from extensive parsing.
if (resist_type == RESIST_PHYSICAL) {
level_mod = ResistPhysical(temp_level_diff, caster->GetLevel());
level_mod = ResistPhysical(temp_level_diff, caster_level);
}
else {
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
{
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
int a = (RuleI(Casting,ResistFalloff)-1) - caster_level;
if(a > 0)
{
temp_level_diff = a;
@@ -4380,7 +4398,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
level_mod = -level_mod;
}
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
if(IsNPC() && (caster_level - GetLevel()) < -20)
{
level_mod = 1000;
}
@@ -4391,7 +4409,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
int level_diff;
if(GetLevel() >= RuleI(Casting,ResistFalloff))
{
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster_level;
if(level_diff < 0)
{
level_diff = 0;
@@ -4399,12 +4417,12 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
}
else
{
level_diff = GetLevel() - caster->GetLevel();
level_diff = GetLevel() - caster_level;
}
level_mod += (2 * level_diff);
}
}
if (CharismaCheck)
{
/*
@@ -4510,14 +4528,14 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
if(IsNPC())
{
if(GetLevel() > caster->GetLevel() && GetLevel() >= 17 && caster->GetLevel() <= 50)
if(GetLevel() > caster_level && GetLevel() >= 17 && caster_level <= 50)
{
partial_modifier += 5;
}
if(GetLevel() >= 30 && caster->GetLevel() < 50)
if(GetLevel() >= 30 && caster_level < 50)
{
partial_modifier += (caster->GetLevel() - 25);
partial_modifier += (caster_level - 25);
}
if(GetLevel() < 15)
@@ -4528,9 +4546,9 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
if(caster->IsNPC())
{
if((GetLevel() - caster->GetLevel()) >= 20)
if((GetLevel() - caster_level) >= 20)
{
partial_modifier += (GetLevel() - caster->GetLevel()) * 1.5;
partial_modifier += (GetLevel() - caster_level) * 1.5;
}
}
@@ -4708,7 +4726,7 @@ void Mob::Stun(int duration)
{
stunned = true;
stunned_timer.Start(duration);
SendStunAppearance();
SendAddPlayerState(PlayerState::Stunned);
}
}
@@ -4716,6 +4734,7 @@ void Mob::UnStun() {
if(stunned && stunned_timer.Enabled()) {
stunned = false;
stunned_timer.Disable();
SendRemovePlayerState(PlayerState::Stunned);
}
}
@@ -4745,14 +4764,12 @@ void Client::UnStun() {
void NPC::Stun(int duration) {
Mob::Stun(duration);
SetRunAnimSpeed(0);
SendPosition();
SetCurrentSpeed(0);
}
void NPC::UnStun() {
Mob::UnStun();
SetRunAnimSpeed(static_cast<int8>(GetRunspeed()));
SendPosition();
SetCurrentSpeed(GetRunspeed());
}
void Mob::Mesmerize()
@@ -5053,7 +5070,7 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) {
spells[buffs[i].spellid].base[j],
spells[buffs[i].spellid].max[j],
buffs[i].casterlevel, buffs[i].spellid);
Log.Out(Logs::General, Logs::Normal,
Log.Out(Logs::General, Logs::Normal,
"FindType: type = %d; value = %d; threshold = %d",
type, value, threshold);
if (value < threshold)
@@ -5091,7 +5108,7 @@ bool Mob::IsCombatProc(uint16 spell_id) {
return false;
}
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id) {
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id, int level_override) {
if(spell_id == SPELL_UNKNOWN)
return(false);
@@ -5102,6 +5119,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b
PermaProcs[i].spellID = spell_id;
PermaProcs[i].chance = iChance;
PermaProcs[i].base_spellID = base_spell_id;
PermaProcs[i].level_override = level_override;
Log.Out(Logs::Detail, Logs::Spells, "Added permanent proc spell %d with chance %d to slot %d", spell_id, iChance, i);
return true;
@@ -5114,6 +5132,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b
SpellProcs[i].spellID = spell_id;
SpellProcs[i].chance = iChance;
SpellProcs[i].base_spellID = base_spell_id;;
SpellProcs[i].level_override = level_override;
Log.Out(Logs::Detail, Logs::Spells, "Added spell-granted proc spell %d with chance %d to slot %d", spell_id, iChance, i);
return true;
}
@@ -5129,6 +5148,7 @@ bool Mob::RemoveProcFromWeapon(uint16 spell_id, bool bAll) {
SpellProcs[i].spellID = SPELL_UNKNOWN;
SpellProcs[i].chance = 0;
SpellProcs[i].base_spellID = SPELL_UNKNOWN;
SpellProcs[i].level_override = -1;
Log.Out(Logs::Detail, Logs::Spells, "Removed proc %d from slot %d", spell_id, i);
}
}
@@ -5254,8 +5274,31 @@ void Client::SendBuffDurationPacket(Buffs_Struct &buff)
sbf->slot = 2;
sbf->spellid = buff.spellid;
sbf->slotid = 0;
sbf->effect = 255;
sbf->level = buff.casterlevel > 0 ? buff.casterlevel : GetLevel();
if (IsEffectInSpell(buff.spellid, SE_TotalHP))
{
// If any of the lower 6 bits are set, the GUI changes MAX_HP AGAIN.
// If its set to 0 the effect is cancelled.
// 128 seems to work (ie: change only duration).
sbf->effect = 128;
}
else if (IsEffectInSpell(buff.spellid, SE_CurrentHP))
{
// This is mostly a problem when we try and update duration on a
// dot or a hp->mana conversion. Zero cancels the effect, any
// other value has the GUI doing that value at the same time server
// is doing theirs. This makes the two match.
int index = GetSpellEffectIndex(buff.spellid, SE_CurrentHP);
sbf->effect = abs(spells[buff.spellid].base[index]);
}
else
{
// Default to what old code did until we find a better fix for
// other spell lines.
sbf->effect=sbf->level;
}
sbf->bufffade = 0;
sbf->duration = buff.ticsremaining;
sbf->num_hits = buff.numhits;
@@ -5353,6 +5396,12 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
buff->entity_id = GetID();
buff->count = count;
buff->all_buffs = 1;
// there are more types, the client doesn't seem to really care though. The others are also currently hard to fill in here ...
// (see comment in common/eq_packet_structs.h)
if (for_target)
buff->type = IsClient() ? 5 : 7;
else
buff->type = 0;
uint32 index = 0;
for(unsigned int i = 0; i < buff_count; ++i)
+14
View File
@@ -231,6 +231,17 @@
#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close!
#define SPELL_LEVEL_REQ 1226 //This spell only works on people who are level %1 and under.
#define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire.
#define CORPSE_ITEM_LOST 1228 //Your items will no longer stay with you when you respawn on death. You will now need to return to your corpse for your items.
#define CORPSE_EXP_LOST 1229 //You will now lose experience when you die.
#define FLICKERS_PALE_LIGHT 1230 //Your %1 flickers with a pale light.
#define PULSES_WITH_LIGHT 1231 //Your %1 pulses with light as your vision sharpens.
#define FEEDS_WITH_POWER 1232 //Your %1 feeds you with power.
#define POWER_DRAIN_INTO 1233 //You feel your power drain into your %1.
#define SEEMS_DRAINED 1234 //Your %1 seems drained of power.
#define ALIVE_WITH_POWER 1235 //Your %1 feels alive with power.
#define SPARKLES 1236 //Your %1 sparkles.
#define GROWS_DIM 1237 //Your %1 grows dim.
#define BEGINS_TO_SHINE 1238 //Your %1 begins to shine.
#define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name.
#define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death.
#define DUEL_ACCEPTED 1384 //%1 has already accepted a duel with someone else.
@@ -339,6 +350,8 @@
#define YOU_HEAL 9068 //You have healed %1 for %2 points of damage.
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
#define GLOWS_BLUE 9074 //Your %1 glows blue.
#define GLOWS_RED 9075 //Your %1 glows red.
#define SHAKE_OFF_STUN 9077
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2.
@@ -373,6 +386,7 @@
#define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself.
#define NOT_IN_CONTROL 12368 //You do not have control of yourself right now.
#define ALREADY_CASTING 12442 //You are already casting a spell!
#define SHIMMERS_BRIEFLY 12444 //Your %1 shimmers briefly.
#define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name.
#define SENSE_CORPSE_NONE 12447 //You don't sense any corpses.
#define SCREECH_BUFF_BLOCK 12448 //Your immunity buff protected you from the spell %1!
+4
View File
@@ -1561,6 +1561,8 @@ bool ZoneDatabase::EnableRecipe(uint32 recipe_id)
if (!results.Success())
return results.RowsAffected() > 0;
return false;
}
bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
@@ -1571,4 +1573,6 @@ bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
if (!results.Success())
return results.RowsAffected() > 0;
return false;
}
+1 -1
View File
@@ -192,7 +192,7 @@ void Trap::Trigger(Mob* trigger)
int dmg = zone->random.Int(effectvalue, effectvalue2);
trigger->SetHP(trigger->GetHP() - dmg);
a->damage = dmg;
a->sequence = zone->random.Int(0, 1234567);
a->meleepush_xy = zone->random.Int(0, 1234567);
a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID();
a->spellid = 0;
a->target = trigger->GetID();
+38 -26
View File
@@ -394,6 +394,7 @@ void NPC::SetWaypointPause()
if (cur_wp_pause == 0) {
AIwalking_timer->Start(100);
AIwalking_timer->Trigger();
}
else
{
@@ -436,9 +437,8 @@ void NPC::NextGuardPosition() {
{
if(moved)
{
moved=false;
SetMoving(false);
SendPosition();
moved = false;
SetCurrentSpeed(0);
}
}
}
@@ -489,10 +489,16 @@ float Mob::CalculateHeadingToTarget(float in_x, float in_y) {
return (256*(360-angle)/360.0f);
}
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ) {
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ) {
if(GetID()==0)
return true;
if(speed <= 0)
{
SetCurrentSpeed(0);
return true;
}
if ((m_Position.x-x == 0) && (m_Position.y-y == 0)) {//spawn is at target coords
if(m_Position.z-z != 0) {
m_Position.z = z;
@@ -514,6 +520,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
return true;
}
bool send_update = false;
int compare_steps = IsBoat() ? 1 : 20;
if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) {
@@ -586,7 +593,12 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
m_TargetV.x = x - nx;
m_TargetV.y = y - ny;
m_TargetV.z = z - nz;
SetCurrentSpeed((int8)speed);
pRunAnimSpeed = speed;
if(IsClient())
{
animation = speed;
}
//pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO);
//speed *= NPC_SPEED_MULTIPLIER;
@@ -596,10 +608,10 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
// 2: get unit vector
// --------------------------------------------------------------------------
float mag = sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z);
tar_vector = speed / mag;
tar_vector = (float)speed / mag;
// mob move fix
int numsteps = (int) ( mag * 20 / speed) + 1;
int numsteps = (int) ( mag * 16.0f / (float)speed);
// mob move fix
@@ -609,9 +621,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
if (numsteps>1)
{
tar_vector=1.0f ;
m_TargetV.x = m_TargetV.x/numsteps;
m_TargetV.y = m_TargetV.y/numsteps;
m_TargetV.z = m_TargetV.z/numsteps;
m_TargetV.x = 1.25f * m_TargetV.x/(float)numsteps;
m_TargetV.y = 1.25f * m_TargetV.y/(float)numsteps;
m_TargetV.z = 1.25f *m_TargetV.z/(float)numsteps;
float new_x = m_Position.x + m_TargetV.x;
float new_y = m_Position.y + m_TargetV.y;
@@ -643,7 +655,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
}
else {
tar_vector/=20;
tar_vector/=16.0f;
float new_x = m_Position.x + m_TargetV.x*tar_vector;
float new_y = m_Position.y + m_TargetV.y*tar_vector;
@@ -699,25 +711,27 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f);
if (IsClient())
{
SendPosUpdate(1);
CastToClient()->ResetPositionTimer();
}
else
{
// force an update now
move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount);
SendPosUpdate();
SetAppearance(eaStanding, false);
}
SetAppearance(eaStanding, false);
pLastChange = Timer::GetCurrentTime();
return true;
}
bool Mob::CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ) {
if(IsNPC() || IsClient() || IsPet()) {
pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO);
speed *= NPC_SPEED_MULTIPLIER;
}
bool Mob::CalculateNewPosition2(float x, float y, float z, int speed, bool checkZ, bool calcHeading) {
return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ);
}
bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool checkZ) {
bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ, bool calcHeading) {
if(GetID()==0)
return true;
@@ -726,14 +740,12 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
float nz = m_Position.z;
// if NPC is rooted
if (speed == 0.0) {
if (speed == 0) {
SetHeading(CalculateHeadingToTarget(x, y));
if(moved){
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
moved=false;
}
SetRunAnimSpeed(0);
Log.Out(Logs::Detail, Logs::AI, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z);
return true;
}
@@ -745,8 +757,8 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
if (m_TargetV.x == 0 && m_TargetV.y == 0)
return false;
pRunAnimSpeed = (uint8)(speed*NPC_RUNANIM_RATIO);
speed *= NPC_SPEED_MULTIPLIER;
SetCurrentSpeed((int8)(speed)); //*NPC_RUNANIM_RATIO);
//speed *= NPC_SPEED_MULTIPLIER;
Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed);
@@ -1039,7 +1051,7 @@ void ZoneDatabase::AssignGrid(Client *client, int grid, int spawn2id) {
if (!results.Success())
return;
if (results.RowsAffected() != 1) {
return;
}
+30 -27
View File
@@ -742,32 +742,36 @@ void WorldServer::Process() {
break;
}
case ServerOP_SyncWorldTime: {
if(zone!=0) {
if (zone != 0 && !zone->is_zone_time_localized) {
Log.Out(Logs::Moderate, Logs::Zone_Server, "%s Received Message SyncWorldTime", __FUNCTION__);
eqTimeOfDay* newtime = (eqTimeOfDay*) pack->pBuffer;
zone->zone_time.setEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime);
eqTimeOfDay* newtime = (eqTimeOfDay*)pack->pBuffer;
zone->zone_time.SetCurrentEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime);
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct));
TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer;
zone->zone_time.getEQTimeOfDay(time(0), tod);
TimeOfDay_Struct* time_of_day = (TimeOfDay_Struct*)outapp->pBuffer;
zone->zone_time.GetCurrentEQTimeOfDay(time(0), time_of_day);
entity_list.QueueClients(0, outapp, false);
safe_delete(outapp);
//TEST
char timeMessage[255];
time_t timeCurrent = time(nullptr);
TimeOfDay_Struct eqTime;
zone->zone_time.getEQTimeOfDay( timeCurrent, &eqTime);
//if ( eqTime.hour >= 0 && eqTime.minute >= 0 )
//{
sprintf(timeMessage,"EQTime [%02d:%s%d %s]",
((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12),
(eqTime.minute < 10) ? "0" : "",
eqTime.minute,
(eqTime.hour >= 13) ? "pm" : "am"
);
Log.Out(Logs::General, Logs::Zone_Server, "Time Broadcast Packet: %s", timeMessage);
zone->GotCurTime(true);
//}
//Test
/* Buffer garbage to generate debug message */
char time_message[255];
time_t current_time = time(nullptr);
TimeOfDay_Struct eq_time;
zone->zone_time.GetCurrentEQTimeOfDay(current_time, &eq_time);
sprintf(time_message, "EQTime [%02d:%s%d %s]",
((eq_time.hour - 1) % 12) == 0 ? 12 : ((eq_time.hour - 1) % 12),
(eq_time.minute < 10) ? "0" : "",
eq_time.minute,
(eq_time.hour >= 13) ? "pm" : "am"
);
Log.Out(Logs::General, Logs::Zone_Server, "Time Broadcast Packet: %s", time_message);
zone->SetZoneHasCurrentTime(true);
}
if (zone->is_zone_time_localized){
Log.Out(Logs::General, Logs::Zone_Server, "Received request to sync time from world, but our time is localized currently");
}
break;
}
@@ -1406,17 +1410,16 @@ void WorldServer::Process() {
// CONSENT_INVALID_NAME = 397
// TARGET_NOT_FOUND = 101
safe_delete(pack);
pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
ServerPacket *scs_pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)scs_pack->pBuffer;
strcpy(scs->grantname, s->grantname);
strcpy(scs->ownername, s->ownername);
scs->permission = s->permission;
scs->zone_id = s->zone_id;
scs->instance_id = s->instance_id;
scs->message_string_id = TARGET_NOT_FOUND;
worldserver.SendPacket(pack);
safe_delete(pack);
worldserver.SendPacket(scs_pack);
safe_delete(scs_pack);
}
break;
}
+43 -16
View File
@@ -241,8 +241,8 @@ bool Zone::LoadZoneObjects() {
data.object_type = type;
data.linked_list_addr[0] = 0;
data.linked_list_addr[1] = 0;
data.unknown008 = (uint32)atoi(row[11]);
data.unknown010 = (uint32)atoi(row[12]);
data.size = (uint32)atoi(row[11]);
data.solidtype = (uint32)atoi(row[12]);
data.unknown020 = (uint32)atoi(row[13]);
data.unknown024 = (uint32)atoi(row[14]);
data.unknown076 = (uint32)atoi(row[15]);
@@ -697,7 +697,7 @@ void Zone::Shutdown(bool quite)
Log.Out(Logs::General, Logs::Status, "Zone Shutdown: %s (%i)", zone->GetShortName(), zone->GetZoneID());
petition_list.ClearPetitions();
zone->GotCurTime(false);
zone->SetZoneHasCurrentTime(false);
if (!quite)
Log.Out(Logs::General, Logs::Normal, "Zone shutdown: going to sleep");
ZoneLoaded = false;
@@ -760,6 +760,8 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
qGlobals = nullptr;
default_ruleset = 0;
is_zone_time_localized = false;
loglevelvar = 0;
merchantvar = 0;
tradevar = 0;
@@ -805,7 +807,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
totalBS = 0;
aas = nullptr;
totalAAs = 0;
gottime = false;
zone_has_current_time = false;
Instance_Shutdown_Timer = nullptr;
bool is_perma = false;
@@ -1484,7 +1486,7 @@ void Zone::Repop(uint32 delay) {
void Zone::GetTimeSync()
{
if (worldserver.Connected() && !gottime) {
if (worldserver.Connected() && !zone_has_current_time) {
ServerPacket* pack = new ServerPacket(ServerOP_GetWorldTime, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
@@ -1508,17 +1510,42 @@ void Zone::SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute
}
}
void Zone::SetTime(uint8 hour, uint8 minute)
void Zone::SetTime(uint8 hour, uint8 minute, bool update_world /*= true*/)
{
if (worldserver.Connected()) {
ServerPacket* pack = new ServerPacket(ServerOP_SetWorldTime, sizeof(eqTimeOfDay));
eqTimeOfDay* eqtod = (eqTimeOfDay*)pack->pBuffer;
zone_time.getEQTimeOfDay(time(0), &eqtod->start_eqtime);
eqtod->start_eqtime.minute=minute;
eqtod->start_eqtime.hour=hour;
eqtod->start_realtime=time(0);
printf("Setting master time on world server to: %d:%d (%d)\n", hour, minute, (int)eqtod->start_realtime);
worldserver.SendPacket(pack);
eqTimeOfDay* eq_time_of_day = (eqTimeOfDay*)pack->pBuffer;
zone_time.GetCurrentEQTimeOfDay(time(0), &eq_time_of_day->start_eqtime);
eq_time_of_day->start_eqtime.minute = minute;
eq_time_of_day->start_eqtime.hour = hour;
eq_time_of_day->start_realtime = time(0);
/* By Default we update worlds time, but we can optionally no update world which updates the rest of the zone servers */
if (update_world){
Log.Out(Logs::General, Logs::Zone_Server, "Setting master time on world server to: %d:%d (%d)\n", hour, minute, (int)eq_time_of_day->start_realtime);
worldserver.SendPacket(pack);
/* Set Time Localization Flag */
zone->is_zone_time_localized = false;
}
/* When we don't update world, we are localizing ourselves, we become disjointed from normal syncs and set time locally */
else{
Log.Out(Logs::General, Logs::Zone_Server, "Setting zone localized time...");
zone->zone_time.SetCurrentEQTimeOfDay(eq_time_of_day->start_eqtime, eq_time_of_day->start_realtime);
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct));
TimeOfDay_Struct* time_of_day = (TimeOfDay_Struct*)outapp->pBuffer;
zone->zone_time.GetCurrentEQTimeOfDay(time(0), time_of_day);
entity_list.QueueClients(0, outapp, false);
safe_delete(outapp);
/* Set Time Localization Flag */
zone->is_zone_time_localized = true;
}
safe_delete(pack);
}
}
@@ -1900,7 +1927,7 @@ bool Zone::IsSpellBlocked(uint32 spell_id, const glm::vec3& location)
}
case 2:
{
if (!IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference))
if (IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference))
return true;
break;
}
@@ -1934,7 +1961,7 @@ const char* Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3& locat
}
case 2:
{
if(!IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference))
if(IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference))
return blocked_spells[x].message;
break;
}
@@ -2209,9 +2236,9 @@ void Zone::ReloadWorld(uint32 Option){
entity_list.ClearAreas();
parse->ReloadQuests();
} else if(Option == 1) {
zone->Repop(0);
entity_list.ClearAreas();
parse->ReloadQuests();
zone->Repop(0);
}
}
+8 -3
View File
@@ -43,6 +43,7 @@ struct ZonePoint
int32 target_zone_instance;
uint32 client_version_mask;
};
struct ZoneClientAuth_Struct {
uint32 ip; // client's IP address
uint32 wid; // client's WorldID#
@@ -85,6 +86,10 @@ public:
Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name);
~Zone();
/* When zone has its own version of time */
bool is_zone_time_localized;
bool Init(bool iStaticZone);
bool LoadZoneCFG(const char* filename, uint16 instance_id, bool DontLoadDefault = false);
bool SaveZoneCFG();
@@ -153,7 +158,7 @@ public:
inline bool InstantGrids() { return(!initgrids_timer.Enabled()); }
void SetStaticZone(bool sz) { staticzone = sz; }
inline bool IsStaticZone() { return staticzone; }
inline void GotCurTime(bool time) { gottime = time; }
inline void SetZoneHasCurrentTime(bool time) { zone_has_current_time = time; }
void SpawnConditionChanged(const SpawnCondition &c, int16 old_value);
@@ -206,7 +211,7 @@ public:
EQTime zone_time;
void GetTimeSync();
void SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute);
void SetTime(uint8 hour, uint8 minute);
void SetTime(uint8 hour, uint8 minute, bool update_world = true);
void weatherSend();
bool CanBind() const { return(can_bind); }
@@ -319,7 +324,7 @@ private:
bool staticzone;
bool gottime;
bool zone_has_current_time;
uint32 pQueuedMerchantsWorkID;
uint32 pQueuedTempMerchantsWorkID;
+155 -146
View File
@@ -196,7 +196,7 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct
zone_data->time_type = atoi(row[30]);
//not in the DB yet:
zone_data->gravity = atof(row[56]);
zone_data->gravity = atof(row[56]);
Log.Out(Logs::General, Logs::Debug, "Zone Gravity is %f", zone_data->gravity);
allow_mercs = true;
@@ -248,7 +248,7 @@ void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint3
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);
QueryDatabase(query);
return;
}
@@ -258,14 +258,14 @@ void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint3
"start, "
"duration, "
"instance_id) "
"VALUES "
"VALUES "
"(%u, "
"%u, "
"%u, "
"%u)",
spawn2_id,
spawn2_id,
current_time,
time_left,
time_left,
instance_id
);
QueryDatabase(query);
@@ -1054,8 +1054,8 @@ bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Str
for (i = 0; i < MAX_PP_LANGUAGE; ++i)
pp->languages[i] = 0;
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
if (i < MAX_PP_LANGUAGE){
pp->languages[i] = atoi(row[1]);
}
@@ -1102,14 +1102,14 @@ bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct
"FROM "
"`character_skills` "
"WHERE `id` = %u ORDER BY `skill_id`", character_id);
auto results = database.QueryDatabase(query);
auto results = database.QueryDatabase(query);
int i = 0;
/* Initialize Skill */
for (i = 0; i < MAX_PP_SKILL; ++i)
pp->skills[i] = 0;
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
if (i < MAX_PP_SKILL)
pp->skills[i] = atoi(row[1]);
}
@@ -1260,7 +1260,7 @@ bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struc
bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp){
std::string query = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %u LIMIT 2", character_id);
auto results = database.QueryDatabase(query);
auto results = database.QueryDatabase(query);
for (auto row = results.begin(); row != results.end(); ++row) {
@@ -1706,15 +1706,15 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru
pp->careerRadCrystals,
pp->currentEbonCrystals,
pp->careerEbonCrystals);
auto results = database.QueryDatabase(query);
Log.Out(Logs::General, Logs::None, "Saving Currency for character ID: %i, done", character_id);
auto results = database.QueryDatabase(query);
Log.Out(Logs::General, Logs::None, "Saving Currency for character ID: %i, done", character_id);
return true;
}
bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level){
std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value)"
" VALUES (%u, %u, %u)",
character_id, aa_id, current_level);
bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges){
std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value, charges)"
" VALUES (%u, %u, %u, %u)",
character_id, aa_id, current_level, charges);
auto results = QueryDatabase(rquery);
Log.Out(Logs::General, Logs::None, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level);
return true;
@@ -1803,7 +1803,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
std::string where_condition = "";
if (bulk_load){
Log.Out(Logs::General, Logs::Debug, "Performing bulk NPC Types 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 "
@@ -1911,7 +1911,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
"npc_types.handtexture, "
"npc_types.legtexture, "
"npc_types.feettexture "
"FROM npc_types %s",
"FROM npc_types %s",
where_condition.c_str()
);
@@ -2184,7 +2184,7 @@ const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 client
return nullptr;
}
const NPCType *npc;
const NPCType *npc = nullptr;
// Process each row returned.
for (auto row = results.begin(); row != results.end(); ++row) {
@@ -2370,7 +2370,7 @@ bool ZoneDatabase::LoadCurrentMerc(Client *client) {
if(!results.Success())
return false;
if(results.RowCount() == 0)
return false;
@@ -2505,7 +2505,7 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) {
"TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, "
"CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, "
"caston_x, Persistent, caston_y, caston_z, ExtraDIChance) "
"VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);",
"VALUES (%u, %u, %u, %u, %u, %d, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);",
merc->GetMercID(), buffs[buffCount].spellid, buffs[buffCount].casterlevel,
spells[buffs[buffCount].spellid].buffdurationformula, buffs[buffCount].ticsremaining,
CalculatePoisonCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0,
@@ -2981,47 +2981,50 @@ void ZoneDatabase::SaveBuffs(Client *client) {
query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, "
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, "
"magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance) "
"VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', "
"'%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid,
"magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance, "
"instrument_mod) "
"VALUES('%u', '%u', '%u', '%u', '%s', '%d', '%u', '%u', '%u', '%u', '%u', '%u', "
"'%i', '%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid,
buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining,
buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune,
buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune,
buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z,
buffs[index].ExtraDIChance);
buffs[index].ExtraDIChance, buffs[index].instrument_mod);
QueryDatabase(query);
}
}
void ZoneDatabase::LoadBuffs(Client *client) {
void ZoneDatabase::LoadBuffs(Client *client)
{
Buffs_Struct *buffs = client->GetBuffs();
uint32 max_slots = client->GetMaxBuffSlots();
for(int index = 0; index < max_slots; ++index)
for (int index = 0; index < max_slots; ++index)
buffs[index].spellid = SPELL_UNKNOWN;
std::string query = StringFormat("SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, "
"counters, numhits, melee_rune, magic_rune, persistent, dot_rune, "
"caston_x, caston_y, caston_z, ExtraDIChance "
"FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID());
auto results = QueryDatabase(query);
if (!results.Success()) {
"counters, numhits, melee_rune, magic_rune, persistent, dot_rune, "
"caston_x, caston_y, caston_z, ExtraDIChance, instrument_mod "
"FROM `character_buffs` WHERE `character_id` = '%u'",
client->CharacterID());
auto results = QueryDatabase(query);
if (!results.Success()) {
return;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 slot_id = atoul(row[1]);
if(slot_id >= client->GetMaxBuffSlots())
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 slot_id = atoul(row[1]);
if (slot_id >= client->GetMaxBuffSlots())
continue;
uint32 spell_id = atoul(row[0]);
if(!IsValidSpell(spell_id))
continue;
uint32 spell_id = atoul(row[0]);
if (!IsValidSpell(spell_id))
continue;
Client *caster = entity_list.GetClientByName(row[3]);
Client *caster = entity_list.GetClientByName(row[3]);
uint32 caster_level = atoi(row[2]);
uint32 ticsremaining = atoul(row[4]);
int32 ticsremaining = atoi(row[4]);
uint32 counters = atoul(row[5]);
uint32 numhits = atoul(row[6]);
uint32 melee_rune = atoul(row[7]);
@@ -3032,53 +3035,54 @@ void ZoneDatabase::LoadBuffs(Client *client) {
int32 caston_y = atoul(row[12]);
int32 caston_z = atoul(row[13]);
int32 ExtraDIChance = atoul(row[14]);
uint32 instrument_mod = atoul(row[15]);
buffs[slot_id].spellid = spell_id;
buffs[slot_id].casterlevel = caster_level;
buffs[slot_id].casterlevel = caster_level;
if(caster) {
buffs[slot_id].casterid = caster->GetID();
strcpy(buffs[slot_id].caster_name, caster->GetName());
buffs[slot_id].client = true;
} else {
buffs[slot_id].casterid = 0;
if (caster) {
buffs[slot_id].casterid = caster->GetID();
strcpy(buffs[slot_id].caster_name, caster->GetName());
buffs[slot_id].client = true;
} else {
buffs[slot_id].casterid = 0;
strcpy(buffs[slot_id].caster_name, "");
buffs[slot_id].client = false;
}
}
buffs[slot_id].ticsremaining = ticsremaining;
buffs[slot_id].ticsremaining = ticsremaining;
buffs[slot_id].counters = counters;
buffs[slot_id].numhits = numhits;
buffs[slot_id].melee_rune = melee_rune;
buffs[slot_id].magic_rune = magic_rune;
buffs[slot_id].persistant_buff = persistent? true: false;
buffs[slot_id].persistant_buff = persistent ? true : false;
buffs[slot_id].dot_rune = dot_rune;
buffs[slot_id].caston_x = caston_x;
buffs[slot_id].caston_y = caston_y;
buffs[slot_id].caston_z = caston_z;
buffs[slot_id].ExtraDIChance = ExtraDIChance;
buffs[slot_id].RootBreakChance = 0;
buffs[slot_id].UpdateClient = false;
}
buffs[slot_id].caston_y = caston_y;
buffs[slot_id].caston_z = caston_z;
buffs[slot_id].ExtraDIChance = ExtraDIChance;
buffs[slot_id].RootBreakChance = 0;
buffs[slot_id].UpdateClient = false;
buffs[slot_id].instrument_mod = instrument_mod;
}
max_slots = client->GetMaxBuffSlots();
for(int index = 0; index < max_slots; ++index) {
if(!IsValidSpell(buffs[index].spellid))
for (int index = 0; index < max_slots; ++index) {
if (!IsValidSpell(buffs[index].spellid))
continue;
for(int effectIndex = 0; effectIndex < 12; ++effectIndex) {
for (int effectIndex = 0; effectIndex < 12; ++effectIndex) {
if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) {
buffs[index].spellid = SPELL_UNKNOWN;
break;
}
buffs[index].spellid = SPELL_UNKNOWN;
break;
}
if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) {
if(buffs[index].persistant_buff)
break;
if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) {
if (buffs[index].persistant_buff)
break;
buffs[index].spellid = SPELL_UNKNOWN;
buffs[index].spellid = SPELL_UNKNOWN;
break;
}
}
@@ -3124,16 +3128,16 @@ void ZoneDatabase::SavePetInfo(Client *client)
if (query.length() == 0)
query = StringFormat("INSERT INTO `character_pet_buffs` "
"(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, "
"`ticsremaining`, `counters`) "
"VALUES (%u, %u, %u, %u, %u, %u, %d)",
"`ticsremaining`, `counters`, `instrument_mod`) "
"VALUES (%u, %u, %u, %u, %u, %d, %d, %u)",
client->CharacterID(), pet, index, petinfo->Buffs[index].spellid,
petinfo->Buffs[index].level, petinfo->Buffs[index].duration,
petinfo->Buffs[index].counters);
petinfo->Buffs[index].counters, petinfo->Buffs[index].bard_modifier);
else
query += StringFormat(", (%u, %u, %u, %u, %u, %u, %d)",
query += StringFormat(", (%u, %u, %u, %u, %u, %d, %d, %u)",
client->CharacterID(), pet, index, petinfo->Buffs[index].spellid,
petinfo->Buffs[index].level, petinfo->Buffs[index].duration,
petinfo->Buffs[index].counters);
petinfo->Buffs[index].counters, petinfo->Buffs[index].bard_modifier);
}
database.QueryDatabase(query);
query.clear();
@@ -3171,7 +3175,8 @@ void ZoneDatabase::UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type
QueryDatabase(query);
}
void ZoneDatabase::LoadPetInfo(Client *client) {
void ZoneDatabase::LoadPetInfo(Client *client)
{
// Load current pet and suspended pet
PetInfo *petinfo = client->GetPetInfo(0);
@@ -3180,17 +3185,18 @@ void ZoneDatabase::LoadPetInfo(Client *client) {
memset(petinfo, 0, sizeof(PetInfo));
memset(suspended, 0, sizeof(PetInfo));
std::string query = StringFormat("SELECT `pet`, `petname`, `petpower`, `spell_id`, "
"`hp`, `mana`, `size` FROM `character_pet_info` "
"WHERE `char_id` = %u", client->CharacterID());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
std::string query = StringFormat("SELECT `pet`, `petname`, `petpower`, `spell_id`, "
"`hp`, `mana`, `size` FROM `character_pet_info` "
"WHERE `char_id` = %u",
client->CharacterID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return;
}
PetInfo *pi;
PetInfo *pi;
for (auto row = results.begin(); row != results.end(); ++row) {
uint16 pet = atoi(row[0]);
uint16 pet = atoi(row[0]);
if (pet == 0)
pi = petinfo;
@@ -3199,7 +3205,7 @@ void ZoneDatabase::LoadPetInfo(Client *client) {
else
continue;
strncpy(pi->Name,row[1],64);
strncpy(pi->Name, row[1], 64);
pi->petpower = atoi(row[2]);
pi->SpellID = atoi(row[3]);
pi->HP = atoul(row[4]);
@@ -3207,56 +3213,60 @@ void ZoneDatabase::LoadPetInfo(Client *client) {
pi->size = atof(row[6]);
}
query = StringFormat("SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, "
"`ticsremaining`, `counters` FROM `character_pet_buffs` "
"WHERE `char_id` = %u", client->CharacterID());
results = QueryDatabase(query);
if (!results.Success()) {
return;
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint16 pet = atoi(row[0]);
if (pet == 0)
pi = petinfo;
else if (pet == 1)
pi = suspended;
else
continue;
uint32 slot_id = atoul(row[1]);
if(slot_id >= RuleI(Spells, MaxTotalSlotsPET))
continue;
uint32 spell_id = atoul(row[2]);
if(!IsValidSpell(spell_id))
continue;
uint32 caster_level = atoi(row[3]);
int caster_id = 0;
// The castername field is currently unused
uint32 ticsremaining = atoul(row[5]);
uint32 counters = atoul(row[6]);
pi->Buffs[slot_id].spellid = spell_id;
pi->Buffs[slot_id].level = caster_level;
pi->Buffs[slot_id].player_id = caster_id;
pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs
pi->Buffs[slot_id].duration = ticsremaining;
pi->Buffs[slot_id].counters = counters;
}
query = StringFormat("SELECT `pet`, `slot`, `item_id` "
"FROM `character_pet_inventory` "
"WHERE `char_id`=%u",client->CharacterID());
results = database.QueryDatabase(query);
if (!results.Success()) {
query = StringFormat("SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, "
"`ticsremaining`, `counters`, `instrument_mod` FROM `character_pet_buffs` "
"WHERE `char_id` = %u",
client->CharacterID());
results = QueryDatabase(query);
if (!results.Success()) {
return;
}
for(auto row = results.begin(); row != results.end(); ++row) {
uint16 pet = atoi(row[0]);
for (auto row = results.begin(); row != results.end(); ++row) {
uint16 pet = atoi(row[0]);
if (pet == 0)
pi = petinfo;
else if (pet == 1)
pi = suspended;
else
continue;
uint32 slot_id = atoul(row[1]);
if (slot_id >= RuleI(Spells, MaxTotalSlotsPET))
continue;
uint32 spell_id = atoul(row[2]);
if (!IsValidSpell(spell_id))
continue;
uint32 caster_level = atoi(row[3]);
int caster_id = 0;
// The castername field is currently unused
int32 ticsremaining = atoi(row[5]);
uint32 counters = atoul(row[6]);
uint8 bard_mod = atoul(row[7]);
pi->Buffs[slot_id].spellid = spell_id;
pi->Buffs[slot_id].level = caster_level;
pi->Buffs[slot_id].player_id = caster_id;
pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs
pi->Buffs[slot_id].duration = ticsremaining;
pi->Buffs[slot_id].counters = counters;
pi->Buffs[slot_id].bard_modifier = bard_mod;
}
query = StringFormat("SELECT `pet`, `slot`, `item_id` "
"FROM `character_pet_inventory` "
"WHERE `char_id`=%u",
client->CharacterID());
results = database.QueryDatabase(query);
if (!results.Success()) {
return;
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint16 pet = atoi(row[0]);
if (pet == 0)
pi = petinfo;
else if (pet == 1)
@@ -3266,11 +3276,10 @@ void ZoneDatabase::LoadPetInfo(Client *client) {
int slot = atoi(row[1]);
if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END)
continue;
pi->Items[slot] = atoul(row[2]);
}
continue;
pi->Items[slot] = atoul(row[2]);
}
}
bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id) {
@@ -3390,7 +3399,7 @@ bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, in
"ON DUPLICATE KEY UPDATE `current_value`=%i,`temp`=%i",
char_id, faction_id, value, temp, value, temp);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
else
@@ -3535,7 +3544,7 @@ uint32 ZoneDatabase::GetCharacterCorpseDecayTimer(uint32 corpse_db_id){
auto results = QueryDatabase(query);
auto row = results.begin();
if (results.Success() && results.RowsAffected() != 0)
return atoul(row[0]);
return atoul(row[0]);
return 0;
}
@@ -3622,8 +3631,8 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui
"`wc_6` = %u, "
"`wc_7` = %u, "
"`wc_8` = %u, "
"`wc_9` = %u ",
EscapeString(charname).c_str(),
"`wc_9` = %u ",
EscapeString(charname).c_str(),
zoneid,
instanceid,
charid,
@@ -3676,21 +3685,21 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui
corpse_items_query = StringFormat("REPLACE INTO `character_corpse_items` \n"
" (corpse_id, equip_slot, item_id, charges, aug_1, aug_2, aug_3, aug_4, aug_5, aug_6, attuned) \n"
" VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u) \n",
last_insert_id,
last_insert_id,
dbpc->items[i].equip_slot,
dbpc->items[i].item_id,
dbpc->items[i].charges,
dbpc->items[i].aug_1,
dbpc->items[i].aug_2,
dbpc->items[i].aug_3,
dbpc->items[i].aug_4,
dbpc->items[i].item_id,
dbpc->items[i].charges,
dbpc->items[i].aug_1,
dbpc->items[i].aug_2,
dbpc->items[i].aug_3,
dbpc->items[i].aug_4,
dbpc->items[i].aug_5,
dbpc->items[i].aug_6,
dbpc->items[i].attuned
);
first_entry = 1;
}
else{
else{
corpse_items_query = corpse_items_query + StringFormat(", (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u) \n",
last_insert_id,
dbpc->items[i].equip_slot,
+1 -1
View File
@@ -278,7 +278,7 @@ public:
bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, const glm::vec4& position, uint8 is_home);
bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);
bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level);
bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges);
bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color);