taisei/src/plrmodes/youmu_b.c
2018-07-30 10:04:39 +03:00

442 lines
10 KiB
C

/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include <float.h>
#include "global.h"
#include "plrmodes.h"
#include "youmu.h"
static complex youmu_homing_target(complex org, complex fallback) {
double mindst = DBL_MAX;
complex target = fallback;
if(global.boss && boss_is_vulnerable(global.boss)) {
target = global.boss->pos;
mindst = cabs(target - org);
}
for(Enemy *e = global.enemies.first; e; e = e->next) {
if(e->hp == ENEMY_IMMUNE){
continue;
}
double dst = cabs(e->pos - org);
if(dst < mindst) {
mindst = dst;
target = e->pos;
}
}
return target;
}
static void youmu_homing_draw_common(Projectile *p, float clrfactor, float scale, float alpha) {
Color c = p->color;
color_mul(&c, RGBA(0.7f + 0.3f * clrfactor, 0.9f + 0.1f * clrfactor, 1, 1));
if(alpha <= 0) {
return;
}
bool special_snowflake_shader_bullshit = p->shader_params.vector[1] != 0;
if(special_snowflake_shader_bullshit) {
// FIXME: maybe move this to logic someh-- nah. Don't even bother with this crap.
float old = p->shader_params.vector[1];
p->shader_params.vector[1] = alpha;
youmu_common_draw_proj(p, &c, scale);
p->shader_params.vector[1] = old;
} else {
color_mul_scalar(&c, alpha);
youmu_common_draw_proj(p, &c, scale);
}
}
static void youmu_homing_draw_proj(Projectile *p, int t) {
float a = clamp(1.0f - (float)t / p->args[2], 0, 1);
youmu_homing_draw_common(p, a, 1, 0.5f);
}
static void youmu_homing_draw_trail(Projectile *p, int t) {
float a = clamp(1.0f - (float)t / p->timeout, 0, 1);
youmu_homing_draw_common(p, a, 5 * (1 - a), 0.15f * a);
}
static void youmu_trap_draw_trail(Projectile *p, int t) {
float a = clamp(1.0f - (float)t / p->timeout, 0, 1);
youmu_homing_draw_common(p, a, 2 - a, 0.15f * a);
}
static void youmu_trap_draw_child_proj(Projectile *p, int t) {
float to = p->args[2];
float a = clamp(1.0 - 3 * ((t - (to - to/3)) / to), 0, 1);
a = 1 - pow(1 - a, 2);
youmu_homing_draw_common(p, a, 1 + 2 * pow(1 - a, 2), a);
}
static float youmu_trap_charge(int t) {
return pow(clamp(t / 60.0, 0, 1), 1.5);
}
static Projectile* youmu_homing_trail(Projectile *p, complex v, int to) {
return PARTICLE(
.sprite_ptr = p->sprite,
.pos = p->pos,
.color = &p->color,
.angle = p->angle,
.rule = linear,
.timeout = to,
.draw_rule = youmu_homing_draw_trail,
.args = { v },
.flags = PFLAG_NOREFLECT,
.shader_ptr = p->shader,
.shader_params = &p->shader_params,
.layer = LAYER_PARTICLE_LOW,
);
}
static int youmu_homing(Projectile *p, int t) { // a[0]: velocity, a[1]: aim (r: linear, i: accelerated), a[2]: timeout, a[3]: initial target
if(t == EVENT_DEATH || t == EVENT_BIRTH) {
return ACTION_ACK;
}
if(t > creal(p->args[2])) {
return ACTION_DESTROY;
}
p->args[3] = youmu_homing_target(p->pos, p->args[3]);
double v = cabs(p->args[0]);
complex aimdir = cexp(I*carg(p->args[3] - p->pos));
p->args[0] += creal(p->args[1]) * aimdir;
p->args[0] = v * cexp(I*carg(p->args[0])) + cimag(p->args[1]) * aimdir;
p->angle = carg(p->args[0]);
p->pos += p->args[0];
Projectile *trail = youmu_homing_trail(p, 0.5 * p->args[0], 12);
trail->args[2] = p->args[2];
p->shader_params.vector[0] = cimag(p->args[2]);
trail->shader_params.vector[0] = cimag(p->args[2]);
return 1;
}
static Projectile* youmu_trap_trail(Projectile *p, complex v, int t, bool additive) {
Projectile *trail = youmu_homing_trail(p, v, t);
trail->draw_rule = youmu_trap_draw_trail;
// trail->args[3] = global.frames - p->birthtime;
trail->shader_params.vector[0] = p->shader_params.vector[0];
if(additive) {
trail->color.a = 0;
}
return trail;
}
static int youmu_trap(Projectile *p, int t) {
if(t == EVENT_DEATH) {
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 15,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
return ACTION_ACK;
}
// FIXME: replace this with timeout?
double expiretime = creal(p->args[1]);
if(t > expiretime) {
return ACTION_DESTROY;
}
if(t < 0) {
return ACTION_ACK;
}
float charge = youmu_trap_charge(t);
p->shader_params.vector[0] = charge;
if(!(global.plr.inputflags & INFLAG_FOCUS)) {
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 20,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 23,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
int cnt = rint(creal(p->args[2]) * (0.25 + 0.75 * charge));
int dmg = cimag(p->args[2]);
complex aim = p->args[3];
for(int i = 0; i < cnt; ++i) {
int dur = 55 + 20 * nfrand();
float a = (i / (float)cnt) * M_PI * 2;
complex dir = cexp(I*(a));
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,
.damage = dmg,
.draw_rule = youmu_trap_draw_child_proj,
.shader = "sprite_youmu_charged_shot",
.shader_params = &(ShaderCustomParams){{ 0, 1 }},
);
}
// TODO: dedicated sound for this?
play_sound("enemydeath");
play_sound("hit");
return ACTION_DESTROY;
}
p->angle = global.frames + t;
p->pos += p->args[0] * (0.01 + 0.99 * max(0, (10 - t) / 10.0));
youmu_trap_trail(p, cexp(I*p->angle), 30 * (1 + charge), true);
youmu_trap_trail(p, cexp(I*-p->angle), 30, false);
return 1;
}
static void youmu_particle_slice_draw(Projectile *p, int t) {
double lifetime = p->timeout;
double tt = t/lifetime;
double f = 0;
if(tt > 0.1) {
f = min(1,(tt-0.1)/0.2);
}
if(tt > 0.5) {
f = 1+(tt-0.5)/0.5;
}
r_mat_push();
r_mat_translate(creal(p->pos), cimag(p->pos),0);
r_mat_rotate_deg(p->angle/M_PI*180,0,0,1);
r_mat_scale(f,1,1);
//draw_texture(0,0,"part/youmu_slice");
ProjDrawCore(p, &p->color);
r_mat_pop();
double slicelen = 500;
f = sqrt(f);
complex slicepos=p->pos-(tt>0.1)*slicelen*I*cexp(I*p->angle)*(5*pow(tt-0.1,1.1)-0.5);
draw_sprite_batched_p(creal(slicepos), cimag(slicepos), aniplayer_get_frame(&global.plr.ani));
}
static int youmu_particle_slice_logic(Projectile *p, int t) {
if(t < 0) {
return ACTION_ACK;
}
double lifetime = p->timeout;
double tt = t/lifetime;
double a = 0;
if(tt > 0.) {
a = min(1,(tt-0.)/0.2);
}
if(tt > 0.5) {
a = max(0,1-(tt-0.5)/0.5);
}
p->color = *RGBA(a, a, a, 0);
complex phase = cexp(p->angle*I);
if(t%5 == 0) {
tsrand_fill(4);
PARTICLE(
.sprite = "petal",
.pos = p->pos-400*phase,
.rule = accelerated,
.draw_rule = Petal,
.color = RGBA(0.1, 0.1, 0.5, 0),
.args = {
phase,
phase*cexp(0.1*I),
afrand(1) + afrand(2)*I,
afrand(3) + 360.0*I*afrand(0)
},
.layer = LAYER_PARTICLE_HIGH | 0x2,
);
}
return ACTION_NONE;
}
static void YoumuSlash(Enemy *e, int t, bool render) {
}
static int youmu_slash(Enemy *e, int t) {
if(t > creal(e->args[0]))
return ACTION_DESTROY;
if(t < 0)
return 1;
if(global.frames - global.plr.recovery > 0) {
return ACTION_DESTROY;
}
TIMER(&t);
FROM_TO(0,10000,3) {
complex pos = cexp(I*_i)*(100+10*_i*_i*0.01);
PARTICLE(
.sprite = "youmu_slice",
.color = RGBA(1, 1, 1, 0),
.pos = e->pos+pos,
.draw_rule = youmu_particle_slice_draw,
.rule = youmu_particle_slice_logic,
.flags = PFLAG_NOREFLECT,
.timeout = 100,
.angle = carg(pos),
.layer = LAYER_PARTICLE_HIGH | 0x1,
);
}
return 1;
}
static int youmu_asymptotic(Projectile *p, int t) {
if(t < 0) {
return ACTION_ACK;
}
p->angle = carg(p->args[0]);
p->args[1] *= 0.8;
p->pos += p->args[0] * (p->args[1] + 1);
youmu_homing_trail(p, cexp(I*p->angle), 5);
return 1;
}
static void youmu_haunting_power_shot(Player *plr, int p) {
int d = -2;
double spread = 0.5 * (1 + 0.25 * sin(global.frames/10.0));
double speed = 8;
if(2 * plr->power / 100 < p || (global.frames + d * p) % 12) {
return;
}
float np = (float)p / (2 * plr->power / 100);
for(int sign = -1; sign < 2; sign += 2) {
complex dir = cexp(I*carg(sign*p*spread-speed*I));
PROJECTILE(
.sprite = "hghost",
.pos = plr->pos,
.rule = youmu_asymptotic,
.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,
.damage = 30,
.shader = "sprite_default",
);
}
}
static void youmu_haunting_shot(Player *plr) {
youmu_common_shot(plr);
if(player_should_shoot(plr, true)) {
if(plr->inputflags & INFLAG_FOCUS) {
int pwr = plr->power / 100;
if(!(global.frames % (45 - 4 * pwr))) {
int pcnt = 11 + pwr * 4;
int pdmg = 120 - 18 * 4 * (1 - pow(1 - pwr / 4.0, 1.5));
complex aim = 0.75;
PROJECTILE("youhoming", plr->pos, RGB(1, 1, 1), youmu_trap,
.args = { -30.0*I, 120, pcnt+pdmg*I, aim },
.type = PlrProj+1000,
.shader = "sprite_youmu_charged_shot",
.shader_params = &(ShaderCustomParams){{ 0, 1 }},
);
}
} else {
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,
.damage = 120,
.shader = "sprite_default",
);
}
for(int p = 1; p <= 2*PLR_MAX_POWER/100; ++p) {
youmu_haunting_power_shot(plr, p);
}
}
}
}
static void youmu_haunting_bomb(Player *plr) {
play_sound("bomb_youmu_b");
create_enemy_p(&plr->slaves, global.plr.pos, ENEMY_BOMB, YoumuSlash, youmu_slash, 280,0,0,0);
}
static void youmu_haunting_preload(void) {
const int flags = RESF_DEFAULT;
preload_resources(RES_SPRITE, flags,
"proj/youmu",
"part/youmu_slice",
NULL);
preload_resources(RES_SHADER_PROGRAM, flags,
"sprite_youmu_charged_shot",
NULL);
preload_resources(RES_TEXTURE, flags,
"youmu_bombbg1",
NULL);
preload_resources(RES_SFX, flags | RESF_OPTIONAL,
"bomb_youmu_b",
NULL);
}
PlayerMode plrmode_youmu_b = {
.name = "Haunting Sign",
.character = &character_youmu,
.dialog = &dialog_youmu,
.shot_mode = PLR_SHOT_YOUMU_HAUNTING,
.procs = {
.property = youmu_common_property,
.bomb = youmu_haunting_bomb,
.bombbg = youmu_common_bombbg,
.shot = youmu_haunting_shot,
.preload = youmu_haunting_preload,
},
};