Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Core/Spells: Fixed Soulwell charges removal on use. #7839

Merged
merged 1 commit into from

3 participants

Warpten Gildor kandera
Warpten
Collaborator

What's the bug

  • Warlock's soulwell should not lose charges if you cannot store a stone in your inventory, either because you don't have free space or because you already have a stone.
  • Soulwell shouldn't lose two charges per use.

What does the fix fix

  • Fixes charges loss

What does the fix bug out

  • No matter what, you will always get min. one, max. two SpellCastResult error strings. The reason for this is that the Soulwell sends CMSG_REPORT_USE and CMSG_USE. The two packet handlers related to these call GameObjectAI::GossipHello. So we have a race between the GossipHello hook called by REPORT_USE and the one called by USE.

Case 1:
a) Player has no stone.
b) Player clicks on the well.
c) First call of the hook will add the stone, second one will send an error string (SpellCastResult)
d) The well loses 1 charge.
You now have one stone, and one error string.

Case 2:
a) Player has a stone.
b) Player clicks on the well.
c) First call of the hook will print the error string, second one will do the same.
d) Charges are not affected.
You now have one stone, and two error strings.

Why using the boolean returned by GossipHello ?

It was designed for that, and it'll prevent, in the case of the soulwell, GameObject::Use from removing a charge itself later on (See case GAMEOBJECT_TYPE_SPELLCASTER).

What is the correct fix ?

We need to somewhat have access to the SpellCastResult in Unit::CastSpell in order to determine if charges are going to be effectively removed. I can address that in another patch if i get a "GO" message from any dev.

Closes #7397.

kandera kandera merged commit 42c047b into from
Gildor

The fix it's not working properly #8325

