Separate StageInfo-related APIs from game-stage code (#227)

This commit is contained in:
Andrei Alexeyev 2020-05-16 23:41:54 +03:00 committed by GitHub
parent f7763fe6f4
commit 00e4837827
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 406 additions and 306 deletions

View file

@ -16,6 +16,7 @@
#include "entity.h"
#include "util/glm.h"
#include "portrait.h"
#include "stages/stage5.h" // for unlockable bonus BGM
static void ent_draw_boss(EntityInterface *ent);
static DamageResult ent_damage_boss(EntityInterface *ent, const DamageInfo *dmg);
@ -110,7 +111,7 @@ static inline bool healthbar_style_is_radial(void) {
return config_get_int(CONFIG_HEALTHBAR_STYLE) > 0;
}
static const Color* boss_healthbar_color(AttackType atype) {
static const Color *boss_healthbar_color(AttackType atype) {
static const Color colors[] = {
[AT_Normal] = { 0.50, 0.50, 0.60, 1.00 },
[AT_Move] = { 1.00, 1.00, 1.00, 1.00 },
@ -124,11 +125,11 @@ static const Color* boss_healthbar_color(AttackType atype) {
return colors + atype;
}
static StageProgress* get_spellstage_progress(Attack *a, StageInfo **out_stginfo, bool write) {
static StageProgress *get_spellstage_progress(Attack *a, StageInfo **out_stginfo, bool write) {
if(!write || (global.replaymode == REPLAY_RECORD && global.stage->type == STAGE_STORY)) {
StageInfo *i = stage_get_by_spellcard(a->info, global.diff);
StageInfo *i = stageinfo_get_by_spellcard(a->info, global.diff);
if(i) {
StageProgress *p = stage_get_progress_from_info(i, global.diff, write);
StageProgress *p = stageinfo_get_progress(i, global.diff, write);
if(out_stginfo) {
*out_stginfo = i;

View file

@ -210,7 +210,7 @@ int cli_args(int argc, char **argv, CLIAction *a) {
case CLI_PlayReplay:
case CLI_VerifyReplay:
case CLI_SelectStage:
if(stage_get(stageid) == NULL) {
if(stageinfo_get_by_id(stageid) == NULL) {
log_fatal("Invalid stage id: %X", stageid);
}
break;

View file

@ -47,6 +47,7 @@
#include "log.h"
#include "framerate.h"
#include "renderer/api.h"
#include "stageinfo.h"
enum {
// defaults

View file

@ -14,7 +14,7 @@
#include "global.h"
#include "video.h"
#include "audio/audio.h"
#include "stage.h"
#include "stageinfo.h"
#include "menu/mainmenu.h"
#include "menu/savereplay.h"
#include "gamepad.h"
@ -45,7 +45,7 @@ static void taisei_shutdown(void) {
audio_shutdown();
video_shutdown();
gamepad_shutdown();
stage_free_array();
stageinfo_shutdown();
config_shutdown();
vfs_shutdown();
events_shutdown();
@ -213,7 +213,7 @@ int main(int argc, char **argv) {
setlocale(LC_ALL, "C");
init_log();
stage_init_array(); // cli_args depends on this
stageinfo_init(); // cli_args depends on this
// commandline arguments should be parsed as early as possible
cli_args(argc, argv, &ctx->cli); // stage_init_array goes first!
@ -223,9 +223,11 @@ int main(int argc, char **argv) {
}
if(ctx->cli.type == CLI_DumpStages) {
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
tsfprintf(stdout, "%X %s: %s\n", stg->id, stg->title, stg->subtitle);
});
}
main_quit(ctx, 0);
}
@ -373,7 +375,7 @@ static void main_singlestg(MainContext *mctx) {
CLIAction *a = &mctx->cli;
log_info("Entering stage skip mode: Stage %X", a->stageid);
StageInfo* stg = stage_get(a->stageid);
StageInfo *stg = stageinfo_get_by_id(a->stageid);
assert(stg); // properly checked before this
SingleStageContext *ctx = calloc(1, sizeof(*ctx));

View file

@ -39,7 +39,7 @@ static void start_game_internal(MenuData *menu, StageInfo *info, bool difficulty
if(info == NULL) {
global.is_practice_mode = false;
ctx->current_stage = ctx->restart_stage = dynarray_get_ptr(&stages, 0);
ctx->current_stage = ctx->restart_stage = stageinfo_get_by_id(1);
} else {
global.is_practice_mode = (info->type != STAGE_EXTRA);
ctx->current_stage = info;

View file

@ -32,9 +32,12 @@ void main_menu_update_practice_menus(void) {
spell_practice_entry->action = NULL;
stage_practice_entry->action = NULL;
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
if(stg->type == STAGE_SPELL) {
StageProgress *p = stage_get_progress_from_info(stg, D_Any, false);
StageProgress *p = stageinfo_get_progress(stg, D_Any, false);
if(p && p->unlocked) {
spell_practice_entry->action = menu_action_enter_spellpractice;
@ -44,7 +47,7 @@ void main_menu_update_practice_menus(void) {
}
} else if(stg->type == STAGE_STORY) {
for(Difficulty d = D_Easy; d <= D_Lunatic; ++d) {
StageProgress *p = stage_get_progress_from_info(stg, d, false);
StageProgress *p = stageinfo_get_progress(stg, d, false);
if(p && p->unlocked) {
stage_practice_entry->action = menu_action_enter_stagepractice;
@ -54,7 +57,7 @@ void main_menu_update_practice_menus(void) {
}
}
}
});
}
}
static void begin_main_menu(MenuData *m) {

View file

@ -84,7 +84,7 @@ static void start_replay(MenuData *menu, void *arg) {
ReplayStage *stg = rpy->stages + stagenum;
char buf[64];
if(!stage_get(stg->stage)) {
if(!stageinfo_get_by_id(stg->stage)) {
replay_destroy_events(rpy);
snprintf(buf, sizeof(buf), "Can't replay this stage: unknown stage ID %X", stg->stage);
replayview_set_submenu(menu, replayview_sub_messagebox(menu, buf));
@ -119,7 +119,7 @@ static MenuData* replayview_sub_stageselect(MenuData *parent, ReplayviewItemCont
m->context = parent->context;
for(int i = 0; i < rpy->numstages; ++i) {
add_menu_entry(m, stage_get(rpy->stages[i].stage)->title, start_replay, ictx)/*->transition = TransFadeBlack*/;
add_menu_entry(m, stageinfo_get_by_id(rpy->stages[i].stage)->title, start_replay, ictx)/*->transition = TransFadeBlack*/;
}
return m;
@ -276,7 +276,7 @@ static void replayview_drawitem(MenuEntry *e, int item, int cnt) {
case 4:
a = ALIGN_LEFT;
if(rpy->numstages == 1) {
StageInfo *stg = stage_get(rpy->stages[0].stage);
StageInfo *stg = stageinfo_get_by_id(rpy->stages[0].stage);
if(stg) {
snprintf(tmp, sizeof(tmp), "%s", stg->title);

View file

@ -31,7 +31,10 @@ MenuData* create_spell_menu(void) {
m->flags = MF_Abortable;
m->transition = TransFadeBlack;
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
if(stg->type != STAGE_SPELL) {
continue;
}
@ -40,7 +43,8 @@ MenuData* create_spell_menu(void) {
add_menu_separator(m);
}
StageProgress *p = stage_get_progress_from_info(stg, D_Any, false);
StageProgress *p = stageinfo_get_progress(stg, D_Any, false);
if(p && p->unlocked) {
snprintf(title, sizeof(title), "%s: %s", stg->title, stg->subtitle);
add_menu_entry(m, title, start_game, stg);
@ -50,7 +54,7 @@ MenuData* create_spell_menu(void) {
}
lastdiff = stg->difficulty;
});
}
add_menu_separator(m);
add_menu_entry(m, "Back", menu_action_close, NULL);

View file

@ -13,6 +13,7 @@
#include "options.h"
#include "global.h"
#include "video.h"
#include "stageinfo.h"
static void draw_stgpract_menu(MenuData *m) {
draw_options_menu_bg(m);
@ -30,12 +31,15 @@ MenuData* create_stgpract_menu(Difficulty diff) {
m->flags = MF_Abortable;
m->transition = TransFadeBlack;
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
if(stg->type != STAGE_STORY) {
break;
}
StageProgress *p = stage_get_progress_from_info(stg, diff, false);
StageProgress *p = stageinfo_get_progress(stg, diff, false);
if(p && p->unlocked) {
snprintf(title, sizeof(title), "%s: %s", stg->title, stg->subtitle);
@ -44,7 +48,7 @@ MenuData* create_stgpract_menu(Difficulty diff) {
snprintf(title, sizeof(title), "%s: ???????", stg->title);
add_menu_entry(m, title, NULL, NULL);
}
});
}
add_menu_separator(m);
add_menu_entry(m, "Back", menu_action_close, NULL);

View file

@ -15,6 +15,7 @@
#include "stageselect.h"
#include "common.h"
#include "video.h"
#include "stageinfo.h"
static void draw_stage_menu(MenuData *m) {
draw_options_menu_bg(m);
@ -33,7 +34,9 @@ MenuData* create_stage_menu(void) {
m->flags = MF_Abortable;
m->transition = TransFadeBlack;
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
Difficulty diff = stg->difficulty;
if(diff < lastdiff || (diff == D_Extra && lastdiff != D_Extra) || (diff && !lastdiff)) {
@ -44,7 +47,7 @@ MenuData* create_stage_menu(void) {
add_menu_entry(m, title, start_game, stg);
lastdiff = diff;
});
}
add_menu_separator(m);
add_menu_entry(m, "Back", menu_action_close, NULL);

View file

@ -91,6 +91,7 @@ taisei_src = files(
'replay.c',
'stage.c',
'stagedraw.c',
'stageinfo.c',
'stageobjects.c',
'stagetext.c',
'stageutils.c',

View file

@ -11,7 +11,7 @@
#include <zlib.h>
#include "progress.h"
#include "stage.h"
#include "stageinfo.h"
#include "version.h"
/*
@ -165,7 +165,7 @@ static void progress_read(SDL_RWops *file) {
switch(cmd) {
case PCMD_UNLOCK_STAGES:
while(cur < cmdsize) {
StageProgress *p = stage_get_progress(SDL_ReadLE16(vfile), D_Any, true);
StageProgress *p = stageinfo_get_progress_by_id(SDL_ReadLE16(vfile), D_Any, true);
if(p) {
p->unlocked = true;
}
@ -177,11 +177,11 @@ static void progress_read(SDL_RWops *file) {
while(cur < cmdsize) {
uint16_t stg = SDL_ReadLE16(vfile);
uint8_t dflags = SDL_ReadU8(vfile);
StageInfo *info = stage_get(stg);
StageInfo *info = stageinfo_get_by_id(stg);
for(uint diff = D_Easy; diff <= D_Lunatic; ++diff) {
if(dflags & (uint)pow(2, diff - D_Easy)) {
StageProgress *p = stage_get_progress_from_info(info, diff, true);
StageProgress *p = stageinfo_get_progress(info, diff, true);
if(p) {
p->unlocked = true;
}
@ -200,7 +200,7 @@ static void progress_read(SDL_RWops *file) {
while(cur < cmdsize) {
uint16_t stg = SDL_ReadLE16(vfile); cur += sizeof(uint16_t);
Difficulty diff = SDL_ReadU8(vfile); cur += sizeof(uint8_t);
StageProgress *p = stage_get_progress(stg, diff, true);
StageProgress *p = stageinfo_get_progress_by_id(stg, diff, true);
// assert(p != NULL);
@ -315,12 +315,15 @@ typedef struct cmd_writer_t {
static void progress_prepare_cmd_unlock_stages(size_t *bufsize, void **arg) {
int num_unlocked = 0;
dynarray_foreach_elem(&stages, StageInfo *stg, {
StageProgress *p = stage_get_progress_from_info(stg, D_Any, false);
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
StageProgress *p = stageinfo_get_progress(stg, D_Any, false);
if(p && p->unlocked) {
++num_unlocked;
}
});
}
if(num_unlocked) {
*bufsize += CMD_HEADER_SIZE;
@ -340,12 +343,15 @@ static void progress_write_cmd_unlock_stages(SDL_RWops *vfile, void **arg) {
SDL_WriteU8(vfile, PCMD_UNLOCK_STAGES);
SDL_WriteLE16(vfile, num_unlocked * 2);
dynarray_foreach_elem(&stages, StageInfo *stg, {
StageProgress *p = stage_get_progress_from_info(stg, D_Any, false);
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
StageProgress *p = stageinfo_get_progress(stg, D_Any, false);
if(p && p->unlocked) {
SDL_WriteLE16(vfile, stg->id);
}
});
}
}
//
@ -355,7 +361,10 @@ static void progress_write_cmd_unlock_stages(SDL_RWops *vfile, void **arg) {
static void progress_prepare_cmd_unlock_stages_with_difficulties(size_t *bufsize, void **arg) {
int num_unlocked = 0;
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
if(stg->difficulty) {
continue;
}
@ -363,7 +372,8 @@ static void progress_prepare_cmd_unlock_stages_with_difficulties(size_t *bufsize
bool unlocked = false;
for(Difficulty diff = D_Easy; diff <= D_Lunatic; ++diff) {
StageProgress *p = stage_get_progress_from_info(stg, diff, false);
StageProgress *p = stageinfo_get_progress(stg, diff, false);
if(p && p->unlocked) {
unlocked = true;
}
@ -372,7 +382,7 @@ static void progress_prepare_cmd_unlock_stages_with_difficulties(size_t *bufsize
if(unlocked) {
++num_unlocked;
}
});
}
if(num_unlocked) {
*bufsize += CMD_HEADER_SIZE;
@ -392,7 +402,10 @@ static void progress_write_cmd_unlock_stages_with_difficulties(SDL_RWops *vfile,
SDL_WriteU8(vfile, PCMD_UNLOCK_STAGES_WITH_DIFFICULTY);
SDL_WriteLE16(vfile, num_unlocked * 3);
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
if(stg->difficulty) {
continue;
}
@ -400,7 +413,7 @@ static void progress_write_cmd_unlock_stages_with_difficulties(SDL_RWops *vfile,
uint8_t dflags = 0;
for(Difficulty diff = D_Easy; diff <= D_Lunatic; ++diff) {
StageProgress *p = stage_get_progress_from_info(stg, diff, false);
StageProgress *p = stageinfo_get_progress(stg, diff, false);
if(p && p->unlocked) {
dflags |= (uint)pow(2, diff - D_Easy);
}
@ -410,7 +423,7 @@ static void progress_write_cmd_unlock_stages_with_difficulties(SDL_RWops *vfile,
SDL_WriteLE16(vfile, stg->id);
SDL_WriteU8(vfile, dflags);
}
});
}
}
//
@ -448,7 +461,9 @@ static void progress_prepare_cmd_stage_playinfo(size_t *bufsize, void **arg) {
struct cmd_stage_playinfo_data *data = malloc(sizeof(struct cmd_stage_playinfo_data));
memset(data, 0, sizeof(struct cmd_stage_playinfo_data));
dynarray_foreach_elem(&stages, StageInfo *stg, {
int n = stageinfo_get_num_stages();
for(int i = 0; i < n; ++i) {
StageInfo *stg = stageinfo_get_by_index(i);
Difficulty d_first, d_last;
if(stg->difficulty == D_Any) {
@ -459,7 +474,7 @@ static void progress_prepare_cmd_stage_playinfo(size_t *bufsize, void **arg) {
}
for(Difficulty d = d_first; d <= d_last; ++d) {
StageProgress *p = stage_get_progress_from_info(stg, d, false);
StageProgress *p = stageinfo_get_progress(stg, d, false);
if(p && (p->num_played || p->num_cleared)) {
struct cmd_stage_playinfo_data_elem *e = malloc(sizeof(struct cmd_stage_playinfo_data_elem));
@ -472,7 +487,7 @@ static void progress_prepare_cmd_stage_playinfo(size_t *bufsize, void **arg) {
(void)list_push(&data->elems, e);
}
}
});
}
*arg = data;

View file

@ -868,13 +868,13 @@ void replay_play(Replay *rpy, int firstidx, CallChain next) {
static void replay_do_play(CallChainResult ccr) {
ReplayContext *ctx = ccr.ctx;
ReplayStage *rstg = NULL;
StageInfo *gstg = NULL;
StageInfo *stginfo = NULL;
while(ctx->stage_idx < global.replay.numstages) {
rstg = global.replay_stage = global.replay.stages + ctx->stage_idx++;
gstg = stage_get(rstg->stage);
stginfo = stageinfo_get_by_id(rstg->stage);
if(!gstg) {
if(!stginfo) {
log_warn("Invalid stage %X in replay at %i skipped.", rstg->stage, ctx->stage_idx);
continue;
}
@ -882,11 +882,11 @@ static void replay_do_play(CallChainResult ccr) {
break;
}
if(gstg == NULL) {
if(stginfo == NULL) {
replay_do_cleanup(ccr);
} else {
global.plr.mode = plrmode_find(rstg->plr_char, rstg->plr_shot);
stage_enter(gstg, CALLCHAIN(replay_do_post_play, ctx));
stage_enter(stginfo, CALLCHAIN(replay_do_post_play, ctx));
}
}

View file

@ -25,185 +25,7 @@
#include "stageobjects.h"
#include "eventloop/eventloop.h"
#include "common_tasks.h"
#ifdef DEBUG
#define DPSTEST
#include "stages/dpstest.h"
#endif
StageInfoArray stages = { 0 };
static void add_stage(uint16_t id, StageProcs *procs, StageType type, const char *title, const char *subtitle, AttackInfo *spell, Difficulty diff) {
StageInfo *stg = dynarray_append(&stages);
stg->id = id;
stg->procs = procs;
stg->type = type;
stralloc(&stg->title, title);
stralloc(&stg->subtitle, subtitle);
stg->spell = spell;
stg->difficulty = diff;
}
static void add_spellpractice_stage(StageInfo *s, AttackInfo *a, int *spellnum, uint16_t spellbits, Difficulty diff) {
uint16_t id = spellbits | a->idmap[diff - D_Easy] | (s->id << 8);
char *title = strfmt("Spell %d", ++(*spellnum));
char *subtitle = strjoin(a->name, " ~ ", difficulty_name(diff), NULL);
add_stage(id, s->procs->spellpractice_procs, STAGE_SPELL, title, subtitle, a, diff);
free(title);
free(subtitle);
}
static void add_spellpractice_stages(int *spellnum, bool (*filter)(AttackInfo*), uint16_t spellbits) {
for(int i = 0 ;; ++i) {
StageInfo *s = dynarray_get_ptr(&stages, i);
if(s->type == STAGE_SPELL || !s->spell) {
break;
}
for(AttackInfo *a = s->spell; a->name; ++a) {
if(!filter(a)) {
continue;
}
for(Difficulty diff = D_Easy; diff < D_Easy + NUM_SELECTABLE_DIFFICULTIES; ++diff) {
if(a->idmap[diff - D_Easy] >= 0) {
add_spellpractice_stage(s, a, spellnum, spellbits, diff);
// stages may have gotten realloc'd, so we must update the pointer
s = dynarray_get_ptr(&stages, i);
}
}
}
}
}
static bool spellfilter_normal(AttackInfo *spell) {
return spell->type != AT_ExtraSpell;
}
static bool spellfilter_extra(AttackInfo *spell) {
return spell->type == AT_ExtraSpell;
}
#include "stages/corotest.h"
void stage_init_array(void) {
int spellnum = 0;
// id procs type title subtitle spells diff
add_stage(1, &stage1_procs, STAGE_STORY, "Stage 1", "Misty Lake", (AttackInfo*)&stage1_spells, D_Any);
add_stage(2, &stage2_procs, STAGE_STORY, "Stage 2", "Walk Along the Border", (AttackInfo*)&stage2_spells, D_Any);
add_stage(3, &stage3_procs, STAGE_STORY, "Stage 3", "Through the Tunnel of Light", (AttackInfo*)&stage3_spells, D_Any);
add_stage(4, &stage4_procs, STAGE_STORY, "Stage 4", "Forgotten Mansion", (AttackInfo*)&stage4_spells, D_Any);
add_stage(5, &stage5_procs, STAGE_STORY, "Stage 5", "Climbing the Tower of Babel", (AttackInfo*)&stage5_spells, D_Any);
add_stage(6, &stage6_procs, STAGE_STORY, "Stage 6", "Roof of the World", (AttackInfo*)&stage6_spells, D_Any);
#ifdef DPSTEST
add_stage(0x40|0, &stage_dpstest_single_procs, STAGE_SPECIAL, "DPS Test", "Single target", NULL, D_Normal);
add_stage(0x40|1, &stage_dpstest_multi_procs, STAGE_SPECIAL, "DPS Test", "Multiple targets", NULL, D_Normal);
add_stage(0x40|2, &stage_dpstest_boss_procs, STAGE_SPECIAL, "DPS Test", "Boss", NULL, D_Normal);
#endif
// generate spellpractice stages
add_spellpractice_stages(&spellnum, spellfilter_normal, STAGE_SPELL_BIT);
add_spellpractice_stages(&spellnum, spellfilter_extra, STAGE_SPELL_BIT | STAGE_EXTRASPELL_BIT);
#ifdef SPELL_BENCHMARK
add_spellpractice_stage(dynarray_get_ptr(&stages, 0), &stage1_spell_benchmark, &spellnum, STAGE_SPELL_BIT, D_Extra);
#endif
add_stage(0xC0, &corotest_procs, STAGE_SPECIAL, "Coroutines!", "wow such concurrency very async", NULL, D_Any);
add_stage(0xC1, &extra_procs, STAGE_SPECIAL, "Extra Stage", "Descent into Madness", NULL, D_Extra);
dynarray_compact(&stages);
#ifdef DEBUG
dynarray_foreach(&stages, int i, StageInfo *stg, {
if(stg->type == STAGE_SPELL && !(stg->id & STAGE_SPELL_BIT)) {
log_fatal("Spell stage has an ID without the spell bit set: 0x%04x", stg->id);
}
dynarray_foreach(&stages, int j, StageInfo *stg2, {
if(stg != stg2 && stg->id == stg2->id) {
log_fatal("Duplicate ID %X in stages array, indices: %i, %i", stg->id, i, j);
}
});
});
#endif
}
void stage_free_array(void) {
dynarray_foreach_elem(&stages, StageInfo *stg, {
free(stg->title);
free(stg->subtitle);
free(stg->progress);
});
dynarray_free_data(&stages);
}
StageInfo* stage_get(uint16_t n) {
dynarray_foreach_elem(&stages, StageInfo *stg, {
if(stg->id == n) {
return stg;
}
});
return NULL;
}
StageInfo* stage_get_by_spellcard(AttackInfo *spell, Difficulty diff) {
if(!spell) {
return NULL;
}
dynarray_foreach_elem(&stages, StageInfo *stg, {
if(stg->spell == spell && stg->difficulty == diff) {
return stg;
}
});
return NULL;
}
StageProgress* stage_get_progress_from_info(StageInfo *stage, Difficulty diff, bool allocate) {
// D_Any stages will have a separate StageProgress for every selectable difficulty.
// Stages with a fixed difficulty setting (spellpractice, extra stage...) obviously get just one and the diff parameter is ignored.
// This stuff must stay around until progress_save(), which happens on shutdown.
// So do NOT try to free any pointers this function returns, that will fuck everything up.
if(!stage) {
return NULL;
}
bool fixed_diff = (stage->difficulty != D_Any);
if(!fixed_diff && (diff < D_Easy || diff > D_Lunatic)) {
return NULL;
}
if(!stage->progress) {
if(!allocate) {
return NULL;
}
size_t allocsize = sizeof(StageProgress) * (fixed_diff ? 1 : NUM_SELECTABLE_DIFFICULTIES);
stage->progress = malloc(allocsize);
memset(stage->progress, 0, allocsize);
}
return stage->progress + (fixed_diff ? 0 : diff - D_Easy);
}
StageProgress* stage_get_progress(uint16_t id, Difficulty diff, bool allocate) {
return stage_get_progress_from_info(stage_get(id), diff, allocate);
}
#include "stageinfo.h"
static void stage_start(StageInfo *stage) {
global.timer = 0;
@ -680,7 +502,7 @@ void stage_finish(int gameover) {
prev_gameover != GAMEOVER_SCORESCREEN &&
(gameover == GAMEOVER_SCORESCREEN || gameover == GAMEOVER_WIN)
) {
StageProgress *p = stage_get_progress_from_info(global.stage, global.diff, true);
StageProgress *p = stageinfo_get_progress(global.stage, global.diff, true);
if(p) {
++p->num_cleared;
@ -775,7 +597,7 @@ static void stage_give_clear_bonus(const StageInfo *stage, StageClearBonus *bonu
// FIXME: this is clunky...
if(!global.is_practice_mode && stage->type == STAGE_STORY) {
StageInfo *next = stage_get(stage->id + 1);
StageInfo *next = stageinfo_get_by_id(stage->id + 1);
if(next == NULL || next->type != STAGE_STORY) {
bonus->all_clear = true;
@ -967,7 +789,7 @@ void stage_enter(StageInfo *stage, CallChain next) {
log_debug("Start time: %"PRIu64, start_time);
log_debug("Random seed: 0x%"PRIx64, seed);
StageProgress *p = stage_get_progress_from_info(stage, global.diff, true);
StageProgress *p = stageinfo_get_progress(stage, global.diff, true);
if(p) {
log_debug("You played this stage %u times", p->num_played);
@ -980,7 +802,7 @@ void stage_enter(StageInfo *stage, CallChain next) {
ReplayStage *stg = global.replay_stage;
assert(stg != NULL);
assert(stage_get(stg->stage) == stage);
assert(stageinfo_get_by_id(stg->stage) == stage);
rng_seed(&global.rand_game, stg->rng_seed);

View file

@ -19,6 +19,7 @@
#include "dialog.h"
#include "coroutine.h"
#include "dynarray.h"
#include "stageinfo.h"
/* taisei's strange macro language.
*
@ -60,51 +61,6 @@
#define FROM_TO_SND(snd,start,end,step) PLAY_FOR(snd,start,end); FROM_TO(start,end,step)
#define FROM_TO_INT_SND(snd,start,end,step,dur,istep) FROM_TO_INT(start,end,step,dur,2) { play_loop(snd); }FROM_TO_INT(start,end,step,dur,istep)
typedef void (*StageProc)(void);
typedef bool (*ShaderRule)(Framebuffer*); // true = drawn to color buffer
// two highest bits of uint16_t, WAY higher than the amount of spells in this game can ever possibly be
#define STAGE_SPELL_BIT 0x8000
#define STAGE_EXTRASPELL_BIT 0x4000
typedef enum StageType {
STAGE_STORY = 1,
STAGE_EXTRA,
STAGE_SPELL,
STAGE_SPECIAL,
} StageType;
typedef struct StageProcs StageProcs;
struct StageProcs {
StageProc begin;
StageProc preload;
StageProc end;
StageProc draw;
StageProc event;
StageProc update;
ShaderRule *shader_rules;
ShaderRule *postprocess_rules;
StageProcs *spellpractice_procs;
};
typedef struct StageInfo {
uint16_t id; // must match type of ReplayStage.stage in replay.h
StageProcs *procs;
StageType type;
char *title;
char *subtitle;
AttackInfo *spell;
Difficulty difficulty;
// Do NOT access this directly!
// Use stage_get_progress or stage_get_progress_from_info, which will lazy-initialize it and pick the correct offset.
StageProgress *progress;
} StageInfo;
typedef DYNAMIC_ARRAY(StageInfo) StageInfoArray;
extern StageInfoArray stages;
typedef struct StageClearBonus {
uint64_t base;
uint64_t lives;
@ -114,15 +70,6 @@ typedef struct StageClearBonus {
bool all_clear;
} StageClearBonus;
StageInfo* stage_get(uint16_t); // NOTE: This returns the stage BY ID, not by the array index!
StageInfo* stage_get_by_spellcard(AttackInfo *spell, Difficulty diff);
StageProgress* stage_get_progress(uint16_t id, Difficulty diff, bool allocate);
StageProgress* stage_get_progress_from_info(StageInfo *stage, Difficulty diff, bool allocate);
void stage_init_array(void);
void stage_free_array(void);
void stage_enter(StageInfo *stage, CallChain next);
void stage_finish(int gameover);
@ -164,12 +111,4 @@ DECLARE_EXTERN_TASK(stage_bookmark, { const char *name; });
#define STAGE_BOOKMARK_DELAYED(delay, name) ((void)0)
#endif
#include "stages/stage1.h"
#include "stages/stage2.h"
#include "stages/stage3.h"
#include "stages/stage4.h"
#include "stages/stage5.h"
#include "stages/stage6.h"
#include "stages/extra.h"
#endif // IGUARD_stage_h

227
src/stageinfo.c Normal file
View file

@ -0,0 +1,227 @@
/*
* 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 "stageinfo.h"
#include "stages/stage1.h"
#include "stages/stage2.h"
#include "stages/stage3.h"
#include "stages/stage4.h"
#include "stages/stage5.h"
#include "stages/stage6.h"
#include "stages/extra.h"
#ifdef DEBUG
#define DPSTEST
#include "stages/dpstest.h"
#define COROTEST
#include "stages/corotest.h"
#endif
static struct {
DYNAMIC_ARRAY(StageInfo) stages;
} stageinfo;
static void add_stage(
uint16_t id,
StageProcs *procs,
StageType type,
const char *title,
const char *subtitle,
AttackInfo *spell,
Difficulty diff
) {
StageInfo *stg = dynarray_append(&stageinfo.stages);
stg->id = id;
stg->procs = procs;
stg->type = type;
stralloc(&stg->title, title);
stralloc(&stg->subtitle, subtitle);
stg->spell = spell;
stg->difficulty = diff;
}
static void add_spellpractice_stage(
StageInfo *s,
AttackInfo *a,
int *spellnum,
uint16_t spellbits,
Difficulty diff
) {
uint16_t id = spellbits | a->idmap[diff - D_Easy] | (s->id << 8);
char *title = strfmt("Spell %d", ++(*spellnum));
char *subtitle = strjoin(a->name, " ~ ", difficulty_name(diff), NULL);
add_stage(id, s->procs->spellpractice_procs, STAGE_SPELL, title, subtitle, a, diff);
free(title);
free(subtitle);
}
static void add_spellpractice_stages(
int *spellnum,
bool (*filter)(AttackInfo*),
uint16_t spellbits
) {
for(int i = 0 ;; ++i) {
StageInfo *s = dynarray_get_ptr(&stageinfo.stages, i);
if(s->type == STAGE_SPELL || !s->spell) {
break;
}
for(AttackInfo *a = s->spell; a->name; ++a) {
if(!filter(a)) {
continue;
}
for(Difficulty diff = D_Easy; diff < D_Easy + NUM_SELECTABLE_DIFFICULTIES; ++diff) {
if(a->idmap[diff - D_Easy] >= 0) {
add_spellpractice_stage(s, a, spellnum, spellbits, diff);
// stages may have gotten realloc'd, so we must update the pointer
s = dynarray_get_ptr(&stageinfo.stages, i);
}
}
}
}
}
static bool spellfilter_normal(AttackInfo *spell) {
return spell->type != AT_ExtraSpell;
}
static bool spellfilter_extra(AttackInfo *spell) {
return spell->type == AT_ExtraSpell;
}
void stageinfo_init(void) {
int spellnum = 0;
// id procs type title subtitle spells diff
add_stage(1, &stage1_procs, STAGE_STORY, "Stage 1", "Misty Lake", (AttackInfo*)&stage1_spells, D_Any);
add_stage(2, &stage2_procs, STAGE_STORY, "Stage 2", "Walk Along the Border", (AttackInfo*)&stage2_spells, D_Any);
add_stage(3, &stage3_procs, STAGE_STORY, "Stage 3", "Through the Tunnel of Light", (AttackInfo*)&stage3_spells, D_Any);
add_stage(4, &stage4_procs, STAGE_STORY, "Stage 4", "Forgotten Mansion", (AttackInfo*)&stage4_spells, D_Any);
add_stage(5, &stage5_procs, STAGE_STORY, "Stage 5", "Climbing the Tower of Babel", (AttackInfo*)&stage5_spells, D_Any);
add_stage(6, &stage6_procs, STAGE_STORY, "Stage 6", "Roof of the World", (AttackInfo*)&stage6_spells, D_Any);
#ifdef DPSTEST
add_stage(0x40|0, &stage_dpstest_single_procs, STAGE_SPECIAL, "DPS Test", "Single target", NULL, D_Normal);
add_stage(0x40|1, &stage_dpstest_multi_procs, STAGE_SPECIAL, "DPS Test", "Multiple targets", NULL, D_Normal);
add_stage(0x40|2, &stage_dpstest_boss_procs, STAGE_SPECIAL, "DPS Test", "Boss", NULL, D_Normal);
#endif
// generate spellpractice stages
add_spellpractice_stages(&spellnum, spellfilter_normal, STAGE_SPELL_BIT);
add_spellpractice_stages(&spellnum, spellfilter_extra, STAGE_SPELL_BIT | STAGE_EXTRASPELL_BIT);
#ifdef SPELL_BENCHMARK
add_spellpractice_stage(dynarray_get_ptr(&stageinfo.stages, 0), &stage1_spell_benchmark, &spellnum, STAGE_SPELL_BIT, D_Extra);
#endif
#ifdef COROTEST
add_stage(0xC0, &corotest_procs, STAGE_SPECIAL, "Coroutines!", "wow such concurrency very async", NULL, D_Any);
#endif
add_stage(0xC1, &extra_procs, STAGE_SPECIAL, "Extra Stage", "Descent into Madness", NULL, D_Extra);
dynarray_compact(&stageinfo.stages);
#ifdef DEBUG
dynarray_foreach(&stageinfo.stages, int i, StageInfo *stg, {
if(stg->type == STAGE_SPELL && !(stg->id & STAGE_SPELL_BIT)) {
log_fatal("Spell stage has an ID without the spell bit set: 0x%04x", stg->id);
}
dynarray_foreach(&stageinfo.stages, int j, StageInfo *stg2, {
if(stg != stg2 && stg->id == stg2->id) {
log_fatal("Duplicate ID %X in stages array, indices: %i, %i", stg->id, i, j);
}
});
});
#endif
}
void stageinfo_shutdown(void) {
dynarray_foreach_elem(&stageinfo.stages, StageInfo *stg, {
free(stg->title);
free(stg->subtitle);
free(stg->progress);
});
dynarray_free_data(&stageinfo.stages);
}
size_t stageinfo_get_num_stages(void) {
return stageinfo.stages.num_elements;
}
StageInfo *stageinfo_get_by_index(size_t index) {
return dynarray_get_ptr(&stageinfo.stages, index);
}
StageInfo *stageinfo_get_by_id(uint16_t n) {
// TODO speed this up (use a hashtable maybe)
dynarray_foreach_elem(&stageinfo.stages, StageInfo *stg, {
if(stg->id == n) {
return stg;
}
});
return NULL;
}
StageInfo *stageinfo_get_by_spellcard(AttackInfo *spell, Difficulty diff) {
if(!spell) {
return NULL;
}
dynarray_foreach_elem(&stageinfo.stages, StageInfo *stg, {
if(stg->spell == spell && stg->difficulty == diff) {
return stg;
}
});
return NULL;
}
StageProgress *stageinfo_get_progress(StageInfo *stage, Difficulty diff, bool allocate) {
// D_Any stages will have a separate StageProgress for every selectable difficulty.
// Stages with a fixed difficulty setting (spellpractice, extra stage...) obviously get just one and the diff parameter is ignored.
// This stuff must stay around until progress_save(), which happens on shutdown.
// So do NOT try to free any pointers this function returns, that will fuck everything up.
bool fixed_diff = (stage->difficulty != D_Any);
if(!fixed_diff && (diff < D_Easy || diff > D_Lunatic)) {
return NULL;
}
if(!stage->progress) {
if(!allocate) {
return NULL;
}
size_t allocsize = sizeof(StageProgress) * (fixed_diff ? 1 : NUM_SELECTABLE_DIFFICULTIES);
stage->progress = malloc(allocsize);
memset(stage->progress, 0, allocsize);
}
return stage->progress + (fixed_diff ? 0 : diff - D_Easy);
}
StageProgress *stageinfo_get_progress_by_id(uint16_t id, Difficulty diff, bool allocate) {
return stageinfo_get_progress(stageinfo_get_by_id(id), diff, allocate);
}

72
src/stageinfo.h Normal file
View file

@ -0,0 +1,72 @@
/*
* 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>.
*/
#ifndef IGUARD_stageinfo_h
#define IGUARD_stageinfo_h
#include "taisei.h"
#include "renderer/api.h"
#include "difficulty.h"
#include "boss.h"
#include "progress.h"
typedef void (*StageProc)(void);
typedef bool (*ShaderRule)(Framebuffer*); // true = drawn to color buffer
// two highest bits of uint16_t, WAY higher than the amount of spells in this game can ever possibly be
#define STAGE_SPELL_BIT 0x8000
#define STAGE_EXTRASPELL_BIT 0x4000
typedef enum StageType {
STAGE_STORY = 1,
STAGE_EXTRA,
STAGE_SPELL,
STAGE_SPECIAL,
} StageType;
typedef struct StageProcs StageProcs;
struct StageProcs {
StageProc begin;
StageProc preload;
StageProc end;
StageProc draw;
StageProc event;
StageProc update;
ShaderRule *shader_rules;
ShaderRule *postprocess_rules;
StageProcs *spellpractice_procs;
};
typedef struct StageInfo {
uint16_t id; // must match type of ReplayStage.stage in replay.h
StageProcs *procs;
StageType type;
char *title;
char *subtitle;
AttackInfo *spell;
Difficulty difficulty;
// Do NOT access this directly!
// Use stage_get_progress or stage_get_progress_from_info, which will lazy-initialize it and pick the correct offset.
StageProgress *progress;
} StageInfo;
size_t stageinfo_get_num_stages(void);
StageInfo *stageinfo_get_by_index(size_t index);
StageInfo *stageinfo_get_by_id(uint16_t id);
StageInfo *stageinfo_get_by_spellcard(AttackInfo *spell, Difficulty diff);
StageProgress *stageinfo_get_progress(StageInfo *stageinfo, Difficulty diff, bool allocate);
StageProgress *stageinfo_get_progress_by_id(uint16_t id, Difficulty diff, bool allocate);
void stageinfo_init(void);
void stageinfo_shutdown(void);
#endif // IGUARD_stageinfo_h

View file

@ -11,7 +11,7 @@
#include "taisei.h"
#include "stage.h"
#include "stageinfo.h"
extern StageProcs corotest_procs;

View file

@ -11,7 +11,7 @@
#include "taisei.h"
#include "stage.h"
#include "stageinfo.h"
extern StageProcs stage_dpstest_single_procs;
extern StageProcs stage_dpstest_multi_procs;

View file

@ -16,6 +16,7 @@
#include "stagedraw.h"
#include "resource/model.h"
#include "stagetext.h"
#include "stageutils.h"
TASK(glider_bullet, {
cmplx pos; double dir; double spacing; int interval;

View file

@ -11,7 +11,7 @@
#include "taisei.h"
#include "stage.h"
#include "stageinfo.h"
extern StageProcs extra_procs;

View file

@ -11,7 +11,7 @@
#include "taisei.h"
#include "stage.h"
#include "stageinfo.h"
#if defined(DEBUG) && !defined(SPELL_BENCHMARK)
#define SPELL_BENCHMARK

View file

@ -9,6 +9,7 @@
#include "taisei.h"
#include "stage1_events.h"
#include "stage1.h"
#include "global.h"
#include "stagetext.h"
#include "common_tasks.h"

View file

@ -11,7 +11,7 @@
#include "taisei.h"
#include "stage.h"
#include "stageinfo.h"
extern struct stage2_spells_s {
// this struct must contain only fields of type AttackInfo

View file

@ -9,6 +9,7 @@
#include "taisei.h"
#include "stage2_events.h"
#include "stage2.h"
#include "global.h"
#include "stage.h"
#include "enemy.h"

View file

@ -11,7 +11,7 @@
#include "taisei.h"
#include "stage.h"
#include "stageinfo.h"
extern struct stage3_spells_s {
// this struct must contain only fields of type AttackInfo

View file

@ -9,6 +9,7 @@
#include "taisei.h"
#include "stage3_events.h"
#include "stage3.h"
#include "global.h"
#include "stage.h"
#include "enemy.h"

View file

@ -11,7 +11,7 @@
#include "taisei.h"
#include "stage.h"
#include "stageinfo.h"
extern struct stage4_spells_s {
// this struct must contain only fields of type AttackInfo

View file

@ -9,6 +9,7 @@
#include "taisei.h"
#include "stage4_events.h"
#include "stage4.h"