mirror of
https://github.com/ornfelt/azerothcore_lua_scripts
synced 2026-08-22 06:26:03 -04:00
951 lines
39 KiB
Diff
951 lines
39 KiB
Diff
diff --git a/sql/custom/characters/2017_05_03_00_characters_reforging.sql b/sql/custom/characters/2017_05_03_00_characters_reforging.sql
|
|
new file mode 100644
|
|
index 000000000000..8b51859c1e2a
|
|
--- /dev/null
|
|
+++ b/sql/custom/characters/2017_05_03_00_characters_reforging.sql
|
|
@@ -0,0 +1,10 @@
|
|
+CREATE TABLE IF NOT EXISTS `custom_reforging` (
|
|
+ `GUID` INT(10) UNSIGNED NOT NULL COMMENT 'item guid low',
|
|
+ `increase` INT(10) UNSIGNED NOT NULL COMMENT 'stat_type',
|
|
+ `decrease` INT(10) UNSIGNED NOT NULL COMMENT 'stat_type',
|
|
+ `stat_value` INT(10) NOT NULL DEFAULT '0' COMMENT 'stat change',
|
|
+ `Owner` INT(10) UNSIGNED NULL DEFAULT NULL COMMENT 'player guid',
|
|
+ PRIMARY KEY (`GUID`)
|
|
+)
|
|
+COLLATE='utf8_general_ci'
|
|
+ENGINE=InnoDB;
|
|
diff --git a/src/server/game/Entities/Item/Reforging.cpp b/src/server/game/Entities/Item/Reforging.cpp
|
|
new file mode 100644
|
|
index 000000000000..2b05cb07a06a
|
|
--- /dev/null
|
|
+++ b/src/server/game/Entities/Item/Reforging.cpp
|
|
@@ -0,0 +1,244 @@
|
|
+#include "Reforging.h"
|
|
+
|
|
+#include "Define.h"
|
|
+#include "Player.h"
|
|
+#include "WorldSession.h"
|
|
+#include "Item.h"
|
|
+#include "ItemTemplate.h"
|
|
+#include "Bag.h"
|
|
+#include "ObjectGuid.h"
|
|
+#include "WorldPacket.h"
|
|
+#include "Opcodes.h"
|
|
+#include "ObjectMgr.h"
|
|
+#include "SpellInfo.h"
|
|
+#include "SpellMgr.h"
|
|
+#include <vector>
|
|
+#include <string>
|
|
+
|
|
+static const bool send_cache_packets = true; // change player cache?
|
|
+
|
|
+std::vector<Item*> GetItemList(const Player* player)
|
|
+{
|
|
+ std::vector<Item*> itemlist;
|
|
+
|
|
+ // Copy paste from Player::GetItemByGuid(guid)
|
|
+
|
|
+ for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
|
+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
|
+ itemlist.push_back(pItem);
|
|
+
|
|
+ for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
|
|
+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
|
+ itemlist.push_back(pItem);
|
|
+
|
|
+ for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i)
|
|
+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
|
+ itemlist.push_back(pItem);
|
|
+
|
|
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
|
|
+ if (Bag* pBag = player->GetBagByPos(i))
|
|
+ for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
|
+ if (Item* pItem = pBag->GetItemByPos(j))
|
|
+ itemlist.push_back(pItem);
|
|
+
|
|
+ for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
|
|
+ if (Bag* pBag = player->GetBagByPos(i))
|
|
+ for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
|
+ if (Item* pItem = pBag->GetItemByPos(j))
|
|
+ itemlist.push_back(pItem);
|
|
+
|
|
+ return itemlist;
|
|
+}
|
|
+
|
|
+void RemoveReforge(Player* player, uint32 itemguid, bool update)
|
|
+{
|
|
+ if (!itemguid || player->reforgeMap.empty() ||
|
|
+ player->reforgeMap.find(itemguid) == player->reforgeMap.end())
|
|
+ return;
|
|
+
|
|
+ Item* invItem = update ? player->GetItemByGuid(ObjectGuid(HighGuid::Item, 0, itemguid)) : NULL;
|
|
+ if (invItem && invItem->IsEquipped())
|
|
+ player->_ApplyItemMods(invItem, invItem->GetSlot(), false);
|
|
+ player->reforgeMap.erase(itemguid);
|
|
+ if (invItem && invItem->IsEquipped())
|
|
+ player->_ApplyItemMods(invItem, invItem->GetSlot(), true);
|
|
+ if (invItem)
|
|
+ SendReforgePacket(player, invItem->GetEntry());
|
|
+}
|
|
+
|
|
+void SendReforgePackets(Player* player)
|
|
+{
|
|
+ if (!send_cache_packets)
|
|
+ return;
|
|
+
|
|
+ std::vector<Item*> items = GetItemList(player);
|
|
+ for (std::vector<Item*>::const_iterator it = items.begin(); it != items.end(); ++it)
|
|
+ SendReforgePacket(player, (*it)->GetEntry(), (*it)->GetGUID().GetCounter());
|
|
+}
|
|
+
|
|
+// Supply lowguid or reforge! (or both)
|
|
+// Warning, this function may modify player->reforgeMap when lowguid is supplied
|
|
+void SendReforgePacket(Player* player, uint32 entry, uint32 lowguid, const ReforgeData* reforge)
|
|
+{
|
|
+ ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(entry);
|
|
+ if (!pProto)
|
|
+ return;
|
|
+
|
|
+ if (lowguid)
|
|
+ {
|
|
+ if (!player->reforgeMap.empty() && player->reforgeMap.find(lowguid) != player->reforgeMap.end())
|
|
+ reforge = &player->reforgeMap[lowguid];
|
|
+ else
|
|
+ RemoveReforge(player, lowguid, true);
|
|
+ }
|
|
+
|
|
+ // Update player cache (self only) pure visual.
|
|
+ // HandleItemQuerySingleOpcode copy paste
|
|
+ std::string Name = pProto->Name1;
|
|
+ std::string Description = pProto->Description;
|
|
+ LocaleConstant loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
|
|
+ if (ItemLocale const* il = sObjectMgr->GetItemLocale(pProto->ItemId))
|
|
+ {
|
|
+ ObjectMgr::GetLocaleString(il->Name, loc_idx, Name);
|
|
+ ObjectMgr::GetLocaleString(il->Description, loc_idx, Description);
|
|
+ }
|
|
+ WorldPacket data(SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
|
|
+ data << pProto->ItemId;
|
|
+ data << pProto->Class;
|
|
+ data << pProto->SubClass;
|
|
+ data << pProto->SoundOverrideSubclass;
|
|
+ data << Name;
|
|
+ data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
|
|
+ data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
|
|
+ data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
|
|
+ data << pProto->DisplayInfoID;
|
|
+ data << pProto->Quality;
|
|
+ data << pProto->Flags;
|
|
+ data << pProto->Flags2;
|
|
+ data << pProto->BuyPrice;
|
|
+ data << pProto->SellPrice;
|
|
+ data << pProto->InventoryType;
|
|
+ data << pProto->AllowableClass;
|
|
+ data << pProto->AllowableRace;
|
|
+ data << pProto->ItemLevel;
|
|
+ data << pProto->RequiredLevel;
|
|
+ data << pProto->RequiredSkill;
|
|
+ data << pProto->RequiredSkillRank;
|
|
+ data << pProto->RequiredSpell;
|
|
+ data << pProto->RequiredHonorRank;
|
|
+ data << pProto->RequiredCityRank;
|
|
+ data << pProto->RequiredReputationFaction;
|
|
+ data << pProto->RequiredReputationRank;
|
|
+ data << int32(pProto->MaxCount);
|
|
+ data << int32(pProto->Stackable);
|
|
+ data << pProto->ContainerSlots;
|
|
+ data << pProto->StatsCount + (reforge ? 1 : 0); // increase stat count by 1
|
|
+ bool decreased = false;
|
|
+ for (uint32 i = 0; i < pProto->StatsCount; ++i)
|
|
+ {
|
|
+ data << pProto->ItemStat[i].ItemStatType;
|
|
+ if (reforge && !decreased && pProto->ItemStat[i].ItemStatType == reforge->decrease)
|
|
+ {
|
|
+ data << pProto->ItemStat[i].ItemStatValue - reforge->stat_value;
|
|
+ decreased = true;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ data << pProto->ItemStat[i].ItemStatValue;
|
|
+ }
|
|
+ }
|
|
+ if (reforge)
|
|
+ {
|
|
+ data << reforge->increase; // set new stat type
|
|
+ data << reforge->stat_value; // and value
|
|
+ }
|
|
+ data << pProto->ScalingStatDistribution; // scaling stats distribution
|
|
+ data << pProto->ScalingStatValue; // some kind of flags used to determine stat values column
|
|
+ for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
|
|
+ {
|
|
+ data << pProto->Damage[i].DamageMin;
|
|
+ data << pProto->Damage[i].DamageMax;
|
|
+ data << pProto->Damage[i].DamageType;
|
|
+ }
|
|
+
|
|
+ // resistances (7)
|
|
+ data << pProto->Armor;
|
|
+ data << pProto->HolyRes;
|
|
+ data << pProto->FireRes;
|
|
+ data << pProto->NatureRes;
|
|
+ data << pProto->FrostRes;
|
|
+ data << pProto->ShadowRes;
|
|
+ data << pProto->ArcaneRes;
|
|
+
|
|
+ data << pProto->Delay;
|
|
+ data << pProto->AmmoType;
|
|
+ data << pProto->RangedModRange;
|
|
+
|
|
+ for (int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s)
|
|
+ {
|
|
+ // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
|
|
+ // use `item_template` or if not set then only use spell cooldowns
|
|
+ SpellInfo const* spell = sSpellMgr->GetSpellInfo(pProto->Spells[s].SpellId);
|
|
+ if (spell)
|
|
+ {
|
|
+ bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0;
|
|
+
|
|
+ data << pProto->Spells[s].SpellId;
|
|
+ data << pProto->Spells[s].SpellTrigger;
|
|
+ data << uint32(-abs(pProto->Spells[s].SpellCharges));
|
|
+
|
|
+ if (db_data)
|
|
+ {
|
|
+ data << uint32(pProto->Spells[s].SpellCooldown);
|
|
+ data << uint32(pProto->Spells[s].SpellCategory);
|
|
+ data << uint32(pProto->Spells[s].SpellCategoryCooldown);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ data << uint32(spell->RecoveryTime);
|
|
+ data << uint32(spell->GetCategory());
|
|
+ data << uint32(spell->CategoryRecoveryTime);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ data << uint32(0);
|
|
+ data << uint32(0);
|
|
+ data << uint32(0);
|
|
+ data << uint32(-1);
|
|
+ data << uint32(0);
|
|
+ data << uint32(-1);
|
|
+ }
|
|
+ }
|
|
+ data << pProto->Bonding;
|
|
+ data << Description;
|
|
+ data << pProto->PageText;
|
|
+ data << pProto->LanguageID;
|
|
+ data << pProto->PageMaterial;
|
|
+ data << pProto->StartQuest;
|
|
+ data << pProto->LockID;
|
|
+ data << int32(pProto->Material);
|
|
+ data << pProto->Sheath;
|
|
+ data << pProto->RandomProperty;
|
|
+ data << pProto->RandomSuffix;
|
|
+ data << pProto->Block;
|
|
+ data << pProto->ItemSet;
|
|
+ data << pProto->MaxDurability;
|
|
+ data << pProto->Area;
|
|
+ data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch
|
|
+ data << pProto->BagFamily;
|
|
+ data << pProto->TotemCategory;
|
|
+ for (int s = 0; s < MAX_ITEM_PROTO_SOCKETS; ++s)
|
|
+ {
|
|
+ data << pProto->Socket[s].Color;
|
|
+ data << pProto->Socket[s].Content;
|
|
+ }
|
|
+ data << pProto->socketBonus;
|
|
+ data << pProto->GemProperties;
|
|
+ data << pProto->RequiredDisenchantSkill;
|
|
+ data << pProto->ArmorDamageModifier;
|
|
+ data << pProto->Duration; // added in 2.4.2.8209, duration (seconds)
|
|
+ data << pProto->ItemLimitCategory; // WotLK, ItemLimitCategory
|
|
+ data << pProto->HolidayId; // Holiday.dbc?
|
|
+ player->GetSession()->SendPacket(&data);
|
|
+}
|
|
diff --git a/src/server/game/Entities/Item/Reforging.h b/src/server/game/Entities/Item/Reforging.h
|
|
new file mode 100644
|
|
index 000000000000..7fbec60084ce
|
|
--- /dev/null
|
|
+++ b/src/server/game/Entities/Item/Reforging.h
|
|
@@ -0,0 +1,18 @@
|
|
+#ifndef REFORGING_H
|
|
+#define REFORGING_H
|
|
+
|
|
+#include "Define.h"
|
|
+#include <vector>
|
|
+
|
|
+class Player;
|
|
+class Item;
|
|
+struct ReforgeData;
|
|
+
|
|
+// Supply lowguid or reforge! (or both)
|
|
+// Warning, this function may modify player->reforgeMap when lowguid is supplied
|
|
+void TC_GAME_API SendReforgePacket(Player* player, uint32 entry, uint32 lowguid = 0, const ReforgeData* reforge = NULL);
|
|
+void TC_GAME_API SendReforgePackets(Player* player);
|
|
+void TC_GAME_API RemoveReforge(Player* player, uint32 itemguid, bool update);
|
|
+std::vector<Item*> TC_GAME_API GetItemList(const Player* player);
|
|
+
|
|
+#endif
|
|
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
|
|
index 8c3fcafaf83a..5893e8364f9f 100644
|
|
--- a/src/server/game/Entities/Player/Player.cpp
|
|
+++ b/src/server/game/Entities/Player/Player.cpp
|
|
@@ -7346,6 +7346,20 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
|
|
if (only_level_scale && (!ssd || !ssv))
|
|
return;
|
|
|
|
+ uint32 statcount = proto->StatsCount;
|
|
+ ReforgeData* reforgeData = NULL;
|
|
+ bool decreased = false;
|
|
+ if (statcount < MAX_ITEM_PROTO_STATS)
|
|
+ {
|
|
+ if (Item* invItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
|
+ {
|
|
+ if (reforgeMap.find(invItem->GetGUID().GetCounter()) != reforgeMap.end())
|
|
+ {
|
|
+ reforgeData = &reforgeMap[invItem->GetGUID().GetCounter()];
|
|
+ ++statcount;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
|
|
{
|
|
uint32 statType = 0;
|
|
@@ -7360,10 +7374,24 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
|
|
}
|
|
else
|
|
{
|
|
- if (i >= proto->StatsCount)
|
|
+ if (i >= statcount)
|
|
continue;
|
|
statType = proto->ItemStat[i].ItemStatType;
|
|
val = proto->ItemStat[i].ItemStatValue;
|
|
+
|
|
+ if (reforgeData)
|
|
+ {
|
|
+ if(i == statcount-1)
|
|
+ {
|
|
+ statType = reforgeData->increase;
|
|
+ val = reforgeData->stat_value;
|
|
+ }
|
|
+ else if (!decreased && reforgeData->decrease == statType)
|
|
+ {
|
|
+ val -= reforgeData->stat_value;
|
|
+ decreased = true;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
if (val == 0)
|
|
@@ -12369,12 +12397,14 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update)
|
|
}
|
|
}
|
|
|
|
+extern void RemoveReforge(Player* player, uint32 itemguid, bool update);
|
|
// Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
|
|
void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
|
|
{
|
|
if (Item* it = GetItemByPos(bag, slot))
|
|
{
|
|
RemoveItem(bag, slot, update);
|
|
+ RemoveReforge(this, it->GetGUID().GetCounter(), true);
|
|
ItemRemovedQuestCheck(it->GetEntry(), it->GetCount());
|
|
it->SetNotRefundable(this, false);
|
|
RemoveItemFromUpdateQueueOf(it, this);
|
|
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
|
|
index b5ddaca836c5..47bd6c444dcb 100644
|
|
--- a/src/server/game/Entities/Player/Player.h
|
|
+++ b/src/server/game/Entities/Player/Player.h
|
|
@@ -155,6 +155,13 @@ typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap;
|
|
typedef std::unordered_map<uint32, PlayerSpell> PlayerSpellMap;
|
|
typedef std::unordered_set<SpellModifier*> SpellModContainer;
|
|
|
|
+struct ReforgeData
|
|
+{
|
|
+ uint32 increase, decrease;
|
|
+ int32 stat_value;
|
|
+};
|
|
+typedef std::unordered_map<uint32, ReforgeData> ReforgeMapType;
|
|
+
|
|
typedef std::unordered_map<uint32 /*instanceId*/, time_t/*releaseTime*/> InstanceTimeMap;
|
|
|
|
enum ActionButtonUpdateState
|
|
@@ -2180,6 +2187,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
|
|
std::string GetMapAreaAndZoneString() const;
|
|
std::string GetCoordsMapAreaAndZoneString() const;
|
|
|
|
+ ReforgeMapType reforgeMap; // reforgeMap[iGUID] = ReforgeData
|
|
+
|
|
std::string GetDebugInfo() const override;
|
|
|
|
protected:
|
|
diff --git a/src/server/scripts/Custom/Reforging/README.md b/src/server/scripts/Custom/Reforging/README.md
|
|
new file mode 100644
|
|
index 000000000000..819e962cda15
|
|
--- /dev/null
|
|
+++ b/src/server/scripts/Custom/Reforging/README.md
|
|
@@ -0,0 +1,40 @@
|
|
+# Reforging [](https://travis-ci.org/Rochet2/TrinityCore)
|
|
+
|
|
+#### About
|
|
+Blizz*like* reforging
|
|
+Change 40% of your item stats to something else
|
|
+Sends item packets so you can see the changes on item tooltips
|
|
+Simple and easy to use interface
|
|
+Made for 3.3.5a.<br />
|
|
+Source: http://rochet2.github.io/Reforging.html
|
|
+
|
|
+Video: https://youtu.be/84EtvKTfqQM
|
|
+
|
|
+#### Installation
|
|
+
|
|
+Available as:
|
|
+- Direct merge: https://github.com/Rochet2/TrinityCore/tree/reforging_3.3.5
|
|
+- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...reforging_3.3.5.diff
|
|
+- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:3.3.5...reforging_3.3.5
|
|
+
|
|
+Using direct merge:
|
|
+- open git bash to source location
|
|
+- do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git`
|
|
+- do `git pull rochet2 reforging_3.3.5`
|
|
+- use cmake and compile
|
|
+
|
|
+Using diff:
|
|
+- DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail.
|
|
+- download the diff by __right clicking__ the link and select __Save link as__
|
|
+- place the downloaded `reforging_3.3.5.diff` to the source root folder
|
|
+- open git bash to source location
|
|
+- do `git apply reforging_3.3.5.diff`
|
|
+- use cmake and compile
|
|
+
|
|
+After compiling:
|
|
+- TrinityCore auto updater should run needed SQLs automatically.
|
|
+- If you do not use the auto updater then run files named `*_reforging.sql` from `\sql\custom` to your databases.
|
|
+- optionally you can also insert a reforger NPC to your database by running `\src\server\scripts\Custom\Reforging\sql\world_npc.sql` to your world database.
|
|
+
|
|
+#### Bugs and Contact
|
|
+Report issues and similar to https://rochet2.github.io/
|
|
diff --git a/src/server/scripts/Custom/Reforging/Reforger.cpp b/src/server/scripts/Custom/Reforging/Reforger.cpp
|
|
new file mode 100644
|
|
index 000000000000..eea92ea4574d
|
|
--- /dev/null
|
|
+++ b/src/server/scripts/Custom/Reforging/Reforger.cpp
|
|
@@ -0,0 +1,488 @@
|
|
+#include "Reforging.h"
|
|
+#include <sstream>
|
|
+#include <string>
|
|
+#include <vector>
|
|
+#include "Creature.h"
|
|
+#include "Define.h"
|
|
+#include "EventProcessor.h"
|
|
+#include "GossipDef.h"
|
|
+#include "Item.h"
|
|
+#include "ItemTemplate.h"
|
|
+#include "ObjectGuid.h"
|
|
+#include "ObjectMgr.h"
|
|
+#include "Player.h"
|
|
+#include "ScriptedCreature.h"
|
|
+#include "ScriptedGossip.h"
|
|
+#include "ScriptMgr.h"
|
|
+#include "SharedDefines.h"
|
|
+#include "Spell.h"
|
|
+#include "Transaction.h"
|
|
+#include "WorldPacket.h"
|
|
+#include "WorldSession.h"
|
|
+#include "DatabaseEnv.h"
|
|
+
|
|
+/*
|
|
+Reforging by Rochet2
|
|
+http://rochet2.github.io/
|
|
+
|
|
+Rules of thumb:
|
|
+Item can be reforged once.
|
|
+Item reforge wont show to anyone but you in tooltips. Stats will be there nevertheless.
|
|
+You will see the increased stats on all tooltips of the same item you reforged.
|
|
+You can disable the stat changes to tooltips by setting send_cache_packets to false in Reforging.cpp.
|
|
+Reforges are stripped when you mail, ah, guildbank the item etc. Only YOU can have the reforge.
|
|
+Only item base stats are reforgable. Enchants and random stats are not.
|
|
+
|
|
+This script is made blizzlike. This means that the reforgable stats etc are from CATACLYSM!
|
|
+I have been informed that some stats were removed etc that would be important to be reforgable.
|
|
+However I do not know what those stats are currently. Do look through the statTypes to add whatever you want.
|
|
+Edit IsReforgable is you want to tweak requirements
|
|
+
|
|
+*/
|
|
+
|
|
+// Remember to add to GetStatName too
|
|
+static const ItemModType statTypes[] = { ITEM_MOD_SPIRIT, ITEM_MOD_DODGE_RATING, ITEM_MOD_PARRY_RATING, ITEM_MOD_HIT_RATING, ITEM_MOD_CRIT_RATING, ITEM_MOD_HASTE_RATING, ITEM_MOD_EXPERTISE_RATING };
|
|
+static const uint8 stat_type_max = sizeof(statTypes) / sizeof(*statTypes);
|
|
+
|
|
+static const char* GetStatName(uint32 ItemStatType)
|
|
+{
|
|
+ switch (ItemStatType)
|
|
+ {
|
|
+ case ITEM_MOD_SPIRIT: return "Spirit"; break;
|
|
+ case ITEM_MOD_DODGE_RATING: return "Dodge rating"; break;
|
|
+ case ITEM_MOD_PARRY_RATING: return "Parry rating"; break;
|
|
+ case ITEM_MOD_HIT_RATING: return "Hit rating"; break;
|
|
+ case ITEM_MOD_CRIT_RATING: return "Crit rating"; break;
|
|
+ case ITEM_MOD_HASTE_RATING: return "Haste rating"; break;
|
|
+ case ITEM_MOD_EXPERTISE_RATING: return "Expertise rating"; break;
|
|
+ default: return NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const char* GetSlotName(uint8 slot, WorldSession* /*session*/)
|
|
+{
|
|
+ switch (slot)
|
|
+ {
|
|
+ case EQUIPMENT_SLOT_HEAD: return "Head";
|
|
+ case EQUIPMENT_SLOT_NECK: return "Neck";
|
|
+ case EQUIPMENT_SLOT_SHOULDERS: return "Shoulders";
|
|
+ case EQUIPMENT_SLOT_BODY: return "Shirt";
|
|
+ case EQUIPMENT_SLOT_CHEST: return "Chest";
|
|
+ case EQUIPMENT_SLOT_WAIST: return "Waist";
|
|
+ case EQUIPMENT_SLOT_LEGS: return "Legs";
|
|
+ case EQUIPMENT_SLOT_FEET: return "Feet";
|
|
+ case EQUIPMENT_SLOT_WRISTS: return "Wrists";
|
|
+ case EQUIPMENT_SLOT_HANDS: return "Hands";
|
|
+ case EQUIPMENT_SLOT_FINGER1: return "Right finger";
|
|
+ case EQUIPMENT_SLOT_FINGER2: return "Left finger";
|
|
+ case EQUIPMENT_SLOT_TRINKET1: return "Right trinket";
|
|
+ case EQUIPMENT_SLOT_TRINKET2: return "Left trinket";
|
|
+ case EQUIPMENT_SLOT_BACK: return "Back";
|
|
+ case EQUIPMENT_SLOT_MAINHAND: return "Main hand";
|
|
+ case EQUIPMENT_SLOT_OFFHAND: return "Off hand";
|
|
+ case EQUIPMENT_SLOT_TABARD: return "Tabard";
|
|
+ case EQUIPMENT_SLOT_RANGED: return "Ranged";
|
|
+ default: return NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static uint32 Melt(uint8 i, uint8 j)
|
|
+{
|
|
+ return (i << 8) + j;
|
|
+}
|
|
+
|
|
+static void Unmelt(uint32 melt, uint8& i, uint8& j)
|
|
+{
|
|
+ i = melt >> 8;
|
|
+ j = melt & 0xFF;
|
|
+}
|
|
+
|
|
+static Item* GetEquippedItem(Player* player, uint32 guidlow)
|
|
+{
|
|
+ for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
|
+ if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
|
+ if (pItem->GetGUID().GetCounter() == guidlow)
|
|
+ return pItem;
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static bool IsReforgable(Item* invItem, Player* player)
|
|
+{
|
|
+ //if (!invItem->IsEquipped())
|
|
+ // return false;
|
|
+ if (invItem->GetOwnerGUID() != player->GetGUID())
|
|
+ return false;
|
|
+ const ItemTemplate* pProto = invItem->GetTemplate();
|
|
+ //if (pProto->ItemLevel < 200)
|
|
+ // return false;
|
|
+ //if (pProto->Quality == ITEM_QUALITY_HEIRLOOM) // block heirlooms necessary?
|
|
+ // return false;
|
|
+ if (!pProto->StatsCount || pProto->StatsCount >= MAX_ITEM_PROTO_STATS) // Mandatory! Do NOT remove or edit
|
|
+ return false;
|
|
+ if (!player->reforgeMap.empty() && player->reforgeMap.find(invItem->GetGUID().GetCounter()) != player->reforgeMap.end()) // Mandatory! Do NOT remove or edit
|
|
+ return false;
|
|
+ for (uint32 i = 0; i < pProto->StatsCount; ++i)
|
|
+ {
|
|
+ if (!GetStatName(pProto->ItemStat[i].ItemStatType))
|
|
+ continue;
|
|
+ if (((int32)floorf((float)pProto->ItemStat[i].ItemStatValue * 0.4f)) > 1)
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void UpdatePlayerReforgeStats(Item* invItem, Player* player, uint32 decrease, uint32 increase) // stat types
|
|
+{
|
|
+ const ItemTemplate* pProto = invItem->GetTemplate();
|
|
+
|
|
+ int32 stat_diff = 0;
|
|
+ for (uint32 i = 0; i < pProto->StatsCount; ++i)
|
|
+ {
|
|
+ if (pProto->ItemStat[i].ItemStatType == increase)
|
|
+ return; // Should not have the increased stat already
|
|
+ if (pProto->ItemStat[i].ItemStatType == decrease)
|
|
+ stat_diff = (int32)floorf((float)pProto->ItemStat[i].ItemStatValue * 0.4f);
|
|
+ }
|
|
+ if (stat_diff <= 0)
|
|
+ return; // Should have some kind of diff
|
|
+
|
|
+ // Update player stats
|
|
+ if (invItem->IsEquipped())
|
|
+ player->_ApplyItemMods(invItem, invItem->GetSlot(), false);
|
|
+ uint32 guidlow = invItem->GetGUID().GetCounter();
|
|
+ ReforgeData& data = player->reforgeMap[guidlow];
|
|
+ data.increase = increase;
|
|
+ data.decrease = decrease;
|
|
+ data.stat_value = stat_diff;
|
|
+ if (invItem->IsEquipped())
|
|
+ player->_ApplyItemMods(invItem, invItem->GetSlot(), true);
|
|
+ // CharacterDatabase.PExecute("REPLACE INTO `custom_reforging` (`GUID`, `increase`, `decrease`, `stat_value`) VALUES (%u, %u, %u, %i)", guidlow, increase, decrease, stat_diff);
|
|
+ player->ModifyMoney(pProto->SellPrice < (10 * GOLD) ? (-10 * GOLD) : -(int32)pProto->SellPrice);
|
|
+ SendReforgePacket(player, invItem->GetEntry(), 0, &data);
|
|
+ // player->SaveToDB();
|
|
+}
|
|
+
|
|
+class REFORGE_PLAYER : public PlayerScript
|
|
+{
|
|
+public:
|
|
+ REFORGE_PLAYER() : PlayerScript("REFORGE_PLAYER")
|
|
+ {
|
|
+ CharacterDatabase.DirectExecute("DELETE FROM `custom_reforging` WHERE NOT EXISTS (SELECT 1 FROM `item_instance` WHERE `item_instance`.`guid` = `custom_reforging`.`GUID`)");
|
|
+ }
|
|
+
|
|
+ class SendRefPackLogin : public BasicEvent
|
|
+ {
|
|
+ public:
|
|
+ SendRefPackLogin(Player* _player) : player(_player)
|
|
+ {
|
|
+ _player->m_Events.AddEvent(this, _player->m_Events.CalculateTime(Milliseconds(1000)));
|
|
+ }
|
|
+
|
|
+ bool Execute(uint64, uint32) override
|
|
+ {
|
|
+ SendReforgePackets(player);
|
|
+ return true;
|
|
+ }
|
|
+ Player* player;
|
|
+ };
|
|
+
|
|
+ void OnLogin(Player* player, bool /*firstLogin*/) override
|
|
+ {
|
|
+ uint32 playerGUID = player->GetGUID().GetCounter();
|
|
+ QueryResult result = CharacterDatabase.PQuery("SELECT `GUID`, `increase`, `decrease`, `stat_value` FROM `custom_reforging` WHERE `Owner` = %u", playerGUID);
|
|
+ if (result)
|
|
+ {
|
|
+ do
|
|
+ {
|
|
+ uint32 lowGUID = (*result)[0].GetUInt32();
|
|
+ Item* invItem = player->GetItemByGuid(ObjectGuid(HighGuid::Item, 0, lowGUID));
|
|
+ if (invItem && invItem->IsEquipped())
|
|
+ player->_ApplyItemMods(invItem, invItem->GetSlot(), false);
|
|
+ ReforgeData& data = player->reforgeMap[lowGUID];
|
|
+ data.increase = (*result)[1].GetUInt32();
|
|
+ data.decrease = (*result)[2].GetUInt32();
|
|
+ data.stat_value = (*result)[3].GetInt32();
|
|
+ if (invItem && invItem->IsEquipped())
|
|
+ player->_ApplyItemMods(invItem, invItem->GetSlot(), true);
|
|
+ // SendReforgePacket(player, entry, lowGUID);
|
|
+ } while (result->NextRow());
|
|
+
|
|
+ // SendReforgePackets(player);
|
|
+ new SendRefPackLogin(player);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ //void OnLogout(Player* player) override
|
|
+ //{
|
|
+ // if (player->reforgeMap.empty())
|
|
+ // return;
|
|
+ // for (ReforgeMapType::const_iterator it = player->reforgeMap.begin(); it != player->reforgeMap.end();)
|
|
+ // {
|
|
+ // ReforgeMapType::const_iterator old_it = it++;
|
|
+ // RemoveReforge(player, old_it->first, false);
|
|
+ // }
|
|
+ //}
|
|
+
|
|
+ void OnSave(Player* player) override
|
|
+ {
|
|
+ uint32 lowguid = player->GetGUID().GetCounter();
|
|
+ auto trans = CharacterDatabase.BeginTransaction();
|
|
+ trans->PAppend("DELETE FROM `custom_reforging` WHERE `Owner` = %u", lowguid);
|
|
+ if (!player->reforgeMap.empty())
|
|
+ {
|
|
+ // Only save items that are in inventory / bank / etc
|
|
+ std::vector<Item*> items = GetItemList(player);
|
|
+ for (std::vector<Item*>::const_iterator it = items.begin(); it != items.end(); ++it)
|
|
+ {
|
|
+ ReforgeMapType::const_iterator it2 = player->reforgeMap.find((*it)->GetGUID().GetCounter());
|
|
+ if (it2 == player->reforgeMap.end())
|
|
+ continue;
|
|
+
|
|
+ const ReforgeData& data = it2->second;
|
|
+ trans->PAppend("REPLACE INTO `custom_reforging` (`GUID`, `increase`, `decrease`, `stat_value`, `Owner`) VALUES (%u, %u, %u, %i, %u)", it2->first, data.increase, data.decrease, data.stat_value, lowguid);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (trans->GetSize()) // basically never false
|
|
+ CharacterDatabase.CommitTransaction(trans);
|
|
+ }
|
|
+};
|
|
+
|
|
+class REFORGER_NPC : public CreatureScript
|
|
+{
|
|
+public:
|
|
+ REFORGER_NPC() : CreatureScript("REFORGER_NPC") { }
|
|
+
|
|
+ class ReforgeAI : public ScriptedAI
|
|
+ {
|
|
+ public:
|
|
+ ReforgeAI(Creature* creature) : ScriptedAI(creature)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ struct Timed : public BasicEvent
|
|
+ {
|
|
+ // This timed event tries to fix modify money breaking gossip
|
|
+ // This event closes the gossip menu and on the second player update tries to open the next menu
|
|
+ Timed(Player* player, Creature* creature) : guid(creature->GetGUID()), player(player), triggered(false)
|
|
+ {
|
|
+ CloseGossipMenuFor(player);
|
|
+ player->m_Events.AddEvent(this, player->m_Events.CalculateTime(Milliseconds(1)));
|
|
+ }
|
|
+
|
|
+ bool Execute(uint64, uint32) override
|
|
+ {
|
|
+ if (!triggered)
|
|
+ {
|
|
+ triggered = true;
|
|
+ player->m_Events.AddEvent(this, player->m_Events.CalculateTime(Milliseconds(1)));
|
|
+ return false;
|
|
+ }
|
|
+ if (Creature* creature = player->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP))
|
|
+ OnGossipHello(player, creature);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ ObjectGuid guid;
|
|
+ Player* player;
|
|
+ bool triggered;
|
|
+ };
|
|
+
|
|
+ bool OnGossipHello(Player* player) override
|
|
+ {
|
|
+ return OnGossipHello(player, me);
|
|
+ }
|
|
+
|
|
+ static bool OnGossipHello(Player* player, Creature* creature)
|
|
+ {
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_BATTLE, "Select slot of the item to reforge:", 0, Melt(MAIN_MENU, 0));
|
|
+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
|
|
+ {
|
|
+ if (Item* invItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
|
+ if (IsReforgable(invItem, player))
|
|
+ if (const char* slotname = GetSlotName(slot, player->GetSession()))
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_TRAINER, slotname, 0, Melt(SELECT_STAT_REDUCE, slot));
|
|
+ }
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_TRAINER, "Remove reforges", 0, Melt(SELECT_RESTORE, 0));
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Update menu", 0, Melt(MAIN_MENU, 0));
|
|
+ SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ bool OnGossipSelect(Player* player, uint32 /*menu_id*/, uint32 gossipListId) override
|
|
+ {
|
|
+ uint32 sender = player->PlayerTalkClass->GetGossipOptionSender(gossipListId);
|
|
+ uint32 action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId);
|
|
+ return OnGossipSelect(player, me, sender, action);
|
|
+ }
|
|
+
|
|
+ static bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 melt)
|
|
+ {
|
|
+ ClearGossipMenuFor(player);
|
|
+
|
|
+ uint8 menu, action;
|
|
+ Unmelt(melt, menu, action);
|
|
+
|
|
+ switch (menu)
|
|
+ {
|
|
+ case MAIN_MENU: OnGossipHello(player, creature); break;
|
|
+ case SELECT_STAT_REDUCE:
|
|
+ // action = slot
|
|
+ if (Item* invItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action))
|
|
+ {
|
|
+ if (IsReforgable(invItem, player))
|
|
+ {
|
|
+ uint32 guidlow = invItem->GetGUID().GetCounter();
|
|
+ const ItemTemplate* pProto = invItem->GetTemplate();
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_BATTLE, "Stat to decrease:", sender, melt);
|
|
+ for (uint32 i = 0; i < pProto->StatsCount; ++i)
|
|
+ {
|
|
+ int32 stat_diff = ((int32)floorf((float)pProto->ItemStat[i].ItemStatValue * 0.4f));
|
|
+ if (stat_diff > 1)
|
|
+ if (const char* stat_name = GetStatName(pProto->ItemStat[i].ItemStatType))
|
|
+ {
|
|
+ std::ostringstream oss;
|
|
+ oss << stat_name << " (" << pProto->ItemStat[i].ItemStatValue << " |cFFDB2222-" << stat_diff << "|r)";
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_TRAINER, oss.str(), guidlow, Melt(SELECT_STAT_INCREASE, i));
|
|
+ }
|
|
+ }
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_TALK, "Back..", 0, Melt(MAIN_MENU, 0));
|
|
+ SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ player->GetSession()->SendNotification("Invalid item selected");
|
|
+ OnGossipHello(player, creature);
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ player->GetSession()->SendNotification("Invalid item selected");
|
|
+ OnGossipHello(player, creature);
|
|
+ }
|
|
+ break;
|
|
+ case SELECT_STAT_INCREASE:
|
|
+ // sender = item guidlow
|
|
+ // action = StatsCount id
|
|
+ {
|
|
+ Item* invItem = GetEquippedItem(player, sender);
|
|
+ if (invItem)
|
|
+ {
|
|
+ const ItemTemplate* pProto = invItem->GetTemplate();
|
|
+ int32 stat_diff = ((int32)floorf((float)pProto->ItemStat[action].ItemStatValue * 0.4f));
|
|
+
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_BATTLE, "Stat to increase:", sender, melt);
|
|
+ for (uint8 i = 0; i < stat_type_max; ++i)
|
|
+ {
|
|
+ bool cont = false;
|
|
+ for (uint32 j = 0; j < pProto->StatsCount; ++j)
|
|
+ {
|
|
+ if (statTypes[i] == pProto->ItemStat[j].ItemStatType) // skip existing stats on item
|
|
+ {
|
|
+ cont = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (cont)
|
|
+ continue;
|
|
+ if (const char* stat_name = GetStatName(statTypes[i]))
|
|
+ {
|
|
+ std::ostringstream oss;
|
|
+ oss << stat_name << " |cFF3ECB3C+" << stat_diff << "|r";
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, oss.str(), sender, Melt(i, (uint8)pProto->ItemStat[action].ItemStatType), "Are you sure you want to reforge\n\n" + pProto->Name1, (pProto->SellPrice < (10 * GOLD) ? (10 * GOLD) : pProto->SellPrice), false);
|
|
+ }
|
|
+ }
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_TALK, "Back..", 0, Melt(SELECT_STAT_REDUCE, invItem->GetSlot()));
|
|
+ SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ player->GetSession()->SendNotification("Invalid item selected");
|
|
+ OnGossipHello(player, creature);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case SELECT_RESTORE:
|
|
+ {
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_BATTLE, "Select slot to remove reforge from:", sender, melt);
|
|
+ if (!player->reforgeMap.empty())
|
|
+ {
|
|
+ for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
|
|
+ {
|
|
+ if (Item* invItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
|
+ if (player->reforgeMap.find(invItem->GetGUID().GetCounter()) != player->reforgeMap.end())
|
|
+ if (const char* slotname = GetSlotName(slot, player->GetSession()))
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, slotname, invItem->GetGUID().GetCounter(), Melt(RESTORE, 0), "Remove reforge from\n\n" + invItem->GetTemplate()->Name1, 0, false);
|
|
+ }
|
|
+ }
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Update menu", sender, melt);
|
|
+ AddGossipItemFor(player, GOSSIP_ICON_TALK, "Back..", 0, Melt(MAIN_MENU, 0));
|
|
+ SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
|
+ }
|
|
+ break;
|
|
+ case RESTORE:
|
|
+ // sender = item guidlow
|
|
+ {
|
|
+ if (player->GetItemByGuid(ObjectGuid(HighGuid::Item, 0, sender)))
|
|
+ {
|
|
+ if (!player->reforgeMap.empty() && player->reforgeMap.find(sender) != player->reforgeMap.end())
|
|
+ RemoveReforge(player, sender, true);
|
|
+ }
|
|
+ OnGossipHello(player, creature);
|
|
+ }
|
|
+ break;
|
|
+ default: // Reforge
|
|
+ // sender = item guidlow
|
|
+ // menu = stat type to increase index to statTypes[]
|
|
+ // action = stat type to decrease
|
|
+ {
|
|
+ if (menu < stat_type_max)
|
|
+ {
|
|
+ Item* invItem = GetEquippedItem(player, sender);
|
|
+ if (invItem && IsReforgable(invItem, player))
|
|
+ {
|
|
+ if (player->HasEnoughMoney(invItem->GetTemplate()->SellPrice < (10 * GOLD) ? (10 * GOLD) : invItem->GetTemplate()->SellPrice))
|
|
+ {
|
|
+ // int32 stat_diff = ((int32)floorf((float)invItem->GetTemplate()->ItemStat[action].ItemStatValue * 0.4f));
|
|
+ UpdatePlayerReforgeStats(invItem, player, action, statTypes[menu]); // rewrite this function
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ player->GetSession()->SendNotification("Not enough money");
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ player->GetSession()->SendNotification("Invalid item selected");
|
|
+ }
|
|
+ }
|
|
+ // OnGossipHello(player, creature);
|
|
+ new Timed(player, creature);
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ enum Menus
|
|
+ {
|
|
+ MAIN_MENU = 200, // stat_type_max
|
|
+ SELECT_ITEM,
|
|
+ SELECT_STAT_REDUCE,
|
|
+ SELECT_STAT_INCREASE,
|
|
+ SELECT_RESTORE,
|
|
+ RESTORE,
|
|
+ REFORGE,
|
|
+ };
|
|
+ };
|
|
+
|
|
+ CreatureAI* GetAI(Creature* creature) const override
|
|
+ {
|
|
+ return new ReforgeAI(creature);
|
|
+ }
|
|
+};
|
|
+
|
|
+void AddSC_REFORGER_NPC()
|
|
+{
|
|
+ new REFORGER_NPC;
|
|
+ new REFORGE_PLAYER;
|
|
+}
|
|
diff --git a/src/server/scripts/Custom/Reforging/sql/world_npc.sql b/src/server/scripts/Custom/Reforging/sql/world_npc.sql
|
|
new file mode 100644
|
|
index 000000000000..d4a62fc11d73
|
|
--- /dev/null
|
|
+++ b/src/server/scripts/Custom/Reforging/sql/world_npc.sql
|
|
@@ -0,0 +1,6 @@
|
|
+SET
|
|
+@Entry = 190011,
|
|
+@Name = "Thaumaturge Vashreen";
|
|
+
|
|
+INSERT INTO `creature_template` (`entry`, `modelid1`, `modelid2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `scale`, `rank`, `dmgschool`, `baseattacktime`, `rangeattacktime`, `unit_class`, `unit_flags`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `AIName`, `MovementType`, `HoverHeight`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`) VALUES
|
|
+(@Entry, 20988, 0, @Name, "Arcane Reforger", NULL, 0, 80, 80, 2, 35, 1, 1, 0, 0, 2000, 0, 1, 0, 7, 138936390, 0, 0, 0, '', 0, 1, 0, 0, 1, 0, 0, 'REFORGER_NPC');
|
|
diff --git a/src/server/scripts/Custom/custom_script_loader.cpp b/src/server/scripts/Custom/custom_script_loader.cpp
|
|
index 9e5e9ba2bfd6..74be1bfa6f98 100644
|
|
--- a/src/server/scripts/Custom/custom_script_loader.cpp
|
|
+++ b/src/server/scripts/Custom/custom_script_loader.cpp
|
|
@@ -16,9 +16,11 @@
|
|
*/
|
|
|
|
// This is where scripts' loading functions should be declared:
|
|
+void AddSC_REFORGER_NPC();
|
|
|
|
// The name of this function should match:
|
|
// void Add${NameOfDirectory}Scripts()
|
|
void AddCustomScripts()
|
|
{
|
|
+ AddSC_REFORGER_NPC();
|
|
}
|