303 lines
7.8 KiB
C
303 lines
7.8 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#include "taisei.h"
|
|
|
|
#include "musicroom.h"
|
|
#include "resource/resource.h"
|
|
#include "resource/font.h"
|
|
#include "audio/audio.h"
|
|
#include "progress.h"
|
|
#include "common.h"
|
|
#include "options.h"
|
|
#include "renderer/api.h"
|
|
#include "video.h"
|
|
|
|
enum {
|
|
MSTATE_UNLOCKED = 1,
|
|
MSTATE_CONFIRM = 2,
|
|
MSTATE_PLAYING = 4,
|
|
|
|
MSTATE_COMMENT_BG_VISIBLE = MSTATE_UNLOCKED | MSTATE_CONFIRM | MSTATE_PLAYING,
|
|
MSTATE_COMMENT_VISIBLE = MSTATE_UNLOCKED | MSTATE_PLAYING,
|
|
MSTATE_TITLE_VISIBLE = MSTATE_UNLOCKED | MSTATE_PLAYING,
|
|
};
|
|
|
|
typedef struct MusicEntryParam {
|
|
BGM *bgm;
|
|
ShaderProgram *text_shader;
|
|
uint8_t state;
|
|
} MusicEntryParam;
|
|
|
|
typedef struct MusicRoomContext {
|
|
ResourceGroup rg;
|
|
} MusicRoomContext;
|
|
|
|
static void musicroom_logic(MenuData *m) {
|
|
float prev_selector_x = m->drawdata[0];
|
|
float prev_selector_w = m->drawdata[1];
|
|
animate_menu_list(m);
|
|
|
|
MenuEntry *cursor_entry = dynarray_get_ptr(&m->entries, m->cursor);
|
|
if(cursor_entry->arg) {
|
|
MusicEntryParam *p = cursor_entry->arg;
|
|
float selector_w = SCREEN_W - 200;
|
|
m->drawdata[0] = prev_selector_x;
|
|
m->drawdata[1] = prev_selector_w;
|
|
fapproach_asymptotic_p(&m->drawdata[0], 0.5 * selector_w, 0.1, 1e-5);
|
|
fapproach_asymptotic_p(&m->drawdata[1], 2 * selector_w, 0.1, 1e-5);
|
|
fapproach_asymptotic_p(&m->drawdata[3], (p->state & MSTATE_COMMENT_BG_VISIBLE) != 0, 0.2, 1e-5);
|
|
} else {
|
|
fapproach_asymptotic_p(&m->drawdata[3], 0, 0.2, 1e-5);
|
|
}
|
|
|
|
BGM *current_bgm = audio_bgm_current();
|
|
|
|
dynarray_foreach(&m->entries, int i, MenuEntry *e, {
|
|
MusicEntryParam *p = e->arg;
|
|
|
|
if(p) {
|
|
if(i != m->cursor && e->drawdata / 10.0 <= 0.02) {
|
|
p->state &= ~MSTATE_CONFIRM;
|
|
}
|
|
|
|
if(current_bgm && current_bgm == p->bgm) {
|
|
p->state |= MSTATE_PLAYING;
|
|
} else {
|
|
p->state &= ~MSTATE_PLAYING;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
static void musicroom_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|
if(!e->name) {
|
|
return;
|
|
}
|
|
|
|
MusicEntryParam *p = e->arg;
|
|
|
|
if(!p) {
|
|
text_draw(e->name, &(TextParams) {
|
|
.pos = { 20 - e->drawdata, 20 * i },
|
|
.shader = "text_default",
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
char buf[16];
|
|
const char *title = p->state & MSTATE_TITLE_VISIBLE ? e->name : "???????";
|
|
Color clr = *r_color_current();
|
|
TextParams tparams = {
|
|
.pos = { 20 - e->drawdata, 20 * i },
|
|
.shader_ptr = p->text_shader,
|
|
.font_ptr = res_font("standard"),
|
|
.color = &clr,
|
|
};
|
|
bool kerning_saved = font_get_kerning_enabled(tparams.font_ptr);
|
|
|
|
tparams.pos.x += text_draw("№ ", &tparams);
|
|
font_set_kerning_enabled(tparams.font_ptr, false);
|
|
snprintf(buf, sizeof(buf), " %i ", i + 1);
|
|
tparams.pos.x += text_width(tparams.font_ptr, " 99 ", 0);
|
|
tparams.align = ALIGN_RIGHT;
|
|
tparams.pos.x += text_draw(buf, &tparams);
|
|
tparams.align = ALIGN_LEFT;
|
|
font_set_kerning_enabled(tparams.font_ptr, kerning_saved);
|
|
|
|
if(!(p->state & MSTATE_TITLE_VISIBLE)) {
|
|
clr.r *= 0.5;
|
|
clr.g *= 0.5;
|
|
clr.b *= 0.5;
|
|
}
|
|
|
|
tparams.pos.x += text_draw(title, &tparams);
|
|
|
|
if(p->state & MSTATE_PLAYING) {
|
|
color_mul(&clr, RGBA(0.1, 0.6, 0.8, 0.8));
|
|
text_draw("Now playing", &(TextParams) {
|
|
.pos = { SCREEN_W - 200, 20 * i },
|
|
.shader_ptr = p->text_shader,
|
|
.align = ALIGN_RIGHT,
|
|
.color = &clr,
|
|
});
|
|
}
|
|
}
|
|
|
|
static void musicroom_draw(MenuData *m) {
|
|
r_state_push();
|
|
draw_options_menu_bg(m);
|
|
draw_menu_title(m, "Music Room");
|
|
draw_menu_list(m, 100, 100, musicroom_draw_item, SCREEN_H / GOLDEN_RATIO, NULL);
|
|
|
|
float comment_height = SCREEN_H * (1 - 1 / GOLDEN_RATIO);
|
|
float comment_alpha = (1 - menu_fade(m)) * m->drawdata[3];
|
|
float comment_offset = smoothstep(0, 1, (1 - comment_alpha)) * comment_height;
|
|
|
|
r_shader_standard_notex();
|
|
r_mat_mv_push();
|
|
r_mat_mv_translate(SCREEN_W * 0.5, SCREEN_H - comment_height * 0.5 + comment_offset, 0);
|
|
r_mat_mv_scale(SCREEN_W, comment_height, 1);
|
|
r_color4(0, 0, 0, 0.6 * comment_alpha);
|
|
r_draw_quad();
|
|
r_mat_mv_pop();
|
|
r_state_pop();
|
|
|
|
Font *const text_font = res_font("standard");
|
|
ShaderProgram *const text_shader = res_shader("text_default");
|
|
const float text_x = 50;
|
|
const float text_y = SCREEN_H - comment_height + font_get_lineskip(text_font) * 1.5 + comment_offset;
|
|
|
|
dynarray_foreach_elem(&m->entries, MenuEntry *e, {
|
|
float a = e->drawdata / 10.0 * comment_alpha;
|
|
|
|
if(a < 0.05 || !e->arg) {
|
|
continue;
|
|
}
|
|
|
|
const char *comment;
|
|
MusicEntryParam *p = e->arg;
|
|
Color *clr = RGBA(a, a, a, a);
|
|
|
|
if(p->state & MSTATE_CONFIRM) {
|
|
comment = (
|
|
"\nYou have not unlocked this track yet!\n\n"
|
|
"If you wish to hear it anyway, please select it again to confirm."
|
|
);
|
|
clr->g *= 0.3;
|
|
clr->b *= 0.2;
|
|
} else if(!(p->state & MSTATE_COMMENT_VISIBLE)) {
|
|
continue;
|
|
} else if(!(comment = bgm_get_comment(p->bgm))) {
|
|
comment = "\nNo comment available";
|
|
}
|
|
|
|
text_draw_wrapped(comment, SCREEN_W - text_x * 2, &(TextParams) {
|
|
.pos = { text_x + 0.5*(SCREEN_W - text_x * 2), text_y },
|
|
.font_ptr = text_font,
|
|
.shader_ptr = text_shader,
|
|
.color = clr,
|
|
.align = ALIGN_CENTER,
|
|
});
|
|
|
|
if(p->state & MSTATE_COMMENT_VISIBLE) {
|
|
const char *artist = bgm_get_artist(p->bgm);
|
|
|
|
if(artist) {
|
|
const char *prefix = "— ";
|
|
char buf[strlen(prefix) + strlen(artist) + 1];
|
|
strcpy(buf, prefix);
|
|
strcat(buf, artist);
|
|
|
|
text_draw(buf, &(TextParams) {
|
|
.pos = { SCREEN_W - text_x, SCREEN_H + comment_offset - font_get_lineskip(text_font) },
|
|
.font_ptr = text_font,
|
|
.shader_ptr = text_shader,
|
|
.color = RGBA(a, a, a, a),
|
|
.align = ALIGN_RIGHT,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
static void action_play_bgm(MenuData *m, void *arg) {
|
|
MusicEntryParam *p = arg;
|
|
assume(p != NULL);
|
|
|
|
if(p->state & (MSTATE_CONFIRM | MSTATE_UNLOCKED)) {
|
|
p->state &= ~MSTATE_CONFIRM;
|
|
audio_bgm_play(p->bgm, true, 0, 0);
|
|
} else if (!(p->state & MSTATE_PLAYING)) {
|
|
p->state |= MSTATE_CONFIRM;
|
|
}
|
|
}
|
|
|
|
static void add_bgm(MenuData *m, const char *bgm_name, bool preload) {
|
|
MusicRoomContext *ctx = m->context;
|
|
|
|
if(preload) {
|
|
res_group_preload(&ctx->rg, RES_BGM, RESF_OPTIONAL, bgm_name, NULL);
|
|
return;
|
|
}
|
|
|
|
BGM *bgm = res_bgm(bgm_name);
|
|
const char *title = bgm ? bgm_get_title(bgm) : NULL;
|
|
|
|
if(!title) {
|
|
title = "Unknown track";
|
|
}
|
|
|
|
auto p = ALLOC(MusicEntryParam, {
|
|
.bgm = bgm,
|
|
.text_shader = res_shader("text_default"),
|
|
});
|
|
|
|
if(progress_is_bgm_unlocked(bgm_name)) {
|
|
p->state |= MSTATE_UNLOCKED;
|
|
}
|
|
|
|
MenuEntry *e = add_menu_entry(m, title, action_play_bgm, p);
|
|
e->transition = NULL;
|
|
}
|
|
|
|
static void musicroom_free(MenuData *m) {
|
|
MusicRoomContext *ctx = m->context;
|
|
res_group_release(&ctx->rg);
|
|
mem_free(ctx);
|
|
|
|
dynarray_foreach_elem(&m->entries, MenuEntry *e, {
|
|
mem_free(e->arg);
|
|
});
|
|
}
|
|
|
|
MenuData *create_musicroom_menu(void) {
|
|
auto ctx = ALLOC(MusicRoomContext);
|
|
res_group_init(&ctx->rg);
|
|
|
|
MenuData *m = alloc_menu();
|
|
m->logic = musicroom_logic;
|
|
m->draw = musicroom_draw;
|
|
m->end = musicroom_free;
|
|
m->transition = TransFadeBlack;
|
|
m->flags = MF_Abortable;
|
|
m->context = ctx;
|
|
|
|
for(int preload = 1; preload >= 0; --preload) {
|
|
add_bgm(m, "menu", preload);
|
|
add_bgm(m, "stage1", preload);
|
|
add_bgm(m, "stage1boss", preload);
|
|
add_bgm(m, "stage2", preload);
|
|
add_bgm(m, "stage2boss", preload);
|
|
add_bgm(m, "stage3", preload);
|
|
add_bgm(m, "stage3boss", preload);
|
|
add_bgm(m, "scuttle", preload);
|
|
add_bgm(m, "stage4", preload);
|
|
add_bgm(m, "stage4boss", preload);
|
|
add_bgm(m, "stage5", preload);
|
|
add_bgm(m, "stage5boss", preload);
|
|
add_bgm(m, "bonus0", preload);
|
|
add_bgm(m, "stage6", preload);
|
|
add_bgm(m, "stage6boss_phase1", preload);
|
|
add_bgm(m, "stage6boss_phase2", preload);
|
|
add_bgm(m, "stage6boss_phase3", preload);
|
|
add_bgm(m, "ending", preload);
|
|
add_bgm(m, "credits", preload);
|
|
add_bgm(m, "gameover", preload);
|
|
}
|
|
|
|
add_menu_separator(m);
|
|
add_menu_entry(m, "Back", menu_action_close, NULL);
|
|
|
|
while(!dynarray_get(&m->entries, m->cursor).action) {
|
|
++m->cursor;
|
|
}
|
|
|
|
return m;
|
|
}
|