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 }