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 }