preview and apply skill changes based on equipment

This commit is contained in:
Lephenixnoir 2022-03-19 17:14:00 +00:00
parent e0c46db0d7
commit b844a1d434
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
14 changed files with 155 additions and 41 deletions

View File

@ -72,6 +72,7 @@ set(ASSETS
assets-cg/items/stick2.aseprite
assets-cg/items/sword1.aseprite
assets-cg/items/sword2.aseprite
assets-cg/items/armor1.aseprite
# Player animations
assets-cg/player/player_up.aseprite
assets-cg/player/player_right.aseprite

View File

@ -47,6 +47,7 @@ ITEM_IDS = {
"scepter2": 102,
"sword1": 103,
"sword2": 104,
"armor1": 105,
}
def convert_level(input, params):

Binary file not shown.

View File

@ -6,12 +6,14 @@ player_spawn: 12,5
spawner: 18,5
spawner: 5,7
wave: 9s 3*slime/1
item: armor1
wave: 12s 12*slime/1
item: sword1
wave: 18s 10*slime/1 4*bat/2
wave: 4s 6*bat/2
wave: 18s 10*slime/1 2*bat/2
wave: 8s 6*bat/2
item: potion_hp
wave: 8s 16*slime/1 8*bat/2
wave: 8s 16*slime/1 6*bat/2
delay: 2s
wave: 6s 8*slime/1 8*bat/2 2*fire_slime/4
delay: 2s

View File

@ -87,6 +87,7 @@ ANIM1(item_stick1);
ANIM1(item_stick2);
ANIM1(item_sword1);
ANIM1(item_sword2);
ANIM1(item_armor1);
/* Animation functions. */

View File

@ -119,3 +119,4 @@ extern anim_t anims_item_stick1;
extern anim_t anims_item_stick2;
extern anim_t anims_item_sword1;
extern anim_t anims_item_sword2;
extern anim_t anims_item_armor1;

View File

@ -1,6 +1,7 @@
#include "item.h"
#include "aoe.h"
#include "player.h"
#include "skills.h"
#include "comp/fighter.h"
#include "comp/physical.h"
#include "comp/visible.h"
@ -30,6 +31,8 @@ anim_t const *item_anim(int item)
return &anims_item_sword1;
else if(item == ITEM_SWORD2)
return &anims_item_sword2;
else if(item == ITEM_ARMOR1)
return &anims_item_armor1;
return NULL;
}
@ -102,6 +105,7 @@ int item_equipment_slot(int item)
case ITEM_SWORD2: return 0;
case ITEM_SCEPTER1: return 0;
case ITEM_SCEPTER2: return 0;
case ITEM_ARMOR1: return 1;
default: return -1;
}
}
@ -127,6 +131,33 @@ fighter_stat_model_t item_stat_model(int item)
.DEF = fix(0.15),
.MAG = fix(0.6),
};
case ITEM_ARMOR1: return (fighter_stat_model_t){
.DEF = fix(0.25),
};
default: return (fighter_stat_model_t){ 0 };
}
}
void item_skills(int item, int *skill1, int *skill2, int *skill3)
{
if(item == ITEM_SWORD1) {
*skill1 = AOE_SLASH;
*skill2 = AOE_SHOCK;
}
if(item == ITEM_SWORD2) {
*skill1 = AOE_SLASH;
*skill2 = AOE_SHOCK;
}
if(item == ITEM_SCEPTER1) {
*skill1 = AOE_SLASH; // TODO: Base ranged attack
*skill2 = AOE_JUDGEMENT;
}
if(item == ITEM_SCEPTER2) {
*skill1 = AOE_SLASH;
*skill2 = AOE_JUDGEMENT;
*skill3 = AOE_BULLET;
}
if(item == ITEM_ARMOR1) {
*skill1 = SKILL_DASH;
}
}

View File

@ -23,6 +23,7 @@ enum {
ITEM_SCEPTER2,
ITEM_SWORD1,
ITEM_SWORD2,
ITEM_ARMOR1,
/**/ ITEM_EQUIPMENT_END,
};
@ -40,3 +41,6 @@ int item_equipment_slot(int item);
/* Stat increases for each item that can be equipped. */
fighter_stat_model_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);

View File

