-
Notifications
You must be signed in to change notification settings - Fork 31
/
Reforging.cpp
531 lines (496 loc) · 23.6 KB
/
Reforging.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
#include "ScriptPCH.h"
struct accepted_stat_format {uint32 stat_type; char* stat_name; int32 base_stat_change; };
// ########################################################################################
// Reforging config
// ########################################################################################
static const uint32 tokenEntry = 65717; // token entry
static const uint8 maxTokenAmount = 3; // max token cost (multiplies the max stat amount also)
static const uint8 maxReforges = 3; // maximum reforges for one item
static const bool send_cache_packets = true; // change player cache?
static const accepted_stat_format accepted_stat_types[] =
{
// See ItemPrototype.h for ItemModType definitions. You can add more rows for more stats.
{ITEM_MOD_HEALTH, "Health", 7500},
{ITEM_MOD_STRENGTH, "Strength", 900},
{ITEM_MOD_INTELLECT, "Intellect", 900},
{ITEM_MOD_AGILITY, "Agility", 300},
{ITEM_MOD_SPIRIT, "Spirit", 1250},
{ITEM_MOD_STAMINA, "Stamina", 1000},
{ITEM_MOD_ATTACK_POWER, "Attack power", 1250},
{ITEM_MOD_SPELL_POWER, "Spell power", 1000},
};
// ########################################################################################
static const uint32 accepted_stat_types_max = sizeof(accepted_stat_types) / sizeof(accepted_stat_format);
struct playerItems_format {uint32 Entry, lowGUID;};
static UNORDERED_MAP<uint32, std::vector<playerItems_format> > playerItems;
static const void SendReforgePackets(Player* player)
{
if(!send_cache_packets)
return;
uint32 pGUID = player->GetGUIDLow();
if(playerItems.find(pGUID) == playerItems.end())
return;
for (std::vector<playerItems_format>::iterator it = playerItems[pGUID].begin(); it != playerItems[pGUID].end(); it++)
{
// See how enchanthing is done
// Update player cache (self only) pure visual.
// HandleItemQuerySingleOpcode copy paste:
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(it->Entry);
std::string Name = proto->Name1;
std::string Description = proto->Description;
int loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
if (ItemLocale const* il = sObjectMgr->GetItemLocale(proto->ItemId))
{
ObjectMgr::GetLocaleString(il->Name, loc_idx, Name);
ObjectMgr::GetLocaleString(il->Description, loc_idx, Description);
}
}
WorldPacket data(SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
data << proto->ItemId;
data << proto->Class;
data << proto->SubClass;
data << int32(proto->SoundOverrideSubclass);
data << Name;
data << uint8(0x00);
data << uint8(0x00);
data << uint8(0x00);
data << proto->DisplayInfoID;
data << proto->Quality;
data << proto->Flags;
data << proto->Flags2;
data << proto->BuyPrice;
data << proto->SellPrice;
data << proto->InventoryType;
data << proto->AllowableClass;
data << proto->AllowableRace;
data << proto->ItemLevel;
data << proto->RequiredLevel;
data << proto->RequiredSkill;
data << proto->RequiredSkillRank;
data << proto->RequiredSpell;
data << proto->RequiredHonorRank;
data << proto->RequiredCityRank;
data << proto->RequiredReputationFaction;
data << proto->RequiredReputationRank;
data << int32(proto->MaxCount);
data << int32(proto->Stackable);
data << proto->ContainerSlots;
data << proto->StatsCount;
for (uint32 i = 0; i < proto->StatsCount; ++i)
{
data << proto->ItemStat[i].ItemStatType;
if(sObjectMgr->_itemFakeStatStore[it->lowGUID].find(i) != sObjectMgr->_itemFakeStatStore[it->lowGUID].end())
data << proto->ItemStat[i].ItemStatValue + sObjectMgr->_itemFakeStatStore[it->lowGUID][i];
else
data << proto->ItemStat[i].ItemStatValue;
}
data << proto->ScalingStatDistribution;
data << proto->ScalingStatValue;
for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
{
data << proto->Damage[i].DamageMin;
data << proto->Damage[i].DamageMax;
data << proto->Damage[i].DamageType;
}
data << proto->Armor;
data << proto->HolyRes;
data << proto->FireRes;
data << proto->NatureRes;
data << proto->FrostRes;
data << proto->ShadowRes;
data << proto->ArcaneRes;
data << proto->Delay;
data << proto->AmmoType;
data << proto->RangedModRange;
for (int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s)
{
SpellInfo const* spell = sSpellMgr->GetSpellInfo(proto->Spells[s].SpellId);
if (spell)
{
bool db_data = proto->Spells[s].SpellCooldown >= 0 || proto->Spells[s].SpellCategoryCooldown >= 0;
data << proto->Spells[s].SpellId;
data << proto->Spells[s].SpellTrigger;
data << uint32(-abs(proto->Spells[s].SpellCharges));
if (db_data)
{
data << uint32(proto->Spells[s].SpellCooldown);
data << uint32(proto->Spells[s].SpellCategory);
data << uint32(proto->Spells[s].SpellCategoryCooldown);
}
else
{
data << uint32(spell->RecoveryTime);
data << uint32(spell->Category);
data << uint32(spell->CategoryRecoveryTime);
}
}
else
{
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint32(-1);
data << uint32(0);
data << uint32(-1);
}
}
data << proto->Bonding;
data << Description;
data << proto->PageText;
data << proto->LanguageID;
data << proto->PageMaterial;
data << proto->StartQuest;
data << proto->LockID;
data << int32(proto->Material);
data << proto->Sheath;
data << proto->RandomProperty;
data << proto->RandomSuffix;
data << proto->Block;
data << proto->ItemSet;
data << proto->MaxDurability;
data << proto->Area;
data << proto->Map;
data << proto->BagFamily;
data << proto->TotemCategory;
for (int s = 0; s < MAX_ITEM_PROTO_SOCKETS; ++s)
{
data << proto->Socket[s].Color;
data << proto->Socket[s].Content;
}
data << proto->socketBonus;
data << proto->GemProperties;
data << proto->RequiredDisenchantSkill;
data << proto->ArmorDamageModifier;
data << proto->Duration;
data << proto->ItemLimitCategory;
data << proto->HolidayId;
player->GetSession()->SendPacket(&data);
}
}
class REFORGING_DATA_HANDLER : public PlayerScript
{
public:
REFORGING_DATA_HANDLER() : PlayerScript("REFORGING_DATA_HANDLER")
{
// DB cleanup on startup
CharacterDatabase.Execute("DELETE FROM custom_reforges WHERE NOT EXISTS (SELECT 1 FROM item_instance WHERE item_instance.guid = custom_reforges.iGUID)");
}
// Called when a player logs out.
void OnLogout(Player* player)
{
DeleteSavedReforges(player->GetGUIDLow()); // delete stored reforges from the player (not DB)
}
void OnLogin(Player* player)
{
uint32 pGUID = player->GetGUIDLow();
DeleteSavedReforges(pGUID); // delete saved reforges from the player before loading them
QueryResult result = CharacterDatabase.PQuery("SELECT `iGUID`, `stat_count_id`, `stat_diff`, `Entry` FROM `custom_reforges` WHERE pGUID = %u and EXISTS (SELECT 1 FROM item_instance WHERE item_instance.guid = custom_reforges.iGUID)", pGUID);
if (result)
{
UNORDERED_MAP<uint32, std::map<uint32, int32> > stat_temp_container; // iGUID <stat_count, stat_diff>
std::set<uint32> sent_error_message;
do
{
uint32 lowGUID = (*result)[0].GetUInt32();
if(sObjectMgr->_itemFakeStatStore.find(lowGUID) != sObjectMgr->_itemFakeStatStore.end() && sObjectMgr->_itemFakeStatStore[lowGUID].size() >= maxReforges)
{
if(sent_error_message.find(lowGUID) != sent_error_message.end())
continue;
sLog->outErrorDb("Too many reforges for item lowGUID %u, ignoring extra reforges.", lowGUID);
sent_error_message.insert(lowGUID);
}
else
{
stat_temp_container[lowGUID][(*result)[1].GetUInt32()] = (*result)[2].GetInt32();
playerItems_format temp = {(*result)[3].GetUInt32(), lowGUID}; // temp var so we can insert our stuff
playerItems[pGUID].push_back(temp);
}
} while (result->NextRow());
// apply stat changes for all reforged items equipped and save them to container
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
{
if(Item* invItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
uint32 invGUID = invItem->GetGUIDLow();
if(stat_temp_container.find(invGUID) != stat_temp_container.end())
{
player->_ApplyItemMods(invItem, slot, false);
for(std::map<uint32, int32>::iterator stat_count_id = stat_temp_container[invGUID].begin(); stat_count_id != stat_temp_container[invGUID].end(); stat_count_id++)
{
sObjectMgr->_itemFakeStatStore[invGUID][stat_count_id->first] = stat_count_id->second;
}
player->_ApplyItemMods(invItem, slot, true);
stat_temp_container.erase(invGUID); // saved reforges for this item, delete from temp
}
}
}
// save the rest of the player's reforges to the container if more exist
if(!stat_temp_container.empty())
{
for(UNORDERED_MAP<uint32, std::map<uint32, int32> >::iterator iGUID = stat_temp_container.begin(); iGUID != stat_temp_container.end(); iGUID++)
{
for(std::map<uint32, int32>::iterator stat_count_id = iGUID->second.begin(); stat_count_id != iGUID->second.end(); stat_count_id++)
{
sObjectMgr->_itemFakeStatStore[iGUID->first][stat_count_id->first] = stat_count_id->second;
}
}
}
}
SendReforgePackets(player);
}
private:
void DeleteSavedReforges(uint32 pGUID)
{
if(playerItems.find(pGUID) == playerItems.end())
return; // Already empty!
for (std::vector<playerItems_format>::iterator it = playerItems[pGUID].begin(); it != playerItems[pGUID].end(); it++)
{
if(sObjectMgr->_itemFakeStatStore.find(it->lowGUID) != sObjectMgr->_itemFakeStatStore.end())
sObjectMgr->_itemFakeStatStore.erase(it->lowGUID);
}
playerItems.erase(pGUID);
}
};
class REFORGER_NPC : public CreatureScript
{
public:
REFORGER_NPC() : CreatureScript("REFORGER_NPC")
{
for(uint32 i = 0; i < accepted_stat_types_max; i++)
{
stat_format temp = {accepted_stat_types[i].stat_name, accepted_stat_types[i].base_stat_change};
stat_types[accepted_stat_types[i].stat_type] = temp;
}
}
bool OnGossipHello(Player* player, Creature* creature)
{
if(selectedItem.find(player->GetGUIDLow()) != selectedItem.end())
selectedItem.erase(player->GetGUIDLow());
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Buy Reforge Tokens.", VENDOR, 0);
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, "Select equipped item in slot:", 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(char* slotname = GetSlotName(slot, player->GetSession()))
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, slotname, SELECT_STAT_INCREASE, slot);
}
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Update menu", MAIN_MENU, 0);
player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
{
player->PlayerTalkClass->ClearMenus();
switch(sender)
{
case VENDOR:
player->GetSession()->SendListInventory(creature->GetGUID());
break;
case MAIN_MENU: OnGossipHello(player, creature); break;
case SELECT_STAT_INCREASE:
// action = slot
if (Item* invItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, action))
{
if(IsReforgable(invItem, player))
{
selectedItem[player->GetGUIDLow()] = invItem;
const ItemTemplate* proto = invItem->GetTemplate();
char label[250];
snprintf(label, 250, "Selected item:\n%s", invItem->GetTemplate()->Name1.c_str());
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, label, sender, action);
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, "Select stat to increase:", sender, action);
bool hasfakestats = ((sObjectMgr->_itemFakeStatStore.find(invItem->GetGUIDLow()) != sObjectMgr->_itemFakeStatStore.end()) ? true : false);
for (uint32 i = 0; i < proto->StatsCount; ++i)
{
if(hasfakestats && sObjectMgr->_itemFakeStatStore[invItem->GetGUIDLow()].find(i) != sObjectMgr->_itemFakeStatStore[invItem->GetGUIDLow()].end())
continue; // already modded this stat, skip.
if(char* stat_name = GetStatName(proto->ItemStat[i].ItemStatType))
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, stat_name, SELECT_TOKEN_COST, i);
}
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TALK, "Back..", MAIN_MENU, 0);
player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
}
else
{
player->GetSession()->SendNotification("Item not reforgable");
OnGossipHello(player, creature);
}
}
else
{
player->GetSession()->SendNotification("No item selected");
OnGossipHello(player, creature);
}
break;
case SELECT_TOKEN_COST:
// action = stat_count_id
{
Item* invItem = selectedItem[player->GetGUIDLow()];
if (invItem && invItem->IsInWorld() && IsReforgable(invItem, player))
{
const ItemTemplate* proto = invItem->GetTemplate();
char label[250];
char popup[250];
snprintf(label, 250, "Selected item:\n%s", invItem->GetTemplate()->Name1.c_str());
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, label, sender, action);
snprintf(label, 250, "Selected stat: %s", stat_types[proto->ItemStat[action].ItemStatType].stat_name);
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, label, sender, action);
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_BATTLE, "Select amount to increase:", sender, action);
for (uint32 i = 1; i <= maxTokenAmount; ++i)
{
snprintf(label, 250, "Increase by %i for %u x %s", stat_types[proto->ItemStat[action].ItemStatType].stat_base_value*i, i, GetItemName(tokenEntry, player->GetSession()).c_str());
snprintf(popup, 250, "Are you sure?\nThe item is set untradeable and non-refundable\n\nCosts %u x %s", i, GetItemName(tokenEntry, player->GetSession()).c_str());
player->ADD_GOSSIP_ITEM_EXTENDED(GOSSIP_ICON_TRAINER, label, i, action, popup, 0, false); // sender = token_count, action = stat_count_id
}
player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TALK, "Back..", SELECT_STAT_INCREASE, invItem->GetSlot());
player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
}
else
{
player->GetSession()->SendNotification("Invalid item selected");
OnGossipHello(player, creature);
}
}
break;
default: // Reforge
// sender = token_count, action = stat_count_id
{
Item* invItem = selectedItem[player->GetGUIDLow()];
if (invItem && invItem->IsInWorld() && IsReforgable(invItem, player))
{
if(sender && sender <= maxTokenAmount)
{
if(player->HasItemCount(tokenEntry, sender))
{
player->DestroyItemCount(tokenEntry, sender, true);
UpdatePlayerReforgeStats(invItem, player, action, sender);
}
else
{
player->GetSession()->SendNotification("Not enough tokens");
OnGossipSelect(player, creature, SELECT_TOKEN_COST, action);
return true;
}
}
else
player->GetSession()->SendNotification("Invalid token count");
}
else
player->GetSession()->SendNotification("Invalid item selected");
OnGossipHello(player, creature);
}
}
return true;
}
private:
enum Senders
{
MAIN_MENU = GOSSIP_ACTION_INFO_DEF+1,
SELECT_ITEM,
SELECT_STAT_INCREASE,
SELECT_TOKEN_COST,
VENDOR = GOSSIP_ACTION_INFO_DEF+2,
REFORGE,
};
struct stat_format {char* stat_name; int32 stat_base_value; };
UNORDERED_MAP<uint32, struct stat_format> stat_types;
UNORDERED_MAP<uint32, Item*> selectedItem;
char* GetStatName(uint32 stat_type)
{
if(stat_types.find(stat_type) != stat_types.end())
return stat_types[stat_type].stat_name;
return NULL;
}
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_RANGED : return "Ranged";
case EQUIPMENT_SLOT_TABARD : return "Tabard";
default: return NULL;
}
}
std::string GetItemName(uint32 entry, WorldSession* session)
{
const ItemTemplate* itemTemplate = sObjectMgr->GetItemTemplate(entry);
std::string name = itemTemplate->Name1;
int loc_idx = session->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
if (ItemLocale const* il = sObjectMgr->GetItemLocale(itemTemplate->ItemId))
sObjectMgr->GetLocaleString(il->Name, loc_idx, name);
return name;
}
bool IsReforgable(Item* invItem, Player* player)
{
if(invItem->IsEquipped() && invItem->GetOwnerGUID() == player->GetGUID())
{
if(sObjectMgr->_itemFakeStatStore.find(invItem->GetGUIDLow()) != sObjectMgr->_itemFakeStatStore.end())
if(sObjectMgr->_itemFakeStatStore[invItem->GetGUIDLow()].size() >= maxReforges)
return false;
const ItemTemplate* proto = invItem->GetTemplate();
// block heirlooms necessary? probably. (their stats are handled differently)
if(proto->Quality != ITEM_QUALITY_HEIRLOOM && proto->StatsCount > 0)
{
for (uint32 i = 0; i < proto->StatsCount; ++i)
{
if(stat_types.find(proto->ItemStat[i].ItemStatType) == stat_types.end())
continue;
if(proto->ItemStat[i].ItemStatValue == 0)
continue;
if(sObjectMgr->_itemFakeStatStore.find(invItem->GetGUIDLow()) != sObjectMgr->_itemFakeStatStore.end())
{
if(sObjectMgr->_itemFakeStatStore[invItem->GetGUIDLow()].find(i) == sObjectMgr->_itemFakeStatStore[invItem->GetGUIDLow()].end())
return true; // found a stat that is not increased yet and is accepted by the script
}
else
return true; // the item is not reforged yet and a good stat was found
}
}
}
return false;
}
void UpdatePlayerReforgeStats(Item* invItem, Player* player, uint32 stat_count_id, uint32 token_count)
{
const ItemTemplate* proto = invItem->GetTemplate();
uint32 stat_diff = stat_types[proto->ItemStat[stat_count_id].ItemStatType].stat_base_value * token_count;
uint32 guidlow = invItem->GetGUIDLow();
// non tradeable and refundable
invItem->SetNotRefundable(player);
invItem->SetBinding(true);
// Update player stats
player->_ApplyItemMods(invItem, invItem->GetSlot(), false);
sObjectMgr->_itemFakeStatStore[guidlow][stat_count_id] = stat_diff; // save to container for item stats applying (used a lot)
playerItems_format temp = {invItem->GetEntry(), guidlow}; // temp var so we can insert our stuff to playerItems_format
playerItems[player->GetGUIDLow()].push_back(temp); // save entry and guid of the item so we can delete the item data on logout
player->_ApplyItemMods(invItem, invItem->GetSlot(), true);
// save to DB
player->SaveToDB(); // save player to DB too, since if a crash comes by, he will get a free reforge and no token removing is saved.
CharacterDatabase.PExecute("REPLACE INTO `custom_reforges` (`pGUID`, `Entry`, `iGUID`, `stat_count_id`, `stat_diff`) VALUES (%u, %u, %u, %u, %i)", player->GetGUIDLow(), invItem->GetEntry(), guidlow, stat_count_id, stat_diff);
SendReforgePackets(player);
}
};
void AddSC_REFORGER_NPC()
{
new REFORGER_NPC;
new REFORGING_DATA_HANDLER;
}