Skip to content

Commit 77ed1b7

Browse files
committed
feat: Implement vendor interface
1 parent 189cdcb commit 77ed1b7

4 files changed

+186
-65
lines changed

conf/mod-zone-difficulty.conf.dist

+9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ ModZoneDifficulty.Enable = 1
2525

2626
ModZoneDifficulty.DebugInfo = 0
2727

28+
#
29+
# ModZoneDifficulty.UseVendorInterface
30+
# Description: Displays a vendor interface to claim rewards instead of gossips.
31+
# Default: 0 - Disabled
32+
# 1 - Enabled
33+
#
34+
35+
ModZoneDifficulty.UseVendorInterface = 0
36+
2837
#
2938
# ModZoneDifficulty.SpellBuff.OnlyBosses
3039
# Description: Spell damage buffs will only affect bosses.

src/ZoneDifficulty.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ struct ZoneDifficultyHAI
5858
bool TriggeredCast;
5959
};
6060

61+
struct VendorSelectionData
62+
{
63+
uint8 category;
64+
uint8 slot;
65+
};
66+
6167
int32 const DUEL_INDEX = 0x7FFFFFFF;
6268
int32 const DUEL_AREA = 2402; // Forbidding Sea (Wetlands)
6369

@@ -121,13 +127,16 @@ enum ZoneDifficultySettings
121127
TYPE_RAID_T9 = 15,
122128
TYPE_RAID_T10 = 16,
123129

130+
TYPE_MAX_TIERS,
131+
124132
// Completed tiers settings
125133
SETTING_BLACK_TEMPLE = 0
126134
};
127135

128136
enum Misc
129137
{
130-
NPC_ILLIDAN_STORMRAGE = 22917
138+
NPC_ILLIDAN_STORMRAGE = 22917,
139+
NPC_REWARD_CHROMIE = 1128002,
131140
};
132141

133142
class ZoneDifficulty
@@ -157,12 +166,14 @@ class ZoneDifficulty
157166
[[nodiscard]] bool ShouldNerfInDuels(Unit* target);
158167
[[nodiscard]] bool ShouldNerfMap(uint32 mapId) { return NerfInfo.find(mapId) != NerfInfo.end(); };
159168
[[nodiscard]] int32 GetLowestMatchingPhase(uint32 mapId, uint32 phaseMask);
169+
void RewardItem(Player* player, uint8 category, uint8 itemType, uint8 counter, Creature* creature, uint32 itemEntry);
160170

161171
bool IsEnabled{ false };
162172
bool IsDebugInfoEnabled{ false };
163173
float MythicmodeHpModifier{ 2.0 };
164174
bool MythicmodeEnable{ false };
165175
bool MythicmodeInNormalDungeons{ false };
176+
bool UseVendorInterface{ false };
166177
std::vector<uint32> DailyHeroicQuests;
167178
std::map<uint32, uint32> HeroicTBCQuestMapList;
168179
std::map<uint32, uint8> EncounterCounter;
@@ -190,6 +201,8 @@ class ZoneDifficulty
190201
ZoneDifficultyHAIMap MythicmodeAI;
191202
typedef std::map<uint32, std::map<uint32, std::map<uint32, bool> > > ZoneDifficultyEncounterLogMap;
192203
ZoneDifficultyEncounterLogMap Logs;
204+
typedef std::unordered_map<ObjectGuid, VendorSelectionData> ZoneDifficultyVendorSelectionMap;
205+
ZoneDifficultyVendorSelectionMap SelectionCache;
193206
};
194207

195208
#define sZoneDifficulty ZoneDifficulty::instance()

