basic entity damage generalization
This commit is contained in:
parent
ffcb2aff9e
commit
e9ef77abee
20 changed files with 204 additions and 117 deletions
19
src/boss.c
19
src/boss.c
|
@ -15,6 +15,7 @@
|
|||
#include "entity.h"
|
||||
|
||||
static void ent_draw_boss(EntityInterface *ent);
|
||||
static DamageResult ent_damage_boss(EntityInterface *ent, const DamageInfo *dmg);
|
||||
|
||||
Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
|
||||
Boss *buf = calloc(1, sizeof(Boss));
|
||||
|
@ -35,6 +36,7 @@ Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
|
|||
|
||||
buf->ent.draw_layer = LAYER_BOSS;
|
||||
buf->ent.draw_func = ent_draw_boss;
|
||||
buf->ent.damage_func = ent_damage_boss;
|
||||
ent_register(&buf->ent, ENT_BOSS);
|
||||
|
||||
return buf;
|
||||
|
@ -456,20 +458,25 @@ bool boss_is_vulnerable(Boss *boss) {
|
|||
return boss->current && boss->current->type != AT_Move && boss->current->type != AT_SurvivalSpell && boss->current->starttime < global.frames && !boss->current->finished;
|
||||
}
|
||||
|
||||
bool boss_damage(Boss *boss, int dmg) {
|
||||
if(!boss_is_vulnerable(boss))
|
||||
return false;
|
||||
static DamageResult ent_damage_boss(EntityInterface *ent, const DamageInfo *dmg) {
|
||||
Boss *boss = ENT_CAST(ent, Boss);
|
||||
|
||||
if(dmg > 0 && global.frames-boss->lastdamageframe > 2) {
|
||||
if(!boss_is_vulnerable(boss) || dmg->type == DMG_ENEMY_SHOT || dmg->type == DMG_ENEMY_COLLISION) {
|
||||
return DMG_RESULT_IMMUNE;
|
||||
}
|
||||
|
||||
if(dmg->amount > 0 && global.frames-boss->lastdamageframe > 2) {
|
||||
boss->lastdamageframe = global.frames;
|
||||
}
|
||||
|
||||
boss->current->hp -= dmg;
|
||||
if(boss->current->hp < boss->current->maxhp*0.1) {
|
||||
boss->current->hp -= dmg->amount;
|
||||
|
||||
if(boss->current->hp < boss->current->maxhp * 0.1) {
|
||||
play_loop("hit1");
|
||||
} else {
|
||||
play_loop("hit0");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
12
src/boss.h
12
src/boss.h
|
@ -67,7 +67,7 @@ typedef struct AttackInfo {
|
|||
AttackType type;
|
||||
char *name;
|
||||
float timeout;
|
||||
int hp;
|
||||
float hp;
|
||||
|
||||
BossRule rule;
|
||||
BossRule draw_rule;
|
||||
|
@ -81,14 +81,12 @@ typedef struct Attack {
|
|||
AttackType type;
|
||||
|
||||
int starttime;
|
||||
|
||||
int timeout;
|
||||
|
||||
int maxhp;
|
||||
int hp;
|
||||
|
||||
int endtime;
|
||||
|
||||
float maxhp;
|
||||
float hp;
|
||||
|
||||
bool finished;
|
||||
int failtime;
|
||||
double scorevalue;
|
||||
|
@ -143,8 +141,6 @@ bool boss_is_dying(Boss *boss) attr_nonnull(1); // true if the last attack is ov
|
|||
bool boss_is_fleeing(Boss *boss) attr_nonnull(1);
|
||||
bool boss_is_vulnerable(Boss *boss) attr_nonnull(1);
|
||||
|
||||
bool boss_damage(Boss *boss, int dmg) attr_nonnull(1); // subtract dmg hitpoints from hp returns true on success
|
||||
|
||||
void boss_death(Boss **boss) attr_nonnull(1);
|
||||
void boss_kill_projectiles(void);
|
||||
|
||||
|
|
21
src/enemy.c
21
src/enemy.c
|
@ -32,6 +32,7 @@ Enemy* _enemy_attach_dbginfo(Enemy *e, DebugInfo *dbg) {
|
|||
#endif
|
||||
|
||||
static void ent_draw_enemy(EntityInterface *ent);
|
||||
static DamageResult ent_damage_enemy(EntityInterface *ienemy, const DamageInfo *dmg);
|
||||
|
||||
static void fix_pos0_visual(Enemy *e) {
|
||||
double x = creal(e->pos0_visual);
|
||||
|
@ -57,7 +58,7 @@ static void fix_pos0_visual(Enemy *e) {
|
|||
e->pos0_visual = x + y * I;
|
||||
}
|
||||
|
||||
Enemy *create_enemy_p(EnemyList *enemies, complex pos, int hp, EnemyVisualRule visual_rule, EnemyLogicRule logic_rule,
|
||||
Enemy *create_enemy_p(EnemyList *enemies, complex pos, float hp, EnemyVisualRule visual_rule, EnemyLogicRule logic_rule,
|
||||
complex a1, complex a2, complex a3, complex a4) {
|
||||
if(IN_DRAW_CODE) {
|
||||
log_fatal("Tried to spawn an enemy while in drawing code");
|
||||
|
@ -87,6 +88,7 @@ Enemy *create_enemy_p(EnemyList *enemies, complex pos, int hp, EnemyVisualRule v
|
|||
|
||||
e->ent.draw_layer = LAYER_ENEMY;
|
||||
e->ent.draw_func = ent_draw_enemy;
|
||||
e->ent.damage_func = ent_damage_enemy;
|
||||
|
||||
fix_pos0_visual(e);
|
||||
ent_register(&e->ent, ENT_ENEMY);
|
||||
|
@ -311,25 +313,26 @@ void enemy_kill_all(EnemyList *enemies) {
|
|||
}
|
||||
}
|
||||
|
||||
bool enemy_damage(Enemy *enemy, int damage) {
|
||||
if(!enemy_is_vulnerable(enemy)) {
|
||||
return false;
|
||||
static DamageResult ent_damage_enemy(EntityInterface *ienemy, const DamageInfo *dmg) {
|
||||
Enemy *enemy = ENT_CAST(ienemy, Enemy);
|
||||
|
||||
if(!enemy_is_vulnerable(enemy) || dmg->type == DMG_ENEMY_SHOT || dmg->type == DMG_ENEMY_COLLISION) {
|
||||
return DMG_RESULT_IMMUNE;
|
||||
}
|
||||
|
||||
enemy->hp -= damage;
|
||||
enemy->hp -= dmg->amount;
|
||||
|
||||
if(enemy->hp <= 0) {
|
||||
enemy->hp = ENEMY_KILLED;
|
||||
}
|
||||
|
||||
if(enemy->hp < enemy->spawn_hp*0.1) {
|
||||
if(enemy->hp < enemy->spawn_hp * 0.1) {
|
||||
play_loop("hit1");
|
||||
} else {
|
||||
play_loop("hit0");
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
return DMG_RESULT_OK;
|
||||
}
|
||||
|
||||
void process_enemies(EnemyList *enemies) {
|
||||
|
@ -345,7 +348,7 @@ void process_enemies(EnemyList *enemies) {
|
|||
int action = enemy->logic_rule(enemy, global.frames - enemy->birthtime);
|
||||
|
||||
if(enemy->hp > ENEMY_IMMUNE && enemy->alpha >= 1.0 && cabs(enemy->pos - global.plr.pos) < 7) {
|
||||
player_death(&global.plr);
|
||||
ent_damage(&global.plr.ent, &(DamageInfo) { .type = DMG_ENEMY_COLLISION });
|
||||
}
|
||||
|
||||
enemy->alpha = approach(enemy->alpha, 1.0, 1.0/60.0);
|
||||
|
|
|
@ -44,8 +44,8 @@ struct Enemy {
|
|||
EnemyLogicRule logic_rule;
|
||||
EnemyVisualRule visual_rule;
|
||||
|
||||
int spawn_hp;
|
||||
int hp;
|
||||
float spawn_hp;
|
||||
float hp;
|
||||
|
||||
complex args[RULE_ARGC];
|
||||
float alpha;
|
||||
|
@ -61,7 +61,7 @@ struct Enemy {
|
|||
#define create_enemy1c(p,h,d,l,a1) create_enemy_p(&global.enemies,p,h,d,l,a1,0,0,0)
|
||||
|
||||
Enemy *create_enemy_p(
|
||||
EnemyList *enemies, complex pos, int hp, EnemyVisualRule draw_rule, EnemyLogicRule logic_rule,
|
||||
EnemyList *enemies, complex pos, float hp, EnemyVisualRule draw_rule, EnemyLogicRule logic_rule,
|
||||
complex a1, complex a2, complex a3, complex a4
|
||||
);
|
||||
|
||||
|
@ -78,7 +78,6 @@ void process_enemies(EnemyList *enemies);
|
|||
bool enemy_is_vulnerable(Enemy *enemy);
|
||||
bool enemy_in_viewport(Enemy *enemy);
|
||||
void enemy_kill_all(EnemyList *enemies);
|
||||
bool enemy_damage(Enemy *enemy, int damage);
|
||||
|
||||
void Fairy(Enemy*, int t, bool render);
|
||||
void Swirl(Enemy*, int t, bool render);
|
||||
|
|
21
src/entity.c
21
src/entity.c
|
@ -11,6 +11,7 @@
|
|||
#include "entity.h"
|
||||
#include "util.h"
|
||||
#include "renderer/api.h"
|
||||
#include "global.h"
|
||||
|
||||
static struct {
|
||||
EntityInterface **array;
|
||||
|
@ -111,3 +112,23 @@ void ent_draw(EntityPredicate predicate) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
DamageResult ent_damage(EntityInterface *ent, const DamageInfo *damage) {
|
||||
if(ent->damage_func == NULL) {
|
||||
return DMG_RESULT_INAPPLICABLE;
|
||||
}
|
||||
|
||||
return ent->damage_func(ent, damage);
|
||||
}
|
||||
|
||||
void ent_area_damage(complex origin, float radius, const DamageInfo *damage) {
|
||||
for(Enemy *e = global.enemies.first; e; e = e->next) {
|
||||
if(cabs(origin - e->pos) < radius) {
|
||||
ent_damage(&e->ent, damage);
|
||||
}
|
||||
}
|
||||
|
||||
if(global.boss && cabs(origin - global.boss->pos) < radius) {
|
||||
ent_damage(&global.boss->ent, damage);
|
||||
}
|
||||
}
|
||||
|
|
28
src/entity.h
28
src/entity.h
|
@ -48,13 +48,35 @@ typedef enum EntityType {
|
|||
|
||||
typedef struct EntityInterface EntityInterface;
|
||||
typedef struct EntityListNode EntityListNode;
|
||||
|
||||
typedef enum DamageType {
|
||||
DMG_UNDEFINED,
|
||||
DMG_ENEMY_SHOT,
|
||||
DMG_ENEMY_COLLISION,
|
||||
DMG_PLAYER_SHOT,
|
||||
DMG_PLAYER_BOMB,
|
||||
} DamageType;
|
||||
|
||||
typedef enum DamageResult {
|
||||
DMG_RESULT_OK,
|
||||
DMG_RESULT_IMMUNE,
|
||||
DMG_RESULT_INAPPLICABLE,
|
||||
} DamageResult;
|
||||
|
||||
typedef struct DamageInfo {
|
||||
float amount;
|
||||
DamageType type;
|
||||
} DamageInfo;
|
||||
|
||||
typedef void (*EntityDrawFunc)(EntityInterface *ent);
|
||||
typedef bool (*EntityPredicate)(EntityInterface *ent);
|
||||
typedef DamageResult (*EntityDamageFunc)(EntityInterface *target, const DamageInfo *damage);
|
||||
|
||||
#define ENTITY_INTERFACE_BASE(typename) struct { \
|
||||
OBJECT_INTERFACE(typename); \
|
||||
EntityType type; \
|
||||
EntityDrawFunc draw_func; \
|
||||
EntityDamageFunc damage_func; \
|
||||
drawlayer_t draw_layer; \
|
||||
uint32_t spawn_id; \
|
||||
uint index; \
|
||||
|
@ -107,6 +129,8 @@ static inline attr_must_inline const char* ent_type_name(EntityType type) {
|
|||
|
||||
void ent_init(void);
|
||||
void ent_shutdown(void);
|
||||
void ent_register(EntityInterface *ent, EntityType type);
|
||||
void ent_unregister(EntityInterface *ent);
|
||||
void ent_register(EntityInterface *ent, EntityType type) attr_nonnull(1);
|
||||
void ent_unregister(EntityInterface *ent) attr_nonnull(1);
|
||||
void ent_draw(EntityPredicate predicate);
|
||||
DamageResult ent_damage(EntityInterface *ent, const DamageInfo *damage) attr_nonnull(1, 2);
|
||||
void ent_area_damage(complex origin, float radius, const DamageInfo *damage) attr_nonnull(3);
|
||||
|
|
|
@ -277,7 +277,7 @@ void process_lasers(void) {
|
|||
}
|
||||
} else {
|
||||
if(collision_laser_curve(laser)) {
|
||||
player_death(&global.plr);
|
||||
ent_damage(&global.plr.ent, &(DamageInfo) { .type = DMG_ENEMY_SHOT });
|
||||
}
|
||||
|
||||
if(laser->lrule) {
|
||||
|
|
51
src/player.c
51
src/player.c
|
@ -42,6 +42,8 @@ double player_property(Player *plr, PlrProperty prop) {
|
|||
}
|
||||
|
||||
static void ent_draw_player(EntityInterface *ent);
|
||||
static DamageResult ent_damage_player(EntityInterface *ent, const DamageInfo *dmg);
|
||||
|
||||
static void player_spawn_focus_circle(Player *plr);
|
||||
|
||||
void player_stage_post_init(Player *plr) {
|
||||
|
@ -64,6 +66,7 @@ void player_stage_post_init(Player *plr) {
|
|||
|
||||
plr->ent.draw_layer = LAYER_PLAYER;
|
||||
plr->ent.draw_func = ent_draw_player;
|
||||
plr->ent.damage_func = ent_damage_player;
|
||||
ent_register(&plr->ent, ENT_PLAYER);
|
||||
|
||||
player_spawn_focus_circle(plr);
|
||||
|
@ -276,14 +279,16 @@ void player_logic(Player* plr) {
|
|||
}
|
||||
}
|
||||
|
||||
const int damage = 100;
|
||||
DamageInfo dmg;
|
||||
dmg.amount = 100;
|
||||
dmg.type = DMG_PLAYER_BOMB;
|
||||
|
||||
for(Enemy *en = global.enemies.first; en; en = en->next) {
|
||||
enemy_damage(en, damage);
|
||||
ent_damage(&en->ent, &dmg);
|
||||
}
|
||||
|
||||
if(global.boss) {
|
||||
boss_damage(global.boss, damage);
|
||||
ent_damage(&global.boss->ent, &dmg);
|
||||
}
|
||||
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL);
|
||||
|
@ -534,6 +539,21 @@ void player_death(Player *plr) {
|
|||
}
|
||||
}
|
||||
|
||||
static DamageResult ent_damage_player(EntityInterface *ent, const DamageInfo *dmg) {
|
||||
Player *plr = ENT_CAST(ent, Player);
|
||||
|
||||
if(
|
||||
(global.frames - abs(plr->recovery) < 0) ||
|
||||
plr->iddqd ||
|
||||
(dmg->type != DMG_ENEMY_SHOT && dmg->type != DMG_ENEMY_COLLISION)
|
||||
) {
|
||||
return DMG_RESULT_IMMUNE;
|
||||
}
|
||||
|
||||
player_death(plr);
|
||||
return DMG_RESULT_OK;
|
||||
}
|
||||
|
||||
static PlrInputFlag key_to_inflag(KeyIndex key) {
|
||||
switch(key) {
|
||||
case KEY_UP: return INFLAG_UP;
|
||||
|
@ -963,7 +983,28 @@ void player_add_points(Player *plr, uint points) {
|
|||
}
|
||||
}
|
||||
|
||||
void player_register_damage(Player *plr, int damage) {
|
||||
void player_register_damage(Player *plr, EntityInterface *target, const DamageInfo *damage) {
|
||||
if(damage->type != DMG_PLAYER_SHOT && damage->type != DMG_PLAYER_BOMB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(target != NULL) {
|
||||
switch(target->type) {
|
||||
case ENT_ENEMY: {
|
||||
player_add_points(&global.plr, damage->amount * 0.5);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENT_BOSS: {
|
||||
player_add_points(&global.plr, damage->amount * 0.2);
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef PLR_DPS_STATS
|
||||
while(global.frames > plr->dmglogframe) {
|
||||
memmove(plr->dmglog + 1, plr->dmglog, sizeof(plr->dmglog) - sizeof(*plr->dmglog));
|
||||
|
@ -971,7 +1012,7 @@ void player_register_damage(Player *plr, int damage) {
|
|||
plr->dmglogframe++;
|
||||
}
|
||||
|
||||
plr->dmglog[0] += damage;
|
||||
plr->dmglog[0] += damage->amount;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ void player_add_lives(Player *plr, int lives);
|
|||
void player_add_bombs(Player *plr, int bombs);
|
||||
void player_add_points(Player *plr, uint points);
|
||||
|
||||
void player_register_damage(Player *plr, int dmg);
|
||||
void player_register_damage(Player *plr, EntityInterface *target, const DamageInfo *damage);
|
||||
|
||||
void player_cancel_bomb(Player *plr, int delay);
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ double marisa_common_property(Player *plr, PlrProperty prop) {
|
|||
UNREACHABLE;
|
||||
}
|
||||
|
||||
void marisa_common_shot(Player *plr, int dmg) {
|
||||
void marisa_common_shot(Player *plr, float dmg) {
|
||||
play_loop("generic_shot");
|
||||
|
||||
if(!(global.frames % 6)) {
|
||||
|
@ -62,7 +62,8 @@ void marisa_common_shot(Player *plr, int dmg) {
|
|||
.color = c,
|
||||
.rule = linear,
|
||||
.args = { -20.0*I },
|
||||
.type = PlrProj+dmg,
|
||||
.type = PlrProj,
|
||||
.damage = dmg,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@
|
|||
extern PlayerCharacter character_marisa;
|
||||
|
||||
double marisa_common_property(Player *plr, PlrProperty prop);
|
||||
void marisa_common_shot(Player *plr, int dmg);
|
||||
void marisa_common_shot(Player *plr, float dmg);
|
||||
void marisa_common_slave_visual(Enemy *e, int t, bool render);
|
||||
void marisa_common_masterspark_draw(int t);
|
||||
|
|
|
@ -54,7 +54,7 @@ static void draw_laser_beam(complex src, complex dst, double size, double step,
|
|||
r_mat_pop();
|
||||
}
|
||||
|
||||
static void trace_laser(Enemy *e, complex vel, int damage) {
|
||||
static void trace_laser(Enemy *e, complex vel, float damage) {
|
||||
ProjCollisionResult col;
|
||||
ProjectileList lproj = { .first = NULL };
|
||||
|
||||
|
@ -64,14 +64,15 @@ static void trace_laser(Enemy *e, complex vel, int damage) {
|
|||
.dest = &lproj,
|
||||
.pos = e->pos,
|
||||
.size = 28*(1+I),
|
||||
.type = PlrProj + damage,
|
||||
.type = PlrProj,
|
||||
.damage = damage,
|
||||
.rule = linear,
|
||||
.args = { vel },
|
||||
);
|
||||
|
||||
bool first_found = false;
|
||||
int timeofs = 0;
|
||||
int col_types = (PCOL_ENEMY | PCOL_BOSS);
|
||||
int col_types = PCOL_ENTITY;
|
||||
|
||||
struct enemy_col {
|
||||
LIST_INTERFACE(struct enemy_col);
|
||||
|
@ -100,9 +101,9 @@ static void trace_laser(Enemy *e, complex vel, int damage) {
|
|||
.flags = PFLAG_NOREFLECT,
|
||||
);
|
||||
|
||||
if(col.type == PCOL_ENEMY) {
|
||||
if(col.type == PCOL_ENTITY && col.entity->type == ENT_ENEMY) {
|
||||
c = malloc(sizeof(struct enemy_col));
|
||||
c->enemy = col.entity;
|
||||
c->enemy = ENT_CAST(col.entity, Enemy);
|
||||
list_push(&prev_collisions, c);
|
||||
} else {
|
||||
col_types &= ~col.type;
|
||||
|
@ -113,10 +114,6 @@ static void trace_laser(Enemy *e, complex vel, int damage) {
|
|||
|
||||
apply_projectile_collision(&lproj, lproj.first, &col);
|
||||
|
||||
if(col.type == PCOL_BOSS) {
|
||||
assert(!col.fatal);
|
||||
}
|
||||
|
||||
if(c) {
|
||||
c->original_hp = ((Enemy*)col.entity)->hp;
|
||||
((Enemy*)col.entity)->hp = ENEMY_IMMUNE;
|
||||
|
|
|
@ -87,7 +87,8 @@ static int marisa_star_slave(Enemy *e, int t) {
|
|||
.rule = marisa_star_projectile,
|
||||
// .draw_rule = marisa_star,
|
||||
.args = { v, a },
|
||||
.type = PlrProj + e->args[3],
|
||||
.type = PlrProj,
|
||||
.damage = e->args[3],
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -60,7 +60,8 @@ void youmu_common_shot(Player *plr) {
|
|||
.color = c,
|
||||
.rule = linear,
|
||||
.args = { -20.0*I },
|
||||
.type = PlrProj+120,
|
||||
.type = PlrProj,
|
||||
.damage = 120,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
|
||||
|
@ -70,7 +71,8 @@ void youmu_common_shot(Player *plr) {
|
|||
.color = c,
|
||||
.rule = linear,
|
||||
.args = { -20.0*I },
|
||||
.type = PlrProj+120,
|
||||
.type = PlrProj,
|
||||
.damage = 120,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -173,7 +173,7 @@ static void myon_proj_draw(Projectile *p, int t) {
|
|||
youmu_common_draw_proj(p, &p->color, 1);
|
||||
}
|
||||
|
||||
static Projectile* youmu_mirror_myon_proj(char *tex, complex pos, double speed, double angle, double aoffs, double upfactor, int dmg) {
|
||||
static Projectile* youmu_mirror_myon_proj(char *tex, complex pos, double speed, double angle, double aoffs, double upfactor, float dmg) {
|
||||
complex dir = cexp(I*(M_PI/2 + aoffs)) * upfactor + cexp(I * (angle + aoffs)) * (1 - upfactor);
|
||||
dir = dir / cabs(dir);
|
||||
|
||||
|
@ -205,7 +205,8 @@ static Projectile* youmu_mirror_myon_proj(char *tex, complex pos, double speed,
|
|||
.rule = myon_proj,
|
||||
.args = { speed*dir },
|
||||
.draw_rule = myon_proj_draw,
|
||||
.type = PlrProj+dmg,
|
||||
.type = PlrProj,
|
||||
.damage = dmg,
|
||||
.shader = "sprite_youmu_myon_shot",
|
||||
);
|
||||
}
|
||||
|
@ -316,9 +317,10 @@ static int youmu_mirror_self_proj(Projectile *p, int t) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static Projectile* youmu_mirror_self_shot(Player *plr, complex ofs, complex vel, int dmg, double turntime) {
|
||||
static Projectile* youmu_mirror_self_shot(Player *plr, complex ofs, complex vel, float dmg, double turntime) {
|
||||
return PROJECTILE("youmu", plr->pos + ofs, 0,
|
||||
.type = PlrProj+dmg,
|
||||
.type = PlrProj,
|
||||
.damage = dmg,
|
||||
.shader = "sprite_default",
|
||||
.draw_rule = myon_proj_draw,
|
||||
.rule = youmu_mirror_self_proj,
|
||||
|
|
|
@ -203,7 +203,8 @@ static int youmu_trap(Projectile *p, int t) {
|
|||
|
||||
PROJECTILE("youmu", p->pos, RGBA(1, 1, 1, 0.85), youmu_homing,
|
||||
.args = { 5 * (1 + charge) * dir, (1 + charge) * aim, dur + charge*I, creal(p->pos) - VIEWPORT_H*I },
|
||||
.type = PlrProj + dmg,
|
||||
.type = PlrProj,
|
||||
.damage = dmg,
|
||||
.draw_rule = youmu_trap_draw_child_proj,
|
||||
.shader = "sprite_youmu_charged_shot",
|
||||
.shader_params = &(ShaderCustomParams){{ 0, 1 }},
|
||||
|
@ -357,7 +358,8 @@ static void youmu_haunting_power_shot(Player *plr, int p) {
|
|||
.color = RGB(0.7 + 0.3 * (1-np), 0.8 + 0.2 * sqrt(1-np), 1.0),
|
||||
.draw_rule = youmu_homing_draw_proj,
|
||||
.args = { speed * dir * (1 - 0.25 * (1 - np)), 3 * (1 - pow(1 - np, 2)), 60, },
|
||||
.type = PlrProj+30,
|
||||
.type = PlrProj,
|
||||
.damage = 30,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
}
|
||||
|
@ -386,7 +388,8 @@ static void youmu_haunting_shot(Player *plr) {
|
|||
if(!(global.frames % 6)) {
|
||||
PROJECTILE("hghost", plr->pos, RGB(0.75, 0.9, 1), youmu_homing,
|
||||
.args = { -10.0*I, 0.1 + 0.2*I, 60, VIEWPORT_W*0.5 },
|
||||
.type = PlrProj+120,
|
||||
.type = PlrProj,
|
||||
.damage = 120,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ static ProjArgs defaults_proj = {
|
|||
.draw_rule = ProjDraw,
|
||||
.dest = &global.projs,
|
||||
.type = EnemyProj,
|
||||
.damage_type = DMG_ENEMY_SHOT,
|
||||
.color = RGB(1, 1, 1),
|
||||
.blend = BLEND_PREMUL_ALPHA,
|
||||
.shader = "sprite_bullet",
|
||||
|
@ -31,6 +32,7 @@ static ProjArgs defaults_part = {
|
|||
.draw_rule = ProjDraw,
|
||||
.dest = &global.particles,
|
||||
.type = Particle,
|
||||
.damage_type = DMG_UNDEFINED,
|
||||
.color = RGB(1, 1, 1),
|
||||
.blend = BLEND_PREMUL_ALPHA,
|
||||
.shader = "sprite_default",
|
||||
|
@ -101,17 +103,27 @@ static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
|
|||
args->color = defaults->color;
|
||||
}
|
||||
|
||||
if(!args->max_viewport_dist && (args->type == Particle || args->type >= PlrProj)) {
|
||||
if(!args->max_viewport_dist && (args->type == Particle || args->type == PlrProj)) {
|
||||
args->max_viewport_dist = 300;
|
||||
}
|
||||
|
||||
if(!args->layer) {
|
||||
if(args->type >= PlrProj) {
|
||||
if(args->type == PlrProj) {
|
||||
args->layer = LAYER_PLAYER_SHOT;
|
||||
} else {
|
||||
args->layer = defaults->layer;
|
||||
}
|
||||
}
|
||||
|
||||
if(args->damage_type == DMG_UNDEFINED) {
|
||||
args->damage_type = defaults->damage_type;
|
||||
|
||||
if(args->type == PlrProj && args->damage_type == DMG_ENEMY_SHOT) {
|
||||
args->damage_type = DMG_PLAYER_SHOT;
|
||||
}
|
||||
}
|
||||
|
||||
assert(args->type <= PlrProj);
|
||||
}
|
||||
|
||||
static void projectile_size(Projectile *p, double *w, double *h) {
|
||||
|
@ -207,6 +219,8 @@ static Projectile* _create_projectile(ProjArgs *args) {
|
|||
p->collision_size = args->collision_size;
|
||||
p->flags = args->flags;
|
||||
p->timeout = args->timeout;
|
||||
p->damage = args->damage;
|
||||
p->damage_type = args->damage_type;
|
||||
|
||||
if(args->shader_params != NULL) {
|
||||
p->shader_params = *args->shader_params;
|
||||
|
@ -221,9 +235,7 @@ static Projectile* _create_projectile(ProjArgs *args) {
|
|||
// p->collision_size *= 10;
|
||||
// p->size *= 5;
|
||||
|
||||
if((p->type == EnemyProj || p->type >= PlrProj) && (creal(p->size) <= 0 || cimag(p->size) <= 0)) {
|
||||
// FIXME: debug info is not actually attached at this point!
|
||||
set_debug_info(&p->debug);
|
||||
if((p->type == EnemyProj || p->type == PlrProj) && (creal(p->size) <= 0 || cimag(p->size) <= 0)) {
|
||||
log_fatal("Tried to spawn a projectile with invalid size %f x %f", creal(p->size), cimag(p->size));
|
||||
}
|
||||
|
||||
|
@ -281,7 +293,8 @@ void calc_projectile_collision(Projectile *p, ProjCollisionResult *out_col) {
|
|||
out_col->entity = NULL;
|
||||
out_col->fatal = false;
|
||||
out_col->location = p->pos;
|
||||
out_col->damage = 0;
|
||||
out_col->damage.amount = p->damage;
|
||||
out_col->damage.type = p->damage_type;
|
||||
|
||||
if(p->type == EnemyProj) {
|
||||
Ellipse e_proj = {
|
||||
|
@ -310,27 +323,24 @@ void calc_projectile_collision(Projectile *p, ProjCollisionResult *out_col) {
|
|||
}
|
||||
|
||||
if(lineseg_ellipse_intersect(seg, e_proj)) {
|
||||
out_col->type = PCOL_PLAYER;
|
||||
out_col->entity = &global.plr;
|
||||
out_col->type = PCOL_ENTITY;
|
||||
out_col->entity = &global.plr.ent;
|
||||
out_col->fatal = true;
|
||||
} else {
|
||||
e_proj.axes = projectile_graze_size(p);
|
||||
|
||||
if(creal(e_proj.axes) > 1 && lineseg_ellipse_intersect(seg, e_proj)) {
|
||||
out_col->type = PCOL_PLAYER_GRAZE;
|
||||
out_col->entity = &global.plr;
|
||||
out_col->entity = &global.plr.ent;
|
||||
out_col->location = global.plr.pos;
|
||||
}
|
||||
}
|
||||
} else if(p->type >= PlrProj) {
|
||||
int damage = p->type - PlrProj;
|
||||
|
||||
} else if(p->type == PlrProj) {
|
||||
for(Enemy *e = global.enemies.first; e; e = e->next) {
|
||||
if(e->hp != ENEMY_IMMUNE && cabs(e->pos - p->pos) < 30) {
|
||||
out_col->type = PCOL_ENEMY;
|
||||
out_col->entity = e;
|
||||
out_col->type = PCOL_ENTITY;
|
||||
out_col->entity = &e->ent;
|
||||
out_col->fatal = true;
|
||||
out_col->damage = damage;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -338,10 +348,9 @@ void calc_projectile_collision(Projectile *p, ProjCollisionResult *out_col) {
|
|||
|
||||
if(global.boss && cabs(global.boss->pos - p->pos) < 42) {
|
||||
if(boss_is_vulnerable(global.boss)) {
|
||||
out_col->type = PCOL_BOSS;
|
||||
out_col->entity = global.boss;
|
||||
out_col->type = PCOL_ENTITY;
|
||||
out_col->entity = &global.boss->ent;
|
||||
out_col->fatal = true;
|
||||
out_col->damage = damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -358,19 +367,11 @@ void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCol
|
|||
break;
|
||||
}
|
||||
|
||||
case PCOL_PLAYER: {
|
||||
if(global.frames - abs(((Player*)col->entity)->recovery) >= 0) {
|
||||
player_death(col->entity);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PCOL_PLAYER_GRAZE: {
|
||||
if(p->flags & PFLAG_GRAZESPAM) {
|
||||
player_graze(col->entity, col->location, 10, 2);
|
||||
player_graze(ENT_CAST(col->entity, Player), col->location, 10, 2);
|
||||
} else {
|
||||
player_graze(col->entity, col->location, 10 + 10 * p->graze_counter, 3 + p->graze_counter);
|
||||
player_graze(ENT_CAST(col->entity, Player), col->location, 10 + 10 * p->graze_counter, 3 + p->graze_counter);
|
||||
}
|
||||
|
||||
p->graze_counter++;
|
||||
|
@ -379,19 +380,11 @@ void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCol
|
|||
break;
|
||||
}
|
||||
|
||||
case PCOL_ENEMY: {
|
||||
Enemy *e = col->entity;
|
||||
player_add_points(&global.plr, col->damage * 0.5);
|
||||
player_register_damage(&global.plr, col->damage);
|
||||
enemy_damage(e, col->damage);
|
||||
break;
|
||||
}
|
||||
|
||||
case PCOL_BOSS: {
|
||||
if(boss_damage(col->entity, col->damage)) {
|
||||
player_add_points(&global.plr, col->damage * 0.2);
|
||||
player_register_damage(&global.plr, col->damage);
|
||||
case PCOL_ENTITY: {
|
||||
if(ent_damage(col->entity, &col->damage) == DMG_RESULT_OK) {
|
||||
player_register_damage(&global.plr, col->entity, &col->damage);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -416,11 +409,6 @@ static void ent_draw_projectile(EntityInterface *ent) {
|
|||
r_shader_ptr(proj->shader);
|
||||
|
||||
#ifdef PROJ_DEBUG
|
||||
if(proj->type == PlrProj) {
|
||||
set_debug_info(&proj->debug);
|
||||
log_fatal("Projectile with type PlrProj");
|
||||
}
|
||||
|
||||
static Projectile prev_state;
|
||||
memcpy(&prev_state, proj, sizeof(Projectile));
|
||||
|
||||
|
@ -487,7 +475,7 @@ Projectile* spawn_projectile_clear_effect(Projectile *proj) {
|
|||
}
|
||||
|
||||
bool clear_projectile(ProjectileList *projlist, Projectile *proj, bool force, bool now) {
|
||||
if(proj->type >= PlrProj || (!force && !projectile_is_clearable(proj))) {
|
||||
if(proj->type == PlrProj || (!force && !projectile_is_clearable(proj))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,18 +75,20 @@ struct Projectile {
|
|||
ShaderCustomParams shader_params;
|
||||
BlendMode blend;
|
||||
int birthtime;
|
||||
float damage;
|
||||
float angle;
|
||||
ProjType type;
|
||||
DamageType damage_type;
|
||||
int max_viewport_dist;
|
||||
ProjFlags flags;
|
||||
|
||||
short graze_counter;
|
||||
int graze_counter_reset_timer;
|
||||
|
||||
// XXX: this is in frames of course, but needs to be float
|
||||
// to avoid subtle truncation and integer division gotchas.
|
||||
float timeout;
|
||||
|
||||
int graze_counter_reset_timer;
|
||||
short graze_counter;
|
||||
|
||||
#ifdef PROJ_DEBUG
|
||||
DebugInfo debug;
|
||||
#endif
|
||||
|
@ -111,6 +113,8 @@ typedef struct ProjArgs {
|
|||
Sprite *sprite_ptr;
|
||||
complex size; // affects default draw order, out-of-viewport culling, and grazing
|
||||
complex collision_size; // affects collision with player (TODO: make this work for player projectiles too?)
|
||||
float damage;
|
||||
DamageType damage_type;
|
||||
int max_viewport_dist;
|
||||
drawlayer_t layer;
|
||||
|
||||
|
@ -137,19 +141,17 @@ struct ProjPrototype {
|
|||
|
||||
typedef enum ProjCollisionType {
|
||||
PCOL_NONE = 0,
|
||||
PCOL_ENEMY = (1 << 0),
|
||||
PCOL_BOSS = (1 << 1),
|
||||
PCOL_PLAYER = (1 << 2),
|
||||
PCOL_PLAYER_GRAZE = (1 << 3),
|
||||
PCOL_VOID = (1 << 4),
|
||||
PCOL_ENTITY = (1 << 0),
|
||||
PCOL_PLAYER_GRAZE = (1 << 1),
|
||||
PCOL_VOID = (1 << 2),
|
||||
} ProjCollisionType;
|
||||
|
||||
typedef struct ProjCollisionResult {
|
||||
ProjCollisionType type;
|
||||
bool fatal; // for the projectile
|
||||
complex location;
|
||||
int damage;
|
||||
void *entity; // TODO: make this use the actual entity API
|
||||
DamageInfo damage;
|
||||
EntityInterface *entity;
|
||||
} ProjCollisionResult;
|
||||
|
||||
Projectile* create_projectile(ProjArgs *args);
|
||||
|
|
|
@ -897,7 +897,7 @@ void stage_draw_hud_text(struct labels_s* labels) {
|
|||
}
|
||||
|
||||
// hack to update the graph every frame
|
||||
player_register_damage(&global.plr, 0);
|
||||
player_register_damage(&global.plr, NULL, &(DamageInfo) { .amount = 0, .type = DMG_PLAYER_SHOT });
|
||||
|
||||
for(int i = 0; i < framespan; ++i) {
|
||||
totaldmg += global.plr.dmglog[i];
|
||||
|
|
|
@ -1101,8 +1101,8 @@ int kurumi_extra_drainer(Projectile *p, int time) {
|
|||
if(time > 40 && e->hp > 0) {
|
||||
// TODO: maybe add a special sound for this?
|
||||
|
||||
int drain = min(4, e->hp);
|
||||
enemy_damage(e, drain);
|
||||
float drain = min(4, e->hp);
|
||||
ent_damage(&e->ent, &(DamageInfo) { .amount = drain });
|
||||
global.boss->current->hp = min(global.boss->current->maxhp, global.boss->current->hp + drain * 2);
|
||||
}
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue