/* * This software is licensed under the terms of the MIT-License * See COPYING for further information. * --- * Copyright (C) 2011, Lukas Weber */ #include "player.h" #include "projectile.h" #include "global.h" #include "plrmodes.h" #include "stage.h" void init_player(Player *plr) { memset(plr, 0, sizeof(Player)); plr->pos = VIEWPORT_W/2 + I*(VIEWPORT_H-20); plr->lifes = PLR_START_LIVES; plr->bombs = PLR_START_BOMBS; plr->deathtime = -1; plr->continues = 0; plr->points = 0; } void prepare_player_for_next_stage(Player *plr) { plr->recovery = 0; plr->respawntime = 0; plr->deathtime = -1; plr->graze = 0; plr->movetime = 0; plr->prevmove = 0; plr->prevmovetime = 0; plr->axis_lr = 0; plr->axis_ud = 0; } Animation *player_get_ani(Character cha) { Animation *ani = NULL; switch(cha) { case Youmu: ani = get_ani("youmu"); break; case Marisa: ani = get_ani("marisa"); break; } return ani; } void player_set_power(Player *plr, short npow) { switch(plr->cha) { case Youmu: youmu_power(plr, npow); break; case Marisa: marisa_power(plr, npow); break; } plr->power = npow; if(plr->power > PLR_MAXPOWER) plr->power = PLR_MAXPOWER; if(plr->power < 0) plr->power = 0; } void player_move(Player *plr, complex delta) { float speed = 0.01*VIEWPORT_W; if(plr->inputflags & INFLAG_FOCUS) speed /= 2.0; if(plr->cha == Marisa && plr->shot == MarisaLaser && global.frames - plr->recovery < 0) speed /= 5.0; complex opos = plr->pos - VIEWPORT_W/2.0 - VIEWPORT_H/2.0*I; complex npos = opos + delta*speed; Animation *ani = player_get_ani(plr->cha); short xfac = fabs(creal(npos)) < fabs(creal(opos)) || fabs(creal(npos)) < VIEWPORT_W/2.0 - ani->w/2; short yfac = fabs(cimag(npos)) < fabs(cimag(opos)) || fabs(cimag(npos)) < VIEWPORT_H/2.0 - ani->h/2; plr->pos += (creal(delta)*xfac + cimag(delta)*yfac*I)*speed; } void player_draw(Player* plr) { draw_enemies(plr->slaves); glPushMatrix(); glTranslatef(creal(plr->pos), cimag(plr->pos), 0); if(plr->focus) { glPushMatrix(); glRotatef(global.frames*10, 0, 0, 1); glScalef(1, 1, 1); glColor4f(1, 1, 1, 0.2 * (clamp(plr->focus, 0, 15) / 15.0)); draw_texture(0, 0, "fairy_circle"); glColor4f(1,1,1,1); glPopMatrix(); } glDisable(GL_CULL_FACE); if(plr->dir) { glPushMatrix(); glScalef(-1,1,1); } int clr_changed = 0; if(global.frames - abs(plr->recovery) < 0 && (global.frames/8)&1) { glColor4f(0.4,0.4,1,0.9); clr_changed = 1; } draw_animation_p(0, 0, !plr->moving, player_get_ani(plr->cha)); if(clr_changed) glColor3f(1,1,1); if(plr->dir) glPopMatrix(); glEnable(GL_CULL_FACE); if(plr->focus) { glPushMatrix(); glColor4f(1, 1, 1, plr->focus / 30.0); glRotatef(global.frames, 0, 0, -1); draw_texture(0, 0, "focus"); glColor4f(1, 1, 1, 1); glPopMatrix(); } glPopMatrix(); } void player_logic(Player* plr) { process_enemies(&plr->slaves); if(plr->deathtime < -1) { plr->deathtime++; plr->pos -= 0.7*I; return; } plr->focus = approach(plr->focus, (plr->inputflags & INFLAG_FOCUS) ? 30 : 0, 1); switch(plr->cha) { case Youmu: youmu_shot(plr); break; case Marisa: marisa_shot(plr); break; } if(global.frames == plr->deathtime) player_realdeath(plr); if(global.frames - plr->recovery < 0) { Enemy *en; for(en = global.enemies; en; en = en->next) if(!en->unbombable && en->hp > ENEMY_IMMUNE) en->hp -= 300; Projectile *p; for(p = global.projs; p; p = p->next) if(p->type >= FairyProj) p->type = DeadProj; if(global.boss && global.boss->current) { AttackType at = global.boss->current->type; if(at != AT_Move && at != AT_SurvivalSpell) global.boss->dmg += 30; } } } void player_bomb(Player *plr) { if(global.frames - plr->recovery >= 0 && (plr->bombs > 0 || plr->iddqd) && global.frames - plr->respawntime >= 60) { delete_projectiles(&global.projs); switch(plr->cha) { case Marisa: marisa_bomb(plr); break; case Youmu: youmu_bomb(plr); break; } plr->bombs--; if(plr->deathtime > 0) { plr->deathtime = -1; plr->bombs /= 2; } if(plr->bombs < 0) { plr->bombs = 0; } plr->recovery = global.frames + BOMB_RECOVERY; } } void player_realdeath(Player *plr) { plr->deathtime = -DEATH_DELAY-1; plr->respawntime = global.frames; plr->inputflags &= ~INFLAGS_MOVE; const double arc = 0.3 * M_PI; int drop = max(2, (plr->power * 0.15) / POWER_VALUE); for(int i = 0; i < drop; ++i) { double ofs = arc * (i/((double)drop - 1)); create_item(plr->pos, (12+3*frand()) * (cexp(I*(1.5*M_PI - 0.5*arc + ofs)) - 1.0*I), Power); } plr->pos = VIEWPORT_W/2 + VIEWPORT_H*I+30.0*I; plr->recovery = -(global.frames + DEATH_DELAY + 150); if(plr->bombs < PLR_START_BOMBS) plr->bombs = PLR_START_BOMBS; if(plr->iddqd) return; player_set_power(plr, plr->power * 0.7); if(plr->lifes-- == 0 && global.replaymode != REPLAY_PLAY) stage_gameover(); } void player_death(Player *plr) { if(plr->deathtime == -1 && global.frames - abs(plr->recovery) > 0) { play_sound("death"); int i; for(i = 0; i < 20; i++) { tsrand_fill(2); create_particle2c("flare", plr->pos, 0, Shrink, timeout_linear, 40, (3+afrand(0)*7)*cexp(I*tsrand_a(1)))->type=PlrProj; } create_particle2c("blast", plr->pos, rgb(1,0.5,0.3), GrowFade, timeout, 35, 2.4)->type=PlrProj; plr->deathtime = global.frames + DEATHBOMB_TIME; } } static PlrInputFlag key_to_inflag(KeyIndex key) { switch(key) { 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; default: return 0; } } void player_setinputflag(Player *plr, KeyIndex key, bool mode) { PlrInputFlag flag = key_to_inflag(key); if(!flag) { return; } if(mode) { if(flag & INFLAGS_MOVE) { plr->prevmove = plr->curmove; plr->prevmovetime = plr->movetime; plr->curmove = flag; plr->movetime = global.frames; } plr->inputflags |= flag; } else { plr->inputflags &= ~flag; } } void player_event(Player* plr, int type, int key) { switch(type) { case EV_PRESS: switch(key) { case KEY_BOMB: player_bomb(plr); break; case KEY_IDDQD: plr->iddqd = !plr->iddqd; break; default: player_setinputflag(plr, key, true); break; } break; case EV_RELEASE: player_setinputflag(plr, key, false); break; case EV_AXIS_LR: plr->axis_lr = key; break; case EV_AXIS_UD: plr->axis_ud = key; break; } } // free-axis movement bool player_applymovement_gamepad(Player *plr) { if(!plr->axis_lr && !plr->axis_ud) { if(plr->gamepadmove) { plr->gamepadmove = false; plr->inputflags &= ~INFLAGS_MOVE; } return false; } complex direction = (plr->axis_lr + plr->axis_ud*I) / (double)GAMEPAD_AXIS_MAX; if(cabs(direction) > 1) direction /= cabs(direction); double real = creal(direction); double imag = cimag(direction); int sr = SIGN(real); int si = SIGN(imag); player_setinputflag(plr, KEY_UP, si == -1); player_setinputflag(plr, KEY_DOWN, si == 1); player_setinputflag(plr, KEY_LEFT, sr == -1); player_setinputflag(plr, KEY_RIGHT, sr == 1); if(direction) { plr->gamepadmove = true; player_move(&global.plr, direction); } return true; } void player_applymovement(Player *plr) { if(plr->deathtime < -1) return; bool gamepad = player_applymovement_gamepad(plr); plr->moving = false; int up = plr->inputflags & INFLAG_UP, down = plr->inputflags & INFLAG_DOWN, left = plr->inputflags & INFLAG_LEFT, right = plr->inputflags & INFLAG_RIGHT; if(left && !right) { plr->moving = true; plr->dir = 1; } else if(right && !left) { plr->moving = true; plr->dir = 0; } if(gamepad) return; complex direction = 0; if(up) direction -= 1.0*I; if(down) direction += 1.0*I; if(left) direction -= 1; if(right) direction += 1; if(cabs(direction)) direction /= cabs(direction); if(direction) player_move(&global.plr, direction); } void player_input_workaround(Player *plr) { if(global.dialog) return; for(KeyIndex key = KEYIDX_FIRST; key <= KEYIDX_LAST; ++key) { int flag = key_to_inflag(key); if(flag) { int event = -1; bool flagset = plr->inputflags & flag; bool keyheld = gamekeypressed(key); if(flagset && !keyheld) { // something ate the release event (most likely the ingame menu) event = EV_RELEASE; } else if(!flagset && keyheld) { // something ate the press event (most likely the ingame menu) event = EV_PRESS; } if(event != -1) { player_event(plr, event, key); replay_stage_event(global.replay_stage, global.frames, event, key); } } } } void player_graze(Player *plr, complex pos, int pts) { plr->points += pts; plr->graze++; play_sound("graze"); int i = 0; for(i = 0; i < 5; ++i) { tsrand_fill(3); create_particle2c("flare", pos, 0, Shrink, timeout_linear, 5 + 5 * afrand(2), (1+afrand(0)*5)*cexp(I*tsrand_a(1)))->type=PlrProj; } }