Eilo eilo referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
3  src/server/game/Entities/GameObject/GameObject.cpp
View
@@ -1080,7 +1080,8 @@ void GameObject::Use(Unit* user)
if (sScriptMgr->OnGossipHello(playerUser, this))
return;
- AI()->GossipHello(playerUser);
+ if (AI()->GossipHello(playerUser))
+ return;
}
// If cooldown data present in template
3  src/server/game/Handlers/SpellHandler.cpp
View
@@ -303,7 +303,8 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;
- go->AI()->GossipHello(_player);
+ if (go->AI()->GossipHello(_player))
+ return;
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
}
3  src/server/game/Spells/SpellEffects.cpp
View
@@ -2001,7 +2001,8 @@ void Spell::SendLoot(uint64 guid, LootType loottype)
if (sScriptMgr->OnGossipHello(player, gameObjTarget))
return;
- gameObjTarget->AI()->GossipHello(player);
+ if (gameObjTarget->AI()->GossipHello(player))
+ return;
switch (gameObjTarget->GetGoType())
{
166 src/server/scripts/World/go_scripts.cpp
View
@@ -58,7 +58,7 @@ EndContentData */
## go_cat_figurine
######*/
-enum eCatFigurine
+enum CatFigurine
{
SPELL_SUMMON_GHOST_SABER = 5968,
};
@@ -178,7 +178,7 @@ class go_field_repair_bot_74A : public GameObjectScript
## go_gilded_brazier (Paladin First Trail quest (9678))
######*/
-enum eGildedBrazier
+enum GildedBrazier
{
NPC_STILLBLADE = 17716,
};
@@ -282,7 +282,7 @@ class go_jump_a_tron : public GameObjectScript
## go_ethereum_prison
######*/
-enum eEthereumPrison
+enum EthereumPrison
{
SPELL_REP_LC = 39456,
SPELL_REP_SHAT = 39457,
@@ -367,7 +367,7 @@ class go_ethereum_stasis : public GameObjectScript
## go_resonite_cask
######*/
-enum eResoniteCask
+enum ResoniteCask
{
NPC_GOGGEROC = 11920
};
@@ -410,7 +410,7 @@ class go_sacred_fire_of_life : public GameObjectScript
## go_shrine_of_the_birds
######*/
-enum eShrineOfTheBirds
+enum ShrineOfTheBirds
{
NPC_HAWK_GUARD = 22992,
NPC_EAGLE_GUARD = 22993,
@@ -456,7 +456,7 @@ class go_shrine_of_the_birds : public GameObjectScript
## go_southfury_moonstone
######*/
-enum eSouthfury
+enum Southfury
{
NPC_RIZZLE = 23002,
SPELL_BLACKJACK = 39865, //stuns player
@@ -484,7 +484,7 @@ class go_southfury_moonstone : public GameObjectScript
## go_tele_to_dalaran_crystal
######*/
-enum eDalaranCrystal
+enum DalaranCrystal
{
QUEST_LEARN_LEAVE_RETURN = 12790,
QUEST_TELE_CRYSTAL_FLAG = 12845
@@ -536,7 +536,7 @@ class go_tele_to_violet_stand : public GameObjectScript
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_5 "Purchase 5 Unstable Flask of the Beast for the cost of 50 Apexis Shards"
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_RETURN "Use the fel crystalforge to make another purchase."
-enum eFelCrystalforge
+enum FelCrystalforge
{
SPELL_CREATE_1_FLASK_OF_BEAST = 40964,
SPELL_CREATE_5_FLASK_OF_BEAST = 40965,
@@ -595,7 +595,7 @@ class go_fel_crystalforge : public GameObjectScript
#define GOSSIP_BASHIR_CRYSTALFORGE_ITEM_5 "Purchase 5 Unstable Flask of the Sorcerer for the cost of 50 Apexis Shards"
#define GOSSIP_BASHIR_CRYSTALFORGE_ITEM_RETURN "Use the bashir crystalforge to make another purchase."
-enum eBashirCrystalforge
+enum BashirCrystalforge
{
SPELL_CREATE_1_FLASK_OF_SORCERER = 40968,
SPELL_CREATE_5_FLASK_OF_SORCERER = 40970,
@@ -648,7 +648,7 @@ class go_bashir_crystalforge : public GameObjectScript
## matrix_punchograph
######*/
-enum eMatrixPunchograph
+enum MatrixPunchograph
{
ITEM_WHITE_PUNCH_CARD = 9279,
ITEM_YELLOW_PUNCH_CARD = 9280,
@@ -713,7 +713,7 @@ class go_matrix_punchograph : public GameObjectScript
## go_scourge_cage
######*/
-enum eScourgeCage
+enum ScourgeCage
{
NPC_SCOURGE_PRISONER = 25610
};
@@ -740,7 +740,7 @@ class go_scourge_cage : public GameObjectScript
## go_arcane_prison
######*/
-enum eArcanePrison
+enum ArcanePrison
{
QUEST_PRISON_BREAK = 11587,
SPELL_ARCANE_PRISONER_KILL_CREDIT = 45456
@@ -787,7 +787,7 @@ class go_blood_filled_orb : public GameObjectScript
## go_jotunheim_cage
######*/
-enum eJotunheimCage
+enum JotunheimCage
{
NPC_EBON_BLADE_PRISONER_HUMAN = 30186,
NPC_EBON_BLADE_PRISONER_NE = 30194,
@@ -842,7 +842,7 @@ class go_jotunheim_cage : public GameObjectScript
}
};
-enum eTableTheka
+enum TableTheka
{
GOSSIP_TABLE_THEKA = 1653,
@@ -869,7 +869,7 @@ class go_table_theka : public GameObjectScript
## go_inconspicuous_landmark
######*/
-enum eInconspicuousLandmark
+enum InconspicuousLandmark
{
SPELL_SUMMON_PIRATES_TREASURE_AND_TRIGGER_MOB = 11462,
ITEM_CUERGOS_KEY = 9275,
@@ -895,7 +895,7 @@ class go_inconspicuous_landmark : public GameObjectScript
## go_ethereal_teleport_pad
######*/
-enum eEtherealTeleportPad
+enum EtherealTeleportPad
{
NPC_IMAGE_WIND_TRADER = 20518,
ITEM_TELEPORTER_POWER_PACK = 28969,
@@ -921,45 +921,105 @@ class go_ethereal_teleport_pad : public GameObjectScript
## go_soulwell
######*/
-class go_soulwell : public GameObjectScript
+enum SoulWellData
{
-public:
- go_soulwell() : GameObjectScript("go_soulwell") { }
+ GO_SOUL_WELL_R1 = 181621,
+ GO_SOUL_WELL_R2 = 193169,
- bool OnGossipHello(Player* player, GameObject* go)
- {
- Unit* caster = go->GetOwner();
- if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
- return true;
+ SPELL_IMPROVED_HEALTH_STONE_R1 = 18692,
+ SPELL_IMPROVED_HEALTH_STONE_R2 = 18693,
- if (!player->IsInSameRaidWith(static_cast<Player*>(caster)))
- return true;
+ SPELL_CREATE_MASTER_HEALTH_STONE_R0 = 34130,
+ SPELL_CREATE_MASTER_HEALTH_STONE_R1 = 34149,
+ SPELL_CREATE_MASTER_HEALTH_STONE_R2 = 34150,
+
+ SPELL_CREATE_FEL_HEALTH_STONE_R0 = 58890,
+ SPELL_CREATE_FEL_HEALTH_STONE_R1 = 58896,
+ SPELL_CREATE_FEL_HEALTH_STONE_R2 = 58898,
+};
- // Repeating this at every use is ugly and inefficient. But as long as we don't have proper
- // GO scripting with at least On Create and On Update events, the other options are no less
- // ugly and hacky.
- uint32 newSpell = 0;
- if (go->GetEntry() == 193169) // Soulwell for rank 2
+class go_soulwell : public GameObjectScript
+{
+ public:
+ go_soulwell() : GameObjectScript("go_soulwell") {}
+
+ struct go_soulwellAI : public GameObjectAI
{
- if (caster->HasAura(18693)) // Improved Healthstone rank 2
- newSpell = 58898;
- else if (caster->HasAura(18692)) // Improved Healthstone rank 1
- newSpell = 58896;
- else newSpell = 58890;
- }
- else if (go->GetEntry() == 181621) // Soulwell for rank 1
+ go_soulwellAI(GameObject* go) : GameObjectAI(go)
+ {
+ _stoneSpell = 0;
+ _stoneId = 0;
+ switch (go->GetEntry())
+ {
+ case GO_SOUL_WELL_R1:
+ _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R0;
+ if (Unit* owner = go->GetOwner())
+ {
+ if (owner->HasAura(SPELL_IMPROVED_HEALTH_STONE_R1))
+ _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R1;
+ else if (owner->HasAura(SPELL_CREATE_MASTER_HEALTH_STONE_R2))
+ _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R2;
+ }
+ break;
+ case GO_SOUL_WELL_R2:
+ _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R0;
+ if (Unit* owner = go->GetOwner())
+ {
+ if (owner->HasAura(SPELL_IMPROVED_HEALTH_STONE_R1))
+ _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R1;
+ else if (owner->HasAura(SPELL_CREATE_MASTER_HEALTH_STONE_R2))
+ _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R2;
+ }
+ break;
+ }
+ if (_stoneSpell == 0) // Should never happen
+ return;
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_stoneSpell);
+ if (!spellInfo)
+ return;
+
+ _stoneId = spellInfo->Effects[EFFECT_0].ItemType;
+ }
+
+ /// Due to the fact that this GameObject triggers CMSG_GAMEOBJECT_USE
+ /// _and_ CMSG_GAMEOBJECT_REPORT_USE, this GossipHello hook is called
+ /// twice. The script's handling is fine as it won't remove two charges
+ /// on the well. We have to find how to segregate REPORT_USE and USE.
+ bool GossipHello(Player* player)
+ {
+ Unit* owner = go->GetOwner();
+ if (_stoneSpell == 0 || _stoneId == 0)
+ return true;
+
+ if (!owner || owner->GetTypeId() != TYPEID_PLAYER || !player->IsInSameRaidWith(owner->ToPlayer()))
+ return true;
+
+ // Don't try to add a stone if we already have one.
+ if (player->HasItemCount(_stoneId, 1))
+ {
+ if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(_stoneSpell))
+ Spell::SendCastResult(player, spell, 0, SPELL_FAILED_TOO_MANY_OF_ITEM);
+ return true;
+ }
+
+ owner->CastSpell(player, _stoneSpell, true);
+ // Item has to actually be created to remove a charge on the well.
+ if (player->HasItemCount(_stoneId, 1))
+ go->AddUse();
+
+ return false;
+ }
+
+ private:
+ uint32 _stoneSpell;
+ uint32 _stoneId;
+ };
+
+ GameObjectAI* GetAI(GameObject* go) const
{
- if (caster->HasAura(18693)) // Improved Healthstone rank 2
- newSpell = 34150;
- else if (caster->HasAura(18692)) // Improved Healthstone rank 1
- newSpell = 34149;
- else newSpell = 34130;
+ return new go_soulwellAI(go);
}
-
- go->AddUse();
- player->CastSpell(player, newSpell, true);
- return true;
- }
};
/*######
@@ -967,7 +1027,7 @@ class go_soulwell : public GameObjectScript
## go_dragonflayer_cage
######*/
-enum ePrisonersOfWyrmskull
+enum PrisonersOfWyrmskull
{
QUEST_PRISONERS_OF_WYRMSKULL = 11255,
NPC_PRISONER_PRIEST = 24086,
@@ -1017,7 +1077,7 @@ class go_dragonflayer_cage : public GameObjectScript
## go_tadpole_cage
######*/
-enum eTadpoles
+enum Tadpoles
{
QUEST_OH_NOES_THE_TADPOLES = 11560,
NPC_WINTERFIN_TADPOLE = 25201
@@ -1052,7 +1112,7 @@ class go_tadpole_cage : public GameObjectScript
#define GOSSIP_USE_OUTHOUSE "Use the outhouse."
#define GO_ANDERHOLS_SLIDER_CIDER_NOT_FOUND "Quest item Anderhol's Slider Cider not found."
-enum eAmberpineOuthouse
+enum AmberpineOuthouse
{
ITEM_ANDERHOLS_SLIDER_CIDER = 37247,
NPC_OUTHOUSE_BUNNY = 27326,
@@ -1114,7 +1174,7 @@ class go_amberpine_outhouse : public GameObjectScript
## go_hive_pod
######*/
-enum eHives
+enum Hives
{
QUEST_HIVE_IN_THE_TOWER = 9544,
NPC_HIVE_AMBUSHER = 13301
@@ -1281,7 +1341,7 @@ class go_frostblade_shrine : public GameObjectScript
## go_midsummer_bonfire
######*/
-enum eMidsummerBonfire
+enum MidsummerBonfire
{
STAMP_OUT_BONFIRE_QUEST_COMPLETE = 45458,
};
Something went wrong with that request. Please try again.