Add a basic music room
This commit is contained in:
parent
b314cbd7d7
commit
a63e8f4a69
62 changed files with 572 additions and 69 deletions
|
@ -1,4 +1,5 @@
|
|||
title = Divine Correction for Outliers
|
||||
artist = Tuck V
|
||||
loop = res/bgm/bonus0.ogg
|
||||
loop_point = 2.909093
|
||||
loop_point = 2.909093
|
||||
comment = The decision to arrange a character’s existing theme or to come up with something entirely new is difficult and likely one ZUN himself struggles with.\n\nI know my strengths lie in the latter, which ultimately resulted in the stormy composition usually heard at the top of the tower, but Iku has such an interesting original theme I had to at least accept the challenge.\n\nUltimately, I can’t decide if I did the original justice with this one or not, but I know it holds a special place in my heart.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
title = Existential Field
|
||||
artist = Tuck V
|
||||
intro = res/bgm/credits.ogg
|
||||
loop = res/bgm/credits.ogg
|
||||
comment = The staff roll theme.\n\nA sense of inspiration and the future is maintained from Excitation Field.\n\nOne that’s a lot gentler than a thunderstorm… perhaps a bit more like a rainbow.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
title = Dream Cycle
|
||||
artist = Tuck V
|
||||
loop = res/bgm/ending.ogg
|
||||
comment = The ending’s theme.\n\nIn summation, class, what have we learned today?\n\nKnowledge is the forbidden fruit; from it stem the powers of light and darkness. But it’s about time the protagonists have had a snack.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = Delta Concision
|
||||
artist = Tuck V
|
||||
loop = res/bgm/menu.ogg
|
||||
loop_point = 13.71
|
||||
loop_point = 13.71
|
||||
comment = An introduction to the occident.\n\nMany paths branch from one. Echoes from across the sea yield a new adventure.
|
||||
|
|
|
@ -2,3 +2,4 @@ title = Logic Bombardier
|
|||
artist = Tuck V
|
||||
loop = res/bgm/scuttle.ogg
|
||||
loop_point = 5.64
|
||||
comment = A secret theme for a most beloved character…\n\nI wanted to play with the concept of Scuttle’s motif as heard in the tunnel of light and try to apply it to a full-length song.\n\nThe results were interesting! Her theme is as much an experiment as her existence.\n\nSupposing she were to appear as a boss, I think it might sound different given a different role in the incident.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = The Fog Invites Unseen Mischief
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage1/stage.ogg
|
||||
loop_point = 6.857143
|
||||
loop_point = 6.857143
|
||||
comment = The first stage’s theme.\n\nA relaxing mist illustrated with subdued instruments. Familiar shadows stir trouble just out of view. Ripples of times past dance across Misty Lake.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = An Impish Reflection on the Water
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage1/boss.ogg
|
||||
loop_point = 3.200000
|
||||
loop_point = 3.200000
|
||||
comment = Cirno’s theme.\n\nShe’s feeling just as brave this time around, and acting a fair bit stronger.\n\nHowever, she’s really just a minor annoyance, cold and brittle. The protagonists brush off her pranks and move on.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = Treasure the Wager, Treasure the Odds
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage2/stage.ogg
|
||||
loop_point = 10.124989
|
||||
loop_point = 10.124989
|
||||
comment = The second stage’s theme.\n\nFor such a calm and serene path, there sure are a lot of troublemakers — a deceptively fast-paced theme, to be sure. One excitable example makes a brief appearance. The rhythm of this theme is meant to make you feel lucky.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
title = The Cheerful Presence of a Dark God
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage2/boss.ogg
|
||||
loop = res/bgm/stage2/boss.ogg
|
||||
comment = Hina Kagiyama’s theme.\n\nThis time, it’s all up to chance.\n\nIt’s a little merciful sometimes, but you still have to do your best, no matter what. Gambling might be a poor stress-reliever…
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
title = Fizeau's Finding - Lightray Vector
|
||||
title = Fizeau’s Finding - Lightray Vector
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage3/stage.ogg
|
||||
loop = res/bgm/stage3/stage.ogg
|
||||
comment = The third stage’s theme.\n\nIt’s usually tricky to see in the dark. It pulls you in, engulfs you, like a sense-smothering flame. But this time, it might be best to protect your eyes. A fluttering little bug feels most at home in the dead of night, and most alive in a sea of light.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = Lightningbug ~ Lightning Heart
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage3/boss.ogg
|
||||
loop_point = 1.500000
|
||||
loop_point = 1.500000
|
||||
comment = Wriggle Nightbug’s theme.\n\nA very motivated bug. She’s at the top of the world! — No, someone else is, she’s in the middle of a tunnel. Regardless, she knows she can do anything if she puts her mind to it, right?\n\nSounds a little “beach”-like in the introduction…
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = Evidence of Ki Domestication
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage4/stage.ogg
|
||||
loop_point = 32.470590
|
||||
loop_point = 32.470590
|
||||
comment = The fourth stage’s theme.\n\nAnother trip back into the orient… like a pendulum swinging back and forth.\n\nThis theme draws inspiration from a particularly elusive musician living on the other side of the sea known for the Chinese influences in his music.\n\nSmells like rust.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = Marginal Red ~ The Red Word
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage4/boss.ogg
|
||||
loop_point = 24.000000
|
||||
loop_point = 24.000000
|
||||
comment = Kurumi’s theme.\n\nOld faces and new dreams. She’s taken a new residence, something a little more comfortable than a Lake of Blood, but this confrontation is anything but. A mix of fears make the walls feel like they’re closing in.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
title = Speak Not Falsely ~ Excitation Field
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage5/stage.ogg
|
||||
loop = res/bgm/stage5/stage.ogg
|
||||
comment = The fifth stage’s theme.\n\nAnd back again… how did that song go? “No reason to get excited…”\n\nBut in honesty, this is a combination of Eastern and Western influences now. If it were like a pendulum up to this point, then this theme is like an oscillating electric current.\n\nThose who climb risk a long fall, so look to the top and don’t make a single dishonest step.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
title = A Prayer Worth Three Hundred Coulombs
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage5/boss.ogg
|
||||
loop_point = 12.80
|
||||
loop_point = 12.80
|
||||
comment = Iku Nagae’s theme.\n\nThis song went through the wringer. Perhaps that’s appropriate for what the protagonists have to do. Imagine climbing a tower in the middle of a vicious thunderstorm and try not to feel exhausted.\n\nIku provides the first tribulation.
|
||||
|
|
|
@ -2,3 +2,4 @@ title = Summit of Revelations
|
|||
artist = Tuck V
|
||||
loop = res/bgm/stage6/stage.ogg
|
||||
loop_point = 13.71
|
||||
comment = The sixth stage’s theme.\n\nAt the top of the tower, you can see the world, and at what risk?\n\nA grand architectural marvel may take thousands of years to complete… this song was no different! But the end result feels like a great discovery.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
title = Cosmological Battle ~ The Vacuum Catastrophe
|
||||
artist = Tuck V
|
||||
loop = res/bgm/stage6/boss_phase1.ogg
|
||||
comment = Elly’s first theme.\n\nAn old friend. I always found myself inspired by the idea of a completely inconsequential character from forgotten times climbing to great heights after fading into obscurity.\n\nShe may have been a lowly gate guard the last time you saw her, but the apple’s fallen on her head. She’s a guard of a different kind of gate now.
|
||||
|
|
|
@ -2,3 +2,4 @@ title = Deified Emergent Property ~ Ambivalent Soul
|
|||
artist = Tuck V
|
||||
loop = res/bgm/stage6/boss_phase2.ogg
|
||||
loop_point = 1.6
|
||||
comment = Elly’s second theme.\n\nThe most frightening and wondrous powers of the world remain just out of reach to our mortal minds. But a couple of truly brilliant people just may have found the bridge between our world and theirs…\n\nElly is a new person now, so an arrange wouldn’t speak the truth.
|
||||
|
|
|
@ -2,3 +2,4 @@ title = Immutable Truth
|
|||
artist = Tuck V
|
||||
loop = res/bgm/stage6/boss_phase3.ogg
|
||||
loop_point = 35.64
|
||||
comment = Elly’s last spell.\n\nThe most ambitious and impossibly necessary tasks must encapsulate absolutely everything. Many think of science as a body of knowledge, a sealed bubble, within which all “great discoveries” have already been achieved, but the contrary could not be more true. Science is an unending series of dark curtains to pull back, each new finding more magnificent and imperceptible than the last.
|
||||
|
|
|
@ -194,10 +194,9 @@ static void load_config_files(void) {
|
|||
parse_keyvalue_file_cb(SFX_PATH_PREFIX "volumes.conf", store_sfx_volume, NULL);
|
||||
}
|
||||
|
||||
static inline char* get_bgm_desc(char *name) {
|
||||
Music *music = get_music(name);
|
||||
assert(music != NULL);
|
||||
return music->title;
|
||||
static inline char* get_bgm_title(char *name) {
|
||||
MusicMetadata *meta = get_resource_data(RES_BGM_METADATA, name, RESF_OPTIONAL);
|
||||
return meta ? meta->title : NULL;
|
||||
}
|
||||
|
||||
int get_default_sfx_volume(const char *sfx) {
|
||||
|
@ -289,7 +288,7 @@ void start_bgm(const char *name) {
|
|||
}
|
||||
|
||||
// Support drawing BGM title in game loop (only when music changed!)
|
||||
if((current_bgm.title = get_bgm_desc(current_bgm.name)) != NULL) {
|
||||
if((current_bgm.title = get_bgm_title(current_bgm.name)) != NULL) {
|
||||
current_bgm.started_at = global.frames;
|
||||
// Boss BGM title color may differ from the one at beginning of stage
|
||||
current_bgm.isboss = strendswith(current_bgm.name, "boss");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "resource/sfx.h"
|
||||
#include "resource/bgm.h"
|
||||
#include "resource/bgm_metadata.h"
|
||||
|
||||
#define LOOPTIMEOUTFRAMES 10
|
||||
#define DEFAULT_SFX_VOLUME 100
|
||||
|
@ -35,8 +36,8 @@ typedef struct Sound {
|
|||
} Sound;
|
||||
|
||||
typedef struct Music {
|
||||
char *title;
|
||||
MusicImpl *impl;
|
||||
MusicMetadata *meta;
|
||||
} Music;
|
||||
|
||||
typedef struct CurrentBGM {
|
||||
|
|
|
@ -883,6 +883,11 @@ void boss_finish_current_attack(Boss *boss) {
|
|||
if(p) {
|
||||
++p->num_cleared;
|
||||
}
|
||||
|
||||
// HACK
|
||||
if(boss->current->info == &stage5_spells.extra.overload) {
|
||||
stage_unlock_bgm("bonus0");
|
||||
}
|
||||
} else if(boss->current->type != AT_ExtraSpell) {
|
||||
boss->failed_spells++;
|
||||
}
|
||||
|
|
|
@ -440,6 +440,7 @@ static RenderFrameAction credits_render_frame(void *arg) {
|
|||
|
||||
static void credits_end_loop(void *ctx) {
|
||||
credits_free();
|
||||
progress_unlock_bgm("credits");
|
||||
run_call_chain(&credits.cc, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -281,6 +281,7 @@ static void ending_loop_end(void *ctx) {
|
|||
CallChain cc = e->cc;
|
||||
free_ending(e);
|
||||
free(e);
|
||||
progress_unlock_bgm("ending");
|
||||
run_call_chain(&cc, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -203,12 +203,13 @@ void draw_menu_title(MenuData *m, const char *title) {
|
|||
});
|
||||
}
|
||||
|
||||
void draw_menu_list(MenuData *m, float x, float y, void (*draw)(void*, int, int)) {
|
||||
void draw_menu_list(MenuData *m, float x, float y, void (*draw)(MenuEntry*, int, int), float scroll_threshold) {
|
||||
r_mat_push();
|
||||
float offset = ((((m->ecount+5) * 20) > SCREEN_H)? min(0, SCREEN_H * 0.7 - y - m->drawdata[2]) : 0);
|
||||
float offset = smoothmin(0, scroll_threshold * 0.8 - y - m->drawdata[2], 80);
|
||||
r_mat_translate(x, y + offset, 0);
|
||||
|
||||
draw_menu_selector(m->drawdata[0], m->drawdata[2], m->drawdata[1], 34, m->frames);
|
||||
ShaderProgram *text_shader = r_shader_get("text_default");
|
||||
|
||||
for(int i = 0; i < m->ecount; ++i) {
|
||||
MenuEntry *e = &(m->entries[i]);
|
||||
|
@ -235,10 +236,10 @@ void draw_menu_list(MenuData *m, float x, float y, void (*draw)(void*, int, int)
|
|||
if(draw && i < m->ecount-1) {
|
||||
draw(e, i, m->ecount);
|
||||
} else if(e->name) {
|
||||
ShaderProgram *sh_prev = r_shader_current();
|
||||
r_shader("text_default");
|
||||
text_draw(e->name, &(TextParams) { .pos = { 20 - e->drawdata, 20*i } });
|
||||
r_shader_ptr(sh_prev);
|
||||
text_draw(e->name, &(TextParams) {
|
||||
.pos = { 20 - e->drawdata, 20*i },
|
||||
.shader_ptr = text_shader,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,7 +248,7 @@ void draw_menu_list(MenuData *m, float x, float y, void (*draw)(void*, int, int)
|
|||
|
||||
void animate_menu_list_entry(MenuData *m, int i) {
|
||||
MenuEntry *e = &(m->entries[i]);
|
||||
e->drawdata += 0.2 * (10*(i == m->cursor) - e->drawdata);
|
||||
fapproach_asymptotic_p(&e->drawdata, 10 * (i == m->cursor), 0.2, 1e-4);
|
||||
}
|
||||
|
||||
void animate_menu_list_entries(MenuData *m) {
|
||||
|
@ -260,9 +261,9 @@ void animate_menu_list(MenuData *m) {
|
|||
MenuEntry *s = m->entries + m->cursor;
|
||||
int w = text_width(get_font("standard"), s->name, 0);
|
||||
|
||||
m->drawdata[0] += (10 + w/2.0 - m->drawdata[0])/10.0;
|
||||
m->drawdata[1] += (w*2 - m->drawdata[1])/10.0;
|
||||
m->drawdata[2] += (20*m->cursor - m->drawdata[2])/10.0;
|
||||
fapproach_asymptotic_p(&m->drawdata[0], 10 + w * 0.5, 0.1, 1e-5);
|
||||
fapproach_asymptotic_p(&m->drawdata[1], w * 2, 0.1, 1e-5);
|
||||
fapproach_asymptotic_p(&m->drawdata[2], 20 * m->cursor, 0.1, 1e-5);
|
||||
|
||||
animate_menu_list_entries(m);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ void start_game(MenuData *m, void *arg);
|
|||
void start_game_no_difficulty_menu(MenuData *m, void *arg);
|
||||
void draw_menu_selector(float x, float y, float w, float h, float t);
|
||||
void draw_menu_title(MenuData *m, const char *title);
|
||||
void draw_menu_list(MenuData *m, float x, float y, void (*draw)(void*, int, int));
|
||||
void draw_menu_list(MenuData *m, float x, float y, void (*draw)(MenuEntry*, int, int), float scroll_threshold);
|
||||
void animate_menu_list(MenuData *m);
|
||||
void animate_menu_list_entries(MenuData *m);
|
||||
void animate_menu_list_entry(MenuData *m, int i);
|
||||
|
|
|
@ -109,6 +109,7 @@ MenuData* create_main_menu(void) {
|
|||
add_menu_entry(m, "Select Stage", menu_action_enter_stagemenu, NULL);
|
||||
#endif
|
||||
add_menu_entry(m, "Replays", menu_action_enter_replayview, NULL);
|
||||
add_menu_entry(m, "Music Room", menu_action_enter_musicroom, NULL);
|
||||
add_menu_entry(m, "Options", menu_action_enter_options, NULL);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
add_menu_entry(m, "Quit", menu_action_close, NULL)->transition = TransFadeBlack;
|
||||
|
@ -119,6 +120,7 @@ MenuData* create_main_menu(void) {
|
|||
spell_practice_entry = m->entries + 3;
|
||||
main_menu_update_practice_menus();
|
||||
|
||||
progress_unlock_bgm("menu");
|
||||
start_bgm("menu");
|
||||
|
||||
return m;
|
||||
|
@ -134,7 +136,7 @@ void draw_main_menu(MenuData *menu) {
|
|||
draw_sprite(150.5, 100, "menu/logo");
|
||||
|
||||
r_mat_push();
|
||||
r_mat_translate(0, SCREEN_H-270, 0);
|
||||
r_mat_translate(0, SCREEN_H - 15 - 35 * menu->ecount, 0);
|
||||
draw_menu_selector(50 + menu->drawdata[1]/2, menu->drawdata[2], 1.5 * menu->drawdata[1], 64, menu->frames);
|
||||
r_shader("text_default");
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ menu_src = files(
|
|||
'ingamemenu.c',
|
||||
'mainmenu.c',
|
||||
'menu.c',
|
||||
'musicroom.c',
|
||||
'options.c',
|
||||
'replayview.c',
|
||||
'savereplay.c',
|
||||
|
|
166
src/menu/musicroom.c
Normal file
166
src/menu/musicroom.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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@alienslab.net>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "musicroom.h"
|
||||
#include "resource/resource.h"
|
||||
#include "resource/bgm_metadata.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"
|
||||
|
||||
static void musicroom_logic(MenuData *m) {
|
||||
animate_menu_list(m);
|
||||
fapproach_asymptotic_p(&m->drawdata[3], m->entries[m->cursor].arg != NULL, 0.2, 1e-5);
|
||||
}
|
||||
|
||||
static void musicroom_draw_item(MenuEntry *e, int i, int cnt) {
|
||||
if(!e->name) {
|
||||
return;
|
||||
}
|
||||
|
||||
text_draw(e->name, &(TextParams) {
|
||||
.pos = { 20 - e->drawdata, 20 * i },
|
||||
.shader = "text_default",
|
||||
});
|
||||
|
||||
if(
|
||||
e->arg &&
|
||||
current_bgm.music &&
|
||||
current_bgm.music->meta &&
|
||||
current_bgm.music->meta == get_resource_data(RES_BGM_METADATA, e->arg, RESF_OPTIONAL)
|
||||
) {
|
||||
text_draw("Now playing", &(TextParams) {
|
||||
.pos = { SCREEN_W - 200, 20 * i },
|
||||
.shader = "text_default",
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = RGBA(0.1, 0.6, 0.8, 0.8),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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_push();
|
||||
r_mat_translate(SCREEN_W * 0.5, SCREEN_H - comment_height * 0.5 + comment_offset, 0);
|
||||
r_mat_scale(SCREEN_W, comment_height, 1);
|
||||
r_color4(0, 0, 0, 0.6 * comment_alpha);
|
||||
r_draw_quad();
|
||||
r_mat_pop();
|
||||
r_state_pop();
|
||||
|
||||
Font *const text_font = get_font("standard");
|
||||
ShaderProgram *const text_shader = r_shader_get("text_default");
|
||||
const float text_x = 50;
|
||||
const float text_y = SCREEN_H - comment_height + font_get_lineskip(text_font) * 1.5 + comment_offset;
|
||||
|
||||
for(int i = 0; i < m->ecount; ++i) {
|
||||
MenuEntry *e = m->entries + i;
|
||||
float a = e->drawdata / 10.0 * comment_alpha;
|
||||
|
||||
if(a < 0.05 || !e->arg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *comment;
|
||||
MusicMetadata *meta = get_resource_data(RES_BGM_METADATA, e->arg, RESF_OPTIONAL);
|
||||
|
||||
if(meta && meta->comment) {
|
||||
comment = meta->comment;
|
||||
} else {
|
||||
comment = "No comment available";
|
||||
}
|
||||
|
||||
text_draw_wrapped(comment, SCREEN_W - text_x * 2, &(TextParams) {
|
||||
.pos = { text_x, text_y },
|
||||
.font_ptr = text_font,
|
||||
.shader_ptr = text_shader,
|
||||
.color = RGBA(a, a, a, a),
|
||||
});
|
||||
|
||||
if(meta->artist) {
|
||||
const char *prefix = "— ";
|
||||
char buf[strlen(prefix) + strlen(meta->artist) + 1];
|
||||
strcpy(buf, prefix);
|
||||
strcat(buf, meta->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) {
|
||||
preload_resource(RES_BGM, arg, RESF_OPTIONAL);
|
||||
start_bgm(arg);
|
||||
}
|
||||
|
||||
static void add_bgm(MenuData *m, const char *bgm) {
|
||||
if(progress_is_bgm_unlocked(bgm)) {
|
||||
MusicMetadata *meta = get_resource_data(RES_BGM_METADATA, bgm, RESF_OPTIONAL | RESF_PRELOAD);
|
||||
MenuEntry *e = add_menu_entry(m, (meta && meta->title) ? meta->title : "Unknown track", action_play_bgm, (void*)bgm);
|
||||
e->transition = NULL;
|
||||
} else {
|
||||
add_menu_entry(m, "???????", NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
MenuData* create_musicroom_menu(void) {
|
||||
MenuData *m = alloc_menu();
|
||||
m->logic = musicroom_logic;
|
||||
m->draw = musicroom_draw;
|
||||
m->transition = TransMenuDark;
|
||||
m->flags = MF_Abortable;
|
||||
|
||||
add_bgm(m, "menu");
|
||||
add_bgm(m, "stage1");
|
||||
add_bgm(m, "stage1boss");
|
||||
add_bgm(m, "stage2");
|
||||
add_bgm(m, "stage2boss");
|
||||
add_bgm(m, "stage3");
|
||||
add_bgm(m, "stage3boss");
|
||||
add_bgm(m, "scuttle");
|
||||
add_bgm(m, "stage4");
|
||||
add_bgm(m, "stage4boss");
|
||||
add_bgm(m, "stage5");
|
||||
add_bgm(m, "stage5boss");
|
||||
add_bgm(m, "bonus0");
|
||||
add_bgm(m, "stage6");
|
||||
add_bgm(m, "stage6boss_phase1");
|
||||
add_bgm(m, "stage6boss_phase2");
|
||||
add_bgm(m, "stage6boss_phase3");
|
||||
add_bgm(m, "ending");
|
||||
add_bgm(m, "credits");
|
||||
add_menu_separator(m);
|
||||
add_menu_entry(m, "Back", menu_action_close, NULL);
|
||||
|
||||
while(!m->entries[m->cursor].action) {
|
||||
++m->cursor;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
18
src/menu/musicroom.h
Normal file
18
src/menu/musicroom.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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@alienslab.net>.
|
||||
*/
|
||||
|
||||
#ifndef IGUARD_menu_musicroom_h
|
||||
#define IGUARD_menu_musicroom_h
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
MenuData* create_musicroom_menu(void);
|
||||
|
||||
#endif // IGUARD_menu_musicroom_h
|
|
@ -346,7 +346,7 @@ static void replayview_draw(MenuData *m) {
|
|||
draw_options_menu_bg(m);
|
||||
draw_menu_title(m, "Replays");
|
||||
|
||||
draw_menu_list(m, 50, 100, replayview_drawitem);
|
||||
draw_menu_list(m, 50, 100, replayview_drawitem, SCREEN_H);
|
||||
|
||||
if(ctx->submenu) {
|
||||
ctx->submenu->draw(ctx->submenu);
|
||||
|
|
|
@ -12,11 +12,12 @@
|
|||
#include "common.h"
|
||||
#include "options.h"
|
||||
#include "global.h"
|
||||
#include "video.h"
|
||||
|
||||
static void draw_spell_menu(MenuData *m) {
|
||||
draw_options_menu_bg(m);
|
||||
draw_menu_title(m, "Spell Practice");
|
||||
draw_menu_list(m, 100, 100, NULL);
|
||||
draw_menu_list(m, 100, 100, NULL, SCREEN_H);
|
||||
}
|
||||
|
||||
MenuData* create_spell_menu(void) {
|
||||
|
|
|
@ -12,11 +12,12 @@
|
|||
#include "common.h"
|
||||
#include "options.h"
|
||||
#include "global.h"
|
||||
#include "video.h"
|
||||
|
||||
static void draw_stgpract_menu(MenuData *m) {
|
||||
draw_options_menu_bg(m);
|
||||
draw_menu_title(m, "Stage Practice");
|
||||
draw_menu_list(m, 100, 100, NULL);
|
||||
draw_menu_list(m, 100, 100, NULL, SCREEN_H);
|
||||
}
|
||||
|
||||
MenuData* create_stgpract_menu(Difficulty diff) {
|
||||
|
|
|
@ -14,11 +14,12 @@
|
|||
#include "stage.h"
|
||||
#include "stageselect.h"
|
||||
#include "common.h"
|
||||
#include "video.h"
|
||||
|
||||
static void draw_stage_menu(MenuData *m) {
|
||||
draw_options_menu_bg(m);
|
||||
draw_menu_title(m, "Select Stage");
|
||||
draw_menu_list(m, 100, 100, NULL);
|
||||
draw_menu_list(m, 100, 100, NULL, SCREEN_H);
|
||||
}
|
||||
|
||||
MenuData* create_stage_menu(void) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "spellpractice.h"
|
||||
#include "stagepractice.h"
|
||||
#include "difficultyselect.h"
|
||||
#include "musicroom.h"
|
||||
#include "global.h"
|
||||
#include "submenus.h"
|
||||
|
||||
|
@ -56,3 +57,7 @@ static void stgpract_do_choose_stage(CallChainResult ccr) {
|
|||
enter_menu(create_stgpract_menu(progress.game_settings.difficulty), NO_CALLCHAIN);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_action_enter_musicroom(MenuData *menu, void *arg) {
|
||||
enter_menu(create_musicroom_menu(), NO_CALLCHAIN);
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@ void menu_action_enter_stagemenu(MenuData *menu, void *arg);
|
|||
void menu_action_enter_replayview(MenuData *menu, void *arg);
|
||||
void menu_action_enter_spellpractice(MenuData *menu, void *arg);
|
||||
void menu_action_enter_stagepractice(MenuData *menu, void *arg);
|
||||
void menu_action_enter_musicroom(MenuData *menu, void *arg);
|
||||
|
||||
#endif // IGUARD_menu_submenus_h
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
|
||||
- PCMD_GAME_VERSION
|
||||
Sets the game version this file was last written with
|
||||
|
||||
- PCMD_UNLOCK_BGMS
|
||||
Unlocks BGMs in the music room
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -247,6 +251,12 @@ static void progress_read(SDL_RWops *file) {
|
|||
}
|
||||
break;
|
||||
|
||||
case PCMD_UNLOCK_BGMS:
|
||||
if(progress_read_verify_cmd_size(vfile, cmd, cmdsize, sizeof(uint64_t))) {
|
||||
progress.unlocked_bgms |= SDL_ReadLE64(vfile);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warn("Unknown command %x (%u bytes). Will preserve as-is and not interpret", cmd, cmdsize);
|
||||
|
||||
|
@ -565,6 +575,20 @@ static void progress_write_cmd_game_version(SDL_RWops *vfile, void **arg) {
|
|||
taisei_version_write(vfile, &v);
|
||||
}
|
||||
|
||||
//
|
||||
// PCMD_UNLOCK_BGMS
|
||||
//
|
||||
|
||||
static void progress_prepare_cmd_unlock_bgms(size_t *bufsize, void **arg) {
|
||||
*bufsize += CMD_HEADER_SIZE + sizeof(uint64_t);
|
||||
}
|
||||
|
||||
static void progress_write_cmd_unlock_bgms(SDL_RWops *vfile, void **arg) {
|
||||
SDL_WriteU8(vfile, PCMD_UNLOCK_BGMS);
|
||||
SDL_WriteLE16(vfile, sizeof(uint64_t));
|
||||
SDL_WriteLE64(vfile, progress.unlocked_bgms);
|
||||
}
|
||||
|
||||
//
|
||||
// Copy unhandled commands from the original file
|
||||
//
|
||||
|
@ -610,6 +634,7 @@ static void progress_write(SDL_RWops *file) {
|
|||
{progress_prepare_cmd_endings, progress_write_cmd_endings, NULL},
|
||||
{progress_prepare_cmd_game_settings, progress_write_cmd_game_settings, NULL},
|
||||
// {progress_prepare_cmd_test, progress_write_cmd_test, NULL},
|
||||
{progress_prepare_cmd_unlock_bgms, progress_write_cmd_unlock_bgms, NULL},
|
||||
{progress_prepare_cmd_unknown, progress_write_cmd_unknown, NULL},
|
||||
{NULL}
|
||||
};
|
||||
|
@ -645,7 +670,7 @@ static void progress_write(SDL_RWops *file) {
|
|||
SDL_RWwrite(file, &cs, 4, 1);
|
||||
|
||||
if(!SDL_RWwrite(file, buf, bufsize, 1)) {
|
||||
log_fatal("SDL_RWwrite() failed: %s", SDL_GetError());
|
||||
log_error("SDL_RWwrite() failed: %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -653,6 +678,8 @@ static void progress_write(SDL_RWops *file) {
|
|||
SDL_RWclose(vfile);
|
||||
}
|
||||
|
||||
#define PROGRESS_UNLOCK_ALL
|
||||
|
||||
#ifdef PROGRESS_UNLOCK_ALL
|
||||
static void progress_unlock_all(void) {
|
||||
StageInfo *stg;
|
||||
|
@ -665,6 +692,8 @@ static void progress_unlock_all(void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
progress.unlocked_bgms = UINT64_MAX;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -736,3 +765,47 @@ uint32_t progress_times_any_good_ending_achieved(void) {
|
|||
|
||||
return x;
|
||||
}
|
||||
|
||||
static ProgressBGMID progress_bgm_id(const char *bgm) {
|
||||
static const char* map[] = {
|
||||
[PBGM_MENU] = "menu",
|
||||
[PBGM_STAGE1] = "stage1",
|
||||
[PBGM_STAGE1_BOSS] = "stage1boss",
|
||||
[PBGM_STAGE2] = "stage2",
|
||||
[PBGM_STAGE2_BOSS] = "stage2boss",
|
||||
[PBGM_STAGE3] = "stage3",
|
||||
[PBGM_STAGE3_BOSS] = "stage3boss",
|
||||
[PBGM_STAGE4] = "stage4",
|
||||
[PBGM_STAGE4_BOSS] = "stage4boss",
|
||||
[PBGM_STAGE5] = "stage5",
|
||||
[PBGM_STAGE5_BOSS] = "stage5boss",
|
||||
[PBGM_STAGE6] = "stage6",
|
||||
[PBGM_STAGE6_BOSS1] = "stage6boss_phase1",
|
||||
[PBGM_STAGE6_BOSS2] = "stage6boss_phase2",
|
||||
[PBGM_STAGE6_BOSS3] = "stage6boss_phase3",
|
||||
[PBGM_ENDING] = "ending",
|
||||
[PBGM_CREDITS] = "credits",
|
||||
[PBGM_BONUS0] = "bonus0",
|
||||
[PBGM_BONUS1] = "scuttle",
|
||||
};
|
||||
|
||||
for(int i = 0; i < ARRAY_SIZE(map); ++i) {
|
||||
if(!strcmp(map[i], bgm)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
static inline uint64_t progress_bgm_bit(ProgressBGMID id) {
|
||||
return (UINT64_C(1) << id);
|
||||
}
|
||||
|
||||
bool progress_is_bgm_unlocked(const char *name) {
|
||||
return progress.unlocked_bgms & progress_bgm_bit(progress_bgm_id(name));
|
||||
}
|
||||
|
||||
void progress_unlock_bgm(const char *name) {
|
||||
progress.unlocked_bgms |= progress_bgm_bit(progress_bgm_id(name));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#endif
|
||||
|
||||
typedef enum ProgfileCommand {
|
||||
// do not reorder this!
|
||||
// Do not reorder this!
|
||||
|
||||
PCMD_UNLOCK_STAGES = 0x00,
|
||||
PCMD_UNLOCK_STAGES_WITH_DIFFICULTY = 0x01,
|
||||
|
@ -31,23 +31,48 @@ typedef enum ProgfileCommand {
|
|||
PCMD_ENDINGS = 0x04,
|
||||
PCMD_GAME_SETTINGS = 0x05,
|
||||
PCMD_GAME_VERSION = 0x06,
|
||||
PCMD_UNLOCK_BGMS = 0x07,
|
||||
} ProgfileCommand;
|
||||
|
||||
typedef struct StageProgress {
|
||||
// keep this struct small if you can
|
||||
// see stage_get_progress_from_info() in stage.c for more information
|
||||
|
||||
uint unlocked : 1;
|
||||
// Keep this struct small if you can
|
||||
// See stage_get_progress_from_info() in stage.c for more information
|
||||
|
||||
uint32_t num_played;
|
||||
uint32_t num_cleared;
|
||||
uchar unlocked : 1;
|
||||
} StageProgress;
|
||||
|
||||
typedef enum ProgressBGMID {
|
||||
// Do not reorder! Append at the end only, reserve space if removing.
|
||||
|
||||
PBGM_MENU,
|
||||
PBGM_STAGE1,
|
||||
PBGM_STAGE1_BOSS,
|
||||
PBGM_STAGE2,
|
||||
PBGM_STAGE2_BOSS,
|
||||
PBGM_STAGE3,
|
||||
PBGM_STAGE3_BOSS,
|
||||
PBGM_STAGE4,
|
||||
PBGM_STAGE4_BOSS,
|
||||
PBGM_STAGE5,
|
||||
PBGM_STAGE5_BOSS,
|
||||
PBGM_STAGE6,
|
||||
PBGM_STAGE6_BOSS1,
|
||||
PBGM_STAGE6_BOSS2,
|
||||
PBGM_STAGE6_BOSS3,
|
||||
PBGM_ENDING,
|
||||
PBGM_CREDITS,
|
||||
PBGM_BONUS0, // old Iku theme
|
||||
PBGM_BONUS1, // Scuttle theme
|
||||
} ProgressBGMID;
|
||||
|
||||
struct UnknownCmd;
|
||||
|
||||
typedef struct GlobalProgress {
|
||||
uint32_t hiscore;
|
||||
uint32_t achieved_endings[NUM_ENDINGS];
|
||||
uint64_t unlocked_bgms;
|
||||
struct UnknownCmd *unknown;
|
||||
|
||||
struct {
|
||||
|
@ -66,4 +91,7 @@ void progress_unload(void);
|
|||
uint32_t progress_times_any_ending_achieved(void);
|
||||
uint32_t progress_times_any_good_ending_achieved(void);
|
||||
|
||||
bool progress_is_bgm_unlocked(const char *name);
|
||||
void progress_unlock_bgm(const char *name);
|
||||
|
||||
#endif // IGUARD_progress_h
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "sfxbgm_common.h"
|
||||
#include "util.h"
|
||||
|
||||
static char* bgm_path(const char *name) {
|
||||
static char *bgm_path(const char *name) {
|
||||
return sfxbgm_make_path(BGM_PATH_PREFIX, name, true);
|
||||
}
|
||||
|
||||
|
@ -32,37 +32,25 @@ static MusicImpl *load_music(const char *path) {
|
|||
|
||||
static void *load_bgm_begin(const char *path, uint flags) {
|
||||
Music *mus = calloc(1, sizeof(Music));
|
||||
double loop_point = 0;
|
||||
|
||||
if(strendswith(path, ".bgm")) {
|
||||
char *intro = NULL;
|
||||
char *loop = NULL;
|
||||
char *basename = resource_util_basename(BGM_PATH_PREFIX, path);
|
||||
mus->meta = get_resource_data(RES_BGM_METADATA, basename, flags);
|
||||
free(basename);
|
||||
|
||||
if(!parse_keyvalue_file_with_spec(path, (KVSpec[]) {
|
||||
{ "artist" }, // don’t print a warning because this field is supposed to be here
|
||||
{ "title", .out_str = &mus->title },
|
||||
{ "loop", .out_str = &loop },
|
||||
{ "loop_point", .out_double = &loop_point },
|
||||
{ NULL }
|
||||
})) {
|
||||
log_error("Failed to parse bgm config '%s'", path);
|
||||
} else {
|
||||
mus->impl = load_music(loop);
|
||||
if(mus->meta) {
|
||||
mus->impl = load_music(mus->meta->loop_path);
|
||||
}
|
||||
|
||||
free(intro);
|
||||
free(loop);
|
||||
} else {
|
||||
mus->impl = load_music(path);
|
||||
}
|
||||
|
||||
if(!mus->impl) {
|
||||
free(mus->title);
|
||||
free(mus);
|
||||
mus = NULL;
|
||||
log_error("Failed to load bgm '%s'", path);
|
||||
} else if(loop_point > 0) {
|
||||
_a_backend.funcs.music_set_loop_point(mus->impl, loop_point);
|
||||
} else if(mus->meta->loop_point > 0) {
|
||||
_a_backend.funcs.music_set_loop_point(mus->impl, mus->meta->loop_point);
|
||||
}
|
||||
|
||||
return mus;
|
||||
|
@ -75,7 +63,6 @@ static void *load_bgm_end(void *opaque, const char *path, uint flags) {
|
|||
static void unload_bgm(void *vmus) {
|
||||
Music *mus = vmus;
|
||||
_a_backend.funcs.music_unload(mus->impl);
|
||||
free(mus->title);
|
||||
free(mus);
|
||||
}
|
||||
|
||||
|
|
75
src/resource/bgm_metadata.c
Normal file
75
src/resource/bgm_metadata.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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@alienslab.net>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "bgm_metadata.h"
|
||||
#include "bgm.h"
|
||||
#include "util.h"
|
||||
|
||||
static char *bgm_meta_path(const char *name) {
|
||||
return strjoin(BGM_PATH_PREFIX, name, ".bgm", NULL);
|
||||
}
|
||||
|
||||
static bool check_bgm_meta_path(const char *path) {
|
||||
return strendswith(path, ".bgm") && strstartswith(path, BGM_PATH_PREFIX);
|
||||
}
|
||||
|
||||
static void free_metadata_fields(MusicMetadata *meta) {
|
||||
free(meta->artist);
|
||||
free(meta->comment);
|
||||
free(meta->loop_path);
|
||||
free(meta->title);
|
||||
}
|
||||
|
||||
static void *load_bgm_meta_begin(const char *path, uint flags) {
|
||||
MusicMetadata meta = { 0 };
|
||||
|
||||
if(!parse_keyvalue_file_with_spec(path, (KVSpec[]) {
|
||||
{ "artist", .out_str = &meta.artist },
|
||||
{ "title", .out_str = &meta.title },
|
||||
{ "comment", .out_str = &meta.comment },
|
||||
{ "loop", .out_str = &meta.loop_path },
|
||||
{ "loop_point", .out_double = &meta.loop_point },
|
||||
{ NULL }
|
||||
})) {
|
||||
log_error("Failed to parse BGM metadata '%s'", path);
|
||||
free_metadata_fields(&meta);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(meta.comment) {
|
||||
expand_escape_sequences(meta.comment);
|
||||
}
|
||||
|
||||
return memdup(&meta, sizeof(meta));
|
||||
}
|
||||
|
||||
static void *load_bgm_meta_end(void *opaque, const char *path, uint flags) {
|
||||
return opaque;
|
||||
}
|
||||
|
||||
static void unload_bgm_meta(void *vmus) {
|
||||
MusicMetadata *meta = vmus;
|
||||
free_metadata_fields(meta);
|
||||
free(meta);
|
||||
}
|
||||
|
||||
ResourceHandler bgm_metadata_res_handler = {
|
||||
.type = RES_BGM_METADATA,
|
||||
.typename = "bgm metadata",
|
||||
.subdir = BGM_PATH_PREFIX,
|
||||
|
||||
.procs = {
|
||||
.find = bgm_meta_path,
|
||||
.check = check_bgm_meta_path,
|
||||
.begin_load = load_bgm_meta_begin,
|
||||
.end_load = load_bgm_meta_end,
|
||||
.unload = unload_bgm_meta,
|
||||
},
|
||||
};
|
26
src/resource/bgm_metadata.h
Normal file
26
src/resource/bgm_metadata.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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@alienslab.net>.
|
||||
*/
|
||||
|
||||
#ifndef IGUARD_resource_bgm_metadata_h
|
||||
#define IGUARD_resource_bgm_metadata_h
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
typedef struct MusicMetadata {
|
||||
char *title;
|
||||
char *artist;
|
||||
char *comment;
|
||||
char *loop_path;
|
||||
double loop_point;
|
||||
} MusicMetadata;
|
||||
|
||||
extern ResourceHandler bgm_metadata_res_handler;
|
||||
|
||||
#endif // IGUARD_resource_bgm_metadata_h
|
|
@ -1261,7 +1261,7 @@ void text_wrap(Font *font, const char *src, double width, char *buf, size_t bufs
|
|||
strcpy(src_copy, src);
|
||||
*buf = 0;
|
||||
|
||||
while((next = strtok_r(NULL, " \t\n", &sptr))) {
|
||||
while((next = strtok_r(NULL, " \t", &sptr))) {
|
||||
int curwidth;
|
||||
|
||||
if(!*next) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
resource_src = files(
|
||||
'animation.c',
|
||||
'bgm.c',
|
||||
'bgm_metadata.c',
|
||||
'font.c',
|
||||
'model.c',
|
||||
'postprocess.c',
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "animation.h"
|
||||
#include "sfx.h"
|
||||
#include "bgm.h"
|
||||
#include "bgm_metadata.h"
|
||||
#include "shader_object.h"
|
||||
#include "shader_program.h"
|
||||
#include "model.h"
|
||||
|
@ -33,6 +34,7 @@ ResourceHandler *_handlers[] = {
|
|||
[RES_ANIM] = &animation_res_handler,
|
||||
[RES_SFX] = &sfx_res_handler,
|
||||
[RES_BGM] = &bgm_res_handler,
|
||||
[RES_BGM_METADATA] = &bgm_metadata_res_handler,
|
||||
[RES_MODEL] = &model_res_handler,
|
||||
[RES_POSTPROCESS] = &postprocess_res_handler,
|
||||
[RES_SPRITE] = &sprite_res_handler,
|
||||
|
|
|
@ -18,6 +18,7 @@ typedef enum ResourceType {
|
|||
RES_ANIM,
|
||||
RES_SFX,
|
||||
RES_BGM,
|
||||
RES_BGM_METADATA,
|
||||
RES_SHADER_OBJECT,
|
||||
RES_SHADER_PROGRAM,
|
||||
RES_MODEL,
|
||||
|
|
|
@ -885,3 +885,9 @@ void stage_end_loop(void* ctx) {
|
|||
run_call_chain(&s->cc, NULL);
|
||||
free(s);
|
||||
}
|
||||
|
||||
void stage_unlock_bgm(const char *bgm) {
|
||||
if(global.replaymode != REPLAY_PLAY && !global.plr.continues_used) {
|
||||
progress_unlock_bgm(bgm);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,6 +145,8 @@ void stage_set_voltage_thresholds(uint easy, uint normal, uint hard, uint lunati
|
|||
|
||||
bool stage_is_cleared(void);
|
||||
|
||||
void stage_unlock_bgm(const char *bgm);
|
||||
|
||||
#include "stages/stage1.h"
|
||||
#include "stages/stage2.h"
|
||||
#include "stages/stage3.h"
|
||||
|
|
|
@ -1236,10 +1236,12 @@ void stage1_events(void) {
|
|||
|
||||
AT(5000) {
|
||||
enemy_kill_all(&global.enemies);
|
||||
stage_unlock_bgm("stage1");
|
||||
global.boss = create_cirno();
|
||||
}
|
||||
|
||||
AT(5100) {
|
||||
stage_unlock_bgm("stage1boss");
|
||||
global.dialog = stage1_dialog_post_boss();
|
||||
}
|
||||
|
||||
|
|
|
@ -839,10 +839,12 @@ void stage2_events(void) {
|
|||
create_enemy1c(VIEWPORT_W*(0.5+0.1*sqrt(_i)*(1-2*(_i&1)))-10.0*I, 2000, BigFairy, stage2_accel_circle, 2.0*I);
|
||||
|
||||
AT(5100) {
|
||||
stage_unlock_bgm("stage2");
|
||||
global.boss = create_hina();
|
||||
}
|
||||
|
||||
AT(5180) {
|
||||
stage_unlock_bgm("stage2boss");
|
||||
global.dialog = stage2_dialog_post_boss();
|
||||
}
|
||||
|
||||
|
|
|
@ -361,6 +361,7 @@ static void stage3_spellpractice_start(void) {
|
|||
if(global.stage->spell->draw_rule == scuttle_spellbg) {
|
||||
skip_background_anim(stage3_update, 2800, &global.timer, NULL);
|
||||
global.boss = stage3_spawn_scuttle(BOSS_DEFAULT_SPAWN_POS);
|
||||
stage_unlock_bgm("scuttle");
|
||||
stage_start_bgm("scuttle");
|
||||
} else {
|
||||
skip_background_anim(stage3_update, 5300 + STAGE3_MIDBOSS_TIME, &global.timer, NULL);
|
||||
|
|
|
@ -1561,10 +1561,12 @@ void stage3_events(void) {
|
|||
}
|
||||
|
||||
AT(5300 + midboss_time) {
|
||||
stage_unlock_bgm("stage3");
|
||||
global.boss = stage3_create_boss();
|
||||
}
|
||||
|
||||
AT(5400 + midboss_time) {
|
||||
stage_unlock_bgm("stage3boss");
|
||||
global.dialog = stage3_dialog_post_boss();
|
||||
}
|
||||
|
||||
|
|
|
@ -1496,11 +1496,15 @@ void stage4_events(void) {
|
|||
FROM_TO(4800 + midboss_time, 5200 + midboss_time, 10)
|
||||
create_enemy1c(20.0*I+I*VIEWPORT_H/3*frand()+VIEWPORT_W*(_i&1), 100, Swirl, stage4_explosive, (1-2*(_i&1))*3+I);
|
||||
|
||||
AT(5300 + midboss_time)
|
||||
AT(5300 + midboss_time) {
|
||||
stage_unlock_bgm("stage4");
|
||||
global.boss = create_kurumi();
|
||||
}
|
||||
|
||||
AT(5400 + midboss_time)
|
||||
AT(5400 + midboss_time) {
|
||||
stage_unlock_bgm("stage4boss");
|
||||
global.dialog = stage4_dialog_post_boss();
|
||||
}
|
||||
|
||||
AT(5405 + midboss_time) {
|
||||
stage_finish(GAMEOVER_SCORESCREEN);
|
||||
|
|
|
@ -1308,10 +1308,12 @@ void stage5_events(void) {
|
|||
}
|
||||
|
||||
AT(6960) {
|
||||
stage_unlock_bgm("stage5");
|
||||
global.boss = create_iku();
|
||||
}
|
||||
|
||||
AT(6980) {
|
||||
stage_unlock_bgm("stage5boss");
|
||||
global.dialog = stage5_dialog_post_boss();
|
||||
}
|
||||
|
||||
|
|
|
@ -923,6 +923,7 @@ static void elly_paradigm_shift(Boss *b, int t) {
|
|||
|
||||
AT(100) {
|
||||
if(global.stage->type != STAGE_SPELL) {
|
||||
stage_unlock_bgm("stage6boss_phase1");
|
||||
stage_start_bgm("stage6boss_phase2");
|
||||
stagetext_add("Paradigm Shift!", VIEWPORT_W/2+I*(VIEWPORT_H/2+64), ALIGN_CENTER, get_font("big"), RGB(1, 1, 1), 0, 120, 10, 30);
|
||||
}
|
||||
|
@ -2837,6 +2838,7 @@ static void elly_begin_toe(Boss *b, int t) {
|
|||
|
||||
AT(1) {
|
||||
start_fall_over();
|
||||
stage_unlock_bgm("stage6boss_phase2");
|
||||
stage_start_bgm("stage6boss_phase3");
|
||||