enemy,enemy_classes: redesign draw callbacks
Allows to pass arbitrary data to draw callbacks. Groundwork for alternative fairy spawn animations.
This commit is contained in:
parent
907eb8ff4e
commit
57f6d3dfd8
7 changed files with 193 additions and 187 deletions
33
src/enemy.c
33
src/enemy.c
|
@ -83,7 +83,7 @@ static inline void enemy_update(Enemy *e, int t) {
|
|||
e->dir = creal(v) < 0;
|
||||
}
|
||||
|
||||
Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisualRule visual_rule) {
|
||||
Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisual visual) {
|
||||
if(IN_DRAW_CODE) {
|
||||
log_fatal("Tried to spawn an enemy while in drawing code");
|
||||
}
|
||||
|
@ -97,9 +97,8 @@ Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisualRule v
|
|||
e->pos0_visual = pos;
|
||||
e->spawn_hp = hp;
|
||||
e->hp = hp;
|
||||
e->alpha = 1.0;
|
||||
e->flags = 0;
|
||||
e->visual_rule = visual_rule;
|
||||
e->visual = visual;
|
||||
e->hurt_radius = 7;
|
||||
e->hit_radius = 30;
|
||||
|
||||
|
@ -185,7 +184,7 @@ cmplx enemy_visual_pos(Enemy *enemy) {
|
|||
return enemy->pos;
|
||||
}
|
||||
|
||||
double t = (global.frames - enemy->birthtime) / 30.0;
|
||||
real t = (global.frames - enemy->birthtime) / 30.0;
|
||||
|
||||
if(t >= 1) {
|
||||
return enemy->pos;
|
||||
|
@ -197,17 +196,17 @@ cmplx enemy_visual_pos(Enemy *enemy) {
|
|||
return p;
|
||||
}
|
||||
|
||||
static void call_visual_rule(Enemy *e, bool render) {
|
||||
cmplx tmp = e->pos;
|
||||
e->pos = enemy_visual_pos(e);
|
||||
e->visual_rule(e, global.frames - e->birthtime, render);
|
||||
e->pos = tmp;
|
||||
static void draw_enemy(Enemy *e) {
|
||||
e->visual.draw(e, (EnemyDrawParams) {
|
||||
.time = global.frames - e->birthtime,
|
||||
.pos = enemy_visual_pos(e),
|
||||
});
|
||||
}
|
||||
|
||||
static void ent_draw_enemy(EntityInterface *ent) {
|
||||
Enemy *e = ENT_CAST(ent, Enemy);
|
||||
|
||||
if(!e->visual_rule) {
|
||||
if(!e->visual.draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -216,7 +215,7 @@ static void ent_draw_enemy(EntityInterface *ent) {
|
|||
memcpy(&prev_state, e, sizeof(Enemy));
|
||||
#endif
|
||||
|
||||
call_visual_rule(e, true);
|
||||
draw_enemy(e);
|
||||
|
||||
#ifdef ENEMY_DEBUG
|
||||
if(memcmp(&prev_state, e, sizeof(Enemy))) {
|
||||
|
@ -300,11 +299,7 @@ static DamageResult ent_damage_enemy(EntityInterface *ienemy, const DamageInfo *
|
|||
}
|
||||
|
||||
float enemy_get_hurt_radius(Enemy *enemy) {
|
||||
if(enemy->flags & EFLAG_NO_HURT || enemy->alpha < 1.0f) {
|
||||
return 0;
|
||||
} else {
|
||||
return enemy->hurt_radius;
|
||||
}
|
||||
return (enemy->flags & EFLAG_NO_HURT) ? 0 : enemy->hurt_radius;
|
||||
}
|
||||
|
||||
static bool should_auto_kill(Enemy *enemy) {
|
||||
|
@ -331,16 +326,10 @@ void process_enemies(EnemyList *enemies) {
|
|||
ent_damage(&global.plr.ent, &(DamageInfo) { .type = DMG_ENEMY_COLLISION });
|
||||
}
|
||||
|
||||
enemy->alpha = approach(enemy->alpha, 1.0, 1.0/60.0);
|
||||
|
||||
if(should_auto_kill(enemy)) {
|
||||
delete_enemy(enemies, enemy);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(enemy->visual_rule) {
|
||||
call_visual_rule(enemy, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
25
src/enemy.h
25
src/enemy.h
|
@ -28,7 +28,6 @@
|
|||
#endif
|
||||
|
||||
typedef LIST_ANCHOR(Enemy) EnemyList;
|
||||
typedef void (*EnemyVisualRule)(Enemy*, int t, bool render);
|
||||
|
||||
typedef enum EnemyFlag {
|
||||
EFLAG_KILLED = (1 << 0), // is dead, pending removal (internal)
|
||||
|
@ -50,12 +49,26 @@ typedef enum EnemyFlag {
|
|||
0,
|
||||
} EnemyFlag;
|
||||
|
||||
typedef struct EnemyDrawParams {
|
||||
cmplx pos; // NOTE: subject to slide-in correction at screen edges
|
||||
int time;
|
||||
} EnemyDrawParams;
|
||||
|
||||
typedef void (*EnemyDrawFunc)(Enemy*, EnemyDrawParams);
|
||||
|
||||
typedef struct EnemyVisual {
|
||||
EnemyDrawFunc draw;
|
||||
void *drawdata;
|
||||
} EnemyVisual;
|
||||
|
||||
#define ENEMY_NOVISUAL ((EnemyVisual) {})
|
||||
|
||||
DEFINE_ENTITY_TYPE(Enemy, {
|
||||
cmplx pos;
|
||||
cmplx pos0;
|
||||
cmplx pos0_visual;
|
||||
MoveParams move;
|
||||
EnemyVisualRule visual_rule;
|
||||
EnemyVisual visual;
|
||||
|
||||
COEVENTS_ARRAY(
|
||||
predamage,
|
||||
|
@ -95,8 +108,6 @@ DEFINE_ENTITY_TYPE(Enemy, {
|
|||
float spawn_hp;
|
||||
float hp;
|
||||
|
||||
float alpha;
|
||||
|
||||
float hit_radius;
|
||||
float hurt_radius;
|
||||
|
||||
|
@ -109,15 +120,15 @@ DEFINE_ENTITY_TYPE(Enemy, {
|
|||
)
|
||||
});
|
||||
|
||||
Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisualRule draw_rule);
|
||||
Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisual visual);
|
||||
|
||||
#ifdef ENEMY_DEBUG
|
||||
Enemy *_enemy_attach_dbginfo(Enemy *p, DebugInfo *dbg);
|
||||
#define create_enemy_p(...) _enemy_attach_dbginfo(create_enemy_p(__VA_ARGS__), _DEBUG_INFO_PTR_)
|
||||
#endif
|
||||
|
||||
#define create_enemy(pos, hp, draw_rule) \
|
||||
create_enemy_p(&global.enemies, pos, hp, draw_rule)
|
||||
#define create_enemy(pos, hp, visual) \
|
||||
create_enemy_p(&global.enemies, pos, hp, visual)
|
||||
|
||||
void delete_enemy(EnemyList *enemies, Enemy* enemy);
|
||||
void delete_enemies(EnemyList *enemies);
|
||||
|
|
|
@ -19,16 +19,52 @@
|
|||
#define ECLASS_HP_HUGE_FAIRY 8000
|
||||
#define ECLASS_HP_SUPER_FAIRY 28000
|
||||
|
||||
typedef struct BaseEnemyVisualParams {
|
||||
union {
|
||||
Sprite *spr;
|
||||
Animation *ani;
|
||||
};
|
||||
} BaseEnemyVisualParams;
|
||||
|
||||
typedef struct FairyVisualParams {
|
||||
BaseEnemyVisualParams base;
|
||||
} FairyVisualParams;
|
||||
|
||||
SpriteParams ecls_anyfairy_sprite_params(
|
||||
Enemy *fairy,
|
||||
EnemyDrawParams draw_params,
|
||||
SpriteParamsBuffer *out_spbuf
|
||||
) {
|
||||
FairyVisualParams *vp = fairy->visual.drawdata;
|
||||
auto ani = vp->base.ani;
|
||||
const char *seqname = !fairy->moving ? "main" : (fairy->dir ? "left" : "right");
|
||||
Sprite *spr = animation_get_frame(ani, get_ani_sequence(ani, seqname), draw_params.time);
|
||||
|
||||
out_spbuf->color = *RGB(1, 1, 1);
|
||||
|
||||
return (SpriteParams) {
|
||||
.color = &out_spbuf->color,
|
||||
.sprite_ptr = spr,
|
||||
.pos.as_cmplx = draw_params.pos,
|
||||
};
|
||||
}
|
||||
|
||||
static void anyfairy_draw(Enemy *e, EnemyDrawParams p) {
|
||||
SpriteParamsBuffer spbuf;
|
||||
SpriteParams sp = ecls_anyfairy_sprite_params(e, p, &spbuf);
|
||||
r_draw_sprite(&sp);
|
||||
}
|
||||
|
||||
TASK(fairy_circle, {
|
||||
BoxedEnemy e;
|
||||
Sprite *sprite;
|
||||
Color color;
|
||||
float spin_rate;
|
||||
float scale_base;
|
||||
float scale_osc_ampl;
|
||||
float scale_osc_freq;
|
||||
Color color;
|
||||
}) {
|
||||
Projectile *p = TASK_BIND(PARTICLE(
|
||||
Projectile *circle = TASK_BIND(PARTICLE(
|
||||
.sprite_ptr = ARGS.sprite,
|
||||
.color = &ARGS.color,
|
||||
.flags = PFLAG_NOMOVE | PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE | PFLAG_NOAUTOREMOVE,
|
||||
|
@ -38,14 +74,14 @@ TASK(fairy_circle, {
|
|||
float scale_osc_phase = 0.0f;
|
||||
|
||||
for(Enemy *e; (e = ENT_UNBOX(ARGS.e)); YIELD) {
|
||||
p->pos = enemy_visual_pos(e);
|
||||
p->opacity = e->alpha;
|
||||
p->angle += ARGS.spin_rate;
|
||||
p->scale = (ARGS.scale_base + ARGS.scale_osc_ampl * sinf(scale_osc_phase)) * (1.0f + I);
|
||||
circle->pos = enemy_visual_pos(e);
|
||||
circle->angle += ARGS.spin_rate;
|
||||
float s = ARGS.scale_base + ARGS.scale_osc_ampl * sinf(scale_osc_phase);
|
||||
circle->scale = CMPLXF(s, s);
|
||||
scale_osc_phase += ARGS.scale_osc_freq;
|
||||
}
|
||||
|
||||
kill_projectile(p);
|
||||
kill_projectile(circle);
|
||||
}
|
||||
|
||||
TASK(fairy_flame_emitter, {
|
||||
|
@ -114,7 +150,6 @@ TASK(fairy_stardust_emitter, {
|
|||
|
||||
old_pos = e->pos;
|
||||
|
||||
|
||||
if(!(t % period)) {
|
||||
RNG_ARRAY(rng, 4);
|
||||
cmplx pos = e->pos + vrng_sreal(rng[0]) * 12 + vrng_sreal(rng[1]) * 10 * I;
|
||||
|
@ -134,58 +169,6 @@ TASK(fairy_stardust_emitter, {
|
|||
}
|
||||
}
|
||||
|
||||
static void draw_fairy(Enemy *e, int t, Animation *ani) {
|
||||
const char *seqname = !e->moving ? "main" : (e->dir ? "left" : "right");
|
||||
Sprite *spr = animation_get_frame(ani, get_ani_sequence(ani, seqname), t);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.color = RGBA_MUL_ALPHA(1, 1, 1, e->alpha),
|
||||
.sprite_ptr = spr,
|
||||
.pos.as_cmplx = e->pos,
|
||||
});
|
||||
}
|
||||
|
||||
static void visual_swirl(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.color = RGBA_MUL_ALPHA(1, 1, 1, e->alpha),
|
||||
.sprite = "enemy/swirl",
|
||||
.pos.as_cmplx = e->pos,
|
||||
.rotation.angle = t * 10 * DEG2RAD,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void visual_fairy_blue(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
draw_fairy(e, t, res_anim("enemy/fairy_blue"));
|
||||
}
|
||||
}
|
||||
|
||||
static void visual_fairy_red(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
draw_fairy(e, t, res_anim("enemy/fairy_red"));
|
||||
}
|
||||
}
|
||||
|
||||
static void visual_big_fairy(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
draw_fairy(e, t, res_anim("enemy/bigfairy"));
|
||||
}
|
||||
}
|
||||
|
||||
static void visual_huge_fairy(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
draw_fairy(e, t, res_anim("enemy/hugefairy"));
|
||||
}
|
||||
}
|
||||
|
||||
static void visual_super_fairy(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
draw_fairy(e, t, res_anim("enemy/superfairy"));
|
||||
}
|
||||
}
|
||||
|
||||
TASK(enemy_drop_items, { BoxedEnemy e; ItemCounts items; }) {
|
||||
// NOTE: Don't bind here! We need this task to outlive the enemy.
|
||||
Enemy *e = NOT_NULL(ENT_UNBOX(ARGS.e));
|
||||
|
@ -193,68 +176,116 @@ TASK(enemy_drop_items, { BoxedEnemy e; ItemCounts items; }) {
|
|||
if(e->damage_info && DAMAGETYPE_IS_PLAYER(e->damage_info->type)) {
|
||||
common_drop_items(e->pos, &ARGS.items);
|
||||
}
|
||||
|
||||
// NOTE: Needed to keep drawdata alive for this frame (see _spawn below)
|
||||
YIELD;
|
||||
}
|
||||
|
||||
static Enemy *spawn(cmplx pos, const ItemCounts *item_drops, real hp, EnemyVisualRule visual) {
|
||||
Enemy *e = create_enemy(pos, hp, visual);
|
||||
static Enemy *_spawn(
|
||||
cmplx pos, const ItemCounts *item_drops, real hp, EnemyDrawFunc draw, size_t drawdata_alloc
|
||||
) {
|
||||
assert(drawdata_alloc > 0);
|
||||
Enemy *e = create_enemy(pos, hp, (EnemyVisual) { draw });
|
||||
|
||||
if(item_drops) {
|
||||
INVOKE_TASK_WHEN(&e->events.killed, enemy_drop_items, ENT_BOX(e), *item_drops);
|
||||
}
|
||||
// NOTE: almost all enemies drop items, so we can abuse the itemdrop task's stack to hold onto
|
||||
// the draw data buffer for us.
|
||||
|
||||
auto t = INVOKE_TASK_WHEN(&e->events.killed, enemy_drop_items, {
|
||||
.e = ENT_BOX(e),
|
||||
.items = LIKELY(item_drops) ? *item_drops : (ItemCounts) { }
|
||||
});
|
||||
e->visual.drawdata = cotask_malloc(t, drawdata_alloc);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
// Spawn enemy; allocate and initialize its drawdata buffer
|
||||
#define spawn(_pos, _items, _hp, _draw, ...) ({ \
|
||||
auto _e = _spawn(_pos, _items, _hp, _draw, sizeof(__VA_ARGS__)); \
|
||||
*(typeof(__VA_ARGS__)*)(_e->visual.drawdata) = __VA_ARGS__; \
|
||||
_e; \
|
||||
});
|
||||
|
||||
static void swirl_draw(Enemy *e, EnemyDrawParams p) {
|
||||
BaseEnemyVisualParams *vp = e->visual.drawdata;
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.color = RGB(1, 1 ,1),
|
||||
.sprite_ptr = vp->spr,
|
||||
.pos.as_cmplx = p.pos,
|
||||
.rotation.angle = p.time * 10 * DEG2RAD,
|
||||
});
|
||||
}
|
||||
|
||||
Enemy *(espawn_swirl)(cmplx pos, const ItemCounts *item_drops) {
|
||||
Enemy *e = spawn(pos, item_drops, ECLASS_HP_SWIRL, visual_swirl);
|
||||
return spawn(pos, item_drops, ECLASS_HP_SWIRL, swirl_draw, (BaseEnemyVisualParams) {
|
||||
.spr = res_sprite("enemy/swirl"),
|
||||
});
|
||||
}
|
||||
|
||||
static Enemy *spawn_fairy(cmplx pos, const ItemCounts *item_drops, real hp, Animation *ani) {
|
||||
return spawn(pos, item_drops, hp, anyfairy_draw, (FairyVisualParams) {
|
||||
.base.ani = ani,
|
||||
});
|
||||
}
|
||||
|
||||
static Enemy *espawn_fairy_weak(
|
||||
cmplx pos, const ItemCounts *item_drops, Animation *fairy_ani, Sprite *circle_spr
|
||||
) {
|
||||
auto e = spawn_fairy(pos, item_drops, ECLASS_HP_FAIRY, fairy_ani);
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = circle_spr,
|
||||
.color = *RGB(1, 1, 1),
|
||||
.spin_rate = 10 * DEG2RAD,
|
||||
.scale_base = 0.8f,
|
||||
.scale_osc_ampl = 1.0f / 6.0f,
|
||||
.scale_osc_freq = 0.1f,
|
||||
);
|
||||
return e;
|
||||
}
|
||||
|
||||
Enemy *(espawn_fairy_blue)(cmplx pos, const ItemCounts *item_drops) {
|
||||
Enemy *e = spawn(pos, item_drops, ECLASS_HP_FAIRY, visual_fairy_blue);
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = res_sprite("fairy_circle"),
|
||||
.spin_rate = 10 * DEG2RAD,
|
||||
.scale_base = 0.8f,
|
||||
.scale_osc_ampl = 1.0f / 6.0f,
|
||||
.scale_osc_freq = 0.1f,
|
||||
.color = *RGB(1, 1, 1)
|
||||
return espawn_fairy_weak(
|
||||
pos, item_drops,
|
||||
res_anim("enemy/fairy_blue"),
|
||||
res_sprite("fairy_circle")
|
||||
);
|
||||
return e;
|
||||
}
|
||||
|
||||
Enemy *(espawn_fairy_red)(cmplx pos, const ItemCounts *item_drops) {
|
||||
Enemy *e = spawn(pos, item_drops, ECLASS_HP_FAIRY, visual_fairy_red);
|
||||
return espawn_fairy_weak(
|
||||
pos, item_drops,
|
||||
res_anim("enemy/fairy_red"),
|
||||
res_sprite("fairy_circle_red")
|
||||
);
|
||||
}
|
||||
|
||||
Enemy *(espawn_big_fairy)(cmplx pos, const ItemCounts *item_drops) {
|
||||
auto e = spawn_fairy(pos, item_drops, ECLASS_HP_BIG_FAIRY, res_anim("enemy/bigfairy"));
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = res_sprite("fairy_circle_red"),
|
||||
.sprite = res_sprite("fairy_circle_big"),
|
||||
.color = *RGB(1, 1, 1),
|
||||
.spin_rate = 10 * DEG2RAD,
|
||||
.scale_base = 0.8f,
|
||||
.scale_osc_ampl = 1.0f / 6.0f,
|
||||
.scale_osc_freq = 0.1f,
|
||||
.color = *RGB(1, 1, 1)
|
||||
);
|
||||
return e;
|
||||
}
|
||||
|
||||
Enemy *(espawn_big_fairy)(cmplx pos, const ItemCounts *item_drops) {
|
||||
Enemy *e = spawn(pos, item_drops, ECLASS_HP_BIG_FAIRY, visual_big_fairy);
|
||||
INVOKE_TASK(fairy_flame_emitter, ENT_BOX(e),
|
||||
.period = 5,
|
||||
.color = *RGBA(0.0, 0.2, 0.3, 0.0)
|
||||
);
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = res_sprite("fairy_circle_big"),
|
||||
.spin_rate = 5 * DEG2RAD,
|
||||
.scale_base = 0.75f,
|
||||
.scale_osc_ampl = 0.15f,
|
||||
.scale_osc_freq = 1.0f / 15.0f,
|
||||
.color = *RGBA(1, 1, 1, 0.8)
|
||||
);
|
||||
return e;
|
||||
}
|
||||
|
||||
Enemy *(espawn_huge_fairy)(cmplx pos, const ItemCounts *item_drops) {
|
||||
Enemy *e = spawn(pos, item_drops, ECLASS_HP_HUGE_FAIRY, visual_huge_fairy);
|
||||
auto e = spawn_fairy(pos, item_drops, ECLASS_HP_HUGE_FAIRY, res_anim("enemy/hugefairy"));
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = res_sprite("fairy_circle_big"),
|
||||
.color = *RGBA(1, 1, 1, 0.95),
|
||||
.spin_rate = 5 * DEG2RAD,
|
||||
.scale_base = 0.85f,
|
||||
.scale_osc_ampl = 0.1f,
|
||||
.scale_osc_freq = 1.0f / 15.0f,
|
||||
);
|
||||
INVOKE_TASK(fairy_flame_emitter, ENT_BOX(e),
|
||||
.period = 6,
|
||||
.color = *RGBA(0.0, 0.2, 0.3, 0.0)
|
||||
|
@ -263,19 +294,19 @@ Enemy *(espawn_huge_fairy)(cmplx pos, const ItemCounts *item_drops) {
|
|||
.period = 6,
|
||||
.color = *RGBA(0.3, 0.0, 0.2, 0.0)
|
||||
);
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = res_sprite("fairy_circle_big"),
|
||||
.spin_rate = 5 * DEG2RAD,
|
||||
.scale_base = 0.85f,
|
||||
.scale_osc_ampl = 0.1f,
|
||||
.scale_osc_freq = 1.0f / 15.0f,
|
||||
.color = *RGBA(1, 1, 1, 0.95)
|
||||
);
|
||||
return e;
|
||||
}
|
||||
|
||||
Enemy *(espawn_super_fairy)(cmplx pos, const ItemCounts *item_drops) {
|
||||
Enemy *e = spawn(pos, item_drops, ECLASS_HP_SUPER_FAIRY, visual_super_fairy);
|
||||
auto e = spawn_fairy(pos, item_drops, ECLASS_HP_SUPER_FAIRY, res_anim("enemy/superfairy"));
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = res_sprite("fairy_circle_big_and_mean"),
|
||||
.color = *RGBA(1, 1, 1, 0.6),
|
||||
.spin_rate = 5 * DEG2RAD,
|
||||
.scale_base = 0.9f,
|
||||
.scale_osc_ampl = 0.1f,
|
||||
.scale_osc_freq = 1.0f / 15.0f,
|
||||
);
|
||||
INVOKE_TASK(fairy_flame_emitter, ENT_BOX(e),
|
||||
.period = 5,
|
||||
.color = *RGBA(0.2, 0.0, 0.3, 0.0)
|
||||
|
@ -284,13 +315,5 @@ Enemy *(espawn_super_fairy)(cmplx pos, const ItemCounts *item_drops) {
|
|||
.period = 15,
|
||||
.color = *RGBA(0.0, 0.0, 0.0, 0.8)
|
||||
);
|
||||
INVOKE_TASK(fairy_circle, ENT_BOX(e),
|
||||
.sprite = res_sprite("fairy_circle_big_and_mean"),
|
||||
.spin_rate = 5 * DEG2RAD,
|
||||
.scale_base = 0.9f,
|
||||
.scale_osc_ampl = 0.1f,
|
||||
.scale_osc_freq = 1.0f / 15.0f,
|
||||
.color = *RGBA(1, 1, 1, 0.6)
|
||||
);
|
||||
return e;
|
||||
}
|
||||
|
|
|
@ -39,3 +39,9 @@ Enemy *espawn_super_fairy(cmplx pos, const ItemCounts *item_drops);
|
|||
#define espawn_big_fairy_box(...) ENT_BOX(espawn_big_fairy(__VA_ARGS__))
|
||||
#define espawn_huge_fairy_box(...) ENT_BOX(espawn_huge_fairy(__VA_ARGS__))
|
||||
#define espawn_super_fairy_box(...) ENT_BOX(espawn_super_fairy(__VA_ARGS__))
|
||||
|
||||
SpriteParams ecls_anyfairy_sprite_params(
|
||||
Enemy *fairy,
|
||||
EnemyDrawParams draw_params,
|
||||
SpriteParamsBuffer *out_spbuf
|
||||
);
|
||||
|
|
|
@ -20,21 +20,19 @@ TASK(spinner_bullet_redirect, { BoxedProjectile p; MoveParams move; }) {
|
|||
p->move.velocity += ov;
|
||||
}
|
||||
|
||||
static void amulet_visual(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.color = RGBA(2, 1, 1, 0),
|
||||
.sprite = "fairy_circle_big",
|
||||
.pos.as_cmplx = e->pos,
|
||||
.rotation.angle = t * 5 * DEG2RAD,
|
||||
});
|
||||
static void amulet_draw(Enemy *e, EnemyDrawParams p) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.color = RGBA(2, 1, 1, 0),
|
||||
.sprite = "fairy_circle_big",
|
||||
.pos.as_cmplx = p.pos,
|
||||
.rotation.angle = p.time * 5 * DEG2RAD,
|
||||
});
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "enemy/swirl",
|
||||
.pos.as_cmplx = e->pos,
|
||||
.rotation.angle = t * -10 * DEG2RAD,
|
||||
});
|
||||
}
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "enemy/swirl",
|
||||
.pos.as_cmplx = p.pos,
|
||||
.rotation.angle = p.time * -10 * DEG2RAD,
|
||||
});
|
||||
}
|
||||
|
||||
TASK(amulet_fire_spinners, { BoxedEnemy core; BoxedProjectileArray *spinners; }) {
|
||||
|
@ -77,7 +75,7 @@ TASK(amulet, {
|
|||
MoveParams move;
|
||||
CoEvent *death_event;
|
||||
}) {
|
||||
Enemy *core = create_enemy(ARGS.pos, 2000, amulet_visual);
|
||||
Enemy *core = create_enemy(ARGS.pos, 2000, (EnemyVisual) { amulet_draw });
|
||||
core->hurt_radius = 18;
|
||||
core->hit_radius = 36;
|
||||
core->flags |= EFLAG_NO_VISUAL_CORRECTION;
|
||||
|
|
|
@ -16,11 +16,7 @@
|
|||
|
||||
// TODO SUPER REDESIGN THIS, IT'S A MESS!
|
||||
|
||||
static void kurumi_extra_shield_visual(Enemy *e, int time, bool render) {
|
||||
if(!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void kurumi_extra_shield_draw(Enemy *e, EnemyDrawParams p) {
|
||||
// TODO: something nicer here
|
||||
|
||||
float h = clampf(e->hp / e->spawn_hp, 0, 1);
|
||||
|
@ -29,37 +25,20 @@ static void kurumi_extra_shield_visual(Enemy *e, int time, bool render) {
|
|||
r_draw_sprite(&(SpriteParams) {
|
||||
.color = RGBA_MUL_ALPHA(
|
||||
1 + (1 - h), 0.3 + 0.7 * h, 0.2 + 0.8 * h,
|
||||
e->alpha * (1 + 1 - h)
|
||||
1 + 1 - h
|
||||
),
|
||||
.sprite = "enemy/swirl",
|
||||
.pos.as_cmplx = e->pos,
|
||||
.rotation.angle = time * -10 * DEG2RAD,
|
||||
.pos.as_cmplx = p.pos,
|
||||
.rotation.angle = p.time * -10 * DEG2RAD,
|
||||
.shader = "sprite_negative",
|
||||
});
|
||||
}
|
||||
|
||||
static void draw_negative_fairy(Enemy *e, int t, Animation *ani) {
|
||||
const char *seqname = !e->moving ? "main" : (e->dir ? "left" : "right");
|
||||
Sprite *spr = animation_get_frame(ani, get_ani_sequence(ani, seqname), t);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.shader = "sprite_negative",
|
||||
.color = RGBA_MUL_ALPHA(1, 1, 1, e->alpha),
|
||||
.sprite_ptr = spr,
|
||||
.pos.as_cmplx = e->pos,
|
||||
});
|
||||
}
|
||||
|
||||
static void kurumi_extra_fairy_visual(Enemy *e, int time, bool render) {
|
||||
if(render) {
|
||||
draw_negative_fairy(e, time, res_anim("enemy/fairy_blue"));
|
||||
}
|
||||
}
|
||||
|
||||
static void kurumi_extra_bigfairy_visual(Enemy *e, int time, bool render) {
|
||||
if(render) {
|
||||
draw_negative_fairy(e, time, res_anim("enemy/superfairy"));
|
||||
}
|
||||
static void draw_negative_fairy(Enemy *e, EnemyDrawParams p) {
|
||||
SpriteParamsBuffer spbuf;
|
||||
SpriteParams sp = ecls_anyfairy_sprite_params(e, p, &spbuf);
|
||||
sp.shader_ptr = res_shader("sprite_negative");
|
||||
r_draw_sprite(&sp);
|
||||
}
|
||||
|
||||
TASK(kurumi_vladsarmy_shield_death_proj, { cmplx pos; MoveParams move; }) {
|
||||
|
@ -119,7 +98,7 @@ TASK(kurumi_vladsarmy_shield, { BoxedBoss boss; real angle; }) {
|
|||
int hp = 1500;
|
||||
Boss *b = NOT_NULL(ENT_UNBOX(ARGS.boss));
|
||||
|
||||
Enemy *e = create_enemy(b->pos, hp, kurumi_extra_shield_visual);
|
||||
Enemy *e = create_enemy(b->pos, hp, (EnemyVisual) { kurumi_extra_shield_draw });
|
||||
e->flags = EFLAG_IMPENETRABLE;
|
||||
|
||||
int timeout = 800;
|
||||
|
@ -135,7 +114,7 @@ TASK(kurumi_vladsarmy_shield, { BoxedBoss boss; real angle; }) {
|
|||
|
||||
TASK(kurumi_vladsarmy_bigfairy, { cmplx pos; cmplx target; }) {
|
||||
Enemy *e = TASK_BIND(espawn_big_fairy(ARGS.pos, ITEMS(.points = 2, .power = 1)));
|
||||
e->visual_rule = kurumi_extra_bigfairy_visual;
|
||||
e->visual.draw = draw_negative_fairy;
|
||||
|
||||
int escapetime = difficulty_value(400, 400, 400, 4000);
|
||||
|
||||
|
@ -240,7 +219,7 @@ TASK(kurumi_vladsarmy_drainer, { BoxedBoss boss; BoxedEnemy enemy; }) {
|
|||
|
||||
TASK(kurumi_vladsarmy_fairy, { cmplx start_pos; cmplx target_pos; int attack_time; int chase_time; int attack_type; BoxedBoss boss; }){
|
||||
Enemy *e = TASK_BIND(espawn_fairy_blue(ARGS.start_pos, ITEMS(.points = 1)));
|
||||
e->visual_rule = kurumi_extra_fairy_visual;
|
||||
e->visual.draw = draw_negative_fairy;
|
||||
e->flags |= EFLAG_NO_AUTOKILL;
|
||||
|
||||
e->move = move_from_towards(e->pos, ARGS.target_pos, 0.1);
|
||||
|
|
|
@ -78,7 +78,7 @@ TASK(lightning_slave_move, { BoxedEnemy e; cmplx velocity; }) {
|
|||
}
|
||||
|
||||
TASK(lightning_slave, { cmplx pos; cmplx move_arg; }) {
|
||||
Enemy *e = TASK_BIND(create_enemy(ARGS.pos, 1, NULL));
|
||||
Enemy *e = TASK_BIND(create_enemy(ARGS.pos, 1, ENEMY_NOVISUAL));
|
||||
e->flags = EFLAGS_GHOST;
|
||||
|
||||
INVOKE_TASK(lightning_slave_move, ENT_BOX(e), ARGS.move_arg);
|
||||
|
|
Loading…
Reference in a new issue