player: refactor power handling

Separate concepts of "stored" and "effective" power.
This commit is contained in:
Andrei Alexeyev 2022-10-03 20:36:21 +02:00
parent b746738c5c
commit 3c7283f1c7
No known key found for this signature in database
GPG key ID: 72D26128040B9690
12 changed files with 72 additions and 58 deletions

View file

@ -248,7 +248,7 @@ void process_items(void) {
bool may_collect = true; bool may_collect = true;
if( if(
(item->type == ITEM_POWER_MINI && global.plr.power == PLR_MAX_POWER) || (item->type == ITEM_POWER_MINI && global.plr.power_stored >= PLR_MAX_POWER_EFFECTIVE) ||
(item->type == ITEM_SURGE && !player_is_powersurge_active(&global.plr)) (item->type == ITEM_SURGE && !player_is_powersurge_active(&global.plr))
) { ) {
item->type = ITEM_PIV; item->type = ITEM_PIV;

View file

@ -39,7 +39,7 @@ void player_init(Player *plr) {
plr->lives = PLR_START_LIVES; plr->lives = PLR_START_LIVES;
plr->bombs = PLR_START_BOMBS; plr->bombs = PLR_START_BOMBS;
plr->point_item_value = PLR_START_PIV; plr->point_item_value = PLR_START_PIV;
plr->power = 100; plr->power_stored = 100;
plr->deathtime = -1; plr->deathtime = -1;
plr->continuetime = -1; plr->continuetime = -1;
plr->bomb_triggertime = -1; plr->bomb_triggertime = -1;
@ -112,35 +112,48 @@ static void player_full_power(Player *plr) {
stagetext_add("Full Power!", VIEWPORT_W * 0.5 + VIEWPORT_H * 0.33 * I, ALIGN_CENTER, res_font("big"), RGB(1, 1, 1), 0, 60, 20, 20); stagetext_add("Full Power!", VIEWPORT_W * 0.5 + VIEWPORT_H * 0.33 * I, ALIGN_CENTER, res_font("big"), RGB(1, 1, 1), 0, 60, 20, 20);
} }
static int player_track_effective_power_change(Player *plr) {
int old_effective = plr->_prev_effective_power;
int new_effective = player_get_effective_power(plr);
if(old_effective != new_effective) {
coevent_signal(&plr->events.effective_power_changed);
}
plr->_prev_effective_power = old_effective;
return new_effective;
}
bool player_set_power(Player *plr, short npow) { bool player_set_power(Player *plr, short npow) {
int pow_base = clamp(npow, 0, PLR_MAX_POWER); int old_stored = plr->power_stored;
int pow_overflow = clamp(npow - PLR_MAX_POWER, 0, PLR_MAX_POWER_OVERFLOW); int new_stored = iclamp(npow, 0, PLR_MAX_POWER_STORED);
plr->power_stored = new_stored;
int oldpow = plr->power; if(old_stored / 100 < new_stored / 100) {
int oldpow_over = plr->power_overflow;
plr->power = pow_base;
plr->power_overflow = pow_overflow;
if((oldpow + oldpow_over) / 100 < (pow_base + pow_overflow) / 100) {
play_sfx("powerup"); play_sfx("powerup");
} }
if(plr->power == PLR_MAX_POWER && oldpow < PLR_MAX_POWER) { bool change = old_stored != new_stored;
player_full_power(plr);
}
bool change = (oldpow + oldpow_over) != (plr->power + plr->power_overflow);
if(change) { if(change) {
coevent_signal(&plr->events.power_changed); if(new_stored >= PLR_MAX_POWER_EFFECTIVE && old_stored < PLR_MAX_POWER_EFFECTIVE) {
player_full_power(plr);
}
coevent_signal(&plr->events.stored_power_changed);
} }
player_track_effective_power_change(plr);
return change; return change;
} }
bool player_add_power(Player *plr, short pdelta) { bool player_add_power(Player *plr, short pdelta) {
return player_set_power(plr, plr->power + plr->power_overflow + pdelta); return player_set_power(plr, plr->power_stored + pdelta);
}
int player_get_effective_power(Player *plr) {
return iclamp(plr->power_stored, 0, PLR_MAX_POWER_EFFECTIVE);
} }
void player_move(Player *plr, cmplx delta) { void player_move(Player *plr, cmplx delta) {
@ -554,6 +567,8 @@ DEFINE_TASK(player_logic) {
Player *plr = TASK_BIND(ARGS.plr); Player *plr = TASK_BIND(ARGS.plr);
uint prev_inputflags = 0; uint prev_inputflags = 0;
plr->_prev_effective_power = player_get_effective_power(plr);
for(;;) { for(;;) {
YIELD; YIELD;
@ -580,7 +595,7 @@ DEFINE_TASK(player_logic) {
stats_track_continue_used(&plr->stats); stats_track_continue_used(&plr->stats);
player_set_power(plr, 0); player_set_power(plr, 0);
stage_clear_hazards(CLEAR_HAZARDS_ALL); stage_clear_hazards(CLEAR_HAZARDS_ALL);
spawn_items(plr->deathpos, ITEM_POWER, (int)ceil(PLR_MAX_POWER/(double)POWER_VALUE)); spawn_items(plr->deathpos, ITEM_POWER, (int)ceil(PLR_MAX_POWER_EFFECTIVE/(real)POWER_VALUE));
} }
aniplayer_update(&plr->ani); aniplayer_update(&plr->ani);
@ -612,6 +627,8 @@ DEFINE_TASK(player_logic) {
} else if(plr->deathtime > global.frames) { } else if(plr->deathtime > global.frames) {
stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW); stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
} }
player_track_effective_power_change(plr);
} }
} }
@ -677,7 +694,7 @@ static bool player_powersurge(Player *plr) {
!player_is_alive(plr) || !player_is_alive(plr) ||
player_is_bomb_active(plr) || player_is_bomb_active(plr) ||
player_is_powersurge_active(plr) || player_is_powersurge_active(plr) ||
plr->power + plr->power_overflow < PLR_POWERSURGE_POWERCOST plr->power_stored < PLR_POWERSURGE_POWERCOST
) { ) {
return false; return false;
} }
@ -856,7 +873,7 @@ void player_realdeath(Player *plr) {
return; return;
} }
int total_power = plr->power + plr->power_overflow; int total_power = plr->power_stored;
int drop = fmax(2, (total_power * 0.15) / POWER_VALUE); int drop = fmax(2, (total_power * 0.15) / POWER_VALUE);
spawn_items(plr->deathpos, ITEM_POWER, drop); spawn_items(plr->deathpos, ITEM_POWER, drop);

