add ranged magic attack with auto-aim on scepter in lv2

This commit is contained in:
Lephenixnoir 2022-06-25 17:04:07 +01:00
parent 1b75c9353d
commit a84592b280
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
11 changed files with 134 additions and 24 deletions

View File

@ -6,4 +6,6 @@ player_spawn: 5,1
spawner: 5,4
spawner: 18,5
wave: 10s 4*slime/1
item: scepter1
wave: 20s 40*slime/1

View File

@ -53,7 +53,7 @@ entity_t *aoe_make_attack(uint16_t type, entity_t *origin, vec2 dir)
anim = &anims_skill_hit;
hitbox = (rect){ -fix(4)/16, fix(3)/16, -fix(4)/16, fix(3)/16 };
}
else if(type == AOE_PROJECTILE) {
else if(type == AOE_PROJECTILE || type == AOE_PROJECTILE_FAST) {
anim = &anims_skill_projectile;
hitbox = (rect){ -fix(1)/16, fix(1)/16, -fix(1)/16, fix(1)/16 };
distance = fix(0.5);
@ -121,10 +121,13 @@ entity_t *aoe_make_attack(uint16_t type, entity_t *origin, vec2 dir)
aoe->data.generic.strength = f->MAG;
aoe->data.generic.dir = p->facing;
}
else if(type == AOE_PROJECTILE) {
else if(type == AOE_PROJECTILE || type == AOE_PROJECTILE_FAST) {
aoe->data.projectile.strength = f->ATK;
aoe->data.projectile.direction = dir;
aoe->data.projectile.speed = fix(3.0);
if(type == AOE_PROJECTILE_FAST)
aoe->data.projectile.speed = fix(6.0);
else
aoe->data.projectile.speed = fix(3.0);
p->facing = (dir.x >= 0 ? RIGHT : LEFT);
v->z = fix(0.5);
v->shadow_size = 3;
@ -207,7 +210,7 @@ static bool attack_apply(game_t *game, aoe_t *aoe, entity_t *target)
dir = fdir(aoe->data.generic.dir);
damage = aoe->data.generic.strength;
}
else if(aoe->type == AOE_PROJECTILE) {
else if(aoe->type == AOE_PROJECTILE || aoe->type == AOE_PROJECTILE_FAST) {
dir = aoe->data.projectile.direction;
damage = aoe->data.projectile.strength;
r /= 2;
@ -281,6 +284,7 @@ void aoe_apply(game_t *game, entity_t *entity, entity_t *e)
if(aoe->type == AOE_HIT
|| aoe->type == AOE_PROJECTILE
|| aoe->type == AOE_PROJECTILE_FAST
|| aoe->type == AOE_SLASH
|| aoe->type == AOE_IMPALE
|| aoe->type == AOE_SHOCK
@ -318,7 +322,7 @@ void aoe_update(game_t *game, entity_t *entity, fixed_t dt)
physical_t *p = getcomp(entity, physical);
aoe_t *aoe = getcomp(entity, aoe);
if(aoe->type == AOE_PROJECTILE) {
if(aoe->type == AOE_PROJECTILE || aoe->type == AOE_PROJECTILE_FAST) {
fixed_t v = aoe->data.projectile.speed;
p->x += fmul(fmul(aoe->data.projectile.direction.x, v), dt);
p->y += fmul(fmul(aoe->data.projectile.direction.y, v), dt);

View File

@ -15,6 +15,7 @@ enum {
AOE_HIT,
/* Simple ranged attack */
AOE_PROJECTILE,
AOE_PROJECTILE_FAST,
/* Normal attack by a slashing weapon */
AOE_SLASH,
/* Impaling attack using a slashing weapon */
@ -58,7 +59,7 @@ typedef struct
struct { int type; } item;
/* Generic attacks: source and ATK strength */
struct { int strength; int dir; } generic;
/* AOE_PROJECTILE: speed and direction of movement */
/* AOE_PROJECTILE, AOE_PROJECTILE_FAST: speed and direction */
struct { int strength; vec2 direction; fixed_t speed; } projectile;
/* AOE_SHOCK: origin and ATK strength */
struct { int strength; vec2 origin; } shock;

View File

@ -30,6 +30,23 @@ vec2 vec_i2f_center(ivec2 p)
return (vec2){ fix(p.x) + fix(0.5), fix(p.y) + fix(0.5) };
}
fixed_t vec_dot(vec2 v, vec2 u)
{
return fmul(v.x, u.x) + fmul(v.y, u.y);
}
vec2 vec_rotate_30(vec2 v)
{
return (vec2){ fmul(fix(0.866), v.x) - v.y / 2,
v.x / 2 + fmul(fix(0.866), v.y) };
}
vec2 vec_rotate_m30(vec2 v)
{
return (vec2){ fmul(fix(0.866), v.x) + v.y / 2,
-v.x / 2 + fmul(fix(0.866), v.y) };
}
//---
// Rect operations
//---

View File

@ -55,6 +55,14 @@ vec2 vec_i2f_center(ivec2);
ivec2 vec_f2i(vec2);
irect rect_f2i(rect);
/* Dot product. */
fixed_t vec_dot(vec2, vec2);
/* Rotate a vector by 30° */
vec2 vec_rotate_30(vec2);
/* Rotate a vector by -30° */
vec2 vec_rotate_m30(vec2);
//---
// Point operations
//---

View File

@ -204,11 +204,11 @@ void item_skills(int item, int *skill1, int *skill2, int *skill3)
*skill2 = AOE_SHOCK;
}
if(item == ITEM_SCEPTER1) {
*skill1 = AOE_SLASH; // TODO: Base ranged attack
*skill1 = AOE_PROJECTILE_FAST;
*skill2 = AOE_JUDGEMENT;
}
if(item == ITEM_SCEPTER2) {
*skill1 = AOE_SLASH;
*skill1 = AOE_PROJECTILE_FAST;
*skill2 = AOE_JUDGEMENT;
*skill3 = AOE_BULLET;
}

View File

@ -126,6 +126,7 @@ int main(void)
player_f->player = &player_data;
for(int i = 0; i < 6; i++)
player_f->skills[i] = -1;
player_f->skills[0] = AOE_SLASH;
for(int i = 0; i < 6; i++)
player_f->actions_cooldown[i] = fix(0.0);
/* Initialize stats. This will level up to level 1 */
@ -368,6 +369,11 @@ int main(void)
player_f->skills[2] = AOE_JUDGEMENT;
player_f->skills[3] = SKILL_DASH;
player_f->skills[4] = AOE_BULLET;
player_data.inventory[4] = ITEM_SWORD1;
player_data.inventory[5] = ITEM_SWORD2;
player_data.inventory[6] = ITEM_SCEPTER1;
player_data.inventory[7] = ITEM_SCEPTER2;
}
if(ev.key == KEY_F5 && debug.dev_menu)
debug.show_perf ^= 1;
@ -553,23 +559,32 @@ int main(void)
/* Player attack */
if(!debug.paused && !game.menu_open && player_f->HP > 0 && attack
&& !player_f->current_attack) {
int hit_number=0, effect=AOE_SLASH;
if(player_f->skills[0] == AOE_SLASH) {
int hit_number=0, effect=AOE_SLASH;
/* If hitting within .25s of the previous hit ending, combo! */
if(abs(player_f->combo_delay) < fix(0.25))
hit_number = player_f->combo_next;
player_f->combo_next = (hit_number + 1) % player_f->combo_length;
/* If hitting within .25s of the previous hit ending, combo! */
if(abs(player_f->combo_delay) < fix(0.25))
hit_number = player_f->combo_next;
player_f->combo_next = (hit_number+1) % player_f->combo_length;
if(hit_number == 0) effect = AOE_SLASH;
if(hit_number == 1) effect = AOE_IMPALE;
entity_t *aoe = aoe_make_attack_4(effect, player,player_p->facing);
getcomp(aoe, visible)->z = fix(0.25);
game_add_entity(&game, aoe);
if(hit_number == 0) effect = AOE_SLASH;
if(hit_number == 1) effect = AOE_IMPALE;
entity_t *aoe = aoe_make_attack_4(effect, player,
player_p->facing);
getcomp(aoe, visible)->z = fix(0.25);
game_add_entity(&game, aoe);
visible_set_anim(player, &anims_player_Attack, 2);
player_f->current_attack = aoe;
player_f->attack_follows_movement = true;
player_f->combo_delay = getcomp(aoe, aoe)->lifetime;
visible_set_anim(player, &anims_player_Attack, 2);
player_f->current_attack = aoe;
player_f->attack_follows_movement = true;
player_f->combo_delay = getcomp(aoe, aoe)->lifetime;
}
else if(player_f->skills[0] == AOE_PROJECTILE_FAST) {
vec2 dir = pathfinding_autoaim(&game, player, game.map);
if(dir.x == 0 && dir.y == 0)
dir = fdir(player_p->facing);
skill_use(&game, player, 0, dir);
}
}
/* Ideas for additional skills:

View File

@ -1,5 +1,8 @@
#include "pathfinding.h"
#include "game.h"
#include "util.h"
#include "comp/physical.h"
#include "comp/fighter.h"
#include <stdlib.h>
#include <string.h>
@ -316,3 +319,43 @@ vec2 pfc_shortcut_one(pfg_path_t const *grid, vec2 start, vec2 end,
return target;
}
//---
// Auto-aiming
//---
vec2 pathfinding_autoaim(game_t const *game, entity_t *src, map_t const *map)
{
physical_t *src_p = getcomp(src, physical);
if(!src_p)
return (vec2){ 0, 0 };
vec2 src_pos = { src_p->x, src_p->y };
vec2 src_facing = fdir(src_p->facing);
vec2 current_best_dir = { 0, 0 };
fixed_t best_dist2 = -1;
for(int i = 0; i < game->entity_count; i++) {
entity_t *e = game->entities[i];
physical_t *p = getcomp(e, physical);
fighter_t *f = getcomp(e, fighter);
if(!p || !f || !f->enemy)
continue;
vec2 relpos = { p->x - src_pos.x, p->y - src_pos.y };
if(vec_dot(relpos, vec_rotate_30(src_facing)) < 0)
continue;
if(vec_dot(relpos, vec_rotate_m30(src_facing)) < 0)
continue;
fixed_t dist2 = fmul(relpos.x, relpos.x) + fmul(relpos.y, relpos.y);
if(dist2 < best_dist2 || best_dist2 < 0) {
/* TODO: Prune based on raycasts */
best_dist2 = dist2;
current_best_dir = fnormalize(relpos);
}
}
return current_best_dir;
}

View File

@ -6,6 +6,7 @@
#include "map.h"
#include "geometry.h"
#include "comp/entity.h"
//---
// Pathfinding on the grid (pfg)
@ -136,6 +137,9 @@ vec2 pfc_shortcut_one(pfg_path_t const *path, vec2 start,
vec2 end, rect hitbox);
//---
// Auto-aiming
//---
vec2 *pathfind(map_t const *map, vec2 start, vec2 end, int *count,
ivec2 **p_grid, int *p_grid_length, rect hitbox);
struct game;
vec2 pathfinding_autoaim(struct game const *game, entity_t *origin,
map_t const *map);

View File

@ -14,6 +14,8 @@ fixed_t skill_cooldown(int skill)
return fix(5.0);
else if(skill == AOE_PROJECTILE)
return fix(2.0);
else if(skill == AOE_PROJECTILE_FAST)
return fix(0.18);
else if(skill == AOE_SHOCK)
return fix(6.0);
else if(skill == AOE_JUDGEMENT)
@ -52,6 +54,19 @@ bool skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
else
visible_set_anim(e, f->enemy->id->anim_attack, 2);
}
else if(skill == AOE_PROJECTILE_FAST) {
if(f->current_attack)
return false;
entity_t *aoe = aoe_make_attack(AOE_PROJECTILE_FAST, e, dir);
getcomp(aoe, visible)->z = fix(0.25);
game_add_entity(game, aoe);
if(!f->enemy)
visible_set_anim(e, &anims_player_Attack, 2);
else
visible_set_anim(e, f->enemy->id->anim_attack, 2);
}
else if(skill == AOE_SHOCK) {
if(f->current_attack)
return false;

View File

@ -13,6 +13,7 @@ enum {
/* Other skills in the AOE enumeration that are valid here: */
// AOE_PROJECTILE,
// AOE_PROJECTILE_FAST,
// AOE_SHOCK,
// AOE_JUDGEMENT,
// AOE_BULLET,