1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: drawstatus.cpp
5 Desc: contains drawStatus()
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 "../magic/magic.hpp"
18 #include "../sound.hpp"
19 #include "../net.hpp"
20 #include "../menu.hpp"
21 #include "../player.hpp"
22 #include "interface.hpp"
23 #include "../colors.hpp"
24
25 //char enemy_name[128];
26 //Sint32 enemy_hp = 0, enemy_maxhp = 0, enemy_oldhp = 0;
27 //Uint32 enemy_timer = 0, enemy_lastuid = 0;
28 //Uint32 enemy_bar_color[MAXPLAYERS] = { 0 }; // color for each player's enemy bar to display. multiplayer clients only refer to their own [clientnum] entry.
29 int magicBoomerangHotbarSlot = -1;
30 Uint32 hotbarTooltipLastGameTick = 0;
31
32 /*-------------------------------------------------------------------------------
33
34 handleDamageIndicators
35
36 draws damage indicators, fades them, culls them, etc.
37
38 -------------------------------------------------------------------------------*/
39
handleDamageIndicators(int player)40 void handleDamageIndicators(int player)
41 {
42 node_t* node, *nextnode;
43 for ( node = damageIndicators.first; node != NULL; node = nextnode )
44 {
45 nextnode = node->next;
46 damageIndicator_t* damageIndicator = (damageIndicator_t*)node->element;
47
48 double tangent = atan2( damageIndicator->y / 16 - cameras[player].y, damageIndicator->x / 16 - cameras[player].x );
49 double angle = tangent - cameras[player].ang;
50 angle += 3 * PI / 2;
51 while ( angle >= PI )
52 {
53 angle -= PI * 2;
54 }
55 while ( angle < -PI )
56 {
57 angle += PI * 2;
58 }
59 SDL_Rect pos;
60 pos.x = xres / 2;
61 pos.y = yres / 2;
62 pos.x += 200 * cos(angle);
63 pos.y += 200 * sin(angle);
64 pos.w = damage_bmp->w;
65 pos.h = damage_bmp->h;
66 if ( stats[clientnum]->HP > 0 )
67 {
68 drawImageRotatedAlpha( damage_bmp, NULL, &pos, angle, (Uint8)(damageIndicator->alpha * 255) );
69 }
70
71 damageIndicator->alpha = std::min(damageIndicator->ticks, 120) / 120.f;
72 if ( damageIndicator->alpha <= 0 )
73 {
74 list_RemoveNode(node);
75 }
76 }
77 }
78
handleDamageIndicatorTicks()79 void handleDamageIndicatorTicks()
80 {
81 node_t* node;
82 for ( node = damageIndicators.first; node != NULL; node = node->next )
83 {
84 damageIndicator_t* damageIndicator = (damageIndicator_t*)node->element;
85 damageIndicator->ticks--;
86 }
87 }
88
89 /*-------------------------------------------------------------------------------
90
91 newDamageIndicator
92
93 creates a new damage indicator on the hud
94
95 -------------------------------------------------------------------------------*/
96
newDamageIndicator(double x,double y)97 damageIndicator_t* newDamageIndicator(double x, double y)
98 {
99 damageIndicator_t* damageIndicator;
100
101 // allocate memory for the indicator
102 if ( (damageIndicator = (damageIndicator_t*) malloc(sizeof(damageIndicator_t))) == NULL )
103 {
104 printlog( "failed to allocate memory for new damage indicator!\n" );
105 exit(1);
106 }
107
108 // add the indicator to the list of indicators
109 damageIndicator->node = list_AddNodeLast(&damageIndicators);
110 damageIndicator->node->element = damageIndicator;
111 damageIndicator->node->deconstructor = &defaultDeconstructor;
112 damageIndicator->node->size = sizeof(damageIndicator_t);
113
114 damageIndicator->x = x;
115 damageIndicator->y = y;
116 damageIndicator->alpha = 1.f;
117 damageIndicator->ticks = 120; // two seconds
118
119 return damageIndicator;
120 }
121
122 /*-------------------------------------------------------------------------------
123
124 updateEnemyBarColor
125
126 updates the enemy hp bar color depending on an entities status effects
127
128 -------------------------------------------------------------------------------*/
129
updateEnemyBarStatusEffectColor(int player,const Entity & target,const Stat & targetStats)130 void updateEnemyBarStatusEffectColor(int player, const Entity &target, const Stat &targetStats)
131 {
132 if ( targetStats.EFFECTS[EFF_POISONED] )
133 {
134 if ( colorblind )
135 {
136 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = SDL_MapRGB(mainsurface->format, 0, 0, 64); // Display blue
137 }
138 else
139 {
140 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = SDL_MapRGB(mainsurface->format, 0, 64, 0); // Display green
141 }
142 }
143 else if ( targetStats.EFFECTS[EFF_PARALYZED] )
144 {
145 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = SDL_MapRGB(mainsurface->format, 112, 112, 0);
146 }
147 else if ( targetStats.EFFECTS[EFF_CONFUSED] || targetStats.EFFECTS[EFF_DISORIENTED] )
148 {
149 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = SDL_MapRGB(mainsurface->format, 92, 0, 92);
150 }
151 else if ( targetStats.EFFECTS[EFF_PACIFY] )
152 {
153 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = SDL_MapRGB(mainsurface->format, 128, 32, 80);
154 }
155 else if ( targetStats.EFFECTS[EFF_BLIND] )
156 {
157 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = SDL_MapRGB(mainsurface->format, 64, 64, 64);
158 }
159 else
160 {
161 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = 0;
162 }
163 }
164
165 /*-------------------------------------------------------------------------------
166
167 updateEnemyBar
168
169 updates the enemy hp bar for the given player
170
171 -------------------------------------------------------------------------------*/
172
updateEnemyBar(Entity * source,Entity * target,char * name,Sint32 hp,Sint32 maxhp,bool lowPriorityTick)173 void updateEnemyBar(Entity* source, Entity* target, char* name, Sint32 hp, Sint32 maxhp, bool lowPriorityTick)
174 {
175 // server/singleplayer only function.
176 int player = -1;
177 int c;
178
179 if (!source || !target)
180 {
181 return;
182 }
183
184 for (c = 0; c < MAXPLAYERS; c++)
185 {
186 if (source == players[c]->entity)
187 {
188 player = c;
189 break;
190 }
191 }
192
193 if ( player == -1 )
194 {
195 if ( source->behavior == &actMonster && source->monsterAllySummonRank != 0
196 && (target->behavior == &actMonster || target->behavior == &actPlayer) )
197 {
198 if ( source->monsterAllyGetPlayerLeader() && source->monsterAllyGetPlayerLeader() != target )
199 {
200 player = source->monsterAllyIndex; // don't update enemy bar if attacking leader.
201 }
202 }
203 else if ( source->behavior == &actMonster && source->monsterIllusionTauntingThisUid != 0 )
204 {
205 Entity* parent = uidToEntity(source->parent);
206 if ( parent && parent->behavior == &actPlayer && parent != target )
207 {
208 player = parent->skill[2]; // don't update enemy bar if attacking leader.
209 }
210 }
211 else if ( source->behavior == &actMonster && monsterIsImmobileTurret(source, nullptr)
212 && (target->behavior == &actMonster || target->behavior == &actPlayer) )
213 {
214 if ( source->monsterAllyGetPlayerLeader() && source->monsterAllyGetPlayerLeader() != target )
215 {
216 player = source->monsterAllyIndex; // don't update enemy bar if attacking leader.
217 }
218 }
219 }
220
221 int playertarget = -1;
222 for (c = 0; c < MAXPLAYERS; c++)
223 {
224 if (target == players[c]->entity)
225 {
226 playertarget = c;
227 break;
228 }
229 }
230
231 Stat* stats = target->getStats();
232 if ( stats )
233 {
234 if ( stats->HP != stats->OLDHP )
235 {
236 if ( playertarget == clientnum )
237 {
238 newDamageIndicator(source->x, source->y);
239 }
240 else if ( playertarget > 0 && multiplayer == SERVER )
241 {
242 strcpy((char*)net_packet->data, "DAMI");
243 SDLNet_Write32(source->x, &net_packet->data[4]);
244 SDLNet_Write32(source->y, &net_packet->data[8]);
245 net_packet->address.host = net_clients[playertarget - 1].host;
246 net_packet->address.port = net_clients[playertarget - 1].port;
247 net_packet->len = 12;
248 sendPacketSafe(net_sock, -1, net_packet, playertarget - 1);
249 }
250 }
251
252 if ( player >= 0 )
253 {
254 updateEnemyBarStatusEffectColor(player, *target, *stats); // set color depending on status effects of the target.
255 }
256 }
257 else if ( player >= 0 )
258 {
259 enemyHPDamageBarHandler.enemy_bar_client_colors[player] = 0;
260 }
261
262 //if ( player >= 0 )
263 //{
264 // if ( enemy_lastuid != target->getUID() || enemy_timer == 0 )
265 // {
266 // // if new target or timer expired, get new OLDHP value.
267 // if ( stats )
268 // {
269 // enemy_oldhp = stats->OLDHP;
270 // }
271 // }
272 // if ( !stats )
273 // {
274 // enemy_oldhp = hp; // chairs/tables and things.
275 // }
276 // enemy_lastuid = target->getUID();
277 //}
278 if ( player == clientnum )
279 {
280 if ( stats )
281 {
282 enemyHPDamageBarHandler.addEnemyToList(hp, maxhp, stats->OLDHP,
283 enemyHPDamageBarHandler.enemy_bar_client_colors[player], target->getUID(), name, lowPriorityTick);
284 }
285 else
286 {
287 enemyHPDamageBarHandler.addEnemyToList(hp, maxhp, hp,
288 enemyHPDamageBarHandler.enemy_bar_client_colors[player], target->getUID(), name, lowPriorityTick);
289 }
290 }
291 else if ( player > 0 && multiplayer == SERVER )
292 {
293 strcpy((char*)net_packet->data, "ENHP");
294 SDLNet_Write32(hp, &net_packet->data[4]);
295 SDLNet_Write32(maxhp, &net_packet->data[8]);
296 SDLNet_Write32(enemyHPDamageBarHandler.enemy_bar_client_colors[player], &net_packet->data[12]);
297 if ( stats )
298 {
299 SDLNet_Write32(stats->OLDHP, &net_packet->data[16]);
300 }
301 else
302 {
303 SDLNet_Write32(hp, &net_packet->data[16]);
304 }
305 SDLNet_Write32(target->getUID(), &net_packet->data[20]);
306 net_packet->data[24] = lowPriorityTick ? 1 : 0; // 1 == true
307 strcpy((char*)(&net_packet->data[25]), name);
308 net_packet->data[25 + strlen(name)] = 0;
309 net_packet->address.host = net_clients[player - 1].host;
310 net_packet->address.port = net_clients[player - 1].port;
311 net_packet->len = 25 + strlen(name) + 1;
312 sendPacketSafe(net_sock, -1, net_packet, player - 1);
313 }
314 }
315
316 /*-------------------------------------------------------------------------------
317
318 drawStatus
319
320 Draws various status bar elements, such as textbox, health, magic,
321 and the hotbar
322
323 -------------------------------------------------------------------------------*/
324
325 bool mouseInBoundsRealtimeCoords(int, int, int, int); //Defined in playerinventory.cpp. Dirty hack, you should be ashamed of yourself.
326
warpMouseToSelectedHotbarSlot()327 void warpMouseToSelectedHotbarSlot()
328 {
329 SDL_Rect pos;
330 pos.x = ((xres / 2) - 5 * hotbar_img->w * uiscale_hotbar) + (current_hotbar * hotbar_img->w * uiscale_hotbar) + (hotbar_img->w * uiscale_hotbar / 2);
331 pos.y = STATUS_Y - (hotbar_img->h * uiscale_hotbar / 2);
332 SDL_WarpMouseInWindow(screen, pos.x, pos.y);
333 }
334
drawStatus()335 void drawStatus()
336 {
337 SDL_Rect pos, initial_position;
338 Sint32 x, y, z, c, i;
339 node_t* node;
340 string_t* string;
341 pos.x = STATUS_X;
342
343 if ( !hide_statusbar )
344 {
345 pos.y = STATUS_Y;
346 }
347 else
348 {
349 pos.y = yres - 16;
350 }
351 //To garner the position of the hotbar.
352 initial_position.x = HOTBAR_START_X;
353 initial_position.y = pos.y;
354 initial_position.w = 0;
355 initial_position.h = 0;
356 pos.w = status_bmp->w * uiscale_chatlog;
357 pos.h = status_bmp->h * uiscale_chatlog;
358 if ( !hide_statusbar )
359 {
360 drawImageScaled(status_bmp, NULL, &pos);
361 }
362
363 interfaceMessageStatusBar.x = pos.x;
364 interfaceMessageStatusBar.y = pos.y;
365 interfaceMessageStatusBar.w = pos.w;
366 interfaceMessageStatusBar.h = pos.h;
367
368 // enemy health
369 enemyHPDamageBarHandler.displayCurrentHPBar();
370
371 // messages
372 if ( !hide_statusbar )
373 {
374 x = xres / 2 - (status_bmp->w * uiscale_chatlog / 2) + 24 * uiscale_chatlog;
375 y = yres;
376 textscroll = std::max(std::min<Uint32>(list_Size(&messages) - 3, textscroll), 0u);
377 c = 0;
378 for ( node = messages.last; node != NULL; node = node->prev )
379 {
380 c++;
381 if ( c <= textscroll )
382 {
383 continue;
384 }
385 string = (string_t*)node->element;
386 if ( uiscale_chatlog >= 1.5 )
387 {
388 y -= TTF16_HEIGHT * string->lines;
389 if ( y < yres - (status_bmp->h * uiscale_chatlog) + 8 * uiscale_chatlog )
390 {
391 break;
392 }
393 }
394 else if ( uiscale_chatlog != 1.f )
395 {
396 y -= TTF12_HEIGHT * string->lines;
397 if ( y < yres - status_bmp->h * 1.1 + 4 )
398 {
399 break;
400 }
401 }
402 else
403 {
404 y -= TTF12_HEIGHT * string->lines;
405 if ( y < yres - status_bmp->h + 4 )
406 {
407 break;
408 }
409 }
410 z = 0;
411 for ( i = 0; i < strlen(string->data); i++ )
412 {
413 if ( string->data[i] != 10 ) // newline
414 {
415 z++;
416 }
417 else
418 {
419 z = 0;
420 }
421 if ( z == 65 )
422 {
423 if ( string->data[i] != 10 )
424 {
425 char* tempString = (char*)malloc(sizeof(char) * (strlen(string->data) + 2));
426 strcpy(tempString, string->data);
427 strcpy((char*)(tempString + i + 1), (char*)(string->data + i));
428 tempString[i] = 10;
429 free(string->data);
430 string->data = tempString;
431 string->lines++;
432 }
433 z = 0;
434 }
435 }
436 Uint32 color = SDL_MapRGBA(mainsurface->format, 0, 0, 0, 255); // black color
437 if ( uiscale_chatlog >= 1.5 )
438 {
439 ttfPrintTextColor(ttf16, x, y, color, false, string->data);
440 }
441 else
442 {
443 ttfPrintTextColor(ttf12, x, y, color, false, string->data);
444 }
445 }
446 if ( mousestatus[SDL_BUTTON_LEFT] )
447 {
448 if ( omousey >= yres - status_bmp->h * uiscale_chatlog + 7 && omousey < yres - status_bmp->h * uiscale_chatlog + (7 + 27) * uiscale_chatlog )
449 {
450 if ( omousex >= xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 618 * uiscale_chatlog && omousex < xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 618 * uiscale_chatlog + 11 * uiscale_chatlog )
451 {
452 // text scroll up
453 buttonclick = 3;
454 textscroll++;
455 mousestatus[SDL_BUTTON_LEFT] = 0;
456 }
457 }
458 else if ( omousey >= yres - status_bmp->h * uiscale_chatlog + 34 && omousey < yres - status_bmp->h * uiscale_chatlog + (34 + 28) * uiscale_chatlog )
459 {
460 if ( omousex >= xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 618 * uiscale_chatlog && omousex < xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 618 * uiscale_chatlog + 11 * uiscale_chatlog )
461 {
462 // text scroll down
463 buttonclick = 12;
464 textscroll--;
465 if ( textscroll < 0 )
466 {
467 textscroll = 0;
468 }
469 mousestatus[SDL_BUTTON_LEFT] = 0;
470 }
471 }
472 else if ( omousey >= yres - status_bmp->h * uiscale_chatlog + 62 && omousey < yres - status_bmp->h * uiscale_chatlog + (62 + 31) * uiscale_chatlog )
473 {
474 if ( omousex >= xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 618 * uiscale_chatlog && omousex < xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 618 * uiscale_chatlog + 11 * uiscale_chatlog )
475 {
476 // text scroll down all the way
477 buttonclick = 4;
478 textscroll = 0;
479 mousestatus[SDL_BUTTON_LEFT] = 0;
480 }
481 }
482 /*else if( omousey>=yres-status_bmp->h+8 && omousey<yres-status_bmp->h+8+30 ) {
483 if( omousex>=xres/2-status_bmp->w/2+618 && omousex<xres/2-status_bmp->w/2+618+11 ) {
484 // text scroll up all the way
485 buttonclick=13;
486 textscroll=list_Size(&messages)-4;
487 mousestatus[SDL_BUTTON_LEFT]=0;
488 }
489 }*/
490 }
491
492 // mouse wheel
493 if ( !shootmode )
494 {
495 if ( mousex >= STATUS_X && mousex < STATUS_X + status_bmp->w * uiscale_chatlog )
496 {
497 if ( mousey >= initial_position.y && mousey < initial_position.y + status_bmp->h * uiscale_chatlog )
498 {
499 if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
500 {
501 mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
502 textscroll--;
503 if ( textscroll < 0 )
504 {
505 textscroll = 0;
506 }
507 }
508 else if ( mousestatus[SDL_BUTTON_WHEELUP] )
509 {
510 mousestatus[SDL_BUTTON_WHEELUP] = 0;
511 textscroll++;
512 }
513 }
514 }
515 }
516 if (showfirst)
517 {
518 textscroll = list_Size(&messages) - 3;
519 }
520
521
522 //Text scroll up button.
523 if ( buttonclick == 3 )
524 {
525 pos.x = xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 617 * uiscale_chatlog;
526 pos.y = yres - status_bmp->h * uiscale_chatlog + 7 * uiscale_chatlog;
527 pos.w = 11 * uiscale_chatlog;
528 pos.h = 27 * uiscale_chatlog;
529 drawRect(&pos, SDL_MapRGB(mainsurface->format, 255, 255, 255), 80);
530 //drawImage(textup_bmp, NULL, &pos);
531 }
532 //Text scroll down all the way button.
533 if ( buttonclick == 4 )
534 {
535 pos.x = xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 617 * uiscale_chatlog;
536 pos.y = yres - status_bmp->h * uiscale_chatlog + 62 * uiscale_chatlog;
537 pos.w = 11 * uiscale_chatlog;
538 pos.h = 31 * uiscale_chatlog;
539 drawRect(&pos, SDL_MapRGB(mainsurface->format, 255, 255, 255), 80);
540 //drawImage(textdown_bmp, NULL, &pos);
541 }
542 //Text scroll down button.
543 if ( buttonclick == 12 )
544 {
545 pos.x = xres / 2 - status_bmp->w * uiscale_chatlog / 2 + 617 * uiscale_chatlog;
546 pos.y = yres - status_bmp->h * uiscale_chatlog + 34 * uiscale_chatlog;
547 pos.w = 11 * uiscale_chatlog;
548 pos.h = 28 * uiscale_chatlog;
549 drawRect(&pos, SDL_MapRGB(mainsurface->format, 255, 255, 255), 80);
550 //drawImage(textup_bmp, NULL, &pos);
551 }
552 //Text scroll up all the way button.
553 /*if( buttonclick==13 ) {
554 pos.x=xres/2-status_bmp->w/2+617; pos.y=yres-status_bmp->h+8;
555 pos.w=11; pos.h=30;
556 drawRect(&pos,SDL_MapRGB(mainsurface->format,255,255,255),80);
557 //drawImage(textdown_bmp, NULL, &pos);
558 }*/
559 }
560
561 int playerStatusBarWidth = 38 * uiscale_playerbars;
562 int playerStatusBarHeight = 156 * uiscale_playerbars;
563
564 // PLAYER HEALTH BAR
565 // Display Health bar border
566 pos.x = 38 + 38 * uiscale_playerbars;
567 pos.w = playerStatusBarWidth;
568 pos.h = playerStatusBarHeight;
569 pos.y = yres - (playerStatusBarHeight + 12);
570 drawTooltip(&pos);
571 if ( stats[clientnum] && stats[clientnum]->HP > 0
572 && stats[clientnum]->EFFECTS[EFF_HP_REGEN] )
573 {
574 bool lowDurationFlash = !((ticks % 50) - (ticks % 25));
575 bool lowDuration = stats[clientnum]->EFFECTS_TIMERS[EFF_HP_REGEN] > 0 &&
576 (stats[clientnum]->EFFECTS_TIMERS[EFF_HP_REGEN] < TICKS_PER_SECOND * 5);
577 if ( (lowDuration && !lowDurationFlash) || !lowDuration )
578 {
579 if ( colorblind )
580 {
581 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 255)); // blue
582 }
583 else
584 {
585 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 0)); // green
586 }
587 }
588 }
589
590 // Display "HP" at top of Health bar
591 ttfPrintText(ttf12, pos.x + (playerStatusBarWidth / 2 - 10), pos.y + 6, language[306]);
592
593 // Display border between actual Health bar and "HP"
594 //pos.x = 76;
595 pos.w = playerStatusBarWidth;
596 pos.h = 0;
597 pos.y = yres - (playerStatusBarHeight - 9);
598 drawTooltip(&pos);
599 if ( stats[clientnum] && stats[clientnum]->HP > 0
600 && stats[clientnum]->EFFECTS[EFF_HP_REGEN] )
601 {
602 bool lowDurationFlash = !((ticks % 50) - (ticks % 25));
603 bool lowDuration = stats[clientnum]->EFFECTS_TIMERS[EFF_HP_REGEN] > 0 &&
604 (stats[clientnum]->EFFECTS_TIMERS[EFF_HP_REGEN] < TICKS_PER_SECOND * 5);
605 if ( (lowDuration && !lowDurationFlash) || !lowDuration )
606 {
607 if ( colorblind )
608 {
609 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 255)); // blue
610 }
611 else
612 {
613 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 0)); // green
614 }
615 }
616 }
617
618 // Display the actual Health bar's faint background
619 pos.x = 42 + 38 * uiscale_playerbars;
620 pos.w = playerStatusBarWidth - 5;
621 pos.h = playerStatusBarHeight - 27;
622 pos.y = yres - 15 - pos.h;
623
624 // Change the color depending on if you are poisoned
625 Uint32 color = 0;
626 if ( stats[clientnum]->EFFECTS[EFF_POISONED] )
627 {
628 if ( colorblind )
629 {
630 color = SDL_MapRGB(mainsurface->format, 0, 0, 48); // Display blue
631 }
632 else
633 {
634 color = SDL_MapRGB(mainsurface->format, 0, 48, 0); // Display green
635 }
636 }
637 else
638 {
639 color = SDL_MapRGB(mainsurface->format, 48, 0, 0); // Display red
640 }
641
642 // Draw the actual Health bar's faint background with specified color
643 drawRect(&pos, color, 255);
644
645 // If the Player is alive, base the size of the actual Health bar off remaining HP
646 if ( stats[clientnum]->HP > 0 )
647 {
648 //pos.x = 80;
649 pos.w = playerStatusBarWidth - 5;
650 pos.h = (playerStatusBarHeight - 27) * (static_cast<double>(stats[clientnum]->HP) / stats[clientnum]->MAXHP);
651 pos.y = yres - 15 - pos.h;
652
653 if ( stats[clientnum]->EFFECTS[EFF_POISONED] )
654 {
655 if ( !colorblind )
656 {
657 color = SDL_MapRGB(mainsurface->format, 0, 128, 0);
658 }
659 else
660 {
661 color = SDL_MapRGB(mainsurface->format, 0, 0, 128);
662 }
663 }
664 else
665 {
666 color = SDL_MapRGB(mainsurface->format, 128, 0, 0);
667 }
668
669 // Only draw the actual Health bar if the Player is alive
670 drawRect(&pos, color, 255);
671 }
672
673 // Print out the amount of HP the Player currently has
674 snprintf(tempstr, 4, "%d", stats[clientnum]->HP);
675 if ( uiscale_playerbars >= 1.5 )
676 {
677 pos.x += uiscale_playerbars * 2;
678 }
679 printTextFormatted(font12x12_bmp, pos.x + 16 * uiscale_playerbars - strlen(tempstr) * 6, yres - (playerStatusBarHeight / 2 + 8), tempstr);
680 int xoffset = pos.x;
681
682 // hunger icon
683 if ( stats[clientnum]->type != AUTOMATON
684 && (svFlags & SV_FLAG_HUNGER) && stats[clientnum]->HUNGER <= 250 && (ticks % 50) - (ticks % 25) )
685 {
686 pos.x = xoffset + playerStatusBarWidth + 10; // was pos.x = 128;
687 pos.y = yres - 160;
688 pos.w = 64;
689 pos.h = 64;
690 if ( players[clientnum] && players[clientnum]->entity && players[clientnum]->entity->playerRequiresBloodToSustain() )
691 {
692 drawImageScaled(hunger_blood_bmp, NULL, &pos);
693 }
694 else
695 {
696 drawImageScaled(hunger_bmp, NULL, &pos);
697 }
698 }
699
700 if ( stats[clientnum]->type == AUTOMATON )
701 {
702 if ( stats[clientnum]->HUNGER > 300 || (ticks % 50) - (ticks % 25) )
703 {
704 pos.x = xoffset + playerStatusBarWidth + 10; // was pos.x = 128;
705 pos.y = yres - 160;
706 pos.w = 64;
707 pos.h = 64;
708 if ( stats[clientnum]->HUNGER > 1200 )
709 {
710 drawImageScaled(hunger_boiler_hotflame_bmp, nullptr, &pos);
711 }
712 else
713 {
714 if ( stats[clientnum]->HUNGER > 600 )
715 {
716 drawImageScaledPartial(hunger_boiler_flame_bmp, nullptr, &pos, 1.f);
717 }
718 else
719 {
720 float percent = (stats[clientnum]->HUNGER - 300) / 300.f; // always show a little bit more at the bottom (10-20%)
721 drawImageScaledPartial(hunger_boiler_flame_bmp, nullptr, &pos, percent);
722 }
723 }
724 drawImageScaled(hunger_boiler_bmp, nullptr, &pos);
725 }
726 }
727
728 // minotaur icon
729 if ( minotaurlevel && (ticks % 50) - (ticks % 25) )
730 {
731 pos.x = xoffset + playerStatusBarWidth + 10; // was pos.x = 128;
732 pos.y = yres - 160 + 64 + 2;
733 pos.w = 64;
734 pos.h = 64;
735 drawImageScaled(minotaur_bmp, nullptr, &pos);
736 }
737
738
739 // PLAYER MAGIC BAR
740 // Display the Magic bar border
741 pos.x = 12 * uiscale_playerbars;
742 pos.w = playerStatusBarWidth;
743 pos.h = playerStatusBarHeight;
744 pos.y = yres - (playerStatusBarHeight + 12);
745 drawTooltip(&pos);
746 if ( stats[clientnum] && stats[clientnum]->HP > 0
747 && stats[clientnum]->EFFECTS[EFF_MP_REGEN] )
748 {
749 bool lowDurationFlash = !((ticks % 50) - (ticks % 25));
750 bool lowDuration = stats[clientnum]->EFFECTS_TIMERS[EFF_MP_REGEN] > 0 &&
751 (stats[clientnum]->EFFECTS_TIMERS[EFF_MP_REGEN] < TICKS_PER_SECOND * 5);
752 if ( (lowDuration && !lowDurationFlash) || !lowDuration )
753 {
754 if ( colorblind )
755 {
756 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 255)); // blue
757 }
758 else
759 {
760 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 0)); // green
761 }
762 }
763 }
764 Uint32 mpColorBG = SDL_MapRGB(mainsurface->format, 0, 0, 48);
765 Uint32 mpColorFG = SDL_MapRGB(mainsurface->format, 0, 24, 128);
766 if ( stats[clientnum] && stats[clientnum]->playerRace == RACE_INSECTOID && stats[clientnum]->appearance == 0 )
767 {
768 ttfPrintText(ttf12, pos.x + (playerStatusBarWidth / 2 - 10), pos.y + 6, language[3768]);
769 mpColorBG = SDL_MapRGB(mainsurface->format, 32, 48, 0);
770 mpColorFG = SDL_MapRGB(mainsurface->format, 92, 192, 0);
771 }
772 else if ( stats[clientnum] && stats[clientnum]->type == AUTOMATON )
773 {
774 ttfPrintText(ttf12, pos.x + (playerStatusBarWidth / 2 - 10), pos.y + 6, language[3474]);
775 mpColorBG = SDL_MapRGB(mainsurface->format, 64, 32, 0);
776 mpColorFG = SDL_MapRGB(mainsurface->format, 192, 92, 0);
777 }
778 else
779 {
780 // Display "MP" at the top of Magic bar
781 ttfPrintText(ttf12, pos.x + (playerStatusBarWidth / 2 - 10), pos.y + 6, language[307]);
782 }
783
784 // Display border between actual Magic bar and "MP"
785 //pos.x = 12;
786 pos.w = playerStatusBarWidth;
787 pos.h = 0;
788 pos.y = yres - (playerStatusBarHeight - 9);
789 drawTooltip(&pos);
790 if ( stats[clientnum] && stats[clientnum]->HP > 0
791 && stats[clientnum]->EFFECTS[EFF_MP_REGEN] )
792 {
793 bool lowDurationFlash = !((ticks % 50) - (ticks % 25));
794 bool lowDuration = stats[clientnum]->EFFECTS_TIMERS[EFF_MP_REGEN] > 0 &&
795 (stats[clientnum]->EFFECTS_TIMERS[EFF_MP_REGEN] < TICKS_PER_SECOND * 5);
796 if ( (lowDuration && !lowDurationFlash) || !lowDuration )
797 {
798 if ( colorblind )
799 {
800 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 255)); // blue
801 }
802 else
803 {
804 drawTooltip(&pos, SDL_MapRGB(mainsurface->format, 0, 255, 0)); // green
805 }
806 }
807 }
808
809 // Display the actual Magic bar's faint background
810 pos.x = 4 + 12 * uiscale_playerbars;
811 pos.w = playerStatusBarWidth - 5;
812 pos.h = playerStatusBarHeight - 27;
813 pos.y = yres - 15 - pos.h;
814
815 // Draw the actual Magic bar's faint background
816 drawRect(&pos, mpColorBG, 255); // Display blue
817
818 // If the Player has MP, base the size of the actual Magic bar off remaining MP
819 if ( stats[clientnum]->MP > 0 )
820 {
821 //pos.x = 16;
822 pos.w = playerStatusBarWidth - 5;
823 pos.h = (playerStatusBarHeight - 27) * (static_cast<double>(stats[clientnum]->MP) / stats[clientnum]->MAXMP);
824 pos.y = yres - 15 - pos.h;
825
826 // Only draw the actual Magic bar if the Player has MP
827 drawRect(&pos, mpColorFG, 255); // Display blue
828 }
829
830 // Print out the amount of MP the Player currently has
831 snprintf(tempstr, 4, "%d", stats[clientnum]->MP);
832 printTextFormatted(font12x12_bmp, 32 * uiscale_playerbars - strlen(tempstr) * 6, yres - (playerStatusBarHeight / 2 + 8), tempstr);
833
834 Item* item = nullptr;
835 //Now the hotbar.
836 int num = 0;
837 //Reset the position to the top left corner of the status bar to draw the hotbar slots..
838 //pos.x = initial_position.x;
839 pos.x = (xres / 2) - 5 * hotbar_img->w * uiscale_hotbar;
840 pos.y = initial_position.y - hotbar_img->h * uiscale_hotbar;
841 for ( num = 0; num < NUM_HOTBAR_SLOTS; ++num, pos.x += hotbar_img->w * uiscale_hotbar )
842 {
843 Uint32 color;
844 if ( current_hotbar == num && !openedChest[clientnum] )
845 {
846 color = SDL_MapRGBA(mainsurface->format, 255, 255, 0, 255); //Draw gold border around currently selected hotbar.
847 }
848 else
849 {
850 color = SDL_MapRGBA(mainsurface->format, 255, 255, 255, 60); //Draw normal grey border.
851 }
852 pos.w = hotbar_img->w * uiscale_hotbar;
853 pos.h = hotbar_img->h * uiscale_hotbar;
854 drawImageScaledColor(hotbar_img, NULL, &pos, color);
855 item = uidToItem(hotbar[num].item);
856 if ( item )
857 {
858 if ( item->type == BOOMERANG )
859 {
860 magicBoomerangHotbarSlot = num;
861 }
862 bool used = false;
863 pos.w = hotbar_img->w * uiscale_hotbar;
864 pos.h = hotbar_img->h * uiscale_hotbar;
865
866 SDL_Rect highlightBox;
867 highlightBox.x = pos.x + 2;
868 highlightBox.y = pos.y + 2;
869 highlightBox.w = 60 * uiscale_hotbar;
870 highlightBox.h = 60 * uiscale_hotbar;
871
872 if ( !item->identified )
873 {
874 // give it a yellow background if it is unidentified
875 drawRect(&highlightBox, SDL_MapRGB(mainsurface->format, 128, 128, 0), 64); //31875
876 }
877 else if ( item->beatitude < 0 )
878 {
879 // give it a red background if cursed
880 drawRect(&highlightBox, SDL_MapRGB(mainsurface->format, 128, 0, 0), 64);
881 }
882 else if ( item->beatitude > 0 )
883 {
884 // give it a green background if blessed (light blue if colorblind mode)
885 if ( colorblind )
886 {
887 drawRect(&highlightBox, SDL_MapRGB(mainsurface->format, 50, 128, 128), 64);
888 }
889 else
890 {
891 drawRect(&highlightBox, SDL_MapRGB(mainsurface->format, 0, 128, 0), 64);
892 }
893 }
894 if ( item->status == BROKEN )
895 {
896 drawRect(&highlightBox, SDL_MapRGB(mainsurface->format, 64, 64, 64), 125);
897 }
898
899 drawImageScaled(itemSprite(item), NULL, &pos);
900
901 bool disableItemUsage = false;
902
903 if ( players[clientnum] && players[clientnum]->entity && players[clientnum]->entity->effectShapeshift != NOTHING )
904 {
905 // shape shifted, disable some items
906 if ( !item->usableWhileShapeshifted(stats[clientnum]) )
907 {
908 disableItemUsage = true;
909 drawRect(&highlightBox, SDL_MapRGB(mainsurface->format, 64, 64, 64), 144);
910 }
911 }
912 if ( client_classes[clientnum] == CLASS_SHAMAN )
913 {
914 if ( item->type == SPELL_ITEM && !(playerUnlockedShamanSpell(clientnum, item)) )
915 {
916 disableItemUsage = true;
917 drawRect(&highlightBox, SDL_MapRGB(mainsurface->format, 64, 64, 64), 144);
918 }
919 }
920
921 if ( stats[clientnum]->HP > 0 )
922 {
923 if ( !shootmode && mouseInBounds(pos.x, pos.x + hotbar_img->w * uiscale_hotbar, pos.y, pos.y + hotbar_img->h * uiscale_hotbar) )
924 {
925 if ( (mousestatus[SDL_BUTTON_LEFT]
926 || (*inputPressed(joyimpulses[INJOY_MENU_LEFT_CLICK])
927 && !openedChest[clientnum]
928 && gui_mode != (GUI_MODE_SHOP)
929 && !identifygui_active
930 && !removecursegui_active
931 && !GenericGUI.isGUIOpen()))
932 && !selectedItem )
933 {
934 toggleclick = false;
935 if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] )
936 {
937 hotbar[num].item = 0;
938 }
939 else
940 {
941 //Remove the item if left clicked.
942 selectedItem = item;
943 if ( selectedItem )
944 {
945 playSound(139, 64); // click sound
946 }
947 hotbar[num].item = 0;
948
949 if ( *inputPressed(joyimpulses[INJOY_MENU_LEFT_CLICK]) && !openedChest[clientnum] && gui_mode != (GUI_MODE_SHOP) && !identifygui_active )
950 {
951 *inputPressed(joyimpulses[INJOY_MENU_LEFT_CLICK]) = 0;
952 //itemSelectBehavior = BEHAVIOR_GAMEPAD;
953 toggleclick = true;
954 selectedItemFromHotbar = num;
955 //TODO: Change the mouse cursor to THE HAND.
956 }
957 }
958 }
959 if ( mousestatus[SDL_BUTTON_RIGHT]
960 || (*inputPressed(joyimpulses[INJOY_MENU_USE])
961 && !openedChest[clientnum]
962 && gui_mode != (GUI_MODE_SHOP)
963 && !identifygui_active
964 && !removecursegui_active
965 && !GenericGUI.isGUIOpen()) )
966 {
967 //Use the item if right clicked.
968 mousestatus[SDL_BUTTON_RIGHT] = 0;
969 *inputPressed(joyimpulses[INJOY_MENU_USE]) = 0;
970 bool badpotion = false;
971 bool learnedSpell = false;
972
973 if ( itemCategory(item) == POTION && item->identified )
974 {
975 badpotion = isPotionBad(*item); //So that you wield empty potions be default.
976 }
977 if ( item->type == POTION_EMPTY )
978 {
979 badpotion = true;
980 }
981 if ( itemCategory(item) == SPELLBOOK && (item->identified || itemIsEquipped(item, clientnum)) )
982 {
983 // equipped spellbook will unequip on use.
984 learnedSpell = (playerLearnedSpellbook(item) || itemIsEquipped(item, clientnum));
985 }
986
987 if ( keystatus[SDL_SCANCODE_LSHIFT] || keystatus[SDL_SCANCODE_RSHIFT] )
988 {
989 identifygui_active = false;
990 identifygui_appraising = true;
991
992 //Cleanup identify GUI gamecontroller code here.
993 selectedIdentifySlot = -1;
994
995 identifyGUIIdentify(item);
996 }
997 else
998 {
999 if ( (itemCategory(item) == POTION || itemCategory(item) == SPELLBOOK || item->type == FOOD_CREAMPIE )
1000 && (keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT]) )
1001 {
1002 badpotion = true;
1003 learnedSpell = true;
1004 }
1005
1006 if ( !learnedSpell && item->identified
1007 && itemCategory(item) == SPELLBOOK && players[clientnum] && players[clientnum]->entity )
1008 {
1009 learnedSpell = true; // let's always equip/unequip spellbooks from the hotbar?
1010 spell_t* currentSpell = getSpellFromID(getSpellIDFromSpellbook(item->type));
1011 if ( currentSpell )
1012 {
1013 int skillLVL = stats[clientnum]->PROFICIENCIES[PRO_MAGIC] + statGetINT(stats[clientnum], players[clientnum]->entity);
1014 if ( stats[clientnum]->PROFICIENCIES[PRO_MAGIC] >= 100 )
1015 {
1016 skillLVL = 100;
1017 }
1018 if ( skillLVL >= currentSpell->difficulty )
1019 {
1020 // can learn spell, try that instead.
1021 learnedSpell = false;
1022 }
1023 }
1024 }
1025
1026 if ( itemCategory(item) == SPELLBOOK && stats[clientnum] && stats[clientnum]->type == GOBLIN )
1027 {
1028 learnedSpell = true; // goblinos can't learn spells but always equip books.
1029 }
1030
1031 if ( !badpotion && !learnedSpell )
1032 {
1033 if ( !(isItemEquippableInShieldSlot(item) && cast_animation.active_spellbook) )
1034 {
1035 if ( !disableItemUsage )
1036 {
1037 if ( stats[clientnum] && stats[clientnum]->type == AUTOMATON
1038 && (item->type == TOOL_METAL_SCRAP || item->type == TOOL_MAGIC_SCRAP) )
1039 {
1040 // consume item
1041 if ( multiplayer == CLIENT )
1042 {
1043 strcpy((char*)net_packet->data, "FODA");
1044 SDLNet_Write32((Uint32)item->type, &net_packet->data[4]);
1045 SDLNet_Write32((Uint32)item->status, &net_packet->data[8]);
1046 SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[12]);
1047 SDLNet_Write32((Uint32)item->count, &net_packet->data[16]);
1048 SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
1049 net_packet->data[24] = item->identified;
1050 net_packet->data[25] = clientnum;
1051 net_packet->address.host = net_server.host;
1052 net_packet->address.port = net_server.port;
1053 net_packet->len = 26;
1054 sendPacketSafe(net_sock, -1, net_packet, 0);
1055 }
1056 item_FoodAutomaton(item, clientnum);
1057 }
1058 else
1059 {
1060 useItem(item, clientnum);
1061 }
1062 }
1063 else
1064 {
1065 if ( client_classes[clientnum] == CLASS_SHAMAN && item->type == SPELL_ITEM )
1066 {
1067 messagePlayer(clientnum, language[3488]); // unable to use with current level.
1068 }
1069 else
1070 {
1071 messagePlayer(clientnum, language[3432]); // unable to use in current form message.
1072 }
1073 }
1074 }
1075 }
1076 else
1077 {
1078 if ( !disableItemUsage )
1079 {
1080 playerTryEquipItemAndUpdateServer(item);
1081 }
1082 else
1083 {
1084 if ( client_classes[clientnum] == CLASS_SHAMAN && item->type == SPELL_ITEM )
1085 {
1086 messagePlayer(clientnum, language[3488]); // unable to use with current level.
1087 }
1088 else
1089 {
1090 messagePlayer(clientnum, language[3432]); // unable to use in current form message.
1091 }
1092 }
1093 }
1094 used = true;
1095 if ( disableItemUsage )
1096 {
1097 used = false;
1098 }
1099 }
1100 }
1101 }
1102 }
1103
1104 // item count
1105 if ( !used )
1106 {
1107 if ( item->count > 1 )
1108 {
1109 int digits = numdigits_sint16(item->count);
1110 SDL_Surface* digitFont = font12x12_bmp;
1111 if ( uiscale_hotbar >= 1.5 )
1112 {
1113 digitFont = font16x16_bmp;
1114 printTextFormatted(digitFont, pos.x + hotbar_img->w * uiscale_hotbar - (24 * digits), pos.y + hotbar_img->h * uiscale_hotbar - 24, "%d", item->count);
1115 }
1116 else
1117 {
1118 printTextFormatted(digitFont, pos.x + hotbar_img->w * uiscale_hotbar - (14 * digits), pos.y + hotbar_img->h * uiscale_hotbar - 14, "%d", item->count);
1119 }
1120 }
1121
1122 SDL_Rect src;
1123 src.x = pos.x + 2;
1124 src.h = 16 * uiscale_hotbar;
1125 src.y = pos.y + hotbar_img->h * uiscale_hotbar - src.h - 2;
1126 src.w = 16 * uiscale_hotbar;
1127
1128 // item equipped
1129 if ( itemCategory(item) != SPELL_CAT )
1130 {
1131 if ( itemIsEquipped(item, clientnum) )
1132 {
1133 drawImageScaled(equipped_bmp, NULL, &src);
1134 }
1135 else if ( item->status == BROKEN )
1136 {
1137 drawImageScaled(itembroken_bmp, NULL, &src);
1138 }
1139 }
1140 else
1141 {
1142 spell_t* spell = getSpellFromItem(item);
1143 if ( selected_spell == spell
1144 && (selected_spell_last_appearance == item->appearance || selected_spell_last_appearance == -1 ) )
1145 {
1146 drawImageScaled(equipped_bmp, NULL, &src);
1147 }
1148 }
1149 }
1150 }
1151 if ( uiscale_hotbar >= 1.5 )
1152 {
1153 printTextFormatted(font16x16_bmp, pos.x + 2, pos.y + 2, "%d", (num + 1) % 10); // slot number
1154 }
1155 else
1156 {
1157 printTextFormatted(font12x12_bmp, pos.x + 2, pos.y + 2, "%d", (num + 1) % 10); // slot number
1158 }
1159 }
1160
1161 bool drawHotBarTooltipOnCycle = false;
1162 if ( !intro && hotbarTooltipLastGameTick != 0 && (ticks - hotbarTooltipLastGameTick) < TICKS_PER_SECOND * 2 )
1163 {
1164 drawHotBarTooltipOnCycle = true;
1165 }
1166
1167 if ( !shootmode || drawHotBarTooltipOnCycle )
1168 {
1169 pos.x = initial_position.x;
1170 //Go back through all of the hotbar slots and draw the tooltips.
1171 for ( num = 0; num < NUM_HOTBAR_SLOTS; ++num, pos.x += hotbar_img->w * uiscale_hotbar )
1172 {
1173 item = uidToItem(hotbar[num].item);
1174 if ( item )
1175 {
1176 bool drawTooltipOnSlot = !shootmode && mouseInBounds(pos.x, pos.x + hotbar_img->w * uiscale_hotbar, pos.y, pos.y + hotbar_img->h * uiscale_hotbar);
1177 if ( !drawTooltipOnSlot )
1178 {
1179 if ( drawHotBarTooltipOnCycle && current_hotbar == num )
1180 {
1181 drawTooltipOnSlot = true;
1182 }
1183 }
1184 else
1185 {
1186 if ( !shootmode )
1187 {
1188 // reset timer.
1189 hotbarTooltipLastGameTick = 0;
1190 drawHotBarTooltipOnCycle = false;
1191 }
1192 else
1193 {
1194 if ( drawHotBarTooltipOnCycle )
1195 {
1196 drawTooltipOnSlot = false;
1197 }
1198 }
1199 }
1200
1201 if ( drawTooltipOnSlot )
1202 {
1203 //Tooltip
1204 SDL_Rect src;
1205 src.x = mousex + 16;
1206 src.y = mousey + 8;
1207
1208 if ( drawHotBarTooltipOnCycle )
1209 {
1210 src.x = pos.x + hotbar_img->w * uiscale_hotbar;
1211 src.y = pos.y + hotbar_img->h * uiscale_hotbar;
1212 src.y -= 16;
1213 }
1214
1215 if ( itemCategory(item) == SPELL_CAT )
1216 {
1217 spell_t* spell = getSpellFromItem(item);
1218 if ( drawHotBarTooltipOnCycle )
1219 {
1220 drawSpellTooltip(spell, item, &src);
1221 }
1222 else
1223 {
1224 drawSpellTooltip(spell, item, nullptr);
1225 }
1226 }
1227 else
1228 {
1229 src.w = std::max(13, longestline(item->description())) * TTF12_WIDTH + 8;
1230 src.h = TTF12_HEIGHT * 4 + 8;
1231 char spellEffectText[256] = "";
1232 if ( item->identified )
1233 {
1234 bool learnedSpellbook = false;
1235 if ( itemCategory(item) == SPELLBOOK )
1236 {
1237 learnedSpellbook = playerLearnedSpellbook(item);
1238 if ( !learnedSpellbook && stats[clientnum] && players[clientnum] && players[clientnum]->entity )
1239 {
1240 // spellbook tooltip shows if you have the magic requirement as well (for goblins)
1241 int skillLVL = stats[clientnum]->PROFICIENCIES[PRO_MAGIC] + statGetINT(stats[clientnum], players[clientnum]->entity);
1242 spell_t* spell = getSpellFromID(getSpellIDFromSpellbook(item->type));
1243 if ( spell && skillLVL >= spell->difficulty )
1244 {
1245 learnedSpellbook = true;
1246 }
1247 }
1248 }
1249
1250 if ( itemCategory(item) == WEAPON || itemCategory(item) == ARMOR || itemCategory(item) == THROWN
1251 || itemTypeIsQuiver(item->type) )
1252 {
1253 src.h += TTF12_HEIGHT;
1254 }
1255 else if ( itemCategory(item) == SCROLL && item->identified )
1256 {
1257 src.h += TTF12_HEIGHT;
1258 src.w = std::max((2 + longestline(language[3862]) + longestline(item->getScrollLabel())) * TTF12_WIDTH + 8, src.w);
1259 }
1260 else if ( itemCategory(item) == SPELLBOOK && learnedSpellbook )
1261 {
1262 int height = 1;
1263 char effectType[32] = "";
1264 int spellID = getSpellIDFromSpellbook(item->type);
1265 int damage = drawSpellTooltip(getSpellFromID(spellID), item, nullptr);
1266 real_t dummy = 0.f;
1267 getSpellEffectString(spellID, spellEffectText, effectType, damage, &height, &dummy);
1268 int width = longestline(spellEffectText) * TTF12_WIDTH + 8;
1269 if ( width > src.w )
1270 {
1271 src.w = width;
1272 }
1273 src.h += height * TTF12_HEIGHT;
1274 }
1275 else if ( item->type == TOOL_GYROBOT || item->type == TOOL_DUMMYBOT
1276 || item->type == TOOL_SENTRYBOT || item->type == TOOL_SPELLBOT
1277 || (item->type == ENCHANTED_FEATHER && item->identified) )
1278 {
1279 src.w += 7 * TTF12_WIDTH;
1280 }
1281 }
1282
1283 int furthestX = xres;
1284 if ( proficienciesPage == 0 )
1285 {
1286 if ( src.y < interfaceSkillsSheet.y + interfaceSkillsSheet.h )
1287 {
1288 furthestX = xres - interfaceSkillsSheet.w;
1289 }
1290 }
1291 else
1292 {
1293 if ( src.y < interfacePartySheet.y + interfacePartySheet.h )
1294 {
1295 furthestX = xres - interfacePartySheet.w;
1296 }
1297 }
1298 if ( src.x + src.w + 16 > furthestX ) // overflow right side of screen
1299 {
1300 src.x -= (src.w + 32);
1301 }
1302 if ( src.y + src.h + 16 > yres ) // overflow bottom of screen
1303 {
1304 src.y -= (src.y + src.h + 16 - yres);
1305 }
1306
1307 if ( drawHotBarTooltipOnCycle )
1308 {
1309 drawTooltip(&src);
1310 }
1311 else
1312 {
1313 drawTooltip(&src);
1314 }
1315
1316 Uint32 color = 0xFFFFFFFF;
1317 if ( !item->identified )
1318 {
1319 color = SDL_MapRGB(mainsurface->format, 255, 255, 0);
1320 ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, language[309]);
1321 }
1322 else
1323 {
1324 if ( item->beatitude < 0 )
1325 {
1326 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
1327 ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, language[310]);
1328 }
1329 else if ( item->beatitude == 0 )
1330 {
1331 color = 0xFFFFFFFF;
1332 ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, language[311]);
1333 }
1334 else
1335 {
1336 color = SDL_MapRGB(mainsurface->format, 0, 255, 0);
1337 ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT, color, language[312]);
1338 }
1339 }
1340 if ( item->beatitude == 0 || !item->identified )
1341 {
1342 color = 0xFFFFFFFF;
1343 }
1344
1345 if ( item->type == TOOL_GYROBOT || item->type == TOOL_DUMMYBOT
1346 || item->type == TOOL_SENTRYBOT || item->type == TOOL_SPELLBOT )
1347 {
1348 int health = 100;
1349 if ( !item->tinkeringBotIsMaxHealth() )
1350 {
1351 health = 25 * (item->appearance % 10);
1352 if ( health == 0 && item->status != BROKEN )
1353 {
1354 health = 5;
1355 }
1356 }
1357 ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4, color, "%s (%d%%)", item->description(), health);
1358 }
1359 else if ( item->type == ENCHANTED_FEATHER && item->identified )
1360 {
1361 ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4, color, "%s (%d%%)", item->description(), item->appearance % ENCHANTED_FEATHER_MAX_DURABILITY);
1362 }
1363 else
1364 {
1365 ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4, color, "%s", item->description());
1366 }
1367 int itemWeight = items[item->type].weight * item->count;
1368 if ( itemTypeIsQuiver(item->type) )
1369 {
1370 itemWeight = std::max(1, itemWeight / 5);
1371 }
1372 ttfPrintTextFormatted(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 2, language[313], itemWeight);
1373 ttfPrintTextFormatted(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 3, language[314], item->sellValue(clientnum));
1374 if ( strcmp(spellEffectText, "") )
1375 {
1376 ttfPrintTextFormattedColor(ttf12, src.x + 4, src.y + 4 + TTF12_HEIGHT * 4, SDL_MapRGB(mainsurface->format, 0, 255, 255), spellEffectText);
1377 }
1378
1379 if ( item->identified )
1380 {
1381 if ( itemCategory(item) == WEAPON || itemCategory(item) == THROWN
1382 || itemTypeIsQuiver(item->type) )
1383 {
1384 Monster tmpRace = stats[clientnum]->type;
1385 if ( stats[clientnum]->type == TROLL
1386 || stats[clientnum]->type == RAT
1387 || stats[clientnum]->type == SPIDER
1388 || stats[clientnum]->type == CREATURE_IMP )
1389 {
1390 // these monsters have 0 bonus from weapons, but want the tooltip to say the normal amount.
1391 stats[clientnum]->type = HUMAN;
1392 }
1393
1394 if ( item->weaponGetAttack(stats[clientnum]) >= 0 )
1395 {
1396 color = SDL_MapRGB(mainsurface->format, 0, 255, 255);
1397 }
1398 else
1399 {
1400 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
1401 }
1402 if ( stats[clientnum]->type != tmpRace )
1403 {
1404 color = SDL_MapRGB(mainsurface->format, 127, 127, 127); // grey out the text if monster doesn't benefit.
1405 }
1406
1407 ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 4, color, language[315], item->weaponGetAttack(stats[clientnum]));
1408 stats[clientnum]->type = tmpRace;
1409 }
1410 else if ( itemCategory(item) == ARMOR )
1411 {
1412 Monster tmpRace = stats[clientnum]->type;
1413 if ( stats[clientnum]->type == TROLL
1414 || stats[clientnum]->type == RAT
1415 || stats[clientnum]->type == SPIDER
1416 || stats[clientnum]->type == CREATURE_IMP )
1417 {
1418 // these monsters have 0 bonus from armor, but want the tooltip to say the normal amount.
1419 stats[clientnum]->type = HUMAN;
1420 }
1421
1422 if ( item->armorGetAC(stats[clientnum]) >= 0 )
1423 {
1424 color = SDL_MapRGB(mainsurface->format, 0, 255, 255);
1425 }
1426 else
1427 {
1428 color = SDL_MapRGB(mainsurface->format, 255, 0, 0);
1429 }
1430 if ( stats[clientnum]->type != tmpRace )
1431 {
1432 color = SDL_MapRGB(mainsurface->format, 127, 127, 127); // grey out the text if monster doesn't benefit.
1433 }
1434
1435 ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 4, color, language[316], item->armorGetAC(stats[clientnum]));
1436 stats[clientnum]->type = tmpRace;
1437 }
1438 else if ( itemCategory(item) == SCROLL )
1439 {
1440 color = SDL_MapRGB(mainsurface->format, 0, 255, 255);
1441 ttfPrintTextFormattedColor(ttf12, src.x + 4 + TTF12_WIDTH, src.y + 4 + TTF12_HEIGHT * 4, color, "%s%s", language[3862], item->getScrollLabel());
1442 }
1443 }
1444 }
1445 if ( !drawHotBarTooltipOnCycle && hotbar_numkey_quick_add )
1446 {
1447 Uint32 swapItem = 0;
1448 if ( keystatus[SDL_SCANCODE_1] )
1449 {
1450 keystatus[SDL_SCANCODE_1] = 0;
1451 swapItem = hotbar[0].item;
1452 hotbar[0].item = hotbar[num].item;
1453 hotbar[num].item = swapItem;
1454 }
1455 if ( keystatus[SDL_SCANCODE_2] )
1456 {
1457 keystatus[SDL_SCANCODE_2] = 0;
1458 swapItem = hotbar[1].item;
1459 hotbar[1].item = hotbar[num].item;
1460 hotbar[num].item = swapItem;
1461 }
1462 if ( keystatus[SDL_SCANCODE_3] )
1463 {
1464 keystatus[SDL_SCANCODE_3] = 0;
1465 swapItem = hotbar[2].item;
1466 hotbar[2].item = hotbar[num].item;
1467 hotbar[num].item = swapItem;
1468 }
1469 if ( keystatus[SDL_SCANCODE_4] )
1470 {
1471 keystatus[SDL_SCANCODE_4] = 0;
1472 swapItem = hotbar[3].item;
1473 hotbar[3].item = hotbar[num].item;
1474 hotbar[num].item = swapItem;
1475 }
1476 if ( keystatus[SDL_SCANCODE_5] )
1477 {
1478 keystatus[SDL_SCANCODE_5] = 0;
1479 swapItem = hotbar[4].item;
1480 hotbar[4].item = hotbar[num].item;
1481 hotbar[num].item = swapItem;
1482 }
1483 if ( keystatus[SDL_SCANCODE_6] )
1484 {
1485 keystatus[SDL_SCANCODE_6] = 0;
1486 swapItem = hotbar[5].item;
1487 hotbar[5].item = hotbar[num].item;
1488 hotbar[num].item = swapItem;
1489 }
1490 if ( keystatus[SDL_SCANCODE_7] )
1491 {
1492 keystatus[SDL_SCANCODE_7] = 0;
1493 swapItem = hotbar[6].item;
1494 hotbar[6].item = hotbar[num].item;
1495 hotbar[num].item = swapItem;
1496 }
1497 if ( keystatus[SDL_SCANCODE_8] )
1498 {
1499 keystatus[SDL_SCANCODE_8] = 0;
1500 swapItem = hotbar[7].item;
1501 hotbar[7].item = hotbar[num].item;
1502 hotbar[num].item = swapItem;
1503 }
1504 if ( keystatus[SDL_SCANCODE_9] )
1505 {
1506 keystatus[SDL_SCANCODE_9] = 0;
1507 swapItem = hotbar[8].item;
1508 hotbar[8].item = hotbar[num].item;
1509 hotbar[num].item = swapItem;
1510 }
1511 if ( keystatus[SDL_SCANCODE_0] )
1512 {
1513 keystatus[SDL_SCANCODE_0] = 0;
1514 swapItem = hotbar[9].item;
1515 hotbar[9].item = hotbar[num].item;
1516 hotbar[num].item = swapItem;
1517 }
1518 }
1519 }
1520 }
1521 }
1522
1523 // minimap pinging.
1524 int minimapTotalScale = minimapScaleQuickToggle + minimapScale;
1525 if ( map.height > 64 || map.width > 64 )
1526 {
1527 int maxDimension = std::max(map.height, map.width);
1528 maxDimension -= 64;
1529 int numMinimapSizesToReduce = 0;
1530 while ( maxDimension > 0 )
1531 {
1532 maxDimension -= 32;
1533 ++numMinimapSizesToReduce;
1534 }
1535 minimapTotalScale = std::max(1, minimapScale - numMinimapSizesToReduce) + minimapScaleQuickToggle;
1536 }
1537 if ( !FollowerMenu.selectMoveTo && mouseInBounds(xres - map.width * minimapTotalScale, xres, yres - map.height * minimapTotalScale, yres) ) // mouse within minimap pixels (each map tile is 4 pixels)
1538 {
1539 if ( mousestatus[SDL_BUTTON_RIGHT] || (*inputPressed(joyimpulses[INJOY_MENU_USE])) )
1540 {
1541 mousestatus[SDL_BUTTON_RIGHT] = 0;
1542 *inputPressed(joyimpulses[INJOY_MENU_USE]) = 0;
1543 if ( minimapPingGimpTimer == -1 )
1544 {
1545 MinimapPing newPing(ticks, clientnum, (omousex - (xres - map.width * minimapTotalScale)) / minimapTotalScale, (omousey - (yres - map.height * minimapTotalScale)) / minimapTotalScale);
1546 minimapPingGimpTimer = TICKS_PER_SECOND / 4;
1547 if ( multiplayer != CLIENT )
1548 {
1549 minimapPingAdd(newPing);
1550 }
1551 sendMinimapPing(clientnum, newPing.x, newPing.y);
1552 }
1553 }
1554 }
1555 }
1556 if ( minimapPingGimpTimer >= 0 )
1557 {
1558 --minimapPingGimpTimer;
1559 }
1560
1561 //NOTE: If you change the number of hotbar slots, you *MUST* change this.
1562 if ( !command && stats[clientnum]->HP > 0 )
1563 {
1564 Item* item = NULL;
1565 if ( !(!shootmode && hotbar_numkey_quick_add &&
1566 (
1567 (omousex >= INVENTORY_STARTX
1568 && omousex <= INVENTORY_STARTX + INVENTORY_SIZEX * INVENTORY_SLOTSIZE
1569 && omousey >= INVENTORY_STARTY
1570 && omousey <= INVENTORY_STARTY + INVENTORY_SIZEY * INVENTORY_SLOTSIZE
1571 )
1572 ||
1573 (omousex >= initial_position.x
1574 && omousex <= initial_position.x + hotbar_img->w * uiscale_hotbar * 10
1575 && omousey >= initial_position.y - hotbar_img->h * uiscale_hotbar
1576 && omousey <= initial_position.y
1577 )
1578 )
1579 ) )
1580 {
1581 // if hotbar_numkey_quick_add is enabled, then the number keys won't do the default equip function
1582 // skips equipping items if the mouse is in the hotbar or inventory area. otherwise the below code runs.
1583 if ( keystatus[SDL_SCANCODE_1] )
1584 {
1585 keystatus[SDL_SCANCODE_1] = 0;
1586 item = uidToItem(hotbar[0].item);
1587 }
1588 if ( keystatus[SDL_SCANCODE_2] )
1589 {
1590 keystatus[SDL_SCANCODE_2] = 0;
1591 item = uidToItem(hotbar[1].item);
1592 }
1593 if ( keystatus[SDL_SCANCODE_3] )
1594 {
1595 keystatus[SDL_SCANCODE_3] = 0;
1596 item = uidToItem(hotbar[2].item);
1597 }
1598 if ( keystatus[SDL_SCANCODE_4] )
1599 {
1600 keystatus[SDL_SCANCODE_4] = 0;
1601 item = uidToItem(hotbar[3].item);
1602 }
1603 if ( keystatus[SDL_SCANCODE_5] )
1604 {
1605 keystatus[SDL_SCANCODE_5] = 0;
1606 item = uidToItem(hotbar[4].item);
1607 }
1608 if ( keystatus[SDL_SCANCODE_6] )
1609 {
1610 keystatus[SDL_SCANCODE_6] = 0;
1611 item = uidToItem(hotbar[5].item);
1612 }
1613 if ( keystatus[SDL_SCANCODE_7] )
1614 {
1615 keystatus[SDL_SCANCODE_7] = 0;
1616 item = uidToItem(hotbar[6].item);
1617 }
1618 if ( keystatus[SDL_SCANCODE_8] )
1619 {
1620 keystatus[SDL_SCANCODE_8] = 0;
1621 item = uidToItem(hotbar[7].item);
1622 }
1623 if ( keystatus[SDL_SCANCODE_9] )
1624 {
1625 keystatus[SDL_SCANCODE_9] = 0;
1626 item = uidToItem(hotbar[8].item);
1627 }
1628 if ( keystatus[SDL_SCANCODE_0] )
1629 {
1630 keystatus[SDL_SCANCODE_0] = 0;
1631 item = uidToItem(hotbar[9].item);
1632 }
1633 }
1634
1635 //Moving the cursor changes the currently selected hotbar slot.
1636 if ( (mousexrel || mouseyrel) && !shootmode )
1637 {
1638 pos.x = initial_position.x;
1639 pos.y = initial_position.y - hotbar_img->h * uiscale_hotbar;
1640 for ( c = 0; c < NUM_HOTBAR_SLOTS; ++c, pos.x += hotbar_img->w * uiscale_hotbar )
1641 {
1642 if ( mouseInBoundsRealtimeCoords(pos.x, pos.x + hotbar_img->w * uiscale_hotbar, pos.y, pos.y + hotbar_img->h * uiscale_hotbar) )
1643 {
1644 selectHotbarSlot(c);
1645 }
1646 }
1647 }
1648
1649 bool bumper_moved = false;
1650 //Gamepad change hotbar selection.
1651 if ( (*inputPressed(joyimpulses[INJOY_GAME_HOTBAR_NEXT]) || *inputPressed(impulses[IN_HOTBAR_SCROLL_RIGHT])) )
1652 {
1653 if ( shootmode && !itemMenuOpen && !openedChest[clientnum]
1654 && gui_mode != (GUI_MODE_SHOP) && !book_open
1655 && !identifygui_active && !removecursegui_active
1656 && !GenericGUI.isGUIOpen() )
1657 {
1658 if ( *inputPressed(impulses[IN_HOTBAR_SCROLL_RIGHT]) )
1659 {
1660 *inputPressed(impulses[IN_HOTBAR_SCROLL_RIGHT]) = 0;
1661 hotbarTooltipLastGameTick = ticks;
1662 }
1663 else
1664 {
1665 *inputPressed(joyimpulses[INJOY_GAME_HOTBAR_NEXT]) = 0;
1666 bumper_moved = true;
1667 }
1668 selectHotbarSlot(current_hotbar + 1);
1669 }
1670 else
1671 {
1672 hotbarTooltipLastGameTick = 0;
1673 /*if ( intro || shootmode )
1674 {
1675 if ( *inputPressed(impulses[IN_HOTBAR_SCROLL_RIGHT]) )
1676 {
1677 *inputPressed(impulses[IN_HOTBAR_SCROLL_RIGHT]) = 0;
1678 }
1679 }*/
1680 }
1681 }
1682 if ( *inputPressed(joyimpulses[INJOY_GAME_HOTBAR_PREV]) || *inputPressed(impulses[IN_HOTBAR_SCROLL_LEFT]) )
1683 {
1684 if ( shootmode && !itemMenuOpen && !openedChest[clientnum]
1685 && gui_mode != (GUI_MODE_SHOP) && !book_open
1686 && !identifygui_active && !removecursegui_active
1687 && !GenericGUI.isGUIOpen() )
1688 {
1689 if ( *inputPressed(impulses[IN_HOTBAR_SCROLL_LEFT]) )
1690 {
1691 *inputPressed(impulses[IN_HOTBAR_SCROLL_LEFT]) = 0;
1692 hotbarTooltipLastGameTick = ticks;
1693 }
1694 else
1695 {
1696 *inputPressed(joyimpulses[INJOY_GAME_HOTBAR_PREV]) = 0;
1697 bumper_moved = true;
1698 }
1699 selectHotbarSlot(current_hotbar - 1);
1700 }
1701 else
1702 {
1703 hotbarTooltipLastGameTick = 0;
1704 /*if ( intro || shootmode )
1705 {
1706 if ( *inputPressed(impulses[IN_HOTBAR_SCROLL_LEFT]) )
1707 {
1708 *inputPressed(impulses[IN_HOTBAR_SCROLL_LEFT]) = 0;
1709 }
1710 }*/
1711 }
1712 }
1713
1714 if ( bumper_moved && !itemMenuOpen
1715 && !openedChest[clientnum] && gui_mode != (GUI_MODE_SHOP)
1716 && !book_open && !identifygui_active
1717 && !removecursegui_active && !GenericGUI.isGUIOpen() )
1718 {
1719 warpMouseToSelectedHotbarSlot();
1720 }
1721
1722 if ( !itemMenuOpen && !selectedItem && !openedChest[clientnum] && gui_mode != (GUI_MODE_SHOP) )
1723 {
1724 if ( shootmode && (*inputPressed(joyimpulses[INJOY_GAME_HOTBAR_ACTIVATE]) || *inputPressed(impulses[IN_HOTBAR_SCROLL_SELECT]))
1725 && !openedChest[clientnum] && gui_mode != (GUI_MODE_SHOP)
1726 && !book_open && !identifygui_active
1727 && !removecursegui_active && !GenericGUI.isGUIOpen() )
1728 {
1729 //Activate a hotbar slot if in-game.
1730 if ( *inputPressed(impulses[IN_HOTBAR_SCROLL_SELECT]) )
1731 {
1732 hotbarTooltipLastGameTick = std::max(ticks - TICKS_PER_SECOND, ticks - hotbarTooltipLastGameTick);
1733 *inputPressed(impulses[IN_HOTBAR_SCROLL_SELECT]) = 0;
1734 }
1735 else
1736 {
1737 *inputPressed(joyimpulses[INJOY_GAME_HOTBAR_ACTIVATE]) = 0;
1738 }
1739 item = uidToItem(hotbar[current_hotbar].item);
1740 }
1741
1742 if ( !shootmode && *inputPressed(joyimpulses[INJOY_MENU_HOTBAR_CLEAR]) && !book_open ) //TODO: Don't activate if any of the previous if statement's conditions are true?
1743 {
1744 //Clear a hotbar slot if in-inventory.
1745 *inputPressed(joyimpulses[INJOY_MENU_HOTBAR_CLEAR]) = 0;
1746
1747 hotbar[current_hotbar].item = 0;
1748 }
1749
1750 pos.x = initial_position.x + (current_hotbar * hotbar_img->w * uiscale_hotbar);
1751 pos.y = initial_position.y - hotbar_img->h * uiscale_hotbar;
1752 if ( !shootmode && !book_open && !openedChest[clientnum] && *inputPressed(joyimpulses[INJOY_MENU_DROP_ITEM]) && mouseInBounds(pos.x, pos.x + hotbar_img->w * uiscale_hotbar, pos.y, pos.y + hotbar_img->h * uiscale_hotbar) )
1753 {
1754 //Drop item if this hotbar is currently active & the player pressed the cancel button on the gamepad (typically "b").
1755 *inputPressed(joyimpulses[INJOY_MENU_DROP_ITEM]) = 0;
1756 Item* itemToDrop = uidToItem(hotbar[current_hotbar].item);
1757 if ( itemToDrop )
1758 {
1759 dropItem(itemToDrop, clientnum);
1760 }
1761 }
1762 }
1763
1764 if ( item )
1765 {
1766 bool badpotion = false;
1767 bool learnedSpell = false;
1768 if ( itemCategory(item) == POTION && item->identified )
1769 {
1770 badpotion = isPotionBad(*item);
1771 }
1772 if ( item->type == POTION_EMPTY )
1773 {
1774 badpotion = true; //So that you wield empty potions be default.
1775 }
1776 if ( itemCategory(item) == SPELLBOOK && (item->identified || itemIsEquipped(item, clientnum)) )
1777 {
1778 // equipped spellbook will unequip on use.
1779 learnedSpell = (playerLearnedSpellbook(item) || itemIsEquipped(item, clientnum));
1780 }
1781
1782 if ( (keystatus[SDL_SCANCODE_LALT] || keystatus[SDL_SCANCODE_RALT])
1783 && (itemCategory(item) == POTION || itemCategory(item) == SPELLBOOK || item->type == FOOD_CREAMPIE) )
1784 {
1785 badpotion = true;
1786 learnedSpell = true;
1787 }
1788
1789 if ( !learnedSpell && item->identified
1790 && itemCategory(item) == SPELLBOOK && players[clientnum] && players[clientnum]->entity )
1791 {
1792 learnedSpell = true; // let's always equip/unequip spellbooks from the hotbar?
1793 spell_t* currentSpell = getSpellFromID(getSpellIDFromSpellbook(item->type));
1794 if ( currentSpell )
1795 {
1796 int skillLVL = stats[clientnum]->PROFICIENCIES[PRO_MAGIC] + statGetINT(stats[clientnum], players[clientnum]->entity);
1797 if ( stats[clientnum]->PROFICIENCIES[PRO_MAGIC] >= 100 )
1798 {
1799 skillLVL = 100;
1800 }
1801 if ( skillLVL >= currentSpell->difficulty )
1802 {
1803 // can learn spell, try that instead.
1804 learnedSpell = false;
1805 }
1806 }
1807 }
1808
1809 if ( itemCategory(item) == SPELLBOOK && stats[clientnum] )
1810 {
1811 if ( stats[clientnum]->type == GOBLIN || stats[clientnum]->type == CREATURE_IMP )
1812 {
1813 learnedSpell = true; // goblinos can't learn spells but always equip books.
1814 }
1815 }
1816
1817 bool disableItemUsage = false;
1818 if ( players[clientnum] && players[clientnum]->entity )
1819 {
1820 if ( players[clientnum]->entity->effectShapeshift != NOTHING )
1821 {
1822 if ( !item->usableWhileShapeshifted(stats[clientnum]) )
1823 {
1824 disableItemUsage = true;
1825 }
1826 }
1827 else
1828 {
1829 if ( itemCategory(item) == SPELL_CAT && item->appearance >= 1000 )
1830 {
1831 if ( canUseShapeshiftSpellInCurrentForm(*item) != 1 )
1832 {
1833 disableItemUsage = true;
1834 }
1835 }
1836 }
1837 }
1838 if ( client_classes[clientnum] == CLASS_SHAMAN )
1839 {
1840 if ( item->type == SPELL_ITEM && !(playerUnlockedShamanSpell(clientnum, item)) )
1841 {
1842 disableItemUsage = true;
1843 }
1844 }
1845
1846 if ( !disableItemUsage )
1847 {
1848 if ( !badpotion && !learnedSpell )
1849 {
1850 if ( !(isItemEquippableInShieldSlot(item) && cast_animation.active_spellbook) )
1851 {
1852 if ( stats[clientnum] && stats[clientnum]->type == AUTOMATON
1853 && (item->type == TOOL_METAL_SCRAP || item->type == TOOL_MAGIC_SCRAP) )
1854 {
1855 // consume item
1856 if ( multiplayer == CLIENT )
1857 {
1858 strcpy((char*)net_packet->data, "FODA");
1859 SDLNet_Write32((Uint32)item->type, &net_packet->data[4]);
1860 SDLNet_Write32((Uint32)item->status, &net_packet->data[8]);
1861 SDLNet_Write32((Uint32)item->beatitude, &net_packet->data[12]);
1862 SDLNet_Write32((Uint32)item->count, &net_packet->data[16]);
1863 SDLNet_Write32((Uint32)item->appearance, &net_packet->data[20]);
1864 net_packet->data[24] = item->identified;
1865 net_packet->data[25] = clientnum;
1866 net_packet->address.host = net_server.host;
1867 net_packet->address.port = net_server.port;
1868 net_packet->len = 26;
1869 sendPacketSafe(net_sock, -1, net_packet, 0);
1870 }
1871 item_FoodAutomaton(item, clientnum);
1872 }
1873 else
1874 {
1875 useItem(item, clientnum);
1876 }
1877 }
1878 }
1879 else
1880 {
1881 playerTryEquipItemAndUpdateServer(item);
1882 }
1883 }
1884 else
1885 {
1886 if ( client_classes[clientnum] == CLASS_SHAMAN && item->type == SPELL_ITEM )
1887 {
1888 messagePlayer(clientnum, language[3488]); // unable to use with current level.
1889 }
1890 else
1891 {
1892 messagePlayer(clientnum, language[3432]); // unable to use in current form message.
1893 }
1894 }
1895 }
1896 }
1897
1898 FollowerMenu.drawFollowerMenu();
1899
1900 // stat increase icons
1901 pos.w = 64;
1902 pos.h = 64;
1903 pos.x = xres - pos.w * 3 - 9;
1904 pos.y = (NUMPROFICIENCIES * TTF12_HEIGHT) + (TTF12_HEIGHT * 3) + (32 + 64 + 64 + 3); // 131px from end of prof window.
1905
1906 if ( (!shootmode || lock_right_sidebar) && proficienciesPage == 1
1907 && pos.y < (interfacePartySheet.y + interfacePartySheet.h + 16) )
1908 {
1909 pos.y = interfacePartySheet.y + interfacePartySheet.h + 16;
1910 }
1911
1912 if ( pos.y + pos.h > (yres - map.height * 4) ) // check if overlapping minimap
1913 {
1914 pos.y = (yres - map.height * 4) - (64 + 3); // align above minimap
1915 }
1916
1917 SDL_Surface *tmp_bmp = NULL;
1918
1919 for ( i = 0; i < NUMSTATS; i++ )
1920 {
1921 if ( stats[clientnum]->PLAYER_LVL_STAT_TIMER[i] > 0 && ((ticks % 50) - (ticks % 10)) )
1922 {
1923 stats[clientnum]->PLAYER_LVL_STAT_TIMER[i]--;
1924
1925 switch ( i )
1926 {
1927 // prepare the stat image.
1928 case STAT_STR:
1929 tmp_bmp = str_bmp64u;
1930 break;
1931 case STAT_DEX:
1932 tmp_bmp = dex_bmp64u;
1933 break;
1934 case STAT_CON:
1935 tmp_bmp = con_bmp64u;
1936 break;
1937 case STAT_INT:
1938 tmp_bmp = int_bmp64u;
1939 break;
1940 case STAT_PER:
1941 tmp_bmp = per_bmp64u;
1942 break;
1943 case STAT_CHR:
1944 tmp_bmp = chr_bmp64u;
1945 break;
1946 default:
1947 break;
1948 }
1949 drawImageScaled(tmp_bmp, NULL, &pos);
1950 if ( stats[clientnum]->PLAYER_LVL_STAT_TIMER[i + NUMSTATS] > 0 )
1951 {
1952 // bonus stat acheived, draw additional stat icon above.
1953 pos.y -= 64 + 3;
1954 drawImageScaled(tmp_bmp, NULL, &pos);
1955 pos.y += 64 + 3;
1956 stats[clientnum]->PLAYER_LVL_STAT_TIMER[i + NUMSTATS]--;
1957 }
1958
1959 pos.x += pos.h + 3;
1960 }
1961 }
1962 }
1963
drawSpellTooltip(spell_t * spell,Item * item,SDL_Rect * src)1964 int drawSpellTooltip(spell_t* spell, Item* item, SDL_Rect* src)
1965 {
1966 SDL_Rect pos;
1967 if ( src )
1968 {
1969 pos.x = src->x;
1970 pos.y = src->y;
1971 }
1972 else
1973 {
1974 pos.x = mousex + 16;
1975 pos.y = mousey + 8;
1976 }
1977 bool spellbook = false;
1978 if ( item && itemCategory(item) == SPELLBOOK )
1979 {
1980 spellbook = true;
1981 }
1982 if ( spell )
1983 {
1984 node_t* rootNode = spell->elements.first;
1985 spellElement_t* elementRoot = nullptr;
1986 if ( rootNode )
1987 {
1988 elementRoot = (spellElement_t*)(rootNode->element);
1989 }
1990 int damage = 0;
1991 int mana = 0;
1992 int heal = 0;
1993 spellElement_t* primaryElement = nullptr;
1994 if ( elementRoot )
1995 {
1996 node_t* primaryNode = elementRoot->elements.first;
1997 mana = elementRoot->mana;
1998 heal = mana;
1999 if ( primaryNode )
2000 {
2001 primaryElement = (spellElement_t*)(primaryNode->element);
2002 if ( primaryElement )
2003 {
2004 damage = primaryElement->damage;
2005 }
2006 }
2007 if ( players[clientnum] )
2008 {
2009 int bonus = 0;
2010 if ( spellbook )
2011 {
2012 bonus = 25 * ((shouldInvertEquipmentBeatitude(stats[clientnum]) ? abs(item->beatitude) : item->beatitude));
2013 if ( stats[clientnum] )
2014 {
2015 if ( players[clientnum] && players[clientnum]->entity )
2016 {
2017 bonus += players[clientnum]->entity->getINT() * 0.5;
2018 }
2019 else
2020 {
2021 bonus += statGetINT(stats[clientnum], nullptr) * 0.5;
2022 }
2023 }
2024 if ( bonus < 0 )
2025 {
2026 bonus = 0;
2027 }
2028 }
2029 damage += (damage * (bonus * 0.01 + getBonusFromCasterOfSpellElement(players[clientnum]->entity, primaryElement)));
2030 heal += (heal * (bonus * 0.01 + getBonusFromCasterOfSpellElement(players[clientnum]->entity, primaryElement)));
2031 }
2032 if ( spell->ID == SPELL_HEALING || spell->ID == SPELL_EXTRAHEALING )
2033 {
2034 damage = heal;
2035 }
2036 }
2037 int spellInfoLines = 1;
2038 char spellType[32] = "";
2039 char spellEffectText[256] = "";
2040 real_t sustainCostPerSecond = 0.f;
2041 getSpellEffectString(spell->ID, spellEffectText, spellType, damage, &spellInfoLines, &sustainCostPerSecond);
2042 char tempstr[64] = "";
2043 char spellNameString[128] = "";
2044 if ( item && item->appearance >= 1000 )
2045 {
2046 // shapeshift spells, append the form name here.
2047 switch ( spell->ID )
2048 {
2049 case SPELL_SPEED:
2050 case SPELL_DETECT_FOOD:
2051 snprintf(spellNameString, 127, "%s (%s)", spell->name, language[3408]);
2052 break;
2053 case SPELL_POISON:
2054 case SPELL_SPRAY_WEB:
2055 snprintf(spellNameString, 127, "%s (%s)", spell->name, language[3409]);
2056 break;
2057 case SPELL_STRIKE:
2058 case SPELL_FEAR:
2059 case SPELL_TROLLS_BLOOD:
2060 snprintf(spellNameString, 127, "%s (%s)", spell->name, language[3410]);
2061 break;
2062 case SPELL_LIGHTNING:
2063 case SPELL_CONFUSE:
2064 case SPELL_AMPLIFY_MAGIC:
2065 snprintf(spellNameString, 127, "%s (%s)", spell->name, language[3411]);
2066 break;
2067 default:
2068 strncpy(spellNameString, spell->name, 127);
2069 break;
2070 }
2071 }
2072 else
2073 {
2074 strncpy(spellNameString, spell->name, 127);
2075 }
2076
2077 if ( spell->ID == SPELL_DOMINATE )
2078 {
2079 snprintf(tempstr, 63, language[2977], getCostOfSpell(spell));
2080 }
2081 else if ( spell->ID == SPELL_DEMON_ILLUSION )
2082 {
2083 snprintf(tempstr, 63, language[3853], getCostOfSpell(spell));
2084 }
2085 else
2086 {
2087 if ( players[clientnum] && players[clientnum]->entity )
2088 {
2089 if ( sustainCostPerSecond > 0.01 )
2090 {
2091 snprintf(tempstr, 31, language[3325],
2092 getCostOfSpell(spell, players[clientnum]->entity), sustainCostPerSecond);
2093 }
2094 else
2095 {
2096 snprintf(tempstr, 31, language[308], getCostOfSpell(spell, players[clientnum]->entity));
2097 }
2098 }
2099 else
2100 {
2101 if ( sustainCostPerSecond > 0.01 )
2102 {
2103 snprintf(tempstr, 31, language[3325],
2104 getCostOfSpell(spell), sustainCostPerSecond);
2105 }
2106 else
2107 {
2108 snprintf(tempstr, 31, language[308], getCostOfSpell(spell));
2109 }
2110 }
2111 }
2112 if ( strcmp(spellEffectText, "") )
2113 {
2114 pos.w = (longestline(spellEffectText) + 1) * TTF12_WIDTH + 8;
2115 }
2116 else
2117 {
2118 pos.w = std::max(longestline(spellNameString), longestline(tempstr)) * TTF12_WIDTH + 8;
2119 }
2120
2121 int furthestX = xres;
2122 if ( proficienciesPage == 0 )
2123 {
2124 if ( pos.y < interfaceSkillsSheet.y + interfaceSkillsSheet.h )
2125 {
2126 furthestX = xres - interfaceSkillsSheet.w;
2127 }
2128 }
2129 else
2130 {
2131 if ( pos.y < interfacePartySheet.y + interfacePartySheet.h )
2132 {
2133 furthestX = xres - interfacePartySheet.w;
2134 }
2135 }
2136
2137 if ( pos.x + pos.w + 16 > furthestX ) // overflow right side of screen
2138 {
2139 pos.x -= (pos.w + 32);
2140 }
2141 pos.h = TTF12_HEIGHT * (2 + spellInfoLines + 1) + 8;
2142 if ( spellInfoLines >= 4 )
2143 {
2144 pos.h += 4;
2145 }
2146 else if ( spellInfoLines == 3 )
2147 {
2148 pos.h += 2;
2149 }
2150 if ( pos.y + pos.h + 16 > yres ) // overflow bottom of screen
2151 {
2152 pos.y -= (pos.y + pos.h + 16 - yres);
2153 }
2154 if ( spellbook )
2155 {
2156 return damage;
2157 }
2158
2159 drawTooltip(&pos);
2160 ttfPrintTextFormatted(ttf12, pos.x + 4, pos.y + 4, "%s\n%s\n%s",
2161 spellNameString, tempstr, spellType);
2162 Uint32 effectColor = uint32ColorLightBlue(*mainsurface);
2163 ttfPrintTextFormattedColor(ttf12, pos.x + 4, pos.y + 4, effectColor,
2164 "\n\n\n%s", spellEffectText);
2165 }
2166 else
2167 {
2168 pos.w = longestline("Error: Spell doesn't exist!") * TTF12_WIDTH + 8;
2169 pos.h = TTF12_HEIGHT + 8;
2170 drawTooltip(&pos);
2171 ttfPrintTextFormatted(ttf12, pos.x + 4, pos.y + 4, "%s", "Error: Spell doesn't exist!");
2172 }
2173 return 0;
2174 }
2175
getSpellEffectString(int spellID,char effectTextBuffer[256],char spellType[32],int value,int * spellInfoLines,real_t * sustainCostPerSecond)2176 void getSpellEffectString(int spellID, char effectTextBuffer[256], char spellType[32], int value, int* spellInfoLines, real_t* sustainCostPerSecond)
2177 {
2178 if ( !spellInfoLines || !sustainCostPerSecond )
2179 {
2180 return;
2181 }
2182 switch ( spellID )
2183 {
2184 case SPELL_FORCEBOLT:
2185 case SPELL_MAGICMISSILE:
2186 case SPELL_LIGHTNING:
2187 snprintf(effectTextBuffer, 255, language[3289], value);
2188 snprintf(spellType, 31, language[3303]);
2189 break;
2190 case SPELL_COLD:
2191 snprintf(effectTextBuffer, 255, language[3290], value, language[3294]);
2192 snprintf(spellType, 31, language[3303]);
2193 *spellInfoLines = 2;
2194 break;
2195 case SPELL_POISON:
2196 snprintf(effectTextBuffer, 255, language[3290], value, language[3300]);
2197 snprintf(spellType, 31, language[3303]);
2198 *spellInfoLines = 2;
2199 break;
2200 case SPELL_FIREBALL:
2201 snprintf(effectTextBuffer, 255, language[3290], value, language[3295]);
2202 snprintf(spellType, 31, language[3303]);
2203 *spellInfoLines = 2;
2204 break;
2205 case SPELL_BLEED:
2206 snprintf(effectTextBuffer, 255, language[3291], value, language[3297], language[3294]);
2207 *spellInfoLines = 2;
2208 snprintf(spellType, 31, language[3303]);
2209 break;
2210 case SPELL_SLOW:
2211 snprintf(effectTextBuffer, 255, language[3292], language[3294]);
2212 snprintf(spellType, 31, language[3303]);
2213 break;
2214 case SPELL_SLEEP:
2215 snprintf(effectTextBuffer, 255, language[3292], language[3298]);
2216 snprintf(spellType, 31, language[3303]);
2217 break;
2218 case SPELL_CONFUSE:
2219 snprintf(effectTextBuffer, 255, language[3292], language[3299]);
2220 snprintf(spellType, 31, language[3303]);
2221 break;
2222 case SPELL_ACID_SPRAY:
2223 snprintf(effectTextBuffer, 255, language[3293], value, language[3300]);
2224 snprintf(spellType, 31, language[3304]);
2225 *spellInfoLines = 2;
2226 break;
2227 case SPELL_HEALING:
2228 case SPELL_EXTRAHEALING:
2229 {
2230 snprintf(spellType, 31, language[3301]);
2231 snprintf(effectTextBuffer, 255, language[3307], value);
2232 *spellInfoLines = 2;
2233 break;
2234 }
2235 case SPELL_REFLECT_MAGIC:
2236 snprintf(spellType, 31, language[3302]);
2237 snprintf(effectTextBuffer, 255, language[3308]);
2238 *spellInfoLines = 2;
2239 *sustainCostPerSecond = 6.f;
2240 break;
2241 case SPELL_LEVITATION:
2242 snprintf(spellType, 31, language[3302]);
2243 snprintf(effectTextBuffer, 255, language[3309]);
2244 *sustainCostPerSecond = 0.6;
2245 break;
2246 case SPELL_INVISIBILITY:
2247 snprintf(spellType, 31, language[3302]);
2248 snprintf(effectTextBuffer, 255, language[3310]);
2249 *sustainCostPerSecond = 1.f;
2250 break;
2251 case SPELL_LIGHT:
2252 snprintf(spellType, 31, language[3302]);
2253 snprintf(effectTextBuffer, 255, language[3311]);
2254 *sustainCostPerSecond = 15.f;
2255 break;
2256 case SPELL_REMOVECURSE:
2257 snprintf(spellType, 31, language[3305]);
2258 snprintf(effectTextBuffer, 255, language[3312]);
2259 break;
2260 case SPELL_IDENTIFY:
2261 snprintf(spellType, 31, language[3305]);
2262 snprintf(effectTextBuffer, 255, language[3313]);
2263 break;
2264 case SPELL_MAGICMAPPING:
2265 snprintf(spellType, 31, language[3305]);
2266 snprintf(effectTextBuffer, 255, language[3314]);
2267 *spellInfoLines = 2;
2268 break;
2269 case SPELL_TELEPORTATION:
2270 snprintf(spellType, 31, language[3305]);
2271 snprintf(effectTextBuffer, 255, language[3315]);
2272 break;
2273 case SPELL_OPENING:
2274 snprintf(spellType, 31, language[3303]);
2275 snprintf(effectTextBuffer, 255, language[3316]);
2276 break;
2277 case SPELL_LOCKING:
2278 snprintf(spellType, 31, language[3303]);
2279 snprintf(effectTextBuffer, 255, language[3317]);
2280 break;
2281 case SPELL_CUREAILMENT:
2282 snprintf(spellType, 31, language[3301]);
2283 snprintf(effectTextBuffer, 255, language[3318]);
2284 *spellInfoLines = 2;
2285 break;
2286 case SPELL_DIG:
2287 snprintf(spellType, 31, language[3303]);
2288 snprintf(effectTextBuffer, 255, language[3319]);
2289 break;
2290 case SPELL_SUMMON:
2291 snprintf(spellType, 31, language[3306]);
2292 snprintf(effectTextBuffer, 255, language[3320]);
2293 *spellInfoLines = 3;
2294 break;
2295 case SPELL_STONEBLOOD:
2296 snprintf(spellType, 31, language[3304]);
2297 snprintf(effectTextBuffer, 255, language[3292], language[3296]);
2298 break;
2299 case SPELL_DOMINATE:
2300 snprintf(spellType, 31, language[3303]);
2301 snprintf(effectTextBuffer, 255, language[3321]);
2302 *spellInfoLines = 4;
2303 break;
2304 case SPELL_STEAL_WEAPON:
2305 snprintf(spellType, 31, language[3303]);
2306 snprintf(effectTextBuffer, 255, language[3322]);
2307 *spellInfoLines = 2;
2308 break;
2309 case SPELL_DRAIN_SOUL:
2310 snprintf(spellType, 31, language[3303]);
2311 snprintf(effectTextBuffer, 255, language[3323], value);
2312 *spellInfoLines = 2;
2313 break;
2314 case SPELL_VAMPIRIC_AURA:
2315 snprintf(spellType, 31, language[3302]);
2316 snprintf(effectTextBuffer, 255, language[3324]);
2317 *spellInfoLines = 4;
2318 *sustainCostPerSecond = 0.33;
2319 break;
2320 case SPELL_CHARM_MONSTER:
2321 snprintf(spellType, 31, language[3303]);
2322 snprintf(effectTextBuffer, 255, language[3326]);
2323 *spellInfoLines = 3;
2324 break;
2325 case SPELL_REVERT_FORM:
2326 snprintf(spellType, 31, language[3305]);
2327 snprintf(effectTextBuffer, 255, language[3851]);
2328 *spellInfoLines = 1;
2329 break;
2330 case SPELL_RAT_FORM:
2331 snprintf(spellType, 31, language[3305]);
2332 snprintf(effectTextBuffer, 255, language[3847]);
2333 *spellInfoLines = 2;
2334 break;
2335 case SPELL_SPIDER_FORM:
2336 snprintf(spellType, 31, language[3305]);
2337 snprintf(effectTextBuffer, 255, language[3848]);
2338 *spellInfoLines = 2;
2339 break;
2340 case SPELL_TROLL_FORM:
2341 snprintf(spellType, 31, language[3305]);
2342 snprintf(effectTextBuffer, 255, language[3849]);
2343 *spellInfoLines = 2;
2344 break;
2345 case SPELL_IMP_FORM:
2346 snprintf(spellType, 31, language[3305]);
2347 snprintf(effectTextBuffer, 255, language[3850]);
2348 *spellInfoLines = 2;
2349 break;
2350 case SPELL_SPRAY_WEB:
2351 snprintf(spellType, 31, language[3304]);
2352 snprintf(effectTextBuffer, 255, language[3834]);
2353 *spellInfoLines = 4;
2354 break;
2355 case SPELL_SPEED:
2356 snprintf(spellType, 31, language[3301]);
2357 snprintf(effectTextBuffer, 255, language[3835]);
2358 *spellInfoLines = 2;
2359 break;
2360 case SPELL_FEAR:
2361 snprintf(spellType, 31, language[3301]);
2362 snprintf(effectTextBuffer, 255, language[3836]);
2363 *spellInfoLines = 3;
2364 break;
2365 case SPELL_STRIKE:
2366 snprintf(spellType, 31, language[3305]);
2367 snprintf(effectTextBuffer, 255, language[3838]);
2368 *spellInfoLines = 4;
2369 break;
2370 case SPELL_DETECT_FOOD:
2371 snprintf(spellType, 31, language[3305]);
2372 snprintf(effectTextBuffer, 255, language[3839]);
2373 *spellInfoLines = 1;
2374 break;
2375 case SPELL_WEAKNESS:
2376 snprintf(spellType, 31, language[3303]);
2377 snprintf(effectTextBuffer, 255, language[3837]);
2378 *spellInfoLines = 2;
2379 break;
2380 case SPELL_AMPLIFY_MAGIC:
2381 snprintf(spellType, 31, language[3302]);
2382 snprintf(effectTextBuffer, 255, language[3852]);
2383 *spellInfoLines = 2;
2384 *sustainCostPerSecond = 0.25;
2385 break;
2386 case SPELL_SHADOW_TAG:
2387 snprintf(spellType, 31, language[3303]);
2388 snprintf(effectTextBuffer, 255, language[3843]);
2389 *spellInfoLines = 3;
2390 break;
2391 case SPELL_TELEPULL:
2392 snprintf(spellType, 31, language[3303]);
2393 snprintf(effectTextBuffer, 255, language[3844]);
2394 *spellInfoLines = 3;
2395 break;
2396 case SPELL_DEMON_ILLUSION:
2397 snprintf(spellType, 31, language[3303]);
2398 snprintf(effectTextBuffer, 255, language[3845]);
2399 *spellInfoLines = 3;
2400 break;
2401 case SPELL_TROLLS_BLOOD:
2402 snprintf(spellType, 31, language[3305]);
2403 snprintf(effectTextBuffer, 255, language[3840]);
2404 *spellInfoLines = 2;
2405 break;
2406 case SPELL_SALVAGE:
2407 snprintf(spellType, 31, language[3301]);
2408 snprintf(effectTextBuffer, 255, language[3846]);
2409 *spellInfoLines = 2;
2410 break;
2411 case SPELL_FLUTTER:
2412 snprintf(spellType, 31, language[3305]);
2413 snprintf(effectTextBuffer, 255, language[3841]);
2414 *spellInfoLines = 1;
2415 break;
2416 case SPELL_DASH:
2417 snprintf(spellType, 31, language[3305]);
2418 snprintf(effectTextBuffer, 255, language[3842]);
2419 *spellInfoLines = 3;
2420 break;
2421 case SPELL_SELF_POLYMORPH:
2422 snprintf(spellType, 31, language[3305]);
2423 snprintf(effectTextBuffer, 255, language[3886]);
2424 *spellInfoLines = 2;
2425 break;
2426 case SPELL_9:
2427 case SPELL_10:
2428 default:
2429 break;
2430 }
2431 }