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
|
|
|
*/
|
|
|
|
|
|
|
|
#include "player.h"
|
|
|
|
|
|
|
|
#include "projectile.h"
|
|
|
|
#include "global.h"
|
2011-04-10 11:19:44 +02:00
|
|
|
#include "plrmodes.h"
|
2012-08-17 20:58:23 +02:00
|
|
|
#include "stage.h"
|
2017-09-16 03:15:34 +02:00
|
|
|
#include "stagetext.h"
|
2010-10-12 10:55:23 +02:00
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
void player_init(Player *plr) {
|
2011-06-25 12:41:40 +02:00
|
|
|
memset(plr, 0, sizeof(Player));
|
2017-04-10 00:02:56 +02:00
|
|
|
plr->pos = VIEWPORT_W/2 + I*(VIEWPORT_H-64);
|
2017-03-21 11:09:32 +01:00
|
|
|
plr->lives = PLR_START_LIVES;
|
2011-07-02 12:45:32 +02:00
|
|
|
plr->bombs = PLR_START_BOMBS;
|
2011-06-25 12:41:40 +02:00
|
|
|
plr->deathtime = -1;
|
2017-10-08 13:30:51 +02:00
|
|
|
plr->mode = plrmode_find(0, 0);
|
2011-07-04 09:14:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
void player_stage_pre_init(Player *plr) {
|
2017-02-15 17:09:09 +01:00
|
|
|
plr->recovery = 0;
|
|
|
|
plr->respawntime = 0;
|
|
|
|
plr->deathtime = -1;
|
|
|
|
plr->graze = 0;
|
|
|
|
plr->axis_lr = 0;
|
|
|
|
plr->axis_ud = 0;
|
2017-10-08 23:12:38 +02:00
|
|
|
plrmode_preload(plr->mode);
|
2017-10-08 13:30:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void player_stage_post_init(Player *plr) {
|
|
|
|
// TODO: remove handle_fullpower from player_set_power and get rid of this hack
|
|
|
|
short power = global.plr.power;
|
|
|
|
global.plr.power = -1;
|
|
|
|
delete_enemies(&global.plr.slaves);
|
|
|
|
player_set_power(&global.plr, power, false);
|
|
|
|
|
|
|
|
assert(plr->mode != NULL);
|
|
|
|
aniplayer_create(&plr->ani, get_ani(plr->mode->character->player_sprite_name));
|
|
|
|
|
|
|
|
// ensure the essential callbacks are there. other code tests only for the optional ones
|
|
|
|
assert(plr->mode->procs.shot != NULL);
|
|
|
|
assert(plr->mode->procs.bomb != NULL);
|
2017-02-15 17:09:09 +01:00
|
|
|
}
|
|
|
|
|
2017-03-19 03:56:55 +01:00
|
|
|
static void player_full_power(Player *plr) {
|
|
|
|
play_sound("full_power");
|
2017-04-06 00:46:00 +02:00
|
|
|
stage_clear_hazards(false);
|
2017-09-16 03:15:34 +02:00
|
|
|
stagetext_add("Full Power!", VIEWPORT_W * 0.5 + VIEWPORT_H * 0.33 * I, AL_Center, _fonts.mainmenu, rgb(1, 1, 1), 0, 60, 20, 20);
|
2017-03-19 03:56:55 +01:00
|
|
|
}
|
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
bool player_set_power(Player *plr, short npow, bool handle_fullpower) {
|
2017-03-30 17:11:21 +02:00
|
|
|
npow = clamp(npow, 0, PLR_MAX_POWER);
|
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
if(plr->mode->procs.power) {
|
|
|
|
plr->mode->procs.power(plr, npow);
|
2011-07-04 09:14:08 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-03-19 03:56:55 +01:00
|
|
|
int oldpow = plr->power;
|
2011-07-04 09:14:08 +02:00
|
|
|
plr->power = npow;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-19 11:52:18 +02:00
|
|
|
if(plr->power == PLR_MAX_POWER && oldpow < PLR_MAX_POWER && handle_fullpower) {
|
2017-03-19 03:56:55 +01:00
|
|
|
player_full_power(plr);
|
|
|
|
}
|
2017-09-19 20:46:28 +02:00
|
|
|
|
|
|
|
return oldpow != plr->power;
|
2011-07-04 09:14:08 +02:00
|
|
|
}
|
|
|
|
|
2012-07-20 16:11:24 +02:00
|
|
|
void player_move(Player *plr, complex delta) {
|
2017-03-06 15:24:57 +01:00
|
|
|
float speed = 0.01*VIEWPORT_W;
|
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
if(plr->inputflags & INFLAG_FOCUS) {
|
2017-03-06 15:24:57 +01:00
|
|
|
speed /= 2.0;
|
2017-10-08 13:30:51 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
if(plr->mode->procs.speed_mod) {
|
|
|
|
speed = plr->mode->procs.speed_mod(plr, speed);
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-07-05 15:20:19 +02:00
|
|
|
complex opos = plr->pos - VIEWPORT_W/2.0 - VIEWPORT_H/2.0*I;
|
|
|
|
complex npos = opos + delta*speed;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
Animation *ani = plr->ani.ani;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-06 12:19:17 +02:00
|
|
|
bool xfac = fabs(creal(npos)) < fabs(creal(opos)) || fabs(creal(npos)) < VIEWPORT_W/2.0 - ani->w/2;
|
|
|
|
bool yfac = fabs(cimag(npos)) < fabs(cimag(opos)) || fabs(cimag(npos)) < VIEWPORT_H/2.0 - ani->h/2;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-06 12:19:17 +02:00
|
|
|
complex lastpos = plr->pos;
|
2011-07-05 15:20:19 +02:00
|
|
|
plr->pos += (creal(delta)*xfac + cimag(delta)*yfac*I)*speed;
|
2017-10-06 12:19:17 +02:00
|
|
|
complex realdir = plr->pos - lastpos;
|
|
|
|
|
|
|
|
if(cabs(realdir)) {
|
|
|
|
plr->lastmovedir = realdir / cabs(realdir);
|
|
|
|
}
|
2011-07-05 15:20:19 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-04-10 11:19:44 +02:00
|
|
|
void player_draw(Player* plr) {
|
2017-09-13 15:41:28 +02:00
|
|
|
// FIXME: death animation?
|
2017-03-19 03:14:28 +01:00
|
|
|
if(plr->deathtime > global.frames)
|
|
|
|
return;
|
|
|
|
|
2011-04-26 12:04:45 +02:00
|
|
|
draw_enemies(plr->slaves);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2010-10-12 10:55:23 +02:00
|
|
|
glPushMatrix();
|
2011-03-18 19:03:06 +01:00
|
|
|
glTranslatef(creal(plr->pos), cimag(plr->pos), 0);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-03-06 15:24:57 +01:00
|
|
|
if(plr->focus) {
|
2010-11-28 12:54:13 +01:00
|
|
|
glPushMatrix();
|
|
|
|
glRotatef(global.frames*10, 0, 0, 1);
|
2011-03-19 09:11:05 +01:00
|
|
|
glScalef(1, 1, 1);
|
2017-03-06 15:24:57 +01:00
|
|
|
glColor4f(1, 1, 1, 0.2 * (clamp(plr->focus, 0, 15) / 15.0));
|
2011-03-19 16:21:48 +01:00
|
|
|
draw_texture(0, 0, "fairy_circle");
|
2010-11-28 12:54:13 +01:00
|
|
|
glColor4f(1,1,1,1);
|
|
|
|
glPopMatrix();
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
|
|
|
|
2012-04-05 14:24:55 +02:00
|
|
|
int clr_changed = 0;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-04-05 14:24:55 +02:00
|
|
|
if(global.frames - abs(plr->recovery) < 0 && (global.frames/8)&1) {
|
2011-06-28 19:23:02 +02:00
|
|
|
glColor4f(0.4,0.4,1,0.9);
|
2012-04-05 14:24:55 +02:00
|
|
|
clr_changed = 1;
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-10-03 17:25:38 +02:00
|
|
|
aniplayer_play(&plr->ani,0,0);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-04-05 14:24:55 +02:00
|
|
|
if(clr_changed)
|
|
|
|
glColor3f(1,1,1);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-03-06 15:24:57 +01:00
|
|
|
if(plr->focus) {
|
2010-11-28 12:54:13 +01:00
|
|
|
glPushMatrix();
|
2017-03-06 15:24:57 +01:00
|
|
|
glColor4f(1, 1, 1, plr->focus / 30.0);
|
2010-11-28 12:54:13 +01:00
|
|
|
glRotatef(global.frames, 0, 0, -1);
|
2011-03-19 16:21:48 +01:00
|
|
|
draw_texture(0, 0, "focus");
|
2017-03-06 15:24:57 +01:00
|
|
|
glColor4f(1, 1, 1, 1);
|
2010-11-28 12:54:13 +01:00
|
|
|
glPopMatrix();
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2010-10-12 10:55:23 +02:00
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
|
2017-04-04 02:57:38 +02:00
|
|
|
static void player_fail_spell(Player *plr) {
|
|
|
|
if( !global.boss ||
|
|
|
|
!global.boss->current ||
|
|
|
|
global.boss->current->finished ||
|
|
|
|
global.boss->current->failtime ||
|
2017-10-01 00:41:32 +02:00
|
|
|
global.boss->current->starttime >= global.frames ||
|
2017-04-04 02:57:38 +02:00
|
|
|
global.stage->type == STAGE_SPELL
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
global.boss->current->failtime = global.frames;
|
|
|
|
|
|
|
|
if(global.boss->current->type == AT_ExtraSpell) {
|
|
|
|
boss_finish_current_attack(global.boss);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
bool player_should_shoot(Player *plr, bool extra) {
|
2017-10-16 21:41:50 +02:00
|
|
|
return (plr->inputflags & INFLAG_SHOT) && !global.dialog &&
|
2017-10-08 13:30:51 +02:00
|
|
|
(!extra || (global.frames - plr->recovery >= 0 && plr->deathtime >= -1));
|
|
|
|
}
|
|
|
|
|
2011-06-24 19:16:05 +02:00
|
|
|
void player_logic(Player* plr) {
|
2011-07-05 15:20:19 +02:00
|
|
|
process_enemies(&plr->slaves);
|
2017-10-03 17:25:38 +02:00
|
|
|
aniplayer_update(&plr->ani);
|
2011-07-04 14:10:12 +02:00
|
|
|
if(plr->deathtime < -1) {
|
|
|
|
plr->deathtime++;
|
2017-04-30 18:50:55 +02:00
|
|
|
plr->pos -= I;
|
2011-07-04 14:10:12 +02:00
|
|
|
return;
|
|
|
|
}
|
2012-08-11 14:24:58 +02:00
|
|
|
|
2017-03-06 15:24:57 +01:00
|
|
|
plr->focus = approach(plr->focus, (plr->inputflags & INFLAG_FOCUS) ? 30 : 0, 1);
|
2012-08-11 14:24:58 +02:00
|
|
|
|
2017-10-08 13:30:51 +02:00
|
|
|
if(plr->mode->procs.think) {
|
|
|
|
plr->mode->procs.think(plr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(player_should_shoot(plr, false)) {
|
|
|
|
plr->mode->procs.shot(plr);
|
2012-08-03 17:06:25 +02:00
|
|
|
}
|
|
|
|
|
2011-06-25 12:41:40 +02:00
|
|
|
if(global.frames == plr->deathtime)
|
2012-07-20 16:11:24 +02:00
|
|
|
player_realdeath(plr);
|
2012-08-11 14:24:58 +02:00
|
|
|
|
2011-11-01 21:20:40 +01:00
|
|
|
if(global.frames - plr->recovery < 0) {
|
|
|
|
Enemy *en;
|
|
|
|
for(en = global.enemies; en; en = en->next)
|
2017-10-15 11:57:47 +02:00
|
|
|
if(en->hp > ENEMY_IMMUNE)
|
2012-07-28 19:45:51 +02:00
|
|
|
en->hp -= 300;
|
2012-08-11 14:24:58 +02:00
|
|
|
|
2017-09-27 11:08:32 +02:00
|
|
|
if(global.boss) {
|
|
|
|
boss_damage(global.boss, 30);
|
2012-07-28 19:45:51 +02:00
|
|
|
}
|
2017-04-04 02:57:38 +02:00
|
|
|
|
2017-04-06 00:46:00 +02:00
|
|
|
stage_clear_hazards(false);
|
2017-04-04 02:57:38 +02:00
|
|
|
player_fail_spell(plr);
|
2011-11-01 21:20:40 +01:00
|
|
|
}
|
2011-06-24 19:16:05 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
bool player_bomb(Player *plr) {
|
2012-08-16 15:07:14 +02:00
|
|
|
if(global.boss && global.boss->current && global.boss->current->type == AT_ExtraSpell)
|
2017-09-19 20:46:28 +02:00
|
|
|
return false;
|
2012-08-16 15:07:14 +02:00
|
|
|
|
2017-02-11 02:24:47 +01:00
|
|
|
if(global.frames - plr->recovery >= 0 && (plr->bombs > 0 || plr->iddqd) && global.frames - plr->respawntime >= 60) {
|
2017-04-04 02:57:38 +02:00
|
|
|
player_fail_spell(plr);
|
2017-10-08 13:30:51 +02:00
|
|
|
stage_clear_hazards(false);
|
|
|
|
plr->mode->procs.bomb(plr);
|
2011-06-26 13:45:27 +02:00
|
|
|
plr->bombs--;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-06-25 12:41:40 +02:00
|
|
|
if(plr->deathtime > 0) {
|
|
|
|
plr->deathtime = -1;
|
2017-03-19 03:05:06 +01:00
|
|
|
|
|
|
|
if(plr->bombs)
|
|
|
|
plr->bombs--;
|
2017-02-10 23:05:22 +01:00
|
|
|
}
|
|
|
|
|
2017-02-11 02:24:47 +01:00
|
|
|
if(plr->bombs < 0) {
|
|
|
|
plr->bombs = 0;
|
|
|
|
}
|
|
|
|
|
2011-07-05 15:20:19 +02:00
|
|
|
plr->recovery = global.frames + BOMB_RECOVERY;
|
2017-09-19 20:46:28 +02:00
|
|
|
|
|
|
|
return true;
|
2011-03-23 12:26:30 +01:00
|
|
|
}
|
2017-09-19 20:46:28 +02:00
|
|
|
|
|
|
|
return false;
|
2011-03-23 12:26:30 +01:00
|
|
|
}
|
|
|
|
|
2012-07-20 16:11:24 +02:00
|
|
|
void player_realdeath(Player *plr) {
|
2011-07-04 14:10:12 +02:00
|
|
|
plr->deathtime = -DEATH_DELAY-1;
|
2012-08-11 14:03:41 +02:00
|
|
|
plr->respawntime = global.frames;
|
2017-03-06 15:24:57 +01:00
|
|
|
plr->inputflags &= ~INFLAGS_MOVE;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-13 15:41:28 +02:00
|
|
|
complex death_origin = plr->pos;
|
2012-08-21 13:50:11 +02:00
|
|
|
|
2017-02-23 11:39:31 +01:00
|
|
|
plr->pos = VIEWPORT_W/2 + VIEWPORT_H*I+30.0*I;
|
2011-07-04 14:21:36 +02:00
|
|
|
plr->recovery = -(global.frames + DEATH_DELAY + 150);
|
2017-09-27 11:17:14 +02:00
|
|
|
stage_clear_hazards(false);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-11 02:47:36 +01:00
|
|
|
if(plr->iddqd)
|
|
|
|
return;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-04-04 02:57:38 +02:00
|
|
|
player_fail_spell(plr);
|
2017-09-13 15:41:28 +02:00
|
|
|
|
|
|
|
if(global.stage->type != STAGE_SPELL && global.boss && global.boss->current && global.boss->current->type == AT_ExtraSpell) {
|
|
|
|
// deaths in extra spells "don't count"
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int drop = max(2, (plr->power * 0.15) / POWER_VALUE);
|
|
|
|
spawn_items(death_origin, Power, drop, NULL);
|
|
|
|
|
2017-09-19 11:52:18 +02:00
|
|
|
player_set_power(plr, plr->power * 0.7,true);
|
2017-09-13 15:41:28 +02:00
|
|
|
plr->bombs = PLR_START_BOMBS;
|
|
|
|
plr->bomb_fragments = 0;
|
2017-02-12 01:11:25 +01:00
|
|
|
|
2017-09-13 15:41:28 +02:00
|
|
|
if(plr->lives-- == 0 && global.replaymode != REPLAY_PLAY) {
|
2012-08-17 20:58:23 +02:00
|
|
|
stage_gameover();
|
2017-10-03 08:17:59 +02:00
|
|
|
|
|
|
|
if(plr->lives > 0) {
|
|
|
|
// game continued
|
|
|
|
spawn_items(death_origin, Power, (int)ceil(PLR_MAX_POWER/(double)POWER_VALUE), NULL);
|
|
|
|
}
|
2017-09-13 15:41:28 +02:00
|
|
|
}
|
2011-06-25 12:41:40 +02:00
|
|
|
}
|
|
|
|
|
2012-07-20 16:11:24 +02:00
|
|
|
void player_death(Player *plr) {
|
2011-06-29 17:01:03 +02:00
|
|
|
if(plr->deathtime == -1 && global.frames - abs(plr->recovery) > 0) {
|
2017-01-28 19:56:17 +01:00
|
|
|
play_sound("death");
|
2011-06-28 19:23:02 +02:00
|
|
|
int i;
|
2012-08-07 02:45:38 +02:00
|
|
|
for(i = 0; i < 20; i++) {
|
|
|
|
tsrand_fill(2);
|
2017-03-03 18:41:49 +01:00
|
|
|
create_particle2c("flare", plr->pos, 0, Shrink, timeout_linear, 40, (3+afrand(0)*7)*cexp(I*tsrand_a(1)))->type=PlrProj;
|
2012-08-07 02:45:38 +02:00
|
|
|
}
|
2017-03-09 13:21:36 +01:00
|
|
|
create_particle2c("blast", plr->pos, rgba(1,0.3,0.3,0.5), GrowFadeAdd, timeout, 35, 2.4)->type=PlrProj;
|
2011-06-28 19:23:02 +02:00
|
|
|
plr->deathtime = global.frames + DEATHBOMB_TIME;
|
|
|
|
}
|
2011-07-02 11:39:36 +02:00
|
|
|
}
|
2012-07-14 16:37:52 +02:00
|
|
|
|
2017-03-06 15:24:57 +01:00
|
|
|
static PlrInputFlag key_to_inflag(KeyIndex key) {
|
2012-07-14 16:37:52 +02:00
|
|
|
switch(key) {
|
2017-03-06 15:24:57 +01:00
|
|
|
case KEY_UP: return INFLAG_UP; break;
|
|
|
|
case KEY_DOWN: return INFLAG_DOWN; break;
|
|
|
|
case KEY_LEFT: return INFLAG_LEFT; break;
|
|
|
|
case KEY_RIGHT: return INFLAG_RIGHT; break;
|
|
|
|
case KEY_FOCUS: return INFLAG_FOCUS; break;
|
|
|
|
case KEY_SHOT: return INFLAG_SHOT; break;
|
2017-09-19 20:46:28 +02:00
|
|
|
case KEY_SKIP: return INFLAG_SKIP; break;
|
2017-03-06 15:24:57 +01:00
|
|
|
default: return 0;
|
2012-07-14 16:37:52 +02:00
|
|
|
}
|
2017-03-06 15:24:57 +01:00
|
|
|
}
|
|
|
|
|
2017-10-01 02:48:55 +02:00
|
|
|
bool player_updateinputflags(Player *plr, PlrInputFlag flags) {
|
|
|
|
if(flags == plr->inputflags) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
plr->inputflags = flags;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool player_updateinputflags_moveonly(Player *plr, PlrInputFlag flags) {
|
|
|
|
return player_updateinputflags(plr, (flags & INFLAGS_MOVE) | (plr->inputflags & ~INFLAGS_MOVE));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool player_setinputflag(Player *plr, KeyIndex key, bool mode) {
|
|
|
|
PlrInputFlag newflags = plr->inputflags;
|
|
|
|
PlrInputFlag keyflag = key_to_inflag(key);
|
|
|
|
|
|
|
|
if(!keyflag) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-06 15:24:57 +01:00
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
if(mode) {
|
|
|
|
newflags |= keyflag;
|
2012-08-12 18:00:56 +02:00
|
|
|
} else {
|
2017-09-19 20:46:28 +02:00
|
|
|
newflags &= ~keyflag;
|
2012-08-12 18:00:56 +02:00
|
|
|
}
|
2017-09-19 20:46:28 +02:00
|
|
|
|
2017-10-06 12:19:17 +02:00
|
|
|
return player_updateinputflags(plr, newflags);
|
2012-07-14 16:37:52 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
static bool player_set_axis(int *aptr, uint16_t value) {
|
|
|
|
int16_t new = (int16_t)value;
|
|
|
|
|
|
|
|
if(*aptr == new) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aptr = new;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool player_event(Player *plr, uint8_t type, uint16_t value) {
|
|
|
|
bool useful = true;
|
|
|
|
|
2012-07-14 16:37:52 +02:00
|
|
|
switch(type) {
|
|
|
|
case EV_PRESS:
|
2017-09-19 20:46:28 +02:00
|
|
|
if(global.dialog && (value == KEY_SHOT || value == KEY_BOMB)) {
|
|
|
|
page_dialog(&global.dialog);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(value) {
|
2012-07-14 16:37:52 +02:00
|
|
|
case KEY_BOMB:
|
2017-09-19 20:46:28 +02:00
|
|
|
useful = player_bomb(plr);
|
2012-07-14 16:37:52 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-11 02:24:47 +01:00
|
|
|
case KEY_IDDQD:
|
2017-02-11 02:56:34 +01:00
|
|
|
plr->iddqd = !plr->iddqd;
|
2017-02-11 02:24:47 +01:00
|
|
|
break;
|
|
|
|
|
2017-03-06 13:02:46 +01:00
|
|
|
case KEY_POWERUP:
|
2017-09-19 20:46:28 +02:00
|
|
|
useful = player_set_power(plr, plr->power + 100, true);
|
2017-03-06 13:02:46 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case KEY_POWERDOWN:
|
2017-09-19 20:46:28 +02:00
|
|
|
useful = player_set_power(plr, plr->power - 100, true);
|
2017-03-06 13:02:46 +01:00
|
|
|
break;
|
|
|
|
|
2012-07-14 16:37:52 +02:00
|
|
|
default:
|
2017-09-19 20:46:28 +02:00
|
|
|
useful = player_setinputflag(plr, value, true);
|
2012-07-14 16:37:52 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-14 16:37:52 +02:00
|
|
|
case EV_RELEASE:
|
2017-09-19 20:46:28 +02:00
|
|
|
player_setinputflag(plr, value, false);
|
2012-07-14 16:37:52 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-15 16:36:39 +02:00
|
|
|
case EV_AXIS_LR:
|
2017-09-19 20:46:28 +02:00
|
|
|
useful = player_set_axis(&plr->axis_lr, value);
|
2012-08-15 16:36:39 +02:00
|
|
|
break;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-15 16:36:39 +02:00
|
|
|
case EV_AXIS_UD:
|
2017-09-19 20:46:28 +02:00
|
|
|
useful = player_set_axis(&plr->axis_ud, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EV_INFLAGS:
|
|
|
|
useful = player_updateinputflags(plr, value);
|
2012-08-15 16:36:39 +02:00
|
|
|
break;
|
2017-03-21 03:28:35 +01:00
|
|
|
|
|
|
|
default:
|
2017-09-19 20:46:28 +02:00
|
|
|
log_warn("Can not handle event: [%i:%02x:%04x]", global.frames, type, value);
|
|
|
|
useful = false;
|
2017-03-21 03:28:35 +01:00
|
|
|
break;
|
2012-07-14 16:37:52 +02:00
|
|
|
}
|
2017-09-19 20:46:28 +02:00
|
|
|
|
|
|
|
return useful;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool player_event_with_replay(Player *plr, uint8_t type, uint16_t value) {
|
|
|
|
assert(global.replaymode == REPLAY_RECORD);
|
|
|
|
|
|
|
|
if(player_event(plr, type, value)) {
|
|
|
|
replay_stage_event(global.replay_stage, global.frames, type, value);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
log_debug("Useless event discarded: [%i:%02x:%04x]", global.frames, type, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2012-07-14 16:37:52 +02:00
|
|
|
}
|
|
|
|
|
2012-08-15 16:36:39 +02:00
|
|
|
// free-axis movement
|
2017-02-11 04:52:08 +01:00
|
|
|
bool player_applymovement_gamepad(Player *plr) {
|
2017-02-04 14:41:15 +01:00
|
|
|
if(!plr->axis_lr && !plr->axis_ud) {
|
|
|
|
if(plr->gamepadmove) {
|
2017-02-11 04:52:08 +01:00
|
|
|
plr->gamepadmove = false;
|
2017-03-06 15:24:57 +01:00
|
|
|
plr->inputflags &= ~INFLAGS_MOVE;
|
2017-02-04 14:41:15 +01:00
|
|
|
}
|
2017-02-11 04:52:08 +01:00
|
|
|
return false;
|
2017-02-04 14:41:15 +01:00
|
|
|
}
|
|
|
|
|
2017-02-27 23:58:47 +01:00
|
|
|
complex direction = (plr->axis_lr + plr->axis_ud*I) / (double)GAMEPAD_AXIS_MAX;
|
2012-08-15 17:03:53 +02:00
|
|
|
if(cabs(direction) > 1)
|
|
|
|
direction /= cabs(direction);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-03-06 16:32:51 +01:00
|
|
|
int sr = sign(creal(direction));
|
|
|
|
int si = sign(cimag(direction));
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
player_updateinputflags_moveonly(plr,
|
|
|
|
(INFLAG_UP * (si == -1)) |
|
|
|
|
(INFLAG_DOWN * (si == 1)) |
|
|
|
|
(INFLAG_LEFT * (sr == -1)) |
|
|
|
|
(INFLAG_RIGHT * (sr == 1))
|
|
|
|
);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-04 14:41:15 +01:00
|
|
|
if(direction) {
|
2017-02-11 04:52:08 +01:00
|
|
|
plr->gamepadmove = true;
|
2012-08-15 16:36:39 +02:00
|
|
|
player_move(&global.plr, direction);
|
2017-02-04 14:41:15 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-11 04:52:08 +01:00
|
|
|
return true;
|
2012-08-15 16:36:39 +02:00
|
|
|
}
|
|
|
|
|
2017-10-03 17:25:38 +02:00
|
|
|
static void player_ani_moving(Player *plr, bool moving, bool dir) {
|
|
|
|
plr->ani.stdrow = !moving;
|
|
|
|
plr->ani.mirrored = dir;
|
|
|
|
}
|
|
|
|
|
2012-08-17 20:58:23 +02:00
|
|
|
void player_applymovement(Player *plr) {
|
2012-07-14 16:37:52 +02:00
|
|
|
if(plr->deathtime < -1)
|
|
|
|
return;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-11 04:52:08 +01:00
|
|
|
bool gamepad = player_applymovement_gamepad(plr);
|
2017-10-03 17:25:38 +02:00
|
|
|
player_ani_moving(plr,false,false);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-03-06 15:24:57 +01:00
|
|
|
int up = plr->inputflags & INFLAG_UP,
|
|
|
|
down = plr->inputflags & INFLAG_DOWN,
|
|
|
|
left = plr->inputflags & INFLAG_LEFT,
|
|
|
|
right = plr->inputflags & INFLAG_RIGHT;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-14 16:37:52 +02:00
|
|
|
if(left && !right) {
|
2017-10-03 17:25:38 +02:00
|
|
|
player_ani_moving(plr,true,true);
|
2012-07-14 16:37:52 +02:00
|
|
|
} else if(right && !left) {
|
2017-10-03 17:25:38 +02:00
|
|
|
player_ani_moving(plr,true,false);
|
2012-08-15 16:36:39 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-15 16:36:39 +02:00
|
|
|
if(gamepad)
|
|
|
|
return;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-14 16:37:52 +02:00
|
|
|
complex direction = 0;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-02-23 11:39:31 +01:00
|
|
|
if(up) direction -= 1.0*I;
|
|
|
|
if(down) direction += 1.0*I;
|
2012-07-14 16:37:52 +02:00
|
|
|
if(left) direction -= 1;
|
|
|
|
if(right) direction += 1;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-20 16:11:24 +02:00
|
|
|
if(cabs(direction))
|
|
|
|
direction /= cabs(direction);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-14 16:37:52 +02:00
|
|
|
if(direction)
|
2012-07-20 16:11:24 +02:00
|
|
|
player_move(&global.plr, direction);
|
2012-08-17 20:58:23 +02:00
|
|
|
}
|
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
void player_fix_input(Player *plr) {
|
|
|
|
// correct input state to account for any events we might have missed,
|
|
|
|
// usually because the pause menu ate them up
|
|
|
|
|
|
|
|
PlrInputFlag newflags = plr->inputflags;
|
2017-03-07 00:57:14 +01:00
|
|
|
|
2017-03-06 15:24:57 +01:00
|
|
|
for(KeyIndex key = KEYIDX_FIRST; key <= KEYIDX_LAST; ++key) {
|
|
|
|
int flag = key_to_inflag(key);
|
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
if(flag && !(plr->gamepadmove && (flag & INFLAGS_MOVE))) {
|
2017-03-06 15:24:57 +01:00
|
|
|
bool flagset = plr->inputflags & flag;
|
|
|
|
bool keyheld = gamekeypressed(key);
|
|
|
|
|
|
|
|
if(flagset && !keyheld) {
|
2017-09-19 20:46:28 +02:00
|
|
|
newflags &= ~flag;
|
2017-03-06 15:24:57 +01:00
|
|
|
} else if(!flagset && keyheld) {
|
2017-09-19 20:46:28 +02:00
|
|
|
newflags |= flag;
|
2017-03-06 15:24:57 +01:00
|
|
|
}
|
2017-09-19 20:46:28 +02:00
|
|
|
}
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-19 20:46:28 +02:00
|
|
|
if(newflags != plr->inputflags) {
|
|
|
|
player_event_with_replay(plr, EV_INFLAGS, newflags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(config_get_int(CONFIG_GAMEPAD_AXIS_FREE)) {
|
|
|
|
int axis_lr = gamepad_get_player_axis_value(PLRAXIS_LR);
|
|
|
|
int axis_ud = gamepad_get_player_axis_value(PLRAXIS_UD);
|
|
|
|
|
|
|
|
if(plr->axis_lr != axis_lr) {
|
|
|
|
player_event_with_replay(plr, EV_AXIS_LR, axis_lr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(plr->axis_ud != axis_ud) {
|
|
|
|
player_event_with_replay(plr, EV_AXIS_UD, axis_ud);
|
2012-07-29 15:27:15 +02:00
|
|
|
}
|
|
|
|
}
|
2012-07-14 16:37:52 +02:00
|
|
|
}
|
2012-08-14 16:14:53 +02:00
|
|
|
|
|
|
|
void player_graze(Player *plr, complex pos, int pts) {
|
|
|
|
plr->graze++;
|
2017-03-25 19:18:24 +01:00
|
|
|
|
|
|
|
player_add_points(&global.plr, pts);
|
2012-08-14 16:14:53 +02:00
|
|
|
play_sound("graze");
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-08-14 16:14:53 +02:00
|
|
|
int i = 0; for(i = 0; i < 5; ++i) {
|
|
|
|
tsrand_fill(3);
|
2017-03-03 18:41:49 +01:00
|
|
|
create_particle2c("flare", pos, 0, Shrink, timeout_linear, 5 + 5 * afrand(2), (1+afrand(0)*5)*cexp(I*tsrand_a(1)))->type=PlrProj;
|
2012-08-14 16:14:53 +02:00
|
|
|
}
|
|
|
|
}
|
2017-03-11 04:41:57 +01:00
|
|
|
|
2017-03-21 11:09:32 +01:00
|
|
|
static void player_add_fragments(Player *plr, int frags, int *pwhole, int *pfrags, int maxfrags, int maxwhole, const char *fragsnd, const char *upsnd) {
|
|
|
|
if(*pwhole >= maxwhole) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pfrags += frags;
|
|
|
|
int up = *pfrags / maxfrags;
|
|
|
|
|
|
|
|
*pwhole += up;
|
|
|
|
*pfrags %= maxfrags;
|
|
|
|
|
|
|
|
if(up) {
|
|
|
|
play_sound(upsnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(frags) {
|
|
|
|
// FIXME: when we have the extra life/bomb sounds,
|
|
|
|
// don't play this if upsnd was just played.
|
|
|
|
play_sound(fragsnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(*pwhole > maxwhole) {
|
|
|
|
*pwhole = maxwhole;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_add_life_fragments(Player *plr, int frags) {
|
|
|
|
player_add_fragments(plr, frags, &plr->lives, &plr->life_fragments, PLR_MAX_LIFE_FRAGMENTS, PLR_MAX_LIVES,
|
|
|
|
"item_generic", // FIXME: replacement needed
|
|
|
|
"extra_life"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_add_bomb_fragments(Player *plr, int frags) {
|
|
|
|
player_add_fragments(plr, frags, &plr->bombs, &plr->bomb_fragments, PLR_MAX_BOMB_FRAGMENTS, PLR_MAX_BOMBS,
|
|
|
|
"item_generic", // FIXME: replacement needed
|
|
|
|
"extra_bomb"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_add_lives(Player *plr, int lives) {
|
|
|
|
player_add_life_fragments(plr, PLR_MAX_LIFE_FRAGMENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_add_bombs(Player *plr, int bombs) {
|
|
|
|
player_add_bomb_fragments(plr, PLR_MAX_BOMB_FRAGMENTS);
|
|
|
|
}
|
|
|
|
|
2017-03-25 19:18:24 +01:00
|
|
|
|
|
|
|
static void try_spawn_bonus_item(Player *plr, ItemType type, unsigned int oldpoints, unsigned int reqpoints) {
|
|
|
|
int items = plr->points / reqpoints - oldpoints / reqpoints;
|
|
|
|
|
|
|
|
if(items > 0) {
|
|
|
|
complex p = creal(plr->pos);
|
|
|
|
create_item(p, -5*I, type);
|
|
|
|
spawn_items(p, type, --items, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void player_add_points(Player *plr, unsigned int points) {
|
|
|
|
unsigned int old = plr->points;
|
|
|
|
plr->points += points;
|
|
|
|
|
2017-03-27 13:08:26 +02:00
|
|
|
if(global.stage->type != STAGE_SPELL) {
|
|
|
|
try_spawn_bonus_item(plr, LifeFrag, old, PLR_SCORE_PER_LIFE_FRAG);
|
|
|
|
try_spawn_bonus_item(plr, BombFrag, old, PLR_SCORE_PER_BOMB_FRAG);
|
|
|
|
}
|
2017-03-25 19:18:24 +01:00
|
|
|
}
|
|
|
|
|
2017-03-11 04:41:57 +01:00
|
|
|
void player_preload(void) {
|
|
|
|
const int flags = RESF_DEFAULT;
|
|
|
|
|
|
|
|
preload_resources(RES_TEXTURE, flags,
|
|
|
|
"focus",
|
|
|
|
"fairy_circle",
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
preload_resources(RES_SFX, flags | RESF_OPTIONAL,
|
|
|
|
"graze",
|
|
|
|
"death",
|
2017-03-13 22:59:49 +01:00
|
|
|
"generic_shot",
|
2017-03-19 03:56:55 +01:00
|
|
|
"full_power",
|
2017-03-21 11:09:32 +01:00
|
|
|
"extra_life",
|
|
|
|
"extra_bomb",
|
2017-03-11 04:41:57 +01:00
|
|
|
NULL);
|
|
|
|
}
|