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