mirror of
https://github.com/EQEmu/Server.git
synced 2026-01-03 06:23:53 +00:00
[Bug Fix] Bard update fixes 1 (#1982)
* fix for bard item charge consumables * [Bug Fix] Bards not consuming item click charges on instant cast items. * [Bug Fix] Bard update fixes 1 bards not respecting deity/race/class restrictions on instant cast items
This commit is contained in:
parent
f65a6d2761
commit
1f560529da
@ -8792,39 +8792,41 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
spell_id = item->Click.Effect;
|
||||
bool is_casting_bard_song = false;
|
||||
|
||||
if
|
||||
(
|
||||
spell_id > 0 &&
|
||||
(
|
||||
!IsValidSpell(spell_id) ||
|
||||
casting_spell_id ||
|
||||
delaytimer ||
|
||||
spellend_timer.Enabled() ||
|
||||
IsStunned() ||
|
||||
IsFeared() ||
|
||||
IsMezzed() ||
|
||||
DivineAura() ||
|
||||
(spells[spell_id].target_type == ST_Ring) ||
|
||||
(IsSilenced() && !IsDiscipline(spell_id)) ||
|
||||
(IsAmnesiad() && IsDiscipline(spell_id)) ||
|
||||
(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) ||
|
||||
(inst->IsScaling() && inst->GetExp() <= 0) // charms don't have spells when less than 0
|
||||
)
|
||||
)
|
||||
{
|
||||
/*
|
||||
Bards on live can click items while casting spell gems, it stops that song cast and replaces it with item click cast.
|
||||
Can not click while casting other items.
|
||||
*/
|
||||
if (GetClass() == BARD && IsCasting() && casting_spell_slot < CastingSlot::MaxGems)
|
||||
{
|
||||
is_casting_bard_song = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spell_id > 0) {
|
||||
|
||||
if (!IsValidSpell(spell_id) ||
|
||||
IsStunned() ||
|
||||
IsFeared() ||
|
||||
IsMezzed() ||
|
||||
DivineAura() ||
|
||||
(spells[spell_id].target_type == ST_Ring) ||
|
||||
(IsSilenced() && !IsDiscipline(spell_id)) ||
|
||||
(IsAmnesiad() && IsDiscipline(spell_id)) ||
|
||||
(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) ||
|
||||
(inst->IsScaling() && inst->GetExp() <= 0)) { // charms don't have spells when less than 0
|
||||
|
||||
SendSpellBarEnable(spell_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (casting_spell_id ||
|
||||
delaytimer ||
|
||||
spellend_timer.Enabled()) {
|
||||
|
||||
/*
|
||||
Bards on live can click items while casting spell gems, it stops that song cast and replaces it with item click cast.
|
||||
Can not click while casting other items.
|
||||
*/
|
||||
if (GetClass() == BARD && IsCasting() && casting_spell_slot < CastingSlot::MaxGems)
|
||||
{
|
||||
is_casting_bard_song = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SendSpellBarEnable(spell_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Modern clients don't require pet targeted for item clicks that are ST_Pet
|
||||
if (spell_id > 0 && (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet))
|
||||
|
||||
@ -357,6 +357,8 @@ public:
|
||||
void ApplySpellEffectIllusion(int32 spell_id, Mob* caster, int buffslot, int base, int limit, int max);
|
||||
void ApplyIllusionToCorpse(int32 spell_id, Corpse* new_corpse);
|
||||
void SendIllusionWearChange(Client* c);
|
||||
int16 GetItemSlotToConsumeCharge(int32 spell_id, uint32 inventory_slot);
|
||||
bool CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot);
|
||||
|
||||
//Bard
|
||||
bool ApplyBardPulse(int32 spell_id, Mob *spell_target, EQ::spells::CastingSlot slot);
|
||||
|
||||
231
zone/spells.cpp
231
zone/spells.cpp
@ -206,55 +206,10 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
}
|
||||
|
||||
//Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits.
|
||||
if(item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
|
||||
if (item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
|
||||
{
|
||||
EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(item_slot);
|
||||
int bitmask = 1;
|
||||
bitmask = bitmask << (CastToClient()->GetClass() - 1);
|
||||
if( itm && itm->GetItem()->Classes != 65535 ) {
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that requires equipping but shouldn't let them equip it
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that they don't meet the race/class requirements to cast
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class");
|
||||
}
|
||||
else {
|
||||
if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::RoF)
|
||||
{
|
||||
// Line 181 in eqstr_us.txt was changed in RoF+
|
||||
Message(Chat::Yellow, "Your race, class, or deity cannot use this item.");
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageString(Chat::Red, CANNOT_USE_ITEM);
|
||||
}
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
if (itm && (itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && item_slot > EQ::invslot::EQUIPMENT_END){
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are attempting to cast a must equip clicky without having it equipped
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
if (!CheckItemRaceClassDietyRestrictionsOnCast(item_slot)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1627,57 +1582,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
|
||||
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)
|
||||
&& inventory_slot != 0xFFFFFFFF) // 10 is an item
|
||||
{
|
||||
bool fromaug = false;
|
||||
EQ::ItemData* augitem = nullptr;
|
||||
uint32 recastdelay = 0;
|
||||
int recasttype = 0;
|
||||
|
||||
while (true) {
|
||||
if (item == nullptr)
|
||||
break;
|
||||
|
||||
for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) {
|
||||
const EQ::ItemInstance* aug_i = item->GetAugment(r);
|
||||
|
||||
if (!aug_i)
|
||||
continue;
|
||||
const EQ::ItemData* aug = aug_i->GetItem();
|
||||
if (!aug)
|
||||
continue;
|
||||
|
||||
if (aug->Click.Effect == spell_id)
|
||||
{
|
||||
recastdelay = aug_i->GetItem()->RecastDelay;
|
||||
recasttype = aug_i->GetItem()->RecastType;
|
||||
fromaug = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug)
|
||||
{
|
||||
//const ItemData* item = item->GetItem();
|
||||
int16 charges = item->GetItem()->MaxCharges;
|
||||
|
||||
if(fromaug) { charges = -1; } //Don't destroy the parent item
|
||||
|
||||
if(charges > -1) { // charged item, expend a charge
|
||||
LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetCharges(), item->GetItem()->MaxCharges);
|
||||
DeleteChargeFromSlot = inventory_slot;
|
||||
} else {
|
||||
LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetItem()->MaxCharges);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogSpells("Item used to cast spell [{}] was missing from inventory slot [{}] after casting!", spell_id, inventory_slot);
|
||||
Message(Chat::Red, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot);
|
||||
InterruptSpell();
|
||||
return;
|
||||
}
|
||||
DeleteChargeFromSlot = GetItemSlotToConsumeCharge(spell_id, inventory_slot);
|
||||
}
|
||||
|
||||
// we're done casting, now try to apply the spell
|
||||
@ -1699,8 +1604,9 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
|
||||
|
||||
TryTriggerOnCastFocusEffect(focusTriggerOnCast, spell_id);
|
||||
|
||||
if(DeleteChargeFromSlot >= 0)
|
||||
if (DeleteChargeFromSlot >= 0) {
|
||||
CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true);
|
||||
}
|
||||
|
||||
//
|
||||
// at this point the spell has successfully been cast
|
||||
@ -6638,7 +6544,128 @@ void Mob::DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time
|
||||
CastSpell(spell_id, target_id, CastingSlot::Item, cast_time, 0, 0, item_slot);
|
||||
}
|
||||
//Instant cast items do not stop bard songs or interrupt casting.
|
||||
else if (DoCastingChecksOnCaster(spell_id)) {
|
||||
SpellFinished(spell_id, entity_list.GetMob(target_id), CastingSlot::Item, 0, item_slot);
|
||||
else if (CheckItemRaceClassDietyRestrictionsOnCast(item_slot) && DoCastingChecksOnCaster(spell_id)) {
|
||||
int16 DeleteChargeFromSlot = GetItemSlotToConsumeCharge(spell_id, item_slot);
|
||||
if (SpellFinished(spell_id, entity_list.GetMob(target_id), CastingSlot::Item, 0, item_slot)) {
|
||||
if (IsClient() && DeleteChargeFromSlot >= 0) {
|
||||
CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16 Mob::GetItemSlotToConsumeCharge(int32 spell_id, uint32 inventory_slot)
|
||||
{
|
||||
int16 DeleteChargeFromSlot = -1;
|
||||
|
||||
if (!IsClient() || inventory_slot == 0xFFFFFFFF) {
|
||||
return DeleteChargeFromSlot;
|
||||
}
|
||||
|
||||
EQ::ItemInstance *item = nullptr;
|
||||
item = CastToClient()->GetInv().GetItem(inventory_slot);
|
||||
|
||||
bool fromaug = false;
|
||||
EQ::ItemData* augitem = nullptr;
|
||||
|
||||
while (true) {
|
||||
if (item == nullptr)
|
||||
break;
|
||||
|
||||
for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) {
|
||||
const EQ::ItemInstance* aug_i = item->GetAugment(r);
|
||||
|
||||
if (!aug_i) {
|
||||
continue;
|
||||
}
|
||||
const EQ::ItemData* aug = aug_i->GetItem();
|
||||
if (!aug) {
|
||||
continue;
|
||||
}
|
||||
if (aug->Click.Effect == spell_id){
|
||||
fromaug = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug){
|
||||
int16 charges = item->GetItem()->MaxCharges;
|
||||
|
||||
if (fromaug) { charges = -1; } //Don't destroy the parent item
|
||||
|
||||
if (charges > -1) { // charged item, expend a charge
|
||||
LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetCharges(), item->GetItem()->MaxCharges);
|
||||
DeleteChargeFromSlot = inventory_slot;
|
||||
}
|
||||
else {
|
||||
LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetItem()->MaxCharges);
|
||||
}
|
||||
}
|
||||
else{
|
||||
LogSpells("Item used to cast spell [{}] was missing from inventory slot [{}] after casting!", spell_id, inventory_slot);
|
||||
Message(Chat::Red, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot);
|
||||
InterruptSpell();
|
||||
return DeleteChargeFromSlot;
|
||||
}
|
||||
return DeleteChargeFromSlot;
|
||||
}
|
||||
|
||||
bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) {
|
||||
|
||||
if (inventory_slot == 0xFFFFFFFF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits.
|
||||
EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(inventory_slot);
|
||||
int bitmask = 1;
|
||||
bitmask = bitmask << (CastToClient()->GetClass() - 1);
|
||||
if (itm && itm->GetItem()->Classes != 65535) {
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that requires equipping but shouldn't let them equip it
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that they don't meet the race/class requirements to cast
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class");
|
||||
}
|
||||
else {
|
||||
if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::RoF)
|
||||
{
|
||||
// Line 181 in eqstr_us.txt was changed in RoF+
|
||||
Message(Chat::Yellow, "Your race, class, or deity cannot use this item.");
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageString(Chat::Red, CANNOT_USE_ITEM);
|
||||
}
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
if (itm && (itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && inventory_slot > EQ::invslot::EQUIPMENT_END) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are attempting to cast a must equip clicky without having it equipped
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user