preview and apply skill changes based on equipment
This commit is contained in:
parent
e0c46db0d7
commit
b844a1d434
|
@ -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
|
||||
|
|
|
@ -47,6 +47,7 @@ ITEM_IDS = {
|
|||
"scepter2": 102,
|
||||
"sword1": 103,
|
||||
"sword2": 104,
|
||||
"armor1": 105,
|
||||
}
|
||||
|
||||
def convert_level(input, params):
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -87,6 +87,7 @@ ANIM1(item_stick1);
|
|||
ANIM1(item_stick2);
|
||||
ANIM1(item_sword1);
|
||||
ANIM1(item_sword2);
|
||||
ANIM1(item_armor1);
|
||||
|
||||
/* Animation functions. */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
31
src/item.c
31
src/item.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
60
src/main.c
60
src/main.c
|
@ -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
|
||||
|
|
18
src/player.c
18
src/player.c
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
24
src/render.c
24
src/render.c
|
@ -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);
|
||||
|
|
41
src/skills.c
41
src/skills.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue