NXEngine v1.0.0.6
[NXEngine.git] / statusbar.cpp
blob4fc7254f81e127d1c01ace53e3e28f4cf129dd50
2 #include "nx.h"
3 #include "statusbar.h"
4 #include "statusbar.fdh"
6 #ifdef WIDESCREEN
7 #define STATUS_X 10
8 #define STATUS_Y 10
9 #else
10 #define STATUS_X 16
11 #define STATUS_Y 16
12 #endif
14 #define HEALTH_X (STATUS_X+0)
15 #define HEALTH_Y (STATUS_Y+24)
16 #define HEALTHFILL_X (HEALTH_X+24)
17 #define HEALTHFILL_Y (HEALTH_Y+1)
18 #define HEALTHFILL_MAXLEN 39
20 #define WEAPONBAR_Y (STATUS_Y+1)
21 #define CURWEAPON_X (STATUS_X+1)
23 #define XPBAR_Y (STATUS_Y+16) // Y coordinate of "XP" area
24 #define XPBAR_X (STATUS_X+24) // X of yellow XP bar
26 #define AMMO_X (STATUS_X+32)
27 #define AMMO_Y (STATUS_Y+0)
29 #define NIKU_X STATUS_X
30 #define NIKU_Y 8
32 #define FRAME_XP_BAR 0 // empty bar frame
33 #define FRAME_XP_FILL 1 // sprite to fill bar with
34 #define FRAME_XP_FLASH 2 // white flashing when XP is gained
35 #define FRAME_XP_MAX 3 // "MAX" when XP is at max on L3
37 //int displayed_health = 0;
38 //int healthdectimer = 0;
39 StatusBar statusbar;
40 static PercentBar PHealthBar;
42 // the "slide" effect when changing weapons
43 struct stWeaponSlide
45 int lv_offset; // offset of XP bar
46 int wpn_offset; // offset of weapon bar
47 int ammo_offset; // offset of ammo, in addition to wpn_offset
48 char timer;
49 int move_dir;
50 int firstWeapon; // weapon to show as current weapon
51 } slide;
52 #define SLIDE_LV_OFFSET 16
53 #define SLIDE_TIMER_START 5
56 bool statusbar_init(void)
58 InitPercentBar(&PHealthBar, player->hp);
60 memset(&slide, 0, sizeof(slide));
61 slide.firstWeapon = player->curWeapon;
62 return 0;
66 void DrawStatusBar(void)
68 int level, curxp, maxxp;
69 int w, x;
70 bool maxed_out;
72 //debug("%08x", game.bossbar.object);
73 //debug("%s", game.bossbar.defeated ? "true" : "false");
75 // handle animations etc
76 RunStatusBar();
78 // draw boss bar
79 if (game.bossbar.object && !game.bossbar.defeated)
81 #define BOSSBAR_W 198
82 // BOSS_X = 32 at normal resolution
83 #define BOSS_X ((SCREEN_WIDTH / 2) - (BOSSBAR_W / 2) - 29)
84 #define BOSS_Y (SCREEN_HEIGHT-20)
85 draw_sprite(BOSS_X, BOSS_Y, SPR_TEXTBOX, 0, 0);
86 draw_sprite(BOSS_X, BOSS_Y+8, SPR_TEXTBOX, 2, 0);
87 draw_sprite(BOSS_X+8, BOSS_Y+4, SPR_BOSSHPICON, 0, 0);
89 // e.g. bosses w/ multiple forms (Ballos)
90 if (game.bossbar.object->hp > game.bossbar.starting_hp)
91 game.bossbar.starting_hp = game.bossbar.object->hp;
93 RunPercentBar(&game.bossbar.bar, game.bossbar.object->hp);
94 DrawPercentBar(&game.bossbar.bar, BOSS_X+40, BOSS_Y+5, game.bossbar.object->hp, game.bossbar.starting_hp, BOSSBAR_W);
97 if (game.frozen || player->inputs_locked) return;
98 if (fade.getstate() != FS_NO_FADE) return;
100 if (player->hp)
102 if (!player->hurt_flash_state)
104 if (!game.debug.god)
106 // -- draw the health bar -----------------------------
107 draw_sprite(HEALTH_X, HEALTH_Y, SPR_HEALTHBAR, 0, 0);
109 DrawPercentBar(&PHealthBar, HEALTHFILL_X, HEALTHFILL_Y, player->hp, player->maxHealth, HEALTHFILL_MAXLEN);
111 // draw the health in numbers
112 DrawNumberRAlign(HEALTH_X+24, HEALTH_Y, SPR_WHITENUMBERS, PHealthBar.displayed_value);
115 // -- draw the XP bar ---------------------------------
116 level = player->weapons[player->curWeapon].level;
117 curxp = player->weapons[player->curWeapon].xp;
118 maxxp = player->weapons[player->curWeapon].max_xp[level];
120 if (player->curWeapon == WPN_NONE)
122 curxp = 0;
123 maxxp = 1;
126 // draw XP bar and fill it
127 draw_sprite(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_BAR, 0);
129 maxed_out = ((curxp == maxxp) && level == 2);
130 if (!maxed_out)
131 DrawPercentage(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_FILL, curxp, maxxp, sprites[SPR_XPBAR].w);
133 // draw the white flashing if we just got more XP
134 // the time-left and flash-state are in separate variables--
135 // otherwise the Spur will not flash XP bar
136 if (statusbar.xpflashcount)
138 if (++statusbar.xpflashstate & 2)
140 draw_sprite(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_FLASH, 0);
143 statusbar.xpflashcount--;
145 else statusbar.xpflashstate = 0;
147 // draw "MAX"
148 if (maxed_out)
149 draw_sprite(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_MAX, 0);
151 // Level Number
152 DrawWeaponLevel(HEALTH_X + slide.lv_offset, XPBAR_Y, player->curWeapon);
155 // -- draw the weapon bar -----------------------------
156 // draw current weapon
157 if (player->curWeapon != WPN_NONE)
158 draw_sprite(CURWEAPON_X + slide.wpn_offset, WEAPONBAR_Y, SPR_ARMSICONS, slide.firstWeapon, 0);
160 // draw ammo, note we draw ammo of firstweapon NOT current weapon, for slide effect
161 DrawWeaponAmmo((AMMO_X + slide.wpn_offset + slide.ammo_offset), AMMO_Y, slide.firstWeapon);
163 // draw other weapons
164 w = slide.firstWeapon;
165 x = STATUS_X + 64 + slide.wpn_offset + 1;
166 for(;;)
168 if (++w >= WPN_COUNT) w = 0;
169 if (w==slide.firstWeapon) break;
171 if (player->weapons[w].hasWeapon)
173 draw_sprite(x, WEAPONBAR_Y, SPR_ARMSICONS, w, RIGHT);
174 x += 16;
178 DrawAirLeft((SCREEN_WIDTH/2) - (5*8), ((SCREEN_HEIGHT)/2)-16);
182 void DrawAirLeft(int x, int y)
184 if (player->airshowtimer)
186 draw_sprite(x, y, SPR_AIR, (player->airleft%30 > 10) ? 0:1, RIGHT);
188 if (player->airshowtimer%6 < 4)
189 DrawNumber(x+32, y, player->airleft/10);
193 void DrawWeaponAmmo(int x, int y, int wpn)
195 // draw slash
196 if (!player->hurt_flash_state || game.mode != GM_NORMAL)
198 draw_sprite(x, y+8, SPR_WHITENUMBERS, 11, 0);
201 if (!player->weapons[wpn].maxammo)
202 { // ammo is "not applicable"
203 x += 16;
204 draw_sprite(x, y, SPR_NAAMMO, 0, 0);
205 draw_sprite(x, y+8, SPR_NAAMMO, 0, 0);
207 else
209 DrawNumber(x, y, player->weapons[wpn].ammo);
210 DrawNumber(x, y+8, player->weapons[wpn].maxammo);
214 void DrawWeaponLevel(int x, int y, int wpn)
216 int level = (player->weapons[wpn].level + 1);
217 if (wpn == WPN_NONE) level = 0;
219 draw_sprite(x, y, SPR_XPLEVELICON, 0, 0);
220 draw_sprite(x+16, y, SPR_WHITENUMBERS, level, 0);
224 static void RunStatusBar(void)
226 // handle slowly decreasing the health when player is hurt
227 // note how it only decrements while it's actually visible--i thought that was a nice touch
228 if (!player->hurt_flash_state)
230 RunPercentBar(&PHealthBar, player->hp);
233 if (game.frozen || player->inputs_locked) return;
234 if (fade.getstate() != FS_NO_FADE) return;
236 // sliding effect when changing weapons
237 if (slide.lv_offset)
238 { // next weapon
239 slide.lv_offset += slide.move_dir;
240 if (--slide.timer > 0)
242 slide.wpn_offset += slide.move_dir;
244 else
246 if (!slide.timer) slide.firstWeapon = player->curWeapon;
247 slide.wpn_offset = slide.lv_offset;
248 slide.ammo_offset = 0;
251 else
253 slide.firstWeapon = player->curWeapon;
258 // start the slide effect. if dir = LEFT, slides left (next weapon), if RIGHT does "prev weapon"
259 // newwpn = the weapon to change to
260 void weapon_slide(int dir, int newwpn)
262 int sign;
263 if (slide.lv_offset) slide.firstWeapon = player->curWeapon; // if already sliding change immediately
264 if (dir==RIGHT) sign = -1; else sign = 1;
266 slide.lv_offset = SLIDE_LV_OFFSET * sign;
267 slide.timer = SLIDE_TIMER_START;
268 slide.ammo_offset = 16 * sign;
269 slide.move_dir = -2 * sign;
270 player->curWeapon = newwpn;
273 // the opening slide effect on load/new game
274 void weapon_introslide()
276 if (player->curWeapon == WPN_NONE)
278 weapon_slide(LEFT, player->curWeapon);
279 return;
282 stat_PrevWeapon(true);
283 slide.lv_offset = 0;
284 stat_NextWeapon(true);
287 // switches to the next weapon in inventory
288 void stat_NextWeapon(bool quiet)
290 int w;
292 w = player->curWeapon;
293 if (w == WPN_NONE) return;
295 for(;;)
297 if (++w >= WPN_COUNT) w = 0;
299 if (player->weapons[w].hasWeapon || w == player->curWeapon)
301 if (!quiet) sound(SND_SWITCH_WEAPON);
302 weapon_slide(LEFT, w);
303 return;
305 } while(w != player->curWeapon);
308 // switches to the previous weapon in inventory
309 void stat_PrevWeapon(bool quiet)
311 int w;
313 w = player->curWeapon;
314 if (w == WPN_NONE) return;
316 for(;;)
318 if (--w < 0) w = WPN_COUNT-1;
320 if (player->weapons[w].hasWeapon || w == player->curWeapon)
322 if (!quiet) sound(SND_SWITCH_WEAPON);
323 weapon_slide(RIGHT, w);
324 return;
330 void InitPercentBar(PercentBar *bar, int starting_value)
332 PHealthBar.displayed_value = starting_value;
333 PHealthBar.dectimer = 0;
336 void RunPercentBar(PercentBar *bar, int current_value)
338 if (current_value != bar->displayed_value)
340 if (current_value > bar->displayed_value)
342 bar->displayed_value = current_value;
344 else
346 if (++bar->dectimer > 0x1e)
348 bar->displayed_value--;
352 else bar->dectimer = 0;
355 void DrawPercentBar(PercentBar *bar, int x, int y, int curvalue, int maxvalue, int width)
357 if (bar->displayed_value != curvalue)
358 DrawPercentage(x, y, SPR_HEALTHFILL, 1, bar->displayed_value, maxvalue, width);
360 DrawPercentage(x, y, SPR_HEALTHFILL, 0, curvalue, maxvalue, width);
364 void c------------------------------() {}
367 // draws number "num" at x,y.
368 // leading zeroes are omitted, however, the space for them is still
369 // skipped over (left blank). thus it always leaves space for 3 digits.
370 void DrawNumber(int x, int y, int num)
372 static const int numtable[3] = { 1000, 100, 10 };
373 int place, digit, total;
375 if (num > 9999) num = 9999;
377 place = total = 0;
378 while(place < 3)
380 digit = 0;
381 while(num >= numtable[place])
383 num -= numtable[place];
384 digit++;
387 total += digit;
389 if (total)
390 draw_sprite(x+(place*8), y, SPR_WHITENUMBERS, digit);
392 place++;
395 draw_sprite(x+(3*8), y, SPR_WHITENUMBERS, num);
399 void DrawPercentage(int x, int y, int fill_sprite, int fsframe, int curvalue, int maxvalue, int width_at_max)
401 if (curvalue < 0) curvalue = 0;
402 if (curvalue > 0 || maxvalue==0)
404 int fillwidth;
406 if (curvalue >= maxvalue)
408 fillwidth = width_at_max;
410 else
412 fillwidth = (int)(((float)width_at_max / (float)maxvalue) * (float)curvalue);
413 if (!fillwidth) return;
416 draw_sprite_clip_width(x, y, fill_sprite, fsframe, fillwidth);
420 // draws a given number using sprite 's' as the font
421 // the numbers are drawn right-aligned to "x".
422 void DrawNumberRAlign(int x, int y, int s, int num)
424 char str[50];
425 int i, len;
426 int fontwidth = sprites[s].w;
428 sprintf(str, "%d", num);
429 x -= strlen(str) * fontwidth;
431 len = strlen(str);
432 for(i=0;i<len;i++)
434 draw_sprite(x, y, s, str[i] - '0');
435 x += fontwidth;
439 void DrawTwoDigitNumber(int x, int y, int num)
441 DrawDigit(x+0, y, num/10);
442 DrawDigit(x+8, y, num%10);
445 void DrawDigit(int x, int y, int digit)
447 draw_sprite(x, y, SPR_WHITENUMBERS, digit);
451 void c------------------------------() {}
454 // Nikumaru Counter
455 void niku_run()
457 if (player->equipmask & EQUIP_NIKUMARU)
459 if (!game.frozen && !player->inputs_locked)
461 if (game.counter < 300000) // 100'00"0
462 game.counter++;
465 else
467 game.counter = 0;
471 void niku_draw(int value, bool force_white)
473 int clkframe = (game.counter % 30) <= 10;
474 if (game.frozen || player->inputs_locked || force_white) clkframe = 0;
476 draw_sprite(NIKU_X, NIKU_Y, SPR_NIKU_CLOCK, clkframe);
478 int mins = (value / 3000); // the game runs at 50 fps
479 int secs = (value / 50) % 60;
480 int tens = (value / 5) % 10;
482 DrawNumber(NIKU_X, NIKU_Y, mins);
483 DrawTwoDigitNumber(NIKU_X+36, NIKU_Y, secs);
484 DrawDigit(NIKU_X+56, NIKU_Y, tens);
486 draw_sprite(NIKU_X+30, NIKU_Y, SPR_NIKU_PUNC);