src/mod_zone_difficulty_handler.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -1014,3 +1014,50 @@ bool ZoneDifficulty::HasCompletedFullTier(uint32 category, uint32 playerGuid)
10141014
}
10151015
return true;
10161016
}
1017+
1018+
void ZoneDifficulty::RewardItem(Player* player, uint8 category, uint8 itemType, uint8 counter, Creature* creature, uint32 itemEntry)
1019+
{
1020+
// Check (again) if the player has enough score in the respective category.
1021+
uint32 availableScore = player->GetPlayerSetting(ModZoneDifficultyString + "score", category).value;
1022+
1023+
auto reward = sZoneDifficulty->Rewards[category][itemType][counter];
1024+
1025+
if (itemEntry)
1026+
{
1027+
for (auto const& item : sZoneDifficulty->Rewards[category][itemType])
1028+
{
1029+
if (item.Entry == itemEntry)
1030+
reward = item;
1031+
}
1032+
}
1033+
1034+
if (availableScore < reward.Price)
1035+
{
1036+
if (player->GetSession())
1037+
player->GetSession()->SendAreaTriggerMessage("Not enough points.");
1038+
1039+
return;
1040+
}
1041+
1042+
// Check if the player has the neccesary achievement
1043+
if (reward.Achievement)
1044+
{
1045+
if (!player->HasAchieved(reward.Achievement))
1046+
{
1047+
std::string gossip = "You do not have the required achievement with ID ";
1048+
gossip.append(std::to_string(reward.Achievement));
1049+
gossip.append(" to receive this item. Before i can give it to you, you need to complete the whole dungeon where it can be obtained.");
1050+
creature->Whisper(gossip, LANG_UNIVERSAL, player);
1051+
CloseGossipMenuFor(player);
1052+
return;
1053+
}
1054+
}
1055+
1056+
//LOG_INFO("module", "MOD-ZONE-DIFFICULTY: Sending item with category {}, itemType {}, counter {}", category, itemType, counter);
1057+
sZoneDifficulty->DeductMythicmodeScore(player, category, reward.Price);
1058+
sZoneDifficulty->SendItem(player, category, itemType, counter);
1059+
1060+
if (player->GetSession())
1061+
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(reward.Entry))
1062+
player->GetSession()->SendAreaTriggerMessage("You were rewarded %s for %u points.", proto->Name1.c_str(), reward.Price);
1063+
};

src/mod_zone_difficulty_scripts.cpp

+116-64
Original file line numberDiff line numberDiff line change
@@ -397,46 +397,6 @@ class mod_zone_difficulty_unitscript : public UnitScript
397397
}
398398
};
399399

400-
class mod_zone_difficulty_playerscript : public PlayerScript
401-
{
402-
public:
403-
mod_zone_difficulty_playerscript() : PlayerScript("mod_zone_difficulty_playerscript") { }
404-
405-
void OnMapChanged(Player* player) override
406-
{
407-
uint32 mapId = player->GetMapId();
408-
if (sZoneDifficulty->DisallowedBuffs.find(mapId) != sZoneDifficulty->DisallowedBuffs.end())
409-
{
410-
for (auto aura : sZoneDifficulty->DisallowedBuffs[mapId])
411-
{
412-
player->RemoveAura(aura);
413-
}
414-
}
415-
}
416-
417-
void OnLogin(Player* player) override
418-
{
419-
if (sZoneDifficulty->MythicmodeScore.empty())
420-
return;
421-
422-
if (sZoneDifficulty->MythicmodeScore.find(player->GetGUID().GetCounter()) != sZoneDifficulty->MythicmodeScore.end())
423-
{
424-
for (int i = 1; i <= 16; ++i)
425-
{
426-
uint32 availableScore = 0;
427-
428-
if (sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].find(i) != sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].end())
429-
availableScore = sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()][i];
430-
431-
player->UpdatePlayerSetting(ModZoneDifficultyString + "score", i, availableScore);
432-
}
433-
434-
sZoneDifficulty->MythicmodeScore.erase(player->GetGUID().GetCounter());
435-
CharacterDatabase.Execute("DELETE FROM zone_difficulty_mythicmode_score WHERE GUID = {}", player->GetGUID().GetCounter());
436-
}
437-
}
438-
};
439-
440400
class mod_zone_difficulty_petscript : public PetScript
441401
{
442402
public:
@@ -470,6 +430,7 @@ class mod_zone_difficulty_worldscript : public WorldScript
470430
sZoneDifficulty->MythicmodeHpModifier = sConfigMgr->GetOption<float>("ModZoneDifficulty.Mythicmode.HpModifier", 2);
471431
sZoneDifficulty->MythicmodeEnable = sConfigMgr->GetOption<bool>("ModZoneDifficulty.Mythicmode.Enable", false);
472432
sZoneDifficulty->MythicmodeInNormalDungeons = sConfigMgr->GetOption<bool>("ModZoneDifficulty.Mythicmode.InNormalDungeons", false);
433+
sZoneDifficulty->UseVendorInterface = sConfigMgr->GetOption<bool>("ModZoneDifficulty.UseVendorInterface", false);
473434
sZoneDifficulty->LoadMapDifficultySettings();
474435
}
475436

