Merge branch 'master' into rebalance
This commit is contained in:
commit
a175c38416
7 changed files with 195 additions and 30 deletions
86
src/boss.c
86
src/boss.c
|
@ -25,11 +25,19 @@ Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
void draw_boss_text_ex(Alignment align, float x, float y, const char *text, TTF_Font *fnt, float alpha) {
|
||||
glColor4f(0,0,0,alpha);
|
||||
draw_text(align, x+1, y+1, text, fnt);
|
||||
glColor4f(1,1,1,alpha);
|
||||
draw_text(align, x, y, text, fnt);
|
||||
|
||||
if(alpha < 1) {
|
||||
glColor4f(1,1,1,1);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_boss_text(Alignment align, float x, float y, const char *text) {
|
||||
glColor4f(0,0,0,1);
|
||||
draw_text(align, x+1, y+1, text, _fonts.standard);
|
||||
glColor4f(1,1,1,1);
|
||||
draw_text(align, x, y, text, _fonts.standard);
|
||||
draw_boss_text_ex(align, x, y, text, _fonts.standard, 1);
|
||||
}
|
||||
|
||||
void spell_opening(Boss *b, int time) {
|
||||
|
@ -78,6 +86,34 @@ Color boss_healthbar_color(AttackType atype) {
|
|||
}
|
||||
}
|
||||
|
||||
static StageProgress* get_spellstage_progress(Attack *a, StageInfo **out_stginfo, bool write) {
|
||||
if(out_stginfo) {
|
||||
*out_stginfo = NULL;
|
||||
}
|
||||
|
||||
if(!write || (global.replaymode == REPLAY_RECORD && global.stage->type == STAGE_STORY)) {
|
||||
StageInfo *i = stage_get_by_spellcard(a->info, global.diff);
|
||||
if(i) {
|
||||
StageProgress *p = stage_get_progress_from_info(i, global.diff, write);
|
||||
|
||||
if(out_stginfo) {
|
||||
*out_stginfo = i;
|
||||
}
|
||||
|
||||
if(p) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
else if(a->type == AT_Spellcard || a->type == AT_ExtraSpell) {
|
||||
log_warn("FIXME: spellcard '%s' is not available in spell practice mode!", a->name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void draw_boss(Boss *boss) {
|
||||
draw_animation_p(creal(boss->pos), cimag(boss->pos) + 6*sin(global.frames/25.0), boss->anirow, boss->ani);
|
||||
draw_boss_text(AL_Left, 10, 20, boss->name);
|
||||
|
@ -90,9 +126,21 @@ void draw_boss(Boss *boss) {
|
|||
|
||||
if(boss->current->type != AT_Move) {
|
||||
char buf[16];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%.2f", max(0, (boss->current->timeout - global.frames + boss->current->starttime)/(float)FPS));
|
||||
draw_boss_text(AL_Center, VIEWPORT_W - 20, 10, buf);
|
||||
|
||||
StageProgress *p = get_spellstage_progress(boss->current, NULL, false);
|
||||
if(p) {
|
||||
float a = clamp((global.frames - boss->current->starttime - 60) / 60.0, 0, 1);
|
||||
snprintf(buf, sizeof(buf), "%u / %u", p->num_cleared, p->num_played);
|
||||
draw_boss_text_ex(AL_Right,
|
||||
VIEWPORT_W + stringwidth(buf, _fonts.small) * pow(1 - a, 2),
|
||||
35 + stringheight(buf, _fonts.small),
|
||||
buf, _fonts.small, a
|
||||
);
|
||||
}
|
||||
|
||||
int nextspell, lastspell;
|
||||
for(nextspell = 0; nextspell < boss->acount - 1; nextspell++) {
|
||||
int t = boss->attacks[nextspell].type;
|
||||
|
@ -256,6 +304,14 @@ void boss_finish_current_attack(Boss *boss) {
|
|||
AttackType t = boss->current->type;
|
||||
if(t == AT_Spellcard || t == AT_ExtraSpell || t == AT_SurvivalSpell) {
|
||||
boss_give_spell_bonus(boss, boss->current, &global.plr);
|
||||
|
||||
if(!boss->current->failtime) {
|
||||
StageProgress *p = get_spellstage_progress(boss->current, NULL, true);
|
||||
|
||||
if(p) {
|
||||
++p->num_cleared;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,20 +439,16 @@ void free_attack(Attack *a) {
|
|||
void start_attack(Boss *b, Attack *a) {
|
||||
log_debug("%s", a->name);
|
||||
|
||||
if(global.replaymode == REPLAY_RECORD && global.stage->type == STAGE_STORY && !global.continues) {
|
||||
StageInfo *i = stage_get_by_spellcard(a->info, global.diff);
|
||||
if(i) {
|
||||
StageProgress *p = stage_get_progress_from_info(i, global.diff, true);
|
||||
if(p && !p->unlocked) {
|
||||
log_info("Spellcard unlocked! %s: %s", i->title, i->subtitle);
|
||||
p->unlocked = true;
|
||||
}
|
||||
StageInfo *i;
|
||||
StageProgress *p = get_spellstage_progress(a, &i, true);
|
||||
|
||||
if(p) {
|
||||
++p->num_played;
|
||||
|
||||
if(!p->unlocked && !global.continues) {
|
||||
log_info("Spellcard unlocked! %s: %s", i->title, i->subtitle);
|
||||
p->unlocked = true;
|
||||
}
|
||||
#if DEBUG
|
||||
else if(a->type == AT_Spellcard || a->type == AT_ExtraSpell) {
|
||||
log_warn("FIXME: spellcard '%s' is not available in spell practice mode!", a->name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
a->starttime = global.frames + (a->type == AT_ExtraSpell? ATTACK_START_DELAY_EXTRA : ATTACK_START_DELAY);
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include "color.h"
|
||||
|
||||
struct Color;
|
||||
|
||||
typedef enum {
|
||||
D_Any = 0,
|
||||
D_Easy,
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
|
||||
- PCMD_HISCORE
|
||||
Sets the "Hi-Score" (highest score ever attained in one game session)
|
||||
|
||||
- PCMD_STAGE_PLAYINFO
|
||||
Sets the times played and times cleared counters for a list of stage/difficulty combinations
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -174,6 +177,19 @@ static void progress_read(SDL_RWops *file) {
|
|||
SDL_RWread(vfile, c->data, c->size, 1);
|
||||
|
||||
break;
|
||||
|
||||
case PCMD_STAGE_PLAYINFO:
|
||||
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);
|
||||
|
||||
assert(p != NULL);
|
||||
|
||||
p->num_played = SDL_ReadLE32(vfile); cur += sizeof(uint32_t);
|
||||
p->num_cleared = SDL_ReadLE32(vfile); cur += sizeof(uint32_t);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,6 +325,80 @@ static void progress_write_cmd_hiscore(SDL_RWops *vfile, void **arg) {
|
|||
SDL_WriteLE32(vfile, progress.hiscore);
|
||||
}
|
||||
|
||||
//
|
||||
// PCMD_STAGE_PLAYINFO
|
||||
//
|
||||
|
||||
struct cmd_stage_playinfo_data_elem {
|
||||
struct cmd_stage_playinfo_data_elem *next;
|
||||
struct cmd_stage_playinfo_data_elem *prev;
|
||||
uint16_t stage;
|
||||
uint8_t diff;
|
||||
uint32_t num_played;
|
||||
uint32_t num_cleared;
|
||||
};
|
||||
|
||||
struct cmd_stage_playinfo_data {
|
||||
size_t size;
|
||||
struct cmd_stage_playinfo_data_elem *elems;
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
for(StageInfo *stg = stages; stg->procs; ++stg) {
|
||||
Difficulty d_first, d_last;
|
||||
|
||||
if(stg->difficulty == D_Any) {
|
||||
d_first = D_Easy;
|
||||
d_last = D_Lunatic;
|
||||
} else {
|
||||
d_first = d_last = D_Any;
|
||||
}
|
||||
|
||||
for(Difficulty d = d_first; d <= d_last; ++d) {
|
||||
StageProgress *p = stage_get_progress_from_info(stg, d, false);
|
||||
|
||||
if(p && (p->num_played || p->num_cleared)) {
|
||||
struct cmd_stage_playinfo_data_elem *e = create_element((void**)&data->elems, sizeof(struct cmd_stage_playinfo_data_elem));
|
||||
e->stage = stg->id; data->size += sizeof(uint16_t);
|
||||
e->diff = d; data->size += sizeof(uint8_t);
|
||||
e->num_played = p->num_played; data->size += sizeof(uint32_t);
|
||||
e->num_cleared = p->num_cleared; data->size += sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*arg = data;
|
||||
|
||||
if(data->size) {
|
||||
*bufsize += CMD_HEADER_SIZE + data->size;
|
||||
}
|
||||
}
|
||||
|
||||
static void progress_write_cmd_stage_playinfo(SDL_RWops *vfile, void **arg) {
|
||||
struct cmd_stage_playinfo_data *data = *arg;
|
||||
|
||||
if(!data->size) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
SDL_WriteU8(vfile, PCMD_STAGE_PLAYINFO);
|
||||
SDL_WriteLE16(vfile, data->size);
|
||||
|
||||
for(struct cmd_stage_playinfo_data_elem *e = data->elems; e; e = e->next) {
|
||||
SDL_WriteLE16(vfile, e->stage);
|
||||
SDL_WriteU8(vfile, e->diff);
|
||||
SDL_WriteLE32(vfile, e->num_played);
|
||||
SDL_WriteLE32(vfile, e->num_cleared);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
delete_all_elements((void**)&data->elems, delete_element);
|
||||
free(data);
|
||||
}
|
||||
|
||||
//
|
||||
// Copy unhandled commands from the original file
|
||||
//
|
||||
|
@ -349,6 +439,7 @@ static void progress_write(SDL_RWops *file) {
|
|||
{progress_prepare_cmd_unlock_stages, progress_write_cmd_unlock_stages, NULL},
|
||||
{progress_prepare_cmd_unlock_stages_with_difficulties, progress_write_cmd_unlock_stages_with_difficulties, NULL},
|
||||
{progress_prepare_cmd_hiscore, progress_write_cmd_hiscore, NULL},
|
||||
{progress_prepare_cmd_stage_playinfo, progress_write_cmd_stage_playinfo, NULL},
|
||||
// {progress_prepare_cmd_test, progress_write_cmd_test, NULL},
|
||||
{progress_prepare_cmd_unknown, progress_write_cmd_unknown, NULL},
|
||||
{NULL}
|
||||
|
|
|
@ -15,22 +15,26 @@
|
|||
#define PROGRESS_MAXFILESIZE 4096
|
||||
|
||||
#ifdef DEBUG
|
||||
// #define PROGRESS_UNLOCK_ALL
|
||||
// #define PROGRESS_UNLOCK_ALL
|
||||
#endif
|
||||
|
||||
typedef enum ProgfileCommand {
|
||||
PCMD_UNLOCK_STAGES,
|
||||
PCMD_UNLOCK_STAGES_WITH_DIFFICULTY,
|
||||
PCMD_HISCORE,
|
||||
// do not reorder this!
|
||||
|
||||
PCMD_UNLOCK_STAGES = 0x00,
|
||||
PCMD_UNLOCK_STAGES_WITH_DIFFICULTY = 0x01,
|
||||
PCMD_HISCORE = 0x02,
|
||||
PCMD_STAGE_PLAYINFO = 0x03,
|
||||
} ProgfileCommand;
|
||||
|
||||
typedef struct StageProgress {
|
||||
// keep this struct small if you can, it leaks
|
||||
// see stage_get_progress_from_info() in stage.c for more information
|
||||
// keep this struct small if you can
|
||||
// see stage_get_progress_from_info() in stage.c for more information
|
||||
|
||||
bool unlocked;
|
||||
// short num_played;
|
||||
// short num_cleared;
|
||||
unsigned int unlocked : 1;
|
||||
|
||||
uint32_t num_played;
|
||||
uint32_t num_cleared;
|
||||
} StageProgress;
|
||||
|
||||
struct UnknownCmd;
|
||||
|
|
|
@ -101,14 +101,14 @@ void init_fonts(void) {
|
|||
fontrenderer_init(&resources.fontren);
|
||||
_fonts.standard = load_font("gfx/LinBiolinum.ttf", 20);
|
||||
_fonts.mainmenu = load_font("gfx/immortal.ttf", 35);
|
||||
|
||||
_fonts.small = load_font("gfx/LinBiolinum.ttf", 14);
|
||||
}
|
||||
|
||||
void free_fonts(void) {
|
||||
fontrenderer_free(&resources.fontren);
|
||||
TTF_CloseFont(_fonts.standard);
|
||||
TTF_CloseFont(_fonts.mainmenu);
|
||||
|
||||
TTF_CloseFont(_fonts.small);
|
||||
TTF_Quit();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ int charwidth(char c, TTF_Font *font);
|
|||
struct Fonts {
|
||||
TTF_Font *standard;
|
||||
TTF_Font *mainmenu;
|
||||
TTF_Font *small;
|
||||
};
|
||||
|
||||
extern struct Fonts _fonts;
|
||||
|
|
19
src/stage.c
19
src/stage.c
|
@ -876,6 +876,16 @@ 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);
|
||||
|
||||
if(global.replaymode == REPLAY_PLAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
StageProgress *p = stage_get_progress_from_info(global.stage, global.diff, true);
|
||||
|
||||
if(p) {
|
||||
++p->num_cleared;
|
||||
}
|
||||
}
|
||||
|
||||
static void stage_preload(void) {
|
||||
|
@ -941,6 +951,15 @@ void stage_loop(StageInfo *stage) {
|
|||
}
|
||||
|
||||
log_debug("Random seed: %u", seed);
|
||||
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
if(!global.replay_stage) {
|
||||
log_fatal("Attemped to replay a NULL stage");
|
||||
|
|
Loading…
Reference in a new issue