1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: identify_and_appraise.cpp
5 Desc: contains identify and appraisal related (GUI) code.
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 "../draw.hpp"
14 #include "../game.hpp"
15 #include "../stat.hpp"
16 #include "../items.hpp"
17 #include "../net.hpp"
18 #include "../player.hpp"
19 #include "interface.hpp"
20 #include "../scores.hpp"
21
22 //Identify GUI definitions.
23 bool identifygui_active = false;
24 bool identifygui_appraising = false;
25 int identifygui_offset_x = 0;
26 int identifygui_offset_y = 0;
27 bool dragging_identifyGUI = false;
28 int identifyscroll = 0;
29 Item* identify_items[NUM_IDENTIFY_GUI_ITEMS];
30 SDL_Surface* identifyGUI_img;
31
32 int selectedIdentifySlot = -1;
33
rebuildIdentifyGUIInventory()34 void rebuildIdentifyGUIInventory()
35 {
36 list_t* identify_inventory = &stats[clientnum]->inventory;
37 node_t* node = nullptr;
38 Item* item = nullptr;
39 int c = 0;
40
41 if (identify_inventory)
42 {
43 //Count the number of items in the identify GUI "inventory".
44 for (node = identify_inventory->first; node != NULL; node = node->next)
45 {
46 item = (Item*) node->element;
47 if (item && !item->identified)
48 {
49 c++;
50 }
51 }
52 identifyscroll = std::max(0, std::min(identifyscroll, c - 4));
53 for (c = 0; c < 4; ++c)
54 {
55 identify_items[c] = NULL;
56 }
57 c = 0;
58
59 //Assign the visible items to the GUI slots.
60 for (node = identify_inventory->first; node != NULL; node = node->next)
61 {
62 if (node->element)
63 {
64 item = (Item*) node->element;
65 if (item && !item->identified) //Skip over all identified items.
66 {
67 c++;
68 if (c <= identifyscroll)
69 {
70 continue;
71 }
72 identify_items[c - identifyscroll - 1] = item;
73 if (c > 3 + identifyscroll)
74 {
75 break;
76 }
77 }
78 }
79 }
80 }
81 }
82
CloseIdentifyGUI()83 void CloseIdentifyGUI()
84 {
85 identifygui_active = false;
86 selectedIdentifySlot = -1;
87 }
88
updateIdentifyGUI()89 void updateIdentifyGUI()
90 {
91 //if (openedChest[clientnum])
92 // return; //Cannot have the identify and chest GUIs open at the same time.
93
94 SDL_Rect pos;
95 node_t* node;
96 int y, c;
97
98 //Identify GUI.
99 if (identifygui_active)
100 {
101 //Center the identify GUI.
102 pos.x = IDENTIFY_GUI_X;
103 pos.y = IDENTIFY_GUI_Y;
104 drawImage(identifyGUI_img, NULL, &pos);
105
106 //Buttons
107 if ( mousestatus[SDL_BUTTON_LEFT] )
108 {
109 //Identify GUI scroll up button.
110 if (omousey >= IDENTIFY_GUI_Y + 16 && omousey < IDENTIFY_GUI_Y + 52)
111 {
112 if (omousex >= IDENTIFY_GUI_X + (identifyGUI_img->w - 28) && omousex < IDENTIFY_GUI_X + (identifyGUI_img->w - 12))
113 {
114 buttonclick = 7;
115 identifyscroll--;
116 mousestatus[SDL_BUTTON_LEFT] = 0;
117 }
118 }
119 //Identify GUI scroll down button.
120 else if (omousey >= IDENTIFY_GUI_Y + 52 && omousey < IDENTIFY_GUI_Y + 88)
121 {
122 if (omousex >= IDENTIFY_GUI_X + (identifyGUI_img->w - 28) && omousex < IDENTIFY_GUI_X + (identifyGUI_img->w - 12))
123 {
124 buttonclick = 8;
125 identifyscroll++;
126 mousestatus[SDL_BUTTON_LEFT] = 0;
127 }
128 }
129 else if (omousey >= IDENTIFY_GUI_Y && omousey < IDENTIFY_GUI_Y + 15)
130 {
131 //Identify GUI close button.
132 if (omousex >= IDENTIFY_GUI_X + 393 && omousex < IDENTIFY_GUI_X + 407)
133 {
134 buttonclick = 9;
135 mousestatus[SDL_BUTTON_LEFT] = 0;
136 }
137 if (omousex >= IDENTIFY_GUI_X && omousex < IDENTIFY_GUI_X + 377 && omousey >= IDENTIFY_GUI_Y && omousey < IDENTIFY_GUI_Y + 15)
138 {
139 gui_clickdrag = true;
140 dragging_identifyGUI = true;
141 dragoffset_x = omousex - IDENTIFY_GUI_X;
142 dragoffset_y = omousey - IDENTIFY_GUI_Y;
143 mousestatus[SDL_BUTTON_LEFT] = 0;
144 }
145 }
146 }
147
148 // mousewheel
149 if ( omousex >= IDENTIFY_GUI_X + 12 && omousex < IDENTIFY_GUI_X + (identifyGUI_img->w - 28) )
150 {
151 if ( omousey >= IDENTIFY_GUI_Y + 16 && omousey < IDENTIFY_GUI_Y + (identifyGUI_img->h - 8) )
152 {
153 if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
154 {
155 mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
156 identifyscroll++;
157 }
158 else if ( mousestatus[SDL_BUTTON_WHEELUP] )
159 {
160 mousestatus[SDL_BUTTON_WHEELUP] = 0;
161 identifyscroll--;
162 }
163 }
164 }
165
166 if (dragging_identifyGUI)
167 {
168 if (gui_clickdrag)
169 {
170 identifygui_offset_x = (omousex - dragoffset_x) - (IDENTIFY_GUI_X - identifygui_offset_x);
171 identifygui_offset_y = (omousey - dragoffset_y) - (IDENTIFY_GUI_Y - identifygui_offset_y);
172 if (IDENTIFY_GUI_X <= 0)
173 {
174 identifygui_offset_x = 0 - (IDENTIFY_GUI_X - identifygui_offset_x);
175 }
176 if (IDENTIFY_GUI_X > 0 + xres - identifyGUI_img->w)
177 {
178 identifygui_offset_x = (0 + xres - identifyGUI_img->w) - (IDENTIFY_GUI_X - identifygui_offset_x);
179 }
180 if (IDENTIFY_GUI_Y <= 0)
181 {
182 identifygui_offset_y = 0 - (IDENTIFY_GUI_Y - identifygui_offset_y);
183 }
184 if (IDENTIFY_GUI_Y > 0 + yres - identifyGUI_img->h)
185 {
186 identifygui_offset_y = (0 + yres - identifyGUI_img->h) - (IDENTIFY_GUI_Y - identifygui_offset_y);
187 }
188 }
189 else
190 {
191 dragging_identifyGUI = false;
192 }
193 }
194
195 list_t* identify_inventory = &stats[clientnum]->inventory;
196
197 if (!identify_inventory)
198 {
199 messagePlayer(0, "Warning: stats[%d].inventory is not a valid list. This should not happen.", clientnum);
200 }
201 else
202 {
203 //Print the window label signifying this as the identify GUI.
204 char* window_name;
205 if (identifygui_appraising)
206 {
207 window_name = language[317];
208 }
209 else
210 {
211 window_name = language[318];
212 }
213 ttfPrintText(ttf8, (IDENTIFY_GUI_X + 2 + ((identifyGUI_img->w / 2) - ((TTF8_WIDTH * longestline(window_name)) / 2))), IDENTIFY_GUI_Y + 4, window_name);
214
215 //Identify GUI up button.
216 if (buttonclick == 7)
217 {
218 pos.x = IDENTIFY_GUI_X + (identifyGUI_img->w - 28);
219 pos.y = IDENTIFY_GUI_Y + 16;
220 pos.w = 0;
221 pos.h = 0;
222 drawImage(invup_bmp, NULL, &pos);
223 }
224 //Identify GUI down button.
225 if (buttonclick == 8)
226 {
227 pos.x = IDENTIFY_GUI_X + (identifyGUI_img->w - 28);
228 pos.y = IDENTIFY_GUI_Y + 52;
229 pos.w = 0;
230 pos.h = 0;
231 drawImage(invdown_bmp, NULL, &pos);
232 }
233 //Identify GUI close button.
234 if (buttonclick == 9)
235 {
236 pos.x = IDENTIFY_GUI_X + 393;
237 pos.y = IDENTIFY_GUI_Y;
238 pos.w = 0;
239 pos.h = 0;
240 drawImage(invclose_bmp, NULL, &pos);
241 identifygui_active = false;
242 identifygui_appraising = false;
243
244 //Cleanup identify GUI gamecontroller code here.
245 selectedIdentifySlot = -1;
246 //TODO: closeIdentifyGUI() instead.
247 }
248
249 Item* item = NULL;
250
251 bool selectingSlot = false;
252 SDL_Rect slotPos;
253 slotPos.x = IDENTIFY_GUI_X;
254 slotPos.w = inventoryoptionChest_bmp->w;
255 slotPos.y = IDENTIFY_GUI_Y + 16;
256 slotPos.h = inventoryoptionChest_bmp->h;
257 for ( int i = 0; i < NUM_IDENTIFY_GUI_ITEMS; ++i, slotPos.y += slotPos.h )
258 {
259 pos.x = slotPos.x + 12;
260 pos.w = 0;
261 pos.h = 0;
262
263 if ( omousey >= slotPos.y && omousey < slotPos.y + slotPos.h && identify_items[i] )
264 {
265 pos.y = slotPos.y;
266 drawImage(inventoryoptionChest_bmp, nullptr, &pos);
267 selectedIdentifySlot = i;
268 selectingSlot = true;
269 if ( mousestatus[SDL_BUTTON_LEFT] || *inputPressed(joyimpulses[INJOY_MENU_USE]) )
270 {
271 *inputPressed(joyimpulses[INJOY_MENU_USE]) = 0;
272 mousestatus[SDL_BUTTON_LEFT] = 0;
273 identifyGUIIdentify(identify_items[i]);
274
275 rebuildIdentifyGUIInventory();
276 if ( identify_items[i] == nullptr )
277 {
278 if ( identify_items[0] == nullptr )
279 {
280 //Go back to inventory.
281 selectedIdentifySlot = -1;
282 warpMouseToSelectedInventorySlot();
283 }
284 else
285 {
286 //Move up one slot.
287 --selectedIdentifySlot;
288 warpMouseToSelectedIdentifySlot();
289 }
290 }
291 }
292 }
293 }
294
295 if ( !selectingSlot )
296 {
297 selectedIdentifySlot = -1;
298 }
299
300 //Okay, now prepare to render all the items.
301 y = IDENTIFY_GUI_Y + 22;
302 c = 0;
303 if (identify_inventory)
304 {
305 rebuildIdentifyGUIInventory();
306
307 //Actually render the items.
308 c = 0;
309 for (node = identify_inventory->first; node != NULL; node = node->next)
310 {
311 if (node->element)
312 {
313 item = (Item*) node->element;
314 if (item && !item->identified) //Skip over all identified items.
315 {
316 c++;
317 if (c <= identifyscroll)
318 {
319 continue;
320 }
321 char tempstr[64] = { 0 };
322 strncpy(tempstr, item->description(), 46);
323 if ( strlen(tempstr) == 46 )
324 {
325 strcat(tempstr, " ...");
326 }
327 ttfPrintText(ttf8, IDENTIFY_GUI_X + 36, y, tempstr);
328 pos.x = IDENTIFY_GUI_X + 16;
329 pos.y = IDENTIFY_GUI_Y + 17 + 18 * (c - identifyscroll - 1);
330 pos.w = 16;
331 pos.h = 16;
332 drawImageScaled(itemSprite(item), NULL, &pos);
333 y += 18;
334 if (c > 3 + identifyscroll)
335 {
336 break;
337 }
338 }
339 }
340 }
341 }
342 }
343 }
344 } //updateIdentifyGUI()
345
identifyGUIIdentify(Item * item)346 void identifyGUIIdentify(Item* item)
347 {
348 if (!item)
349 {
350 return;
351 }
352 if (item->identified)
353 {
354 messagePlayer(clientnum, language[319], item->getName());
355 return;
356 }
357
358 if (!identifygui_appraising)
359 {
360 item->identified = true;
361 messagePlayer(clientnum, language[320], item->description());
362 if (appraisal_timer > 0 && appraisal_item && appraisal_item == item->uid)
363 {
364 appraisal_timer = 0;
365 appraisal_item = 0;
366 }
367 identifygui_active = false;
368 }
369 else
370 {
371 //Appraising.
372
373 //If appraisal skill >= LEGENDARY, then auto-complete appraisal. Else, do the normal routine.
374 if ( stats[clientnum]->PROFICIENCIES[PRO_APPRAISAL] >= CAPSTONE_UNLOCK_LEVEL[PRO_APPRAISAL] )
375 {
376 item->identified = true;
377 messagePlayer(clientnum, language[320], item->description());
378 if (appraisal_timer > 0 && appraisal_item && appraisal_item == item->uid)
379 {
380 appraisal_timer = 0;
381 appraisal_item = 0;
382 }
383 if ( item->type == GEM_GLASS )
384 {
385 steamStatisticUpdate(STEAM_STAT_RHINESTONE_COWBOY, STEAM_STAT_INT, 1);
386 }
387 }
388 else
389 {
390 messagePlayer(clientnum, language[321], item->description());
391
392 //Tick the timer in act player.
393 //Once the timer hits zero, roll to see if the item is identified.
394 //If it is identified, identify it and print out a message for the player.
395
396 identifygui_appraising = false;
397 appraisal_timer = getAppraisalTime(item);
398 appraisal_timermax = appraisal_timer;
399 appraisal_item = item->uid;
400 }
401 }
402
403 //Cleanup identify GUI gamecontroller code here.
404 selectedIdentifySlot = -1;
405 }
406
getAppraisalTime(Item * item)407 int getAppraisalTime(Item* item)
408 {
409 int appraisal_time;
410 if ( item->type != GEM_GLASS )
411 {
412 appraisal_time = (items[item->type].value * 60) / (stats[clientnum]->PROFICIENCIES[PRO_APPRAISAL] + 1); // time in ticks until item is appraised
413 int playerCount = 0;
414 for ( int i = 0; i < MAXPLAYERS; ++i )
415 {
416 if ( !client_disconnected[i] )
417 {
418 ++playerCount;
419 }
420 }
421 if ( playerCount == 3 )
422 {
423 appraisal_time /= 1.25;
424 }
425 else if ( playerCount == 4 )
426 {
427 appraisal_time /= 1.5;
428 }
429 //messagePlayer(clientnum, "time: %d", appraisal_time);
430 }
431 else
432 {
433 appraisal_time = (1000 * 60) / (stats[clientnum]->PROFICIENCIES[PRO_APPRAISAL] + 1); // time in ticks until item is appraised+-
434 int playerCount = 0;
435 for ( int i = 0; i < MAXPLAYERS; ++i )
436 {
437 if ( !client_disconnected[i] )
438 {
439 ++playerCount;
440 }
441 }
442 if ( playerCount == 3 )
443 {
444 appraisal_time /= 1.15;
445 }
446 else if ( playerCount == 4 )
447 {
448 appraisal_time /= 1.25;
449 }
450 }
451 appraisal_time = std::min(std::max(1, appraisal_time), 36000);
452 return appraisal_time;
453 }
454
getItemInfoFromIdentifyGUI(int slot)455 inline Item* getItemInfoFromIdentifyGUI(int slot)
456 {
457 if ( slot >= 4 )
458 {
459 return nullptr; //Out of bounds,
460 }
461
462 return identify_items[slot];
463 }
464
selectIdentifySlot(int slot)465 void selectIdentifySlot(int slot)
466 {
467 if ( slot < selectedIdentifySlot )
468 {
469 //Moving up.
470
471 /*
472 * Possible cases:
473 * * 1) Move cursor up the GUI through different selectedIdentifySlot.
474 * * 2) Page up through identifyscroll--
475 * * 3) Scrolling up past top of Identify GUI, no identifyscroll (move back to inventory)
476 */
477
478 if ( selectedIdentifySlot <= 0 )
479 {
480 //Covers cases 2 & 3.
481
482 /*
483 * Possible cases:
484 * * A) Hit very top of Identify "inventory", can't go any further. Return to inventory.
485 * * B) Page up, scrolling through identifyscroll.
486 */
487
488 if ( identifyscroll <= 0 )
489 {
490 //Case 3/A: Return to inventory.
491 selectedIdentifySlot = -1;
492 }
493 else
494 {
495 //Case 2/B: Page up through Identify "inventory".
496 --identifyscroll;
497 }
498 }
499 else
500 {
501 //Covers case 1.
502
503 //Move cursor up the GUI through different selectedIdentifySlot (--selectedIdentifySlot).
504 --selectedIdentifySlot;
505 warpMouseToSelectedIdentifySlot();
506 }
507 }
508 else if ( slot > selectedIdentifySlot )
509 {
510 //Moving down.
511
512 /*
513 * Possible cases:
514 * * 1) Moving cursor down through GUI through different selectedIdentifySlot.
515 * * 2) Scrolling down past bottom of Identify GUI through identifyscroll++
516 * * 3) Scrolling down past bottom of Identify GUI, max Identify scroll (revoke move -- can't go beyond limit of Identify GUI).
517 */
518
519 if ( selectedIdentifySlot >= NUM_IDENTIFY_GUI_ITEMS - 1 )
520 {
521 //Covers cases 2 & 3.
522 ++identifyscroll; //identifyscroll is automatically sanitized in updateIdentifyGUI().
523 }
524 else
525 {
526 //Covers case 1.
527 //Move cursor down through the GUi through different selectedIdentifySlot (++selectedIdentifySlot).
528 //This is a little bit trickier since must revoke movement if there is no item in the next slot!
529
530 /*
531 * Two possible cases:
532 * * A) Items below this. Advance selectedIdentifySlot to them.
533 * * B) On last item already. Do nothing (revoke movement).
534 */
535
536 Item* item = getItemInfoFromIdentifyGUI(selectedIdentifySlot + 1);
537
538 if ( item )
539 {
540 ++selectedIdentifySlot;
541 warpMouseToSelectedIdentifySlot();
542 }
543 else
544 {
545 //No more items. Stop.
546 }
547 }
548 }
549 }
550
warpMouseToSelectedIdentifySlot()551 void warpMouseToSelectedIdentifySlot()
552 {
553 SDL_Rect slotPos;
554 slotPos.x = IDENTIFY_GUI_X;
555 slotPos.w = inventoryoptionChest_bmp->w;
556 slotPos.h = inventoryoptionChest_bmp->h;
557 slotPos.y = IDENTIFY_GUI_Y + 16 + (slotPos.h * selectedIdentifySlot);
558
559 SDL_WarpMouseInWindow(screen, slotPos.x + (slotPos.w / 2), slotPos.y + (slotPos.h / 2));
560 }
561