Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Paul Coene
2016-08-23 13:55:52 -04:00
88 changed files with 3316 additions and 592 deletions
+7 -3
View File
@@ -867,7 +867,7 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
aai->max_level = ability->GetMaxLevel(this);
aai->prev_id = rank->prev_id;
if(rank->next && !CanUseAlternateAdvancementRank(rank->next) || ability->charges > 0) {
if((rank->next && !CanUseAlternateAdvancementRank(rank->next)) || ability->charges > 0) {
aai->next_id = -1;
} else {
aai->next_id = rank->next_id;
@@ -1183,12 +1183,12 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
CommonBreakInvisible();
// Bards can cast instant cast AAs while they are casting another song
if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), ALTERNATE_ABILITY_SPELL_SLOT, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQEmu::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
return;
}
ExpendAlternateAdvancementCharge(ability->id);
} else {
if(!CastSpell(rank->spell, target_id, ALTERNATE_ABILITY_SPELL_SLOT, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
if(!CastSpell(rank->spell, target_id, EQEmu::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
return;
}
}
@@ -1240,6 +1240,10 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
CastToClient()->GetEPP().expended_aa += r->cost;
}
}
if (IsClient()) {
auto c = CastToClient();
c->RemoveExpendedAA(ability->first_rank_id);
}
aa_ranks.erase(iter.first);
}
+53 -7
View File
@@ -156,10 +156,21 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) {
return;
}
if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GREEN ) {
towho->Message(0, "...%s is red to me (basically)", mob->GetName(),
dist2, iAggroRange2);
return;
if (RuleB(Aggro, UseLevelAggro))
{
if (GetLevel() < 18 && mob->GetLevelCon(GetLevel()) == CON_GREEN && GetBodyType() != 3)
{
towho->Message(0, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2);
return;
}
}
else
{
if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GREEN ) {
towho->Message(0, "...%s is red to me (basically)", mob->GetName(),
dist2, iAggroRange2);
return;
}
}
if(verbose) {
@@ -321,11 +332,12 @@ bool Mob::CheckWillAggro(Mob *mob) {
int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap
if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE)
heroicCHA_mod = THREATENLY_ARRGO_CHANCE;
if
if (RuleB(Aggro, UseLevelAggro) &&
(
//old InZone check taken care of above by !mob->CastToClient()->Connected()
(
( GetINT() <= RuleI(Aggro, IntAggroThreshold) )
( GetLevel() >= 18 )
||(GetBodyType() == 3)
||( mob->IsClient() && mob->CastToClient()->IsSitting() )
||( mob->GetLevelCon(GetLevel()) != CON_GREEN )
@@ -344,6 +356,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
)
)
)
)
{
//FatherNiwtit: make sure we can see them. last since it is very expensive
if(CheckLosFN(mob)) {
@@ -351,6 +364,39 @@ bool Mob::CheckWillAggro(Mob *mob) {
return( mod_will_aggro(mob, this) );
}
}
else
{
if
(
//old InZone check taken care of above by !mob->CastToClient()->Connected()
(
( GetINT() <= RuleI(Aggro, IntAggroThreshold) )
||( mob->IsClient() && mob->CastToClient()->IsSitting() )
||( mob->GetLevelCon(GetLevel()) != CON_GREEN )
)
&&
(
(
fv == FACTION_SCOWLS
||
(mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr)
||
(
fv == FACTION_THREATENLY
&& zone->random.Roll(THREATENLY_ARRGO_CHANCE - heroicCHA_mod)
)
)
)
)
{
//FatherNiwtit: make sure we can see them. last since it is very expensive
if(CheckLosFN(mob)) {
Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName());
return( mod_will_aggro(mob, this) );
}
}
}
Log.Out(Logs::Detail, Logs::Aggro, "Is In zone?:%d\n", mob->InZone());
Log.Out(Logs::Detail, Logs::Aggro, "Dist^2: %f\n", dist2);
@@ -462,7 +508,7 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
{
//if they are in range, make sure we are not green...
//then jump in if they are our friend
if(attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN)
if(mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN)
{
bool useprimfaction = false;
if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction())
+63 -70
View File
@@ -23,6 +23,7 @@
#include "../common/skills.h"
#include "../common/spdat.h"
#include "../common/string_util.h"
#include "../common/data_verification.h"
#include "queryserv.h"
#include "quest_parser_collection.h"
#include "string_ids.h"
@@ -369,18 +370,15 @@ bool Mob::AvoidDamage(Mob *other, int32 &damage, int hand)
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
}
//////////////////////////////////////////////////////////
// make enrage same as riposte
/////////////////////////////////////////////////////////
if (IsEnraged() && InFront) {
damage = -3;
Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack.");
}
// riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo
bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance;
// Need to check if we have something in MainHand to actually attack with (or fists)
if (hand != EQEmu::legacy::SlotRange && CanThisClassRiposte() && InFront && !ImmuneRipo) {
if (IsEnraged()) {
damage = -3;
Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack.");
return true;
}
if (IsClient())
CastToClient()->CheckIncreaseSkill(EQEmu::skills::SkillRiposte, other, -10);
// check auto discs ... I guess aa/items too :P
@@ -1205,7 +1203,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1,
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, true, -1, false, special);
@@ -1802,7 +1800,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
if(attacked_timer.Check())
{
Log.Out(Logs::Detail, Logs::Combat, "Triggering EVENT_ATTACK due to attack by %s", other->GetName());
Log.Out(Logs::Detail, Logs::Combat, "Triggering EVENT_ATTACK due to attack by %s", other ? other->GetName() : "nullptr");
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
}
attacked_timer.Start(CombatEventTimer_expire);
@@ -1821,7 +1819,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill
if(IsLDoNTrapped())
{
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
SpellFinished(GetLDoNTrapSpellID(), other, 10, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
SpellFinished(GetLDoNTrapSpellID(), other, EQEmu::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
SetLDoNTrapSpellID(0);
SetLDoNTrapped(false);
SetLDoNTrapDetected(false);
@@ -3169,68 +3167,63 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
BuffFadeByEffect(SE_Mez);
}
//check stun chances if bashing
if (damage > 0 && ((skill_used == EQEmu::skills::SkillBash || skill_used == EQEmu::skills::SkillKick) && attacker)) {
// NPCs can stun with their bash/kick as soon as they receive it.
// Clients can stun mobs under level 56 with their kick when they get level 55 or greater.
// Clients have a chance to stun if the mob is 56+
// Calculate the chance to stun
int stun_chance = 0;
if (!GetSpecialAbility(UNSTUNABLE)) {
if (attacker->IsNPC()) {
stun_chance = RuleI(Combat, NPCBashKickStunChance);
} else if (attacker->IsClient()) {
// Less than base immunity
// Client vs. Client always uses the chance
if (!IsClient() && GetLevel() <= RuleI(Spells, BaseImmunityLevel)) {
if (skill_used == EQEmu::skills::SkillBash) // Bash always will
stun_chance = 100;
else if (attacker->GetLevel() >= RuleI(Combat, ClientStunLevel))
stun_chance = 100; // only if you're over level 55 and using kick
} else { // higher than base immunity or Client vs. Client
// not sure on this number, use same as NPC for now
if (skill_used == EQEmu::skills::SkillKick && attacker->GetLevel() < RuleI(Combat, ClientStunLevel))
stun_chance = RuleI(Combat, NPCBashKickStunChance);
else if (skill_used == EQEmu::skills::SkillBash)
stun_chance = RuleI(Combat, NPCBashKickStunChance) +
attacker->spellbonuses.StunBashChance +
attacker->itembonuses.StunBashChance +
attacker->aabonuses.StunBashChance;
}
}
// broken up for readability
// This is based on what the client is doing
// We had a bunch of stuff like BaseImmunityLevel checks, which I think is suppose to just be for spells
// This is missing some merc checks, but those mostly just skipped the spell bonuses I think ...
bool can_stun = false;
int stunbash_chance = 0; // bonus
if (attacker) {
if (skill_used == EQEmu::skills::SkillBash) {
can_stun = true;
if (attacker->IsClient())
stunbash_chance = attacker->spellbonuses.StunBashChance +
attacker->itembonuses.StunBashChance +
attacker->aabonuses.StunBashChance;
} else if (skill_used == EQEmu::skills::SkillKick &&
(attacker->GetLevel() > 55 || attacker->IsNPC()) && GetClass() == WARRIOR) {
can_stun = true;
}
if (stun_chance && zone->random.Roll(stun_chance)) {
// Passed stun, try to resist now
int stun_resist = itembonuses.StunResist + spellbonuses.StunResist;
int frontal_stun_resist = itembonuses.FrontalStunResist + spellbonuses.FrontalStunResist;
Log.Out(Logs::Detail, Logs::Combat, "Stun passed, checking resists. Was %d chance.", stun_chance);
if (IsClient()) {
stun_resist += aabonuses.StunResist;
frontal_stun_resist += aabonuses.FrontalStunResist;
}
// frontal stun check for ogres/bonuses
if (((GetBaseRace() == OGRE && IsClient()) ||
(frontal_stun_resist && zone->random.Roll(frontal_stun_resist))) &&
!attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) {
Log.Out(Logs::Detail, Logs::Combat, "Frontal stun resisted. %d chance.", frontal_stun_resist);
} else {
// Normal stun resist check.
if (stun_resist && zone->random.Roll(stun_resist)) {
if ((GetBaseRace() == OGRE || GetBaseRace() == OGGOK_CITIZEN) &&
!attacker->BehindMob(this, attacker->GetX(), attacker->GetY()))
can_stun = false;
if (GetSpecialAbility(UNSTUNABLE))
can_stun = false;
}
if (can_stun) {
int bashsave_roll = zone->random.Int(0, 100);
if (bashsave_roll > 98 || bashsave_roll > (55 - stunbash_chance)) {
// did stun -- roll other resists
// SE_FrontalStunResist description says any angle now a days
int stun_resist2 = spellbonuses.FrontalStunResist + itembonuses.FrontalStunResist +
aabonuses.FrontalStunResist;
if (zone->random.Int(1, 100) > stun_resist2) {
// stun resist 2 failed
// time to check SE_StunResist and mod2 stun resist
int stun_resist =
spellbonuses.StunResist + itembonuses.StunResist + aabonuses.StunResist;
if (zone->random.Int(0, 100) >= stun_resist) {
// did stun
// nothing else to check!
Stun(2000); // straight 2 seconds every time
} else {
// stun resist passed!
if (IsClient())
Message_StringID(MT_Stun, SHAKE_OFF_STUN);
Log.Out(Logs::Detail, Logs::Combat, "Stun Resisted. %d chance.", stun_resist);
} else {
Log.Out(Logs::Detail, Logs::Combat, "Stunned. %d resist chance.", stun_resist);
Stun(zone->random.Int(0, 2) * 1000); // 0-2 seconds
}
} else {
// stun resist 2 passed!
if (IsClient())
Message_StringID(MT_Stun, AVOID_STUNNING_BLOW);
}
} else {
Log.Out(Logs::Detail, Logs::Combat, "Stun failed. %d chance.", stun_chance);
// main stun failed -- extra interrupt roll
if (IsCasting() &&
!EQEmu::ValueWithin(casting_spell_id, 859, 1023)) // these spells are excluded
// 90% chance >< -- stun immune won't reach this branch though :(
if (zone->random.Int(0, 9) > 1)
InterruptSpell();
}
}
@@ -3268,7 +3261,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
a->damage = damage;
a->spellid = spell_id;
a->special = special;
a->meleepush_xy = attacker->GetHeading() * 2.0f;
a->meleepush_xy = attacker ? attacker->GetHeading() * 2.0f : 0.0f;
if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used);
@@ -3805,7 +3798,7 @@ void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage)
void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts)
{
if(damage < 1)
if(damage < 1 || !defender)
return;
// decided to branch this into it's own function since it's going to be duplicating a lot of the
@@ -3826,8 +3819,8 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
bool IsBerskerSPA = false;
//1: Try Slay Undead
if (defender && (defender->GetBodyType() == BT_Undead ||
defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) {
if (defender->GetBodyType() == BT_Undead ||
defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire) {
int32 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0];
if (SlayRateBonus) {
float slayChance = static_cast<float>(SlayRateBonus) / 10000.0f;
+36 -18
View File
@@ -1709,8 +1709,8 @@ bool Bot::LoadPet()
NPC *pet_inst = GetPet()->CastToNPC();
SpellBuff_Struct pet_buffs[BUFF_COUNT];
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * BUFF_COUNT));
SpellBuff_Struct pet_buffs[PET_BUFF_COUNT];
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * PET_BUFF_COUNT));
if (!botdb.LoadPetBuffs(GetBotID(), pet_buffs))
bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetBuffs(), GetCleanName());
@@ -1741,11 +1741,11 @@ bool Bot::SavePet()
return false;
char* pet_name = new char[64];
SpellBuff_Struct pet_buffs[BUFF_COUNT];
SpellBuff_Struct pet_buffs[PET_BUFF_COUNT];
uint32 pet_items[EQEmu::legacy::EQUIPMENT_SIZE];
memset(pet_name, 0, 64);
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * BUFF_COUNT));
memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * PET_BUFF_COUNT));
memset(pet_items, 0, (sizeof(uint32) * EQEmu::legacy::EQUIPMENT_SIZE));
pet_inst->GetPetState(pet_buffs, pet_items, pet_name);
@@ -2085,7 +2085,7 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills:
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
if ((skillinuse == EQEmu::skills::SkillDragonPunch) && GetAA(aaDragonPunch) && zone->random.Int(0, 99) < 25){
SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff);
SpellFinished(904, other, EQEmu::CastingSlot::Item, 0, -1, spells[904].ResistDiff);
other->Stun(100);
}
@@ -5900,7 +5900,7 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) {
Mob::DoBuffTic(buff, slot, caster);
}
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost,
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot, int32 cast_time, int32 mana_cost,
uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) {
bool Result = false;
if(zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) {
@@ -5920,7 +5920,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
Message_StringID(13, MELEE_SILENCE);
if(casting_spell_id)
AI_Event_SpellCastFinished(false, casting_spell_slot);
AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
return false;
}
@@ -5929,7 +5929,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
if(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()){
Message_StringID(13, SPELL_WOULDNT_HOLD);
if(casting_spell_id)
AI_Event_SpellCastFinished(false, casting_spell_slot);
AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
return false;
}
@@ -5940,7 +5940,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
return false;
}
if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id)) {
if(slot < EQEmu::CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
InterruptSpell(fizzle_msg, 0x121, spell_id);
@@ -5954,7 +5954,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t
Log.Out(Logs::Detail, Logs::Spells, "Casting a new spell/song while singing a song. Killing old song %d.", bardsong);
bardsong = 0;
bardsong_target_id = 0;
bardsong_slot = 0;
bardsong_slot = EQEmu::CastingSlot::Gem1;
bardsong_timer.Disable();
}
@@ -6084,7 +6084,7 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) {
return Result;
}
bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, uint16 slot) {
bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::CastingSlot slot) {
bool Result = false;
SpellTargetType targetType = spells[spell_id].targettype;
if(targetType == ST_GroupClientAndPet) {
@@ -6097,7 +6097,7 @@ bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
return Result;
}
bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, uint32 aa_id) {
bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, uint32 aa_id) {
bool Result = false;
if(GetClass() == BARD)
cast_time = 0;
@@ -6201,7 +6201,7 @@ void Bot::GenerateSpecialAttacks() {
SetSpecialAbility(SPECATK_TRIPLE, 1);
}
bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) {
bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
if(GetClass() == BARD) {
if(!ApplyNextBardPulse(bardsong, this, bardsong_slot))
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
@@ -6211,7 +6211,7 @@ bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot
return true;
}
bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) {
bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
if(spellTarget) {
if(IsGrouped() && (spellTarget->IsBot() || spellTarget->IsClient()) && RuleB(Bots, GroupBuffing)) {
bool noGroupSpell = false;
@@ -6223,7 +6223,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16
bool spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32));
bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self));
bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN));
bool slotequal = (slot == USE_ITEM_SPELL_SLOT);
bool slotequal = (slot == EQEmu::CastingSlot::Item);
if(spellequal || slotequal) {
if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) {
if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) &&
@@ -6262,7 +6262,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16
return true;
}
bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) {
bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
bool isMainGroupMGB = false;
if(isMainGroupMGB && (GetClass() != BARD)) {
BotGroupSay(this, "MGB %s", spells[spell_id].name);
@@ -8159,7 +8159,25 @@ bool Bot::GetNeedsCured(Mob *tar) {
int buffsWithCounters = 0;
needCured = true;
for (unsigned int j = 0; j < buff_count; j++) {
if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) {
// this should prevent crashes until the cause can be found
if (!tar->GetBuffs()) {
std::string mob_type = "Unknown";
if (tar->IsClient())
mob_type = "Client";
else if (tar->IsBot())
mob_type = "Bot";
else if (tar->IsMerc())
mob_type = "Merc";
else if (tar->IsPet())
mob_type = "Pet";
else if (tar->IsNPC())
mob_type = "NPC";
Log.Out(Logs::General, Logs::Error, "Bot::GetNeedsCured() processed mob type '%s' with a null buffs pointer (mob: '%s')", mob_type.c_str(), tar->GetName());
continue;
}
else if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) {
if(CalculateCounters(tar->GetBuffs()[j].spellid) > 0) {
buffsWithCounters++;
if(buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) {
@@ -8251,7 +8269,7 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) {
if(IsCasting())
InterruptSpell();
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
return true;
}
+6 -6
View File
@@ -274,9 +274,9 @@ public:
virtual void SetAttackTimer();
uint32 GetClassHPFactor();
virtual int32 CalcMaxHP();
bool DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic);
bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic);
bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic);
bool DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
void SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color);
void Camp(bool databaseSave = true);
virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false);
@@ -374,12 +374,12 @@ public:
virtual float GetAOERange(uint16 spell_id);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
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,
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0,
uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr, uint32 aa_id = 0);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, uint16 slot);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::CastingSlot slot);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
// Bot Equipment & Inventory Class Methods
void BotTradeSwapItem(Client* client, int16 lootSlot, const ItemInst* inst, const ItemInst* inst_swap, uint32 equipableSlots, std::string* errorMessage, bool swap = true);
+1 -1
View File
@@ -7613,7 +7613,7 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id,
if (annouce_cast)
Bot::BotGroupSay(casting_bot, "Attempting to cast '%s' on %s", spells[spell_id].name, target_mob->GetCleanName());
return casting_bot->CastSpell(spell_id, target_mob->GetID(), 1, -1, -1, dont_root_before);
return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQEmu::CastingSlot::Gem2, -1, -1, dont_root_before);
}
bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command)
+2 -2
View File
@@ -1472,7 +1472,7 @@ bool BotDatabase::LoadPetBuffs(const uint32 bot_id, SpellBuff_Struct* pet_buffs)
return true;
int buff_index = 0;
for (auto row = results.begin(); row != results.end() && buff_index < BUFF_COUNT; ++row) {
for (auto row = results.begin(); row != results.end() && buff_index < PET_BUFF_COUNT; ++row) {
pet_buffs[buff_index].spellid = atoi(row[0]);
pet_buffs[buff_index].level = atoi(row[1]);
pet_buffs[buff_index].duration = atoi(row[2]);
@@ -1509,7 +1509,7 @@ bool BotDatabase::SavePetBuffs(const uint32 bot_id, const SpellBuff_Struct* pet_
if (!saved_pet_index)
return true;
for (int buff_index = 0; buff_index < BUFF_COUNT; ++buff_index) {
for (int buff_index = 0; buff_index < PET_BUFF_COUNT; ++buff_index) {
if (!pet_buffs[buff_index].spellid || pet_buffs[buff_index].spellid == SPELL_UNKNOWN)
continue;
+95 -61
View File
@@ -44,6 +44,7 @@ extern volatile bool RunLoops;
#include "zonedb.h"
#include "petitions.h"
#include "command.h"
#include "water_map.h"
#ifdef BOTS
#include "bot_command.h"
#endif
@@ -155,7 +156,8 @@ Client::Client(EQStreamInterface* ieqs)
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f)
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
last_region_type(RegionTypeUnsupported)
{
for(int cf=0; cf < _FilterCount; cf++)
ClientFilters[cf] = FilterShow;
@@ -565,6 +567,11 @@ bool Client::SaveAA() {
return true;
}
void Client::RemoveExpendedAA(int aa_id)
{
database.QueryDatabase(StringFormat("DELETE from `character_alternate_abilities` WHERE `id` = %d and `aa_id` = %d", character_id, aa_id));
}
bool Client::Save(uint8 iCommitNow) {
if(!ClientDataLoaded())
return false;
@@ -1787,7 +1794,7 @@ const int32& Client::SetMana(int32 amount) {
}
void Client::SendManaUpdatePacket() {
if (!Connected() || IsCasting())
if (!Connected())
return;
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) {
@@ -1801,7 +1808,8 @@ void Client::SendManaUpdatePacket() {
ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer;
manachange->new_mana = cur_mana;
manachange->stamina = cur_end;
manachange->spell_id = casting_spell_id; //always going to be 0... since we check IsCasting()
manachange->spell_id = casting_spell_id;
manachange->keepcasting = 1;
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
@@ -2476,13 +2484,15 @@ uint16 Client::GetMaxSkillAfterSpecializationRules(EQEmu::skills::SkillType skil
return Result;
}
void Client::SetPVP(bool toggle) {
void Client::SetPVP(bool toggle, bool message) {
m_pp.pvp = toggle ? 1 : 0;
if(GetPVP())
this->Message_StringID(MT_Shout,PVP_ON);
else
Message(13, "You no longer follow the ways of discord.");
if (message) {
if(GetPVP())
this->Message_StringID(MT_Shout,PVP_ON);
else
Message(13, "You no longer follow the ways of discord.");
}
SendAppearancePacket(AT_PVP, GetPVP());
Save();
@@ -3674,58 +3684,58 @@ void Client::SacrificeConfirm(Client *caster)
//Essentially a special case death function
void Client::Sacrifice(Client *caster)
{
if(GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)){
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
if(exploss < GetEXP()){
SetEXP(GetEXP()-exploss, GetAAXP());
SendLogoutPackets();
if (GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)) {
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
if (exploss < GetEXP()) {
SetEXP(GetEXP() - exploss, GetAAXP());
SendLogoutPackets();
//make our become corpse packet, and queue to ourself before OP_Death.
EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct));
BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer;
bc->spawn_id = GetID();
bc->x = GetX();
bc->y = GetY();
bc->z = GetZ();
QueuePacket(&app2);
// make our become corpse packet, and queue to ourself before OP_Death.
EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct));
BecomeCorpse_Struct *bc = (BecomeCorpse_Struct *)app2.pBuffer;
bc->spawn_id = GetID();
bc->x = GetX();
bc->y = GetY();
bc->z = GetZ();
QueuePacket(&app2);
// make death packet
EQApplicationPacket app(OP_Death, sizeof(Death_Struct));
Death_Struct* d = (Death_Struct*)app.pBuffer;
d->spawn_id = GetID();
d->killer_id = caster ? caster->GetID() : 0;
d->bindzoneid = GetPP().binds[0].zoneId;
d->spell_id = SPELL_UNKNOWN;
d->attack_skill = 0xe7;
d->damage = 0;
app.priority = 6;
entity_list.QueueClients(this, &app);
// make death packet
EQApplicationPacket app(OP_Death, sizeof(Death_Struct));
Death_Struct *d = (Death_Struct *)app.pBuffer;
d->spawn_id = GetID();
d->killer_id = caster ? caster->GetID() : 0;
d->bindzoneid = GetPP().binds[0].zoneId;
d->spell_id = SPELL_UNKNOWN;
d->attack_skill = 0xe7;
d->damage = 0;
app.priority = 6;
entity_list.QueueClients(this, &app);
BuffFadeAll();
UnmemSpellAll();
Group *g = GetGroup();
if(g){
g->MemberZoned(this);
}
Raid *r = entity_list.GetRaidByClient(this);
if(r){
r->MemberZoned(this);
}
ClearAllProximities();
if(RuleB(Character, LeaveCorpses)){
auto new_corpse = new Corpse(this, 0);
entity_list.AddCorpse(new_corpse, GetID());
SetID(0);
entity_list.QueueClients(this, &app2, true);
}
Save();
GoToDeath();
caster->SummonItem(RuleI(Spells, SacrificeItemID));
}
}
else{
caster->Message_StringID(13, SAC_TOO_LOW); //This being is not a worthy sacrifice.
}
BuffFadeAll();
UnmemSpellAll();
Group *g = GetGroup();
if (g) {
g->MemberZoned(this);
}
Raid *r = entity_list.GetRaidByClient(this);
if (r) {
r->MemberZoned(this);
}
ClearAllProximities();
if (RuleB(Character, LeaveCorpses)) {
auto new_corpse = new Corpse(this, 0);
entity_list.AddCorpse(new_corpse, GetID());
SetID(0);
entity_list.QueueClients(this, &app2, true);
}
Save();
GoToDeath();
if (caster) // I guess it's possible?
caster->SummonItem(RuleI(Spells, SacrificeItemID));
}
} else {
caster->Message_StringID(13, SAC_TOO_LOW); // This being is not a worthy sacrifice.
}
}
void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) {
@@ -4604,7 +4614,7 @@ void Client::HandleLDoNOpen(NPC *target)
if(target->GetLDoNTrapSpellID() != 0)
{
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false);
@@ -4726,7 +4736,7 @@ void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type)
break;
case -1:
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false);
@@ -4745,7 +4755,7 @@ void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type)
if(target->IsLDoNTrapped())
{
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false);
@@ -8396,7 +8406,7 @@ void Client::SendColoredText(uint32 color, std::string message)
void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction) {
auto outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct));
memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer));
memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct));
QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer;
qr->mob_id = target->GetID(); // Entity ID for the from mob name
@@ -8527,3 +8537,27 @@ uint32 Client::GetMoney(uint8 type, uint8 subtype) {
int Client::GetAccountAge() {
return (time(nullptr) - GetAccountCreation());
}
void Client::CheckRegionTypeChanges()
{
if (!zone->HasWaterMap())
return;
auto new_region = zone->watermap->ReturnRegionType(glm::vec3(m_Position));
// still same region, do nothing
if (last_region_type == new_region)
return;
// region type changed
last_region_type = new_region;
// PVP is the only state we need to keep track of, so we can just return now for PVP servers
if (RuleI(World, PVPSettings) > 0)
return;
if (last_region_type == RegionTypePVP)
SetPVP(true, false);
else if (GetPVP())
SetPVP(false, false);
}
+13 -2
View File
@@ -27,6 +27,7 @@ class Object;
class Raid;
class Seperator;
class ServerPacket;
enum WaterRegionType : int;
namespace EQEmu
{
@@ -105,6 +106,7 @@ enum { //Type arguments to the Message* routines.
#define SPELLBAR_UNLOCK 0x2bc
enum { //scribing argument to MemorizeSpell
memSpellUnknown = -1, // this modifies some state data
memSpellScribing = 0,
memSpellMemorize = 1,
memSpellForget = 2,
@@ -326,6 +328,7 @@ public:
/* New PP Save Functions */
bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); }
bool SaveAA();
void RemoveExpendedAA(int aa_id);
inline bool ClientDataLoaded() const { return client_data_loaded; }
inline bool Connected() const { return (client_state == CLIENT_CONNECTED); }
@@ -359,9 +362,9 @@ public:
int32 LevelRegen();
void HPTick();
void SetGM(bool toggle);
void SetPVP(bool toggle);
void SetPVP(bool toggle, bool message = true);
inline bool GetPVP() const { return zone->GetZoneID() == 77 ? true : (m_pp.pvp != 0); }
inline bool GetPVP() const { return m_pp.pvp != 0; }
inline bool GetGM() const { return m_pp.gm != 0; }
inline void SetBaseClass(uint32 i) { m_pp.class_=i; }
@@ -510,6 +513,8 @@ public:
virtual int GetMaxSongSlots() const { return 12; }
virtual int GetMaxDiscSlots() const { return 1; }
virtual int GetMaxTotalSlots() const { return 38; }
virtual uint32 GetFirstBuffSlot(bool disc, bool song);
virtual uint32 GetLastBuffSlot(bool disc, bool song);
virtual void InitializeBuffSlots();
virtual void UninitializeBuffSlots();
@@ -889,6 +894,9 @@ public:
void SendDisciplineTimer(uint32 timer_id, uint32 duration);
bool UseDiscipline(uint32 spell_id, uint32 target);
void SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration);
bool IsLinkedSpellReuseTimerReady(uint32 timer_id);
bool CheckTitle(int titleset);
void EnableTitle(int titleset);
void RemoveTitle(int titleset);
@@ -1230,6 +1238,8 @@ public:
void SendHPUpdateMarquee();
void CheckRegionTypeChanges();
protected:
friend class Mob;
void CalcItemBonuses(StatBonuses* newbon);
@@ -1414,6 +1424,7 @@ private:
uint8 zonesummon_ignorerestrictions;
ZoneMode zone_mode;
WaterRegionType last_region_type;
Timer position_timer;
uint8 position_timer_counter;
+14
View File
@@ -1036,6 +1036,13 @@ int32 Client::CalcAC()
if (avoidance < 0) {
avoidance = 0;
}
if (RuleB(Character, EnableAvoidanceCap)) {
if (avoidance > RuleI(Character, AvoidanceCap)) {
avoidance = RuleI(Character, AvoidanceCap);
}
}
int mitigation = 0;
if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) {
//something is wrong with this, naked casters have the wrong natural AC
@@ -1113,6 +1120,13 @@ int32 Client::GetACAvoid()
if (avoidance < 0) {
avoidance = 0;
}
if (RuleB(Character, EnableAvoidanceCap)) {
if ((avoidance * 1000 / 847) > RuleI(Character, AvoidanceCap)) {
return RuleI(Character, AvoidanceCap);
}
}
return (avoidance * 1000 / 847);
}
+42 -45
View File
@@ -502,6 +502,27 @@ void Client::CompleteConnect()
SetDuelTarget(0);
SetDueling(false);
database.LoadPetInfo(this);
/*
This was moved before the spawn packets are sent
in hopes that it adds more consistency...
Remake pet
*/
if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) {
MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size);
if (GetPet() && GetPet()->IsNPC()) {
NPC *pet = GetPet()->CastToNPC();
pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items);
pet->CalcBonuses();
pet->SetHP(m_petinfo.HP);
pet->SetMana(m_petinfo.Mana);
}
m_petinfo.SpellID = 0;
}
/* Moved here so it's after where we load the pet data. */
if (!GetAA(aaPersistentMinion))
memset(&m_suspendedminion, 0, sizeof(PetInfo));
EnteringMessages(this);
LoadZoneFlags();
@@ -1628,27 +1649,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (m_pp.RestTimer)
rest_timer.Start(m_pp.RestTimer * 1000);
database.LoadPetInfo(this);
/*
This was moved before the spawn packets are sent
in hopes that it adds more consistency...
Remake pet
*/
if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) {
MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size);
if (GetPet() && GetPet()->IsNPC()) {
NPC *pet = GetPet()->CastToNPC();
pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items);
pet->CalcBonuses();
pet->SetHP(m_petinfo.HP);
pet->SetMana(m_petinfo.Mana);
}
m_petinfo.SpellID = 0;
}
/* Moved here so it's after where we load the pet data. */
if (!GetAA(aaPersistentMinion))
memset(&m_suspendedminion, 0, sizeof(PetInfo));
/* Server Zone Entry Packet */
outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct));
ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer;
@@ -3966,6 +3966,7 @@ void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app)
void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
{
using EQEmu::CastingSlot;
if (app->size != sizeof(CastSpell_Struct)) {
std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl;
return;
@@ -3978,13 +3979,13 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer;
m_TargetRing = glm::vec3(castspell->x_pos, castspell->y_pos, castspell->z_pos);
m_TargetRing = glm::vec3(castspell->x_pos, castspell->y_pos, castspell->z_pos);
Log.Out(Logs::General, Logs::Spells, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot);
CastingSlot slot = static_cast<CastingSlot>(castspell->slot);
/* Memorized Spell */
if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){
if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id) {
uint16 spell_to_cast = 0;
if (castspell->slot < MAX_PP_MEMSPELL) {
spell_to_cast = m_pp.mem_spells[castspell->slot];
@@ -3998,20 +3999,12 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
return;
}
CastSpell(spell_to_cast, castspell->target_id, castspell->slot);
CastSpell(spell_to_cast, castspell->target_id, slot);
}
/* Spell Slot or Potion Belt Slot */
else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)|| (castspell->slot == TARGET_RING_SPELL_SLOT)) // ITEM or POTION cast
else if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) // ITEM or POTION cast
{
//discipline, using the item spell slot
if (castspell->inventoryslot == INVALID_INDEX) {
if (!UseDiscipline(castspell->spell_id, castspell->target_id)) {
Log.Out(Logs::General, Logs::Spells, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id);
InterruptSpell(castspell->spell_id);
}
return;
}
else if (m_inv.SupportsClickCasting(castspell->inventoryslot) || (castspell->slot == POTION_BELT_SPELL_SLOT) || (castspell->slot == TARGET_RING_SPELL_SLOT)) // sanity check
if (m_inv.SupportsClickCasting(castspell->inventoryslot) || slot == CastingSlot::PotionBelt) // sanity check
{
// packet field types will be reviewed as packet transistions occur
const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field
@@ -4036,7 +4029,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot);
if (i == 0) {
CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot);
CastSpell(item->Click.Effect, castspell->target_id, slot, item->CastTime, 0, 0, castspell->inventoryslot);
}
else {
InterruptSpell(castspell->spell_id);
@@ -4056,7 +4049,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot);
if (i == 0) {
CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot);
CastSpell(item->Click.Effect, castspell->target_id, slot, item->CastTime, 0, 0, castspell->inventoryslot);
}
else {
InterruptSpell(castspell->spell_id);
@@ -4081,8 +4074,8 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
InterruptSpell(castspell->spell_id);
}
}
/* Discipline */
else if (castspell->slot == DISCIPLINE_SPELL_SLOT) {
/* Discipline -- older clients use the same slot as items, but we translate to it's own */
else if (slot == CastingSlot::Discipline) {
if (!UseDiscipline(castspell->spell_id, castspell->target_id)) {
Log.Out(Logs::General, Logs::Spells, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id);
InterruptSpell(castspell->spell_id);
@@ -4090,7 +4083,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
}
}
/* ABILITY cast (LoH and Harm Touch) */
else if (castspell->slot == ABILITY_SPELL_SLOT) {
else if (slot == CastingSlot::Ability) {
uint16 spell_to_cast = 0;
if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) {
@@ -4120,7 +4113,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
}
if (spell_to_cast > 0) // if we've matched LoH or HT, cast now
CastSpell(spell_to_cast, castspell->target_id, castspell->slot);
CastSpell(spell_to_cast, castspell->target_id, slot);
}
return;
}
@@ -4583,8 +4576,11 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
safe_delete(outapp);
}
if(zone->watermap && zone->watermap->InLiquid(glm::vec3(m_Position)))
CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17);
if (zone->watermap) {
if (zone->watermap->InLiquid(glm::vec3(m_Position)))
CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17);
CheckRegionTypeChanges();
}
return;
}
@@ -8410,6 +8406,7 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app)
void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
{
using EQEmu::CastingSlot;
if (app->size != sizeof(ItemVerifyRequest_Struct))
{
Log.Out(Logs::General, Logs::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size);
@@ -8554,7 +8551,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
if (i == 0) {
if (!IsCastWhileInvis(item->Click.Effect))
CommonBreakInvisible(); // client can't do this for us :(
CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id);
CastSpell(item->Click.Effect, target_id, CastingSlot::Item, item->CastTime, 0, 0, slot_id);
}
}
else
@@ -8583,7 +8580,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
if (i == 0) {
if (!IsCastWhileInvis(augitem->Click.Effect))
CommonBreakInvisible(); // client can't do this for us :(
CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id);
CastSpell(augitem->Click.Effect, target_id, CastingSlot::Item, augitem->CastTime, 0, 0, slot_id);
}
}
else
+1 -1
View File
@@ -624,7 +624,7 @@ bool Client::Process() {
{
//client logged out or errored out
//ResetTrade();
if (client_state != CLIENT_KICKED) {
if (client_state != CLIENT_KICKED && !zoning && !instalog) {
Save();
}
+9 -5
View File
@@ -2199,14 +2199,14 @@ void command_castspell(Client *c, const Seperator *sep)
else
if (c->GetTarget() == 0)
if(c->Admin() >= commandInstacast)
c->SpellFinished(spellid, 0, USE_ITEM_SPELL_SLOT, 0, -1, spells[spellid].ResistDiff);
c->SpellFinished(spellid, 0, EQEmu::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff);
else
c->CastSpell(spellid, 0, USE_ITEM_SPELL_SLOT, 0);
c->CastSpell(spellid, 0, EQEmu::CastingSlot::Item, 0);
else
if(c->Admin() >= commandInstacast)
c->SpellFinished(spellid, c->GetTarget(), 10, 0, -1, spells[spellid].ResistDiff);
c->SpellFinished(spellid, c->GetTarget(), EQEmu::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff);
else
c->CastSpell(spellid, c->GetTarget()->GetID(), USE_ITEM_SPELL_SLOT, 0);
c->CastSpell(spellid, c->GetTarget()->GetID(), EQEmu::CastingSlot::Item, 0);
}
}
@@ -4339,11 +4339,15 @@ void command_goto(Client *c, const Seperator *sep)
void command_iteminfo(Client *c, const Seperator *sep)
{
auto inst = c->GetInv()[EQEmu::legacy::SlotCursor];
if (!inst) { c->Message(13, "Error: You need an item on your cursor for this command"); }
if (!inst) {
c->Message(13, "Error: You need an item on your cursor for this command");
return;
}
auto item = inst->GetItem();
if (!item) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer");
c->Message(13, "Error: This item has no data reference");
return;
}
EQEmu::SayLinkEngine linker;
-7
View File
@@ -17,13 +17,6 @@
#define _NPCPET(x) (x && x->IsNPC() && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsNPC())
#define _BECOMENPCPET(x) (x && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsClient() && x->CastToMob()->GetOwner()->CastToClient()->IsBecomeNPC())
#define USE_ITEM_SPELL_SLOT 10
#define POTION_BELT_SPELL_SLOT 11
#define TARGET_RING_SPELL_SLOT 12
#define DISCIPLINE_SPELL_SLOT 10
#define ABILITY_SPELL_SLOT 9
#define ALTERNATE_ABILITY_SPELL_SLOT 0xFF
//LOS Parameters:
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from
#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS
+3 -3
View File
@@ -1163,9 +1163,9 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
}
char buf[88];
char corpse_name[64];
strcpy(corpse_name, corpse_name);
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(corpse_name));
char q_corpse_name[64];
strcpy(q_corpse_name, corpse_name);
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(q_corpse_name));
buf[87] = '\0';
std::vector<EQEmu::Any> args;
args.push_back(inst);
+3 -3
View File
@@ -684,9 +684,9 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
}
if (reduced_recast > 0)
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
else{
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
return true;
}
@@ -694,7 +694,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
}
else
{
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
}
return(true);
}
+62 -12
View File
@@ -927,12 +927,18 @@ bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket *app, Client *client)
Entity *EntityList::GetEntityMob(uint16 id)
{
return mob_list.count(id) ? mob_list.at(id) : nullptr;
auto it = mob_list.find(id);
if (it != mob_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetEntityMerc(uint16 id)
{
return merc_list.count(id) ? merc_list.at(id) : nullptr;
auto it = merc_list.find(id);
if (it != merc_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetEntityMob(const char *name)
@@ -952,12 +958,18 @@ Entity *EntityList::GetEntityMob(const char *name)
Entity *EntityList::GetEntityDoor(uint16 id)
{
return door_list.count(id) ? door_list.at(id) : nullptr;
auto it = door_list.find(id);
if (it != door_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetEntityCorpse(uint16 id)
{
return corpse_list.count(id) ? corpse_list.at(id) : nullptr;
auto it = corpse_list.find(id);
if (it != corpse_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetEntityCorpse(const char *name)
@@ -977,22 +989,34 @@ Entity *EntityList::GetEntityCorpse(const char *name)
Entity *EntityList::GetEntityTrap(uint16 id)
{
return trap_list.count(id) ? trap_list.at(id) : nullptr;
auto it = trap_list.find(id);
if (it != trap_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetEntityObject(uint16 id)
{
return object_list.count(id) ? object_list.at(id) : nullptr;
auto it = object_list.find(id);
if (it != object_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetEntityBeacon(uint16 id)
{
return beacon_list.count(id) ? beacon_list.at(id) : nullptr;
auto it = beacon_list.find(id);
if (it != beacon_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetEntityEncounter(uint16 id)
{
return encounter_list.count(id) ? encounter_list.at(id) : nullptr;
auto it = encounter_list.find(id);
if (it != encounter_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetID(uint16 get_id)
@@ -1188,6 +1212,8 @@ void EntityList::ChannelMessage(Mob *from, uint8 chan_num, uint8 language,
void EntityList::ChannelMessageSend(Mob *to, uint8 chan_num, uint8 language, const char *message, ...)
{
if (!to->IsClient())
return;
va_list argptr;
char buffer[4096];
@@ -1195,8 +1221,7 @@ void EntityList::ChannelMessageSend(Mob *to, uint8 chan_num, uint8 language, con
vsnprintf(buffer, 4096, message, argptr);
va_end(argptr);
if (client_list.count(to->GetID()))
client_list.at(to->GetID())->ChannelMessageSend(0, 0, chan_num, language, buffer);
to->CastToClient()->ChannelMessageSend(0, 0, chan_num, language, buffer);
}
void EntityList::SendZoneSpawns(Client *client)
@@ -1232,7 +1257,9 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
maxspawns = mob_list.size();
auto bzsp = new BulkZoneSpawnPacket(client, maxspawns);
int32 race=-1;
bool delaypkt = false;
const glm::vec4& cpos = client->GetPosition();
const float dmax = 600.0 * 600.0;
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
spawn = it->second;
if (spawn && spawn->GetID() > 0 && spawn->Spawned()) {
@@ -1240,8 +1267,30 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
spawn->CastToClient()->IsHoveringForRespawn()))
continue;
race = spawn->GetRace();
#if 1
const glm::vec4& spos = spawn->GetPosition();
delaypkt = false;
if (DistanceSquared(cpos, spos) > dmax || (spawn->IsClient() && (spawn->GetRace() == MINOR_ILL_OBJ || spawn->GetRace() == TREE)))
delaypkt = true;
if (delaypkt) {
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);
}
spawn->SendArmorAppearance(client);
#else
/* original code kept for spawn packet research */
int32 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)) {
@@ -1259,6 +1308,7 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
// Despite being sent in the OP_ZoneSpawns packet, the client
// does not display worn armor correctly so display it.
spawn->SendArmorAppearance(client);
#endif
}
}
safe_delete(bzsp);
+36 -6
View File
@@ -148,14 +148,29 @@ public:
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
Mob *GetTargetForVirus(Mob* spreader, int range);
inline NPC *GetNPCByID(uint16 id)
{ return npc_list.count(id) ? npc_list.at(id) : nullptr; }
{
auto it = npc_list.find(id);
if (it != npc_list.end())
return it->second;
return nullptr;
}
NPC *GetNPCByNPCTypeID(uint32 npc_id);
inline Merc *GetMercByID(uint16 id)
{ return merc_list.count(id) ? merc_list.at(id) : nullptr; }
{
auto it = merc_list.find(id);
if (it != merc_list.end())
return it->second;
return nullptr;
}
Client *GetClientByName(const char *name);
Client *GetClientByAccID(uint32 accid);
inline Client *GetClientByID(uint16 id)
{ return client_list.count(id) ? client_list.at(id) : nullptr; }
{
auto it = client_list.find(id);
if (it != client_list.end())
return it->second;
return nullptr;
}
Client *GetClientByCharID(uint32 iCharID);
Client *GetClientByWID(uint32 iWID);
Client *GetClient(uint32 ip, uint16 port);
@@ -172,7 +187,12 @@ public:
Corpse *GetCorpseByOwner(Client* client);
Corpse *GetCorpseByOwnerWithinRange(Client* client, Mob* center, int range);
inline Corpse *GetCorpseByID(uint16 id)
{ return corpse_list.count(id) ? corpse_list.at(id) : nullptr; }
{
auto it = corpse_list.find(id);
if (it != corpse_list.end())
return it->second;
return nullptr;
}
Corpse *GetCorpseByDBID(uint32 dbid);
Corpse *GetCorpseByName(const char* name);
@@ -181,10 +201,20 @@ public:
Client* FindCorpseDragger(uint16 CorpseID);
inline Object *GetObjectByID(uint16 id)
{ return object_list.count(id) ? object_list.at(id) : nullptr; }
{
auto it = object_list.find(id);
if (it != object_list.end())
return it->second;
return nullptr;
}
Object *GetObjectByDBID(uint32 id);
inline Doors *GetDoorsByID(uint16 id)
{ return door_list.count(id) ? door_list.at(id) : nullptr; }
{
auto it = door_list.find(id);
if (it != door_list.end())
return it->second;
return nullptr;
}
Doors *GetDoorsByDoorID(uint32 id);
Doors *GetDoorsByDBID(uint32 id);
void RemoveAllCorpsesByCharID(uint32 charid);
+11 -11
View File
@@ -759,28 +759,28 @@ bool Lua_Mob::CastSpell(int spell_id, int target_id) {
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, slot);
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot));
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, slot, cast_time);
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time);
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost);
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost);
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot));
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot));
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer,
int timer_duration) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
static_cast<uint32>(timer), static_cast<uint32>(timer_duration));
}
@@ -789,7 +789,7 @@ bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, in
Lua_Safe_Call_Bool();
int16 res = resist_adjust;
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
static_cast<uint32>(timer), static_cast<uint32>(timer_duration), &res);
}
@@ -800,27 +800,27 @@ bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target) {
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, slot);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot));
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, slot, mana_used);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used);
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, slot, mana_used, inventory_slot);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot);
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, slot, mana_used, inventory_slot, resist_adjust);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust);
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust, bool proc) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, slot, mana_used, inventory_slot, resist_adjust, proc);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust, proc);
}
void Lua_Mob::SpellEffect(Lua_Mob caster, int spell_id, double partial) {
+1 -1
View File
@@ -163,7 +163,7 @@ float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const {
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))
if (std::abs(from.z - result->z) < std::abs(ClosestZ - from.z))
return result->z;
}
+2 -2
View File
@@ -1963,7 +1963,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon
SendPosition();
SetMoving(false);
result = CastSpell(spellid, tar->GetID(), 1, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0);
result = CastSpell(spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0);
if(IsCasting() && IsSitting())
Stand();
@@ -4015,7 +4015,7 @@ bool Merc::UseDiscipline(int32 spell_id, int32 target) {
if(IsCasting())
InterruptSpell();
CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT);
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
return(true);
}
+27 -27
View File
@@ -2357,7 +2357,6 @@ void Mob::SetZone(uint32 zone_id, uint32 instance_id)
{
CastToClient()->GetPP().zone_id = zone_id;
CastToClient()->GetPP().zoneInstance = instance_id;
CastToClient()->Save();
}
Save();
}
@@ -3224,13 +3223,13 @@ void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on, int lev
if(twinproc_chance && zone->random.Roll(twinproc_chance))
twinproc = true;
if (IsBeneficialSpell(spell_id)) {
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override);
if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override);
if(twinproc)
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, level_override);
SpellFinished(spell_id, on, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override);
if(twinproc)
SpellOnTarget(spell_id, on, false, false, 0, true, level_override);
}
@@ -3518,10 +3517,9 @@ void Mob::TryTriggerOnCast(uint32 spell_id, bool aa_trigger)
}
}
void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
{
if(!IsValidSpell(focus_spell) || !IsValidSpell(spell_id))
if (!IsValidSpell(focus_spell) || !IsValidSpell(spell_id))
return;
uint32 trigger_spell_id = 0;
@@ -3532,15 +3530,17 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
if (rank)
trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id);
if(IsValidSpell(trigger_spell_id) && GetTarget())
SpellFinished(trigger_spell_id, GetTarget(), 10, 0, -1, spells[trigger_spell_id].ResistDiff);
if (IsValidSpell(trigger_spell_id) && GetTarget())
SpellFinished(trigger_spell_id, GetTarget(), EQEmu::CastingSlot::Item, 0, -1,
spells[trigger_spell_id].ResistDiff);
}
else{
else {
trigger_spell_id = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id);
if(IsValidSpell(trigger_spell_id) && GetTarget()){
SpellFinished(trigger_spell_id, GetTarget(),10, 0, -1, spells[trigger_spell_id].ResistDiff);
if (IsValidSpell(trigger_spell_id) && GetTarget()) {
SpellFinished(trigger_spell_id, GetTarget(), EQEmu::CastingSlot::Item, 0, -1,
spells[trigger_spell_id].ResistDiff);
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell);
}
}
@@ -3570,7 +3570,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect)
{
// If we trigger an effect then its over.
if (IsValidSpell(spells[spell_id].base2[i])){
SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
SpellFinished(spells[spell_id].base2[i], target, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
return true;
}
}
@@ -3589,7 +3589,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect)
if(zone->random.Int(0, 100) <= spells[spell_id].base[effect])
{
if (IsValidSpell(spells[spell_id].base2[effect])){
SpellFinished(spells[spell_id].base2[effect], target, 10, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff);
SpellFinished(spells[spell_id].base2[effect], target, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff);
return true; //Only trigger once of these per spell effect.
}
}
@@ -3666,7 +3666,7 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
}
if (use_spell){
SpellFinished(spells[spell_id].base[i], this, 10, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spells[spell_id].base[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
if(!TryFadeEffect(e))
BuffFadeBySlot(e);
@@ -3694,7 +3694,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id)
if(zone->random.Roll(focus))
{
Message(MT_Spells,"You twincast %s!",spells[spell_id].name);
SpellFinished(spell_id, target, 10, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, target, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
}
}
@@ -3712,7 +3712,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id)
{
if(zone->random.Roll(focus))
{
SpellFinished(spell_id, target, 10, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, target, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
}
}
@@ -3831,10 +3831,10 @@ bool Mob::TryFadeEffect(int slot)
if(IsValidSpell(spell_id))
{
if (IsBeneficialSpell(spell_id)) {
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
else if(!(IsClient() && CastToClient()->dead)) {
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
return true;
}
@@ -3868,7 +3868,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
SpellFinished(focus_trigger, target);
else
SpellFinished(focus_trigger, this, 10, 0, -1, spells[focus_trigger].ResistDiff);
SpellFinished(focus_trigger, this, EQEmu::CastingSlot::Item, 0, -1, spells[focus_trigger].ResistDiff);
}
// For detrimental spells, if the triggered spell is beneficial, then it will land on the caster
// if the triggered spell is also detrimental, then it will land on the target
@@ -3878,7 +3878,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
SpellFinished(focus_trigger, this);
else
SpellFinished(focus_trigger, target, 10, 0, -1, spells[focus_trigger].ResistDiff);
SpellFinished(focus_trigger, target, EQEmu::CastingSlot::Item, 0, -1, spells[focus_trigger].ResistDiff);
}
CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell);
@@ -4529,7 +4529,7 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
if (IsValidSpell(spells[spell_id].base2[i]) && spells[spell_id].max[i] <= level)
{
if(zone->random.Roll(spells[spell_id].base[i]))
SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
SpellFinished(spells[spell_id].base2[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
}
}
}
@@ -4544,17 +4544,17 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
if(aabonuses.SpellOnKill[i] && IsValidSpell(aabonuses.SpellOnKill[i]) && (level >= aabonuses.SpellOnKill[i + 2])) {
if(zone->random.Roll(static_cast<int>(aabonuses.SpellOnKill[i + 1])))
SpellFinished(aabonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
SpellFinished(aabonuses.SpellOnKill[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
}
if(itembonuses.SpellOnKill[i] && IsValidSpell(itembonuses.SpellOnKill[i]) && (level >= itembonuses.SpellOnKill[i + 2])){
if(zone->random.Roll(static_cast<int>(itembonuses.SpellOnKill[i + 1])))
SpellFinished(itembonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
SpellFinished(itembonuses.SpellOnKill[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
}
if(spellbonuses.SpellOnKill[i] && IsValidSpell(spellbonuses.SpellOnKill[i]) && (level >= spellbonuses.SpellOnKill[i + 2])) {
if(zone->random.Roll(static_cast<int>(spellbonuses.SpellOnKill[i + 1])))
SpellFinished(spellbonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
SpellFinished(spellbonuses.SpellOnKill[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
}
}
@@ -4571,19 +4571,19 @@ bool Mob::TrySpellOnDeath()
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) {
if(IsClient() && aabonuses.SpellOnDeath[i] && IsValidSpell(aabonuses.SpellOnDeath[i])) {
if(zone->random.Roll(static_cast<int>(aabonuses.SpellOnDeath[i + 1]))) {
SpellFinished(aabonuses.SpellOnDeath[i], this, 10, 0, -1, spells[aabonuses.SpellOnDeath[i]].ResistDiff);
SpellFinished(aabonuses.SpellOnDeath[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnDeath[i]].ResistDiff);
}
}
if(itembonuses.SpellOnDeath[i] && IsValidSpell(itembonuses.SpellOnDeath[i])) {
if(zone->random.Roll(static_cast<int>(itembonuses.SpellOnDeath[i + 1]))) {
SpellFinished(itembonuses.SpellOnDeath[i], this, 10, 0, -1, spells[itembonuses.SpellOnDeath[i]].ResistDiff);
SpellFinished(itembonuses.SpellOnDeath[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[itembonuses.SpellOnDeath[i]].ResistDiff);
}
}
if(spellbonuses.SpellOnDeath[i] && IsValidSpell(spellbonuses.SpellOnDeath[i])) {
if(zone->random.Roll(static_cast<int>(spellbonuses.SpellOnDeath[i + 1]))) {
SpellFinished(spellbonuses.SpellOnDeath[i], this, 10, 0, -1, spells[spellbonuses.SpellOnDeath[i]].ResistDiff);
SpellFinished(spellbonuses.SpellOnDeath[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spellbonuses.SpellOnDeath[i]].ResistDiff);
}
}
}
+12 -8
View File
@@ -26,6 +26,7 @@
#include "aa_ability.h"
#include "aa.h"
#include "../common/light_source.h"
#include "../common/emu_constants.h"
#include <set>
#include <vector>
#include <memory>
@@ -218,7 +219,7 @@ public:
//Song
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot);
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, EQEmu::CastingSlot slot);
void BardPulse(uint16 spell_id, Mob *caster);
//Spell
@@ -248,29 +249,30 @@ public:
void SendSpellBarEnable(uint16 spellid);
void ZeroCastingVars();
virtual void SpellProcess();
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1,
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1,
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 *resist_adjust = nullptr,
uint32 aa_id = 0);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1,
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1,
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 resist_adjust = 0,
uint32 aa_id = 0);
void CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, uint16 mana_used,
void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQEmu::CastingSlot 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,
bool SpellFinished(uint16 spell_id, Mob *target, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, uint16 mana_used = 0,
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, 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, uint16 slot);
CastAction_type &CastAction, EQEmu::CastingSlot slot, bool isproc = false);
virtual bool CheckFizzle(uint16 spell_id);
virtual bool CheckSpellLevelRestriction(uint16 spell_id);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
virtual float GetAOERange(uint16 spell_id);
void InterruptSpell(uint16 spellid = SPELL_UNKNOWN);
void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN);
void StopCasting();
inline bool IsCasting() const { return((casting_spell_id != 0)); }
uint16 CastingSpellID() const { return casting_spell_id; }
bool DoCastingChecks();
@@ -306,6 +308,8 @@ public:
virtual int GetMaxSongSlots() const { return 0; }
virtual int GetMaxDiscSlots() const { return 0; }
virtual int GetMaxTotalSlots() const { return 0; }
virtual uint32 GetFirstBuffSlot(bool disc, bool song);
virtual uint32 GetLastBuffSlot(bool disc, bool song);
virtual void InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; }
virtual void UninitializeBuffSlots() { }
EQApplicationPacket *MakeBuffsPacket(bool for_target = true);
@@ -1216,7 +1220,7 @@ protected:
int attacked_count;
bool delaytimer;
uint16 casting_spell_targetid;
uint16 casting_spell_slot;
EQEmu::CastingSlot casting_spell_slot;
uint16 casting_spell_mana;
uint32 casting_spell_inventory_slot;
uint32 casting_spell_timer;
@@ -1226,7 +1230,7 @@ protected:
uint32 casting_spell_aa_id;
bool casting_spell_checks;
uint16 bardsong;
uint8 bardsong_slot;
EQEmu::CastingSlot bardsong_slot;
uint32 bardsong_target_id;
bool ActiveProjectileATK;
+7 -5
View File
@@ -341,7 +341,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
SetCurrentSpeed(0);
}
return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
}
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes) {
@@ -673,11 +673,11 @@ void Client::AI_SpellCast()
}
uint32 spell_to_cast = 0xFFFFFFFF;
uint32 slot_to_use = 10;
EQEmu::CastingSlot slot_to_use = EQEmu::CastingSlot::Item;
if(valid_spells.size() == 1)
{
spell_to_cast = valid_spells[0];
slot_to_use = slots[0];
slot_to_use = static_cast<EQEmu::CastingSlot>(slots[0]);
}
else if(valid_spells.empty())
{
@@ -687,7 +687,7 @@ void Client::AI_SpellCast()
{
uint32 idx = zone->random.Int(0, (valid_spells.size()-1));
spell_to_cast = valid_spells[idx];
slot_to_use = slots[idx];
slot_to_use = static_cast<EQEmu::CastingSlot>(slots[idx]);
}
if(IsMezSpell(spell_to_cast) || IsFearSpell(spell_to_cast))
@@ -2362,8 +2362,10 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
return a.priority > b.priority;
});
if (IsValidSpell(attack_proc_spell))
if (IsValidSpell(attack_proc_spell)) {
AddProcToWeapon(attack_proc_spell, true, proc_chance);
innate_proc_spell_id = attack_proc_spell;
}
if (IsValidSpell(range_proc_spell))
AddRangedProc(range_proc_spell, (rproc_chance + 100));
+82 -35
View File
@@ -233,6 +233,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
npc_spells_id = 0;
HasAISpell = false;
HasAISpellEffects = false;
innate_proc_spell_id = 0;
if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs))
{
@@ -2006,44 +2007,90 @@ void NPC::LevelScale() {
float scaling = (((random_level / (float)level) - 1) * (scalerate / 100.0f));
// Compensate for scale rates at low levels so they don't add too much
uint8 scale_adjust = 1;
if(level > 0 && level <= 5)
scale_adjust = 10;
if(level > 5 && level <= 10)
scale_adjust = 5;
if(level > 10 && level <= 15)
scale_adjust = 3;
if(level > 15 && level <= 25)
scale_adjust = 2;
if (RuleB(NPC, NewLevelScaling)) {
if (scalerate == 0 || maxlevel <= 25) {
// pre-pop seems to scale by 20 HP increments while newer by 100
// We also don't want 100 increments on newer noobie zones, check level
if (zone->GetZoneID() < 200 || level < 48) {
max_hp += (random_level - level) * 20;
base_hp += (random_level - level) * 20;
} else {
max_hp += (random_level - level) * 100;
base_hp += (random_level - level) * 100;
}
base_hp += (int)(base_hp * scaling);
max_hp += (int)(max_hp * scaling);
cur_hp = max_hp;
STR += (int)(STR * scaling / scale_adjust);
STA += (int)(STA * scaling / scale_adjust);
AGI += (int)(AGI * scaling / scale_adjust);
DEX += (int)(DEX * scaling / scale_adjust);
INT += (int)(INT * scaling / scale_adjust);
WIS += (int)(WIS * scaling / scale_adjust);
CHA += (int)(CHA * scaling / scale_adjust);
if (MR)
MR += (int)(MR * scaling / scale_adjust);
if (CR)
CR += (int)(CR * scaling / scale_adjust);
if (DR)
DR += (int)(DR * scaling / scale_adjust);
if (FR)
FR += (int)(FR * scaling / scale_adjust);
if (PR)
PR += (int)(PR * scaling / scale_adjust);
cur_hp = max_hp;
max_dmg += (random_level - level) * 2;
} else {
uint8 scale_adjust = 1;
base_hp += (int)(base_hp * scaling);
max_hp += (int)(max_hp * scaling);
cur_hp = max_hp;
if (max_dmg) {
max_dmg += (int)(max_dmg * scaling / scale_adjust);
min_dmg += (int)(min_dmg * scaling / scale_adjust);
}
STR += (int)(STR * scaling / scale_adjust);
STA += (int)(STA * scaling / scale_adjust);
AGI += (int)(AGI * scaling / scale_adjust);
DEX += (int)(DEX * scaling / scale_adjust);
INT += (int)(INT * scaling / scale_adjust);
WIS += (int)(WIS * scaling / scale_adjust);
CHA += (int)(CHA * scaling / scale_adjust);
if (MR)
MR += (int)(MR * scaling / scale_adjust);
if (CR)
CR += (int)(CR * scaling / scale_adjust);
if (DR)
DR += (int)(DR * scaling / scale_adjust);
if (FR)
FR += (int)(FR * scaling / scale_adjust);
if (PR)
PR += (int)(PR * scaling / scale_adjust);
}
} else {
// Compensate for scale rates at low levels so they don't add too much
uint8 scale_adjust = 1;
if(level > 0 && level <= 5)
scale_adjust = 10;
if(level > 5 && level <= 10)
scale_adjust = 5;
if(level > 10 && level <= 15)
scale_adjust = 3;
if(level > 15 && level <= 25)
scale_adjust = 2;
base_hp += (int)(base_hp * scaling);
max_hp += (int)(max_hp * scaling);
cur_hp = max_hp;
STR += (int)(STR * scaling / scale_adjust);
STA += (int)(STA * scaling / scale_adjust);
AGI += (int)(AGI * scaling / scale_adjust);
DEX += (int)(DEX * scaling / scale_adjust);
INT += (int)(INT * scaling / scale_adjust);
WIS += (int)(WIS * scaling / scale_adjust);
CHA += (int)(CHA * scaling / scale_adjust);
if (MR)
MR += (int)(MR * scaling / scale_adjust);
if (CR)
CR += (int)(CR * scaling / scale_adjust);
if (DR)
DR += (int)(DR * scaling / scale_adjust);
if (FR)
FR += (int)(FR * scaling / scale_adjust);
if (PR)
PR += (int)(PR * scaling / scale_adjust);
if (max_dmg)
{
max_dmg += (int)(max_dmg * scaling / scale_adjust);
min_dmg += (int)(min_dmg * scaling / scale_adjust);
}
if (max_dmg)
{
max_dmg += (int)(max_dmg * scaling / scale_adjust);
min_dmg += (int)(min_dmg * scaling / scale_adjust);
}
level = random_level;
return;
+2
View File
@@ -407,6 +407,7 @@ public:
void mod_npc_killed_merit(Mob* c);
void mod_npc_killed(Mob* oos);
void AISpellsList(Client *c);
uint16 GetInnateProcSpellID() const { return innate_proc_spell_id; }
uint32 GetHeroForgeModel() const { return herosforgemodel; }
void SetHeroForgeModel(uint32 model) { herosforgemodel = model; }
@@ -454,6 +455,7 @@ protected:
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
AISpellsVar_Struct AISpellVar;
int16 GetFocusEffect(focusType type, uint16 spell_id);
uint16 innate_proc_spell_id;
uint32 npc_spells_effects_id;
std::vector<AISpellsEffects_Struct> AIspellsEffects;
+5 -5
View File
@@ -3982,12 +3982,12 @@ XS(XS_Mob_CastSpell)
{
dXSARGS;
if (items < 3 || items > 7)
Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, spell_id, target_id, slot= 10, casttime= -1, mana_cost= -1, resist_adjust = 0)");
Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, spell_id, target_id, slot= 22, casttime= -1, mana_cost= -1, resist_adjust = 0)");
{
Mob * THIS;
uint16 spell_id = (uint16)SvUV(ST(1));
uint16 target_id = (uint16)SvUV(ST(2));
uint16 slot;
EQEmu::CastingSlot slot;
int32 casttime;
int32 mana_cost;
int16 resist_adjust;
@@ -4002,9 +4002,9 @@ XS(XS_Mob_CastSpell)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 4)
slot = 10;
slot = EQEmu::CastingSlot::Item;
else {
slot = (uint16)SvUV(ST(3));
slot = static_cast<EQEmu::CastingSlot>(SvUV(ST(3)));
}
if (items < 5)
@@ -4085,7 +4085,7 @@ XS(XS_Mob_SpellFinished)
resist_diff = spells[spell_id].ResistDiff;
}
THIS->SpellFinished(spell_id, spell_target, 10, mana_cost, -1, resist_diff);
THIS->SpellFinished(spell_id, spell_target, EQEmu::CastingSlot::Item, mana_cost, -1, resist_diff);
}
XSRETURN_EMPTY;
}
+2 -2
View File
@@ -365,14 +365,14 @@ void QuestManager::castspell(int spell_id, int target_id) {
if (owner) {
Mob *tgt = entity_list.GetMob(target_id);
if(tgt != nullptr)
owner->SpellFinished(spell_id, tgt, 10, 0, -1, spells[spell_id].ResistDiff);
owner->SpellFinished(spell_id, tgt, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
}
void QuestManager::selfcast(int spell_id) {
QuestManagerCurrentQuestVars();
if (initiator)
initiator->SpellFinished(spell_id, initiator, 10, 0, -1, spells[spell_id].ResistDiff);
initiator->SpellFinished(spell_id, initiator, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
void QuestManager::addloot(int item_id, int charges, bool equipitem) {
+3 -3
View File
@@ -149,7 +149,7 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], who, 10, 0, -1,
SpellFinished(aabonuses.SkillAttackProc[2], who, EQEmu::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false);
@@ -786,7 +786,7 @@ 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 (RangeItem->ExpendableArrow || !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 {
@@ -2429,7 +2429,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills:
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1,
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
+67 -38
View File
@@ -194,6 +194,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
bool SE_SpellTrigger_HasCast = false;
// if buff slot, use instrument mod there, otherwise calc it
uint32 instrument_mod = buffslot > -1 ? buffs[buffslot].instrument_mod : caster ? caster->GetInstrumentMod(spell_id) : 10;
// iterate through the effects in the spell
for (i = 0; i < EFFECT_COUNT; i++)
{
@@ -201,7 +203,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
continue;
effect = spell.effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, buffslot > -1 ? buffs[buffslot].instrument_mod : 10, caster ? caster : this);
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, instrument_mod, caster ? caster : this);
if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands))
effect_value = GetMaxHP();
@@ -355,8 +357,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
int32 val = 0;
val = 7500*effect_value;
val = caster->GetActSpellHealing(spell_id, val, this);
val = 7500 * effect_value;
if (caster)
val = caster->GetActSpellHealing(spell_id, val, this);
if (val > 0)
HealDamage(val, caster);
@@ -375,12 +378,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
snprintf(effect_desc, _EDLEN, "Current Mana: %+i", effect_value);
#endif
SetMana(GetMana() + effect_value);
caster->SetMana(caster->GetMana() + std::abs(effect_value));
if (caster)
caster->SetMana(caster->GetMana() + std::abs(effect_value));
if (effect_value < 0)
TryTriggerOnValueAmount(false, true);
#ifdef SPELL_EFFECT_SPAM
caster->Message(0, "You have gained %+i mana!", effect_value);
if (caster)
caster->Message(0, "You have gained %+i mana!", effect_value);
#endif
}
}
@@ -532,7 +537,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
}
}
if (effect == SE_GateCastersBindpoint && caster->IsClient())
if (effect == SE_GateCastersBindpoint && caster && caster->IsClient())
{ // Teleport Bind uses caster's bind point
int index = spells[spell_id].base[i] - 1;
if (index < 0 || index > 4)
@@ -648,7 +653,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
//Added client messages to give some indication this effect is active.
// Is there a message generated? Too disgusted by raids.
uint32 time = spell.base[i] * 10 * 1000;
if (caster->IsClient()) {
if (caster && caster->IsClient()) {
if (caster->IsGrouped()) {
auto group = caster->GetGroup();
for (int i = 0; i < 6; ++i)
@@ -695,7 +700,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
((GetLevel() > max_level) && caster && (!caster->IsNPC() ||
(caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))))))
{
caster->Message_StringID(MT_SpellFailure, IMMUNE_STUN);
if (caster)
caster->Message_StringID(MT_SpellFailure, IMMUNE_STUN);
} else {
int stun_resist = itembonuses.StunResist+spellbonuses.StunResist;
if (IsClient())
@@ -704,7 +710,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if (stun_resist <= 0 || zone->random.Int(0,99) >= stun_resist) {
Log.Out(Logs::Detail, Logs::Combat, "Stunned. We had %d percent resist chance.", stun_resist);
if (caster->IsClient())
if (caster && caster->IsClient())
effect_value += effect_value*caster->GetFocusEffect(focusFcStunTimeMod, spell_id)/100;
Stun(effect_value);
@@ -916,11 +922,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this)
if(caster && caster->IsClient() && caster != this)
caster->CastToClient()->QueuePacket(action_packet);
CastToClient()->QueuePacket(message_packet);
if(caster->IsClient() && caster != this)
if(caster && caster->IsClient() && caster != this)
caster->CastToClient()->QueuePacket(message_packet);
CastToClient()->SetBindPoint(spells[spell_id].base[i] - 1);
@@ -1031,7 +1037,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if(zone->random.Roll(effect_value))
Gate(spells[spell_id].base2[i] - 1);
else
else if (caster)
caster->Message_StringID(MT_SpellFailure,GATE_FAIL);
}
break;
@@ -1043,7 +1049,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
snprintf(effect_desc, _EDLEN, "Cancel Magic: %d", effect_value);
#endif
if(GetSpecialAbility(UNDISPELLABLE)){
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
if (caster)
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
break;
}
@@ -1053,7 +1060,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
spells[buffs[slot].spellid].dispel_flag == 0 &&
!IsDiscipline(buffs[slot].spellid))
{
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
BuffFadeBySlot(slot);
slot = buff_count;
}
@@ -1068,7 +1075,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
snprintf(effect_desc, _EDLEN, "Dispel Detrimental: %d", effect_value);
#endif
if(GetSpecialAbility(UNDISPELLABLE)){
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
if (caster)
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
break;
}
@@ -1078,7 +1086,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
IsDetrimentalSpell(buffs[slot].spellid) &&
spells[buffs[slot].spellid].dispel_flag == 0)
{
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
BuffFadeBySlot(slot);
slot = buff_count;
}
@@ -1093,7 +1101,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
snprintf(effect_desc, _EDLEN, "Dispel Beneficial: %d", effect_value);
#endif
if(GetSpecialAbility(UNDISPELLABLE)){
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
if (caster)
caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name);
break;
}
@@ -1103,7 +1112,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
IsBeneficialSpell(buffs[slot].spellid) &&
spells[buffs[slot].spellid].dispel_flag == 0)
{
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
BuffFadeBySlot(slot);
slot = buff_count;
}
@@ -1120,7 +1129,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if (buffs[slot].spellid != SPELL_UNKNOWN &&
IsDetrimentalSpell(buffs[slot].spellid))
{
if (TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){
BuffFadeBySlot(slot);
}
}
@@ -1509,7 +1518,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
((GetLevel() > max_level)
&& caster && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))))
{
caster->Message_StringID(MT_Shout, IMMUNE_STUN);
if (caster)
caster->Message_StringID(MT_Shout, IMMUNE_STUN);
}
else
{
@@ -1726,7 +1736,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
break;
}
}
else {
else if (caster) {
Raid *r = entity_list.GetRaidByClient(caster->CastToClient());
if(r)
{
@@ -1766,7 +1776,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
Message_StringID(4, CORPSE_CANT_SENSE);
}
}
else
else if (caster)
caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ);
}
else {
@@ -2113,7 +2123,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Sacrifice");
#endif
if(!IsClient() || !caster->IsClient()){
if(!caster || !IsClient() || !caster->IsClient()){
break;
}
CastToClient()->SacrificeConfirm(caster->CastToClient());
@@ -2122,12 +2132,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_SummonPC:
{
if(IsClient()){
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), caster->GetX(), caster->GetY(), caster->GetZ(), caster->GetHeading(), 2, SummonPC);
if (!caster)
break;
if (IsClient()) {
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), caster->GetX(),
caster->GetY(), caster->GetZ(), caster->GetHeading(), 2,
SummonPC);
Message(15, "You have been summoned!");
entity_list.ClearAggro(this);
}
else
} else
caster->Message(13, "This spell can only be cast on players.");
break;
@@ -2174,6 +2187,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_TemporaryPets: //Dook- swarms and wards:
{
if (!caster)
break;
// this makes necro epic 1.5/2.0 proc work properly
if((spell_id != 6882) && (spell_id != 6884)) // Chaotic Jester/Steadfast Servant
{
@@ -2269,6 +2284,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
*/
int16 focus = 0;
int ReuseTime = spells[spell_id].recast_time + spells[spell_id].recovery_time;
if (!caster)
break;
focus = caster->GetFocusEffect(focusFcBaseEffects, spell_id);
@@ -2292,7 +2309,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
snprintf(effect_desc, _EDLEN, "Wake The Dead");
#endif
//meh dupe issue with npc casting this
if(caster->IsClient()){
if(caster && caster->IsClient()){
int dur = spells[spell_id].max[i];
if (!dur)
dur = 60;
@@ -2657,7 +2674,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_Taunt:
{
if (IsNPC()){
if (caster && IsNPC()){
caster->Taunt(this->CastToNPC(), false, static_cast<float>(spell.base[i]), true, spell.base2[i]);
}
break;
@@ -2745,7 +2762,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if (caster && IsValidSpell(spells[spell_id].base2[i])){
if(zone->random.Roll(spells[spell_id].base[i]))
caster->SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
caster->SpellFinished(spells[spell_id].base2[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
}
break;
}
@@ -3668,10 +3685,11 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
break;
}
// These effects always trigger when they fade.
// Should we have this triggered from else where?
case SE_CastOnFadeEffect:
case SE_CastOnFadeEffectNPC:
case SE_CastOnFadeEffectAlways: {
if (buff.ticsremaining == 1) {
if (buff.ticsremaining == 0) {
SpellOnTarget(spells[buff.spellid].base[i], this);
}
break;
@@ -5135,7 +5153,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
if (Caston_spell_id) {
if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id))
SpellFinished(Caston_spell_id, this, 10, 0, -1, spells[Caston_spell_id].ResistDiff);
SpellFinished(Caston_spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[Caston_spell_id].ResistDiff);
}
return (value * lvlModifier / 100);
@@ -6226,7 +6244,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
{
/*If return TRUE spell met all restrictions and can continue (this = target).
This check is used when the spell_new field CastRestriction is defined OR spell effect '0'(DD/Heal) has a defined limit
Range 1 : UNKNOWN
Range 1 : UNKNOWN -- the spells with this seem to not have a restiction, true for now
Range 100 : *Animal OR Humanoid
Range 101 : *Dragon
Range 102 : *Animal OR Insect
@@ -6253,7 +6271,10 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
Range 124 : *Undead HP < 10%
Range 125 : *Clockwork HP < 10%
Range 126 : *Wisp HP < 10%
Range 127-130 : UNKNOWN
Range 127 : UNKNOWN
Range 128 : pure melee -- guess
Range 129 : pure caster -- guess
Range 130 : hybrid -- guess
Range 150 : UNKNOWN
Range 190 : No Raid boss flag *not implemented
Range 191 : This spell will deal less damage to 'exceptionally strong targets' - Raid boss flag *not implemented
@@ -6265,12 +6286,17 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
Range 300 - 303 : UNKOWN *not implemented
Range 304 : Chain + Plate class (buffs)
Range 399 - 409 : Heal if HP within a specified range (400 = 0-25% 401 = 25 - 35% 402 = 35-45% ect)
Range 410 - 411 : UNKOWN
Range 410 - 411 : UNKOWN -- examples are auras that cast on NPCs maybe in combat/out of combat?
Range 500 - 599 : Heal if HP less than a specified value
Range 600 - 699 : Limit to Body Type [base2 - 600 = Body]
Range 700 : NPC only -- from patch notes "Wizard - Arcane Fusion no longer deals damage to non-NPC targets. This should ensure that wizards who fail their Bucolic Gambit are slightly less likely to annihilate themselves."
Range 701 : NOT PET
Range 800 : UKNOWN
Range 800 : UKNOWN -- Target's Target Test (16598)
Range 812 : UNKNOWN -- triggered by Thaumatize Owner
Range 814 : UNKNOWN -- Vegetentacles
Range 815 : UNKNOWN -- Pumpkin Pulp Splash
Range 816 : UNKNOWN -- Rotten Fruit Splash
Range 817 : UNKNOWN -- Tainted Bixie Pollen Splash
Range 818 - 819 : If Undead/If Not Undead
Range 820 - 822 : UKNOWN
Range 835 : Unknown *not implemented
@@ -6290,6 +6316,9 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
switch(value)
{
case 1:
return true;
case 100:
if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid))
return true;
@@ -6683,10 +6712,10 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
if (IsValidSpell(spell_id)) {
if (IsBeneficialSpell(spell_id))
SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
else if(attacker)
SpellFinished(spell_id, attacker, 10, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, attacker, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
}
}
+172 -72
View File
@@ -104,6 +104,8 @@ extern Zone* zone;
extern volatile bool is_zone_loaded;
extern WorldServer worldserver;
using EQEmu::CastingSlot;
// this is run constantly for every mob
void Mob::SpellProcess()
{
@@ -145,13 +147,13 @@ void NPC::SpellProcess()
// the rule is you can cast one triggered (usually timed) spell at a time
// but things like SpellFinished() can run concurrent with a triggered cast
// to allow procs to work
bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot,
uint32 timer, uint32 timer_duration, int16 *resist_adjust,
uint32 aa_id)
{
Log.Out(Logs::Detail, Logs::Spells, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d",
(IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot);
(IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, static_cast<int>(slot), cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot);
if(casting_spell_id == spell_id)
ZeroCastingVars();
@@ -178,7 +180,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if(IsClient())
CastToClient()->SendSpellBarEnable(spell_id);
if(casting_spell_id && IsNPC())
CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot);
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
return(false);
}
//It appears that the Sanctuary effect is removed by a check on the client side (keep this however for redundancy)
@@ -201,7 +203,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if(IsClient())
CastToClient()->SendSpellBarEnable(spell_id);
if(casting_spell_id && IsNPC())
CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot);
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
return(false);
}
@@ -231,7 +233,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
// check for fizzle
// note that CheckFizzle itself doesn't let NPCs fizzle,
// but this code allows for it.
if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id))
if(slot < CastingSlot::MaxGems && !CheckFizzle(spell_id))
{
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
InterruptSpell(fizzle_msg, 0x121, spell_id);
@@ -252,7 +254,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
}
//Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits.
if(item_slot && IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT)))
if(item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
{
ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot);
int bitmask = 1;
@@ -336,14 +338,13 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
// this is the 2nd phase of CastSpell, broken up like this to make it easier
// to repeat a spell for bard songs
//
bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish,
uint32 item_slot, uint32 timer, uint32 timer_duration,
int16 resist_adjust, uint32 aa_id)
{
Mob* pMob = nullptr;
int32 orgcasttime;
EQApplicationPacket *outapp = nullptr;
if(!IsValidSpell(spell_id)) {
InterruptSpell();
@@ -353,7 +354,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
const SPDat_Spell_Struct &spell = spells[spell_id];
Log.Out(Logs::Detail, Logs::Spells, "DoCastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item %d",
spell.name, spell_id, target_id, slot, cast_time, mana_cost, item_slot==0xFFFFFFFF?999:item_slot);
spell.name, spell_id, target_id, static_cast<int>(slot), cast_time, mana_cost, item_slot==0xFFFFFFFF?999:item_slot);
casting_spell_id = spell_id;
casting_spell_slot = slot;
@@ -418,7 +419,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
// If you're at full mana, let it cast even if you dont have enough mana
// we calculated this above, now enforce it
if(mana_cost > 0 && slot != USE_ITEM_SPELL_SLOT)
if(mana_cost > 0 && slot != CastingSlot::Item)
{
int my_curmana = GetMana();
int my_maxmana = GetMaxMana();
@@ -453,8 +454,26 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Casting time %d (orig %d), mana cost %d",
spell_id, cast_time, orgcasttime, mana_cost);
// now tell the people in the area -- we ALWAYS want to send this, even instant cast spells.
// The only time this is skipped is for NPC innate procs and weapon procs. Procs from buffs
// oddly still send this. Since those cases don't reach here, we don't need to check them
if (slot != CastingSlot::Discipline) {
auto outapp = new EQApplicationPacket(OP_BeginCast,sizeof(BeginCast_Struct));
BeginCast_Struct* begincast = (BeginCast_Struct*)outapp->pBuffer;
begincast->caster_id = GetID();
begincast->spell_id = spell_id;
begincast->cast_time = orgcasttime; // client calculates reduced time by itself
outapp->priority = 3;
entity_list.QueueCloseClients(this, outapp, false, 200, 0, true); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS);
safe_delete(outapp);
}
// cast time is 0, just finish it right now and be done with it
if(cast_time == 0) {
if (!DoCastingChecks()) {
StopCasting();
return false;
}
CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust);
return(true);
}
@@ -476,25 +495,14 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if (oSpellWillFinish)
*oSpellWillFinish = Timer::GetCurrentTime() + cast_time + 100;
// now tell the people in the area
outapp = new EQApplicationPacket(OP_BeginCast,sizeof(BeginCast_Struct));
BeginCast_Struct* begincast = (BeginCast_Struct*)outapp->pBuffer;
begincast->caster_id = GetID();
begincast->spell_id = spell_id;
begincast->cast_time = orgcasttime; // client calculates reduced time by itself
outapp->priority = 3;
entity_list.QueueCloseClients(this, outapp, false, 200, 0, true); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS);
safe_delete(outapp);
outapp = nullptr;
if (IsClient() && slot == USE_ITEM_SPELL_SLOT &&item_slot != 0xFFFFFFFF) {
if (IsClient() && slot == CastingSlot::Item && 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();
StopCasting();
return false;
}
@@ -550,6 +558,10 @@ bool Mob::DoCastingChecks()
}
}
if (IsClient() && spells[spell_id].EndurTimerIndex > 0 && casting_spell_slot < CastingSlot::MaxGems)
if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].EndurTimerIndex))
return false;
casting_spell_checks = true;
return true;
}
@@ -767,7 +779,7 @@ void Mob::ZeroCastingVars()
spellend_timer.Disable();
casting_spell_id = 0;
casting_spell_targetid = 0;
casting_spell_slot = 0;
casting_spell_slot = CastingSlot::Gem1;
casting_spell_mana = 0;
casting_spell_inventory_slot = 0;
casting_spell_timer = 0;
@@ -802,7 +814,7 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
}
if(casting_spell_id && IsNPC()) {
CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot);
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
}
if(casting_spell_aa_id && IsClient()) { //Rest AA Timer on failed cast
@@ -874,18 +886,44 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
}
// this is like interrupt, just it doesn't spam interrupt packets to everyone
// There are a few cases where this is what live does :P
void Mob::StopCasting()
{
if (casting_spell_id && IsNPC()) {
CastToNPC()->AI_Event_SpellCastFinished(false, static_cast<uint16>(casting_spell_slot));
}
if (IsClient()) {
auto c = CastToClient();
if (casting_spell_aa_id) { //Rest AA Timer on failed cast
c->Message_StringID(MT_SpellFailure, ABILITY_FAILED);
c->ResetAlternateAdvancementTimer(casting_spell_aa_id);
}
auto outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct));
auto mc = (ManaChange_Struct *)outapp->pBuffer;
mc->new_mana = GetMana();
mc->stamina = GetEndurance();
mc->spell_id = casting_spell_id;
mc->keepcasting = 0;
c->FastQueuePacket(&outapp);
}
ZeroCastingVars();
}
// this is called after the timer is up and the spell is finished
// casting. everything goes through here, including items with zero cast time
// only to be used from SpellProcess
// NOTE: do not put range checking, etc into this function. this should
// just check timed spell specific things before passing off to SpellFinished
// which figures out proper targets etc
void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slot,
uint16 mana_used, uint32 inventory_slot, int16 resist_adjust)
{
bool IsFromItem = false;
if(IsClient() && slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && slot != TARGET_RING_SPELL_SLOT && spells[spell_id].recast_time > 1000) { // 10 is item
if(IsClient() && slot != CastingSlot::Item && slot != CastingSlot::PotionBelt && spells[spell_id].recast_time > 1000) { // 10 is item
if(!CastToClient()->GetPTimers().Expired(&database, pTimerSpellStart + spell_id, false)) {
//should we issue a message or send them a spell gem packet?
Message_StringID(13, SPELL_RECAST);
@@ -895,7 +933,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
}
}
if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT)))
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
{
IsFromItem = true;
ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot);
@@ -1193,7 +1231,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
int16 DeleteChargeFromSlot = -1;
if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT))
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)
&& inventory_slot != 0xFFFFFFFF) // 10 is an item
{
bool fromaug = false;
@@ -1306,8 +1344,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
{
if(IsClient())
{
this->CastToClient()->CheckSongSkillIncrease(spell_id);
this->CastToClient()->MemorizeSpell(slot, spell_id, memSpellSpellbar);
Client *c = CastToClient();
c->CheckSongSkillIncrease(spell_id);
if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems)
c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000);
c->MemorizeSpell(static_cast<uint32>(slot), spell_id, memSpellSpellbar);
}
Log.Out(Logs::Detail, Logs::Spells, "Bard song %d should be started", spell_id);
}
@@ -1319,14 +1360,15 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
SendSpellBarEnable(spell_id);
// this causes the delayed refresh of the spell bar gems
c->MemorizeSpell(slot, spell_id, memSpellSpellbar);
if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems)
c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000);
c->MemorizeSpell(static_cast<uint32>(slot), spell_id, memSpellSpellbar);
// this tells the client that casting may happen again
SetMana(GetMana());
// skills
if(slot < MAX_PP_MEMSPELL)
{
if (EQEmu::skills::IsCastingSkill(spells[spell_id].skill)) {
c->CheckIncreaseSkill(spells[spell_id].skill, nullptr);
// increased chance of gaining channel skill if you regained concentration
@@ -1348,8 +1390,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
}
bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, uint16 slot) {
bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, CastingSlot slot, bool isproc)
{
/*
The basic types of spells:
@@ -1395,6 +1437,11 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
targetType = ST_GroupClientAndPet;
}
// NPC innate procs override the target type to single target.
// Yes. This code will cause issues if they have the proc as innate AND on a weapon. Oh well.
if (isproc && IsNPC() && CastToNPC()->GetInnateProcSpellID() == spell_id)
targetType = ST_Target;
if (spell_target && !spell_target->PassCastRestriction(true, spells[spell_id].CastRestriction)){
Message_StringID(13,SPELL_NEED_TAR);
return false;
@@ -1682,7 +1729,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
case ST_Group:
case ST_GroupNoPets:
{
if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && slot != USE_ITEM_SPELL_SLOT) {
if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB))) {
if( (!target) ||
(target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) ||
(target->IsCorpse()) )
@@ -1728,7 +1775,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
{
if(IsGrouped())
{
group_id_caster = GetGroup()->GetID();
if (Group* group = GetGroup()) {
group_id_caster = group->GetID();
}
}
else if(IsRaidGrouped())
{
@@ -1743,7 +1792,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
Mob *owner = GetOwner();
if(owner->IsGrouped())
{
group_id_caster = owner->GetGroup()->GetID();
if (Group* group = owner->GetGroup()) {
group_id_caster = group->GetID();
}
}
else if(owner->IsRaidGrouped())
{
@@ -1772,7 +1823,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
{
if(spell_target->IsGrouped())
{
group_id_target = spell_target->GetGroup()->GetID();
if (Group* group = spell_target->GetGroup()) {
group_id_target = group->GetID();
}
}
else if(spell_target->IsRaidGrouped())
{
@@ -1787,7 +1840,9 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
Mob *owner = spell_target->GetOwner();
if(owner->IsGrouped())
{
group_id_target = owner->GetGroup()->GetID();
if (Group* group = owner->GetGroup()) {
group_id_target = group->GetID();
}
}
else if(owner->IsRaidGrouped())
{
@@ -1904,7 +1959,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
// only used from CastedSpellFinished, and procs
// 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,
bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used,
uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override)
{
//EQApplicationPacket *outapp = nullptr;
@@ -1974,7 +2029,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
//determine the type of spell target we have
CastAction_type CastAction;
if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot))
if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot, isproc))
return(false);
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: target type %d, target %s, AE center %s", spell_id, CastAction, spell_target?spell_target->GetName():"NONE", ae_center?ae_center->GetName():"NONE");
@@ -2263,7 +2318,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
bool mgb = HasMGB() && spells[spell_id].can_mgb;
// if this was a spell slot or an ability use up the mana for it
if(slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && slot != TARGET_RING_SPELL_SLOT && mana_used > 0)
if(slot != CastingSlot::Item && slot != CastingSlot::PotionBelt && mana_used > 0)
{
mana_used = GetActSpellCost(spell_id, mana_used);
if (mgb) {
@@ -2326,7 +2381,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
}
if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT)))
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
{
ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot);
if(itm && itm->GetItem()->RecastDelay > 0){
@@ -2345,7 +2400,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
if(IsNPC())
CastToNPC()->AI_Event_SpellCastFinished(true, slot);
CastToNPC()->AI_Event_SpellCastFinished(true, static_cast<uint16>(slot));
return true;
}
@@ -2360,8 +2415,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
*
* return false to stop the song
*/
bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot) {
if(slot == USE_ITEM_SPELL_SLOT) {
bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slot) {
if(slot == CastingSlot::Item) {
//bard songs should never come from items...
Log.Out(Logs::Detail, Logs::Spells, "Bard Song Pulse %d: Supposidly cast from an item. Killing song.", spell_id);
return(false);
@@ -2972,8 +3027,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
if
(
effect1 == SE_AttackSpeed ||
effect1 == SE_AttackSpeed2 ||
effect1 == SE_AttackSpeed3
effect1 == SE_AttackSpeed2
)
{
sp1_value -= 100;
@@ -3047,6 +3101,34 @@ bool Client::CheckSpellLevelRestriction(uint16 spell_id)
return true;
}
uint32 Mob::GetFirstBuffSlot(bool disc, bool song)
{
return 0;
}
uint32 Mob::GetLastBuffSlot(bool disc, bool song)
{
return GetCurrentBuffSlots();
}
uint32 Client::GetFirstBuffSlot(bool disc, bool song)
{
if (song)
return GetMaxBuffSlots();
if (disc)
return GetMaxBuffSlots() + GetMaxSongSlots();
return 0;
}
uint32 Client::GetLastBuffSlot(bool disc, bool song)
{
if (song)
return GetMaxBuffSlots() + GetCurrentSongSlots();
if (disc)
return GetMaxBuffSlots() + GetMaxSongSlots() + GetCurrentDiscSlots();
return GetCurrentBuffSlots();
}
// returns the slot the buff was added to, -1 if it wasn't added due to
// stacking problems, and -2 if this is not a buff
// if caster is null, the buff will be added with the caster level being
@@ -3084,18 +3166,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
// we also check if overwriting will occur. this is so after this loop
// we can determine if there will be room for this buff
int buff_count = GetMaxTotalSlots();
uint32 start_slot = 0;
uint32 end_slot = 0;
if (IsDisciplineBuff(spell_id)) {
start_slot = GetMaxBuffSlots() + GetMaxSongSlots();
end_slot = start_slot + GetCurrentDiscSlots();
} else if(spells[spell_id].short_buff_box) {
start_slot = GetMaxBuffSlots();
end_slot = start_slot + GetCurrentSongSlots();
} else {
start_slot = 0;
end_slot = GetCurrentBuffSlots();
}
uint32 start_slot = GetFirstBuffSlot(IsDisciplineBuff(spell_id), spells[spell_id].short_buff_box);
uint32 end_slot = GetLastBuffSlot(IsDisciplineBuff(spell_id), spells[spell_id].short_buff_box);
for (buffslot = 0; buffslot < buff_count; buffslot++) {
const Buffs_Struct &curbuf = buffs[buffslot];
@@ -3107,7 +3179,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
if (ret == -1) { // stop the spell
Log.Out(Logs::Detail, Logs::Spells, "Adding buff %d failed: stacking prevented by spell %d in slot %d with caster level %d",
spell_id, curbuf.spellid, buffslot, curbuf.casterlevel);
if (caster->IsClient() && RuleB(Client, UseLiveBlockedMessage)) {
if (caster && caster->IsClient() && RuleB(Client, UseLiveBlockedMessage)) {
caster->Message(13, "Your %s did not take hold on %s. (Blocked by %s.)", spells[spell_id].name, this->GetName(), spells[curbuf.spellid].name);
}
return -1;
@@ -3769,7 +3841,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
}
if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id)
SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
SpellFinished(spells[spell_id].RecourseLink, this, CastingSlot::Item, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
if (IsDetrimentalSpell(spell_id)) {
@@ -4713,6 +4785,7 @@ void Mob::SendSpellBarEnable(uint16 spell_id)
manachange->new_mana = GetMana();
manachange->spell_id = spell_id;
manachange->stamina = CastToClient()->GetEndurance();
manachange->keepcasting = 0;
outapp->priority = 6;
CastToClient()->QueuePacket(outapp);
safe_delete(outapp);
@@ -5245,7 +5318,7 @@ bool Mob::UseBardSpellLogic(uint16 spell_id, int slot)
spell_id = casting_spell_id;
if(slot == -1)
slot = casting_spell_slot;
slot = static_cast<int>(casting_spell_slot);
// should we treat this as a bard singing?
return
@@ -5273,7 +5346,7 @@ void Mob::_StopSong()
{
bardsong = 0;
bardsong_target_id = 0;
bardsong_slot = 0;
bardsong_slot = CastingSlot::Gem1;
bardsong_timer.Disable();
}
@@ -5351,8 +5424,8 @@ void Mob::SendPetBuffsToClient()
int MaxSlots = GetMaxTotalSlots();
if(MaxSlots > BUFF_COUNT)
MaxSlots = BUFF_COUNT;
if(MaxSlots > PET_BUFF_COUNT)
MaxSlots = PET_BUFF_COUNT;
for(int buffslot = 0; buffslot < MaxSlots; buffslot++)
{
@@ -5449,10 +5522,14 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration)
int Client::GetCurrentBuffSlots() const
{
if(15 + aabonuses.BuffSlotIncrease > 25)
return 25;
else
return 15 + aabonuses.BuffSlotIncrease;
int numbuffs = 15;
// client does check spells and items
numbuffs += aabonuses.BuffSlotIncrease + spellbonuses.BuffSlotIncrease + itembonuses.BuffSlotIncrease;
if (GetLevel() > 70)
numbuffs++;
if (GetLevel() > 74)
numbuffs++;
return EQEmu::ClampUpper(numbuffs, GetMaxBuffSlots());
}
int Client::GetCurrentSongSlots() const
@@ -5644,3 +5721,26 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
++iter;
}
}
// duration in seconds
void Client::SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration)
{
if (timer_id > 19)
return;
Log.Out(Logs::Detail, Logs::Spells, "Setting Linked Spell Reuse %d for %d", timer_id, duration);
GetPTimers().Start(pTimerLinkedSpellReuseStart + timer_id, duration);
auto outapp = new EQApplicationPacket(OP_LinkedReuse, sizeof(LinkedSpellReuseTimer_Struct));
auto lr = (LinkedSpellReuseTimer_Struct *)outapp->pBuffer;
lr->timer_id = timer_id;
lr->start_time = Timer::GetCurrentTime() / 1000;
lr->end_time = lr->start_time + duration;
FastQueuePacket(&outapp);
}
bool Client::IsLinkedSpellReuseTimerReady(uint32 timer_id)
{
if (timer_id > 19)
return true;
return GetPTimers().Expired(&database, pTimerLinkedSpellReuseStart + timer_id, false);
}
+2 -1
View File
@@ -297,6 +297,7 @@
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
#define SUCCOR_FAIL 5169 //The portal collapes before you can escape!
#define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.'
#define AVOID_STUNNING_BLOW 5753 //You avoid the stunning blow.
#define FATAL_BOW_SHOT 5745 //%1 performs a FATAL BOW SHOT!!
#define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia!
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
@@ -354,7 +355,7 @@
#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 SHAKE_OFF_STUN 9077 //You shake off the stun effect!
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2.
#define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master.
+1 -1
View File
@@ -115,7 +115,7 @@ void Trap::Trigger(Mob* trigger)
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
}
if(hiddenTrigger){
hiddenTrigger->SpellFinished(effectvalue, trigger, 10, 0, -1, spells[effectvalue].ResistDiff);
hiddenTrigger->SpellFinished(effectvalue, trigger, EQEmu::CastingSlot::Item, 0, -1, spells[effectvalue].ResistDiff);
}
break;
case trapTypeAlarm:
+3 -1
View File
@@ -8,7 +8,7 @@
extern const ZoneConfig *Config;
enum WaterRegionType {
enum WaterRegionType : int {
RegionTypeUnsupported = -2,
RegionTypeUntagged = -1,
RegionTypeNormal = 0,
@@ -33,6 +33,8 @@ public:
virtual bool InVWater(const glm::vec3& location) const = 0;
virtual bool InLava(const glm::vec3& location) const = 0;
virtual bool InLiquid(const glm::vec3& location) const = 0;
virtual bool InPvP(const glm::vec3& location) const = 0;
virtual bool InZoneLine(const glm::vec3& location) const = 0;
protected:
virtual bool Load(FILE *fp) { return false; }
+8
View File
@@ -30,6 +30,14 @@ bool WaterMapV1::InLiquid(const glm::vec3& location) const {
return InWater(location) || InLava(location);
}
bool WaterMapV1::InPvP(const glm::vec3& location) const {
return ReturnRegionType(location) == RegionTypePVP;
}
bool WaterMapV1::InZoneLine(const glm::vec3& location) const {
return ReturnRegionType(location) == RegionTypeZoneLine;
}
bool WaterMapV1::Load(FILE *fp) {
uint32 bsp_tree_size;
if (fread(&bsp_tree_size, sizeof(bsp_tree_size), 1, fp) != 1) {
+2
View File
@@ -24,6 +24,8 @@ public:
virtual bool InVWater(const glm::vec3& location) const;
virtual bool InLava(const glm::vec3& location) const;
virtual bool InLiquid(const glm::vec3& location) const;
virtual bool InPvP(const glm::vec3& location) const;
virtual bool InZoneLine(const glm::vec3& location) const;
protected:
virtual bool Load(FILE *fp);
+8
View File
@@ -33,6 +33,14 @@ bool WaterMapV2::InLiquid(const glm::vec3& location) const {
return InWater(location) || InLava(location);
}
bool WaterMapV2::InPvP(const glm::vec3& location) const {
return ReturnRegionType(location) == RegionTypePVP;
}
bool WaterMapV2::InZoneLine(const glm::vec3& location) const {
return ReturnRegionType(location) == RegionTypeZoneLine;
}
bool WaterMapV2::Load(FILE *fp) {
uint32 region_count;
if (fread(&region_count, sizeof(region_count), 1, fp) != 1) {
+2
View File
@@ -17,6 +17,8 @@ public:
virtual bool InVWater(const glm::vec3& location) const;
virtual bool InLava(const glm::vec3& location) const;
virtual bool InLiquid(const glm::vec3& location) const;
virtual bool InPvP(const glm::vec3& location) const;
virtual bool InZoneLine(const glm::vec3& location) const;
protected:
virtual bool Load(FILE *fp);
+1 -1
View File
@@ -765,7 +765,7 @@ void WorldServer::Process() {
zone->SetZoneHasCurrentTime(true);
}
if (zone->is_zone_time_localized){
if (zone && 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;
+3 -1
View File
@@ -1594,7 +1594,9 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Clien
iterator.Advance();
}
if(closest_dist > 400.0f && closest_dist < max_distance2)
// if we have a water map and it says we're in a zoneline, lets assume it's just a really big zone line
// this shouldn't open up any exploits since those situations are detected later on
if ((zone->HasWaterMap() && !zone->watermap->InZoneLine(glm::vec3(client->GetPosition()))) || (!zone->HasWaterMap() && closest_dist > 400.0f && closest_dist < max_distance2))
{
if(client)
client->CheatDetected(MQZoneUnknownDest, location.x, location.y, location.z); // Someone is trying to use /zone
+2 -1
View File
@@ -3178,7 +3178,8 @@ void ZoneDatabase::SavePetInfo(Client *client)
query.clear();
// pet buffs!
for (int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) {
int max_slots = RuleI(Spells, MaxTotalSlotsPET);
for (int index = 0; index < max_slots; index++) {
if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0)
continue;
if (query.length() == 0)
+1 -1
View File
@@ -125,7 +125,7 @@ struct PetInfo {
uint32 HP;
uint32 Mana;
float size;
SpellBuff_Struct Buffs[BUFF_COUNT];
SpellBuff_Struct Buffs[PET_BUFF_COUNT];
uint32 Items[EQEmu::legacy::EQUIPMENT_SIZE];
char Name[64];
};