SHOW:
|
|
- or go back to the newest paste.
1 | /* | |
2 | Auto Mall | |
3 | ||
4 | Description: | |
5 | An NPC who, when talked to, allows you to enter an item NAME or ID, and will present you with all items matching that name or ID. | |
6 | The user is then able to purchase the found items. | |
7 | Also, the vendor displays a menu of | |
8 | ||
9 | This vendor is intended for Level 80 Funservers. | |
10 | ||
11 | By Jotox/Classic | |
12 | ||
13 | Copyright (c) 2009 | |
14 | */ | |
15 | ||
16 | //--------------------------- | |
17 | //CONFIG!! | |
18 | //--------------------------- | |
19 | ||
20 | //Max item quality allowed to be purchased. | |
21 | //Green = 2, Blue = 3, Epic = 4, Legendary = 5, Artifact(GM item) = 6, Heirloom = 7 | |
22 | #define MAX_QUALITY 4 //Default: Legendaries and above cannot be purchased. Epic and below can. | |
23 | ||
24 | //Allow item sets (such as tier 7), conjured items, or quest items to be purchased? | |
25 | //0 = Not allowed, 1 = Allowed. | |
26 | #define ALLOW_ITEM_SETS 1 | |
27 | #define ALLOW_CONJURED 0 | |
28 | #define ALLOW_QUEST 0 | |
29 | #define ALLOW_FREE 0 | |
30 | //Note: Free means no extended cost or gold cost. If you allow this, items which sell for more than they are purchased for will be allowed as well. | |
31 | ||
32 | //Enable Extended Cost items. Disabling causes extended cost items to be free (unless you add a gold price to them in your database). | |
33 | #define ENABLE_EXTENDED_COSTS 1 | |
34 | ||
35 | //Enable the banning of items via the "mall_banned_items" table. | |
36 | #define ENABLE_ITEM_BANNING 1 | |
37 | ||
38 | //Maximum number of items found per search. Note: The client can only handle 160 at a time; if more than 160 are found, everything beyond 160 will be discarded. | |
39 | #define MAX_RESULTS 300 | |
40 | ||
41 | //Mall menu -- Mounts and armor and weapons and such. | |
42 | #define ENABLE_MALL_MENU 1 | |
43 | ||
44 | //Item Searching -- Enter an item name or ID and it gives you the results | |
45 | #define ENABLE_ITEM_SEARCHING 1 | |
46 | ||
47 | //Enable bot-to-bot interaction for the portable verison of the script (Fighting enemy bots, buffing ally bots) | |
48 | #define ENABLE_BOT_FIGHTS 1 | |
49 | ||
50 | //NPC ID | |
51 | #define NPC_ID 993312 | |
52 | ||
53 | //Item ID (Item version of the vendor) | |
54 | #define ITEM_ID 99499 | |
55 | ||
56 | //Are you on Arcemu? (0 for Aspire, 1 for ArcEmu) | |
57 | #define ARCEMU 0 | |
58 | ||
59 | //--------------------------- | |
60 | //END CONFIG -- Do not edit below this if you do not know what you're doing. | |
61 | //--------------------------- | |
62 | ||
63 | #include "StdAfx.h" | |
64 | #include "Setup.h" | |
65 | using namespace std; | |
66 | ||
67 | //Arcemu support. | |
68 | #if ARCEMU > 0 | |
69 | typedef Object* ObjectPointer; | |
70 | typedef Player* PlayerPointer; | |
71 | typedef Unit* UnitPointer; | |
72 | typedef Creature* CreaturePointer; | |
73 | typedef Item* ItemPointer; | |
74 | typedef Spell* SpellPointer; | |
75 | #define TO_ITEM(x) static_cast< Item*>(x) | |
76 | #define HEARTHSTONE_TOLOWER(x) arcemu_TOLOWER(x) | |
77 | enum GossipIcons { | |
78 | GOSSIP_ICON_GOSSIP_NORMAL = 0, | |
79 | GOSSIP_ICON_GOSSIP_VENDOR = 1, | |
80 | GOSSIP_ICON_GOSSIP_FLIGHT = 2, | |
81 | GOSSIP_ICON_GOSSIP_TRAINER = 3, | |
82 | GOSSIP_ICON_GOSSIP_ENGINEER1 = 4, | |
83 | GOSSIP_ICON_GOSSIP_ENGINEER2 = 5, | |
84 | GOSSIP_ICON_GOSSIP_AUCTION = 6, | |
85 | GOSSIP_ICON_GOSSIP_EXTRA = 7, | |
86 | GOSSIP_ICON_GOSSIP_TABARD = 8, | |
87 | GOSSIP_ICON_GOSSIP_ARENA = 9, | |
88 | GOSSIP_ICON_GOSSIP_COIN = 9, | |
89 | }; | |
90 | #endif | |
91 | ||
92 | map< uint64, set<uint32> * > CItems;//List of a set of items for every creature GUID | |
93 | map< uint64, set<vector<uint32>*>* > CMenusList;//List of all menus that a creature already has added. | |
94 | map< uint32, uint32 > Extended_Cost_List;//List to keep track of extended cost. | |
95 | map< uint32, bool > banned_items;//List to keep track of banned items. | |
96 | map< uint64, ItemPointer> plr_items; | |
97 | ||
98 | #if ENABLE_MALL_MENU > 0 | |
99 | uint32 counter = 500; | |
100 | ||
101 | struct armor_struc { | |
102 | vector<uint32> head; | |
103 | vector<uint32> shoulder; | |
104 | vector<uint32> body; | |
105 | vector<uint32> wrist; | |
106 | vector<uint32> hands; | |
107 | vector<uint32> waist; | |
108 | vector<uint32> legs; | |
109 | vector<uint32> feet; | |
110 | }; | |
111 | ||
112 | armor_struc armor[4];// Cloth = 0, Leather = 1, Mail = 2, Plate = 3. Total of 4. | |
113 | ||
114 | map< string, uint32 > vector_map_map;//the uint32 points to the string's key in vector_map | |
115 | map< uint32, vector<uint32> *> vector_map; | |
116 | map< uint32, vector<uint32> *> armor_map; | |
117 | ||
118 | #define armor_CLOTH 0 | |
119 | #define armor_LEATHER 1 | |
120 | #define armor_MAIL 2 | |
121 | #define armor_PLATE 3 | |
122 | ||
123 | #endif | |
124 | ||
125 | ||
126 | #define BOT_BLUE 29280 | |
127 | #define BOT_RED 29281 | |
128 | #define BOT_BLANK 29279 | |
129 | ||
130 | #define SIDE_HORDE 1 | |
131 | #define SIDE_ALLIANCE 0 | |
132 | ||
133 | bool IsCustom( uint32 id ) | |
134 | { | |
135 | return id > 56000;//custom items can't be seen. | |
136 | } | |
137 | ||
138 | void AddItemsToCreature( CreaturePointer unit, vector<uint32> *items, bool is_menu = true ) | |
139 | { | |
140 | if( is_menu ) | |
141 | { | |
142 | if( !CMenusList[ unit->GetGUID() ] || CMenusList[ unit->GetGUID() ]->find( items ) != CMenusList[ unit->GetGUID() ]->end() ) | |
143 | return; | |
144 | CMenusList[ unit->GetGUID() ]->insert( items ); | |
145 | } | |
146 | for( vector<uint32>::iterator itr = items->begin(); itr != items->end(); itr++ ) | |
147 | { | |
148 | ItemPrototype * curItem = ItemPrototypeStorage.LookupEntry(*itr); | |
149 | ||
150 | if( !is_menu && CItems[ unit->GetGUID() ]->find( curItem->ItemId ) != CItems[ unit->GetGUID() ]->end() ) | |
151 | continue; | |
152 | ||
153 | #if ARCEMU > 0 | |
154 | unit->AddVendorItem( curItem->ItemId, curItem->MaxCount /*Full stack by default.*/, ( (Extended_Cost_List[curItem->ItemId ] ) ? dbcItemExtendedCost.LookupEntryForced( Extended_Cost_List[ curItem->ItemId ] ) : NULL) );//If we don't add this, when the player tries to buy it will give an error. | |
155 | #else | |
156 | unit->AddVendorItem( curItem->ItemId, curItem->MaxCount /*Full stack by default.*/ );//If we don't add this, when the player tries to buy it will give an error. | |
157 | if( Extended_Cost_List[ curItem->ItemId ] ) | |
158 | { | |
159 | CreatureItem ci; | |
160 | unit->GetSellItemByItemId( curItem->ItemId , ci); | |
161 | ci.extended_cost = dbcItemExtendedCost.LookupEntryForced( Extended_Cost_List[ curItem->ItemId ] ); | |
162 | } | |
163 | #endif | |
164 | ||
165 | CItems[ unit->GetGUID() ]->insert( curItem->ItemId ); | |
166 | } | |
167 | } | |
168 | ||
169 | void SendItemList( CreaturePointer unit, PlayerPointer plr, vector<uint32> * items )//Sends a Mall window with only the items in the "items" vector | |
170 | { | |
171 | if( items->size() < 1 ) | |
172 | return;//.......we shouldn't get here. | |
173 | if( !unit || !plr || !plr->IsInWorld() || !plr->GetSession() ) | |
174 | return;//AHH | |
175 | ||
176 | WorldPacket data(((items->size() * 28) + 9)); // allocate | |
177 | ||
178 | data.SetOpcode( SMSG_LIST_INVENTORY ); | |
179 | data << unit->GetGUID(); | |
180 | data << uint8( 0 ); // placeholder for item count | |
181 | ||
182 | uint32 counter = 0; | |
183 | ||
184 | ItemPrototype * curItem; | |
185 | ||
186 | for( vector<uint32>::iterator itr = items->begin(); itr != items->end(); itr++ ) | |
187 | { | |
188 | curItem = ItemPrototypeStorage.LookupEntry(*itr); | |
189 | ||
190 | if(curItem->AllowableClass && !(plr->getClassMask() & curItem->AllowableClass)) | |
191 | continue; | |
192 | if(curItem->AllowableRace && !(plr->getRaceMask() & curItem->AllowableRace)) | |
193 | continue; | |
194 | if(curItem->RequiredSkill && plr->_HasSkillLine(curItem->RequiredSkill) && plr->_GetSkillLineCurrent( curItem->RequiredSkill ) < curItem->RequiredSkillRank ) | |
195 | continue;//Only show useable recipes. | |
196 | ||
197 | data << (counter + 1);//Count | |
198 | data << curItem->ItemId;//Item id | |
199 | data << curItem->DisplayInfoID;//Display id | |
200 | data << int32(-1);//Available amount | |
201 | data << curItem->BuyPrice;//Buyprice | |
202 | data << int32(-1); // ??? | |
203 | data << curItem->MaxCount; | |
204 | ||
205 | if( Extended_Cost_List[ curItem->ItemId ] ) | |
206 | data << Extended_Cost_List[ curItem->ItemId ]; | |
207 | else | |
208 | data << uint32(0);//Extended cost | |
209 | ||
210 | if( counter == 150 )//15 pages is enough, eh? | |
211 | break; | |
212 | ++counter; | |
213 | } | |
214 | ||
215 | const_cast<uint8*>(data.contents())[8] = (uint8)counter; // set count | |
216 | ||
217 | if( plr->GetSession() ) | |
218 | plr->GetSession()->SendPacket( &data ); | |
219 | ||
220 | if( !counter ) | |
221 | unit->SendChatMessageToPlayer(15, 0, "I could not find an item available for your race and class.", plr);//Whisper, in universal language. | |
222 | } | |
223 | ||
224 | bool stringtest( string thing )//Tests whether a string consists SOLELY of numbers or not. returns true if the string is all numbers. | |
225 | { | |
226 | char str[256]; | |
227 | sprintf(str, "%d", atoi(thing.c_str()) );//If the string stays the same when all non-number characters are removed, it's only numbrs. duh. | |
228 | return str == thing; | |
229 | } | |
230 | ||
231 | bool TestItem( ItemPrototype * it ) | |
232 | { | |
233 | if( !it ) | |
234 | return false; | |
235 | ||
236 | if( IsCustom(it->ItemId) ) | |
237 | return false; | |
238 | ||
239 | if( !(ALLOW_ITEM_SETS) && it->ItemSet )//item sets | |
240 | return false; | |
241 | ||
242 | if( ( ( !(ALLOW_FREE) && (!it->BuyPrice) || it->BuyPrice < it->SellPrice) ) && !Extended_Cost_List[ it->ItemId ] )// free / sell for more than bought and no extended cost | |
243 | return false; | |
244 | ||
245 | if( it->Quality > MAX_QUALITY )//quality | |
246 | return false; | |
247 | ||
248 | if( (!(ALLOW_QUEST) && it->Flags & ITEM_FLAG_QUEST) || (!(ALLOW_CONJURED) && it->Flags & ITEM_FLAG_CONJURED) )//quest / conjured | |
249 | return false; | |
250 | ||
251 | if( !(ALLOW_QUEST) && it->QuestId )//Quest again | |
252 | return false; | |
253 | ||
254 | if( banned_items[ it->ItemId ] ) | |
255 | return false; | |
256 | ||
257 | return true; | |
258 | } | |
259 | ||
260 | bool TestItemEntry( uint32 entry ) | |
261 | { | |
262 | if( IsCustom(entry) ) | |
263 | return false; | |
264 | return TestItem( ItemPrototypeStorage.LookupEntry(entry) ); | |
265 | } | |
266 | ||
267 | void FindItem( UnitPointer unit, PlayerPointer plr, const char* nameC, vector<uint32> &items ) | |
268 | { | |
269 | ||
270 | string name( nameC ); | |
271 | ||
272 | int itemid = atoi(name.c_str()); | |
273 | if( itemid && !(itemid==INT_MIN || itemid==INT_MAX) && stringtest(name) ) | |
274 | { | |
275 | ItemPrototype * it = ItemPrototypeStorage.LookupEntry(itemid);//If it's just an item id, no point in using SQL. eh? | |
276 | ||
277 | if( it && TestItem(it) ) | |
278 | items.push_back(it->ItemId); | |
279 | ||
280 | if( !items.size() ) | |
281 | unit->SendChatMessageToPlayer(15, 0, "Your item was not found, or was not available for purchase. Please check you have the correct Item I.D. and try again.", plr);//Whisper, in universal language. | |
282 | ||
283 | return; | |
284 | } | |
285 | ||
286 | string x = name; | |
287 | HEARTHSTONE_TOLOWER(x); | |
288 | ||
289 | while( x.find("%") != string::npos ) | |
290 | x.erase( x.find("%") );//Remove wildcard characters. | |
291 | ||
292 | if(x.length() < 3) | |
293 | { | |
294 | unit->SendChatMessageToPlayer(15, 0, "Please enter a name 4 or more letters in length.", plr); | |
295 | return; | |
296 | } | |
297 | ||
298 | ItemPrototype * it; | |
299 | uint32 count = 0; | |
300 | ||
301 | QueryResult * result = WorldDatabase.Query("SELECT entry FROM items WHERE quality <= %u %s AND name1 like '%s' ORDER BY quality DESC, class, subclass LIMIT %u", MAX_QUALITY, ((ALLOW_ITEM_SETS) ? "" : "AND itemset = 0") ,("%" + WorldDatabase.EscapeString(x) + "%").c_str(), MAX_RESULTS ); | |
302 | ||
303 | if( !result ) | |
304 | { | |
305 | unit->SendChatMessageToPlayer(15, 0, "I'm sorry, I could not find your item. Please check the name and try again.", plr); | |
306 | return; | |
307 | } | |
308 | ||
309 | do | |
310 | { | |
311 | uint32 entry = result->Fetch()[0].GetUInt32(); | |
312 | it = ItemPrototypeStorage.LookupEntry( entry ); | |
313 | ||
314 | if( !it || !TestItem(it) ) | |
315 | continue; | |
316 | ||
317 | items.push_back(it->ItemId); | |
318 | } while (result->NextRow()); | |
319 | ||
320 | delete result; | |
321 | ||
322 | if( !items.size() ) | |
323 | unit->SendChatMessageToPlayer(15, 0, "I'm sorry, that item is unavailable for purchase.", plr); | |
324 | ||
325 | return; | |
326 | } | |
327 | ||
328 | void HandleRecipes(ObjectPointer pObject, PlayerPointer plr, uint32 Id, uint32 IntId, const char * Code) | |
329 | { | |
330 | GossipMenu *Menu; | |
331 | objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 44333225, plr); | |
332 | ||
333 | ||
334 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Alchemy Recipes", vector_map_map["Alchemy Recipes"]); | |
335 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Blacksmithing Recipes", vector_map_map["Blacksmithing Recipes"]); | |
336 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Cooking Recipes", vector_map_map["Cooking Recipes"]); | |
337 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Enchanting Recipes", vector_map_map["Enchanting Recipes"]); | |
338 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Engineering Recipes", vector_map_map["Engineering Recipes"]); | |
339 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Jewelcrafting Recipes", vector_map_map["Jewelcrafting Recipes"]); | |
340 | //Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Inscription Recipes", vector_map_map["Inscription Recipes"]); | |
341 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Leatherworking Recipes", vector_map_map["Leatherworking Recipes"]); | |
342 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TRAINER, "Tailoring Recipes", vector_map_map["Tailoring Recipes"]); | |
343 | ||
344 | ||
345 | Menu->AddItem( 7, "[Back]", 1055); | |
346 | ||
347 | Menu->SendTo(plr); | |
348 | ||
349 | } | |
350 | ||
351 | ||
352 | void HandleArmor(ObjectPointer pObject, PlayerPointer plr, uint32 Id, uint32 IntId, const char * Code) | |
353 | { | |
354 | ||
355 | if( IntId > 200 ) | |
356 | { | |
357 | vector<uint32> *vpointer = armor_map[ IntId ];//Just because references must be initialized. | |
358 | ||
359 | AddItemsToCreature( TO_CREATURE(pObject), vpointer ); | |
360 | SendItemList( TO_CREATURE(pObject), plr, vpointer );//Show the weapons. | |
361 | return; | |
362 | } | |
363 | ||
364 | GossipMenu *Menu; | |
365 | objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 44333225, plr); | |
366 | ||
367 | uint32 armor_class; | |
368 | ||
369 | switch( plr->getClass() ) | |
370 | { | |
371 | case DEATHKNIGHT: | |
372 | case WARRIOR: | |
373 | case PALADIN: | |
374 | armor_class = armor_PLATE; | |
375 | break; | |
376 | case HUNTER: | |
377 | case SHAMAN: | |
378 | armor_class = armor_MAIL; | |
379 | break; | |
380 | case DRUID: | |
381 | case ROGUE: | |
382 | armor_class = armor_LEATHER; | |
383 | break; | |
384 | default: | |
385 | armor_class = armor_CLOTH; | |
386 | } | |
387 | ||
388 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Helms", 200 + ((armor_class + 1) * 10)); | |
389 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Cloaks", vector_map_map["Cloaks"]); | |
390 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Shoulders", 200 + ((armor_class + 1) * 10) + 1); | |
391 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Chestpieces", 200 + ((armor_class + 1) * 10) + 2); | |
392 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Wristguards", 200 + ((armor_class + 1) * 10) + 3); | |
393 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Gloves", 200 + ((armor_class + 1) * 10) + 4); | |
394 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Waistbelts", 200 + ((armor_class + 1) * 10) + 5); | |
395 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Leggings", 200 + ((armor_class + 1) * 10) + 6); | |
396 | Menu->AddItem( GOSSIP_ICON_GOSSIP_FLIGHT, "Boots", 200 + ((armor_class + 1) * 10) + 7); | |
397 | Menu->AddItem( GOSSIP_ICON_GOSSIP_AUCTION, "Necklaces", vector_map_map["Necklaces"]); | |
398 | Menu->AddItem( GOSSIP_ICON_GOSSIP_AUCTION, "Rings", vector_map_map["Rings"]); | |
399 | Menu->AddItem( GOSSIP_ICON_GOSSIP_AUCTION, "Trinkets", vector_map_map["Trinkets"]); | |
400 | Menu->AddItem( GOSSIP_ICON_GOSSIP_ARENA, "Shields", vector_map_map["Shields"]); | |
401 | Menu->AddItem( GOSSIP_ICON_GOSSIP_ARENA, "Offhands", vector_map_map["Offhands"]); | |
402 | Menu->AddItem( GOSSIP_ICON_GOSSIP_TABARD, "Tabards", vector_map_map["Tabards"]); | |
403 | ||
404 | switch( plr->getClass() ) | |
405 | { | |
406 | case DEATHKNIGHT: | |
407 | Menu->AddItem( GOSSIP_ICON_GOSSIP_AUCTION, "Sigils", vector_map_map["Sigils"]); | |
408 | break; | |
409 | case DRUID: | |
410 | Menu->AddItem( GOSSIP_ICON_GOSSIP_AUCTION, "Idols", vector_map_map["Idols"]); | |
411 | break; | |
412 | case SHAMAN: | |
413 | Menu->AddItem( GOSSIP_ICON_GOSSIP_AUCTION, "Totems", vector_map_map["Totems"]); | |
414 | break; | |
415 | case PALADIN: | |
416 | Menu->AddItem( GOSSIP_ICON_GOSSIP_AUCTION, "Librams", vector_map_map["Librams"]); | |
417 | break; | |
418 | default: | |
419 | break; | |
420 | } | |
421 | ||
422 | ||
423 | Menu->AddItem( 7, "[Back]", 1055); | |
424 | ||
425 | Menu->SendTo(plr); | |
426 | ||
427 | } | |
428 | ||
429 | void HandleWeapons(ObjectPointer pObject, PlayerPointer plr, uint32 Id, uint32 IntId, const char * Code) | |
430 | { | |
431 | ||
432 | GossipMenu *Menu; | |
433 | objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 44333225, plr); | |
434 | ||
435 | Menu->AddItem( 9, "Two-Handed Swords", vector_map_map["Two-Handed Swords"]); | |
436 | Menu->AddItem( 9, "Two-Handed Maces", vector_map_map["Two-Handed Maces"]); | |
437 | Menu->AddItem( 9, "Two-Handed Axes", vector_map_map["Two-Handed Axes"]); | |
438 | Menu->AddItem( 9, "Polearms", vector_map_map["Polearms"]); | |
439 | Menu->AddItem( 9, "Staves", vector_map_map["Staves"]); | |
440 | ||
441 | Menu->AddItem( 6, "One-Handed Swords", vector_map_map["One-Handed Swords"]); | |
442 | Menu->AddItem( 6, "One-Handed Maces", vector_map_map["One-Handed Maces"]); | |
443 | Menu->AddItem( 6, "One-Handed Axes", vector_map_map["One-Handed Axes"]); | |
444 | Menu->AddItem( 6, "Daggers", vector_map_map["Daggers"]); | |
445 | Menu->AddItem( 6, "Fist Weapons", vector_map_map["Fist Weapons"]); | |
446 | ||
447 | Menu->AddItem( 2, "Guns", vector_map_map["Guns"]); | |
448 | Menu->AddItem( 2, "Bows", vector_map_map["Bows"]); | |
449 | Menu->AddItem( 2, "Crossbows", vector_map_map["Crossbows"]); | |
450 | Menu->AddItem( 2, "Wands", vector_map_map["Wands"]); | |
451 | Menu->AddItem( 2, "Thrown",vector_map_map["Thrown"]); | |
452 | ||
453 | Menu->AddItem( 7, "[Back]", 1055); | |
454 | ||
455 | Menu->SendTo(plr); | |
456 | ||
457 | } | |
458 | ||
459 | CreaturePointer make_creature( PlayerPointer plr ) | |
460 | { | |
461 | float angle = -(3.14159265) / 3; | |
462 | CreatureProto * proto = CreatureProtoStorage.LookupEntry( NPC_ID + RandomUInt(3) ); | |
463 | CreatureInfo * info = CreatureNameStorage.LookupEntry( NPC_ID + RandomUInt(3) ); | |
464 | ||
465 | LocationVector v = plr->GetPositionNC(); | |
466 | float m_followAngle = angle + v.o; | |
467 | float x = v.x +(3*(cosf(m_followAngle))); | |
468 | float y = v.y +(3*(sinf(m_followAngle))); | |
469 | CreaturePointer p = plr->GetMapMgr()->CreateCreature(NPC_ID); | |
470 | p->SetInstanceID(plr->GetMapMgr()->GetInstanceID()); | |
471 | p->Load(proto, x, y, v.z, angle);//Fix falling thru ground bug. | |
472 | ||
473 | p->SetZoneId(plr->GetZoneId()); | |
474 | ||
475 | p->GetAIInterface()->Init(p,AITYPE_PET,MOVEMENTTYPE_NONE,plr); | |
476 | p->GetAIInterface()->SetUnitToFollow(plr); | |
477 | p->GetAIInterface()->SetUnitToFollowAngle(angle); | |
478 | p->GetAIInterface()->SetFollowDistance(3.0f); | |
479 | ||
480 | p->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,35);//Players will see them as green, even though they're actually neutral. | |
481 | p->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_ATTACKABLE_9); | |
482 | ||
483 | p->SetUInt32Value(UNIT_FIELD_DISPLAYID, BOT_BLANK ); | |
484 | p->SetFloatValue( OBJECT_FIELD_SCALE_X, 1.1f );//To make him a reasonable size. Otherwise he would be 0.7f | |
485 | p->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, plr->GetGUID() ); | |
486 | //p->SetUInt64Value( UNIT_FIELD_CREATEDBY, plr->GetGUID() ); | |
487 | //p->SetUInt64Value(UNIT_FIELD_CBY, plr->GetGUID()); | |
488 | ||
489 | p->SetUInt32Value( UNIT_FIELD_HEALTH, 5000 );//Set combat stats. For pet combat :D | |
490 | p->SetUInt32Value( UNIT_FIELD_MAXHEALTH, 5000 ); | |
491 | p->SetUInt32Value( UNIT_FIELD_BASEATTACKTIME, 2000 + RandomUInt(100) ); | |
492 | p->SetFloatValue( UNIT_FIELD_MINDAMAGE, 100 ); | |
493 | p->SetFloatValue( UNIT_FIELD_MAXDAMAGE, 700 ); | |
494 | for( uint32 i = 0; i < 9; i++ ) | |
495 | p->SetUInt32Value( UNIT_FIELD_RESISTANCES + i, 0 );//the stationary version has all resistances 666, so let's set this to 0. | |
496 | ||
497 | ||
498 | p->PushToWorld(plr->GetMapMgr()); | |
499 | ||
500 | p->GetAIInterface()->MoveTo( v.x +(2.9*(cosf(m_followAngle))), v.y +(2.9*(sinf(m_followAngle))), v.z, angle);//Hackkkk | |
501 | ||
502 | #if ARCEMU == 0 | |
503 | plr->m_SummonSlots[ 6 ] = p; | |
504 | #endif | |
505 | ||
506 | return p; | |
507 | } | |
508 | ||
509 | class SCRIPT_DECL AutoItemMall : public GossipScript | |
510 | { | |
511 | public: | |
512 | ||
513 | void GossipHello(ObjectPointer pObject, PlayerPointer plr, bool AutoSend) | |
514 | { | |
515 | ||
516 | if( pObject->GetTypeId() == TYPEID_ITEM ) | |
517 | { | |
518 | ||
519 | if( !plr->Cooldown_CanCast(ItemPrototypeStorage.LookupEntry(ITEM_ID), 0) || !plr->Cooldown_CanCast(ItemPrototypeStorage.LookupEntry(ITEM_ID), 1) ) | |
520 | { | |
521 | if( plr->GetSession() ) | |
522 | plr->GetSession()->SendNotification("Your bot is still recharging!"); | |
523 | return; | |
524 | } | |
525 | ||
526 | for (Object::InRangeSet::iterator itr = plr->GetInRangeSetBegin(); itr != plr->GetInRangeSetEnd(); ++itr) | |
527 | { | |
528 | if( (*itr)->IsCreature() && TO_CREATURE(*itr)->GetEntry() >= NPC_ID && TO_CREATURE(*itr)->GetEntry() <= (NPC_ID+3) && (*itr)->GetUInt64Value( UNIT_FIELD_SUMMONEDBY ) == plr->GetGUID() ) | |
529 | { | |
530 | if( TO_CREATURE(*itr)->CombatStatus.IsInCombat() ) | |
531 | { | |
532 | string strings[3] = {"Can't you see I'm busy?", "Wait, I almost have this guy...", "You can't put me away now, I'm fighting!"}; | |
533 | TO_CREATURE(*itr)->SendChatMessageToPlayer(15, 0, strings[RandomUInt(2)].c_str(), plr); | |
534 | return; | |
535 | } | |
536 | TO_CREATURE(*itr)->SendChatMessageToPlayer(15, 0, "Goodbye!", plr); | |
537 | TO_CREATURE(*itr)->SummonExpire(); | |
538 | ItemPrototype * ip = ItemPrototypeStorage.LookupEntry(ITEM_ID); | |
539 | ||
540 | plr->Cooldown_AddItem( ip, 1);//Yay cooldowns! | |
541 | return; | |
542 | } | |
543 | }//Despawn existing vendors, if found. | |
544 | ||
545 | CreaturePointer VendorPersonal = make_creature( plr ); | |
546 | plr_items[ VendorPersonal->GetGUID() ] = TO_ITEM(pObject); | |
547 | ||
548 | VendorPersonal->SendChatMessageToPlayer(15, 0, "Thank you for summoning me! I'm your personal vendor, here for your assistance!", plr); | |
549 | ||
550 | return; | |
551 | } | |
552 | ||
553 | if( TO_CREATURE(pObject)->CombatStatus.IsInCombat() ) | |
554 | return; | |
555 | ||
556 | if( pObject->GetUInt64Value( UNIT_FIELD_SUMMONEDBY ) > 0 && pObject->GetUInt64Value( UNIT_FIELD_SUMMONEDBY ) != plr->GetGUID() ) | |
557 | { | |
558 | string strings[3] = {"You don't own me!", "My master says not to talk to strangers...", "I only talk to my master!"}; | |
559 | TO_UNIT(pObject)->SendChatMessageToPlayer(15, 0, strings[RandomUInt(2)].c_str(), plr); | |
560 | return; | |
561 | } | |
562 | ||
563 | GossipMenu *Menu; | |
564 | objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 44333225, plr); | |
565 | ||
566 | if( ENABLE_ITEM_SEARCHING ) | |
567 | { | |
568 | #if ARCEMU == 0 | |
569 | Menu->AddItem( 1, "Search for an item to purchase.", 1, true); | |
570 | #else | |
571 | Menu->AddItem( 1, "Search for an item to purchase.", 1, 1); | |
572 | #endif | |
573 | } | |
574 | ||
575 | ||
576 | #if ENABLE_MALL_MENU > 0 | |
577 | Menu->AddItem( 9, "Weapons", 100); | |
578 | Menu->AddItem( 9, "Armor", 200); | |
579 | Menu->AddItem( 3, "Professions", 334); | |
580 | Menu->AddItem( 2, "Mounts", vector_map_map["Mounts"]); | |
581 | Menu->AddItem( 2, "Personal Pets", vector_map_map["Personal Pets"]); | |
582 | Menu->AddItem( 1, "Bags", vector_map_map["Bags"]); | |
583 | ||
584 | #endif | |
585 | ||
586 | if( ENABLE_ITEM_SEARCHING ) | |
587 | Menu->AddItem( 0, "How does this Mall work?", 99); | |
588 | ||
589 | #if ENABLE_BOT_FIGHTS > 0 | |
590 | if( pObject->GetUInt64Value( UNIT_FIELD_SUMMONEDBY )==plr->GetGUID() && pObject->GetUInt32Value(UNIT_FIELD_DISPLAYID) == BOT_BLANK )//We're a bot, and we're not flagged yet. | |
591 | Menu->AddItem( 9, "|cffff0000Let Me Fight!", 69); | |
592 | if( pObject->GetUInt64Value( UNIT_FIELD_SUMMONEDBY )==plr->GetGUID() && pObject->GetUInt32Value(UNIT_FIELD_DISPLAYID) == BOT_RED )//We're a red bot. | |
593 | Menu->AddItem( 9, "|cff0099FFSwitch Fuels!", 70); | |
594 | if( pObject->GetUInt64Value( UNIT_FIELD_SUMMONEDBY )==plr->GetGUID() && pObject->GetUInt32Value(UNIT_FIELD_DISPLAYID) == BOT_BLUE )//We're a blue bot! :O | |
595 | Menu->AddItem( 9, "|cffff0000Switch Fuels!", 71); | |
596 | #endif | |
597 | ||
598 | if(AutoSend) | |
599 | Menu->SendTo(plr); | |
600 | } | |
601 | ||
602 | void GossipSelectOption(ObjectPointer pObject, PlayerPointer plr, uint32 Id, uint32 IntId, const char * Code) | |
603 | { | |
604 | if( TO_CREATURE(pObject)->CombatStatus.IsInCombat() ) | |
605 | { | |
606 | TO_UNIT(pObject)->SendChatMessageToPlayer(15, 0, "Can't you see I'm busy right now?", plr); | |
607 | plr->Gossip_Complete(); | |
608 | return; | |
609 | } | |
610 | ||
611 | if( IntId >= 500 && IntId < 1000 ) | |
612 | { | |
613 | vector<uint32> *vpointer = vector_map[ IntId ]; | |
614 | AddItemsToCreature( TO_CREATURE(pObject), vpointer ); | |
615 | SendItemList( TO_CREATURE(pObject), plr, vpointer );//Show them. | |
616 | return; | |
617 | } | |
618 | ||
619 | if( IntId >= 100 && IntId < 200 ) | |
620 | { | |
621 | HandleWeapons( pObject, plr, Id, IntId, Code ); | |
622 | return; | |
623 | } | |
624 | else if( IntId >= 200 && IntId < 300 ) | |
625 | { | |
626 | HandleArmor( pObject, plr, Id, IntId, Code ); | |
627 | return; | |
628 | } | |
629 | ||
630 | ||
631 | switch (IntId) | |
632 | { | |
633 | ||
634 | case 69: | |
635 | case 70: | |
636 | case 71: | |
637 | { | |
638 | plr->Gossip_Complete(); | |
639 | string message = ""; | |
640 | uint32 new_model; | |
641 | if( pObject->GetUInt32Value( UNIT_FIELD_DISPLAYID) == BOT_BLUE ) | |
642 | new_model = BOT_RED; | |
643 | if( pObject->GetUInt32Value( UNIT_FIELD_DISPLAYID) == BOT_RED ) | |
644 | new_model = BOT_BLUE; | |
645 | if( pObject->GetUInt32Value( UNIT_FIELD_DISPLAYID) == BOT_BLANK ) | |
646 | { | |
647 | new_model = RandomUInt(1) ? BOT_RED : BOT_BLUE; | |
648 | message = "Time to kick some ass!"; | |
649 | } | |
650 | pObject->SetUInt32Value( UNIT_FIELD_DISPLAYID, new_model ); | |
651 | if( message != "" ) | |
652 | TO_UNIT(pObject)->SendChatMessageToPlayer(15, 0, message.c_str(), plr); | |
653 | ||
654 | TO_UNIT(pObject)->CastSpell(TO_UNIT(pObject), pObject->GetUInt32Value( UNIT_FIELD_DISPLAYID) == BOT_BLUE ? 36881 : 43045, true); | |
655 | #if ARCEMU==0 | |
656 | //Something screwed up with aspire AI | |
657 | TO_UNIT(pObject)->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, pObject->GetUInt32Value( UNIT_FIELD_DISPLAYID) == BOT_BLUE ? 189 : 35);//189 = neutral, all-attackable faction, fix for aspire not being able to fight each other. | |
658 | TO_UNIT(pObject)->_setFaction(); | |
659 | TO_UNIT(pObject)->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,35);//Players will see them as green, even though they're actually neutral (if they're blue, at least) | |
660 | #endif | |
661 | ||
662 | }break; | |
663 | case 334://Random numbers. lol. whatever. | |
664 | { | |
665 | HandleRecipes( pObject, plr, Id, IntId, Code ); | |
666 | }break; | |
667 | case 1055://Back | |
668 | { | |
669 | GossipHello(pObject, plr, true); | |
670 | }break; | |
671 | case 99://Help pl0x | |
672 | { | |
673 | TO_UNIT(pObject)->SendChatMessageToPlayer(15, 0, "This vendor is an \"All-In-One\" mall vendor.", plr); | |
674 | TO_UNIT(pObject)->SendChatMessageToPlayer(15, 0, "Simply click on one of the options above to view my list of items, or click the \"search\" option and enter the |cffff33ffname|r of the item you're looking for.", plr); | |
675 | ||
676 | GossipHello(pObject, plr, true); | |
677 | }break; | |
678 | ||
679 | case 1://We're looking for an item. | |
680 | { | |
681 | vector<uint32> items; | |
682 | FindItem(TO_UNIT(pObject), plr, Code, items); | |
683 | if( !items.size() ) | |
684 | { | |
685 | GossipHello(pObject, plr, true); | |
686 | return; | |
687 | } | |
688 | ||
689 | AddItemsToCreature( TO_CREATURE( pObject ), &items, false );//Add the items to the creature's Mall list, so the player can actually buy the items instead of just view them | |
690 | ||
691 | SendItemList( TO_CREATURE(pObject), plr, &items );//Show him the list of items. | |
692 | ||
693 | items.clear(); | |
694 | }break; | |
695 | ||
696 | } | |
697 | } | |
698 | ||
699 | void GossipEnd(ObjectPointer pObject, PlayerPointer Plr) | |
700 | { | |
701 | TO_UNIT(pObject)->SendChatMessageToPlayer(15, 0, "Thank you for using the Auto Mall. Please Come Again!", Plr);//If you want, you may as well add your own message about voting on your server's website or somethin. | |
702 | Plr->CleanupGossipMenu(); | |
703 | } | |
704 | ||
705 | void Destroy() | |
706 | { | |
707 | delete this; | |
708 | } | |
709 | }; | |
710 | ||
711 | void LoadItemsFromQuery( char* query, vector<uint32> *items, bool test ) | |
712 | { | |
713 | if( !query || query == "" ) | |
714 | return; | |
715 | ||
716 | QueryResult* result = WorldDatabase.Query(query); | |
717 | if( !result ) | |
718 | printf("\n(Auto Mall): Note: Query: %s was empty (Ignore this if you're not a developer).\n", query); | |
719 | else | |
720 | { | |
721 | items->reserve(result->GetRowCount());//Optimization. | |
722 | do | |
723 | { | |
724 | uint32 id = result->Fetch()[0].GetUInt32(); | |
725 | ||
726 | if( IsCustom(id) ) | |
727 | continue; | |
728 | ||
729 | if( !test || TestItemEntry( id ) ) | |
730 | items->push_back( id ); | |
731 | ||
732 | } while (result->NextRow()); | |
733 | delete result; | |
734 | } | |
735 | ||
736 | } | |
737 | ||
738 | void LoadItemsFromQuery2( char* query, string label, bool test ) | |
739 | { | |
740 | vector_map[counter] = new vector<uint32>; | |
741 | LoadItemsFromQuery(query, vector_map[counter], test); | |
742 | vector_map_map[label] = counter; | |
743 | counter++; | |
744 | } | |
745 | ||
746 | void LoadArmorFromQuery( string subclass, string slot, vector<uint32> * items ) | |
747 | { | |
748 | string query = "SELECT entry FROM items WHERE class = 4 AND subclass = " + subclass + " AND inventorytype = " + slot + " AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC"; | |
749 | ||
750 | char * queryC; | |
751 | queryC = new char[query.length() + 1]; | |
752 | strcpy(queryC, query.c_str());//This is to convert from const char* (.c_str() returns const char) to char* | |
753 | ||
754 | LoadItemsFromQuery( queryC, items, true ); | |
755 | ||
756 | delete[] queryC;//Free. | |
757 | } | |
758 | ||
759 | void LoadItemTables() | |
760 | { | |
761 | QueryResult * result; | |
762 | ||
763 | WorldDatabase.Execute("DELETE FROM creature_spawns WHERE entry >= %u AND entry <= (%u + 3) AND (displayid = 29279 or displayid = 29280 or displayid = 29281);", NPC_ID, NPC_ID);//Just clean this up. | |
764 | ||
765 | if( ENABLE_EXTENDED_COSTS ) | |
766 | { | |
767 | #if ARCEMU==0 | |
768 | result = WorldDatabase.Query("SELECT DISTINCT item, extendedcost FROM vendors WHERE extendedcost > 1");//1 because of a bug with crappy DBs | |
769 | #else | |
770 | result = WorldDatabase.Query("SELECT DISTINCT item, extended_cost FROM vendors WHERE extended_cost > 1");//1 because of a bug with crappy DBs | |
771 | #endif | |
772 | if( !result ) | |
773 | printf("(Auto Mall): Error loading extended costs."); | |
774 | else | |
775 | { | |
776 | do | |
777 | { | |
778 | uint32 id = result->Fetch()[0].GetUInt32(); | |
779 | uint32 ec = result->Fetch()[1].GetUInt32(); | |
780 | if( dbcItemExtendedCost.LookupEntryForced(ec) ) | |
781 | Extended_Cost_List[ id ] = ec; | |
782 | ||
783 | } while (result->NextRow()); | |
784 | delete result; | |
785 | } | |
786 | } | |
787 | ||
788 | if( ENABLE_ITEM_BANNING ) | |
789 | { | |
790 | result = WorldDatabase.Query("SELECT * FROM mall_banned_items"); | |
791 | if( !result ) | |
792 | printf("(Auto Mall): Error loading banned items."); | |
793 | else | |
794 | { | |
795 | do | |
796 | { | |
797 | banned_items[ result->Fetch()[0].GetUInt32() ] = true; | |
798 | } while (result->NextRow()); | |
799 | ||
800 | delete result; | |
801 | } | |
802 | } | |
803 | ||
804 | if( ENABLE_MALL_MENU ) | |
805 | { | |
806 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 15 AND subclass = 5 ORDER BY quality DESC, buyprice DESC", "Mounts", true );//Mounts | |
807 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 15 AND (subclass = 2 or description = 'Teaches you how to summon this companion.') and description != '' ORDER BY quality DESC", "Personal Pets", false );//Pets | |
808 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 7 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "One-Handed Swords", true ); | |
809 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 8 AND requiredlevel >= 71 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Two-Handed Swords", true ); | |
810 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 4 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "One-Handed Maces", true ); | |
811 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 5 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Two-Handed Maces", true ); | |
812 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 0 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "One-Handed Axes", true ); | |
813 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 1 AND requiredlevel >= 71 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Two-Handed Axes", true ); | |
814 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 6 AND requiredlevel >= 71 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Polearms", true ); | |
815 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 10 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Staves", true ); | |
816 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 19 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Wands", true ); | |
817 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 3 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Guns", true ); | |
818 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 2 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Bows", true ); | |
819 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 18 AND requiredlevel >= 71 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Crossbows", true ); | |
820 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 13 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Fist Weapons", true ); | |
821 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 16 AND requiredlevel >= 71 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Thrown", true ); | |
822 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 2 AND subclass = 15 AND requiredlevel >= 71 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Daggers", true ); | |
823 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE containerslots > 16 AND quality >= 2 ORDER BY subclass, containerslots DESC", "Bags", true ); | |
824 | ||
825 | ||
826 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 6 ORDER BY quality DESC, RequiredSkillRank DESC", "Alchemy Recipes", true ); | |
827 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 4 ORDER BY quality DESC, RequiredSkillRank DESC", "Blacksmithing Recipes", true ); | |
828 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 5 ORDER BY quality DESC, RequiredSkillRank DESC", "Cooking Recipes", true ); | |
829 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 8 ORDER BY quality DESC, RequiredSkillRank DESC", "Enchanting Recipes", true ); | |
830 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 3 ORDER BY quality DESC, RequiredSkillRank DESC", "Engineering Recipes", true ); | |
831 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 2 ORDER BY quality DESC, RequiredSkillRank DESC", "Tailoring Recipes", true ); | |
832 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 1 ORDER BY quality DESC, RequiredSkillRank DESC", "Leatherworking Recipes", true ); | |
833 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 9 AND subclass = 10 ORDER BY quality DESC, RequiredSkillRank DESC", "Jewelcrafting Recipes", true ); | |
834 | ||
835 | for( uint8 i = 0; i <= armor_PLATE; i++ ) | |
836 | { | |
837 | armor_struc &armor_local = armor[i]; | |
838 | string strings[4]= {"1", "2", "3", "4"}; | |
839 | string subclass = strings[i]; | |
840 | LoadArmorFromQuery( subclass, "1", &armor_local.head ); | |
841 | LoadArmorFromQuery( subclass, "3", &armor_local.shoulder ); | |
842 | LoadArmorFromQuery( subclass, "5", &armor_local.body ); | |
843 | LoadArmorFromQuery( subclass, "20", &armor_local.body ); | |
844 | LoadArmorFromQuery( subclass, "9", &armor_local.wrist ); | |
845 | LoadArmorFromQuery( subclass, "6", &armor_local.waist ); | |
846 | LoadArmorFromQuery( subclass, "10", &armor_local.hands ); | |
847 | LoadArmorFromQuery( subclass, "7", &armor_local.legs ); | |
848 | LoadArmorFromQuery( subclass, "8", &armor_local.feet ); | |
849 | } | |
850 | ||
851 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 1 AND inventorytype = 16 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Cloaks", true); | |
852 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 0 AND inventorytype = 11 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Rings", true); | |
853 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 0 AND inventorytype = 12 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Trinkets", true); | |
854 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 6 AND inventorytype = 14 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Shields", true); | |
855 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 0 AND inventorytype = 23 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Offhands", true); | |
856 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 0 AND inventorytype = 2 AND requiredlevel >= 75 AND quality >= 3 ORDER BY quality DESC, itemlevel DESC", "Necklaces", true); | |
857 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND inventorytype = 19 ORDER BY quality DESC, itemlevel DESC", "Tabards", false ); | |
858 | ||
859 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 7 ORDER BY quality DESC, itemlevel DESC", "Librams", true ); | |
860 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 8 ORDER BY quality DESC, itemlevel DESC", "Idols", true ); | |
861 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 10 ORDER BY quality DESC, itemlevel DESC", "Sigils", true ); | |
862 | LoadItemsFromQuery2( "SELECT entry FROM items WHERE class = 4 AND subclass = 9 ORDER BY quality DESC, itemlevel DESC", "Totems", true ); | |
863 | ||
864 | ||
865 | ||
866 | //all the items are loaded!! Let's go! | |
867 | ||
868 | uint32 counter2 = 200; | |
869 | uint32 counter; | |
870 | for( uint32 i = 0; i < 4; i++ ) | |
871 | { | |
872 | counter2 += 10; | |
873 | counter = counter2; | |
874 | armor_map[ counter++ ] = &armor[i].head; | |
875 | armor_map[ counter++ ] = &armor[i].shoulder; | |
876 | armor_map[ counter++ ] = &armor[i].body; | |
877 | armor_map[ counter++ ] = &armor[i].wrist; | |
878 | armor_map[ counter++ ] = &armor[i].hands; | |
879 | armor_map[ counter++ ] = &armor[i].waist; | |
880 | armor_map[ counter++ ] = &armor[i].legs; | |
881 | armor_map[ counter++ ] = &armor[i].feet; | |
882 | } | |
883 | ||
884 | SpellEntry* sp = dbcSpell.LookupEntryForced(43045); | |
885 | if( sp ) | |
886 | sp->Effect[2] = 0;//Prevent crash. | |
887 | ||
888 | } | |
889 | ||
890 | } | |
891 | ||
892 | class VendorHook : public CreatureAIScript | |
893 | { | |
894 | public: | |
895 | ADD_CREATURE_FACTORY_FUNCTION(VendorHook); | |
896 | ||
897 | VendorHook(CreaturePointer pCreature) : CreatureAIScript(pCreature) | |
898 | { | |
899 | CItems[ _unit->GetGUID() ] = new set<uint32>; | |
900 | CMenusList[ _unit->GetGUID() ] = new set<vector<uint32>*>; | |
901 | attack_strings[0] = "C'mere, you little..."; | |
902 | attack_strings[1] = "I'll show you!"; | |
903 | attack_strings[2] = "Finally, some action!"; | |
904 | ||
905 | bored_strings[0] = "I'm bored...";//Bored messages. | |
906 | bored_strings[1] = "This is boring!"; | |
907 | bored_strings[2] = "Don't just stand there, do something!"; | |
908 | bored_strings[3] = "Why are you just standing there?"; | |
909 | bored_strings[4] = "Hey, wanna go get some of this? |cff00ff00|TInterface\\icons\\INV_MISC_FIREDANCER_01:48|t|r"; | |
910 | bored_strings[5] = "I'm made for selling, not standing around!"; | |
911 | ||
912 | bored_strings[6] = "I'm outta here. Bye!";//Exit messages. | |
913 | bored_strings[7] = "This is too boring for me. Cya!"; | |
914 | bored_strings[8] = "You can have fun standing there if you want, but I'm gonna go do something else."; | |
915 | ||
916 | last_move = getMSTime(); | |
917 | last_spoke = getMSTime(); | |
918 | x = y = z = 0.0f; | |
919 | RegisterAIUpdateEvent( 2000 );//Once every 2 seconds is plenty. No high CPU usage. at all. -- creature AI updates much more often than this. | |
920 | } | |
921 | ||
922 | ~VendorHook() | |
923 | { | |
924 | last_move = 0; | |
925 | last_spoke = 0; | |
926 | CMenusList[ _unit->GetGUID() ]->clear(); | |
927 | CItems[ _unit->GetGUID() ]->clear(); | |
928 | ||
929 | delete CMenusList[ _unit->GetGUID() ]; | |
930 | delete CItems[ _unit->GetGUID() ]; | |
931 | ||
932 | CMenusList[ _unit->GetGUID() ] = NULL; | |
933 | CItems[ _unit->GetGUID() ] = NULL; | |
934 | plr_items.erase( _unit->GetGUID() ); | |
935 | } | |
936 | ||
937 | void OnDied( UnitPointer killer ) | |
938 | { | |
939 | #if ARCEMU > 0 | |
940 | for (std::set<PlayerPointer>::iterator itr = _unit->GetInRangePlayerSetBegin(); itr != _unit->GetInRangePlayerSetEnd(); ++itr) | |
941 | { | |
942 | #else | |
943 | for(unordered_set<PlayerPointer>::iterator itr = _unit->GetInRangePlayerSetBegin(); itr != _unit->GetInRangePlayerSetEnd(); ++itr) | |
944 | { | |
945 | #endif | |
946 | if( (*itr)->GetGUID() == _unit->GetUInt64Value( UNIT_FIELD_SUMMONEDBY ) ) | |
947 | { | |
948 | ||
949 | ItemPrototype * ip = ItemPrototypeStorage.LookupEntry(ITEM_ID); | |
950 | ||
951 | #if ARCEMU==0 | |
952 | SpellPointer spell(new Spell((*itr), dbcSpell.LookupEntryForced( ip->Spells[0].Id ), false, NULLAURA)); | |
953 | #else | |
954 | SpellPointer spell(new Spell((*itr), dbcSpell.LookupEntryForced( ip->Spells[0].Id ), false, NULL)); | |
955 | ||
956 | #endif | |
957 | spell->i_caster = plr_items[_unit->GetGUID()]; | |
958 | SpellCastTargets target(0); | |
959 | target.m_targetMask |= TARGET_FLAG_UNIT; | |
960 | target.m_unitTarget = (*itr)->GetGUID(); | |
961 | spell->prepare(&target); | |
962 | ||
963 | ||
964 | TO_PLAYER(*itr)->Cooldown_AddItem( ip, 0);//Yay cooldowns! | |
965 | break; | |
966 | } | |
967 | } | |
968 | ||
969 | _unit->SendChatMessage(CHAT_MSG_MONSTER_SAY, LANG_UNIVERSAL, "AHHHHH!"); | |
970 | RemoveAIUpdateEvent(); | |
971 | _unit->SummonExpire(); | |
972 | if( killer->isAlive() ) | |
973 | { | |
974 | killer->CastSpell( killer, 55420, true);//Fireworks for the winner! yay | |
975 | #if ARCEMu==0 | |
976 | //Aspire: need to regenerate the bots. arcemu, they regen fine. | |
977 | killer->SetUInt32Value(UNIT_FIELD_HEALTH, 5000); | |
978 | killer->SetUInt32Value(UNIT_FIELD_POWER1, 99999); | |
979 | #endif | |
980 | } | |
981 | } | |
982 | ||
983 | void Say_Bored() | |
984 | { | |
985 | if( getMSTime() > (last_move + 1000*60*15) && !RandomUInt(20))//Our owner is AFK for more than 15 minutes. Despawn us. (So we don't just keep spamming bored messages.) | |
986 | { | |
987 | _unit->SendChatMessage(CHAT_MSG_MONSTER_SAY, LANG_UNIVERSAL, bored_strings[RandomUInt(2) + 6].c_str()); | |
988 | _unit->SummonExpire(); | |
989 | return; | |
990 | } | |
991 | if( getMSTime() < (last_move + 1000*60*7) || getMSTime() < (last_spoke + 1000*60*8) ) | |
992 | return; | |
993 | if( _unit->CombatStatus.IsInCombat() || !RandomUInt(5))//Random -- to add a little variance. | |
994 | return;//How can a fighting bot be bored? :P | |
995 | _unit->SendChatMessage(CHAT_MSG_MONSTER_SAY, LANG_UNIVERSAL, bored_strings[RandomUInt(5)].c_str()); | |
996 | last_spoke = getMSTime(); | |
997 | } | |
998 | ||
999 | void AIUpdate() | |
1000 | { | |
1001 | if( !_unit->GetUInt64Value( UNIT_FIELD_SUMMONEDBY ) && (_unit->GetUInt32Value( UNIT_FIELD_DISPLAYID ) == BOT_BLANK ||_unit->GetUInt32Value( UNIT_FIELD_DISPLAYID ) == BOT_RED || _unit->GetUInt32Value( UNIT_FIELD_DISPLAYID )== BOT_BLUE ) ) | |
1002 | _unit->SummonExpire();//Despawn us if we're a bot but don't have an owner. This is caused by server crashes while bots are out -- the bots remain when the server comes back up, even if their owner's gone. | |
1003 | if( !_unit->GetUInt64Value( UNIT_FIELD_SUMMONEDBY ) ) | |
1004 | return; | |
1005 | ||
1006 | if( _unit->CombatStatus.IsInCombat() && _unit->GetAIInterface()->GetNextTarget() && _unit->GetAIInterface()->GetNextTarget()->IsPlayer() ) | |
1007 | _unit->CombatStatus.RemoveAttackTarget(_unit->GetAIInterface()->GetNextTarget()); | |
1008 | ||
1009 | Say_Bored(); | |
1010 | ||
1011 | if( !_unit->CombatStatus.IsInCombat() && _unit->GetUInt32Value( UNIT_FIELD_DISPLAYID ) != BOT_BLANK ) | |
1012 | { | |
1013 | for (Object::InRangeSet::iterator itr = _unit->GetInRangeSetBegin(); itr != _unit->GetInRangeSetEnd(); ++itr) | |
1014 | { | |
1015 | if( (*itr)->IsCreature() && TO_CREATURE(*itr)->GetEntry() >= NPC_ID && TO_CREATURE(*itr)->GetEntry() <= (NPC_ID+3) && ((*itr)->GetUInt32Value( UNIT_FIELD_DISPLAYID ) == BOT_RED || (*itr)->GetUInt32Value( UNIT_FIELD_DISPLAYID ) == BOT_BLUE) && (*itr)->GetUInt32Value( UNIT_FIELD_DISPLAYID ) != _unit->GetUInt32Value( UNIT_FIELD_DISPLAYID ) && _unit->GetDistance2dSq(*itr) < 10.0*10.0 && !TO_CREATURE(*itr)->CombatStatus.IsInCombat() ) | |
1016 | { | |
1017 | //WE FOUND AN ENEMY BOT. ATTACK!! | |
1018 | _unit->GetAIInterface()->AttackReaction(TO_CREATURE(*itr), 1, 0); | |
1019 | TO_CREATURE(*itr)->GetAIInterface()->AttackReaction(_unit, 1, 0); | |
1020 | TO_CREATURE(*itr)->GetAIInterface()->StopMovement(500);//Make em stop. fix bug. | |
1021 | _unit->SendChatMessage(CHAT_MSG_MONSTER_SAY, LANG_UNIVERSAL, attack_strings[RandomUInt(2)].c_str() ); | |
1022 | break; | |
1023 | } | |
1024 | } | |
1025 | } | |
1026 | ||
1027 | if( _unit->CombatStatus.IsInCombat() && (last_spell + 8*1000) < getMSTime() && (RandomUInt(1000) > 650 ) ) | |
1028 | { | |
1029 | last_spell = getMSTime(); | |
1030 | _unit->CastSpell( _unit->GetAIInterface()->GetNextTarget() , _unit->GetUInt32Value(UNIT_FIELD_DISPLAYID) == BOT_RED ? 49232 : 49236, true);//Yay cast spell! flame shock / frost shock. | |
1031 | } | |
1032 | ||
1033 | #if ARCEMU > 0 | |
1034 | for (std::set<PlayerPointer>::iterator itr = _unit->GetInRangePlayerSetBegin(); itr != _unit->GetInRangePlayerSetEnd(); ++itr) | |
1035 | { | |
1036 | #else | |
1037 | for(unordered_set<PlayerPointer>::iterator itr = _unit->GetInRangePlayerSetBegin(); itr != _unit->GetInRangePlayerSetEnd(); ++itr) | |
1038 | { | |
1039 | #endif | |
1040 | if( (*itr)->GetGUID() == _unit->GetUInt64Value( UNIT_FIELD_SUMMONEDBY ) ) | |
1041 | { | |
1042 | if( _unit->GetDistance2dSq(*itr) > 30.0*30.0 ) | |
1043 | break;//Our owner is WAY far away. we may as well despawn. | |
1044 | #if ARCEMU == 0 | |
1045 | if( (*itr)->IsMounted() && ((*itr)->m_FlyingAura || _unit->GetDistance2dSq(*itr) > 15.0*15.0 ) )//If they're mounted and flying || moving too fast to catch up with em, we need to be deleted. | |
1046 | break; | |
1047 | #else | |
1048 | if( (*itr)->IsMounted() && ((*itr)->flying_aura || _unit->GetDistance2dSq(*itr) > 15.0*15.0 ) )//If they're mounted and flying || moving too fast to catch up with em, we need to be deleted. | |
1049 | break; | |
1050 | #endif | |
1051 | else | |
1052 | { | |
1053 | if( TO_PLAYER(*itr)->m_isMoving || (x >= TO_PLAYER(*itr)->GetPositionX() + 0.1 || x <= TO_PLAYER(*itr)->GetPositionX() - 0.1)/*fixes a bug*/ || (y >= TO_PLAYER(*itr)->GetPositionY() + 0.1 || y <= TO_PLAYER(*itr)->GetPositionY() - 0.1) || (z >= TO_PLAYER(*itr)->GetPositionZ() + 0.1 || z <= TO_PLAYER(*itr)->GetPositionZ() - 0.1)) | |
1054 | { | |
1055 | last_move = getMSTime(); | |
1056 | x = TO_PLAYER(*itr)->GetPositionX(); | |
1057 | y = TO_PLAYER(*itr)->GetPositionY(); | |
1058 | z = TO_PLAYER(*itr)->GetPositionZ(); | |
1059 | } | |
1060 | return; | |
1061 | } | |
1062 | } | |
1063 | } | |
1064 | _unit->SummonExpire();//If our owner ain't found, delete us. | |
1065 | } | |
1066 | ||
1067 | uint32 last_spoke;// | |
1068 | uint32 last_move;//Keep track of whether or not our owner is idle. If the owner's idle, we say stuff :) (Just to keep it interesting) | |
1069 | float x,y,z; | |
1070 | string attack_strings[3]; | |
1071 | string bored_strings[9]; | |
1072 | uint32 last_spell; | |
1073 | }; | |
1074 | ||
1075 | void SetupVendorScript(ScriptMgr * mgr) | |
1076 | { | |
1077 | for( uint32 i = 0; i < 4; i++ ) | |
1078 | { | |
1079 | mgr->register_gossip_script(NPC_ID + i, (GossipScript*) new AutoItemMall() ); | |
1080 | mgr->register_creature_script(NPC_ID + i, &VendorHook::Create); | |
1081 | } | |
1082 | ||
1083 | mgr->register_item_gossip_script(ITEM_ID, (GossipScript*) new AutoItemMall() ); | |
1084 | ||
1085 | LoadItemTables(); | |
1086 | ||
1087 | printf("\nAuto Mall Loaded Successfully.\n"); | |
1088 | } |