@ -123,10 +123,8 @@ int main(void)
player_f->combo_delay = fix(0);
player_f->enemy = NULL;
player_f->player = &player_data;
player_f->skills[1] = SKILL_DASH;
player_f->skills[2] = AOE_SHOCK;
player_f->skills[3] = AOE_JUDGEMENT;
player_f->skills[4] = AOE_BULLET;
for(int i = 0; i < 6; i++)
player_f->skills[i] = -1;
for(int i = 0; i < 6; i++)
player_f->actions_cooldown[i] = fix(0.0);
/* Initialize stats. This will level up to level 1 */
@ -385,10 +383,14 @@ int main(void)
player_data.equipment[slot] = item;
player_data.inventory[game.menu_cursor] = e;
/* 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);
}
}
}
@ -408,23 +410,34 @@ int main(void)
camera_move(c, 0, fmul(dt, vy));
#endif
/* Player movement input */
int input_dir = -1, next_dir = -1;
if(!debug.paused && !game.menu_open && player_f->HP > 0) {
if(keydown(KEY_UP)) input_dir = UP;
if(keydown(KEY_DOWN)) input_dir = DOWN;
if(keydown(KEY_LEFT)) input_dir = LEFT;
if(keydown(KEY_RIGHT)) input_dir = RIGHT;
}
next_dir = (input_dir >= 0) ? input_dir : player_p->facing;
/* Player skills (including movement skills) */
bool can_use_skill = !debug.paused && !game.menu_open
&& player_f->HP > 0 && !keydown(KEY_VARS);
if(can_use_skill && keydown(KEY_F1))
skill_use(&game, player, 1, fdir(next_dir));
if(can_use_skill && keydown(KEY_F2))
skill_use(&game, player, 2, fdir(next_dir));
if(can_use_skill && keydown(KEY_F3))
skill_use(&game, player, 3, fdir(next_dir));
if(can_use_skill && keydown(KEY_F4))
skill_use(&game, player, 4, fdir(next_dir));
/* Player movement */
if(!debug.paused && !game.menu_open && player_f->HP > 0) {
int dir = -1;
if(keydown(KEY_UP)) dir = UP;
if(keydown(KEY_DOWN)) dir = DOWN;
if(keydown(KEY_LEFT)) dir = LEFT;
if(keydown(KEY_RIGHT)) dir = RIGHT;
mechanical_move4(player, input_dir, dt, game.map);
if(keydown(KEY_F1) && !keydown(KEY_VARS)
&& player_f->actions_cooldown[1] == 0) {
int dash_dir = (dir >= 0) ? dir : player_p->facing;
mechanical_dash(player, dash_dir);
player_f->actions_cooldown[1] = fix(1.0);
}
mechanical_move4(player, dir, dt, game.map);
if(dir >= 0)
if(input_dir >= 0)
visible_set_anim(player, &anims_player_Walking, 1);
else
visible_set_anim(player, &anims_player_Idle, 1);
@ -475,17 +488,6 @@ int main(void)
player_f->combo_delay = getcomp(aoe, aoe)->lifetime;
}
/* Player skills */
bool can_attack = !debug.paused && !game.menu_open && player_f->HP > 0
&& !player_f->current_attack && !keydown(KEY_VARS);
if(can_attack && keydown(KEY_F2))
skill_use(&game, player, 2, fdir(player_p->facing));
if(can_attack && keydown(KEY_F3))
skill_use(&game, player, 3, fdir(player_p->facing));
if(can_attack && keydown(KEY_F4))
skill_use(&game, player, 4, fdir(player_p->facing));
/* Ideas for additional skills:
- Freeze enemies
- Barrier around player

View File

@ -51,6 +51,24 @@ fighter_stat_model_t player_compute_stats(entity_t *e, int *equips)
return fighter_stat_model_instantiate(&model, p->xp_level, fix(2.0));
}
void player_compute_skills(entity_t *e, int *equips, int *skills)
{
(void)e;
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]);
/* Armor */
if(equips[1] >= 0)
item_skills(equips[1], &skills[3], &skills[4], NULL);
/* Accessory */
if(equips[2] >= 0)
item_skills(equips[2], &skills[5], NULL, NULL);
}
bool player_give_item(entity_t *e, int item)
{
player_data_t *p = getcomp(e, fighter)->player;

View File

@ -33,5 +33,8 @@ 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 skills under hypothetical equips */
void player_compute_skills(entity_t *e, int *equips, int *skills);
/* Put an item into the player's inventory. false if inventory is full */
bool player_give_item(entity_t *p, int item);

View File

@ -521,24 +521,20 @@ void render_game(game_t const *g, bool show_hitboxes)
}
/* Render skill icons */
extern bopti_image_t img_skillicons;
static const int skill_size = 23;
static const int skill_box_size = 27;
for(int i = 0; i < 5; i++) {
/* Activity and cooldown */
fixed_t cooldown_total = skill_cooldown(player_f->skills[i+1]);
fixed_t cooldown_remaining = player_f->actions_cooldown[i+1];
int skill = player_f->skills[i+1];
int x = 31 + 48*i + 64*(i>=3);
int y = HUD_Y - 33;
int bg = (cooldown_remaining != 0);
dsubimage(x+2, y+2, &img_skillicons, skill_size * bg, 0, skill_size,
skill_size, DIMAGE_NONE);
dsubimage(x+2, y+2, &img_skillicons, skill_size * (i+2), 0, skill_size,
skill_size, DIMAGE_NONE);
int bg = (cooldown_remaining != 0) ? 2 : 1;
skill_render(x+2, y+2, skill, bg, C_WHITE);
/* Darken the area representing remaining cooldown */
/* Whiten the area representing remaining cooldown */
if(cooldown_total != 0) {
int height = (cooldown_remaining*skill_box_size) / cooldown_total;
int ymin = y + skill_box_size - height;
@ -619,6 +615,18 @@ void render_game(game_t const *g, bool show_hitboxes)
dprint(x2+14, 148, C_WHITE, "DEF");
print_stat(x2+54, 148, ffloor(stats_equip.DEF), player_f->DEF);
dfont(old_font);
/* What the skills would be if the selected item is equipped */
int switched_skills[6] = { -1, -1, -1, -1, -1, -1 };
player_compute_skills(g->player, switched_equipment, switched_skills);
for(int i = 0; i < 5; i++) {
int s1 = player_f->skills[i+1], s2 = switched_skills[i+1];
if(s1 == s2) continue;
int x = 31 + 48*i + 64*(i>=3);
int y = HUD_Y - 33;
skill_render(x+2, y+2, s2, 1, RGB24(0x3868a8));
}
}
prof_leave(ctx);

View File

@ -31,7 +31,12 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
return;
f->actions_cooldown[slot] = skill_cooldown(skill);
if(skill == AOE_PROJECTILE) {
if(skill == SKILL_DASH) {
mechanical_dash(e, frdir(dir));
}
else if(skill == AOE_PROJECTILE) {
if(f->current_attack)
return;
entity_t *aoe = aoe_make_attack(AOE_PROJECTILE, e, dir);
game_add_entity(game, aoe);
@ -41,6 +46,8 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
visible_set_anim(e, f->enemy->id->anim_attack, 2);
}
else if(skill == AOE_SHOCK) {
if(f->current_attack)
return;
entity_t *aoe = aoe_make_attack(AOE_SHOCK, e, dir);
game_add_entity(game, aoe);
@ -55,6 +62,8 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
game_shake(game, 5, fix(0.7));
}
else if(skill == AOE_JUDGEMENT) {
if(f->current_attack)
return;
entity_t *aoe = aoe_make_attack(AOE_JUDGEMENT, e, dir);
game_add_entity(game, aoe);
@ -66,6 +75,8 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
game_shake(game, 3, fix(1.3));
}
else if(skill == AOE_BULLET) {
if(f->current_attack)
return;
entity_t *aoe = aoe_make_attack(AOE_BULLET, e, dir);
game_add_entity(game, aoe);
@ -75,3 +86,31 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
visible_set_anim(e, f->enemy->id->anim_attack, 2);
}
}
void skill_render(int x, int y, int skill, int bg, int color)
{
extern bopti_image_t img_skillicons;
int n = -1;
if(skill == SKILL_DASH)
n = 0;
if(skill == AOE_SHOCK)
n = 1;
if(skill == AOE_JUDGEMENT)
n = 2;
if(skill == AOE_BULLET)
n = 3;
if(bg > 0)
dsubimage(x, y, &img_skillicons, 23*(bg-1), 0, 23, 23, DIMAGE_NONE);
if(n >= 0)
dsubimage(x, y, &img_skillicons, 23*(n+2), 0, 23, 23, DIMAGE_NONE);
/* Hacky color change */
for(int dy = 0; dy < 23; dy++)
for(int dx = 0; dx < 23; dx++) {
int i = (y+dy) * DWIDTH + (x+dx);
if(gint_vram[i] == 0xffff)
gint_vram[i] = color;
}
}

View File

@ -22,3 +22,6 @@ fixed_t skill_cooldown(int skill);
/* Have [e] use its skill in the specified slot. */
void skill_use(game_t *g, entity_t *e, int slot, vec2 dir);
/* Render a skill's image */
void skill_render(int x, int y, int skill, int bg, int color);