Skip to content

Commit

Permalink
Implement a bombshield for Yumemi
Browse files Browse the repository at this point in the history
  • Loading branch information
Akaricchi committed Oct 16, 2020
1 parent e7a9912 commit 916b120
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 5 deletions.
2 changes: 2 additions & 0 deletions resources/00-taisei.pkgdir/gfx/stagex/hex_tiles.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

format = rg8
Binary file not shown.
55 changes: 55 additions & 0 deletions resources/00-taisei.pkgdir/shader/bombshield.frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#version 330 core

#include "lib/util.glslh"
#include "interface/standard.glslh"

UNIFORM(1) sampler2D shell_tex;
UNIFORM(2) sampler2D grid_tex;
UNIFORM(3) vec2 grid_aspect;
UNIFORM(4) sampler2D code_tex;
UNIFORM(5) vec2 code_aspect;

UNIFORM(6) float time;
UNIFORM(7) float alpha;

vec2 edts(vec2 p) {
vec2 uv = p * p;
vec2 s = 2.0 + uv - uv.yx;
vec2 t = (2.0 * sqrt(2.0)) * p;
vec2 t1 = s + t;
vec2 t2 = s - t;
// t1 = abs(t1); t2 = abs(t2);
return 0.5 * (sqrt(t1) - sqrt(t2));
}

void main(void) {
vec2 uv = texCoord;
float f = distance(uv, vec2(0.5));

if(f > 0.5) {
discard;
}

f = 1.0 - 2.0 * f;
float t = time;
vec2 warped_uv = edts((2.0 - pow(f, 1.0/3.0)) * (uv - 0.5)) + 0.5;

vec2 grid = texture(grid_tex, grid_aspect * (warped_uv + vec2(t * 2.0, 0.0))).rg;

vec3 c0 = vec3(0.85 * (warped_uv.y), 0.5, 1.0 - 0.5 * warped_uv.y);
c0 = pow(texture(shell_tex, warped_uv + vec2(t * 1.3, t * 0.73)).rgb, 1.0 - c0);

vec3 c1 = texture(code_tex, code_aspect * (warped_uv + vec2(t * 2.1, t * -0.42))).rrr;
c1 *= vec3(0.2, 0.7, 1.0);

float ring = clamp(smoothstep(0.0, 0.03, f) * (1.0 - f), 0.0, 1.0);
float bg = smoothstep(0.0, 0.1, f);
float h = smoothmax(bg * grid.r, ring, grid.r * (10 - 8 * ((1 - f * f) * alpha)) + ring);

float alpha2 = alpha * alpha;

c0 *= h;
c1 *= grid.g * alpha2;

fragColor = vec4((c0 + c1) * alpha2, 0.6 * bg * alpha);
}
2 changes: 2 additions & 0 deletions resources/00-taisei.pkgdir/shader/bombshield.prog
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

objects = standardnotex.vert bombshield.frag
10 changes: 10 additions & 0 deletions resources/00-taisei.pkgdir/shader/lib/util.glslh
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ vec3 sample_normalmap(sampler2D src, vec2 uv) {
return vec3(xy, z);
}

float smoothmin(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}

float smoothmax(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (a - b) / k, 0.0, 1.0);
return mix(b, a, h) + k * h * (1.0 - h);
}

