Skip to content

Commit

Permalink
feat: Implement vendor interface
Browse files Browse the repository at this point in the history
Nyeriah committed Sep 23, 2024
1 parent 189cdcb commit 77ed1b7
Showing 4 changed files with 186 additions and 65 deletions.
9 changes: 9 additions & 0 deletions conf/mod-zone-difficulty.conf.dist
Original file line number Diff line number Diff line change
@@ -25,6 +25,15 @@ ModZoneDifficulty.Enable = 1

ModZoneDifficulty.DebugInfo = 0

#
# ModZoneDifficulty.UseVendorInterface
# Description: Displays a vendor interface to claim rewards instead of gossips.
# Default: 0 - Disabled
# 1 - Enabled
#

ModZoneDifficulty.UseVendorInterface = 0

#
# ModZoneDifficulty.SpellBuff.OnlyBosses
# Description: Spell damage buffs will only affect bosses.
15 changes: 14 additions & 1 deletion src/ZoneDifficulty.h
Original file line number Diff line number Diff line change
@@ -58,6 +58,12 @@ struct ZoneDifficultyHAI
bool TriggeredCast;
};

struct VendorSelectionData
{
uint8 category;
uint8 slot;
};

int32 const DUEL_INDEX = 0x7FFFFFFF;
int32 const DUEL_AREA = 2402; // Forbidding Sea (Wetlands)

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

TYPE_MAX_TIERS,

// Completed tiers settings
SETTING_BLACK_TEMPLE = 0
};

enum Misc
{
NPC_ILLIDAN_STORMRAGE = 22917
NPC_ILLIDAN_STORMRAGE = 22917,
NPC_REWARD_CHROMIE = 1128002,
};

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

bool IsEnabled{ false };
bool IsDebugInfoEnabled{ false };
float MythicmodeHpModifier{ 2.0 };
bool MythicmodeEnable{ false };
bool MythicmodeInNormalDungeons{ false };
bool UseVendorInterface{ false };
std::vector<uint32> DailyHeroicQuests;
std::map<uint32, uint32> HeroicTBCQuestMapList;
std::map<uint32, uint8> EncounterCounter;
@@ -190,6 +201,8 @@ class ZoneDifficulty
ZoneDifficultyHAIMap MythicmodeAI;
typedef std::map<uint32, std::map<uint32, std::map<uint32, bool> > > ZoneDifficultyEncounterLogMap;
ZoneDifficultyEncounterLogMap Logs;
typedef std::unordered_map<ObjectGuid, VendorSelectionData> ZoneDifficultyVendorSelectionMap;
ZoneDifficultyVendorSelectionMap SelectionCache;
};

#define sZoneDifficulty ZoneDifficulty::instance()
47 changes: 47 additions & 0 deletions src/mod_zone_difficulty_handler.cpp
Original file line number Diff line number Diff line change
@@ -1014,3 +1014,50 @@ bool ZoneDifficulty::HasCompletedFullTier(uint32 category, uint32 playerGuid)
}
return true;
}

void ZoneDifficulty::RewardItem(Player* player, uint8 category, uint8 itemType, uint8 counter, Creature* creature, uint32 itemEntry)
{
// Check (again) if the player has enough score in the respective category.
uint32 availableScore = player->GetPlayerSetting(ModZoneDifficultyString + "score", category).value;

auto reward = sZoneDifficulty->Rewards[category][itemType][counter];

if (itemEntry)
{
for (auto const& item : sZoneDifficulty->Rewards[category][itemType])
{
if (item.Entry == itemEntry)
reward = item;
}
}

if (availableScore < reward.Price)
{
if (player->GetSession())
player->GetSession()->SendAreaTriggerMessage("Not enough points.");

return;
}

// Check if the player has the neccesary achievement
if (reward.Achievement)
{
if (!player->HasAchieved(reward.Achievement))
{
std::string gossip = "You do not have the required achievement with ID ";
gossip.append(std::to_string(reward.Achievement));
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.");
creature->Whisper(gossip, LANG_UNIVERSAL, player);
CloseGossipMenuFor(player);
return;
}
}

//LOG_INFO("module", "MOD-ZONE-DIFFICULTY: Sending item with category {}, itemType {}, counter {}", category, itemType, counter);
sZoneDifficulty->DeductMythicmodeScore(player, category, reward.Price);
sZoneDifficulty->SendItem(player, category, itemType, counter);

if (player->GetSession())
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(reward.Entry))
player->GetSession()->SendAreaTriggerMessage("You were rewarded %s for %u points.", proto->Name1.c_str(), reward.Price);
};
180 changes: 116 additions & 64 deletions src/mod_zone_difficulty_scripts.cpp
Original file line number Diff line number Diff line change
@@ -397,46 +397,6 @@ class mod_zone_difficulty_unitscript : public UnitScript
}
};