@@ -749,6 +710,12 @@ class mod_zone_difficulty_rewardnpc : public CreatureScript
749710
}
750711
//LOG_INFO("module", "MOD-ZONE-DIFFICULTY: Building gossip with category {} and counter {}", category, counter);
751712

713+
if (sZoneDifficulty->UseVendorInterface)
714+
{
715+
ShowItemsInFakeVendor(player, creature, category, counter);
716+
return true;
717+
}
718+
752719
for (size_t i = 0; i < sZoneDifficulty->Rewards[category][counter].size(); ++i)
753720
{
754721
//LOG_INFO("module", "MOD-ZONE-DIFFICULTY: Adding gossip option for entry {}", sZoneDifficulty->Rewards[category][counter][i].Entry);
@@ -831,29 +798,7 @@ class mod_zone_difficulty_rewardnpc : public CreatureScript
831798
counter = counter - 100;
832799
}
833800

834-
// Check (again) if the player has enough score in the respective category.
835-
uint32 availableScore = player->GetPlayerSetting(ModZoneDifficultyString + "score", category).value;
836-
837-
if (availableScore < sZoneDifficulty->Rewards[category][itemType][counter].Price)
838-
return true;
839-
840-
// Check if the player has the neccesary achievement
841-
if (sZoneDifficulty->Rewards[category][itemType][counter].Achievement)
842-
{
843-
if (!player->HasAchieved(sZoneDifficulty->Rewards[category][itemType][counter].Achievement))
844-
{
845-
std::string gossip = "You do not have the required achievement with ID ";
846-
gossip.append(std::to_string(sZoneDifficulty->Rewards[category][itemType][counter].Achievement));
847-
gossip.append(" to receive this item. Before i can give it to you, you need to complete the whole dungeon where it can be obtained.");
848-
creature->Whisper(gossip, LANG_UNIVERSAL, player);
849-
CloseGossipMenuFor(player);
850-
return true;
851-
}
852-
}
853-
854-
//LOG_INFO("module", "MOD-ZONE-DIFFICULTY: Sending item with category {}, itemType {}, counter {}", category, itemType, counter);
855-
sZoneDifficulty->DeductMythicmodeScore(player, category, sZoneDifficulty->Rewards[category][itemType][counter].Price);
856-
sZoneDifficulty->SendItem(player, category, itemType, counter);
801+
sZoneDifficulty->RewardItem(player, category, itemType, counter, creature, 0);
857802
}
858803

859804
SendGossipMenuFor(player, npcText, creature);
@@ -878,6 +823,53 @@ class mod_zone_difficulty_rewardnpc : public CreatureScript
878823
SendGossipMenuFor(player, npcText, creature);
879824
return true;
880825
}
826+
827+
static void ShowItemsInFakeVendor(Player* player, Creature* creature, uint8 category, uint8 slot)
828+
{
829+
auto const& itemList = sZoneDifficulty->Rewards[category][slot];
830+
831+
uint32 itemCount = itemList.size();
832+
833+
WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + itemCount * 8 * 4);
834+
data << uint64(creature->GetGUID().GetRawValue());
835+
836+
uint8 count = 0;
837+
size_t count_pos = data.wpos();
838+
data << uint8(count);
839+
840+
for (uint32 i = 0; i < itemCount && count < MAX_VENDOR_ITEMS; ++i)
841+
{;
842+
EncodeItemToPacket(
843+
data, sObjectMgr->GetItemTemplate(itemList[i].Entry), count,
844+
itemList[i].Price);
845+
}
846+
847+
for (uint32 i = 0; i < itemCount && count < MAX_VENDOR_ITEMS; ++i)
848+
{
849+
if (ItemTemplate const* _proto = sObjectMgr->GetItemTemplate(itemList[i].Entry))
850+
EncodeItemToPacket(data, _proto, count, itemList[i].Price);
851+
}
852+
853+
data.put(count_pos, count);
854+
player->GetSession()->SendPacket(&data);
855+
VendorSelectionData vendorData;
856+
vendorData.category = category;
857+
vendorData.slot = slot;
858+
sZoneDifficulty->SelectionCache[player->GetGUID()] = vendorData;
859+
}
860+
861+
static void EncodeItemToPacket(WorldPacket& data, ItemTemplate const* proto, uint8& slot, uint32 price)
862+
{
863+
data << uint32(slot + 1);
864+
data << uint32(proto->ItemId);
865+
data << uint32(proto->DisplayInfoID);
866+
data << int32(-1); //Infinite Stock
867+
data << uint32(price);
868+
data << uint32(proto->MaxDurability);
869+
data << uint32(1); //Buy Count of 1
870+
data << uint32(0);
871+
slot++;
872+
}
881873
};
882874

