2010-10-12 10:55:23 +02:00
|
|
|
/*
|
2011-03-05 13:44:21 +01:00
|
|
|
* This software is licensed under the terms of the MIT-License
|
2017-02-10 23:05:22 +01:00
|
|
|
* See COPYING for further information.
|
2011-03-05 13:44:21 +01:00
|
|
|
* ---
|
2017-09-12 03:28:15 +02:00
|
|
|
* Copyright (c) 2011-2017, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2017, Andrei Alexeyev <akari@alienslab.net>.
|
2010-10-12 10:55:23 +02:00
|
|
|
*/
|
|
|
|
|
2017-11-25 20:45:11 +01:00
|
|
|
#include "taisei.h"
|
|
|
|
|
2017-03-06 01:25:59 +01:00
|
|
|
#include "util.h"
|
2010-10-12 10:55:23 +02:00
|
|
|
#include "stage.h"
|
|
|
|
|
2012-07-14 19:46:03 +02:00
|
|
|
#include <time.h>
|
2010-10-12 10:55:23 +02:00
|
|
|
#include "global.h"
|
2012-07-28 22:53:53 +02:00
|
|
|
#include "video.h"
|
2017-01-24 14:40:57 +01:00
|
|
|
#include "resource/bgm.h"
|
2012-07-14 16:37:52 +02:00
|
|
|
#include "replay.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "player.h"
|
2011-06-13 18:48:36 +02:00
|
|
|
#include "menu/ingamemenu.h"
|
2012-08-17 20:58:23 +02:00
|
|
|
#include "menu/gameovermenu.h"
|
2017-03-02 11:23:30 +01:00
|
|
|
#include "audio.h"
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
#include "log.h"
|
2017-04-02 16:06:18 +02:00
|
|
|
#include "stagetext.h"
|
2017-04-07 14:20:45 +02:00
|
|
|
#include "stagedraw.h"
|
2017-12-13 20:05:12 +01:00
|
|
|
#include "stageobjects.h"
|
2010-10-12 10:55:23 +02:00
|
|
|
|
2017-02-27 15:27:48 +01:00
|
|
|
static size_t numstages = 0;
|
|
|
|
StageInfo *stages = NULL;
|
|
|
|
|
2017-10-02 23:23:02 +02:00
|
|
|
static void add_stage(uint16_t id, StageProcs *procs, StageType type, const char *title, const char *subtitle, AttackInfo *spell, Difficulty diff) {
|
2017-02-27 15:27:48 +01:00
|
|
|
++numstages;
|
|
|
|
stages = realloc(stages, numstages * sizeof(StageInfo));
|
|
|
|
StageInfo *stg = stages + (numstages - 1);
|
|
|
|
memset(stg, 0, sizeof(StageInfo));
|
|
|
|
|
|
|
|
stg->id = id;
|
|
|
|
stg->procs = procs;
|
|
|
|
stg->type = type;
|
|
|
|
stralloc(&stg->title, title);
|
|
|
|
stralloc(&stg->subtitle, subtitle);
|
|
|
|
stg->spell = spell;
|
|
|
|
stg->difficulty = diff;
|
|
|
|
}
|
|
|
|
|
2017-03-06 01:03:06 +01:00
|
|
|
static void end_stages(void) {
|
2017-10-02 23:23:02 +02:00
|
|
|
add_stage(0, NULL, 0, NULL, NULL, NULL, 0);
|
2017-02-27 15:27:48 +01:00
|
|
|
}
|
2012-07-14 09:40:37 +02:00
|
|
|
|
2017-02-27 16:36:43 +01:00
|
|
|
static void add_spellpractice_stages(int *spellnum, bool (*filter)(AttackInfo*), uint16_t spellbits) {
|
|
|
|
for(int i = 0 ;; ++i) {
|
2017-02-27 15:27:48 +01:00
|
|
|
StageInfo *s = stages + i;
|
2017-02-15 14:04:47 +01:00
|
|
|
|
2017-02-27 16:36:43 +01:00
|
|
|
if(s->type == STAGE_SPELL) {
|
|
|
|
break;
|
2017-02-10 11:39:42 +01:00
|
|
|
}
|
|
|
|
|
2017-02-27 15:27:48 +01:00
|
|
|
for(AttackInfo *a = s->spell; a->rule; ++a) {
|
2017-02-27 16:36:43 +01:00
|
|
|
if(!filter(a)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-02-27 15:27:48 +01:00
|
|
|
for(Difficulty diff = D_Easy; diff < D_Easy + NUM_SELECTABLE_DIFFICULTIES; ++diff) {
|
|
|
|
if(a->idmap[diff - D_Easy] >= 0) {
|
2017-02-27 16:36:43 +01:00
|
|
|
uint16_t id = spellbits | a->idmap[diff - D_Easy] | (s->id << 8);
|
2017-02-10 11:39:42 +01:00
|
|
|
|
2017-09-19 16:35:51 +02:00
|
|
|
char *title = strfmt("Spell %d", ++(*spellnum));
|
2017-03-06 01:03:06 +01:00
|
|
|
char *subtitle = strjoin(a->name, " ~ ", difficulty_name(diff), NULL);
|
2017-02-27 15:27:48 +01:00
|
|
|
|
2017-10-02 23:23:02 +02:00
|
|
|
add_stage(id, s->procs->spellpractice_procs, STAGE_SPELL, title, subtitle, a, diff);
|
2017-02-27 15:27:48 +01:00
|
|
|
s = stages + i; // stages just got realloc'd, so we must update the pointer
|
2017-03-06 01:03:06 +01:00
|
|
|
|
|
|
|
free(title);
|
|
|
|
free(subtitle);
|
2017-02-27 15:27:48 +01:00
|
|
|
}
|
|
|
|
}
|
2017-02-10 11:39:42 +01:00
|
|
|
}
|
2017-02-27 15:27:48 +01:00
|
|
|
}
|
2017-02-27 16:36:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool spellfilter_normal(AttackInfo *spell) {
|
|
|
|
return spell->type != AT_ExtraSpell;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool spellfilter_extra(AttackInfo *spell) {
|
|
|
|
return spell->type == AT_ExtraSpell;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stage_init_array(void) {
|
|
|
|
int spellnum = 0;
|
|
|
|
|
2017-10-02 23:23:02 +02:00
|
|
|
// 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);
|
2017-02-27 16:36:43 +01:00
|
|
|
|
|
|
|
// generate spellpractice stages
|
|
|
|
add_spellpractice_stages(&spellnum, spellfilter_normal, STAGE_SPELL_BIT);
|
|
|
|
add_spellpractice_stages(&spellnum, spellfilter_extra, STAGE_SPELL_BIT | STAGE_EXTRASPELL_BIT);
|
2017-02-27 15:27:48 +01:00
|
|
|
|
|
|
|
end_stages();
|
2017-02-10 11:39:42 +01:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2017-02-27 15:27:48 +01:00
|
|
|
for(int i = 0; stages[i].procs; ++i) {
|
2017-02-10 11:39:42 +01:00
|
|
|
if(stages[i].type == STAGE_SPELL && !(stages[i].id & STAGE_SPELL_BIT)) {
|
2017-03-13 17:03:51 +01:00
|
|
|
log_fatal("Spell stage has an ID without the spell bit set: 0x%04x", stages[i].id);
|
2017-02-10 11:39:42 +01:00
|
|
|
}
|
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
for(int j = 0; stages[j].procs; ++j) {
|
2017-02-10 11:39:42 +01:00
|
|
|
if(i != j && stages[i].id == stages[j].id) {
|
2017-03-13 17:03:51 +01:00
|
|
|
log_fatal("Duplicate ID 0x%04x in stages array, indices: %i, %i", stages[i].id, i, j);
|
2017-02-10 11:39:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-27 15:27:48 +01:00
|
|
|
#endif
|
2017-02-10 11:39:42 +01:00
|
|
|
}
|
|
|
|
|
2017-02-15 13:53:24 +01:00
|
|
|
void stage_free_array(void) {
|
2017-02-26 13:17:48 +01:00
|
|
|
for(StageInfo *stg = stages; stg->procs; ++stg) {
|
2017-02-27 15:27:48 +01:00
|
|
|
free(stg->title);
|
|
|
|
free(stg->subtitle);
|
2017-02-23 15:05:55 +01:00
|
|
|
free(stg->progress);
|
2017-02-15 13:53:24 +01:00
|
|
|
}
|
2017-02-27 15:27:48 +01:00
|
|
|
|
|
|
|
free(stages);
|
2017-02-15 13:53:24 +01:00
|
|
|
}
|
|
|
|
|
2012-07-14 19:46:03 +02:00
|
|
|
// NOTE: This returns the stage BY ID, not by the array index!
|
2017-02-10 11:39:42 +01:00
|
|
|
StageInfo* stage_get(uint16_t n) {
|
2017-02-26 13:17:48 +01:00
|
|
|
for(StageInfo *stg = stages; stg->procs; ++stg)
|
2017-02-10 11:39:42 +01:00
|
|
|
if(stg->id == n)
|
|
|
|
return stg;
|
2012-07-14 09:40:37 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-10 23:34:48 +01:00
|
|
|
StageInfo* stage_get_by_spellcard(AttackInfo *spell, Difficulty diff) {
|
|
|
|
if(!spell)
|
|
|
|
return NULL;
|
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
for(StageInfo *stg = stages; stg->procs; ++stg)
|
2017-02-10 23:34:48 +01:00
|
|
|
if(stg->spell == spell && stg->difficulty == diff)
|
|
|
|
return stg;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-11 13:22:14 +01:00
|
|
|
StageProgress* stage_get_progress_from_info(StageInfo *stage, Difficulty diff, bool allocate) {
|
2017-02-11 12:38:50 +01:00
|
|
|
// 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) {
|
2017-02-11 13:22:14 +01:00
|
|
|
if(!allocate) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-11 12:38:50 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-02-11 13:22:14 +01:00
|
|
|
StageProgress* stage_get_progress(uint16_t id, Difficulty diff, bool allocate) {
|
|
|
|
return stage_get_progress_from_info(stage_get(id), diff, allocate);
|
2017-02-11 12:38:50 +01:00
|
|
|
}
|
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
static void stage_start(StageInfo *stage) {
|
2011-05-08 13:48:25 +02:00
|
|
|
global.timer = 0;
|
2011-06-13 18:48:36 +02:00
|
|
|
global.frames = 0;
|
|
|
|
global.game_over = 0;
|
2012-08-07 17:01:26 +02:00
|
|
|
global.shake_view = 0;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
player_stage_pre_init(&global.plr);
|
2017-02-26 13:17:48 +01:00
|
|
|
|
|
|
|
if(stage->type == STAGE_SPELL) {
|
2017-03-21 11:09:32 +01:00
|
|
|
global.plr.lives = 0;
|
2017-02-26 13:17:48 +01:00
|
|
|
global.plr.bombs = 0;
|
2017-09-11 21:09:30 +02:00
|
|
|
global.plr.power = PLR_SPELLPRACTICE_POWER;
|
|
|
|
} else if(global.is_practice_mode) {
|
|
|
|
global.plr.lives = PLR_STGPRACTICE_LIVES;
|
|
|
|
global.plr.bombs = PLR_STGPRACTICE_BOMBS;
|
|
|
|
global.plr.power = PLR_STGPRACTICE_POWER;
|
2017-02-26 13:17:48 +01:00
|
|
|
}
|
2017-03-02 11:23:30 +01:00
|
|
|
|
|
|
|
reset_sounds();
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
|
|
|
|
2017-10-10 16:06:46 +02:00
|
|
|
static bool ingame_menu_interrupts_bgm(void) {
|
|
|
|
return global.stage->type != STAGE_SPELL;
|
|
|
|
}
|
|
|
|
|
2017-12-17 01:00:06 +01:00
|
|
|
static void stage_fade_bgm(void) {
|
|
|
|
fade_bgm((FPS * FADE_TIME) / 2000.0);
|
|
|
|
}
|
|
|
|
|
2012-08-17 20:58:23 +02:00
|
|
|
void stage_pause(void) {
|
2012-08-12 20:16:40 +02:00
|
|
|
MenuData menu;
|
2017-10-03 08:03:07 +02:00
|
|
|
|
2017-04-20 23:50:33 +02:00
|
|
|
if(global.replaymode == REPLAY_PLAY) {
|
|
|
|
create_ingame_menu_replay(&menu);
|
|
|
|
} else {
|
|
|
|
create_ingame_menu(&menu);
|
|
|
|
}
|
|
|
|
|
2017-10-10 16:06:46 +02:00
|
|
|
if(ingame_menu_interrupts_bgm()) {
|
|
|
|
stop_bgm(false);
|
|
|
|
}
|
|
|
|
|
2017-10-08 07:15:43 +02:00
|
|
|
pause_sounds();
|
2017-02-24 22:58:27 +01:00
|
|
|
menu_loop(&menu);
|
2017-10-10 16:06:46 +02:00
|
|
|
|
|
|
|
if(global.game_over) {
|
|
|
|
stop_sounds();
|
|
|
|
|
|
|
|
if(ingame_menu_interrupts_bgm() || global.game_over != GAMEOVER_RESTART) {
|
2017-12-17 01:00:06 +01:00
|
|
|
stage_fade_bgm();
|
2017-10-10 16:06:46 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
resume_sounds();
|
|
|
|
resume_bgm();
|
|
|
|
}
|
2012-07-14 16:37:52 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 20:58:23 +02:00
|
|
|
void stage_gameover(void) {
|
2017-02-28 00:07:03 +01:00
|
|
|
if(global.stage->type == STAGE_SPELL && config_get_int(CONFIG_SPELLSTAGE_AUTORESTART)) {
|
|
|
|
global.game_over = GAMEOVER_RESTART;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
MenuData menu;
|
|
|
|
create_gameover_menu(&menu);
|
2017-02-26 13:17:48 +01:00
|
|
|
|
2017-10-03 08:09:24 +02:00
|
|
|
// XXX: The save_bgm and restore_bgm code is broken.
|
|
|
|
// Disabled it for now, as we don't have a gameover BGM yet
|
|
|
|
// When we do, it needs to be fixed.
|
|
|
|
// It should be probably implemented by the backend, it has a better idea
|
|
|
|
// about how to manage its own state than the audio frontend code.
|
|
|
|
|
|
|
|
/*
|
2017-02-26 13:17:48 +01:00
|
|
|
bool interrupt_bgm = (global.stage->type != STAGE_SPELL);
|
|
|
|
|
|
|
|
if(interrupt_bgm) {
|
|
|
|
save_bgm();
|
2017-10-02 04:34:51 +02:00
|
|
|
start_bgm("gameover");
|
2017-02-26 13:17:48 +01:00
|
|
|
}
|
2017-10-03 08:09:24 +02:00
|
|
|
*/
|
2017-02-26 13:17:48 +01:00
|
|
|
|
2017-10-08 07:15:43 +02:00
|
|
|
pause_sounds();
|
2017-10-10 16:06:46 +02:00
|
|
|
|
|
|
|
if(ingame_menu_interrupts_bgm()) {
|
|
|
|
stop_bgm(false);
|
|
|
|
}
|
|
|
|
|
2017-02-24 22:58:27 +01:00
|
|
|
menu_loop(&menu);
|
2017-10-08 07:15:43 +02:00
|
|
|
resume_sounds();
|
2017-02-26 13:17:48 +01:00
|
|
|
|
2017-10-03 08:09:24 +02:00
|
|
|
/*
|
2017-02-26 13:17:48 +01:00
|
|
|
if(interrupt_bgm) {
|
|
|
|
restore_bgm();
|
|
|
|
}
|
2017-10-03 08:09:24 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
resume_bgm();
|
2012-08-17 20:58:23 +02:00
|
|
|
}
|
|
|
|
|
2017-12-28 02:35:53 +01:00
|
|
|
static bool stage_input_common(SDL_Event *event, void *arg) {
|
|
|
|
TaiseiEvent type = TAISEI_EVENT(event->type);
|
|
|
|
int32_t code = event->user.code;
|
|
|
|
|
|
|
|
switch(type) {
|
|
|
|
case TE_GAME_KEY_DOWN:
|
|
|
|
switch(code) {
|
|
|
|
case KEY_STOP:
|
|
|
|
// global.game_over = GAMEOVER_DEFEAT;
|
|
|
|
stage_finish(GAMEOVER_DEFEAT);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case KEY_RESTART:
|
|
|
|
// global.game_over = GAMEOVER_RESTART;
|
|
|
|
stage_finish(GAMEOVER_RESTART);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TE_GAME_PAUSE:
|
|
|
|
stage_pause();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
bool stage_input_handler_gameplay(SDL_Event *event, void *arg) {
|
|
|
|
TaiseiEvent type = TAISEI_EVENT(event->type);
|
|
|
|
int32_t code = event->user.code;
|
|
|
|
|
2017-12-28 02:35:53 +01:00
|
|
|
if(stage_input_common(event, arg)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-08-13 17:50:28 +02:00
|
|
|
switch(type) {
|
2017-09-29 21:03:49 +02:00
|
|
|
case TE_GAME_KEY_DOWN:
|
|
|
|
if(code == KEY_HAHAIWIN) {
|
2017-02-15 18:10:56 +01:00
|
|
|
#ifdef DEBUG
|
2017-02-26 13:17:48 +01:00
|
|
|
stage_finish(GAMEOVER_WIN);
|
2017-02-15 18:10:56 +01:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-06 13:02:46 +01:00
|
|
|
#ifndef DEBUG // no cheating for peasants
|
2017-09-29 21:03:49 +02:00
|
|
|
if( code == KEY_IDDQD ||
|
|
|
|
code == KEY_POWERUP ||
|
|
|
|
code == KEY_POWERDOWN)
|
2017-03-06 13:02:46 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
player_event_with_replay(&global.plr, EV_PRESS, code);
|
2012-08-13 17:50:28 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
case TE_GAME_KEY_UP:
|
|
|
|
player_event_with_replay(&global.plr, EV_RELEASE, code);
|
2012-08-07 05:28:41 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
case TE_GAME_AXIS_LR:
|
|
|
|
player_event_with_replay(&global.plr, EV_AXIS_LR, (uint16_t)code);
|
2012-08-15 16:36:39 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
case TE_GAME_AXIS_UD:
|
|
|
|
player_event_with_replay(&global.plr, EV_AXIS_UD, (uint16_t)code);
|
2012-08-15 16:36:39 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-13 17:50:28 +02:00
|
|
|
default: break;
|
|
|
|
}
|
2017-09-29 21:03:49 +02:00
|
|
|
|
|
|
|
return false;
|
2012-08-13 17:50:28 +02:00
|
|
|
}
|
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
bool stage_input_handler_replay(SDL_Event *event, void *arg) {
|
2017-12-28 02:35:53 +01:00
|
|
|
stage_input_common(event, arg);
|
2017-09-29 21:03:49 +02:00
|
|
|
return false;
|
2012-08-13 17:50:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void replay_input(void) {
|
2017-02-10 00:24:19 +01:00
|
|
|
ReplayStage *s = global.replay_stage;
|
2012-07-14 19:46:03 +02:00
|
|
|
int i;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-29 21:03:49 +02:00
|
|
|
events_poll((EventHandler[]){
|
|
|
|
{ .proc = stage_input_handler_replay },
|
|
|
|
{NULL}
|
|
|
|
}, EFLAG_GAME);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-09 05:06:46 +01:00
|
|
|
for(i = s->playpos; i < s->numevents; ++i) {
|
2017-03-21 03:28:35 +01:00
|
|
|
ReplayEvent *e = s->events + i;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-29 15:35:40 +02:00
|
|
|
if(e->frame != global.frames)
|
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-29 15:35:40 +02:00
|
|
|
switch(e->type) {
|
2012-07-14 19:46:03 +02:00
|
|
|
case EV_OVER:
|
2012-08-04 06:37:59 +02:00
|
|
|
global.game_over = GAMEOVER_DEFEAT;
|
2012-07-14 19:46:03 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-03-21 03:28:35 +01:00
|
|
|
case EV_CHECK_DESYNC:
|
|
|
|
s->desync_check = e->value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EV_FPS:
|
|
|
|
s->fps = e->value;
|
|
|
|
break;
|
|
|
|
|
2017-10-29 23:45:24 +01:00
|
|
|
default: {
|
2017-10-31 10:48:30 +01:00
|
|
|
player_event(&global.plr, e->type, (int16_t)e->value, NULL, NULL);
|
2012-07-14 19:46:03 +02:00
|
|
|
break;
|
2017-10-29 23:45:24 +01:00
|
|
|
}
|
2012-07-14 19:46:03 +02:00
|
|
|
}
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-07 05:28:41 +02:00
|
|
|
s->playpos = i;
|
2012-07-14 19:46:03 +02:00
|
|
|
player_applymovement(&global.plr);
|
|
|
|
}
|
|
|
|
|
2012-08-10 22:08:51 +02:00
|
|
|
void stage_input(void) {
|
2017-09-29 21:03:49 +02:00
|
|
|
events_poll((EventHandler[]){
|
|
|
|
{ .proc = stage_input_handler_gameplay },
|
|
|
|
{NULL}
|
|
|
|
}, EFLAG_GAME);
|
2017-09-19 20:46:28 +02:00
|
|
|
player_fix_input(&global.plr);
|
2017-03-07 00:57:14 +01:00
|
|
|
player_applymovement(&global.plr);
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
static void stage_logic(void) {
|
2010-10-12 10:55:23 +02:00
|
|
|
player_logic(&global.plr);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-04-26 12:04:45 +02:00
|
|
|
process_enemies(&global.enemies);
|
2017-02-11 04:52:08 +01:00
|
|
|
process_projectiles(&global.projs, true);
|
2011-04-29 10:26:37 +02:00
|
|
|
process_items();
|
2011-04-24 15:39:17 +02:00
|
|
|
process_lasers();
|
2017-02-11 04:52:08 +01:00
|
|
|
process_projectiles(&global.particles, false);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-30 19:33:07 +02:00
|
|
|
update_sounds();
|
|
|
|
|
2017-11-12 18:16:15 +01:00
|
|
|
if(global.dialog) {
|
|
|
|
int to = global.dialog->messages[global.dialog->pos].timeout;
|
|
|
|
|
|
|
|
if(
|
|
|
|
(to && to >= global.frames) ||
|
|
|
|
((global.plr.inputflags & INFLAG_SKIP) && global.frames - global.dialog->page_time > 3)
|
|
|
|
) {
|
|
|
|
page_dialog(&global.dialog);
|
|
|
|
}
|
2017-11-15 07:05:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(global.boss) {
|
2012-08-19 22:48:36 +02:00
|
|
|
process_boss(&global.boss);
|
2017-11-12 18:16:15 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2010-10-12 10:55:23 +02:00
|
|
|
global.frames++;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-04-05 23:20:30 +02:00
|
|
|
if(!global.dialog && (!global.boss || boss_is_fleeing(global.boss)))
|
2012-07-14 19:46:03 +02:00
|
|
|
global.timer++;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-26 20:27:10 +01:00
|
|
|
if(global.replaymode == REPLAY_PLAY &&
|
|
|
|
global.frames == global.replay_stage->events[global.replay_stage->numevents-1].frame - FADE_TIME &&
|
2017-02-26 13:17:48 +01:00
|
|
|
global.game_over != GAMEOVER_TRANSITIONING) {
|
|
|
|
stage_finish(GAMEOVER_DEFEAT);
|
2017-02-10 00:24:19 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-05 23:02:29 +01:00
|
|
|
// BGM handling
|
2017-02-10 00:24:19 +01:00
|
|
|
if(global.dialog && global.dialog->messages[global.dialog->pos].side == BGM) {
|
2017-10-02 23:23:02 +02:00
|
|
|
stage_start_bgm(global.dialog->messages[global.dialog->pos].msg);
|
2017-02-05 23:02:29 +01:00
|
|
|
page_dialog(&global.dialog);
|
|
|
|
}
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
|
|
|
|
2017-04-06 00:46:00 +02:00
|
|
|
void stage_clear_hazards(bool force) {
|
|
|
|
for(Projectile *p = global.projs; p; p = p->next) {
|
2017-11-05 15:02:49 +01:00
|
|
|
if(p->type == EnemyProj || p->type == FakeProj)
|
2017-04-06 00:46:00 +02:00
|
|
|
p->type = DeadProj;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(Laser *l = global.lasers; l; l = l->next) {
|
|
|
|
if(!l->unclearable || force)
|
|
|
|
l->dead = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-31 14:47:20 +01:00
|
|
|
void stage_clear_hazards_instantly(bool force) {
|
|
|
|
for(Projectile *p = global.projs, *next; p; p = next) {
|
|
|
|
next = p->next;
|
|
|
|
|
2017-11-05 15:02:49 +01:00
|
|
|
if(p->type == EnemyProj || p->type == FakeProj || p->type == DeadProj) {
|
2017-10-31 14:47:20 +01:00
|
|
|
create_bpoint(p->pos);
|
|
|
|
delete_projectile(&global.projs, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: clear these instantly as well
|
|
|
|
for(Laser *l = global.lasers; l; l = l->next) {
|
|
|
|
if(!l->unclearable || force)
|
|
|
|
l->dead = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
static void stage_free(void) {
|
2011-04-26 22:39:50 +02:00
|
|
|
delete_enemies(&global.enemies);
|
2017-05-05 03:44:05 +02:00
|
|
|
delete_enemies(&global.plr.slaves);
|
2011-04-29 10:26:37 +02:00
|
|
|
delete_items();
|
2011-04-26 22:47:13 +02:00
|
|
|
delete_lasers();
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-07 17:07:29 +02:00
|
|
|
delete_projectiles(&global.projs);
|
|
|
|
delete_projectiles(&global.particles);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-06-13 18:48:36 +02:00
|
|
|
if(global.dialog) {
|
|
|
|
delete_dialog(global.dialog);
|
|
|
|
global.dialog = NULL;
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-06-13 18:48:36 +02:00
|
|
|
if(global.boss) {
|
|
|
|
free_boss(global.boss);
|
|
|
|
global.boss = NULL;
|
|
|
|
}
|
2017-04-02 16:06:18 +02:00
|
|
|
|
|
|
|
stagetext_free();
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
2011-06-25 12:41:40 +02:00
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
static void stage_finalize(void *arg) {
|
|
|
|
global.game_over = (intptr_t)arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stage_finish(int gameover) {
|
|
|
|
assert(global.game_over != GAMEOVER_TRANSITIONING);
|
|
|
|
global.game_over = GAMEOVER_TRANSITIONING;
|
|
|
|
set_transition_callback(TransFadeBlack, FADE_TIME, FADE_TIME*2, stage_finalize, (void*)(intptr_t)gameover);
|
2017-12-17 01:00:06 +01:00
|
|
|
stage_fade_bgm();
|
2017-04-06 02:58:57 +02:00
|
|
|
|
2017-12-28 02:35:53 +01:00
|
|
|
if(global.replaymode == REPLAY_PLAY || global.game_over != GAMEOVER_WIN) {
|
2017-04-06 02:58:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
StageProgress *p = stage_get_progress_from_info(global.stage, global.diff, true);
|
|
|
|
|
|
|
|
if(p) {
|
|
|
|
++p->num_cleared;
|
|
|
|
}
|
2017-02-26 13:17:48 +01:00
|
|
|
}
|
|
|
|
|
2017-03-11 04:41:57 +01:00
|
|
|
static void stage_preload(void) {
|
|
|
|
difficulty_preload();
|
|
|
|
projectiles_preload();
|
|
|
|
player_preload();
|
|
|
|
items_preload();
|
|
|
|
boss_preload();
|
|
|
|
|
|
|
|
if(global.stage->type != STAGE_SPELL)
|
|
|
|
enemies_preload();
|
|
|
|
|
|
|
|
global.stage->procs->preload();
|
|
|
|
}
|
|
|
|
|
2017-04-07 14:20:45 +02:00
|
|
|
static void display_stage_title(StageInfo *info) {
|
2017-12-14 04:48:30 +01:00
|
|
|
stagetext_add(info->title, VIEWPORT_W/2 + I * (VIEWPORT_H/2-40), AL_Center, &_fonts.mainmenu, rgb(1, 1, 1), 50, 85, 35, 35);
|
|
|
|
stagetext_add(info->subtitle, VIEWPORT_W/2 + I * (VIEWPORT_H/2), AL_Center, &_fonts.standard, rgb(1, 1, 1), 60, 85, 35, 35);
|
2017-10-02 23:23:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void stage_start_bgm(const char *bgm) {
|
2017-12-17 01:13:10 +01:00
|
|
|
char *old_title = NULL;
|
|
|
|
|
|
|
|
if(current_bgm.title && global.stage->type == STAGE_SPELL) {
|
|
|
|
old_title = strdup(current_bgm.title);
|
|
|
|
}
|
|
|
|
|
2017-10-02 23:23:02 +02:00
|
|
|
start_bgm(bgm);
|
2017-04-07 14:20:45 +02:00
|
|
|
|
2017-12-17 01:13:10 +01:00
|
|
|
if(current_bgm.title && current_bgm.started_at >= 0 && (!old_title || strcmp(current_bgm.title, old_title))) {
|
2017-10-02 02:48:34 +02:00
|
|
|
char txt[strlen(current_bgm.title) + 6];
|
2017-10-02 23:23:02 +02:00
|
|
|
snprintf(txt, sizeof(txt), "BGM: %s", current_bgm.title);
|
2017-12-14 04:48:30 +01:00
|
|
|
stagetext_add(txt, VIEWPORT_W-15 + I * (VIEWPORT_H-20), AL_Right, &_fonts.standard, rgb(1, 1, 1), 30, 85, 35, 35);
|
2017-04-07 14:20:45 +02:00
|
|
|
}
|
2017-12-17 01:13:10 +01:00
|
|
|
|
|
|
|
free(old_title);
|
2017-04-07 14:20:45 +02:00
|
|
|
}
|
|
|
|
|
2017-10-04 07:07:04 +02:00
|
|
|
typedef struct StageFrameState {
|
|
|
|
StageInfo *stage;
|
|
|
|
int transition_delay;
|
|
|
|
uint16_t last_replay_fps;
|
|
|
|
} StageFrameState;
|
|
|
|
|
2017-12-26 09:56:21 +01:00
|
|
|
static void stage_update_fps(StageFrameState *fstate) {
|
|
|
|
if(global.replaymode == REPLAY_RECORD) {
|
2017-12-26 12:07:40 +01:00
|
|
|
uint16_t replay_fps = (uint16_t)rint(global.fps.logic.fps);
|
2017-12-26 09:56:21 +01:00
|
|
|
|
|
|
|
if(replay_fps != fstate->last_replay_fps) {
|
|
|
|
replay_stage_event(global.replay_stage, global.frames, EV_FPS, replay_fps);
|
|
|
|
fstate->last_replay_fps = replay_fps;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-26 12:07:40 +01:00
|
|
|
static FrameAction stage_logic_frame(void *arg) {
|
2017-10-04 07:07:04 +02:00
|
|
|
StageFrameState *fstate = arg;
|
|
|
|
StageInfo *stage = fstate->stage;
|
|
|
|
|
2017-12-26 09:56:21 +01:00
|
|
|
if(--fstate->transition_delay) {
|
|
|
|
update_transition();
|
|
|
|
}
|
|
|
|
|
|
|
|
stage_update_fps(fstate);
|
2017-10-04 07:07:04 +02:00
|
|
|
((global.replaymode == REPLAY_PLAY) ? replay_input : stage_input)();
|
|
|
|
|
|
|
|
if(global.game_over != GAMEOVER_TRANSITIONING) {
|
|
|
|
if((!global.boss || boss_is_fleeing(global.boss)) && !global.dialog) {
|
|
|
|
stage->procs->event();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(stage->type == STAGE_SPELL && !global.boss && global.game_over != GAMEOVER_RESTART) {
|
|
|
|
stage_finish(GAMEOVER_WIN);
|
|
|
|
fstate->transition_delay = 60;
|
|
|
|
}
|
2017-11-15 04:45:41 +01:00
|
|
|
|
|
|
|
stage->procs->update();
|
2017-10-04 07:07:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
replay_stage_check_desync(global.replay_stage, global.frames, (tsrand() ^ global.plr.points) & 0xFFFF, global.replaymode);
|
|
|
|
stage_logic();
|
|
|
|
|
|
|
|
if(global.replaymode == REPLAY_RECORD && global.plr.points > progress.hiscore) {
|
|
|
|
progress.hiscore = global.plr.points;
|
|
|
|
}
|
|
|
|
|
2017-12-26 12:07:40 +01:00
|
|
|
if(global.game_over > 0) {
|
|
|
|
return LFRAME_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(global.frameskip || (global.replaymode == REPLAY_PLAY && gamekeypressed(KEY_SKIP))) {
|
|
|
|
return LFRAME_SKIP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LFRAME_WAIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FrameAction stage_render_frame(void *arg) {
|
|
|
|
StageFrameState *fstate = arg;
|
|
|
|
StageInfo *stage = fstate->stage;
|
|
|
|
|
2017-10-04 07:07:04 +02:00
|
|
|
tsrand_lock(&global.rand_game);
|
|
|
|
tsrand_switch(&global.rand_visual);
|
2017-11-15 07:05:47 +01:00
|
|
|
BEGIN_DRAW_CODE();
|
2017-10-04 07:07:04 +02:00
|
|
|
stage_draw_scene(stage);
|
2017-11-15 07:05:47 +01:00
|
|
|
END_DRAW_CODE();
|
2017-10-04 07:07:04 +02:00
|
|
|
tsrand_unlock(&global.rand_game);
|
|
|
|
tsrand_switch(&global.rand_game);
|
|
|
|
draw_transition();
|
|
|
|
|
2017-12-26 12:07:40 +01:00
|
|
|
return RFRAME_SWAP;
|
2017-10-04 07:07:04 +02:00
|
|
|
}
|
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
void stage_loop(StageInfo *stage) {
|
|
|
|
assert(stage);
|
|
|
|
assert(stage->procs);
|
2017-03-11 03:12:51 +01:00
|
|
|
assert(stage->procs->preload);
|
2017-02-26 13:17:48 +01:00
|
|
|
assert(stage->procs->begin);
|
|
|
|
assert(stage->procs->end);
|
|
|
|
assert(stage->procs->draw);
|
|
|
|
assert(stage->procs->event);
|
2017-11-15 04:45:41 +01:00
|
|
|
assert(stage->procs->update);
|
2017-02-26 13:17:48 +01:00
|
|
|
assert(stage->procs->shader_rules);
|
|
|
|
|
2012-01-06 21:52:55 +01:00
|
|
|
if(global.game_over == GAMEOVER_WIN) {
|
|
|
|
global.game_over = 0;
|
|
|
|
} else if(global.game_over) {
|
|
|
|
return;
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-10 11:39:42 +01:00
|
|
|
// I really want to separate all of the game state from the global struct sometime
|
2017-02-26 13:17:48 +01:00
|
|
|
global.stage = stage;
|
2017-02-10 11:39:42 +01:00
|
|
|
|
2017-12-13 20:05:12 +01:00
|
|
|
stage_objpools_alloc();
|
2017-03-11 04:41:57 +01:00
|
|
|
stage_preload();
|
2017-11-23 03:25:53 +01:00
|
|
|
stage_draw_preload();
|
2017-03-11 03:12:51 +01:00
|
|
|
|
2017-02-05 02:25:17 +01:00
|
|
|
uint32_t seed = (uint32_t)time(0);
|
2012-08-07 02:45:38 +02:00
|
|
|
tsrand_switch(&global.rand_game);
|
2012-07-27 19:11:45 +02:00
|
|
|
tsrand_seed_p(&global.rand_game, seed);
|
2017-02-26 13:17:48 +01:00
|
|
|
stage_start(stage);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-14 19:46:03 +02:00
|
|
|
if(global.replaymode == REPLAY_RECORD) {
|
2017-12-18 17:34:10 +01:00
|
|
|
global.replay_stage = replay_create_stage(&global.replay, stage, seed, global.diff, &global.plr);
|
|
|
|
|
|
|
|
// make sure our player state is consistent with what goes into the replay
|
|
|
|
player_init(&global.plr);
|
|
|
|
replay_stage_sync_player_state(global.replay_stage, &global.plr);
|
2017-02-10 00:24:19 +01:00
|
|
|
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_debug("Random seed: %u", seed);
|
2017-04-06 02:58:57 +02:00
|
|
|
|
|
|
|
StageProgress *p = stage_get_progress_from_info(stage, global.diff, true);
|
|
|
|
|
|
|
|
if(p) {
|
|
|
|
log_debug("You played this stage %u times", p->num_played);
|
|
|
|
log_debug("You cleared this stage %u times", p->num_cleared);
|
|
|
|
|
|
|
|
++p->num_played;
|
2017-09-11 21:09:30 +02:00
|
|
|
|
2017-10-29 23:45:24 +01:00
|
|
|
if(!global.plr.continues_used) {
|
2017-09-11 21:09:30 +02:00
|
|
|
p->unlocked = true;
|
|
|
|
}
|
2017-04-06 02:58:57 +02:00
|
|
|
}
|
2012-07-14 19:46:03 +02:00
|
|
|
} else {
|
2017-02-10 00:24:19 +01:00
|
|
|
if(!global.replay_stage) {
|
2017-03-13 17:03:51 +01:00
|
|
|
log_fatal("Attemped to replay a NULL stage");
|
2017-02-10 00:24:19 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReplayStage *stg = global.replay_stage;
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_debug("REPLAY_PLAY mode: %d events, stage: \"%s\"", stg->numevents, stage_get(stg->stage)->title);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-07 05:28:41 +02:00
|
|
|
tsrand_seed_p(&global.rand_game, stg->seed);
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_debug("Random seed: %u", stg->seed);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-15 18:34:47 +01:00
|
|
|
global.diff = stg->diff;
|
2017-10-08 13:30:51 +02:00
|
|
|
player_init(&global.plr);
|
2017-02-15 18:34:47 +01:00
|
|
|
replay_stage_sync_player_state(stg, &global.plr);
|
2012-08-07 05:28:41 +02:00
|
|
|
stg->playpos = 0;
|
2012-07-14 19:46:03 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
player_stage_post_init(&global.plr);
|
2017-02-26 13:17:48 +01:00
|
|
|
stage->procs->begin();
|
2017-12-17 01:13:10 +01:00
|
|
|
display_stage_title(stage);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-04 07:07:04 +02:00
|
|
|
StageFrameState fstate = { .stage = stage };
|
2017-12-26 12:07:40 +01:00
|
|
|
loop_at_fps(stage_logic_frame, stage_render_frame, &fstate, FPS);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-16 17:47:06 +02:00
|
|
|
if(global.replaymode == REPLAY_RECORD) {
|
2017-02-10 00:24:19 +01:00
|
|
|
replay_stage_event(global.replay_stage, global.frames, EV_OVER, 0);
|
2017-10-29 23:45:24 +01:00
|
|
|
|
|
|
|
if(global.game_over == GAMEOVER_WIN) {
|
|
|
|
global.replay_stage->flags |= REPLAY_SFLAG_CLEAR;
|
|
|
|
}
|
2012-07-16 17:47:06 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-26 13:17:48 +01:00
|
|
|
stage->procs->end();
|
|
|
|
stage_free();
|
2017-11-25 16:51:43 +01:00
|
|
|
player_free(&global.plr);
|
2012-07-27 19:11:45 +02:00
|
|
|
tsrand_switch(&global.rand_visual);
|
2017-02-28 15:38:02 +01:00
|
|
|
free_all_refs();
|
2017-12-13 20:05:12 +01:00
|
|
|
stage_objpools_free();
|
2017-10-08 07:15:43 +02:00
|
|
|
stop_sounds();
|
2011-06-25 12:41:40 +02:00
|
|
|
}
|