1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "headers.h"
21
22 #include "audio/audio.h"
23 #include "graphics/font.h"
24 #include "graphics/graphics.h"
25 #include "graphics/texture_cache.h"
26 #include "hud.h"
27 #include "inventory.h"
28 #include "medal.h"
29 #include "system/error.h"
30
31 extern Game game;
32 extern Entity player, *self, playerWeapon;
33
34 static Hud hud;
35 static Message messageHead;
36
37 static void addMessageToQueue(char *, int, int, int, int);
38 static void getNextMessageFromQueue(void);
39
initHud()40 void initHud()
41 {
42 SDL_Surface *heart;
43
44 hud.itemBox = loadImage("gfx/hud/item_box.png");
45
46 heart = loadImageAsSurface("gfx/hud/heart.png");
47
48 hud.heart = convertSurfaceToTexture(heart, FALSE);
49
50 hud.whiteHeart = convertImageToWhite(heart, TRUE);
51
52 hud.emptyHeart = loadImage("gfx/hud/heart_empty.png");
53
54 hud.spotlight = loadImage("gfx/hud/spotlight.png");
55
56 hud.medalSurface[0] = loadImage("gfx/hud/bronze_medal.png");
57
58 hud.medalSurface[1] = loadImage("gfx/hud/silver_medal.png");
59
60 hud.medalSurface[2] = loadImage("gfx/hud/gold_medal.png");
61
62 hud.medalSurface[3] = loadImage("gfx/hud/ruby_medal.png");
63
64 hud.disabledMedalSurface = loadImage("gfx/hud/disabled_medal.png");
65
66 messageHead.next = NULL;
67
68 hud.bossHealth = NULL;
69
70 hud.medalTextSurface = NULL;
71
72 hud.slimeTimerSurface = NULL;
73 }
74
doHud()75 void doHud()
76 {
77 hud.thinkTime--;
78
79 if (hud.thinkTime <= 0)
80 {
81 hud.thinkTime = 60;
82 }
83
84 hud.medalThinkTime--;
85
86 if (hud.medalThinkTime <= 0)
87 {
88 if (hud.medalTextSurface != NULL)
89 {
90 destroyTexture(hud.medalTextSurface);
91
92 hud.medalTextSurface = NULL;
93
94 medalProcessingFinished();
95 }
96
97 hud.medalThinkTime = 0;
98 }
99
100 hud.infoMessage.thinkTime--;
101
102 if (hud.infoMessage.thinkTime <= 0)
103 {
104 if (hud.infoMessage.surface != NULL)
105 {
106 hud.infoMessage.surface = NULL;
107
108 hud.infoMessage.text[0] = '\0';
109 }
110
111 getNextMessageFromQueue();
112 }
113
114 if (hud.bossHealth != NULL)
115 {
116 if (hud.bossHealthIndex < *hud.bossHealth)
117 {
118 hud.bossHealthIndex += (hud.bossMaxHealth / 100);
119
120 if (hud.bossHealthIndex > *hud.bossHealth)
121 {
122 hud.bossHealthIndex = *hud.bossHealth;
123 }
124 }
125
126 else if (*hud.bossHealth < hud.bossHealthIndex)
127 {
128 hud.bossHealthIndex -= 3;
129
130 if (hud.bossHealthIndex < *hud.bossHealth)
131 {
132 hud.bossHealthIndex = *hud.bossHealth;
133 }
134 }
135 }
136 }
137
drawHud()138 void drawHud()
139 {
140 char quantity[4];
141 char cacheName[10];
142 int i, x, y, h, w, itemBoxMid, quant;
143 float percentage, clipWidth;
144 Entity *e;
145 SDL_Surface *quantitySurface;
146
147 if (game.showHUD == TRUE)
148 {
149 itemBoxMid = (SCREEN_WIDTH - hud.itemBox->w) / 2;
150
151 if (game.status == IN_INVENTORY)
152 {
153 drawBox(itemBoxMid, 15, hud.itemBox->w, hud.itemBox->h, 0, 0, 0, 255);
154 }
155
156 drawSelectedInventoryItem(itemBoxMid, 15, hud.itemBox->w, hud.itemBox->h);
157
158 drawImage(hud.itemBox, itemBoxMid, 15, FALSE, 255);
159
160 x = FALSE;
161
162 if (playerWeapon.inUse == TRUE)
163 {
164 if (strcmpignorecase(playerWeapon.name, "weapon/bow") == 0)
165 {
166 e = getInventoryItemByObjectiveName(playerWeapon.requires);
167
168 if (e != NULL)
169 {
170 x = TRUE;
171
172 quant = e->health;
173 }
174 }
175
176 else if (strcmpignorecase(playerWeapon.name, "weapon/lightning_sword") == 0)
177 {
178 x = TRUE;
179
180 quant = playerWeapon.mental;
181 }
182
183 if (x == TRUE)
184 {
185 if (quant < 0)
186 {
187 quant = 0;
188 }
189
190 if (hud.quantity != quant || (game.frames % (TEXTURE_CACHE_TIME / 2)) == 0)
191 {
192 SNPRINTF(quantity, 4, "%d", quant);
193
194 SNPRINTF(cacheName, 10, "hud_%d", quant);
195
196 hud.quantitySurface = getTextureFromCache(cacheName);
197
198 if (hud.quantitySurface == NULL)
199 {
200 quantitySurface = generateTransparentTextSurface(quantity, game.font, 255, 255, 255, FALSE);
201
202 hud.quantitySurface = convertSurfaceToTexture(quantitySurface, TRUE);
203
204 addTextureToCache(cacheName, hud.quantitySurface, FALSE);
205 }
206
207 hud.quantity = quant;
208 }
209
210 drawImage(hud.quantitySurface, (SCREEN_WIDTH - hud.quantitySurface->w) / 2, 15 + hud.itemBox->h + 5, FALSE, 255);
211 }
212 }
213
214 if (x == FALSE)
215 {
216 hud.quantity = -1;
217 }
218
219 percentage = 0;
220
221 if (hud.bossHealth != NULL)
222 {
223 x = SCREEN_WIDTH - 6;
224 y = 5;
225
226 x -= (hud.heart->w + 5) * 10;
227
228 percentage = hud.bossHealthIndex * 100;
229
230 percentage /= hud.bossMaxHealth;
231
232 for (i=10;i<=100;i+=10)
233 {
234 if (i <= percentage)
235 {
236 drawImage(hud.heart, x, y, FALSE, 255);
237 }
238
239 else if (i - 10 < percentage)
240 {
241 clipWidth = (percentage - (i - 10)) / 10;
242
243 w = hud.heart->w * clipWidth;
244
245 drawClippedImage(hud.heart, 0, 0, x, y, w, hud.heart->h);
246 }
247
248 drawImage(hud.emptyHeart, x, y, FALSE, 255);
249
250 x += hud.heart->w + 5;
251 }
252 }
253
254 if (hud.infoMessage.surface != NULL)
255 {
256 drawImage(hud.infoMessage.surface, (SCREEN_WIDTH - hud.infoMessage.surface->w) / 2, SCREEN_HEIGHT - TILE_SIZE - 1, FALSE, 255);
257 }
258
259 w = h = 5;
260
261 for (i=0;i<player.maxHealth;i++)
262 {
263 if (i != 0 && (i % 10) == 0)
264 {
265 h += hud.heart->h;
266
267 w = 5;
268 }
269
270 if (i < player.health)
271 {
272 if (player.health <= 3 && hud.thinkTime <= 30)
273 {
274 drawImage(hud.whiteHeart, w, h, FALSE, 255);
275 }
276
277 else
278 {
279 drawImage(hud.heart, w, h, FALSE, 255);
280 }
281 }
282
283 drawImage(hud.emptyHeart, w, h, FALSE, 255);
284
285 w += hud.heart->w + 5;
286 }
287 }
288
289 if (hud.medalTextSurface != NULL)
290 {
291 x = SCREEN_WIDTH - hud.medalTextSurface->w - 5;
292
293 drawImage(hud.medalTextSurface, x, 5, FALSE, 255);
294 }
295
296 if (hud.slimeTimerSurface != NULL)
297 {
298 x = player.x + player.w / 2 - hud.slimeTimerSurface->w / 2;
299 y = player.y - hud.slimeTimerSurface->h - 5;
300
301 drawImageToMap(hud.slimeTimerSurface, x, y, FALSE, 255);
302 }
303 }
304
freeHud()305 void freeHud()
306 {
307 int i;
308
309 if (hud.itemBox != NULL)
310 {
311 destroyTexture(hud.itemBox);
312
313 hud.itemBox = NULL;
314 }
315
316 if (hud.heart != NULL)
317 {
318 destroyTexture(hud.heart);
319
320 hud.heart = NULL;
321 }
322
323 if (hud.emptyHeart != NULL)
324 {
325 destroyTexture(hud.emptyHeart);
326
327 hud.emptyHeart = NULL;
328 }
329
330 if (hud.spotlight != NULL)
331 {
332 destroyTexture(hud.spotlight);
333
334 hud.spotlight = NULL;
335 }
336
337 if (hud.infoMessage.surface != NULL)
338 {
339 hud.infoMessage.surface = NULL;
340 }
341
342 if (hud.medalTextSurface != NULL)
343 {
344 destroyTexture(hud.medalTextSurface);
345
346 hud.medalTextSurface = NULL;
347 }
348
349 hud.quantitySurface = NULL;
350
351 for (i=0;i<4;i++)
352 {
353 if (hud.medalSurface[i] != NULL)
354 {
355 destroyTexture(hud.medalSurface[i]);
356
357 hud.medalSurface[i] = NULL;
358 }
359 }
360
361 if (hud.disabledMedalSurface != NULL)
362 {
363 destroyTexture(hud.disabledMedalSurface);
364
365 hud.disabledMedalSurface = NULL;
366 }
367
368 hud.slimeTimerSurface = NULL;
369
370 freeMessageQueue();
371 }
372
setSlimeTimerValue(int value)373 void setSlimeTimerValue(int value)
374 {
375 SDL_Surface *slimeTimerSurface;
376
377 char timeValue[5];
378
379 if (hud.slimeTimerSurface != NULL || value < 0)
380 {
381 hud.slimeTimerSurface = NULL;
382
383 if (value < 0)
384 {
385 return;
386 }
387 }
388
389 SNPRINTF(timeValue, 5, "%d", value);
390
391 hud.slimeTimerSurface = getTextureFromCache(timeValue);
392
393 if (hud.slimeTimerSurface == NULL)
394 {
395 slimeTimerSurface = generateTextSurface(timeValue, game.font, 220, 220, 220, 0, 0, 0);
396
397 hud.slimeTimerSurface = addBorder(slimeTimerSurface, 255, 255, 255, 0, 0, 0);
398
399 addTextureToCache(timeValue, hud.slimeTimerSurface, FALSE);
400 }
401 }
402
setInfoBoxMessage(int thinkTime,int r,int g,int b,char * fmt,...)403 void setInfoBoxMessage(int thinkTime, int r, int g, int b, char *fmt, ...)
404 {
405 char text[MAX_MESSAGE_LENGTH];
406 va_list ap;
407
408 va_start(ap, fmt);
409 vsnprintf(text, sizeof(text), fmt, ap);
410 va_end(ap);
411
412 addMessageToQueue(text, thinkTime, r, g, b);
413 }
414
addMessageToQueue(char * text,int thinkTime,int r,int g,int b)415 static void addMessageToQueue(char *text, int thinkTime, int r, int g, int b)
416 {
417 Message *head, *msg;
418
419 head = &messageHead;
420
421 while (head->next != NULL)
422 {
423 if (strcmpignorecase(text, head->text) == 0)
424 {
425 return;
426 }
427
428 head = head->next;
429 }
430
431 msg = malloc(sizeof(Message));
432
433 if (msg == NULL)
434 {
435 showErrorAndExit("Failed to allocate %d bytes for message queue", (int)sizeof(Message));
436 }
437
438 STRNCPY(msg->text, text, sizeof(messageHead.text));
439
440 msg->thinkTime = thinkTime;
441
442 msg->r = r;
443 msg->g = g;
444 msg->b = b;
445
446 msg->next = NULL;
447
448 head->next = msg;
449 }
450
getNextMessageFromQueue()451 static void getNextMessageFromQueue()
452 {
453 Message *head = messageHead.next;
454 SDL_Surface *infoSurface;
455
456 if (head != NULL)
457 {
458 STRNCPY(hud.infoMessage.text, head->text, sizeof(hud.infoMessage.text));
459
460 hud.infoMessage.r = head->r;
461 hud.infoMessage.g = head->g;
462 hud.infoMessage.b = head->b;
463
464 hud.infoMessage.surface = getTextureFromCache(hud.infoMessage.text);
465
466 if (hud.infoMessage.surface == NULL)
467 {
468 infoSurface = generateTextSurface(hud.infoMessage.text, game.font, hud.infoMessage.r, hud.infoMessage.g, hud.infoMessage.b, 0, 0, 0);
469
470 hud.infoMessage.surface = addBorder(infoSurface, 255, 255, 255, 0, 0, 0);
471
472 addTextureToCache(hud.infoMessage.text, hud.infoMessage.surface, FALSE);
473 }
474
475 hud.infoMessage.thinkTime = (head->thinkTime <= 0 ? 5 : head->thinkTime);
476
477 messageHead.next = head->next;
478
479 free(head);
480 }
481 }
482
freeMessageQueue()483 void freeMessageQueue()
484 {
485 Message *p, *q;
486
487 for (p=messageHead.next;p!=NULL;p=q)
488 {
489 q = p->next;
490
491 free(p);
492 }
493
494 messageHead.next = NULL;
495
496 if (hud.infoMessage.surface != NULL)
497 {
498 hud.infoMessage.surface = NULL;
499
500 hud.infoMessage.text[0] = '\0';
501
502 hud.infoMessage.thinkTime = 0;
503 }
504 }
505
initBossHealthBar()506 void initBossHealthBar()
507 {
508 hud.bossHealth = &self->health;
509
510 hud.bossMaxHealth = self->health;
511
512 hud.bossHealthIndex = 0;
513 }
514
freeBossHealthBar()515 void freeBossHealthBar()
516 {
517 hud.bossHealth = NULL;
518 }
519
drawSpotlight(int x,int y)520 void drawSpotlight(int x, int y)
521 {
522 drawImage(hud.spotlight, x - game.offsetX, y - game.offsetY, FALSE, 255);
523 }
524
showMedal(int medalType,char * message)525 void showMedal(int medalType, char *message)
526 {
527 SDL_Surface *textSurface, *medalSurface;
528 SDL_Rect dest;
529 Texture *medalTexture, *textTexture, *targetTexture;
530
531 if (hud.medalTextSurface != NULL)
532 {
533 return;
534 }
535
536 textSurface = generateTextSurface(_(message), game.font, 0, 220, 0, 0, 0, 0);
537
538 textTexture = convertSurfaceToTexture(textSurface, TRUE);
539
540 medalSurface = createSurface(textSurface->w + hud.medalSurface[medalType]->w + 18, MAX(textTexture->h, hud.medalSurface[medalType]->h), FALSE);
541
542 medalTexture = addBorder(medalSurface, 255, 255, 255, 0, 0, 0);
543
544 targetTexture = createWritableTexture(medalTexture->w, medalTexture->h);
545
546 SDL_SetRenderTarget(game.renderer, targetTexture->texture);
547
548 SDL_RenderCopy(game.renderer, medalTexture->texture, NULL, NULL);
549
550 dest.x = 5 + BORDER_PADDING;
551 dest.y = medalTexture->h / 2 - hud.medalSurface[medalType]->h / 2;
552 dest.w = hud.medalSurface[medalType]->w;
553 dest.h = hud.medalSurface[medalType]->h;
554
555 SDL_RenderCopy(game.renderer, hud.medalSurface[medalType]->texture, NULL, &dest);
556
557 dest.x = hud.medalSurface[medalType]->w + 13 + BORDER_PADDING;
558 dest.y = medalTexture->h / 2 - textTexture->h / 2;
559 dest.w = textTexture->w;
560 dest.h = textTexture->h;
561
562 SDL_RenderCopy(game.renderer, textTexture->texture, NULL, &dest);
563
564 hud.medalTextSurface = targetTexture;
565
566 hud.medalThinkTime = 180;
567
568 playSound("sound/common/trophy");
569
570 SDL_SetRenderTarget(game.renderer, NULL);
571 }
572
spotlightSize()573 int spotlightSize()
574 {
575 return hud.spotlight->w;
576 }
577
getMedalImage(int medalType,int obtained)578 Texture *getMedalImage(int medalType, int obtained)
579 {
580 return obtained == TRUE ? hud.medalSurface[medalType] : hud.disabledMedalSurface;
581 }
582