basic entity damage generalization

This commit is contained in:
Andrei Alexeyev 2018-07-30 10:04:09 +03:00
parent ffcb2aff9e
commit e9ef77abee
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
20 changed files with 204 additions and 117 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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) {

View file

@ -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
}

View file

@ -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);

View file

@ -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",
);
}

View file

@ -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);

View file

@ -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;

View file

@ -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",
);
}

View file

@ -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",
);
}

View file

@ -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,

View file

@ -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",
);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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];

View file

@ -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 {