diff --git a/src/comp/fighter.c b/src/comp/fighter.c index ed803e9..02519cf 100644 --- a/src/comp/fighter.c +++ b/src/comp/fighter.c @@ -4,8 +4,28 @@ #include "enemies.h" #include #include +#include #include +fighter_stat_model_t fighter_stat_model_add(int amount, ...) +{ + va_list args; + va_start(args, amount); + + fighter_stat_model_t model = { 0 }; + + for(int i = 0; i < amount; i++) { + fighter_stat_model_t op = va_arg(args, fighter_stat_model_t); + model.HP += op.HP; + model.ATK += op.ATK; + model.DEF += op.DEF; + model.MAG += op.MAG; + } + + va_end(args); + return model; +} + static int instantiate_stat(fixed_t affinity, fixed_t multiplier, int level) { float a = f2double(affinity); @@ -14,13 +34,23 @@ static int instantiate_stat(fixed_t affinity, fixed_t multiplier, int level) return fround(multiplier * (int)y); } -void fighter_set_stats(fighter_t *f, fighter_stat_model_t const *model, - int level, fixed_t factor) +fighter_stat_model_t fighter_stat_model_instantiate( + fighter_stat_model_t const *model, int level, fixed_t multiplier) { - f->HP_max = instantiate_stat(model->HP, fmul(fix(2.0), factor), level); - f->ATK = instantiate_stat(model->ATK, fix(1.6), level); - f->MAG = instantiate_stat(model->MAG, fix(1.6), level); - f->DEF = instantiate_stat(model->DEF, fix(0.7), level); + fighter_stat_model_t m; + m.HP = fix(instantiate_stat(model->HP, 2 * multiplier, level)); + m.ATK = fix(instantiate_stat(model->ATK, fix(1.6), level)); + m.DEF = fix(instantiate_stat(model->DEF, fix(0.9), level)); + m.MAG = fix(instantiate_stat(model->MAG, fix(1.2), level)); + return m; +} + +void fighter_set_stats(fighter_t *f, fighter_stat_model_t const *instance) +{ + f->HP_max = ffloor(instance->HP); + f->ATK = ffloor(instance->ATK); + f->MAG = ffloor(instance->MAG); + f->DEF = ffloor(instance->DEF); } int fighter_damage(entity_t *e, int base_damage) diff --git a/src/comp/fighter.h b/src/comp/fighter.h index 8861a33..a8d0189 100644 --- a/src/comp/fighter.h +++ b/src/comp/fighter.h @@ -54,9 +54,15 @@ typedef struct } fighter_t; -/* Initialize fighter's stats by using the provided stat model */ -void fighter_set_stats(fighter_t *f, fighter_stat_model_t const *model, - int level, fixed_t multiplier); +/* Adds two or more stat models */ +fighter_stat_model_t fighter_stat_model_add(int amount, ...); + +/* Instantiate a statistics model */ +fighter_stat_model_t fighter_stat_model_instantiate( + fighter_stat_model_t const *model, int level, fixed_t multiplier); + +/* Initialize fighter's stats by using the instantiated stat model */ +void fighter_set_stats(fighter_t *f, fighter_stat_model_t const *instance); /* 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 d727ea8..38d688a 100644 --- a/src/enemies.c +++ b/src/enemies.c @@ -157,7 +157,9 @@ entity_t *enemy_make(int enemy_id) fighter_t *f = getcomp(e, fighter); memset(f, 0, sizeof *f); - fighter_set_stats(f, &data->stats, data->level, fix(1.0)); + fighter_stat_model_t inst = fighter_stat_model_instantiate(&data->stats, + data->level, fix(1.0)); + fighter_set_stats(f, &inst); f->HP = f->HP_max; f->combo_length = 1; f->enemy = malloc(sizeof *f->enemy + data->ai_data_size); diff --git a/src/item.c b/src/item.c index 931cc20..9137693 100644 --- a/src/item.c +++ b/src/item.c @@ -105,3 +105,28 @@ int item_equipment_slot(int item) default: return -1; } } + +fighter_stat_model_t item_stat_model(int item) +{ + switch(item) { + case ITEM_SWORD1: return (fighter_stat_model_t){ + .ATK = fix(0.4), + .MAG = fix(0.2), + }; + case ITEM_SWORD2: return (fighter_stat_model_t){ + .ATK = fix(0.8), + .MAG = fix(0.3), + }; + case ITEM_SCEPTER1: return (fighter_stat_model_t){ + .ATK = fix(0.15), + .DEF = fix(0.1), + .MAG = fix(0.3), + }; + case ITEM_SCEPTER2: return (fighter_stat_model_t){ + .ATK = fix(0.3), + .DEF = fix(0.15), + .MAG = fix(0.6), + }; + default: return (fighter_stat_model_t){ 0 }; + } +} diff --git a/src/item.h b/src/item.h index b34f26e..bf925fe 100644 --- a/src/item.h +++ b/src/item.h @@ -5,6 +5,7 @@ #pragma once #include "comp/entity.h" +#include "comp/fighter.h" #include "geometry.h" #include "anim.h" @@ -36,3 +37,6 @@ 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); diff --git a/src/main.c b/src/main.c index 7096856..34f7c4d 100644 --- a/src/main.c +++ b/src/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,7 @@ int main(void) /* Enable %D for decimal fixed-point in printf()-like functions */ __printf_enable_fixed(); /* Initialize the PRNG */ - srand(0xc0ffee + 1); + srand(rtc_ticks()); /* Initialize the benchmarking/profiling library */ prof_init(); /* Open the USB connection (for screenshots with fxlink) */ @@ -383,6 +384,11 @@ int main(void) int e = player_data.equipment[slot]; player_data.equipment[slot] = item; player_data.inventory[game.menu_cursor] = e; + + 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); } } } diff --git a/src/player.c b/src/player.c index f0d0ec8..ca46d48 100644 --- a/src/player.c +++ b/src/player.c @@ -1,4 +1,5 @@ #include "player.h" +#include "item.h" #include static int xp_to_next_level(int level) @@ -24,14 +25,32 @@ bool player_add_xp(entity_t *e, int points) if(leveled_up) { fighter_t *f = getcomp(p->entity, fighter); int previous_HP_max = f->HP_max; - fighter_set_stats(getcomp(p->entity, fighter), &p->stat_model, - p->xp_level, fix(2.0)); + + 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[4] = { + p->stat_model, + item_stat_model(equips[0]), + item_stat_model(equips[1]), + item_stat_model(equips[2]), + }; + + fighter_stat_model_t model = fighter_stat_model_add(4, + m[0], m[1], m[2], m[3]); + + return fighter_stat_model_instantiate(&model, p->xp_level, fix(2.0)); +} + bool player_give_item(entity_t *e, int item) { player_data_t *p = getcomp(e, fighter)->player; diff --git a/src/player.h b/src/player.h index 9f447f8..df7bdd7 100644 --- a/src/player.h +++ b/src/player.h @@ -30,5 +30,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); + /* Put an item into the player's inventory. false if inventory is full */ bool player_give_item(entity_t *p, int item); diff --git a/src/render.c b/src/render.c index 8f76704..f6560fa 100644 --- a/src/render.c +++ b/src/render.c @@ -14,6 +14,7 @@ #include #include #include +#include #include //--- @@ -412,6 +413,26 @@ void render_panel(int x, int y, bool hflip) uint32_t time_render_map = 0; uint32_t time_render_hud = 0; +static void print_stat_opt(int x, int y, int stat, int reference, + char const *format, ...) +{ + va_list args; + va_start(args, format); + char str[32]; + vsnprintf(str, 32, format, args); + va_end(args); + + int color = C_WHITE; + if(stat < reference) color = RGB24(0xc05458); + if(stat > reference) color = RGB24(0x21c24f); + dtext(x, y, color, str); +} + +static void print_stat(int x, int y, int stat, int reference) +{ + print_stat_opt(x, y, stat, reference, "%d", stat); +} + void render_game(game_t const *g, bool show_hitboxes) { camera_t const *camera = &g->camera; @@ -577,14 +598,26 @@ void render_game(game_t const *g, bool show_hitboxes) } } + /* What the stats would be if the selected item is equipped */ + fighter_stat_model_t stats_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); + dprint(x2+14, 103, C_WHITE, "HP"); - dprint(x2+54, 103, C_WHITE, "%d/%d", player_f->HP, player_f->HP_max); + 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"); - dprint(x2+54, 118, C_WHITE, "%d", player_f->ATK); + print_stat(x2+54, 118, ffloor(stats_equip.ATK), player_f->ATK); dprint(x2+14, 133, C_WHITE, "MAG"); - dprint(x2+54, 133, C_WHITE, "%d", player_f->MAG); + print_stat(x2+54, 133, ffloor(stats_equip.MAG), player_f->MAG); dprint(x2+14, 148, C_WHITE, "DEF"); - dprint(x2+54, 148, C_WHITE, "%d", player_f->DEF); + print_stat(x2+54, 148, ffloor(stats_equip.DEF), player_f->DEF); dfont(old_font); }