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