View difference between Paste ID: gv1BpNmb and f1b132bb3
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
}