diff --git a/CMakeLists.txt b/CMakeLists.txt index 806d521..ec66af8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ set(ASSETS assets-cg/hud_itemslots.png assets-cg/hud_life.png assets-cg/hud_panel.png + assets-cg/hud_small.png assets-cg/hud_xp.ase assets-cg/skillicons.png assets-cg/font_hud.png diff --git a/assets-cg/hud_itemslots.png b/assets-cg/hud_itemslots.png index 4ff3f24..a8ce64b 100644 Binary files a/assets-cg/hud_itemslots.png and b/assets-cg/hud_itemslots.png differ diff --git a/assets-cg/hud_panel.png b/assets-cg/hud_panel.png index 6e2a6e1..1620ce3 100644 Binary files a/assets-cg/hud_panel.png and b/assets-cg/hud_panel.png differ diff --git a/assets-cg/hud_small.png b/assets-cg/hud_small.png new file mode 100644 index 0000000..3be3dd0 Binary files /dev/null and b/assets-cg/hud_small.png differ diff --git a/src/comp/fighter.c b/src/comp/fighter.c index 8f6e086..6f963f7 100644 --- a/src/comp/fighter.c +++ b/src/comp/fighter.c @@ -7,64 +7,52 @@ #include #include -fighter_stat_model_t fighter_stat_model_mul(int amount, ...) +fighter_stats_t fighter_stats_add(int amount, ...) { va_list args; va_start(args, amount); - fighter_stat_model_t model = { - .HP = fix(1), - .ATK = fix(1), - .DEF = fix(1), - .MAG = fix(1), }; + fighter_stats_t m = { 0 }; for(int i = 0; i < amount; i++) { - fighter_stat_model_t op = va_arg(args, fighter_stat_model_t); - model.HP = fmul(model.HP , op.HP); - model.ATK = fmul(model.ATK, op.ATK); - model.DEF = fmul(model.DEF, op.DEF); - model.MAG = fmul(model.MAG, op.MAG); + fighter_stats_t op = va_arg(args, fighter_stats_t); + m.HP += op.HP; + m.ATK += op.ATK; + m.DEF += op.DEF; + m.MAG += op.MAG; } va_end(args); - return model; -} - -static int instantiate_stat(fixed_t affinity, fixed_t multiplier, int level) -{ - float a = f2double(affinity); - float x = level; - float y = powf(x, a) + x + 3 * a; - return fround(multiplier * (int)y); -} - -fighter_stat_model_t fighter_stat_model_instantiate( - fighter_stat_model_t const *model, fighter_stat_model_t const *equipment, - int level, fixed_t hp_multiplier) -{ - fighter_stat_model_t m; - m.HP = fix(instantiate_stat(model->HP, 3 * hp_multiplier, level)); - m.ATK = fix(instantiate_stat(model->ATK, fix(1.6), level)); - m.DEF = fix(instantiate_stat(model->DEF, fix(0.7), level)); - m.MAG = fix(instantiate_stat(model->MAG, fix(1.2), level)); - - /* Apply equipment boost */ - if(equipment) { - m.HP = fmul(m.HP, equipment->HP); - m.ATK = fmul(m.ATK, equipment->ATK); - m.DEF = fmul(m.DEF, equipment->DEF); - m.MAG = fmul(m.MAG, equipment->MAG); - } - return m; } -void fighter_set_stats(fighter_t *f, fighter_stat_model_t const *instance) +fighter_stats_t fighter_stats_instantiate(fighter_stats_t const *base, + fighter_stats_t const *slope, int level) { - f->HP_max = ffloor(instance->HP); - f->ATK = ffloor(instance->ATK); - f->MAG = ffloor(instance->MAG); - f->DEF = ffloor(instance->DEF); + fighter_stats_t m; + m.HP = base->HP + level * slope->HP; + m.ATK = base->ATK + level * slope->ATK; + m.DEF = base->DEF + level * slope->DEF; + m.MAG = base->MAG + level * slope->MAG; + return m; +} + +void fighter_set_stats(fighter_t *f, fighter_stats_t const *stats) +{ + f->HP_max = stats->HP; + f->ATK = stats->ATK; + f->MAG = stats->MAG; + f->DEF = stats->DEF; +} + +void fighter_increase_stats(fighter_t *f, fighter_stats_t const *growth) +{ + int previous_HP_max = f->HP_max; + f->HP_max += growth->HP; + f->HP += (f->HP_max - previous_HP_max); + f->ATK += growth->ATK; + f->MAG += growth->MAG; + f->DEF += growth->DEF; } int fighter_damage(entity_t *e, int base_damage) diff --git a/src/comp/fighter.h b/src/comp/fighter.h index 6630268..5179b05 100644 --- a/src/comp/fighter.h +++ b/src/comp/fighter.h @@ -12,15 +12,15 @@ typedef struct { /* Hit Points */ - fixed_t HP; + int HP; /* Physical attack */ - fixed_t ATK; + int ATK; /* Magical attack */ - fixed_t MAG; + int MAG; /* Physical and magical defense */ - fixed_t DEF; + int DEF; -} fighter_stat_model_t; +} fighter_stats_t; typedef struct { @@ -55,16 +55,17 @@ typedef struct } fighter_t; -/* Multiply equipment stat modifiers */ -fighter_stat_model_t fighter_stat_model_mul(int amount, ...); +/* Add equipment stat modifiers */ +fighter_stats_t fighter_stats_add(int amount, ...); -/* Instantiate a statistics model with optional equipment and HP multiplier */ -fighter_stat_model_t fighter_stat_model_instantiate( - fighter_stat_model_t const *model, fighter_stat_model_t const *equipment, - int level, fixed_t hp_multiplier); +/* Instantiate a statistics model at any level */ +fighter_stats_t fighter_stats_instantiate(fighter_stats_t const *base,fighter_stats_t const *slope, int level); /* Initialize fighter's stats by using the instantiated stat model */ -void fighter_set_stats(fighter_t *f, fighter_stat_model_t const *instance); +void fighter_set_stats(fighter_t *f, fighter_stats_t const *instance); + +/* Increase fighter's stats by specified amount */ +void fighter_increase_stats(fighter_t *f, fighter_stats_t const *growth); /* Damage entity for that amount of raw strength. Returns actual damage after DES is subtracted, and randomization. */ diff --git a/src/enemies.c b/src/enemies.c index 30ea46b..6e64b1d 100644 --- a/src/enemies.c +++ b/src/enemies.c @@ -31,12 +31,9 @@ static enemy_t const slime_1 = { .friction = fix(0.6), .max_disruption_speed = fix(999.0), }, - .stats = { - .HP = fix(1.4), - .ATK = fix(1.3), - .MAG = fix(1.0), - .DEF = fix(1.4), - }, + .stats_base = { .HP=10, .ATK=8, .MAG=5, .DEF=4 }, + .stats_growth = { .HP=8, .ATK=4, .MAG=4, .DEF=3 }, + .shadow_size = 4, .xp = 2, .z = 0, @@ -53,12 +50,9 @@ static enemy_t const bat_2 = { .friction = fix(0.8), .max_disruption_speed = fix(999.0), }, - .stats = { - .HP = fix(1.5), - .ATK = fix(1.5), - .MAG = fix(1.3), - .DEF = fix(1.2), - }, + .stats_base = { .HP=12, .ATK=10, .MAG=5, .DEF=5 }, + .stats_growth = { .HP=10, .ATK=3, .MAG=2, .DEF=1 }, + .shadow_size = 4, .xp = 6, .z = fix(0.75), @@ -75,12 +69,10 @@ static enemy_t const fire_slime_4 = { .friction = fix(0.6), .max_disruption_speed = fix(999.0), }, - .stats = { - .HP = fix(1.4), - .ATK = fix(1.2), - .MAG = fix(1.4), - .DEF = fix(1.4), - }, + /* Same as slime/1 */ + .stats_base = { .HP=10, .ATK=8, .MAG=5, .DEF=4 }, + .stats_growth = { .HP=8, .ATK=4, .MAG=4, .DEF=3 }, + .shadow_size = 4, .xp = 14, .z = 0, @@ -97,13 +89,10 @@ static enemy_t const albinos_bat_6 = { .friction = fix(0.7), .max_disruption_speed = fix(999.0), }, - .stats = { - /* Same as bat/2 */ - .HP = fix(1.5), - .ATK = fix(1.5), - .MAG = fix(1.3), - .DEF = fix(1.2), - }, + /* Same as bat/2 */ + .stats_base = { .HP=12, .ATK=10, .MAG=5, .DEF=5 }, + .stats_growth = { .HP=10, .ATK=3, .MAG=2, .DEF=1 }, + .shadow_size = 4, .xp = 20, .z = fix(0.5), @@ -120,12 +109,9 @@ static enemy_t const gunslinger_8 = { .friction = fix(0.8), .max_disruption_speed = fix(999.0), }, - .stats = { - .HP = fix(1.3), - .ATK = fix(1.6), - .MAG = fix(1.4), - .DEF = fix(1.2), - }, + .stats_base = { .HP=16, .ATK=12, .MAG=3, .DEF=6 }, + .stats_growth = { .HP=12, .ATK=4, .MAG=1, .DEF=1 }, + .shadow_size = 4, .xp = 30, .z = fix(0.25), @@ -184,9 +170,9 @@ entity_t *enemy_make(int enemy_id) fighter_t *f = getcomp(e, fighter); memset(f, 0, sizeof *f); - fighter_stat_model_t inst = fighter_stat_model_instantiate(&data->stats, - NULL, data->level, fix(1.0)); - fighter_set_stats(f, &inst); + fighter_stats_t stats = fighter_stats_instantiate(&data->stats_base, + &data->stats_growth, data->level); + fighter_set_stats(f, &stats); f->HP = f->HP_max; f->combo_length = 1; f->enemy = malloc(sizeof *f->enemy + data->ai_data_size); diff --git a/src/enemies.h b/src/enemies.h index 9db624d..a024d0c 100644 --- a/src/enemies.h +++ b/src/enemies.h @@ -23,7 +23,7 @@ typedef struct /* Movement parameters */ mechanical_limits_t limits; /* Statistics model */ - fighter_stat_model_t stats; + fighter_stats_t stats_base, stats_growth; /* Level */ uint8_t level; /* Shadow size */ diff --git a/src/item.c b/src/item.c index 45a67c9..6a41526 100644 --- a/src/item.c +++ b/src/item.c @@ -36,6 +36,64 @@ anim_t const *item_anim(int item) return NULL; } +char const *item_name(int item) +{ + if(item == ITEM_LIFE) + return "Heart transpl."; + else if(item == ITEM_POTION_ATK) + return "Berserk elixir"; + else if(item == ITEM_POTION_COOLDOWN) + return "Quick reload"; + else if(item == ITEM_POTION_DEF) + return "Turtle potion"; + else if(item == ITEM_POTION_FRZ) + return "Time freeze"; + else if(item == ITEM_POTION_HP) + return "Health potion"; + else if(item == ITEM_POTION_SPD) + return "Speed potion"; + else if(item == ITEM_SCEPTER1) + return "Cypress wand"; + else if(item == ITEM_SCEPTER2) + return "Holy staff"; + else if(item == ITEM_SWORD1) + return "Short sword"; + else if(item == ITEM_SWORD2) + return "Golden blade"; + else if(item == ITEM_ARMOR1) + return "Leather armor"; + return "???"; +} + +char const *item_description(int item) +{ + if(item == ITEM_LIFE) + return NULL; + else if(item == ITEM_POTION_ATK) + return NULL; + else if(item == ITEM_POTION_COOLDOWN) + return NULL; + else if(item == ITEM_POTION_DEF) + return NULL; + else if(item == ITEM_POTION_FRZ) + return NULL; + else if(item == ITEM_POTION_HP) + return NULL; + else if(item == ITEM_POTION_SPD) + return NULL; + else if(item == ITEM_SCEPTER1) + return "On level up:\n ATK+1 MAG+3 DEF+1"; + else if(item == ITEM_SCEPTER2) + return "On level up:\n MAG+5 DEF+1"; + else if(item == ITEM_SWORD1) + return "On level up:\n ATK+3 MAG+1"; + else if(item == ITEM_SWORD2) + return "On level up:\n ATK+5 MAG+0"; + else if(item == ITEM_ARMOR1) + return "On level up:\n DEF+2"; + return NULL; +} + entity_t *item_make(int type, vec2 position) { entity_t *e = aoe_make(AOE_ITEM, position, fix(9999.0)); @@ -110,36 +168,30 @@ int item_equipment_slot(int item) } } -fighter_stat_model_t item_stat_model(int item) +fighter_stats_t item_stat_model(int item) { - fighter_stat_model_t m = { - .HP = fix(1.0), - .ATK = fix(1.0), - .DEF = fix(1.0), - .MAG = fix(1.0), - }; + fighter_stats_t m = { 0 }; switch(item) { case ITEM_SWORD1: - m.ATK = fix(1.4); - m.MAG = fix(1.2); + m.ATK = 3; + m.MAG = 1; break; case ITEM_SWORD2: - m.ATK = fix(1.8); - m.MAG = fix(1.3); + m.ATK = 5; + m.MAG = 0; break; case ITEM_SCEPTER1: - m.ATK = fix(1.15); - m.DEF = fix(1.1); - m.MAG = fix(1.3); + m.ATK = 1; + m.MAG = 3; + m.DEF = 1; break; case ITEM_SCEPTER2: - m.ATK = fix(1.25); - m.DEF = fix(1.15); - m.MAG = fix(1.6); + m.MAG = 5; + m.DEF = 1; break; case ITEM_ARMOR1: - m.DEF = fix(1.3); + m.DEF = 2; break; } diff --git a/src/item.h b/src/item.h index 2216dc5..ef0b743 100644 --- a/src/item.h +++ b/src/item.h @@ -30,6 +30,16 @@ enum { /* Animation for each item. */ anim_t const *item_anim(int item); +/* Item name (fancy huh?). */ +char const *item_name(int item); + +/* Item description, can be multi-line. NULL if none specified. */ +char const *item_description(int item); + +/* Whether an item is a piece of equipment. */ +#define item_is_equip(item) \ + ((item) >= ITEM_EQUIPMENT_START && (item) <= ITEM_EQUIPMENT_END) + /* Create an item. This is just an AOE with a particular type. */ entity_t *item_make(int item, vec2 position); @@ -39,8 +49,8 @@ bool item_pick_up(int item, entity_t *player); /* Which equipment slot an item goes in. */ int item_equipment_slot(int item); -/* Stat increases for each item that can be equipped. */ -fighter_stat_model_t item_stat_model(int item); +/* Stat increases for each item that can be equipped, per level. */ +fighter_stats_t item_stat_model(int item); /* Get skills associated with each item that can be equipped. */ void item_skills(int item, int *skill1, int *skill2, int *skill3); diff --git a/src/main.c b/src/main.c index a40aee3..b88f753 100644 --- a/src/main.c +++ b/src/main.c @@ -100,16 +100,14 @@ int main(void) .dash_duration = fix(1) / 8, .max_disruption_speed = fix(3.0), }, - .stat_model = { - .HP = fix(1.5), - .ATK = fix(1.5), - .DEF = fix(1.5), - .MAG = fix(1.5), - }, + .stats = { .HP=60, .ATK=13, .MAG=10, .DEF=6 }, + .stats_growth = { .HP=25, .ATK=1, .MAG=1, .DEF=1 }, + .xp_level = 0, .xp_to_next_level = 0, .xp_current = 0, - .inventory = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }, +// .inventory = { -1, -1, -1, -1, -1, -1, -1, -1 }, + .inventory = { 1, 2, 3, 5, 101, -1, -1, -1 }, .equipment = { -1, -1, -1 }, }; @@ -131,6 +129,10 @@ int main(void) for(int i = 0; i < 6; i++) player_f->actions_cooldown[i] = fix(0.0); /* Initialize stats. This will level up to level 1 */ + player_f->HP_max = player_data.stats.HP; + player_f->ATK = player_data.stats.ATK; + player_f->MAG = player_data.stats.MAG; + player_f->DEF = player_data.stats.DEF; player_add_xp(player, 0); player_f->HP = player_f->HP_max; @@ -439,29 +441,25 @@ int main(void) /* Inventory movement */ if(!debug.paused && game.menu_open) { - int y = game.menu_cursor / 3, x = game.menu_cursor % 3; + int y = game.menu_cursor / 4, x = game.menu_cursor % 4; y = y + (ev.key == KEY_DOWN) - (ev.key == KEY_UP); x = x + (ev.key == KEY_RIGHT) - (ev.key == KEY_LEFT); - y = max(0, min(y, 2)); - x = max(0, min(x, 2)); - game.menu_cursor = 3 * y + x; + y = max(0, min(y, 1)); + x = max(0, min(x, 3)); + game.menu_cursor = 4 * y + x; } - /* Equipping items */ + /* Equipping and unequipping items */ if(!debug.paused && game.menu_open && ev.key == KEY_SHIFT) { int item = player_data.inventory[game.menu_cursor]; int slot = item_equipment_slot(item); if(item >= 0 && slot >= 0) { - int e = player_data.equipment[slot]; - player_data.equipment[slot] = item; - player_data.inventory[game.menu_cursor] = e; + if(player_data.equipment[slot] == game.menu_cursor) + player_data.equipment[slot] = -1; + else + player_data.equipment[slot] = game.menu_cursor; - /* Update stats */ - fighter_stat_model_t stats = player_compute_stats(player, - player_data.equipment); - fighter_set_stats(player_f, &stats); - player_f->HP = min(player_f->HP, player_f->HP_max); /* Update skills */ player_compute_skills(player, player_data.equipment, player_f->skills); diff --git a/src/player.c b/src/player.c index 8c0c525..637e23d 100644 --- a/src/player.c +++ b/src/player.c @@ -7,10 +7,24 @@ static int xp_to_next_level(int level) return 5 * (level + 2) * (level + 2); } -bool player_add_xp(entity_t *e, int points) +fighter_stats_t player_compute_growth(entity_t *e, int *equips) { player_data_t *p = getcomp(e, fighter)->player; + fighter_stats_t m[3] = { + item_stat_model(p->inventory[equips[0]]), + item_stat_model(p->inventory[equips[1]]), + item_stat_model(p->inventory[equips[2]]), + }; + + return fighter_stats_add(4, m[0], m[1], m[2], p->stats_growth); +} + +bool player_add_xp(entity_t *e, int points) +{ + fighter_t *f = getcomp(e, fighter); + player_data_t *p = f->player; + bool leveled_up = false; p->xp_current += max(points, 0); @@ -18,62 +32,39 @@ bool player_add_xp(entity_t *e, int points) p->xp_current -= p->xp_to_next_level; p->xp_level++; p->xp_to_next_level = xp_to_next_level(p->xp_level); + + fighter_stats_t growth = player_compute_growth(e, p->equipment); + fighter_increase_stats(f, &growth); leveled_up = true; } - /* Recompute statistics */ - if(leveled_up) { - fighter_t *f = getcomp(p->entity, fighter); - int previous_HP_max = f->HP_max; - - fighter_stat_model_t stats = player_compute_stats(e, p->equipment); - fighter_set_stats(getcomp(p->entity, fighter), &stats); - f->HP += (f->HP_max - previous_HP_max); - } - return leveled_up; } -fighter_stat_model_t player_compute_stats(entity_t *e, int *equips) -{ - player_data_t *p = getcomp(e, fighter)->player; - - fighter_stat_model_t m[3] = { - item_stat_model(equips[0]), - item_stat_model(equips[1]), - item_stat_model(equips[2]), - }; - - fighter_stat_model_t equip_model = fighter_stat_model_mul(3, - m[0], m[1], m[2]); - - return fighter_stat_model_instantiate(&p->stat_model, &equip_model, - p->xp_level, fix(3.0)); -} - void player_compute_skills(entity_t *e, int *equips, int *skills) { - (void)e; + player_data_t *p = getcomp(e, fighter)->player; + int *inv = p->inventory; for(int i = 0; i < 6; i++) skills[i] = -1; /* Sword/staff */ if(equips[0] >= 0) - item_skills(equips[0], &skills[0], &skills[1], &skills[2]); + item_skills(inv[equips[0]], &skills[0], &skills[1], &skills[2]); /* Armor */ if(equips[1] >= 0) - item_skills(equips[1], &skills[3], &skills[4], NULL); + item_skills(inv[equips[1]], &skills[3], &skills[4], NULL); /* Accessory */ if(equips[2] >= 0) - item_skills(equips[2], &skills[5], NULL, NULL); + item_skills(inv[equips[2]], &skills[5], NULL, NULL); } bool player_give_item(entity_t *e, int item) { player_data_t *p = getcomp(e, fighter)->player; - for(int i = 0; i < 9; i++) { + for(int i = 0; i < 8; i++) { if(p->inventory[i] < 0) { p->inventory[i] = item; return true; diff --git a/src/player.h b/src/player.h index 4f05d99..a9a9ec9 100644 --- a/src/player.h +++ b/src/player.h @@ -14,15 +14,16 @@ typedef struct player_data { entity_t *entity; /* Mechanical limits (dynamically recomputed) */ mechanical_limits_t mechanical_limits; - /* Fighter model (dynamically recomputed) */ - fighter_stat_model_t stat_model; + /* Fighter model */ + fighter_stats_t stats, stats_growth; /* Experience level, total to next level, current points (within level) */ int xp_level; int xp_to_next_level; int xp_current; /* Inventory */ - int inventory[9]; - /* Current equipped items */ + int inventory[8]; + /* IDs of inventory slots holding currently-equipped item for each kind of + equipment (-1 if none equipped) */ int equipment[3]; } player_data_t; @@ -30,8 +31,8 @@ typedef struct player_data { /* Add XP points to a player. Returns true if levels up */ bool player_add_xp(entity_t *p, int points); -/* Compute player's statistics under hypothetical equips */ -fighter_stat_model_t player_compute_stats(entity_t *e, int *equips); +/* Compute player's statistics growth under hypothetical equips */ +fighter_stats_t player_compute_growth(entity_t *e, int *equips); /* Compute player's skills under hypothetical equips */ void player_compute_skills(entity_t *e, int *equips, int *skills); diff --git a/src/render.c b/src/render.c index 4182b88..2c975e0 100644 --- a/src/render.c +++ b/src/render.c @@ -392,17 +392,17 @@ static void render_info(int x, int y, game_t const *g) } } -void render_panel(int x, int y, bool hflip) +static void render_panel(int x, int y, bool hflip) { extern bopti_image_t img_hud_panel; - int sx = hflip ? 0 : 14; + int w = 130; + int sx = hflip ? 0 : img_hud_panel.width - w; int sy = 0; - int w = 94; dsubimage(x, y, &img_hud_panel, sx, sy, w, 30, DIMAGE_NONE); y += 30; sy += 30; - for(int i = 0; i < 9; i++) { + for(int i = 0; i < 10; i++) { dsubimage(x, y, &img_hud_panel, sx, sy, w, 10, DIMAGE_NONE); y += 10; } @@ -413,6 +413,70 @@ void render_panel(int x, int y, bool hflip) uint32_t time_render_map = 0; uint32_t time_render_hud = 0; +static void subimage_outline(int x, int y, image_t const *img, int left, + int top, size_t w, size_t h, int color) +{ + int alpha = image_alpha(img->format); + + for(int iy = -1; iy < (int)h+1; iy++) + for(int ix = -1; ix < (int)w+1; ix++) { + bool inb = ((size_t)ix < w && (size_t)iy < h); + int pixel = inb ? image_get_pixel(img, left+ix, top+iy) : alpha; + + if(pixel != alpha) { + dpixel(x+ix, y+iy, image_decode_pixel(img, pixel)); + continue; + } + + bool linked = false; + for(int i = 0; i < 4; i++) { + int dy = (i == 0) - (i == 1); + int dx = (i == 2) - (i == 3); + if((size_t)(ix+dx) < w && (size_t)(iy+dy) < h) + linked |= image_get_pixel(img, left+ix+dx, top+iy+dy) != alpha; + } + if(linked) + dpixel(x+ix, y+iy, color); + } +} + +static void anim_frame_render_outline(int x, int y, anim_frame_t const *frame, + int color) +{ + if(!frame) return; + subimage_outline(x - frame->cx, y - frame->cy, frame->sheet, + frame->x, frame->y, frame->w, frame->h, color); +} + +static int small_text(int x, int y, int color, char const *text, int size) +{ + extern bopti_image_t img_hud_small; + if(size < 0) + size = strlen(text); + + if(!strncmp(text, "SHIFT", size)) { + dsubimage(x-1, y+3, &img_hud_small, 0, 0, 27, 8, DIMAGE_NONE); + return 27-1; + } + if(!strncmp(text, "HP", size)) { + dsubimage(x-1, y+3, &img_hud_small, 0, 8, 12, 8, DIMAGE_NONE); + return 12-1; + } + if(!strncmp(text, "ATK", size)) { + dsubimage(x-1, y+3, &img_hud_small, 0, 16, 17, 8, DIMAGE_NONE); + return 17-1; + } + if(!strncmp(text, "MAG", size)) { + dsubimage(x-1, y+3, &img_hud_small, 0, 24, 19, 8, DIMAGE_NONE); + return 19-1; + } + if(!strncmp(text, "DEF", size)) { + dsubimage(x-1, y+3, &img_hud_small, 0, 32, 18, 8, DIMAGE_NONE); + return 18-1; + } + return 0; +} + static void print_stat_opt(int x, int y, int stat, int reference, char const *format, ...) { @@ -428,9 +492,43 @@ static void print_stat_opt(int x, int y, int stat, int reference, dtext(x, y, color, str); } -static void print_stat(int x, int y, int stat, int reference) +static void print_stat(int x, int y, int current, int g_vis, int g_base) { - print_stat_opt(x, y, stat, reference, "%d", stat); + dprint(x, y, C_WHITE, "%d", current); + + int color = C_RGB(10, 10, 10); + if(g_vis < g_base) color = RGB24(0xc05458); + if(g_vis > g_base) color = RGB24(0x21c24f); + dprint(x+30, y, color, "+%d/Lv", g_vis); +} + +static void dtext_multi(int x0, int y, int color, char const *str) +{ + int x = x0; + + while(*str) { + if(!strncmp(str, "HP", 2)) { + x += small_text(x, y, color, str, 2) + 1; + str += 2; + } + else if(!strncmp(str, "ATK", 3) || !strncmp(str, "MAG", 3) || + !strncmp(str, "DEF", 3)) { + x += small_text(x, y, color, str, 3) + 1; + str += 3; + } + else if(*str == '\n') { + str++; + y += 13; + x = x0; + } + else { + int w; + dnsize(str, 1, NULL, &w, NULL); + dtext_opt(x, y, color, C_NONE, DTEXT_LEFT, DTEXT_TOP, str, 1); + x += w + 1; + str++; + } + } } void render_game(game_t const *g, bool show_hitboxes) @@ -551,69 +649,93 @@ void render_game(game_t const *g, bool show_hitboxes) } if(g->menu_time > 0) { - int x1 = cubic(-94, 0, g->menu_time, fix(1)); - int x2 = DWIDTH - 94 - x1; + int x1 = cubic(-130, 0, g->menu_time, fix(1)); + int x2 = DWIDTH - 130 - x1; fighter_t *player_f = getcomp(g->player, fighter); player_data_t *player_data = player_f->player; - render_panel(x1, 27, false); - render_panel(x2, 27, true); + render_panel(x1, 22, false); + render_panel(x2, 22, true); extern font_t font_rogue; font_t const *old_font = dfont(&font_rogue); - dprint_opt(x1+42, 38, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP, + dprint_opt(x1+61, 33, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP, "Inventory"); - dprint_opt(x2+52, 38, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP, + dprint_opt(x2+72, 33, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP, "Status"); - dprint_opt(x2+52, 49, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP, + dprint_opt(x2+72, 44, C_WHITE, C_NONE, DTEXT_CENTER, DTEXT_TOP, "Lv.%d (%d/%d)", player_data->xp_level, player_data->xp_current, player_data->xp_to_next_level); + int selected_item = player_data->inventory[g->menu_cursor]; + extern bopti_image_t img_hud_itemslots; - for(int y = 0; y < 3; y++) - for(int x = 0; x < 3; x++) { - int sx = g->menu_cursor == 3*y + x ? 19 : 0; - dsubimage(x1+8+24*x, 58+24*y, &img_hud_itemslots, sx, 0, 19, 19, - DIMAGE_NONE); + for(int y = 0; y < 2; y++) + for(int x = 0; x < 4; x++) { + int sx = g->menu_cursor == 4*y + x ? 21 : 0; + int x2 = x1 + 15 + 24*x; + int y2 = 48 + 24*y; + dsubimage(x2, y2, &img_hud_itemslots, sx, 0, 21, 21, DIMAGE_NONE); - int item = player_data->inventory[3*y + x]; + int item = player_data->inventory[4*y + x]; + int slot = item_equipment_slot(item); if(item > 0) { anim_t const *anim = item_anim(item); - anim_frame_render(x1+8+24*x + 9, 58+24*y + 9, anim->start[0]); + if(anim) { + if(slot >= 0 && player_data->equipment[slot] == 4*y + x) + anim_frame_render_outline(x2+10, y2+10, anim->start[0], + RGB24(0x979515)); + else + anim_frame_render(x2+10, y2+10, anim->start[0]); + } } } + if(selected_item >= 0) { + int w, h; + dsize(item_name(selected_item), NULL, &w, &h); + drect(x1+5, 99, x1+w+8, 98+h+4, RGB24(0x3d5050)); + dtext(x1+7, 100, C_WHITE, item_name(selected_item)); - for(int x = 0; x < 3; x++) { - dsubimage(x2+20+24*x, 73, &img_hud_itemslots, 38, 0, 19, 19, - DIMAGE_NONE); + char const *desc = item_description(selected_item); + if(desc) + dtext_multi(x1+7, 116, C_WHITE, desc); - int item = player_data->equipment[x]; - if(item > 0) { - anim_t const *anim = item_anim(item); - anim_frame_render(x2+20+24*x + 9, 73 + 9, anim->start[0]); + int dx = small_text(x1+7, 158, -1, "SHIFT", -1); + char const *use_str = "Use"; + if(item_is_equip(selected_item)) { + use_str = "Equip"; + int slot = item_equipment_slot(selected_item); + if(slot >= 0 && player_data->equipment[slot] == g->menu_cursor) + use_str = "Unequip"; } + dtext(x1+7+dx+4, 158, C_WHITE, use_str); } + /* What the growth is with the current equips */ + fighter_stats_t growth_base; + growth_base = player_compute_growth(g->player, player_data->equipment); + /* What the stats would be if the selected item is equipped */ - fighter_stat_model_t stats_equip; + fighter_stats_t growth_equip; int switched_equipment[3]; memcpy(switched_equipment, player_data->equipment, 3*sizeof(int)); - int selected_item = player_data->inventory[g->menu_cursor]; int selected_slot = item_equipment_slot(selected_item); if(selected_item >= 0 && selected_slot >= 0) - switched_equipment[selected_slot] = selected_item; - stats_equip = player_compute_stats(g->player, switched_equipment); - int max_HP_equip = ffloor(stats_equip.HP); + switched_equipment[selected_slot] = g->menu_cursor; + growth_equip = player_compute_growth(g->player, switched_equipment); - dprint(x2+14, 103, C_WHITE, "HP"); - print_stat_opt(x2+54, 103, max_HP_equip, player_f->HP_max, - "%d/%d", player_f->HP, max_HP_equip); - dprint(x2+14, 118, C_WHITE, "ATK"); - print_stat(x2+54, 118, ffloor(stats_equip.ATK), player_f->ATK); - dprint(x2+14, 133, C_WHITE, "MAG"); - print_stat(x2+54, 133, ffloor(stats_equip.MAG), player_f->MAG); - dprint(x2+14, 148, C_WHITE, "DEF"); - print_stat(x2+54, 148, ffloor(stats_equip.DEF), player_f->DEF); + small_text(x2+14, 100, C_WHITE, "HP", -1); + print_stat_opt(x2+44, 100, growth_equip.HP, growth_base.HP, + "%d/%d", player_f->HP, player_f->HP_max); + small_text(x2+14, 114, C_WHITE, "ATK", -1); + print_stat(x2+44, 114, player_f->ATK, growth_equip.ATK, + growth_base.ATK); + small_text(x2+14, 128, C_WHITE, "MAG", -1); + print_stat(x2+44, 128, player_f->MAG, growth_equip.MAG, + growth_base.MAG); + small_text(x2+14, 142, C_WHITE, "DEF", -1); + print_stat(x2+44, 142, player_f->DEF, growth_equip.DEF, + growth_base.DEF); dfont(old_font); /* What the skills would be if the selected item is equipped */ diff --git a/src/render.h b/src/render.h index d9dcb29..ee54967 100644 --- a/src/render.h +++ b/src/render.h @@ -56,9 +56,6 @@ fixed_t camera_ppu(camera_t const *c); // Rendering //--- -/* Render panel overlay. */ -void render_panel(int x, int y, bool hflip); - /* Render a single layer of the map, with animated tiles. ss_x and ss_y are additional displacement for screenshake; default 0. layer is HORIZONTAL (floor), VERTICAL (wall), or CEILING.