desperate attempt at refactoring plrmodes
This commit is contained in:
parent
0dbd045a31
commit
1992a62592
37 changed files with 1314 additions and 980 deletions
|
@ -58,6 +58,12 @@ set(SRCs
|
|||
hashtable.c
|
||||
boss.c
|
||||
plrmodes.c
|
||||
plrmodes/marisa.c
|
||||
plrmodes/marisa_a.c
|
||||
plrmodes/marisa_b.c
|
||||
plrmodes/youmu.c
|
||||
plrmodes/youmu_a.c
|
||||
plrmodes/youmu_b.c
|
||||
laser.c
|
||||
dialog.c
|
||||
fbo.c
|
||||
|
|
24
src/cli.c
24
src/cli.c
|
@ -87,13 +87,7 @@ int cli_args(int argc, char **argv, CLIAction *a) {
|
|||
|
||||
int c;
|
||||
uint16_t stageid = 0;
|
||||
|
||||
#define INVALID_CHAR ((Character)-1)
|
||||
#define INVALID_SHOT ((ShotMode)-1)
|
||||
|
||||
// these may be unsigned depending on the compiler.
|
||||
Character cha = INVALID_CHAR;
|
||||
ShotMode shot = INVALID_SHOT;
|
||||
PlayerMode *plrmode = NULL;
|
||||
|
||||
while((c = getopt_long(argc, argv, optc, opts, 0)) != -1) {
|
||||
char *endptr = NULL;
|
||||
|
@ -135,8 +129,8 @@ int cli_args(int argc, char **argv, CLIAction *a) {
|
|||
|
||||
break;
|
||||
case 's':
|
||||
if(plrmode_parse(optarg,&cha,&shot))
|
||||
log_fatal("Invalid shotmode '%s'",optarg);
|
||||
if(!(plrmode = plrmode_parse(optarg)))
|
||||
log_fatal("Invalid shotmode '%s'", optarg);
|
||||
break;
|
||||
case 'f':
|
||||
a->frameskip = 1;
|
||||
|
@ -170,12 +164,12 @@ int cli_args(int argc, char **argv, CLIAction *a) {
|
|||
}
|
||||
}
|
||||
|
||||
if(a->type != CLI_SelectStage && (cha != INVALID_CHAR || shot != INVALID_SHOT))
|
||||
log_warn("--shotmode was ignored");
|
||||
|
||||
if(cha != INVALID_CHAR && shot != INVALID_SHOT) {
|
||||
a->plrcha = cha;
|
||||
a->plrshot = shot;
|
||||
if(plrmode) {
|
||||
if(a->type == CLI_SelectStage) {
|
||||
a->plrmode = plrmode;
|
||||
} else {
|
||||
log_warn("--shotmode was ignored");
|
||||
}
|
||||
}
|
||||
|
||||
a->stageid = stageid;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "player.h"
|
||||
#include "plrmodes.h"
|
||||
|
||||
typedef enum {
|
||||
CLI_RunNormally = 0,
|
||||
|
@ -26,9 +26,7 @@ struct CLIAction {
|
|||
int stageid;
|
||||
int diff;
|
||||
int frameskip;
|
||||
|
||||
Character plrcha;
|
||||
ShotMode plrshot;
|
||||
PlayerMode *plrmode;
|
||||
};
|
||||
|
||||
int cli_args(int argc, char **argv, CLIAction *a);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Dialog *create_dialog(char *left, char *right) {
|
||||
Dialog *create_dialog(const char *left, const char *right) {
|
||||
Dialog *d = malloc(sizeof(Dialog));
|
||||
memset(d, 0, sizeof(Dialog));
|
||||
|
||||
|
@ -25,11 +25,11 @@ Dialog *create_dialog(char *left, char *right) {
|
|||
return d;
|
||||
}
|
||||
|
||||
void dset_image(Dialog *d, Side side, char *name) {
|
||||
void dset_image(Dialog *d, Side side, const char *name) {
|
||||
d->images[side] = get_tex(name);
|
||||
}
|
||||
|
||||
void dadd_msg(Dialog *d, Side side, char *msg) {
|
||||
void dadd_msg(Dialog *d, Side side, const char *msg) {
|
||||
d->messages = realloc(d->messages, (++d->count)*sizeof(DialogMessage));
|
||||
d->messages[d->count-1].side = side;
|
||||
d->messages[d->count-1].msg = malloc(strlen(msg) + 1);
|
||||
|
|
|
@ -37,9 +37,9 @@ typedef struct Dialog {
|
|||
int birthtime;
|
||||
} Dialog;
|
||||
|
||||
Dialog *create_dialog(char *left, char *right);
|
||||
void dset_image(Dialog *d, Side side, char *name);
|
||||
void dadd_msg(Dialog *d, Side side, char *msg);
|
||||
Dialog *create_dialog(const char *left, const char *right);
|
||||
void dset_image(Dialog *d, Side side, const char *name);
|
||||
void dadd_msg(Dialog *d, Side side, const char *msg);
|
||||
void delete_dialog(Dialog *d);
|
||||
|
||||
void draw_dialog(Dialog *dialog);
|
||||
|
|
23
src/ending.c
23
src/ending.c
|
@ -32,6 +32,10 @@ void add_ending_entry(Ending *e, int dur, char *msg, char *tex) {
|
|||
entry->tex = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* These ending callbacks are referenced in plrmodes/ code
|
||||
*/
|
||||
|
||||
void bad_ending_marisa(Ending *e) {
|
||||
add_ending_entry(e, 300, "After her consciousness had faded while she was falling down the tower,\nMarisa found herself waking up in a clearing of the magical forest.", NULL);
|
||||
add_ending_entry(e, 300, "She saw the sun set.", NULL);
|
||||
|
@ -72,25 +76,10 @@ void create_ending(Ending *e) {
|
|||
memset(e, 0, sizeof(Ending));
|
||||
|
||||
if(global.continues || global.diff == D_Easy) {
|
||||
switch(global.plr.cha) {
|
||||
case Marisa:
|
||||
bad_ending_marisa(e);
|
||||
break;
|
||||
case Youmu:
|
||||
bad_ending_youmu(e);
|
||||
break;
|
||||
}
|
||||
|
||||
global.plr.mode->character->ending.bad(e);
|
||||
add_ending_entry(e, 400, "Try a no continue run on higher difficulties. You can do it!", NULL);
|
||||
} else {
|
||||
switch(global.plr.cha) {
|
||||
case Marisa:
|
||||
good_ending_marisa(e);
|
||||
break;
|
||||
case Youmu:
|
||||
good_ending_youmu(e);
|
||||
break;
|
||||
}
|
||||
global.plr.mode->character->ending.good(e);
|
||||
add_ending_entry(e, 400, "Sorry, extra stage isn’t done yet. ^^", NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,4 +49,10 @@ void ending_loop(void);
|
|||
void free_ending(Ending *e);
|
||||
void ending_preload(void);
|
||||
|
||||
|
||||
/*
|
||||
* These ending callbacks are referenced in plrmodes/ code
|
||||
*/
|
||||
void bad_ending_marisa(Ending *e);
|
||||
void bad_ending_youmu(Ending *e);
|
||||
void good_ending_marisa(Ending *e);
|
||||
void good_ending_youmu(Ending *e);
|
||||
|
|
|
@ -228,9 +228,12 @@ int main(int argc, char **argv) {
|
|||
|
||||
do {
|
||||
global.game_over = 0;
|
||||
init_player(&global.plr);
|
||||
global.plr.cha = a.plrcha;
|
||||
global.plr.shot = a.plrshot;
|
||||
player_init(&global.plr);
|
||||
|
||||
if(a.plrmode) {
|
||||
global.plr.mode = a.plrmode;
|
||||
}
|
||||
|
||||
stage_loop(stg);
|
||||
} while(global.game_over == GAMEOVER_RESTART);
|
||||
|
||||
|
|
|
@ -11,20 +11,25 @@
|
|||
#include "common.h"
|
||||
#include "global.h"
|
||||
|
||||
// static to preserve between menu restarts
|
||||
static CharacterID selected_charid;
|
||||
static ShotModeID selected_shotid;
|
||||
|
||||
void set_player(MenuData *m, void *p) {
|
||||
global.plr.cha = (Character) (uintptr_t) p;
|
||||
selected_charid = (CharacterID)(uintptr_t)p;
|
||||
}
|
||||
|
||||
void set_shotmode(MenuData *m, void *p) {
|
||||
global.plr.shot = (ShotMode) (uintptr_t) p;
|
||||
selected_shotid = (ShotModeID)(uintptr_t)p;
|
||||
}
|
||||
|
||||
void create_shottype_menu(MenuData *m) {
|
||||
create_menu(m);
|
||||
m->transition = NULL;
|
||||
|
||||
add_menu_entry(m, "Laser Sign|Mirror Sign", set_shotmode, (void *) YoumuOpposite);
|
||||
add_menu_entry(m, "Star Sign|Haunting Sign", set_shotmode, (void *) YoumuHoming);
|
||||
for(uintptr_t i = 0; i < NUM_SHOT_MODES_PER_CHARACTER; ++i) {
|
||||
add_menu_entry(m, NULL, set_shotmode, (void*)i);
|
||||
}
|
||||
}
|
||||
|
||||
void char_menu_input(MenuData*);
|
||||
|
@ -41,8 +46,9 @@ void create_char_menu(MenuData *m) {
|
|||
m->context = malloc(sizeof(MenuData));
|
||||
create_shottype_menu(m->context);
|
||||
|
||||
add_menu_entry(m, "dialog/marisa|Kirisame Marisa|Black Magician", set_player, (void *)Marisa)->transition = TransFadeBlack;
|
||||
add_menu_entry(m, "dialog/youmu|Konpaku Yōmu|Half-Phantom Girl", set_player, (void *)Youmu)->transition = TransFadeBlack;
|
||||
for(uintptr_t i = 0; i < NUM_CHARACTERS; ++i) {
|
||||
add_menu_entry(m, NULL, set_player, (void*)i)->transition = TransFadeBlack;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_char_menu(MenuData *menu) {
|
||||
|
@ -55,23 +61,22 @@ void draw_char_menu(MenuData *menu) {
|
|||
glColor4f(0,0,0,0.7);
|
||||
glTranslatef(SCREEN_W/4*3, SCREEN_H/2, 0);
|
||||
glScalef(300, SCREEN_H, 1);
|
||||
|
||||
draw_quad();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
char buf[128];
|
||||
int i;
|
||||
for(i = 0; i < menu->ecount; i++) {
|
||||
strlcpy(buf, menu->entries[i].name, sizeof(buf));
|
||||
char *save;
|
||||
CharacterID current_char;
|
||||
|
||||
char *tex = strtok_r(buf,"|", &save);
|
||||
char *name = strtok_r(NULL, "|", &save);
|
||||
char *title = strtok_r(NULL, "|", &save);
|
||||
for(int i = 0; i < menu->ecount; i++) {
|
||||
PlayerCharacter *pchar = plrchar_get((CharacterID)(uintptr_t)menu->entries[i].arg);
|
||||
assert(pchar != NULL);
|
||||
|
||||
if(!(tex && name && title))
|
||||
continue;
|
||||
const char *tex = pchar->dialog_sprite_name;
|
||||
const char *name = pchar->full_name;
|
||||
const char *title = pchar->title;
|
||||
|
||||
if(menu->cursor == i) {
|
||||
current_char = pchar->id;
|
||||
}
|
||||
|
||||
menu->entries[i].drawdata += 0.08*(1.0*(menu->cursor != i) - menu->entries[i].drawdata);
|
||||
|
||||
|
@ -91,29 +96,36 @@ void draw_char_menu(MenuData *menu) {
|
|||
draw_text(AL_Center, 0, 0, name, _fonts.mainmenu);
|
||||
glPopMatrix();
|
||||
|
||||
if(menu->entries[i].drawdata)
|
||||
if(menu->entries[i].drawdata) {
|
||||
glColor4f(1,1,1,1-menu->entries[i].drawdata*3);
|
||||
draw_text(AL_Center, 0, 70, title, _fonts.standard);
|
||||
|
||||
strlcpy(buf, mod->entries[i].name, sizeof(buf));
|
||||
|
||||
char *mari = strtok_r(buf, "|", &save);
|
||||
char *youmu = strtok_r(NULL, "|", &save);
|
||||
|
||||
char *use = menu->entries[menu->cursor].arg == (void *)Marisa ? mari : youmu;
|
||||
|
||||
if(menu->entries[i].drawdata)
|
||||
} else {
|
||||
glColor4f(1,1,1,1);
|
||||
}
|
||||
|
||||
if(mod->cursor == i)
|
||||
glColor4f(0.9,0.6,0.2,1);
|
||||
draw_text(AL_Center, 0, 200+40*i, use, _fonts.standard);
|
||||
|
||||
draw_text(AL_Center, 0, 70, title, _fonts.standard);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(SCREEN_W/4*3, SCREEN_H/3, 0);
|
||||
|
||||
for(int i = 0; i < mod->ecount; i++) {
|
||||
PlayerMode *mode = plrmode_find(current_char, (ShotModeID)(uintptr_t)mod->entries[i].arg);
|
||||
assert(mode != NULL);
|
||||
|
||||
if(mod->cursor == i) {
|
||||
glColor4f(0.9,0.6,0.2,1);
|
||||
} else {
|
||||
glColor4f(1,1,1,1);
|
||||
}
|
||||
|
||||
draw_text(AL_Center, 0, 200+40*i, mode->name, _fonts.standard);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
glColor4f(1,1,1,0.3*sin(menu->frames/20.0)+0.5);
|
||||
|
||||
for(i = 0; i <= 1; i++) {
|
||||
for(int i = 0; i <= 1; i++) {
|
||||
glPushMatrix();
|
||||
|
||||
glTranslatef(60 + (SCREEN_W/2 - 30)*i, SCREEN_H/2+80, 0);
|
||||
|
@ -180,6 +192,7 @@ void char_menu_input(MenuData *menu) {
|
|||
}
|
||||
|
||||
void free_char_menu(MenuData *menu) {
|
||||
global.plr.mode = plrmode_find(selected_charid, selected_shotid);
|
||||
MenuData *mod = menu->context;
|
||||
destroy_menu(mod);
|
||||
free(mod);
|
||||
|
|
|
@ -20,7 +20,7 @@ static void start_game_internal(MenuData *menu, StageInfo *info, bool difficulty
|
|||
Difficulty stagediff;
|
||||
bool restart;
|
||||
|
||||
init_player(&global.plr);
|
||||
player_init(&global.plr);
|
||||
|
||||
do {
|
||||
restart = false;
|
||||
|
@ -49,9 +49,7 @@ static void start_game_internal(MenuData *menu, StageInfo *info, bool difficulty
|
|||
|
||||
global.replay_stage = NULL;
|
||||
replay_init(&global.replay);
|
||||
|
||||
int chr = global.plr.cha;
|
||||
int sht = global.plr.shot;
|
||||
PlayerMode *mode = global.plr.mode;
|
||||
|
||||
do {
|
||||
restart = false;
|
||||
|
@ -68,13 +66,12 @@ static void start_game_internal(MenuData *menu, StageInfo *info, bool difficulty
|
|||
}
|
||||
|
||||
if(global.game_over == GAMEOVER_RESTART) {
|
||||
init_player(&global.plr);
|
||||
player_init(&global.plr);
|
||||
replay_destroy(&global.replay);
|
||||
replay_init(&global.replay);
|
||||
global.game_over = 0;
|
||||
init_player(&global.plr);
|
||||
global.plr.cha = chr;
|
||||
global.plr.shot = sht;
|
||||
player_init(&global.plr);
|
||||
global.plr.mode = mode;
|
||||
|
||||
restart = true;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "video.h"
|
||||
#include "stage.h"
|
||||
#include "version.h"
|
||||
#include "plrmodes.h"
|
||||
|
||||
void enter_options(MenuData *menu, void *arg) {
|
||||
MenuData m;
|
||||
|
@ -218,8 +219,6 @@ void menu_preload(void) {
|
|||
"mainmenu/logo",
|
||||
"part/smoke",
|
||||
"part/petal",
|
||||
"dialog/marisa",
|
||||
"dialog/youmu",
|
||||
"charselect_arrow",
|
||||
NULL);
|
||||
|
||||
|
@ -232,4 +231,8 @@ void menu_preload(void) {
|
|||
preload_resources(RES_BGM, RESF_PERMANENT | RESF_OPTIONAL,
|
||||
"menu",
|
||||
NULL);
|
||||
|
||||
for(int i = 0; i < NUM_CHARACTERS; ++i) {
|
||||
preload_resource(RES_TEXTURE, plrchar_get(i)->dialog_sprite_name, RESF_PERMANENT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ static void replayview_drawitem(void *n, int item, int cnt) {
|
|||
break;
|
||||
|
||||
case 2:
|
||||
plrmode_repr(tmp, sizeof(tmp), rpy->stages[0].plr_char, rpy->stages[0].plr_shot);
|
||||
plrmode_repr(tmp, sizeof(tmp), plrmode_find(rpy->stages[0].plr_char, rpy->stages[0].plr_shot));
|
||||
tmp[0] = tmp[0] - 'a' + 'A';
|
||||
break;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ void save_rpy(MenuData *menu, void *a) {
|
|||
strftime(strtime, 128, "%Y%m%d_%H-%M-%S_%Z", timeinfo);
|
||||
|
||||
char prepr[16], drepr[16];
|
||||
plrmode_repr(prepr, 16, rpy->stages[0].plr_char, rpy->stages[0].plr_shot);
|
||||
plrmode_repr(prepr, 16, plrmode_find(rpy->stages[0].plr_char, rpy->stages[0].plr_shot));
|
||||
strlcpy(drepr, difficulty_name(rpy->stages[0].diff), 16);
|
||||
drepr[0] += 'a' - 'A';
|
||||
|
||||
|
|
106
src/player.c
106
src/player.c
|
@ -14,39 +14,44 @@
|
|||
#include "stage.h"
|
||||
#include "stagetext.h"
|
||||
|
||||
#include <SDL_bits.h>
|
||||
|
||||
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 init_player(Player *plr) {
|
||||
void player_init(Player *plr) {
|
||||
memset(plr, 0, sizeof(Player));
|
||||
plr->pos = VIEWPORT_W/2 + I*(VIEWPORT_H-64);
|
||||
plr->lives = PLR_START_LIVES;
|
||||
plr->bombs = PLR_START_BOMBS;
|
||||
plr->deathtime = -1;
|
||||
|
||||
aniplayer_create(&plr->ani, player_get_ani(plr->cha));
|
||||
plr->mode = plrmode_find(0, 0);
|
||||
}
|
||||
|
||||
void prepare_player_for_next_stage(Player *plr) {
|
||||
void player_stage_pre_init(Player *plr) {
|
||||
plr->recovery = 0;
|
||||
plr->respawntime = 0;
|
||||
plr->deathtime = -1;
|
||||
plr->graze = 0;
|
||||
plr->axis_lr = 0;
|
||||
plr->axis_ud = 0;
|
||||
|
||||
assert(plr->mode != NULL);
|
||||
plrchar_preload(plr->mode->character);
|
||||
|
||||
if(plr->mode->procs.preload) {
|
||||
plr->mode->procs.preload(plr);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void player_full_power(Player *plr) {
|
||||
|
@ -58,13 +63,8 @@ static void player_full_power(Player *plr) {
|
|||
bool player_set_power(Player *plr, short npow, bool handle_fullpower) {
|
||||
npow = clamp(npow, 0, PLR_MAX_POWER);
|
||||
|
||||
switch(plr->cha) {
|
||||
case Youmu:
|
||||
youmu_power(plr, npow);
|
||||
break;
|
||||
case Marisa:
|
||||
marisa_power(plr, npow);
|
||||
break;
|
||||
if(plr->mode->procs.power) {
|
||||
plr->mode->procs.power(plr, npow);
|
||||
}
|
||||
|
||||
int oldpow = plr->power;
|
||||
|
@ -80,16 +80,18 @@ bool player_set_power(Player *plr, short npow, bool handle_fullpower) {
|
|||
void player_move(Player *plr, complex delta) {
|
||||
float speed = 0.01*VIEWPORT_W;
|
||||
|
||||
if(plr->inputflags & INFLAG_FOCUS)
|
||||
if(plr->inputflags & INFLAG_FOCUS) {
|
||||
speed /= 2.0;
|
||||
}
|
||||
|
||||
if(plr->cha == Marisa && plr->shot == MarisaLaser && global.frames - plr->recovery < 0)
|
||||
speed /= 5.0;
|
||||
if(plr->mode->procs.speed_mod) {
|
||||
speed = plr->mode->procs.speed_mod(plr, speed);
|
||||
}
|
||||
|
||||
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);
|
||||
Animation *ani = plr->ani.ani;
|
||||
|
||||
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;
|
||||
|
@ -104,8 +106,6 @@ void player_move(Player *plr, complex delta) {
|
|||
}
|
||||
|
||||
void player_draw(Player* plr) {
|
||||
plr->ani.ani = player_get_ani(plr->cha); // because plr->cha is not set at init
|
||||
|
||||
// FIXME: death animation?
|
||||
if(plr->deathtime > global.frames)
|
||||
return;
|
||||
|
@ -168,6 +168,11 @@ static void player_fail_spell(Player *plr) {
|
|||
}
|
||||
}
|
||||
|
||||
bool player_should_shoot(Player *plr, bool extra) {
|
||||
return (plr->inputflags & INFLAG_SHOT) &&
|
||||
(!extra || (global.frames - plr->recovery >= 0 && plr->deathtime >= -1));
|
||||
}
|
||||
|
||||
void player_logic(Player* plr) {
|
||||
process_enemies(&plr->slaves);
|
||||
aniplayer_update(&plr->ani);
|
||||
|
@ -179,13 +184,12 @@ void player_logic(Player* plr) {
|
|||
|
||||
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(plr->mode->procs.think) {
|
||||
plr->mode->procs.think(plr);
|
||||
}
|
||||
|
||||
if(player_should_shoot(plr, false)) {
|
||||
plr->mode->procs.shot(plr);
|
||||
}
|
||||
|
||||
if(global.frames == plr->deathtime)
|
||||
|
@ -212,17 +216,8 @@ bool player_bomb(Player *plr) {
|
|||
|
||||
if(global.frames - plr->recovery >= 0 && (plr->bombs > 0 || plr->iddqd) && global.frames - plr->respawntime >= 60) {
|
||||
player_fail_spell(plr);
|
||||
delete_projectiles(&global.projs);
|
||||
|
||||
switch(plr->cha) {
|
||||
case Marisa:
|
||||
marisa_bomb(plr);
|
||||
break;
|
||||
case Youmu:
|
||||
youmu_bomb(plr);
|
||||
break;
|
||||
}
|
||||
|
||||
stage_clear_hazards(false);
|
||||
plr->mode->procs.bomb(plr);
|
||||
plr->bombs--;
|
||||
|
||||
if(plr->deathtime > 0) {
|
||||
|
@ -618,23 +613,14 @@ void player_preload(void) {
|
|||
preload_resources(RES_TEXTURE, flags,
|
||||
"focus",
|
||||
"fairy_circle",
|
||||
"masterspark",
|
||||
"masterspark_ring",
|
||||
NULL);
|
||||
|
||||
preload_resources(RES_SFX, flags | RESF_OPTIONAL,
|
||||
"graze",
|
||||
"death",
|
||||
"generic_shot",
|
||||
"masterspark",
|
||||
"haunt",
|
||||
"full_power",
|
||||
"extra_life",
|
||||
"extra_bomb",
|
||||
NULL);
|
||||
|
||||
preload_resources(RES_ANIM, flags,
|
||||
"youmu",
|
||||
"marisa",
|
||||
NULL);
|
||||
}
|
||||
|
|
44
src/player.h
44
src/player.h
|
@ -55,53 +55,34 @@ enum {
|
|||
INFLAGS_MOVE = INFLAG_UP | INFLAG_DOWN | INFLAG_LEFT | INFLAG_RIGHT
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Youmu = 0,
|
||||
Marisa
|
||||
} Character;
|
||||
|
||||
typedef enum {
|
||||
YoumuOpposite = 0,
|
||||
YoumuHoming,
|
||||
|
||||
MarisaLaser = YoumuOpposite,
|
||||
MarisaStar = YoumuHoming
|
||||
} ShotMode;
|
||||
|
||||
typedef struct {
|
||||
complex pos;
|
||||
short focus;
|
||||
|
||||
short power;
|
||||
|
||||
int graze;
|
||||
unsigned int points;
|
||||
|
||||
int lives;
|
||||
int bombs;
|
||||
|
||||
int life_fragments;
|
||||
int bomb_fragments;
|
||||
short power;
|
||||
|
||||
int recovery;
|
||||
|
||||
int deathtime;
|
||||
int respawntime;
|
||||
|
||||
struct PlayerMode *mode;
|
||||
AniPlayer ani;
|
||||
|
||||
Character cha;
|
||||
ShotMode shot;
|
||||
Enemy *slaves;
|
||||
|
||||
int inputflags;
|
||||
bool gamepadmove;
|
||||
complex lastmovedir;
|
||||
|
||||
int axis_ud;
|
||||
int axis_lr;
|
||||
|
||||
char iddqd;
|
||||
bool iddqd;
|
||||
|
||||
#ifdef PLR_DPS_STATS
|
||||
int total_dmg;
|
||||
|
@ -120,13 +101,24 @@ enum {
|
|||
EV_INFLAGS,
|
||||
};
|
||||
|
||||
void init_player(Player*);
|
||||
void prepare_player_for_next_stage(Player*);
|
||||
// This is called first before we even enter stage_loop.
|
||||
// It's also called right before syncing player state from a replay stage struct, if a replay is being watched or recorded, before every stage.
|
||||
// The entire state is reset here, and defaults for story mode are set.
|
||||
void player_init(Player *plr);
|
||||
|
||||
// This is called early in stage_loop, before creating or reading replay stage data.
|
||||
// State that is not supposed to be preserved between stages is reset here, and any plrmode-specific resources are preloaded.
|
||||
void player_stage_pre_init(Player *plr);
|
||||
|
||||
// This is called right before the stage's begin proc. After that, the actual game loop starts.
|
||||
void player_stage_post_init(Player *plr);
|
||||
|
||||
// Yes, that's 3 different initialization functions right here.
|
||||
|
||||
void player_draw(Player*);
|
||||
void player_logic(Player*);
|
||||
bool player_should_shoot(Player *plr, bool extra);
|
||||
|
||||
void player_set_char(Player*, Character);
|
||||
bool player_set_power(Player *plr, short npow, bool handle_fullpower);
|
||||
|
||||
void player_move(Player*, complex delta);
|
||||
|
@ -147,5 +139,3 @@ void player_add_bombs(Player *plr, int bombs);
|
|||
void player_add_points(Player *plr, unsigned int points);
|
||||
|
||||
void player_preload(void);
|
||||
|
||||
|
||||
|
|
805
src/plrmodes.c
805
src/plrmodes.c
|
@ -8,776 +8,97 @@
|
|||
|
||||
#include <float.h>
|
||||
|
||||
#include "plrmodes.h"
|
||||
#include "player.h"
|
||||
#include "global.h"
|
||||
#include "stage.h"
|
||||
|
||||
static bool should_shoot(bool extra) {
|
||||
return (global.plr.inputflags & INFLAG_SHOT) &&
|
||||
(!extra || (global.frames - global.plr.recovery >= 0 && global.plr.deathtime >= -1));
|
||||
#include "plrmodes.h"
|
||||
#include "plrmodes/marisa.h"
|
||||
#include "plrmodes/youmu.h"
|
||||
|
||||
static PlayerCharacter *player_characters[] = {
|
||||
&character_marisa,
|
||||
&character_youmu,
|
||||
};
|
||||
|
||||
static PlayerMode *player_modes[] = {
|
||||
&plrmode_marisa_a,
|
||||
&plrmode_marisa_b,
|
||||
&plrmode_youmu_a,
|
||||
&plrmode_youmu_b,
|
||||
};
|
||||
|
||||
PlayerCharacter* plrchar_get(CharacterID id) {
|
||||
assert((unsigned)id < NUM_CHARACTERS);
|
||||
PlayerCharacter *pc = player_characters[id];
|
||||
assert(pc->id == id);
|
||||
return pc;
|
||||
}
|
||||
|
||||
/* Yōmu */
|
||||
void plrchar_preload(PlayerCharacter *pc) {
|
||||
preload_resource(RES_ANIM, pc->player_sprite_name, RESF_DEFAULT);
|
||||
preload_resource(RES_TEXTURE, pc->dialog_sprite_name, RESF_DEFAULT);
|
||||
}
|
||||
|
||||
// Haunting Sign
|
||||
int plrmode_repr(char *out, size_t outsize, PlayerMode *mode) {
|
||||
assert(mode->character != NULL);
|
||||
assert((unsigned)mode->shot_mode < NUM_SHOT_MODES_PER_CHARACTER);
|
||||
|
||||
complex youmu_homing_target(complex org, complex fallback) {
|
||||
double mindst = DBL_MAX;
|
||||
complex target = fallback;
|
||||
return snprintf(out, outsize, "%s%c",
|
||||
mode->character->lower_name,
|
||||
mode->shot_mode + 'A'
|
||||
);
|
||||
}
|
||||
|
||||
if(global.boss) {
|
||||
target = global.boss->pos;
|
||||
mindst = cabs(target - org);
|
||||
}
|
||||
PlayerMode* plrmode_find(CharacterID char_id, ShotModeID shot_id) {
|
||||
for(int i = 0; i < NUM_PLAYER_MODES; ++i) {
|
||||
PlayerMode *mode = player_modes[i];
|
||||
|
||||
for(Enemy *e = global.enemies; e; e = e->next) {
|
||||
if(e->hp == ENEMY_IMMUNE){
|
||||
continue;
|
||||
}
|
||||
|
||||
double dst = cabs(e->pos - org);
|
||||
|
||||
if(dst < mindst) {
|
||||
mindst = dst;
|
||||
target = e->pos;
|
||||
if(mode->character->id == char_id && mode->shot_mode == shot_id) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void youmu_homing_draw_common(Projectile *p, int t, float clrfactor, float alpha) {
|
||||
glColor4f(0.7f + 0.3f * clrfactor, 0.9f + 0.1f * clrfactor, 1, alpha);
|
||||
ProjDraw(p, t);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
PlayerMode* plrmode_parse(const char *name) {
|
||||
CharacterID char_id = (CharacterID)-1;
|
||||
ShotModeID shot_id = (ShotModeID)-1;
|
||||
char buf[strlen(name) + 1];
|
||||
strcpy(buf, name);
|
||||
|
||||
void youmu_homing_draw_proj(Projectile *p, int t) {
|
||||
float a = clamp(1.0f - (float)t / p->args[2], 0, 1);
|
||||
youmu_homing_draw_common(p, t, a, 0.5f);
|
||||
}
|
||||
|
||||
void youmu_homing_draw_trail(Projectile *p, int t) {
|
||||
float a = clamp(1.0f - (float)t / p->args[0], 0, 1);
|
||||
youmu_homing_draw_common(p, t, a, 0.15f * a);
|
||||
}
|
||||
|
||||
void youmu_homing_trail(Projectile *p, complex v, int to) {
|
||||
Projectile *trail = create_projectile_p(&global.particles, p->tex, p->pos, 0, youmu_homing_draw_trail, timeout_linear, to, v, 0, 0);
|
||||
trail->type = PlrProj;
|
||||
trail->angle = p->angle;
|
||||
}
|
||||
|
||||
int youmu_homing(Projectile *p, int t) { // a[0]: velocity, a[1]: aim (r: linear, i: accelerated), a[2]: timeout, a[3]: initial target
|
||||
if(t == EVENT_DEATH) {
|
||||
return 1;
|
||||
for(int i = 0; i < sizeof(buf); ++i) {
|
||||
buf[i] = tolower(buf[i]);
|
||||
}
|
||||
|
||||
if(t > creal(p->args[2])) {
|
||||
return ACTION_DESTROY;
|
||||
if(!*buf) {
|
||||
log_debug("Got an empty string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p->args[3] = youmu_homing_target(p->pos, p->args[3]);
|
||||
char shot = buf[sizeof(buf) - 2];
|
||||
|
||||
double v = cabs(p->args[0]);
|
||||
complex aimdir = cexp(I*carg(p->args[3] - p->pos));
|
||||
|
||||
p->args[0] += creal(p->args[1]) * aimdir;
|
||||
p->args[0] = v * cexp(I*carg(p->args[0])) + cimag(p->args[1]) * aimdir;
|
||||
|
||||
p->angle = carg(p->args[0]);
|
||||
p->pos += p->args[0];
|
||||
|
||||
youmu_homing_trail(p, 0.5 * p->args[0], 12);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int youmu_trap(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
create_particle1c("blast", p->pos, 0, Blast, timeout, 15);
|
||||
return 1;
|
||||
if(shot < 'a' || shot >= 'a' + NUM_SHOT_MODES_PER_CHARACTER) {
|
||||
log_debug("Got invalid shotmode: %c", shot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double expiretime = creal(p->args[1]);
|
||||
shot_id = shot - 'a';
|
||||
buf[sizeof(buf) - 2] = 0;
|
||||
|
||||
if(t > expiretime) {
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
for(int i = 0; i < NUM_CHARACTERS; ++i) {
|
||||
PlayerCharacter *chr = player_characters[i];
|
||||
|
||||
if(!(global.plr.inputflags & INFLAG_FOCUS)) {
|
||||
create_particle1c("blast", p->pos, 0, Blast, timeout, 20);
|
||||
create_particle1c("blast", p->pos, 0, Blast, timeout, 23);
|
||||
|
||||
int cnt = creal(p->args[2]);
|
||||
int dmg = cimag(p->args[2]);
|
||||
int dur = 45 + 10 * nfrand(); // creal(p->args[3]) + nfrand() * cimag(p->args[3]);
|
||||
complex aim = p->args[3];
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
float a = (i / (float)cnt) * M_PI * 2;
|
||||
complex dir = cexp(I*(a));
|
||||
Projectile *proj = create_projectile4c("hghost", p->pos, 0, youmu_homing, 5 * dir, aim, dur, global.plr.pos);
|
||||
proj->type = PlrProj + dmg;
|
||||
proj->draw = youmu_homing_draw_proj;
|
||||
}
|
||||
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
|
||||
p->angle = global.frames + t;
|
||||
p->pos += p->args[0] * (0.01 + 0.99 * max(0, (10 - t) / 10.0));
|
||||
|
||||
youmu_homing_trail(p, cexp(I*p->angle), 30);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Slice(Projectile *p, int t) {
|
||||
if(t < creal(p->args[0])/20.0)
|
||||
p->args[1] += 1;
|
||||
|
||||
if(t > creal(p->args[0])-10) {
|
||||
p->args[1] += 3;
|
||||
p->args[2] += 1;
|
||||
}
|
||||
|
||||
float f = p->args[1]/p->args[0]*20.0;
|
||||
|
||||
glColor4f(1,1,1,1.0 - p->args[2]/p->args[0]*20.0);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(creal(p->pos), cimag(p->pos),0);
|
||||
glRotatef(p->angle,0,0,1);
|
||||
glScalef(f,1,1);
|
||||
draw_texture(0,0,"part/youmu_slice");
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glColor4f(1,1,1,1);
|
||||
}
|
||||
|
||||
void YoumuSlash(Enemy *e, int t) {
|
||||
fade_out(10.0/t+sin(t/10.0)*0.1);
|
||||
|
||||
}
|
||||
|
||||
int spin(Projectile *p, int t) {
|
||||
int i = timeout_linear(p, t);
|
||||
|
||||
if(t < 0)
|
||||
return 1;
|
||||
|
||||
p->args[3] += 0.06;
|
||||
p->angle = p->args[3];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int youmu_slash(Enemy *e, int t) {
|
||||
if(t > creal(e->args[0]))
|
||||
return ACTION_DESTROY;
|
||||
if(t < 0)
|
||||
return 1;
|
||||
|
||||
if(global.frames - global.plr.recovery > 0) {
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
|
||||
TIMER(&t);
|
||||
|
||||
AT(0)
|
||||
global.plr.pos = VIEWPORT_W/5.0 + (VIEWPORT_H - 100)*I;
|
||||
|
||||
FROM_TO(8,20,1)
|
||||
global.plr.pos = VIEWPORT_W + (VIEWPORT_H - 100)*I - exp(-_i/8.0+log(4*VIEWPORT_W/5.0));
|
||||
|
||||
FROM_TO(30, 60, 10) {
|
||||
tsrand_fill(3);
|
||||
create_particle1c("youmu_slice", VIEWPORT_W/2.0 - 150 + 100*_i + VIEWPORT_H/2.0*I - 10-10.0*I + 20*afrand(0)+20.0*I*afrand(1), 0, Slice, timeout, 200)->angle = -10.0+20.0*afrand(2);
|
||||
}
|
||||
|
||||
FROM_TO(40,200,1)
|
||||
if(frand() > 0.7) {
|
||||
tsrand_fill(6);
|
||||
create_particle2c("blast", VIEWPORT_W*afrand(0) + (VIEWPORT_H+50)*I, rgb(afrand(1),afrand(2),afrand(3)), Shrink, timeout_linear, 80, 3*(1-2.0*afrand(4))-14.0*I+afrand(5)*2.0*I);
|
||||
}
|
||||
|
||||
int tpar = 30;
|
||||
if(t < 30)
|
||||
tpar = t;
|
||||
|
||||
if(t < creal(e->args[0])-60 && frand() > 0.2) {
|
||||
tsrand_fill(3);
|
||||
create_particle2c("smoke", VIEWPORT_W*afrand(0) + (VIEWPORT_H+100)*I, rgba(0.4,0.4,0.4,afrand(1)*0.2 - 0.2 + 0.6*(tpar/30.0)), PartDraw, spin, 300, -7.0*I+afrand(2)*1.0*I);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Opposite Sign
|
||||
|
||||
int myon_particle_rule(Projectile *p, int t) {
|
||||
float a = 1.0;
|
||||
|
||||
if(t > 0) {
|
||||
double mt = creal(p->args[0]);
|
||||
a *= (mt - t) / mt;
|
||||
}
|
||||
|
||||
p->clr = mix_colors(
|
||||
rgba(0.85, 0.9, 1.0, 1.0),
|
||||
rgba(0.5, 0.7, 1.0, a),
|
||||
1 - pow(1 - a, 1 + psin((t + global.frames) * 0.1)));
|
||||
|
||||
return timeout_linear(p, t);
|
||||
}
|
||||
|
||||
void myon_particle_draw(Projectile *p, int t) {
|
||||
// hack to bypass the bullet color shader
|
||||
Color clr = p->clr;
|
||||
p->clr = 0;
|
||||
parse_color_call(clr, glColor4f);
|
||||
Shrink(p, t);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
p->clr = clr;
|
||||
}
|
||||
|
||||
void YoumuOppositeMyon(Enemy *e, int t) {
|
||||
float a = global.frames * 0.07;
|
||||
complex pos = e->pos + 3 * (cos(a) + I * sin(a));
|
||||
|
||||
complex dir = cexp(I*(0.1 * sin(global.frames * 0.05) + carg(global.plr.pos - e->pos)));
|
||||
double v = 4 * cabs(global.plr.pos - e->pos) / (VIEWPORT_W * 0.5);
|
||||
|
||||
create_particle2c("flare", pos, 0, myon_particle_draw, myon_particle_rule, 20, v*dir)->type = PlrProj;
|
||||
}
|
||||
|
||||
static void youmu_opposite_myon_proj(char *tex, complex pos, double speed, double angle, double aoffs, double upfactor, int dmg) {
|
||||
complex dir = cexp(I*(M_PI/2 + aoffs)) * upfactor + cexp(I * (angle + aoffs)) * (1 - upfactor);
|
||||
dir = dir / cabs(dir);
|
||||
create_projectile1c(tex, pos, 0, linear, speed*dir)->type = PlrProj+dmg;
|
||||
}
|
||||
|
||||
int youmu_opposite_myon(Enemy *e, int t) {
|
||||
if(t == EVENT_BIRTH)
|
||||
e->pos = e->pos0 + global.plr.pos;
|
||||
if(t < 0)
|
||||
return 1;
|
||||
|
||||
Player *plr = &global.plr;
|
||||
float rad = cabs(e->pos0);
|
||||
|
||||
double nfocus = plr->focus / 30.0;
|
||||
|
||||
if(!(plr->inputflags & INFLAG_FOCUS)) {
|
||||
if((plr->inputflags & INFLAGS_MOVE)) {
|
||||
e->pos0 = rad * -plr->lastmovedir;
|
||||
} else {
|
||||
e->pos0 = e->pos - plr->pos;
|
||||
e->pos0 *= rad / cabs(e->pos0);
|
||||
if(!strcmp(buf, chr->lower_name)) {
|
||||
char_id = chr->id;
|
||||
}
|
||||
}
|
||||
|
||||
complex target = plr->pos + e->pos0;
|
||||
e->pos += cexp(I*carg(target - e->pos)) * min(10, 0.07 * max(0, cabs(target - e->pos) - VIEWPORT_W * 0.5 * nfocus));
|
||||
|
||||
if(!(plr->inputflags & INFLAG_FOCUS)) {
|
||||
e->args[0] = plr->pos - e->pos;
|
||||
if(char_id == (CharacterID)-1) {
|
||||
log_debug("Got invalid character: %s", buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(should_shoot(true) && !(global.frames % 6) && global.plr.deathtime >= -1) {
|
||||
int v1 = -21;
|
||||
int v2 = -10;
|
||||
|
||||
double r1 = (psin(global.frames * 2) * 0.5 + 0.5) * 0.1;
|
||||
double r2 = (psin(global.frames * 1.2) * 0.5 + 0.5) * 0.1;
|
||||
|
||||
double a = carg(e->args[0]);
|
||||
double u = 1 - nfocus;
|
||||
|
||||
int p = plr->power / 100;
|
||||
int dmg_center = 160 - 30 * p;
|
||||
int dmg_side = 23 + 2 * p;
|
||||
|
||||
if(plr->power >= 100) {
|
||||
youmu_opposite_myon_proj("youmu", e->pos, v1, a, r1*1, u, dmg_side);
|
||||
youmu_opposite_myon_proj("youmu", e->pos, v1, a, -r1*1, u, dmg_side);
|
||||
}
|
||||
|
||||
if(plr->power >= 200) {
|
||||
youmu_opposite_myon_proj("hghost", e->pos, v2, a, r2*2, 0, dmg_side);
|
||||
youmu_opposite_myon_proj("hghost", e->pos, v2, a, -r2*2, 0, dmg_side);
|
||||
}
|
||||
|
||||
if(plr->power >= 300) {
|
||||
youmu_opposite_myon_proj("youmu", e->pos, v1, a, r1*3, u, dmg_side);
|
||||
youmu_opposite_myon_proj("youmu", e->pos, v1, a, -r1*3, u, dmg_side);
|
||||
}
|
||||
|
||||
if(plr->power >= 400) {
|
||||
youmu_opposite_myon_proj("hghost", e->pos, v2, a, r2*4, 0, dmg_side);
|
||||
youmu_opposite_myon_proj("hghost", e->pos, v2, a, -r2*4, 0, dmg_side);
|
||||
}
|
||||
|
||||
youmu_opposite_myon_proj("hghost", e->pos, v2, a, 0, 0, dmg_center);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int youmu_split(Enemy *e, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
|
||||
if(t > creal(e->args[0]))
|
||||
return ACTION_DESTROY;
|
||||
|
||||
if(global.frames - global.plr.recovery > 0) {
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
|
||||
TIMER(&t);
|
||||
|
||||
FROM_TO(30,200,1) {
|
||||
tsrand_fill(2);
|
||||
create_particle2c("smoke", VIEWPORT_W/2 + VIEWPORT_H/2*I, rgba(0.4,0.4,0.4,afrand(0)*0.2+0.4), PartDraw, spin, 300, 6*cexp(I*afrand(1)*2*M_PI));
|
||||
}
|
||||
|
||||
FROM_TO(100,170,10) {
|
||||
tsrand_fill(3);
|
||||
create_particle1c("youmu_slice", VIEWPORT_W/2.0 + VIEWPORT_H/2.0*I - 200-200.0*I + 400*afrand(0)+400.0*I*afrand(1), 0, Slice, timeout, 100-_i)->angle = 360.0*afrand(2);
|
||||
}
|
||||
|
||||
|
||||
FROM_TO(0, 220, 1) {
|
||||
float talt = atan((t-e->args[0]/2)/30.0)*10+atan(-e->args[0]/2);
|
||||
global.plr.pos = VIEWPORT_W/2.0 + (VIEWPORT_H-80)*I + VIEWPORT_W/3.0*sin(talt);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Youmu Generic
|
||||
|
||||
void youmu_homing_power_shot(Player *plr, int p) {
|
||||
int d = -2;
|
||||
double spread = 4;
|
||||
complex aim = (0.5 + 0.1 * p) + (0.1 - p * 0.025) * I;
|
||||
double speed = 10;
|
||||
|
||||
if(plr->power / 100 < p || (global.frames + d * p) % 12) {
|
||||
return;
|
||||
}
|
||||
|
||||
Projectile **dst = &global.projs;
|
||||
Texture *t = get_tex("proj/hghost");
|
||||
|
||||
for(int sign = -1; sign < 2; sign += 2) {
|
||||
create_projectile_p(dst, t, plr->pos, 0, youmu_homing_draw_proj, youmu_homing,
|
||||
speed * cexp(I*carg(sign*p*spread-speed*I)), aim, 60, VIEWPORT_W*0.5)->type = PlrProj+54;
|
||||
}
|
||||
}
|
||||
|
||||
void youmu_shot(Player *plr) {
|
||||
if(should_shoot(false)) {
|
||||
if(!(global.frames % 4))
|
||||
play_sound("generic_shot");
|
||||
|
||||
if(!(global.frames % 6)) {
|
||||
create_projectile1c("youmu", plr->pos + 10 - I*20, 0, linear, -20.0*I)->type = PlrProj+120;
|
||||
create_projectile1c("youmu", plr->pos - 10 - I*20, 0, linear, -20.0*I)->type = PlrProj+120;
|
||||
}
|
||||
|
||||
if(plr->shot == YoumuHoming && should_shoot(true)) {
|
||||
if(plr->inputflags & INFLAG_FOCUS) {
|
||||
int pwr = plr->power / 100;
|
||||
|
||||
if(!(global.frames % (45 - 4 * pwr))) {
|
||||
int pcnt = 11 + pwr * 4;
|
||||
int pdmg = 120 - 18 * 4 * (1 - pow(1 - pwr / 4.0, 1.5));
|
||||
complex aim = 0.75;
|
||||
|
||||
create_projectile4c("youhoming", plr->pos, 0, youmu_trap, -30.0*I, 120, pcnt+pdmg*I, aim)->type = PlrProj+1000;
|
||||
}
|
||||
} else {
|
||||
if(!(global.frames % 6)) {
|
||||
create_projectile4c("hghost", plr->pos, 0, youmu_homing, -10.0*I, 0.25 + 0.1*I, 60, VIEWPORT_W*0.5)->type = PlrProj+120;
|
||||
}
|
||||
|
||||
for(int p = 1; p <= PLR_MAX_POWER/100; ++p) {
|
||||
youmu_homing_power_shot(plr, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(plr->shot == YoumuOpposite && plr->slaves == NULL)
|
||||
create_enemy_p(&plr->slaves, 40.0*I, ENEMY_IMMUNE, YoumuOppositeMyon, youmu_opposite_myon, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void youmu_bomb(Player *plr) {
|
||||
switch(plr->shot) {
|
||||
case YoumuOpposite:
|
||||
create_enemy_p(&plr->slaves, 40.0*I, ENEMY_BOMB, NULL, youmu_split, 280,0,0,0);
|
||||
|
||||
break;
|
||||
case YoumuHoming:
|
||||
play_sound("haunt");
|
||||
create_enemy_p(&plr->slaves, 40.0*I, ENEMY_BOMB, YoumuSlash, youmu_slash, 280,0,0,0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void youmu_power(Player *plr, short npow) {
|
||||
if(plr->shot == YoumuOpposite && plr->slaves == NULL)
|
||||
create_enemy_p(&plr->slaves, 40.0*I, ENEMY_IMMUNE, YoumuOppositeMyon, youmu_opposite_myon, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* Marisa */
|
||||
|
||||
// Laser Sign
|
||||
void MariLaser(Projectile *p, int t) {
|
||||
if(REF(p->args[1]) == NULL)
|
||||
return;
|
||||
|
||||
if(cimag(p->pos) - cimag(global.plr.pos) < 90) {
|
||||
float s = resources.fbo.fg[0].scale;
|
||||
glScissor(0, s * (VIEWPORT_H - cimag(((Enemy *)REF(p->args[1]))->pos)), VIEWPORT_W*s, VIEWPORT_H*s);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
ProjDraw(p, t);
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
int mari_laser(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
free_ref(p->args[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(REF(p->args[1]) == NULL)
|
||||
return ACTION_DESTROY;
|
||||
|
||||
float angle = creal(p->args[2]);
|
||||
float factor = (1-global.plr.focus/30.0) * !!angle;
|
||||
complex dir = -cexp(I*((angle+0.125*sin(global.frames/25.0)*(angle > 0? 1 : -1))*factor + M_PI/2));
|
||||
p->args[0] = 20*dir;
|
||||
linear(p, t);
|
||||
|
||||
p->pos = ((Enemy *)REF(p->args[1]))->pos + p->pos;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int marisa_laser_slave(Enemy *e, int t) {
|
||||
if(should_shoot(true)) {
|
||||
if(!(global.frames % 4))
|
||||
create_projectile_p(&global.projs, get_tex("proj/marilaser"), 0, 0, MariLaser, mari_laser, 0, add_ref(e),e->args[2],0)->type = PlrProj+e->args[1]*4;
|
||||
|
||||
if(!(global.frames % 3)) {
|
||||
float s = 0.5 + 0.3*sin(global.frames/7.0);
|
||||
create_particle3c("marilaser_part0", 0, rgb(1-s,0.5,s), PartDraw, mari_laser, 0, add_ref(e), e->args[2])->type=PlrProj;
|
||||
}
|
||||
create_particle1c("lasercurve", e->pos, 0, Fade, timeout, 4)->type = PlrProj;
|
||||
}
|
||||
|
||||
e->pos = global.plr.pos + (1 - global.plr.focus/30.0)*e->pos0 + (global.plr.focus/30.0)*e->args[0];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void MariLaserSlave(Enemy *e, int t) {
|
||||
glPushMatrix();
|
||||
glTranslatef(creal(e->pos), cimag(e->pos), -1);
|
||||
glRotatef(global.frames * 3, 0, 0, 1);
|
||||
|
||||
draw_texture(0,0,"part/lasercurve");
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
// Laser sign bomb (implemented as Enemy)
|
||||
|
||||
static void draw_masterspark_ring(complex base, int t, float fade) {
|
||||
glPushMatrix();
|
||||
|
||||
glTranslatef(creal(base), cimag(base)-t*t*0.4, 0);
|
||||
|
||||
float f = sqrt(t/500.0)*1200;
|
||||
|
||||
glColor4f(1,1,1,fade*20.0/t);
|
||||
|
||||
Texture *tex = get_tex("masterspark_ring");
|
||||
glScalef(f/tex->w, 1-tex->h/f,0);
|
||||
draw_texture_p(0,0,tex);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void MasterSpark(Enemy *e, int t) {
|
||||
glPushMatrix();
|
||||
|
||||
float angle = 9 - t/e->args[0]*6.0, fade = 1;
|
||||
|
||||
if(t < creal( |