Implement extra spell gems!

New limits:
    Tit: 9
    SoF: 9
    SoD: 10
    UF: 12
    RoF: 12
    RoF2: 12

The SoF client doesn't actually support 10 like SoF should
RoF/RoF2 actually have 4 extra broken spell gems in the UI. They don't work and
will likely crash your client

Quest stuff assumes you are passing in valid slots.
(note the old default of 10 should be 22)

There are still somethings to do like clean up the memmed spells if one switches
to an older client that doesn't support as many as their previous client.
This commit is contained in:
Michael Cook (mackal)
2016-07-31 17:16:23 -04:00
parent 4c4b0aba0c
commit ef2c17748e
46 changed files with 800 additions and 201 deletions
+36 -37
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,7 +338,7 @@ 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)
@@ -353,7 +355,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 +420,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();
@@ -487,7 +489,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
safe_delete(outapp);
outapp = nullptr;
if (IsClient() && slot == USE_ITEM_SPELL_SLOT &&item_slot != 0xFFFFFFFF) {
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);
@@ -767,7 +769,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 +804,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
@@ -880,12 +882,12 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
// 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::Item && 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 +897,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 +1195,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;
@@ -1307,7 +1309,7 @@ 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);
this->CastToClient()->MemorizeSpell(static_cast<uint32>(slot), spell_id, memSpellSpellbar);
}
Log.Out(Logs::Detail, Logs::Spells, "Bard song %d should be started", spell_id);
}
@@ -1319,21 +1321,18 @@ 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);
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)
{
c->CheckIncreaseSkill(spells[spell_id].skill, nullptr);
c->CheckIncreaseSkill(spells[spell_id].skill, nullptr);
// increased chance of gaining channel skill if you regained concentration
c->CheckIncreaseSkill(EQEmu::skills::SkillChanneling, nullptr, regain_conc ? 5 : 0);
// increased chance of gaining channel skill if you regained concentration
c->CheckIncreaseSkill(EQEmu::skills::SkillChanneling, nullptr, regain_conc ? 5 : 0);
c->CheckSpecializeIncrease(spell_id);
}
c->CheckSpecializeIncrease(spell_id);
}
}
@@ -1348,8 +1347,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)
{
/*
The basic types of spells:
@@ -1682,7 +1681,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) {
if( (!target) ||
(target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) ||
(target->IsCorpse()) )
@@ -1904,7 +1903,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;
@@ -2263,7 +2262,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 +2325,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 +2344,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 +2359,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);
@@ -3769,7 +3768,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, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
if (IsDetrimentalSpell(spell_id)) {
@@ -5245,7 +5244,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 +5272,7 @@ void Mob::_StopSong()
{
bardsong = 0;
bardsong_target_id = 0;
bardsong_slot = 0;
bardsong_slot = CastingSlot::Gem1;
bardsong_timer.Disable();
}