class mod_zone_difficulty_playerscript : public PlayerScript
{
public:
mod_zone_difficulty_playerscript() : PlayerScript("mod_zone_difficulty_playerscript") { }

void OnMapChanged(Player* player) override
{
uint32 mapId = player->GetMapId();
if (sZoneDifficulty->DisallowedBuffs.find(mapId) != sZoneDifficulty->DisallowedBuffs.end())
{
for (auto aura : sZoneDifficulty->DisallowedBuffs[mapId])
{
player->RemoveAura(aura);
}
}
}

void OnLogin(Player* player) override
{
if (sZoneDifficulty->MythicmodeScore.empty())
return;

if (sZoneDifficulty->MythicmodeScore.find(player->GetGUID().GetCounter()) != sZoneDifficulty->MythicmodeScore.end())
{
for (int i = 1; i <= 16; ++i)
{
uint32 availableScore = 0;

if (sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].find(i) != sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].end())
availableScore = sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()][i];

player->UpdatePlayerSetting(ModZoneDifficultyString + "score", i, availableScore);
}

sZoneDifficulty->MythicmodeScore.erase(player->GetGUID().GetCounter());
CharacterDatabase.Execute("DELETE FROM zone_difficulty_mythicmode_score WHERE GUID = {}", player->GetGUID().GetCounter());
}
}
};

class mod_zone_difficulty_petscript : public PetScript
{
public:
@@ -470,6 +430,7 @@ class mod_zone_difficulty_worldscript : public WorldScript
sZoneDifficulty->MythicmodeHpModifier = sConfigMgr->GetOption<float>("ModZoneDifficulty.Mythicmode.HpModifier", 2);
sZoneDifficulty->MythicmodeEnable = sConfigMgr->GetOption<bool>("ModZoneDifficulty.Mythicmode.Enable", false);
sZoneDifficulty->MythicmodeInNormalDungeons = sConfigMgr->GetOption<bool>("ModZoneDifficulty.Mythicmode.InNormalDungeons", false);
sZoneDifficulty->UseVendorInterface = sConfigMgr->GetOption<bool>("ModZoneDifficulty.UseVendorInterface", false);
sZoneDifficulty->LoadMapDifficultySettings();
}

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

if (sZoneDifficulty->UseVendorInterface)
{
ShowItemsInFakeVendor(player, creature, category, counter);
return true;
}

for (size_t i = 0; i < sZoneDifficulty->Rewards[category][counter].size(); ++i)
{
//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
counter = counter - 100;
}

// Check (again) if the player has enough score in the respective category.
uint32 availableScore = player->GetPlayerSetting(ModZoneDifficultyString + "score", category).value;

if (availableScore < sZoneDifficulty->Rewards[category][itemType][counter].Price)
return true;

// Check if the player has the neccesary achievement
if (sZoneDifficulty->Rewards[category][itemType][counter].Achievement)
{
if (!player->HasAchieved(sZoneDifficulty->Rewards[category][itemType][counter].Achievement))
{
std::string gossip = "You do not have the required achievement with ID ";
gossip.append(std::to_string(sZoneDifficulty->Rewards[category][itemType][counter].Achievement));
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.");
creature->Whisper(gossip, LANG_UNIVERSAL, player);
CloseGossipMenuFor(player);
return true;
}
}

//LOG_INFO("module", "MOD-ZONE-DIFFICULTY: Sending item with category {}, itemType {}, counter {}", category, itemType, counter);
sZoneDifficulty->DeductMythicmodeScore(player, category, sZoneDifficulty->Rewards[category][itemType][counter].Price);
sZoneDifficulty->SendItem(player, category, itemType, counter);
sZoneDifficulty->RewardItem(player, category, itemType, counter, creature, 0);
}

SendGossipMenuFor(player, npcText, creature);
@@ -878,6 +823,53 @@ class mod_zone_difficulty_rewardnpc : public CreatureScript
SendGossipMenuFor(player, npcText, creature);
return true;
}