float flip_topleft_to_native(float x) {
#ifdef NATIVE_ORIGIN_BOTTOMLEFT
return 1 - x;
Expand Down
1 change: 1 addition & 0 deletions resources/00-taisei.pkgdir/shader/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ glsl_files = files(
'blur25.frag.glsl',
'blur5.frag.glsl',
'blur9.frag.glsl',
'bombshield.frag.glsl',
'boss_death.frag.glsl',
'boss_zoom.frag.glsl',
'circle_distort.frag.glsl',
Expand Down
2 changes: 1 addition & 1 deletion src/stages/entities.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include "taisei.h"

#include "extra_entities.h"
#include "stagex/entities.h"

#define ENTITIES_STAGES(X, ...) \
ENTITIES_STAGEX(X, __VA_ARGS__) \
Expand Down
1 change: 1 addition & 0 deletions src/stages/stagex/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ static bool glitchmask_draw_predicate(EntityInterface *ent) {
switch(ent->type) {
case ENT_TYPE_ID(Enemy):
case ENT_TYPE_ID(YumemiSlave):
// case ENT_TYPE_ID(BossShield):
return true;

default:
Expand Down
7 changes: 4 additions & 3 deletions src/stages/extra_entities.h → src/stages/stagex/entities.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/

#ifndef IGUARD_stages_extra_entities_h
#define IGUARD_stages_extra_entities_h
#ifndef IGUARD_stages_stagex_entities_h
#define IGUARD_stages_stagex_entities_h

#include "taisei.h"

#define ENTITIES_STAGEX(X, ...) \
X(YumemiSlave, __VA_ARGS__) \
X(BossShield, __VA_ARGS__) \

#endif // IGUARD_stages_extra_entities_h
#endif // IGUARD_stages_stagex_entities_h
2 changes: 1 addition & 1 deletion src/stages/stagex/timeline.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ TASK(boss, { Boss **out_boss; }) {
Boss *boss = stagex_spawn_yumemi(5*VIEWPORT_W/4 - 200*I);
*ARGS.out_boss = global.boss = boss;

#if 0
#if 1
Attack *opening_attack = boss_add_attack(boss, AT_Normal, "Opening", 60, 40000, NULL, NULL);
boss_add_attack_from_info(boss, &stagex_spells.boss.sierpinski, false);
boss_add_attack_from_info(boss, &stagex_spells.boss.infinity_network, false);
Expand Down
160 changes: 160 additions & 0 deletions src/stages/stagex/yumemi.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@
#include "util/glm.h"
#include "global.h"

#define BOMBSHIELD_RADIUS 128

DEFINE_ENTITY_TYPE(BossShield, {
cmplx pos;
Boss *boss;
BoxedEnemy hitbox;
float alpha;

ShaderProgram *shader;

Texture *grid_tex;
Texture *code_tex;
Texture *shell_tex;

vec2 grid_aspect;
vec2 code_aspect;

struct {
Uniform *alpha;
Uniform *time;
Uniform *code_tex;
Uniform *grid_tex;
Uniform *shell_tex;
Uniform *code_aspect;
Uniform *grid_aspect;
} uniforms;
});

static void draw_yumemi_slave(EntityInterface *ent) {
YumemiSlave *slave = ENT_CAST(ent, YumemiSlave);

Expand Down Expand Up @@ -55,6 +83,136 @@ static void draw_yumemi_slave(EntityInterface *ent) {
r_draw_sprite(&sp);
}

static void bombshield_draw(EntityInterface *ent) {
BossShield *shield = ENT_CAST(ent, BossShield);
float alpha = shield->alpha;

if(alpha <= 0) {
return;
}

float size = BOMBSHIELD_RADIUS * 2 * (2.0 - 1.0 * glm_ease_quad_out(alpha));

r_shader_ptr(shield->shader);

r_mat_mv_push();
r_mat_mv_translate(creal(shield->pos), cimag(shield->pos), 0);

if(stagex_drawing_into_glitchmask()) {
float x = rng_f32s() * 16;
float y = rng_f32s() * 16;
r_mat_mv_translate(x, y, 0);
}

r_mat_mv_scale(size, size, 1);
r_uniform_float(shield->uniforms.alpha, alpha);
r_uniform_float(shield->uniforms.time, global.frames / 60.0 * 0.2);
r_uniform_sampler(shield->uniforms.code_tex, shield->code_tex);
r_uniform_sampler(shield->uniforms.grid_tex, shield->grid_tex);
r_uniform_sampler(shield->uniforms.shell_tex, shield->shell_tex);
r_uniform_vec2_vec(shield->uniforms.code_aspect, shield->code_aspect);
r_uniform_vec2_vec(shield->uniforms.grid_aspect, shield->grid_aspect);
r_draw_quad();
r_mat_mv_pop();
}

static bool is_bombshield_actiev(BossShield *shield) {
Boss *boss = NOT_NULL(shield->boss);
return
boss_is_vulnerable(boss) &&
player_is_bomb_active(&global.plr) &&
boss->current && boss->current->type == AT_Spellcard &&
1;
}

static Enemy *get_bombshield_hitbox(BossShield *shield) {
Enemy *hitbox = ENT_UNBOX(shield->hitbox);

if(is_bombshield_actiev(shield)) {
if(!hitbox) {
hitbox = create_enemy_p(&global.enemies, 0, 1, NULL, NULL, 0, 0, 0, 0);
hitbox->flags = (
EFLAG_IMPENETRABLE |
EFLAG_INVULNERABLE |
EFLAG_NO_AUTOKILL |
EFLAG_NO_DEATH_EXPLOSION |
EFLAG_NO_HURT |
EFLAG_NO_VISUAL_CORRECTION |
0
);
hitbox->hit_radius = BOMBSHIELD_RADIUS;
shield->hitbox = ENT_BOX(hitbox);
}
} else {
if(hitbox) {
enemy_kill(hitbox);
hitbox = shield->hitbox.ent = NULL;
}
}

return hitbox;
}

TASK(yumemi_bombshield_expire, { BoxedBossShield shield; }) {
BossShield *shield = TASK_BIND(ARGS.shield);
Enemy *hitbox = ENT_UNBOX(shield->hitbox);

if(hitbox) {
enemy_kill(hitbox);
shield->hitbox.ent = NULL;
}
}

TASK(yumemi_bombshield_controller, { BoxedBoss boss; }) {
Boss *boss = TASK_BIND(ARGS.boss);
BossShield *shield = TASK_HOST_ENT(BossShield);

shield->boss = boss;
shield->ent.draw_func = bombshield_draw;
shield->ent.draw_layer = LAYER_BOSS | 0x10;

shield->shader = res_shader("bombshield");
shield->grid_tex = res_texture("stagex/hex_tiles");
shield->code_tex = res_texture("stagex/bg_binary");
shield->shell_tex = res_texture("stagex/bg");

uint w, h;

r_texture_get_size(shield->grid_tex, 0, &w, &h);
shield->grid_aspect[0] = 512.0f / (float)w;
shield->grid_aspect[1] = 512.0f / (float)h;

r_texture_get_size(shield->code_tex, 0, &w, &h);
shield->code_aspect[0] = 256.0f / (float)w;
shield->code_aspect[1] = 256.0f / (float)h;

shield->uniforms.alpha = r_shader_uniform(shield->shader, "alpha");
shield->uniforms.time = r_shader_uniform(shield->shader, "time");
shield->uniforms.code_tex = r_shader_uniform(shield->shader, "code_tex");
shield->uniforms.grid_tex = r_shader_uniform(shield->shader, "grid_tex");
shield->uniforms.shell_tex = r_shader_uniform(shield->shader, "shell_tex");
shield->uniforms.code_aspect = r_shader_uniform(shield->shader, "code_aspect");
shield->uniforms.grid_aspect = r_shader_uniform(shield->shader, "grid_aspect");

INVOKE_TASK_AFTER(&TASK_EVENTS(THIS_TASK)->finished, yumemi_bombshield_expire, ENT_BOX(shield));

for(;;YIELD) {
shield->pos = boss->pos;
Enemy *hitbox = get_bombshield_hitbox(shield);

if(hitbox) {
hitbox->pos = shield->pos;
boss->bomb_damage_multiplier = 0;
boss->shot_damage_multiplier = 0;
fapproach_p(&shield->alpha, 1.0f, 1.0/30.0f);
} else {
boss->bomb_damage_multiplier = 1;
boss->shot_damage_multiplier = 1;
fapproach_p(&shield->alpha, 0.0f, 1.0/15.0f);
}
}
}

Boss *stagex_spawn_yumemi(cmplx pos) {
Boss *yumemi = create_boss("Okazaki Yumemi", "yumemi", pos - 400 * I);
boss_set_portrait(yumemi, "yumemi", NULL, "normal");
Expand All @@ -63,6 +221,8 @@ Boss *stagex_spawn_yumemi(cmplx pos) {
yumemi->move = move_towards(pos, 0.01);
yumemi->pos = pos;

INVOKE_TASK(yumemi_bombshield_controller, ENT_BOX(yumemi));

return yumemi;
}

Expand Down

0 comments on commit 916b120

Please sign in to comment.