diff --git a/CMakeLists.txt b/CMakeLists.txt index 3872364..bc1dbd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/assets-cg/converters.py b/assets-cg/converters.py index 8f5a425..86eb58b 100644 --- a/assets-cg/converters.py +++ b/assets-cg/converters.py @@ -47,6 +47,7 @@ ITEM_IDS = { "scepter2": 102, "sword1": 103, "sword2": 104, + "armor1": 105, } def convert_level(input, params): diff --git a/assets-cg/items/armor1.aseprite b/assets-cg/items/armor1.aseprite new file mode 100644 index 0000000..5848a4c Binary files /dev/null and b/assets-cg/items/armor1.aseprite differ diff --git a/assets-cg/levels/lv1.txt b/assets-cg/levels/lv1.txt index 43e9ac5..89e4485 100644 --- a/assets-cg/levels/lv1.txt +++ b/assets-cg/levels/lv1.txt @@ -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 diff --git a/src/anim.c b/src/anim.c index 8220a8b..a099e14 100644 --- a/src/anim.c +++ b/src/anim.c @@ -87,6 +87,7 @@ ANIM1(item_stick1); ANIM1(item_stick2); ANIM1(item_sword1); ANIM1(item_sword2); +ANIM1(item_armor1); /* Animation functions. */ diff --git a/src/anim.h b/src/anim.h index 2dbf8b4..89847f7 100644 --- a/src/anim.h +++ b/src/anim.h @@ -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; diff --git a/src/item.c b/src/item.c index 9137693..761ae2b 100644 --- a/src/item.c +++ b/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; + } +} diff --git a/src/item.h b/src/item.h index bf925fe..2216dc5 100644 --- a/src/item.h +++ b/src/item.h @@ -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); diff --git a/src/main.c b/src/main.c index 34f7c4d..50fca21 100644 --- a/src/main.c +++ b/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 diff --git a/src/player.c b/src/player.c index ca46d48..41a5af8 100644 --- a/src/player.c +++ b/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; diff --git a/src/player.h b/src/player.h index df7bdd7..4f05d99 100644 --- a/src/player.h +++ b/src/player.h @@ -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); diff --git a/src/render.c b/src/render.c index f6560fa..7fd520a 100644 --- a/src/render.c +++ b/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); diff --git a/src/skills.c b/src/skills.c index 9cbefef..5248edd 100644 --- a/src/skills.c +++ b/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; + } +} diff --git a/src/skills.h b/src/skills.h index a1b73e1..8ee94e0 100644 --- a/src/skills.h +++ b/src/skills.h @@ -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);