static void ShowItemsInFakeVendor(Player* player, Creature* creature, uint8 category, uint8 slot)
{
auto const& itemList = sZoneDifficulty->Rewards[category][slot];

uint32 itemCount = itemList.size();

WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + itemCount * 8 * 4);
data << uint64(creature->GetGUID().GetRawValue());

uint8 count = 0;
size_t count_pos = data.wpos();
data << uint8(count);

for (uint32 i = 0; i < itemCount && count < MAX_VENDOR_ITEMS; ++i)
{;
EncodeItemToPacket(
data, sObjectMgr->GetItemTemplate(itemList[i].Entry), count,
itemList[i].Price);
}

for (uint32 i = 0; i < itemCount && count < MAX_VENDOR_ITEMS; ++i)
{
if (ItemTemplate const* _proto = sObjectMgr->GetItemTemplate(itemList[i].Entry))
EncodeItemToPacket(data, _proto, count, itemList[i].Price);
}

data.put(count_pos, count);
player->GetSession()->SendPacket(&data);
VendorSelectionData vendorData;
vendorData.category = category;
vendorData.slot = slot;
sZoneDifficulty->SelectionCache[player->GetGUID()] = vendorData;
}

static void EncodeItemToPacket(WorldPacket& data, ItemTemplate const* proto, uint8& slot, uint32 price)
{
data << uint32(slot + 1);
data << uint32(proto->ItemId);
data << uint32(proto->DisplayInfoID);
data << int32(-1); //Infinite Stock
data << uint32(price);
data << uint32(proto->MaxDurability);
data << uint32(1); //Buy Count of 1
data << uint32(0);
slot++;
}
};

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

class mod_zone_difficulty_playerscript : public PlayerScript
{
public:
mod_zone_difficulty_playerscript() : PlayerScript("mod_zone_difficulty_playerscript") { }

void OnMapChanged(Player* player) override
{
uint32 mapId = player->GetMapId();
if (sZoneDifficulty->DisallowedBuffs.find(mapId) != sZoneDifficulty->DisallowedBuffs.end())
{
for (auto aura : sZoneDifficulty->DisallowedBuffs[mapId])
{
player->RemoveAura(aura);
}
}
}

void OnLogin(Player* player) override
{
if (sZoneDifficulty->MythicmodeScore.empty())
return;

if (sZoneDifficulty->MythicmodeScore.find(player->GetGUID().GetCounter()) != sZoneDifficulty->MythicmodeScore.end())
{
for (int i = 1; i <= 16; ++i)
{
uint32 availableScore = 0;

if (sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].find(i) != sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()].end())
availableScore = sZoneDifficulty->MythicmodeScore[player->GetGUID().GetCounter()][i];

player->UpdatePlayerSetting(ModZoneDifficultyString + "score", i, availableScore);
}

sZoneDifficulty->MythicmodeScore.erase(player->GetGUID().GetCounter());
CharacterDatabase.Execute("DELETE FROM zone_difficulty_mythicmode_score WHERE GUID = {}", player->GetGUID().GetCounter());
}
}

void OnLogout(Player* player) override
{
sZoneDifficulty->SelectionCache.erase(player->GetGUID());
}

void OnBeforeBuyItemFromVendor(Player* player, ObjectGuid vendorguid, uint32 vendorslot, uint32& itemEntry, uint8 /*count*/, uint8 /*bag*/, uint8 /*slot*/) override
{
Creature* vendor = player->GetMap()->GetCreature(vendorguid);

if (!vendor)
return;
if (vendor->GetEntry() != NPC_REWARD_CHROMIE)
return;

auto const& data = sZoneDifficulty->SelectionCache[player->GetGUID()];

sZoneDifficulty->RewardItem(player, data.category, data.slot, 0, vendor, itemEntry);
itemEntry = 0; //Prevents the handler from proceeding to core vendor handling
}
};

// Add all scripts in one
void AddModZoneDifficultyScripts()
{
new mod_zone_difficulty_unitscript();
new mod_zone_difficulty_playerscript();
new mod_zone_difficulty_petscript();
new mod_zone_difficulty_worldscript();
new mod_zone_difficulty_globalscript();
new mod_zone_difficulty_rewardnpc();
new mod_zone_difficulty_dungeonmaster();
new mod_zone_difficulty_allcreaturescript();
new mod_zone_difficulty_playerscript();
}

0 comments on commit 77ed1b7

Please sign in to comment.