1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: removecurse.cpp
5 	Desc: GUI code for the remove curse spell.
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 
21 
22 //Remove curse GUI definitions.
23 bool removecursegui_active = false;
24 bool removecursegui_appraising = false;
25 int removecursegui_offset_x = 0;
26 int removecursegui_offset_y = 0;
27 bool dragging_removecurseGUI = false;
28 int removecursescroll = 0;
29 Item* removecurse_items[NUM_REMOVE_CURSE_GUI_ITEMS];
30 SDL_Surface* removecurseGUI_img;
31 
32 int selectedRemoveCurseSlot = -1;
33 
rebuildRemoveCurseGUIInventory()34 void rebuildRemoveCurseGUIInventory()
35 {
36 	list_t* removecurse_inventory = &stats[clientnum]->inventory;
37 	node_t* node = nullptr;
38 	Item* item = nullptr;
39 	int c = 0;
40 
41 	if ( removecurse_inventory )
42 	{
43 		//Count the number of items in the Remove Curse GUI "inventory".
44 		for ( node = removecurse_inventory->first; node != nullptr; node = node->next )
45 		{
46 			item = (Item*) node->element;
47 			if ( item && item->identified && item->beatitude < 0 )
48 			{
49 				++c;
50 			}
51 		}
52 		removecursescroll = std::max(0, std::min(removecursescroll, c - 4));
53 		for ( c = 0; c < 4; ++c )
54 		{
55 			removecurse_items[c] = nullptr;
56 		}
57 		c = 0;
58 
59 		//Assign the visible items to the GUI slots.
60 		for ( node = removecurse_inventory->first; node != nullptr; node = node->next )
61 		{
62 			if ( node->element )
63 			{
64 				item = (Item*) node->element;
65 				if ( item && item->identified && item->beatitude < 0 ) //Skip over all unidentified or uncursed items.
66 				{
67 					++c;
68 					if ( c <= removecursescroll )
69 					{
70 						continue;
71 					}
72 					removecurse_items[c - removecursescroll - 1] = item;
73 					if ( c > 3 + removecursescroll )
74 					{
75 						break;
76 					}
77 				}
78 			}
79 		}
80 	}
81 }
82 
83 
updateRemoveCurseGUI()84 void updateRemoveCurseGUI()
85 {
86 	SDL_Rect pos;
87 	node_t* node;
88 	int y, c;
89 
90 	//Remove Curse GUI.
91 	if (removecursegui_active)
92 	{
93 		//Center the remove curse GUI.
94 		pos.x = REMOVECURSE_GUI_X;
95 		pos.y = REMOVECURSE_GUI_Y;
96 		drawImage(identifyGUI_img, NULL, &pos);
97 
98 		//Buttons
99 		if ( mousestatus[SDL_BUTTON_LEFT] )
100 		{
101 			//Remove Curse GUI scroll up button.
102 			if (omousey >= REMOVECURSE_GUI_Y + 16 && omousey < REMOVECURSE_GUI_Y + 52)
103 			{
104 				if (omousex >= REMOVECURSE_GUI_X + (identifyGUI_img->w - 28) && omousex < REMOVECURSE_GUI_X + (identifyGUI_img->w - 12))
105 				{
106 					buttonclick = 7;
107 					removecursescroll--;
108 					mousestatus[SDL_BUTTON_LEFT] = 0;
109 				}
110 			}
111 			//Remove Curse GUI scroll down button.
112 			else if (omousey >= REMOVECURSE_GUI_Y + 52 && omousey < REMOVECURSE_GUI_Y + 88)
113 			{
114 				if (omousex >= REMOVECURSE_GUI_X + (identifyGUI_img->w - 28) && omousex < REMOVECURSE_GUI_X + (identifyGUI_img->w - 12))
115 				{
116 					buttonclick = 8;
117 					removecursescroll++;
118 					mousestatus[SDL_BUTTON_LEFT] = 0;
119 				}
120 			}
121 			else if (omousey >= REMOVECURSE_GUI_Y && omousey < REMOVECURSE_GUI_Y + 15)
122 			{
123 				//Remove Curse GUI close button.
124 				if (omousex >= REMOVECURSE_GUI_X + 393 && omousex < REMOVECURSE_GUI_X + 407)
125 				{
126 					buttonclick = 9;
127 					mousestatus[SDL_BUTTON_LEFT] = 0;
128 				}
129 				if (omousex >= REMOVECURSE_GUI_X && omousex < REMOVECURSE_GUI_X + 377 && omousey >= REMOVECURSE_GUI_Y && omousey < REMOVECURSE_GUI_Y + 15)
130 				{
131 					gui_clickdrag = true;
132 					dragging_removecurseGUI = true;
133 					dragoffset_x = omousex - REMOVECURSE_GUI_X;
134 					dragoffset_y = omousey - REMOVECURSE_GUI_Y;
135 					mousestatus[SDL_BUTTON_LEFT] = 0;
136 				}
137 			}
138 		}
139 
140 		// mousewheel
141 		if ( omousex >= REMOVECURSE_GUI_X + 12 && omousex < REMOVECURSE_GUI_X + (identifyGUI_img->w - 28) )
142 		{
143 			if ( omousey >= REMOVECURSE_GUI_Y + 16 && omousey < REMOVECURSE_GUI_Y + (identifyGUI_img->h - 8) )
144 			{
145 				if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
146 				{
147 					mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
148 					removecursescroll++;
149 				}
150 				else if ( mousestatus[SDL_BUTTON_WHEELUP] )
151 				{
152 					mousestatus[SDL_BUTTON_WHEELUP] = 0;
153 					removecursescroll--;
154 				}
155 			}
156 		}
157 
158 		if (dragging_removecurseGUI)
159 		{
160 			if (gui_clickdrag)
161 			{
162 				removecursegui_offset_x = (omousex - dragoffset_x) - (REMOVECURSE_GUI_X - removecursegui_offset_x);
163 				removecursegui_offset_y = (omousey - dragoffset_y) - (REMOVECURSE_GUI_Y - removecursegui_offset_y);
164 				if (REMOVECURSE_GUI_X <= 0)
165 				{
166 					removecursegui_offset_x = 0 - (REMOVECURSE_GUI_X - removecursegui_offset_x);
167 				}
168 				if (REMOVECURSE_GUI_X > 0 + xres - identifyGUI_img->w)
169 				{
170 					removecursegui_offset_x = (0 + xres - identifyGUI_img->w) - (REMOVECURSE_GUI_X - removecursegui_offset_x);
171 				}
172 				if (REMOVECURSE_GUI_Y <= 0)
173 				{
174 					removecursegui_offset_y = 0 - (REMOVECURSE_GUI_Y - removecursegui_offset_y);
175 				}
176 				if (REMOVECURSE_GUI_Y > 0 + yres - identifyGUI_img->h)
177 				{
178 					removecursegui_offset_y = (0 + yres - identifyGUI_img->h) - (REMOVECURSE_GUI_Y - removecursegui_offset_y);
179 				}
180 			}
181 			else
182 			{
183 				dragging_removecurseGUI = false;
184 			}
185 		}
186 
187 		list_t* removecurse_inventory = &stats[clientnum]->inventory;
188 
189 		if (!removecurse_inventory)
190 		{
191 			messagePlayer(0, "Warning: stats[%d].inventory is not a valid list. This should not happen.", clientnum);
192 		}
193 		else
194 		{
195 			//Print the window label signifying this as the remove curse GUI.
196 			char* window_name;
197 			window_name = language[346];
198 			ttfPrintText(ttf8, (REMOVECURSE_GUI_X + 2 + ((identifyGUI_img->w / 2) - ((TTF8_WIDTH * longestline(window_name)) / 2))), REMOVECURSE_GUI_Y + 4, window_name);
199 
200 			//Remove Curse GUI up button.
201 			if (buttonclick == 7)
202 			{
203 				pos.x = REMOVECURSE_GUI_X + (identifyGUI_img->w - 28);
204 				pos.y = REMOVECURSE_GUI_Y + 16;
205 				pos.w = 0;
206 				pos.h = 0;
207 				drawImage(invup_bmp, NULL, &pos);
208 			}
209 			//Remove Curse GUI down button.
210 			if (buttonclick == 8)
211 			{
212 				pos.x = REMOVECURSE_GUI_X + (identifyGUI_img->w - 28);
213 				pos.y = REMOVECURSE_GUI_Y + 52;
214 				pos.w = 0;
215 				pos.h = 0;
216 				drawImage(invdown_bmp, NULL, &pos);
217 			}
218 			//Remove Curse GUI close button.
219 			if (buttonclick == 9)
220 			{
221 				pos.x = REMOVECURSE_GUI_X + 393;
222 				pos.y = REMOVECURSE_GUI_Y;
223 				pos.w = 0;
224 				pos.h = 0;
225 				drawImage(invclose_bmp, NULL, &pos);
226 				closeRemoveCurseGUI();
227 			}
228 
229 			Item *item = nullptr;
230 
231 			bool selectingSlot = false;
232 			SDL_Rect slotPos;
233 			slotPos.x = REMOVECURSE_GUI_X;
234 			slotPos.w = inventoryoptionChest_bmp->w;
235 			slotPos.y = REMOVECURSE_GUI_Y + 16;
236 			slotPos.h = inventoryoptionChest_bmp->h;
237 
238 			for ( int i = 0; i < NUM_REMOVE_CURSE_GUI_ITEMS; ++i, slotPos.y += slotPos.h )
239 			{
240 				pos.x = slotPos.x + 12;
241 				pos.w = 0;
242 				pos.h = 0;
243 
244 				if ( omousey >= slotPos.y && omousey < slotPos.y + slotPos.h && removecurse_items[i] )
245 				{
246 					pos.y = slotPos.y;
247 					drawImage(inventoryoptionChest_bmp, nullptr, &pos);
248 					selectedRemoveCurseSlot = i;
249 					selectingSlot = true;
250 					if ( mousestatus[SDL_BUTTON_LEFT] || *inputPressed(joyimpulses[INJOY_MENU_USE]) )
251 					{
252 						*inputPressed(joyimpulses[INJOY_MENU_USE]) = 0;
253 						mousestatus[SDL_BUTTON_LEFT] = 0;
254 						removecurseGUIRemoveCurse(removecurse_items[i]);
255 
256 						rebuildRemoveCurseGUIInventory();
257 						if ( removecurse_items[i] == nullptr )
258 						{
259 							if ( removecurse_items[0] == nullptr )
260 							{
261 								//Go back to inventory.
262 								selectedRemoveCurseSlot = -1;
263 								warpMouseToSelectedInventorySlot();
264 							}
265 							else
266 							{
267 								//Move up one slot.
268 								--selectedRemoveCurseSlot;
269 								warpMouseToSelectedRemoveCurseSlot();
270 							}
271 						}
272 					}
273 				}
274 			}
275 
276 			if ( !selectingSlot )
277 			{
278 				selectedRemoveCurseSlot = -1;
279 			}
280 
281 			//Okay, now prepare to render all the items.
282 			y = REMOVECURSE_GUI_Y + 22;
283 			c = 0;
284 			if (removecurse_inventory)
285 			{
286 				rebuildRemoveCurseGUIInventory();
287 
288 				//Actually render the items.
289 				c = 0;
290 				for (node = removecurse_inventory->first; node != NULL; node = node->next)
291 				{
292 					if (node->element)
293 					{
294 						item = (Item*) node->element;
295 						if (item && item->identified && item->beatitude < 0)   //Skip over all unidentified or uncursed items.
296 						{
297 							c++;
298 							if (c <= removecursescroll)
299 							{
300 								continue;
301 							}
302 							char tempstr[64] = { 0 };
303 							strncpy(tempstr, item->description(), 46);
304 							if ( strlen(tempstr) == 46 )
305 							{
306 								strcat(tempstr, " ...");
307 							}
308 							ttfPrintText(ttf8, REMOVECURSE_GUI_X + 36, y, tempstr);
309 							pos.x = REMOVECURSE_GUI_X + 16;
310 							pos.y = REMOVECURSE_GUI_Y + 17 + 18 * (c - removecursescroll - 1);
311 							pos.w = 16;
312 							pos.h = 16;
313 							drawImageScaled(itemSprite(item), NULL, &pos);
314 							y += 18;
315 							if (c > 3 + removecursescroll)
316 							{
317 								break;
318 							}
319 						}
320 					}
321 				}
322 			}
323 		}
324 	}
325 } //updateRemoveCurseGUI()
326 
removecurseGUIRemoveCurse(Item * item)327 void removecurseGUIRemoveCurse(Item* item)
328 {
329 	if (!item)
330 	{
331 		return;
332 	}
333 	if (item->beatitude >= 0)
334 	{
335 		messagePlayer(clientnum, language[347], item->getName());
336 		return;
337 	}
338 
339 	item->beatitude = 0; //0 = uncursed. > 0 = blessed.
340 	messagePlayer(clientnum, language[348], item->description());
341 	closeRemoveCurseGUI();
342 	if ( multiplayer == CLIENT && itemIsEquipped(item, clientnum) )
343 	{
344 		// the client needs to inform the server that their equipment was uncursed.
345 		int armornum = 0;
346 		if ( item == stats[clientnum]->helmet )
347 		{
348 			armornum = 0;
349 		}
350 		else if ( item == stats[clientnum]->breastplate )
351 		{
352 			armornum = 1;
353 		}
354 		else if ( item == stats[clientnum]->gloves )
355 		{
356 			armornum = 2;
357 		}
358 		else if ( item == stats[clientnum]->shoes )
359 		{
360 			armornum = 3;
361 		}
362 		else if ( item == stats[clientnum]->shield )
363 		{
364 			armornum = 4;
365 		}
366 		else if ( item == stats[clientnum]->weapon )
367 		{
368 			armornum = 5;
369 		}
370 		else if ( item == stats[clientnum]->cloak )
371 		{
372 			armornum = 6;
373 		}
374 		else if ( item == stats[clientnum]->amulet )
375 		{
376 			armornum = 7;
377 		}
378 		else if ( item == stats[clientnum]->ring )
379 		{
380 			armornum = 8;
381 		}
382 		else if ( item == stats[clientnum]->mask )
383 		{
384 			armornum = 9;
385 		}
386 		strcpy((char*)net_packet->data, "RCUR");
387 		net_packet->data[4] = clientnum;
388 		net_packet->data[5] = armornum;
389 		net_packet->address.host = net_server.host;
390 		net_packet->address.port = net_server.port;
391 		net_packet->len = 6;
392 		sendPacketSafe(net_sock, -1, net_packet, 0);
393 	}
394 }
395 
closeRemoveCurseGUI()396 void closeRemoveCurseGUI()
397 {
398 	removecursegui_active = false;
399 
400 	selectedRemoveCurseSlot = -1;
401 }
402 
getItemInfoFromRemoveCurseGUI(int slot)403 inline Item* getItemInfoFromRemoveCurseGUI(int slot)
404 {
405 	if ( slot >= 4 )
406 	{
407 		return nullptr; //Out of bounds,
408 	}
409 
410 	return removecurse_items[slot];
411 }
412 
selectRemoveCurseSlot(int slot)413 void selectRemoveCurseSlot(int slot)
414 {
415 	if ( slot < selectedRemoveCurseSlot )
416 	{
417 		//Moving up.
418 
419 		/*
420 		 * Possible cases:
421 		 * * 1) Move cursor up the GUI through different selectedRemoveCurseSlot.
422 		 * * 2) Page up through removecursescroll--
423 		 * * 3) Scrolling up past top of Remove Curse GUI, no removecursescroll (move back to inventory)
424 		 */
425 
426 		if ( selectedRemoveCurseSlot <= 0 )
427 		{
428 			//Covers cases 2 & 3.
429 
430 			/*
431 			 * Possible cases:
432 			 * * A) Hit very top of Remove Curse "inventory", can't go any further. Return to inventory.
433 			 * * B) Page up, scrolling through removecursescroll.
434 			 */
435 
436 			if ( removecursescroll <= 0 )
437 			{
438 				//Case 3/A: Return to inventory.
439 				selectedRemoveCurseSlot = -1;
440 			}
441 			else
442 			{
443 				//Case 2/B: Page up through Remove Curse "inventory".
444 				--removecursescroll;
445 			}
446 		}
447 		else
448 		{
449 			//Covers case 1.
450 
451 			//Move cursor up the GUI through different selectedRemoveCurseSlot (--selectedRemoveCurseSlot).
452 			--selectedRemoveCurseSlot;
453 			warpMouseToSelectedRemoveCurseSlot();
454 		}
455 	}
456 	else if ( slot > selectedRemoveCurseSlot )
457 	{
458 		//Moving down.
459 
460 		/*
461 		 * Possible cases:
462 		 * * 1) Moving cursor down through GUI through different selectedRemoveCurseSlot.
463 		 * * 2) Scrolling down past bottom of Remove Curse GUI through removecursescroll++
464 		 * * 3) Scrolling down past bottom of Remove Curse GUI, max Remove Curse scroll (revoke move -- can't go beyond limit of Remove Curse GUI).
465 		 */
466 
467 		if ( selectedRemoveCurseSlot >= NUM_REMOVE_CURSE_GUI_ITEMS - 1 )
468 		{
469 			//Covers cases 2 & 3.
470 			++removecursescroll; //removecursescroll is automatically sanitized in updateRemoveCurseGUI().
471 		}
472 		else
473 		{
474 			//Covers case 1.
475 			//Move cursor down through the GUI through different selectedRemoveCurseSlot (++selectedRemoveCurseSlot).
476 			//This is a little bit trickier since must revoke movement if there is no item in the next slot!
477 
478 			/*
479 			 * Two possible cases:
480 			 * * A) Items below this. Advance selectedRemoveCurseSlot to them.
481 			 * * B) On last item already. Do nothing (revoke movement).
482 			 */
483 
484 			Item* item = getItemInfoFromRemoveCurseGUI(selectedRemoveCurseSlot + 1);
485 
486 			if ( item )
487 			{
488 				++selectedRemoveCurseSlot;
489 				warpMouseToSelectedRemoveCurseSlot();
490 			}
491 			else
492 			{
493 				//No more items. Stop.
494 			}
495 		}
496 	}
497 }
498 
warpMouseToSelectedRemoveCurseSlot()499 void warpMouseToSelectedRemoveCurseSlot()
500 {
501 	SDL_Rect slotPos;
502 	slotPos.x = REMOVECURSE_GUI_X;
503 	slotPos.w = inventoryoptionChest_bmp->w;
504 	slotPos.h = inventoryoptionChest_bmp->h;
505 	slotPos.y = REMOVECURSE_GUI_Y + 16 + (slotPos.h * selectedRemoveCurseSlot);
506 
507 	SDL_WarpMouseInWindow(screen, slotPos.x + (slotPos.w / 2), slotPos.y + (slotPos.h / 2));
508 }
509