1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: shops.cpp
5 	Desc: support functions for shop (setup, some comm)
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "main.hpp"
13 #include "game.hpp"
14 #include "stat.hpp"
15 #include "items.hpp"
16 #include "sound.hpp"
17 #include "shops.hpp"
18 #include "interface/interface.hpp"
19 #include "net.hpp"
20 #include "player.hpp"
21 #include "scores.hpp"
22 
23 list_t* shopInv = NULL;
24 Uint32 shopkeeper = 0;
25 Uint32 shoptimer = 0;
26 char* shopspeech = NULL;
27 int shopinventorycategory = 7;
28 int shopitemscroll;
29 Item* shopinvitems[4];
30 Item* sellitem = NULL;
31 int shopkeepertype = 0;
32 char* shopkeepername = NULL;
33 char shopkeepername_client[64];
34 
35 int selectedShopSlot = -1;
36 std::unordered_map<int, std::unordered_set<int>> shopkeeperMysteriousItems(
37 {
38 	{ ARTIFACT_ORB_GREEN, { ARTIFACT_BOW, QUIVER_HUNTING, QUIVER_PIERCE } },
39 	{ ARTIFACT_ORB_BLUE, { ARTIFACT_MACE, ENCHANTED_FEATHER } },
40 	{ ARTIFACT_ORB_RED, { CRYSTAL_SWORD, CRYSTAL_BATTLEAXE, CRYSTAL_SPEAR, CRYSTAL_MACE } }
41 });
42 
43 /*-------------------------------------------------------------------------------
44 
45 	startTradingServer
46 
47 	called on server, initiates trade sequence between player and NPC
48 
49 -------------------------------------------------------------------------------*/
50 
startTradingServer(Entity * entity,int player)51 void startTradingServer(Entity* entity, int player)
52 {
53 	if (!entity || player < 0)
54 	{
55 		return;
56 	}
57 	if ( multiplayer == CLIENT )
58 	{
59 		return;
60 	}
61 	if (!players[player] || !players[player]->entity)
62 	{
63 		return;
64 	}
65 
66 	Stat* stats = entity->getStats();
67 	if ( stats == NULL )
68 	{
69 		return;
70 	}
71 
72 	if ( player == 0 )
73 	{
74 		closeAllGUIs(DONT_CHANGE_SHOOTMODE, CLOSEGUI_DONT_CLOSE_SHOP);
75 		openStatusScreen(GUI_MODE_SHOP, INVENTORY_MODE_ITEM);
76 		shopInv = &stats->inventory;
77 		shopkeeper = entity->getUID();
78 		shoptimer = ticks - 1;
79 		shopspeech = language[194 + rand() % 3];
80 		shopinventorycategory = 7;
81 		sellitem = NULL;
82 		Entity* entity = uidToEntity(shopkeeper);
83 		if ( entity )
84 		{
85 			shopkeepertype = entity->monsterStoreType;
86 		}
87 		shopkeepername = stats->name;
88 		shopitemscroll = 0;
89 
90 		//Initialize shop gamepad code here.
91 		if ( shopinvitems[0] != nullptr )
92 		{
93 			selectedShopSlot = 0;
94 			warpMouseToSelectedShopSlot();
95 		}
96 		else
97 		{
98 			selectedShopSlot = -1;
99 		}
100 	}
101 	else if ( multiplayer == SERVER )
102 	{
103 		// open shop on client
104 		Stat* entitystats = entity->getStats();
105 		strcpy((char*)net_packet->data, "SHOP");
106 		SDLNet_Write32((Uint32)entity->getUID(), &net_packet->data[4]);
107 		net_packet->data[8] = entity->monsterStoreType;
108 		strcpy((char*)(&net_packet->data[9]), entitystats->name);
109 		net_packet->data[9 + strlen(entitystats->name)] = 0;
110 		net_packet->address.host = net_clients[player - 1].host;
111 		net_packet->address.port = net_clients[player - 1].port;
112 		net_packet->len = 9 + strlen(entitystats->name) + 1;
113 		sendPacketSafe(net_sock, -1, net_packet, player - 1);
114 
115 		// fill client's shop inventory with items
116 		node_t* node;
117 		for ( node = entitystats->inventory.first; node != NULL; node = node->next )
118 		{
119 			Item* item = (Item*)node->element;
120 			strcpy((char*)net_packet->data, "SHPI");
121 			SDLNet_Write32(item->type, &net_packet->data[4]);
122 			net_packet->data[8] = (char)item->status;
123 			net_packet->data[9] = (char)item->beatitude;
124 			net_packet->data[10] = (unsigned char)item->count;
125 			SDLNet_Write32((Uint32)item->appearance, &net_packet->data[11]);
126 			if ( item->identified )
127 			{
128 				net_packet->data[15] = 1;
129 			}
130 			else
131 			{
132 				net_packet->data[15] = 0;
133 			}
134 			net_packet->address.host = net_clients[player - 1].host;
135 			net_packet->address.port = net_clients[player - 1].port;
136 			net_packet->len = 16;
137 			sendPacketSafe(net_sock, -1, net_packet, player - 1);
138 		}
139 	}
140 	entity->skill[0] = 4; // talk state
141 	entity->skill[1] = players[player]->entity->getUID();
142 	messagePlayer(player, language[1122], stats->name);
143 }
144 
145 /*-------------------------------------------------------------------------------
146 
147 	buyItemFromShop
148 
149 	buys the given item from the currently open shop
150 
151 -------------------------------------------------------------------------------*/
152 
buyItemFromShop(Item * item)153 void buyItemFromShop(Item* item)
154 {
155 	if ( !item )
156 	{
157 		return;
158 	}
159 
160 	if ( stats[clientnum]->GOLD >= item->buyValue(clientnum) )
161 	{
162 		if ( items[item->type].value * 1.5 >= item->buyValue(clientnum) )
163 		{
164 			shopspeech = language[200 + rand() % 3];
165 		}
166 		else
167 		{
168 			shopspeech = language[197 + rand() % 3];
169 		}
170 		shoptimer = ticks - 1;
171 		Item* itemToPickup = newItem(item->type, item->status, item->beatitude, 1, item->appearance, item->identified, nullptr);
172 		if ( itemTypeIsQuiver(item->type) )
173 		{
174 			itemToPickup->count = item->count;
175 		}
176 		itemPickup(clientnum, itemToPickup);
177 
178 		stats[clientnum]->GOLD -= item->buyValue(clientnum);
179 
180 		if ( stats[clientnum]->playerRace > 0 && players[clientnum] && players[clientnum]->entity->effectPolymorph > NUMMONSTERS )
181 		{
182 			steamStatisticUpdate(STEAM_STAT_ALTER_EGO, STEAM_STAT_INT, item->buyValue(clientnum));
183 		}
184 
185 		playSound(89, 64);
186 		int ocount = item->count;
187 		if ( !itemTypeIsQuiver(item->type) )
188 		{
189 			item->count = 1;
190 		}
191 		messagePlayer(clientnum, language[1123], item->description(), item->buyValue(clientnum));
192 		item->count = ocount;
193 		if ( multiplayer != CLIENT )
194 		{
195 			Entity* entity = uidToEntity(shopkeeper);
196 			if (entity)
197 			{
198 				Stat* shopstats = entity->getStats();
199 				shopstats->GOLD += item->buyValue(clientnum);
200 			}
201 			if ( rand() % 2 )
202 			{
203 				if ( item->buyValue(clientnum) <= 1 )
204 				{
205 					// buying cheap items does not increase trading past basic
206 					if ( stats[clientnum]->PROFICIENCIES[PRO_TRADING] < SKILL_LEVEL_SKILLED )
207 					{
208 						players[clientnum]->entity->increaseSkill(PRO_TRADING);
209 					}
210 				}
211 				else
212 				{
213 					players[clientnum]->entity->increaseSkill(PRO_TRADING);
214 				}
215 			}
216 			else if ( item->buyValue(clientnum) >= 150 )
217 			{
218 				if ( item->buyValue(clientnum) >= 300 || rand() % 2 )
219 				{
220 					players[clientnum]->entity->increaseSkill(PRO_TRADING);
221 				}
222 			}
223 		}
224 		else
225 		{
226 			strcpy((char*)net_packet->data, "SHPB");
227 			SDLNet_Write32(shopkeeper, &net_packet->data[4]);
228 
229 			// send item that was bought to server
230 			SDLNet_Write32(item->type, &net_packet->data[8]);
231 			SDLNet_Write32(item->status, &net_packet->data[12]);
232 			SDLNet_Write32(item->beatitude, &net_packet->data[16]);
233 			SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
234 			if ( itemTypeIsQuiver(item->type) )
235 			{
236 				SDLNet_Write32((Uint32)item->count, &net_packet->data[24]);
237 			}
238 			else
239 			{
240 				SDLNet_Write32((Uint32)(1), &net_packet->data[24]);
241 			}
242 			if ( item->identified )
243 			{
244 				net_packet->data[28] = 1;
245 			}
246 			else
247 			{
248 				net_packet->data[28] = 0;
249 			}
250 			net_packet->data[29] = clientnum;
251 			net_packet->address.host = net_server.host;
252 			net_packet->address.port = net_server.port;
253 			net_packet->len = 30;
254 			sendPacketSafe(net_sock, -1, net_packet, 0);
255 		}
256 		if ( shopIsMysteriousShopkeeper(uidToEntity(shopkeeper)) )
257 		{
258 			buyItemFromMysteriousShopkeepConsumeOrb(*(uidToEntity(shopkeeper)), *item);
259 		}
260 		if ( itemTypeIsQuiver(item->type) )
261 		{
262 			item->count = 1; // so we consume it all up.
263 		}
264 		consumeItem(item, clientnum);
265 	}
266 	else
267 	{
268 		shopspeech = language[203 + rand() % 3];
269 		shoptimer = ticks - 1;
270 		playSound(90, 64);
271 	}
272 }
273 
274 /*-------------------------------------------------------------------------------
275 
276 	sellItemToShop
277 
278 	sells the given item to the currently open shop
279 
280 -------------------------------------------------------------------------------*/
281 
sellItemToShop(Item * item)282 void sellItemToShop(Item* item)
283 {
284 	if ( !item )
285 	{
286 		return;
287 	}
288 	if ( ((item->beatitude < 0 && !shouldInvertEquipmentBeatitude(stats[clientnum]))
289 		|| (item->beatitude > 0 && shouldInvertEquipmentBeatitude(stats[clientnum])))
290 		&& itemIsEquipped(item, clientnum) )
291 	{
292 		if ( item->beatitude > 0 )
293 		{
294 			messagePlayer(clientnum, language[3219], item->getName());
295 		}
296 		else
297 		{
298 			messagePlayer(clientnum, language[1124], item->getName());
299 		}
300 		playSound(90, 64);
301 		return;
302 	}
303 
304 	bool deal = true;
305 	if ( stats[clientnum]->PROFICIENCIES[PRO_TRADING] >= CAPSTONE_UNLOCK_LEVEL[PRO_TRADING] )
306 	{
307 		//Skill capstone: Can sell anything to any shop.
308 		if (shopkeepertype == 10)
309 		{
310 			deal = false;
311 		}
312 	}
313 	else
314 	{
315 		switch ( shopkeepertype )
316 		{
317 			case 0: // arms & armor
318 				if ( itemCategory(item) != WEAPON && itemCategory(item) != ARMOR && itemCategory(item) != THROWN )
319 				{
320 					deal = false;
321 				}
322 				break;
323 			case 1: // hats
324 				if ( itemCategory(item) != ARMOR )
325 				{
326 					deal = false;
327 				}
328 				break;
329 			case 2: // jewelry
330 				if ( itemCategory(item) != RING && itemCategory(item) != AMULET && itemCategory(item) != GEM )
331 				{
332 					deal = false;
333 				}
334 				break;
335 			case 3: // bookstore
336 				if ( itemCategory(item) != SPELLBOOK && itemCategory(item) != SCROLL && itemCategory(item) != BOOK )
337 				{
338 					deal = false;
339 				}
340 				break;
341 			case 4: // potion shop
342 				if ( item->type != TOOL_ALEMBIC && itemCategory(item) != POTION )
343 				{
344 					deal = false;
345 				}
346 				break;
347 			case 5: // magicstaffs
348 				if ( itemCategory(item) != MAGICSTAFF )
349 				{
350 					deal = false;
351 				}
352 				break;
353 			case 6: // food
354 				if ( itemCategory(item) != FOOD )
355 				{
356 					deal = false;
357 				}
358 				break;
359 			case 7: // tools
360 				if ( itemCategory(item) != TOOL && itemCategory(item) != THROWN )
361 				{
362 					deal = false;
363 				}
364 				break;
365 			case 8: // hunting
366 				if ( itemCategory(item) != WEAPON
367 					&& itemCategory(item) != THROWN
368 					&& !itemTypeIsQuiver(item->type)
369 					&& item->type != BRASS_KNUCKLES
370 					&& item->type != IRON_KNUCKLES
371 					&& item->type != SPIKED_GAUNTLETS )
372 				{
373 					deal = false;
374 				}
375 				break;
376 			case 10:
377 				deal = false;
378 				break;
379 			default:
380 				break;
381 		}
382 	}
383 
384 	if ( itemIsEquipped(item, clientnum) )
385 	{
386 		if ( itemCategory(item) == GEM
387 			|| itemCategory(item) == RING
388 			|| itemCategory(item) == AMULET )
389 		{
390 			shopspeech = language[3914];
391 		}
392 		else if ( itemCategory(item) == SPELLBOOK
393 			|| itemCategory(item) == BOOK
394 			|| itemCategory(item) == SCROLL )
395 		{
396 			shopspeech = language[3915];
397 		}
398 		else if ( itemCategory(item) == WEAPON
399 			|| itemCategory(item) == MAGICSTAFF
400 			|| itemCategory(item) == THROWN )
401 		{
402 			shopspeech = language[3911];
403 		}
404 		else if ( itemCategory(item) == ARMOR )
405 		{
406 			shopspeech = language[3910];
407 		}
408 		else if ( itemCategory(item) == TOOL )
409 		{
410 			shopspeech = language[3912];
411 		}
412 		else if ( itemCategory(item) == POTION )
413 		{
414 			shopspeech = language[3913];
415 		}
416 		else if ( itemCategory(item) == FOOD )
417 		{
418 			shopspeech = language[3916];
419 		}
420 		shoptimer = ticks - 1;
421 		playSound(90, 64);
422 		return;
423 	}
424 
425 	if ( !deal )
426 	{
427 		shopspeech = language[212 + rand() % 3];
428 		shoptimer = ticks - 1;
429 		playSound(90, 64);
430 		return;
431 	}
432 
433 	if ( items[item->type].value * .75 <= item->sellValue(clientnum) )
434 	{
435 		shopspeech = language[209 + rand() % 3];
436 	}
437 	else
438 	{
439 		shopspeech = language[206 + rand() % 3];
440 	}
441 	shoptimer = ticks - 1;
442 	Item* sold = newItem(item->type, item->status, item->beatitude, 1, item->appearance, item->identified, shopInv);
443 	if ( itemTypeIsQuiver(item->type) )
444 	{
445 		sold->count = item->count;
446 	}
447 	stats[clientnum]->GOLD += item->sellValue(clientnum);
448 
449 	if ( stats[clientnum]->playerRace > 0 && players[clientnum] && players[clientnum]->entity->effectPolymorph > NUMMONSTERS )
450 	{
451 		steamStatisticUpdate(STEAM_STAT_ALTER_EGO, STEAM_STAT_INT, item->sellValue(clientnum));
452 	}
453 
454 	playSound(89, 64);
455 	int ocount = item->count;
456 	if ( !itemTypeIsQuiver(item->type) )
457 	{
458 		item->count = 1;
459 	}
460 	messagePlayer(clientnum, language[1125], item->description(), item->sellValue(clientnum));
461 	item->count = ocount;
462 	if ( multiplayer != CLIENT )
463 	{
464 		if ( rand() % 2 )
465 		{
466 			if ( item->sellValue(clientnum) <= 1 )
467 			{
468 				// selling cheap items does not increase trading past basic
469 				if ( stats[clientnum]->PROFICIENCIES[PRO_TRADING] < SKILL_LEVEL_SKILLED )
470 				{
471 					players[clientnum]->entity->increaseSkill(PRO_TRADING);
472 				}
473 			}
474 			else
475 			{
476 				players[clientnum]->entity->increaseSkill(PRO_TRADING);
477 			}
478 		}
479 	}
480 	else
481 	{
482 		strcpy((char*)net_packet->data, "SHPS");
483 		SDLNet_Write32(shopkeeper, &net_packet->data[4]);
484 
485 		// send item that was sold to server
486 		SDLNet_Write32(item->type, &net_packet->data[8]);
487 		SDLNet_Write32(item->status, &net_packet->data[12]);
488 		SDLNet_Write32(item->beatitude, &net_packet->data[16]);
489 		SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
490 		if ( itemTypeIsQuiver(item->type) )
491 		{
492 			SDLNet_Write32((Uint32)item->count, &net_packet->data[24]);
493 		}
494 		else
495 		{
496 			SDLNet_Write32((Uint32)(1), &net_packet->data[24]);
497 		}
498 		if ( item->identified )
499 		{
500 			net_packet->data[28] = 1;
501 		}
502 		else
503 		{
504 			net_packet->data[28] = 0;
505 		}
506 		net_packet->data[29] = clientnum;
507 		net_packet->address.host = net_server.host;
508 		net_packet->address.port = net_server.port;
509 		net_packet->len = 30;
510 		sendPacketSafe(net_sock, -1, net_packet, 0);
511 	}
512 	if ( itemTypeIsQuiver(item->type) )
513 	{
514 		item->count = 1; // so we consume it all up.
515 	}
516 	consumeItem(item, clientnum);
517 	sellitem = NULL;
518 }
519 
shopIsMysteriousShopkeeper(Entity * entity)520 bool shopIsMysteriousShopkeeper(Entity* entity)
521 {
522 	if ( !entity )
523 	{
524 		return false;
525 	}
526 	if ( entity->monsterStoreType == 10 )
527 	{
528 		return true;
529 	}
530 	return false;
531 }
532 
buyItemFromMysteriousShopkeepConsumeOrb(Entity & entity,Item & boughtItem)533 void buyItemFromMysteriousShopkeepConsumeOrb(Entity& entity, Item& boughtItem)
534 {
535 	list_t* inventory = nullptr;
536 	if ( multiplayer == CLIENT )
537 	{
538 		inventory = shopInv;
539 	}
540 	else
541 	{
542 		if ( entity.getStats() )
543 		{
544 			inventory = &entity.getStats()->inventory;
545 		}
546 	}
547 	if ( inventory )
548 	{
549 		for ( auto orbCategories : shopkeeperMysteriousItems )
550 		{
551 			if ( orbCategories.second.find(boughtItem.type) != orbCategories.second.end() )
552 			{
553 				// item is part of an orb set. need to consume the orb.
554 				node_t* nextnode = nullptr;
555 				for ( node_t* node = inventory->first; node; node = nextnode )
556 				{
557 					nextnode = node->next;
558 					Item* orb = (Item*)node->element;
559 					if ( orb && orb->type == orbCategories.first )
560 					{
561 						consumeItem(orb, -1);
562 						break;
563 					}
564 				}
565 				break;
566 			}
567 		}
568 	}
569 }