diff --git a/assets-cg/levels/lv2.txt b/assets-cg/levels/lv2.txt index b50fbc1..66d2efe 100644 --- a/assets-cg/levels/lv2.txt +++ b/assets-cg/levels/lv2.txt @@ -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 diff --git a/src/aoe.c b/src/aoe.c index 7db0f5b..aef76e9 100644 --- a/src/aoe.c +++ b/src/aoe.c @@ -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); diff --git a/src/aoe.h b/src/aoe.h index 4da0a47..61a0d70 100644 --- a/src/aoe.h +++ b/src/aoe.h @@ -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; diff --git a/src/geometry.c b/src/geometry.c index 730ec02..2578e32 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -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 //--- diff --git a/src/geometry.h b/src/geometry.h index 6f80b03..16a93b5 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -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 //--- diff --git a/src/item.c b/src/item.c index 25c0caf..48f92fb 100644 --- a/src/item.c +++ b/src/item.c @@ -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; } diff --git a/src/main.c b/src/main.c index 86c8fe2..3b7ac1d 100644 --- a/src/main.c +++ b/src/main.c @@ -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: diff --git a/src/pathfinding.c b/src/pathfinding.c index d348c33..10bc953 100644 --- a/src/pathfinding.c +++ b/src/pathfinding.c @@ -1,5 +1,8 @@ #include "pathfinding.h" +#include "game.h" #include "util.h" +#include "comp/physical.h" +#include "comp/fighter.h" #include #include @@ -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; +} diff --git a/src/pathfinding.h b/src/pathfinding.h index 0c9522b..8195437 100644 --- a/src/pathfinding.h +++ b/src/pathfinding.h @@ -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); diff --git a/src/skills.c b/src/skills.c index 8872166..8c629b1 100644 --- a/src/skills.c +++ b/src/skills.c @@ -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; diff --git a/src/skills.h b/src/skills.h index 80e540b..ca49c13 100644 --- a/src/skills.h +++ b/src/skills.h @@ -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,