View file

@ -30,8 +30,8 @@
#include "replay/eventcodes.h" #include "replay/eventcodes.h"
enum { enum {
PLR_MAX_POWER = 400, PLR_MAX_POWER_EFFECTIVE = 400,
PLR_MAX_POWER_OVERFLOW = 200, PLR_MAX_POWER_STORED = 600,
PLR_MAX_LIVES = 8, PLR_MAX_LIVES = 8,
PLR_MAX_BOMBS = 8, PLR_MAX_BOMBS = 8,
@ -121,7 +121,8 @@ DEFINE_ENTITY_TYPE(Player, {
COEVENTS_ARRAY( COEVENTS_ARRAY(
shoot, shoot,
inputflags_changed, inputflags_changed,
power_changed, stored_power_changed,
effective_power_changed,
bomb_used bomb_used
) events; ) events;
@ -137,8 +138,8 @@ DEFINE_ENTITY_TYPE(Player, {
int bombs; int bombs;
int life_fragments; int life_fragments;
int bomb_fragments; int bomb_fragments;
int power; int power_stored;
int power_overflow; int _prev_effective_power;
int continuetime; int continuetime;
int deathtime; /* time of hit + deathbomb window */ int deathtime; /* time of hit + deathbomb window */
@ -191,6 +192,7 @@ bool player_should_shoot(Player *plr);
bool player_set_power(Player *plr, short npow); bool player_set_power(Player *plr, short npow);
bool player_add_power(Player *plr, short pdelta); bool player_add_power(Player *plr, short pdelta);
int player_get_effective_power(Player *plr);
void player_move(Player*, cmplx delta); void player_move(Player*, cmplx delta);

View file

@ -664,13 +664,13 @@ static void marisa_laser_respawn_slaves(MarisaAController *ctrl, int power_rank)
TASK(marisa_laser_power_handler, { MarisaAController *ctrl; }) { TASK(marisa_laser_power_handler, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl; MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr; Player *plr = ctrl->plr;
int old_power = plr->power / 100; int old_power = player_get_effective_power(plr) / 100;
marisa_laser_respawn_slaves(ctrl, old_power); marisa_laser_respawn_slaves(ctrl, old_power);
for(;;) { for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.power_changed); WAIT_EVENT_OR_DIE(&plr->events.effective_power_changed);
int new_power = plr->power / 100; int new_power = player_get_effective_power(plr) / 100;
if(old_power != new_power) { if(old_power != new_power) {
marisa_laser_respawn_slaves(ctrl, new_power); marisa_laser_respawn_slaves(ctrl, new_power);
old_power = new_power; old_power = new_power;

View file

@ -270,13 +270,13 @@ static void marisa_star_respawn_slaves(MarisaBController *ctrl, int numslaves) {
TASK(marisa_star_power_handler, { MarisaBController *ctrl; }) { TASK(marisa_star_power_handler, { MarisaBController *ctrl; }) {
MarisaBController *ctrl = ARGS.ctrl; MarisaBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr; Player *plr = ctrl->plr;
int old_power = plr->power / 100; int old_power = player_get_effective_power(plr) / 100;
marisa_star_respawn_slaves(ctrl, old_power); marisa_star_respawn_slaves(ctrl, old_power);
for(;;) { for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.power_changed); WAIT_EVENT_OR_DIE(&plr->events.effective_power_changed);
int new_power = plr->power / 100; int new_power = player_get_effective_power(plr) / 100;
if(old_power != new_power) { if(old_power != new_power) {
marisa_star_respawn_slaves(ctrl, new_power); marisa_star_respawn_slaves(ctrl, new_power);
old_power = new_power; old_power = new_power;

View file

@ -740,7 +740,7 @@ static void reimu_spirit_kill_slaves(ReimuAController *ctrl) {
static void reimu_spirit_respawn_slaves(ReimuAController *ctrl) { static void reimu_spirit_respawn_slaves(ReimuAController *ctrl) {
Player *plr = ctrl->plr; Player *plr = ctrl->plr;
int power_rank = plr->power / 100; int power_rank = player_get_effective_power(plr) / 100;
reimu_spirit_kill_slaves(ctrl); reimu_spirit_kill_slaves(ctrl);
@ -779,11 +779,11 @@ TASK(reimu_spirit_focus_handler, { ReimuAController *ctrl; }) {
TASK(reimu_spirit_power_handler, { ReimuAController *ctrl; }) { TASK(reimu_spirit_power_handler, { ReimuAController *ctrl; }) {
ReimuAController *ctrl = ARGS.ctrl; ReimuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr; Player *plr = ctrl->plr;
int old_power = plr->power / 100; int old_power = player_get_effective_power(plr) / 100;
for(;;) { for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.power_changed); WAIT_EVENT_OR_DIE(&plr->events.effective_power_changed);
int new_power = plr->power / 100; int new_power = player_get_effective_power(plr) / 100;
if(old_power != new_power) { if(old_power != new_power) {
reimu_spirit_respawn_slaves(ctrl); reimu_spirit_respawn_slaves(ctrl);
old_power = new_power; old_power = new_power;
@ -832,7 +832,7 @@ TASK(reimu_spirit_shot_volley, { ReimuAController *ctrl; }) {
for(;;) { for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.shoot); WAIT_EVENT_OR_DIE(&plr->events.shoot);
int power_rank = plr->power / 100; int power_rank = player_get_effective_power(plr) / 100;
real damage = SHOT_VOLLEY_DMG; real damage = SHOT_VOLLEY_DMG;
for(int pwr = 0; pwr <= power_rank; ++pwr) { for(int pwr = 0; pwr <= power_rank; ++pwr) {

View file

@ -606,13 +606,13 @@ TASK(reimu_dream_shot_forward, { ReimuBController *ctrl; }) {
TASK(reimu_dream_power_handler, { ReimuBController *ctrl; }) { TASK(reimu_dream_power_handler, { ReimuBController *ctrl; }) {
ReimuBController *ctrl = ARGS.ctrl; ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr; Player *plr = ctrl->plr;
int old_power = plr->power / 100; int old_power = player_get_effective_power(plr) / 100;
reimu_dream_respawn_slaves(ctrl, old_power * 2); reimu_dream_respawn_slaves(ctrl, old_power * 2);
for(;;) { for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.power_changed); WAIT_EVENT_OR_DIE(&plr->events.effective_power_changed);
int new_power = plr->power / 100; int new_power = player_get_effective_power(plr) / 100;
if(old_power != new_power) { if(old_power != new_power) {
reimu_dream_respawn_slaves(ctrl, new_power * 2); reimu_dream_respawn_slaves(ctrl, new_power * 2);
old_power = new_power; old_power = new_power;

View file

@ -240,7 +240,7 @@ TASK(youmu_mirror_myon_shot, { YoumuAController *ctrl; }) {
const real dmg_center = SHOT_MYON_DAMAGE; const real dmg_center = SHOT_MYON_DAMAGE;
const real dmg_side = SHOT_MYON_DAMAGE; const real dmg_side = SHOT_MYON_DAMAGE;
const real speed = -10; const real speed = -10;
const int power_rank = plr->power / 100; const int power_rank = player_get_effective_power(plr) / 100;
real spread; real spread;
cmplx forward; cmplx forward;
@ -567,7 +567,7 @@ TASK(youmu_mirror_shot_forward, { YoumuAController *ctrl; }) {
play_sfx_loop("generic_shot"); play_sfx_loop("generic_shot");
cmplx v = -20 * I; cmplx v = -20 * I;
int power_rank = plr->power / 100; int power_rank = player_get_effective_power(plr) / 100;
real spread = M_PI/64 * (1 + 0.5 * psin(t/15.0)); real spread = M_PI/64 * (1 + 0.5 * psin(t/15.0));

View file

@ -176,7 +176,7 @@ TASK(youmu_haunting_shot_spread, { YoumuBController *ctrl; }) {
continue; continue;
} }
INVOKE_TASK(youmu_burst_shot, ctrl, 2 * plr->power / 100); INVOKE_TASK(youmu_burst_shot, ctrl, 2 * player_get_effective_power(plr) / 100);
WAIT(SHOT_SPREAD_DELAY); WAIT(SHOT_SPREAD_DELAY);
} }
} }
@ -452,12 +452,12 @@ TASK(youmu_haunting_shot_orbs, { YoumuBController *ctrl; }) {
continue; continue;
} }
int power_rank = plr->power / 100; int power_rank = player_get_effective_power(plr) / 100;
INVOKE_TASK(youmu_orb_shot, INVOKE_TASK(youmu_orb_shot,
.ctrl = ctrl, .ctrl = ctrl,
.lifetime = SHOT_ORBS_LIFETIME_BASE + power_rank * SHOT_ORBS_LIFETIME_PER_POWER, .lifetime = SHOT_ORBS_LIFETIME_BASE + power_rank * SHOT_ORBS_LIFETIME_PER_POWER,
.spirit_damage = SHOT_ORBS_SPIRIT_DAMAGE, // 120 - 18 * 4 * (1 - pow(1 - (plr->power / 100) / 4.0, 1.5)); .spirit_damage = SHOT_ORBS_SPIRIT_DAMAGE,
.spirit_spawn_delay = SHOT_ORBS_SPIRIT_SPAWN_DELAY .spirit_spawn_delay = SHOT_ORBS_SPIRIT_SPAWN_DELAY
); );

View file

@ -37,7 +37,7 @@ ReplayStage *replay_stage_new(Replay *rpy, StageInfo *stage, uint64_t start_time
s->plr_life_fragments = plr->life_fragments; s->plr_life_fragments = plr->life_fragments;
s->plr_bombs = plr->bombs; s->plr_bombs = plr->bombs;
s->plr_bomb_fragments = plr->bomb_fragments; s->plr_bomb_fragments = plr->bomb_fragments;
s->plr_power = plr->power + plr->power_overflow; s->plr_power = plr->power_stored;
s->plr_graze = plr->graze; s->plr_graze = plr->graze;
s->plr_point_item_value = plr->point_item_value; s->plr_point_item_value = plr->point_item_value;
s->plr_inputflags = plr->inputflags; s->plr_inputflags = plr->inputflags;
@ -56,8 +56,7 @@ void replay_stage_sync_player_state(ReplayStage *stg, Player *plr) {
plr->life_fragments = stg->plr_life_fragments; plr->life_fragments = stg->plr_life_fragments;
plr->bombs = stg->plr_bombs; plr->bombs = stg->plr_bombs;
plr->bomb_fragments = stg->plr_bomb_fragments; plr->bomb_fragments = stg->plr_bomb_fragments;
plr->power = (stg->plr_power > PLR_MAX_POWER ? PLR_MAX_POWER : stg->plr_power); plr->power_stored = stg->plr_power;
plr->power_overflow = (stg->plr_power > PLR_MAX_POWER ? stg->plr_power - PLR_MAX_POWER : 0);
plr->graze = stg->plr_graze; plr->graze = stg->plr_graze;
plr->point_item_value = stg->plr_point_item_value; plr->point_item_value = stg->plr_point_item_value;
plr->inputflags = stg->plr_inputflags; plr->inputflags = stg->plr_inputflags;

View file

@ -148,14 +148,10 @@ static void stage_start(StageInfo *stage) {
} }
if(global.is_practice_mode) { if(global.is_practice_mode) {
global.plr.power = config_get_int(CONFIG_PRACTICE_POWER); global.plr.power_stored = config_get_int(CONFIG_PRACTICE_POWER);
} }
if(global.plr.power < 0) { global.plr.power_stored = iclamp(global.plr.power_stored, 0, PLR_MAX_POWER_STORED);
global.plr.power = 0;
} else if(global.plr.power > PLR_MAX_POWER) {
global.plr.power = PLR_MAX_POWER;
}
reset_all_sfx(); reset_all_sfx();
} }

View file

@ -1100,15 +1100,15 @@ static inline void stage_draw_hud_power_value(float xpos, float ypos) {
Font *fnt_int = res_font("standard"); Font *fnt_int = res_font("standard");
Font *fnt_fract = res_font("small"); Font *fnt_fract = res_font("small");
int pw = global.plr.power + global.plr.power_overflow; int pw = global.plr.power_stored;
Color *c_whole, c_whole_buf, *c_fract, c_fract_buf; Color *c_whole, c_whole_buf, *c_fract, c_fract_buf;
Color *c_op_mod = RGBA(1, 0.2 + 0.3 * psin(global.frames / 10.0), 0.2, 1.0); Color *c_op_mod = RGBA(1, 0.2 + 0.3 * psin(global.frames / 10.0), 0.2, 1.0);
if(pw <= PLR_MAX_POWER) { if(pw <= PLR_MAX_POWER_EFFECTIVE) {
c_whole = &stagedraw.hud_text.color.active; c_whole = &stagedraw.hud_text.color.active;
c_fract = &stagedraw.hud_text.color.inactive; c_fract = &stagedraw.hud_text.color.inactive;
} else if(pw - PLR_MAX_POWER < 100) { } else if(pw - PLR_MAX_POWER_EFFECTIVE < 100) {
c_whole = &stagedraw.hud_text.color.active; c_whole = &stagedraw.hud_text.color.active;
c_fract = color_mul(color_copy(&c_fract_buf, &stagedraw.hud_text.color.inactive), c_op_mod); c_fract = color_mul(color_copy(&c_fract_buf, &stagedraw.hud_text.color.inactive), c_op_mod);
} else { } else {
@ -1136,7 +1136,7 @@ static inline void stage_draw_hud_power_value(float xpos, float ypos) {
}); });
draw_fraction( draw_fraction(
PLR_MAX_POWER / 100.0, PLR_MAX_POWER_EFFECTIVE / 100.0,
ALIGN_LEFT, ALIGN_LEFT,
xpos, xpos,
ypos, ypos,