Player Statistics (#219)

This commit is contained in:
Alice D 2020-04-26 15:27:13 -04:00 committed by GitHub
parent 7d7f1b157f
commit 56e64498a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 154 additions and 23 deletions

View file

@ -328,7 +328,7 @@ void good_ending_reimu(Ending *e) {
static void init_ending(Ending *e) {
dynarray_ensure_capacity(&e->entries, 32);
if(global.plr.continues_used) {
if(global.plr.stats.total.continues_used) {
global.plr.mode->character->ending.bad(e);
} else {
global.plr.mode->character->ending.good(e);

View file

@ -343,6 +343,7 @@ static void main_singlestg_begin_game(CallChainResult ccr) {
replay_init(&global.replay);
global.gameover = 0;
player_init(&global.plr);
stats_init(&global.plr.stats);
if(ctx->plrmode) {
global.plr.mode = ctx->plrmode;

View file

@ -91,6 +91,7 @@ static void reset_game(StartGameContext *ctx) {
replay_destroy(&global.replay);
replay_init(&global.replay);
player_init(&global.plr);
stats_init(&global.plr.stats);
global.plr.mode = plrmode_find(
progress.game_settings.character,
progress.game_settings.shotmode

View file

@ -11,6 +11,7 @@
#include "menu.h"
#include "gameovermenu.h"
#include "ingamemenu.h"
#include "stats.h"
#include "global.h"
static void continue_game(MenuData *m, void *arg) {
@ -20,7 +21,7 @@ static void continue_game(MenuData *m, void *arg) {
}
static void give_up(MenuData *m, void *arg) {
global.gameover = (MAX_CONTINUES - global.plr.continues_used)? GAMEOVER_ABORT : GAMEOVER_DEFEAT;
global.gameover = (MAX_CONTINUES - global.plr.stats.total.continues_used) ? GAMEOVER_ABORT : GAMEOVER_DEFEAT;
}
MenuData* create_gameover_menu(void) {
@ -40,9 +41,9 @@ MenuData* create_gameover_menu(void) {
m->context = "Game Over";
char s[64];
int c = MAX_CONTINUES - global.plr.continues_used;
int c = MAX_CONTINUES - global.plr.stats.total.continues_used;
snprintf(s, sizeof(s), "Continue (%i)", c);
add_menu_entry(m, s, c? continue_game : NULL, NULL);
add_menu_entry(m, s, c ? continue_game : NULL, NULL);
add_menu_entry(m, "Restart the Game", restart_game, NULL)->transition = TransFadeBlack;
add_menu_entry(m, c? "Give up" : "Return to Title", give_up, NULL)->transition = TransFadeBlack;

View file

@ -93,6 +93,7 @@ taisei_src = files(
'stageobjects.c',
'stagetext.c',
'stageutils.c',
'stats.c',
'taskmanager.c',
'transition.c',
'version.c',

View file

@ -16,6 +16,7 @@
#include "stage.h"
#include "stagetext.h"
#include "stagedraw.h"
#include "stats.h"
#include "entity.h"
#include "util/glm.h"
@ -590,7 +591,7 @@ DEFINE_TASK(player_logic) {
plr->point_item_value = PLR_START_PIV;
plr->life_fragments = 0;
plr->bomb_fragments = 0;
plr->continues_used += 1;
stats_track_continue_used(&plr->stats);
player_set_power(plr, 0);
stage_clear_hazards(CLEAR_HAZARDS_ALL);
spawn_items(plr->deathpos, ITEM_POWER, (int)ceil(PLR_MAX_POWER/(double)POWER_VALUE));
@ -638,6 +639,19 @@ DEFINE_TASK(player_logic) {
void player_logic(Player* plr) {
}
static bool player_can_bomb(Player *plr) {
return (
!player_is_bomb_active(plr)
&& (
plr->bombs > 0 ||
plr->iddqd
)
&& global.frames >= plr->respawntime
);
}
static bool player_bomb(Player *plr) {
if(global.boss && global.boss->current && global.boss->current->type == AT_ExtraSpell)
return false;
@ -648,7 +662,8 @@ static bool player_bomb(Player *plr) {
return false;
}
if(!player_is_bomb_active(plr) && (plr->bombs > 0 || plr->iddqd) && global.frames >= plr->respawntime) {
if(player_can_bomb(plr)) {
stats_track_bomb_used(&plr->stats);
player_fail_spell(plr);
// player_cancel_powersurge(plr);
// stage_clear_hazards(CLEAR_HAZARDS_ALL);
@ -885,6 +900,7 @@ void player_realdeath(Player *plr) {
plr->bomb_fragments = 0;
plr->voltage *= 0.9;
plr->lives--;
stats_track_life_used(&plr->stats);
}
static void player_death_effect_draw_overlay(Projectile *p, int t, ProjDrawRuleArgs args) {

View file

@ -25,6 +25,7 @@
#include "enemy.h"
#include "gamepad.h"
#include "aniplayer.h"
#include "stats.h"
#include "resource/animation.h"
#include "entity.h"
@ -102,6 +103,8 @@ DEFINE_ENTITY_TYPE(Player, {
AniPlayer ani;
Sprite bomb_portrait;
Stats stats;
struct {
float positive;
float negative;
@ -133,7 +136,6 @@ DEFINE_ENTITY_TYPE(Player, {
int bombs;
int life_fragments;
int bomb_fragments;
int continues_used;
int power;
int power_overflow;

View file

@ -41,7 +41,7 @@ ReplayStage* replay_create_stage(Replay *rpy, StageInfo *stage, uint64_t start_t
s->plr_pos_y = floor(cimag(plr->pos));
s->plr_points = plr->points;
s->plr_continues_used = plr->continues_used;
s->plr_continues_used = plr->stats.total.continues_used;
// s->plr_focus = plr->focus; FIXME remove and bump version
s->plr_char = plr->mode->character->id;
s->plr_shot = plr->mode->shot_mode;
@ -60,7 +60,7 @@ ReplayStage* replay_create_stage(Replay *rpy, StageInfo *stage, uint64_t start_t
void replay_stage_sync_player_state(ReplayStage *stg, Player *plr) {
plr->points = stg->plr_points;
plr->continues_used = stg->plr_continues_used;
plr->stats.total.continues_used = stg->plr_continues_used;
plr->mode = plrmode_find(stg->plr_char, stg->plr_shot);
plr->pos = stg->plr_pos_x + I * stg->plr_pos_y;
// plr->focus = stg->plr_focus; FIXME remove and bump version
@ -73,6 +73,12 @@ void replay_stage_sync_player_state(ReplayStage *stg, Player *plr) {
plr->graze = stg->plr_graze;
plr->point_item_value = stg->plr_point_item_value;
plr->inputflags = stg->plr_inputflags;
plr->stats.total.lives_used = stg->plr_stats_total_lives_used;
plr->stats.stage.lives_used = stg->plr_stats_stage_lives_used;
plr->stats.total.bombs_used = stg->plr_stats_total_bombs_used;
plr->stats.stage.bombs_used = stg->plr_stats_stage_bombs_used;
plr->stats.stage.continues_used = stg->plr_stats_stage_continues_used;
}
static void replay_destroy_stage(ReplayStage *stage) {
@ -194,6 +200,14 @@ static uint32_t replay_calc_stageinfo_checksum(ReplayStage *stg, uint16_t versio
cs += stg->plr_points_final;
}
if(version >= REPLAY_STRUCT_VERSION_TS104000_REV0) {
cs += stg->plr_stats_total_lives_used;
cs += stg->plr_stats_stage_lives_used;
cs += stg->plr_stats_total_bombs_used;
cs += stg->plr_stats_stage_bombs_used;
cs += stg->plr_stats_stage_continues_used;
}
log_debug("%08x", cs);
return cs;
}
@ -226,6 +240,14 @@ static bool replay_write_stage(ReplayStage *stg, SDL_RWops *file, uint16_t versi
SDL_WriteLE64(file, stg->plr_points_final);
}
if(version >= REPLAY_STRUCT_VERSION_TS104000_REV0) {
SDL_WriteU8(file, stg->plr_stats_total_lives_used);
SDL_WriteU8(file, stg->plr_stats_stage_lives_used);
SDL_WriteU8(file, stg->plr_stats_total_bombs_used);
SDL_WriteU8(file, stg->plr_stats_stage_bombs_used);
SDL_WriteU8(file, stg->plr_stats_stage_continues_used);
}
if(stg->events.num_elements > UINT16_MAX) {
log_error("Too many events in replay, cannot write this");
return false;
@ -371,6 +393,7 @@ static bool replay_read_header(Replay *rpy, SDL_RWops *file, int64_t filesize, s
case REPLAY_STRUCT_VERSION_TS103000_REV1:
case REPLAY_STRUCT_VERSION_TS103000_REV2:
case REPLAY_STRUCT_VERSION_TS103000_REV3:
case REPLAY_STRUCT_VERSION_TS104000_REV0:
{
if(taisei_version_read(file, &rpy->game_version) != TAISEI_VERSION_SIZE) {
log_error("%s: Failed to read game version", source);
@ -485,6 +508,14 @@ static bool _replay_read_meta(Replay *rpy, SDL_RWops *file, int64_t filesize, co
CHECKPROP(stg->plr_points_final = SDL_ReadLE64(file), zu);
}
if(version >= REPLAY_STRUCT_VERSION_TS104000_REV0) {
CHECKPROP(stg->plr_stats_total_lives_used = SDL_ReadU8(file), u);
CHECKPROP(stg->plr_stats_stage_lives_used = SDL_ReadU8(file), u);
CHECKPROP(stg->plr_stats_total_bombs_used = SDL_ReadU8(file), u);
CHECKPROP(stg->plr_stats_stage_bombs_used = SDL_ReadU8(file), u);
CHECKPROP(stg->plr_stats_stage_continues_used = SDL_ReadU8(file), u);
}
CHECKPROP(stg->num_events = SDL_ReadLE16(file), u);
if(replay_calc_stageinfo_checksum(stg, version) + SDL_ReadLE32(file)) {

View file

@ -54,13 +54,16 @@
// Taisei v1.3 revision 3: add final score at the end of each stage
#define REPLAY_STRUCT_VERSION_TS103000_REV3 12
// Taisei v1.4 revision 0: add statistics for player
#define REPLAY_STRUCT_VERSION_TS104000_REV0 13
/* END supported struct versions */
#define REPLAY_VERSION_COMPRESSION_BIT 0x8000
#define REPLAY_COMPRESSION_CHUNK_SIZE 4096
// What struct version to use when saving recorded replays
#define REPLAY_STRUCT_VERSION_WRITE (REPLAY_STRUCT_VERSION_TS103000_REV3 | REPLAY_VERSION_COMPRESSION_BIT)
#define REPLAY_STRUCT_VERSION_WRITE (REPLAY_STRUCT_VERSION_TS104000_REV0 | REPLAY_VERSION_COMPRESSION_BIT)
#define REPLAY_ALLOC_INITIAL 256
@ -130,6 +133,14 @@ typedef struct ReplayStage {
uint64_t plr_points_final;
/* END REPLAY_STRUCT_VERSION_TS102000_REV3 and above */
/* BEGIN REPLAY_STRUCT_VERSION_TS104000_REV0 and above */
uint8_t plr_stats_total_lives_used;
uint8_t plr_stats_stage_lives_used;
uint8_t plr_stats_total_bombs_used;
uint8_t plr_stats_stage_bombs_used;
uint8_t plr_stats_stage_continues_used;
/* END REPLAY_STRUCT_VERSION_TS104000_REV0 and above */
// player input
// NOTE: only used to read the number of events from file.
// Actual numbers of events stored/allocated are tracked by the .events dynamic array.

View file

@ -214,6 +214,8 @@ static void stage_start(StageInfo *stage) {
player_stage_pre_init(&global.plr);
stats_stage_reset(&global.plr.stats);
if(stage->type == STAGE_SPELL) {
global.is_practice_mode = true;
global.plr.lives = 0;
@ -1051,7 +1053,7 @@ void stage_end_loop(void* ctx) {
}
void stage_unlock_bgm(const char *bgm) {
if(global.replaymode != REPLAY_PLAY && !global.plr.continues_used) {
if(global.replaymode != REPLAY_PLAY && !global.plr.stats.total.continues_used) {
progress_unlock_bgm(bgm);
}
}

View file

@ -187,16 +187,6 @@ void stagetext_table_add_separator(StageTextTable *tbl) {
tbl->pos += I * 0.5 * font_get_lineskip(get_font("standard"));
}
void stagetext_table_test(void) {
StageTextTable tbl;
stagetext_begin_table(&tbl, "Test", RGB(1, 1, 1), RGB(1, 1, 1), VIEWPORT_W/2, 60, 300, 30, 60);
stagetext_table_add(&tbl, "foo", "bar");
stagetext_table_add(&tbl, "qwerty", "asdfg");
stagetext_table_add(&tbl, "top", "kek");
stagetext_table_add_separator(&tbl);
stagetext_table_add_numeric(&tbl, "Total Score", 9000000);
stagetext_end_table(&tbl);
}
StageText *stagetext_list_head(void) {
return textlist;

View file

@ -75,6 +75,4 @@ void stagetext_table_add_numeric(StageTextTable *tbl, const char *title, int n);
void stagetext_table_add_numeric_nonzero(StageTextTable *tbl, const char *title, int n);
void stagetext_table_add_separator(StageTextTable *tbl);
void stagetext_table_test(void);
#endif // IGUARD_stagetext_h

45
src/stats.c Normal file
View file

@ -0,0 +1,45 @@
/*
* 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 "stats.h"
#include "global.h"
#include "log.h"
void stats_init(Stats *stats) {
memset(stats, 0, sizeof(*stats));
}
void stats_track_life_used(Stats *stats) {
stats->total.lives_used++;
stats->stage.lives_used++;
log_debug("lives used (total): %d", stats->total.lives_used);
log_debug("lives used (stage): %d", stats->stage.lives_used);
}
void stats_track_bomb_used(Stats *stats) {
stats->total.bombs_used++;
stats->stage.bombs_used++;
log_debug("bombs used (total): %d", stats->total.bombs_used);
log_debug("bombs used (stage): %d", stats->stage.bombs_used);
}
void stats_track_continue_used(Stats *stats) {
stats->total.continues_used++;
stats->stage.continues_used++;
log_debug("bombs used (total): %d", stats->total.continues_used);
log_debug("bombs used (stage): %d", stats->stage.continues_used);
}
void stats_stage_reset(Stats *stats) {
memset(&stats->stage, 0, sizeof(stats->stage));
log_debug("statistics reset");
}

32
src/stats.h Normal file
View file

@ -0,0 +1,32 @@
/*
* 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_stats_h
#define IGUARD_stats_h
#include "taisei.h"
typedef struct Stats Stats;
struct Stats {
struct {
int lives_used;
int bombs_used;
int continues_used;
} total, stage;
};
void stats_init(Stats *stats);
void stats_track_life_used(Stats *stats);
void stats_track_bomb_used(Stats *stats);
void stats_track_continue_used(Stats *stats);
void stats_stage_reset(Stats *stats);
#endif // IGUARD_stats_h