883875
class mod_zone_difficulty_dungeonmaster : public CreatureScript
@@ -1152,15 +1144,75 @@ class mod_zone_difficulty_allcreaturescript : public AllCreatureScript
11521144
}
11531145
};
11541146

1147+
class mod_zone_difficulty_playerscript : public PlayerScript
1148+
{
1149+
public:
1150+
mod_zone_difficulty_playerscript() : PlayerScript("mod_zone_difficulty_playerscript") { }
1151+
1152+
void OnMapChanged(Player* player) override
1153+
{
1154+
uint32 mapId = player->GetMapId();
1155+
if (sZoneDifficulty->DisallowedBuffs.find(mapId) != sZoneDifficulty->DisallowedBuffs.end())
1156+
{
1157+
for (auto aura : sZoneDifficulty->DisallowedBuffs[mapId])
1158+
{
1159+
player->RemoveAura(aura);
1160+
}
1161+
}
1162+
}
1163+
1164+
void OnLogin(Player* player) override
1165+
{
1166+
if (sZoneDifficulty->MythicmodeScore.empty())
1167+
return;
1168+
1169+
if (sZoneDifficulty->MythicmodeScore.find(player->GetGUID().GetCounter()) != sZoneDifficulty->MythicmodeScore.end())
1170+
{
1171+
for (int i = 1; i <= 16; ++i)
1172+
{
1173+
uint32 availableScore = 0;
1174+
1175+
if (sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].find(i) != sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].end())
1176+
availableScore = sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()][i];
1177+
1178+
player->UpdatePlayerSetting(ModZoneDifficultyString + "score", i, availableScore);
1179+
}
1180+
1181+
sZoneDifficulty->MythicmodeScore.erase(player->GetGUID().GetCounter());
1182+
CharacterDatabase.Execute("DELETE FROM zone_difficulty_mythicmode_score WHERE GUID = {}", player->GetGUID().GetCounter());
1183+
}
1184+
}
1185+
1186+
void OnLogout(Player* player) override
1187+
{
1188+
sZoneDifficulty->SelectionCache.erase(player->GetGUID());
1189+
}
1190+
1191+
void OnBeforeBuyItemFromVendor(Player* player, ObjectGuid vendorguid, uint32 vendorslot, uint32& itemEntry, uint8 /*count*/, uint8 /*bag*/, uint8 /*slot*/) override
1192+
{
1193+
Creature* vendor = player->GetMap()->GetCreature(vendorguid);
1194+
1195+
if (!vendor)
1196+
return;
1197+
if (vendor->GetEntry() != NPC_REWARD_CHROMIE)
1198+
return;
1199+
1200+
auto const& data = sZoneDifficulty->SelectionCache[player->GetGUID()];
1201+
1202+
sZoneDifficulty->RewardItem(player, data.category, data.slot, 0, vendor, itemEntry);
1203+
itemEntry = 0; //Prevents the handler from proceeding to core vendor handling
1204+
}
1205+
};
1206+
11551207
// Add all scripts in one
11561208
void AddModZoneDifficultyScripts()
11571209
{
11581210
new mod_zone_difficulty_unitscript();
1159-
new mod_zone_difficulty_playerscript();
11601211
new mod_zone_difficulty_petscript();
11611212
new mod_zone_difficulty_worldscript();
11621213
new mod_zone_difficulty_globalscript();
11631214
new mod_zone_difficulty_rewardnpc();
11641215
new mod_zone_difficulty_dungeonmaster();
11651216
new mod_zone_difficulty_allcreaturescript();
1217+
new mod_zone_difficulty_playerscript();
11661218
}

0 commit comments

Comments
 (0)