814 lines
23 KiB
C
Executable file
814 lines
23 KiB
C
Executable file
/*
|
|
GAME.H
|
|
This file defines SPECIFICALLY the game logic, completely separate from any libraries.
|
|
Drawing and handling of sprites happens in the main_[platform].c file.
|
|
|
|
Licensed under CC0, public domain, 2023-2025
|
|
Made by blitzdoughnuts
|
|
*/
|
|
|
|
#include "settings.h"
|
|
#include "constants.h"
|
|
|
|
// draw calls
|
|
#define DRAW_PLRUPARROW 1
|
|
#define DRAW_PLRDNARROW 2
|
|
|
|
// misc calls, mainly for frontends
|
|
#define MISC_PAUSEMUSIC 0
|
|
#define MISC_RESUMEMUSIC 1
|
|
#define MISC_STOPMUSIC 2
|
|
#define MISC_GESTURE 3 // the frontend decides what happens when Lukifer gestures
|
|
#define MISC_NEWROOM 4
|
|
|
|
#ifndef CAM_BOUNDS
|
|
#define CAM_BOUNDS 160 // border size of camera from both directions
|
|
#endif
|
|
|
|
#define INTER_LIMIT 32 // how many interactibles can be on a level at a time?
|
|
|
|
#define GAMETITLE "Wake to Hell"
|
|
#define VERSION_NUMBER "0.4.1"
|
|
|
|
#ifndef WTH_PRINT
|
|
#define WTH_PRINT(x...)
|
|
#endif
|
|
|
|
#define keyPressed(keyvalue) (input_keys & keyvalue) && !(input_prevkeys & keyvalue)
|
|
#define flagOn(var, flag) if (!(var & flag)) var ^= flag
|
|
#define flagOff(var, flag) if (var & flag) var ^= flag
|
|
|
|
void signalDraw(uint8_t signal); // interface with main for drawing specific things at specific times
|
|
static inline void signalPlaySFX(uint8_t signal); // play sound
|
|
static inline void signalPlayMUS(uint8_t signal); // play music
|
|
void signalMisc(uint8_t signal); // misc signal
|
|
|
|
uint8_t GAME_STATE = 0;
|
|
uint8_t GAME_FLAGS = 0; // for routes and stuff
|
|
|
|
typedef struct {
|
|
int x, y, hsp, vsp;
|
|
int hsp_sub, vsp_sub;
|
|
uint8_t flags, artifacts;
|
|
uint16_t idleanimtimer;
|
|
uint8_t state;
|
|
uint8_t animframe, animtimer, animflimit, animindex;
|
|
} WTH_Player;
|
|
|
|
struct {
|
|
uint8_t menuselect;
|
|
uint8_t menuindex:4,
|
|
menulimit:4;
|
|
} menu;
|
|
// menuindex and menulimit are 4-bit variables (up to 15)
|
|
|
|
uint8_t bookpage; // upper 4 bits is book type, lower 4 is page number
|
|
|
|
int cam_x, cam_y;
|
|
|
|
typedef struct {
|
|
int x, y;
|
|
uint16_t vars[8];
|
|
uint8_t objID, flags;
|
|
} WTH_Interactible;
|
|
|
|
WTH_Player plr; // the one and only... uh... unstable furry man.
|
|
WTH_Interactible interacts[INTER_LIMIT];
|
|
uint8_t interacts_count = 0; // convenient counter for all present interactibles
|
|
|
|
uint8_t level; // all rooms, 256 is overkill but overkill is what's needed
|
|
uint8_t fade, fademode; // fade variables, basically alpha
|
|
uint8_t fadespeed;
|
|
|
|
uint8_t options[3];
|
|
|
|
uint8_t input_keys;
|
|
uint8_t input_prevkeys;
|
|
uint8_t xtrakeys;
|
|
|
|
void saveGame();
|
|
void loadGame();
|
|
|
|
uint8_t addObject(int nx, int ny, uint8_t objType) {
|
|
#ifdef WTH_DEBUG
|
|
if (interacts_count >= INTER_LIMIT) {
|
|
WTH_PRINT("addObject: Tried adding object over the limit!\n");
|
|
return 0;
|
|
};
|
|
#endif
|
|
|
|
#ifdef WTH_LOOPOBJECTS
|
|
uint8_t index = 0;
|
|
while (interacts[index].flags & INTER_ACTIVE && index < INTER_LIMIT) index++;
|
|
#else
|
|
uint8_t index = interacts_count;
|
|
#endif
|
|
|
|
for (uint8_t i = 0; i < 7; i++) {
|
|
interacts[index].vars[i] = 0;
|
|
};
|
|
interacts[index].x = nx;
|
|
interacts[index].y = ny;
|
|
interacts[index].objID = objType;
|
|
interacts[index].flags ^= INTER_ACTIVE;
|
|
interacts_count++;
|
|
|
|
// OBJECT INIT CODE
|
|
switch (objType)
|
|
{
|
|
case INTERTYPE_YOU:
|
|
interacts[index].vars[2] = WTH_PLR_IDLE_LEN;
|
|
break;
|
|
}
|
|
|
|
return index;
|
|
};
|
|
|
|
static inline void LoadInRoom() {
|
|
flagOff(GAME_FLAGS, FLAG_LOCKCAM);
|
|
for (uint8_t i = 0; i < interacts_count; i++) {
|
|
if ((interacts[i].flags & INTER_ACTIVE)) {
|
|
interacts[i].flags = 0;
|
|
};
|
|
for (uint8_t v = 0; v < 7; v++) interacts[i].vars[v] = 0;
|
|
}
|
|
interacts_count = 0;
|
|
switch (level)
|
|
{
|
|
#ifdef WTH_DEBUG
|
|
case ROOM_TEST:
|
|
addObject(164, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[0].vars[0] = ROOM_HOUSE;
|
|
addObject(320, GROUNDLEVEL, INTERTYPE_YOU);
|
|
addObject(300, 100, INTERTYPE_ARTIFACT);
|
|
addObject(400, 100, INTERTYPE_ARTIFACT);
|
|
addObject(500, 100, INTERTYPE_ARTIFACT);
|
|
addObject(600, 100, INTERTYPE_ARTIFACT);
|
|
addObject(700, 100, INTERTYPE_BOOK);
|
|
addObject(800, GROUNDLEVEL, INTERTYPE_STAIRS);
|
|
interacts[3].vars[0] = 1;
|
|
interacts[4].vars[0] = 2;
|
|
interacts[5].vars[0] = 3;
|
|
addObject(964, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[8].vars[0] = ROOM_HOUSE;
|
|
interacts[8].flags ^= 1 << 3;
|
|
break;
|
|
#endif
|
|
case ROOM_HOUSE:
|
|
addObject(650, 0, INTERTYPE_MOVEBLOCK);
|
|
interacts[0].flags ^= 1;
|
|
addObject(246,200,INTERTYPE_COAT);
|
|
addObject(-48,218,INTERTYPE_DECOR);
|
|
interacts[2].vars[0] = WTH_SPR_DECOR_BED;
|
|
addObject(-360, 0, INTERTYPE_MOVEBLOCK);
|
|
addObject(615, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[4].vars[0] = ROOM_OUTDOORS;
|
|
interacts[4].vars[2] = 1;
|
|
if (!(plr.artifacts & (1 << ARTIFACT_KNIFE))) {
|
|
addObject(-194, 140, INTERTYPE_ARTIFACT);
|
|
interacts[5].vars[0] = ARTIFACT_KNIFE;
|
|
};
|
|
break;
|
|
case ROOM_OUTDOORS:
|
|
addObject(-76, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[0].vars[0] = ROOM_HOUSE;
|
|
interacts[0].vars[2] = 5;
|
|
addObject(64,GROUNDLEVEL - 100,INTERTYPE_DECOR);
|
|
interacts[1].vars[0] = WTH_SPR_DECOR_MAILBOX;
|
|
addObject(-100, 0, INTERTYPE_MOVEBLOCK);
|
|
break;
|
|
case ROOM_STREETS:
|
|
addObject(1920, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[0].vars[0] = ROOM_HOSPITAL;
|
|
addObject(555, 82, INTERTYPE_DECOR);
|
|
interacts[1].vars[0] = WTH_SPR_BG_BAROUT;
|
|
addObject(768, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[2].vars[0] = ROOM_BAR;
|
|
interacts[2].vars[2] = 1;
|
|
break;
|
|
case ROOM_HOSPITAL:
|
|
/* if you DON'T have all the artifacts, allow exiting of the room
|
|
TODO: cutscene of the door slamming */
|
|
addObject(752, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[0].vars[0] = ROOM_H_PATIENTS;
|
|
interacts[0].vars[2] = 2;
|
|
addObject(368, 135, INTERTYPE_ARTIFACT);
|
|
interacts[1].vars[0] = ARTIFACT_BADGE;
|
|
if (plr.artifacts < (1 << ARTIFACT_BADGE) + (1 << ARTIFACT_KNIFE) + (1 << ARTIFACT_MIRROR) + (1 << ARTIFACT_DONUT)) {
|
|
addObject(-20, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[2].vars[0] = ROOM_STREETS;
|
|
interacts[2].vars[2] = 1;
|
|
} else {
|
|
addObject(-20, GROUNDLEVEL, INTERTYPE_DOORSLAM);
|
|
};
|
|
break;
|
|
case ROOM_H_PATIENTS:
|
|
addObject(64, 0, INTERTYPE_MOVEBLOCK);
|
|
addObject(240, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[1].vars[0] = ROOM_HOSPITAL;
|
|
interacts[1].vars[2] = 1;
|
|
addObject(114, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[2].vars[0] = ROOM_H_HAMIE;
|
|
addObject(370, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[3].vars[0] = ROOM_H_HAMIE;
|
|
addObject(540, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[4].vars[0] = ROOM_H_DARK;
|
|
addObject(640, 0, INTERTYPE_MOVEBLOCK);
|
|
interacts[5].flags ^= 1;
|
|
break;
|
|
case ROOM_BAR:
|
|
addObject(50, GROUNDLEVEL, INTERTYPE_DOOR);
|
|
interacts[0].vars[0] = ROOM_STREETS;
|
|
interacts[0].vars[2] = 3;
|
|
for (uint8_t i = 1; i < 9; i += 2) {
|
|
addObject(106 + (56 * i-2),GROUNDLEVEL - 100,INTERTYPE_DECOR);
|
|
interacts[i].vars[0] = WTH_SPR_DECOR_BARSTOOL;
|
|
addObject(interacts[i].x + 75,GROUNDLEVEL,INTERTYPE_GESTURE);
|
|
interacts[i+1].vars[0] = GESTURE_STOOL;
|
|
};
|
|
addObject(650, GROUNDLEVEL, INTERTYPE_MOVEBLOCK);
|
|
interacts[9].flags ^= 1;
|
|
addObject(-64, GROUNDLEVEL, INTERTYPE_MOVEBLOCK);
|
|
break;
|
|
case ROOM_H_DARK:
|
|
addObject(-64, 0, INTERTYPE_MOVEBLOCK);
|
|
addObject(1080, 0, INTERTYPE_MOVEBLOCK);
|
|
interacts[1].flags ^= 1;
|
|
addObject(758, 0, INTERTYPE_DECOR);
|
|
interacts[2].vars[0] = WTH_SPR_DECOR_BARSTOOL; // TODO: make pedestal sprite
|
|
addObject(726, 0, INTERTYPE_YOU); // TODO: add cutscene trigger
|
|
break;
|
|
}
|
|
};
|
|
|
|
static inline void changeRoom(uint8_t input, uint8_t startAtInter) {
|
|
level = input;
|
|
plr.x = 50;
|
|
plr.y = GROUNDLEVEL;
|
|
cam_y = 0;
|
|
if (plr.flags & FLAG_CANTMOVE) plr.flags ^= FLAG_CANTMOVE;
|
|
LoadInRoom();
|
|
if (startAtInter > 0) {
|
|
plr.x = interacts[startAtInter - 1].x;
|
|
|
|
// open door if it spawns over the player
|
|
if (interacts[startAtInter-1].objID == INTERTYPE_DOOR && plr.x >= interacts[startAtInter-1].x - 40 && plr.x < interacts[startAtInter-1].x + 40) interacts[startAtInter-1].flags ^= 1 << 2;
|
|
};
|
|
signalMisc(MISC_NEWROOM);
|
|
};
|
|
|
|
void interact_step(WTH_Interactible *REF) {
|
|
if (!(REF->flags & INTER_ACTIVE)) return;
|
|
switch (REF->objID) {
|
|
case INTERTYPE_DOOR:
|
|
/* flag 1 is the entering door state,
|
|
flag 2 is if the door is open
|
|
(flag 2 is only really used if
|
|
transitioning rooms)
|
|
flag 3 sets if the door is only
|
|
enterable by pressing DOWN */
|
|
if (!(REF->flags & 1 << 1)) {
|
|
if (!(plr.flags & FLAG_GROUNDED)) return;
|
|
if (plr.y == REF->y && plr.x >= REF->x - 40 && plr.x < REF->x + 40) {
|
|
uint8_t doorKey = KEY_UP;
|
|
if (REF->flags & 1 << 3) {
|
|
signalDraw(DRAW_PLRDNARROW);
|
|
doorKey = KEY_DOWN;
|
|
} else signalDraw(DRAW_PLRUPARROW);
|
|
if ((input_keys & doorKey)) {
|
|
signalPlaySFX(4);
|
|
plr.flags ^= FLAG_CANTMOVE | FLAG_HALTANIM;
|
|
plr.animindex = WTH_PLR_DOOR_SPR;
|
|
plr.animframe = plr.animtimer = 0;
|
|
plr.animflimit = WTH_PLR_DOOR_LEN;
|
|
plr.hsp = plr.vsp = 0;
|
|
REF->vars[1] = 40;
|
|
REF->flags ^= 1 << 1;
|
|
fade = 0;
|
|
fademode = FADE_OUT;
|
|
};
|
|
if ((REF->flags & 1 << 2) && plr.hsp) {
|
|
signalPlaySFX(5);
|
|
REF->flags ^= 1 << 2;
|
|
};
|
|
}
|
|
} else {
|
|
if (REF->vars[1] == 0) {
|
|
plr.flags ^= FLAG_HALTANIM | FLAG_REVERSEANIM;
|
|
plr.animindex = WTH_PLR_DOOR_SPR;
|
|
plr.animflimit = plr.animframe = WTH_PLR_DOOR_LEN;
|
|
changeRoom(REF->vars[0], REF->vars[2]);
|
|
fadespeed = 4;
|
|
fade = 255;
|
|
fademode = FADE_IN;
|
|
} else {
|
|
if (plr.x - 1 < REF->x) plr.x++;
|
|
if (plr.x + 1 > REF->x) plr.x--;
|
|
REF->vars[1]--;
|
|
fadespeed++;
|
|
};
|
|
};
|
|
break;
|
|
case INTERTYPE_BOOK:
|
|
if (!(REF->flags & 1 << 4)) {
|
|
if (!(plr.flags & FLAG_GROUNDED)) return;
|
|
if (plr.x >= REF->x - 40 && plr.x < REF->x + 40) {
|
|
signalDraw(DRAW_PLRUPARROW);
|
|
if ((input_keys & KEY_UP)) {
|
|
signalPlaySFX(10);
|
|
plr.flags ^= FLAG_CANTMOVE | FLAG_HALTANIM;
|
|
plr.animindex = 2;
|
|
plr.animframe = plr.animtimer = 0;
|
|
plr.animflimit = WTH_PLR_DOOR_LEN;
|
|
plr.hsp = plr.vsp = 0;
|
|
REF->vars[1] = 40;
|
|
REF->flags ^= 1 << 4;
|
|
bookpage = REF->vars[0] << 4; // shifts booktype to 0xF0; 0x0F should be 0
|
|
if (!(GAME_FLAGS & (FLAG_HAMIEBOOK + REF->vars[0]))) GAME_FLAGS ^= FLAG_HAMIEBOOK + REF->vars[0];
|
|
fade = 0;
|
|
fademode = FADE_OUT;
|
|
};
|
|
}
|
|
} else {
|
|
if (REF->vars[1] == 0) {
|
|
plr.flags ^= FLAG_HALTANIM;
|
|
plr.animindex = 0;
|
|
plr.animflimit = WTH_PLR_IDLE_LEN;
|
|
GAME_STATE = GSTATE_BOOK;
|
|
fade = 255;
|
|
fademode = FADE_IN;
|
|
fadespeed = 3;
|
|
REF->vars[1] = 255;
|
|
REF->flags ^= 1 << 4;
|
|
} else {
|
|
if (plr.x - 1 < REF->x) plr.x++;
|
|
if (plr.x + 1 > REF->x) plr.x--;
|
|
REF->vars[1]--;
|
|
fadespeed++;
|
|
};
|
|
};
|
|
break;
|
|
case INTERTYPE_STAIRS:
|
|
/*
|
|
FLAG 2 = player going up stairs?
|
|
FLAG 4 = go down?
|
|
VAR 0 = snapshot of player Y,
|
|
modified for going up or down
|
|
VAR 1 = time to wait before
|
|
doing something
|
|
*/
|
|
if (REF->flags & 2) {
|
|
if (REF->vars[1] == 0) {
|
|
if (plr.y == REF->vars[0]) {
|
|
REF->flags ^= 2 | 4;
|
|
plr.flags ^= FLAG_CANTMOVE | FLAG_REVERSEANIM;
|
|
plr.animindex--;
|
|
break;
|
|
};
|
|
if (REF->flags & 4) plr.y++;
|
|
else plr.y--;
|
|
} else {
|
|
REF->vars[1]--;
|
|
if (REF->vars[1] == 0) {
|
|
if (plr.flags & FLAG_HALTANIM) plr.flags ^= FLAG_HALTANIM;
|
|
plr.animindex++;
|
|
plr.animflimit = WTH_PLR_UPSTAIR2_LEN;
|
|
plr.animframe = 0;
|
|
};
|
|
};
|
|
} else {
|
|
if (plr.y >= REF->y - 64 && plr.x >= REF->x - 40 && plr.x < REF->x + 40) {
|
|
signalDraw(DRAW_PLRUPARROW);
|
|
if (keyPressed(KEY_UP)) {
|
|
REF->flags ^= 2;
|
|
plr.flags ^= FLAG_CANTMOVE | FLAG_HALTANIM;
|
|
plr.animframe = plr.animtimer = 0;
|
|
if (REF->flags & 4) {
|
|
plr.animindex = WTH_PLR_DNSTAIR1_SPR;
|
|
plr.animflimit = WTH_PLR_DNSTAIR1_LEN;
|
|
REF->vars[0] = plr.y + 64;
|
|
} else {
|
|
plr.animindex = WTH_PLR_UPSTAIR1_SPR;
|
|
plr.animflimit = WTH_PLR_UPSTAIR1_LEN;
|
|
REF->vars[0] = plr.y - 64;
|
|
};
|
|
REF->vars[1] = 18;
|
|
};
|
|
};
|
|
};
|
|
break;
|
|
case INTERTYPE_COAT:
|
|
if (plr.x >= REF->x - 40 && plr.x < REF->x + 40) {
|
|
signalDraw(DRAW_PLRUPARROW);
|
|
if (keyPressed(KEY_UP)) {
|
|
plr.flags ^= FLAG_HASCOAT;
|
|
signalPlaySFX(3);
|
|
};
|
|
};
|
|
break;
|
|
case INTERTYPE_ARTIFACT:
|
|
if (plr.x >= REF->x - 30 && plr.x < REF->x + 30) {
|
|
signalDraw(DRAW_PLRUPARROW);
|
|
if (keyPressed(KEY_UP)) {
|
|
REF->flags ^= INTER_ACTIVE; // disable it
|
|
plr.artifacts ^= 1 << REF->vars[0];
|
|
signalPlaySFX(6 + REF->vars[0]);
|
|
};
|
|
};
|
|
break;
|
|
case INTERTYPE_MOVETRACK:
|
|
/* imagine that the X of the INSTANCE is like a last-frame
|
|
X of the player, while the current plr.x is the current
|
|
frame X; we compare those and change the distance
|
|
variable accordingly
|
|
*/
|
|
if (REF->x != plr.x) {
|
|
REF->vars[0] += (REF->x < plr.x) ? plr.x - REF->x : REF->x - plr.x;
|
|
};
|
|
if (REF->vars[0] > 2500) {
|
|
level = REF->vars[1];
|
|
LoadInRoom();
|
|
while (plr.x > 0) {
|
|
plr.x -= 600;
|
|
cam_x -= 600;
|
|
}
|
|
};
|
|
REF->x = plr.x;
|
|
break;
|
|
case INTERTYPE_YOU:
|
|
/*
|
|
0 = animtimer, 1 = animframe, 2 = animflimit
|
|
*/
|
|
REF->vars[0]++;
|
|
if (REF->vars[0] >= ANIM_SPEED) {
|
|
REF->vars[1]++;
|
|
REF->vars[0] = 0;
|
|
};
|
|
if (REF->vars[1] > REF->vars[2]) REF->vars[1] = 0;
|
|
break;
|
|
case INTERTYPE_MOVEBLOCK:
|
|
/* prevents the player from moving either too
|
|
far to the left, or too far to the right
|
|
flag 1 blocks moving right; by default, it
|
|
only blocks moving left
|
|
*/
|
|
if ((plr.x < REF->x && !(REF->flags & 1)) || (plr.x > REF->x && (REF->flags & 1))) {
|
|
plr.x = REF->x;
|
|
plr.hsp = plr.hsp_sub = 0;
|
|
};
|
|
break;
|
|
case INTERTYPE_DOORSLAM:
|
|
if (REF->flags & 1 << 1) break;
|
|
if (plr.x > REF->x + 320) {
|
|
plr.state = PSTATE_CUTSCENE;
|
|
plr.animindex = WTH_PLR_SPOOKED_SPR;
|
|
plr.animframe = 0;
|
|
plr.animtimer = 0;
|
|
plr.animflimit = WTH_PLR_SPOOKED_LEN;
|
|
REF->flags ^= 1 << 1;
|
|
//TODO: game flag for triggering the door slam cutscene?
|
|
};
|
|
break;
|
|
}
|
|
};
|
|
|
|
#ifndef WTHCOMPILER_NOOPTIONSMENU
|
|
void manageOptions() {
|
|
if (menu.menuselect < 3) {
|
|
options[0] ^= 1 << menu.menuselect;
|
|
} else {
|
|
saveGame(); menu.menuindex = menu.menuselect = 0; menu.menulimit = 2;
|
|
};
|
|
};
|
|
#endif
|
|
|
|
void start() {
|
|
plr.x = 50;
|
|
plr.y = GROUNDLEVEL;
|
|
plr.state = 1;
|
|
cam_x = cam_y = 0;
|
|
GAME_STATE = 0;
|
|
GAME_FLAGS = 0;
|
|
plr.animflimit = WTH_PLR_IDLE_LEN;
|
|
menu.menuselect = 0;
|
|
menu.menulimit = 2;
|
|
#ifdef WTH_ARTIFACTS
|
|
plr.artifacts = WTH_ARTIFACTS;
|
|
#endif
|
|
loadGame();
|
|
};
|
|
|
|
uint8_t running = 1;
|
|
void step() {
|
|
switch (GAME_STATE)
|
|
{
|
|
case GSTATE_MENU:
|
|
if (keyPressed(KEY_UP) && menu.menuselect > 0) {
|
|
menu.menuselect--;
|
|
signalPlaySFX(0);
|
|
};
|
|
if (keyPressed(KEY_DOWN) && menu.menuselect < menu.menulimit) {
|
|
menu.menuselect++;
|
|
signalPlaySFX(0);
|
|
};
|
|
if (keyPressed(KEY_GESTURE)) {
|
|
signalPlaySFX(2);
|
|
#ifndef WTHCOMPILER_NOOPTIONSMENU
|
|
if (menu.menuindex == 1 || menu.menuindex == MINDEX_XOPTIONS) {
|
|
manageOptions();
|
|
} else {
|
|
#endif
|
|
switch (menu.menuselect)
|
|
{
|
|
case 0:
|
|
GAME_STATE = GSTATE_PLAYING;
|
|
signalPlayMUS(0);
|
|
fade = 255;
|
|
fademode = FADE_IN;
|
|
fadespeed = 2;
|
|
#ifdef WTH_DEBUG
|
|
changeRoom(DEBUG_STARTROOM, 0);
|
|
#else
|
|
changeRoom(0, 0);
|
|
#endif
|
|
break;
|
|
#ifndef WTHCOMPILER_NOOPTIONSMENU
|
|
case 1: menu.menuindex = 1; menu.menuselect = 0; menu.menulimit = 3; break;
|
|
#endif
|
|
case 2: running = 0; break;
|
|
}
|
|
#ifndef WTHCOMPILER_NOOPTIONSMENU
|
|
};
|
|
#endif
|
|
};
|
|
break;
|
|
case GSTATE_PLAYING:
|
|
if (!(options[0] & WTHOPTS_NOFADE)) {
|
|
if (fademode == FADE_IN && fade > 0) {
|
|
if (fade - fadespeed < 0) {
|
|
fade = 0;
|
|
fademode = FADE_NONE;
|
|
} else
|
|
fade -= fadespeed;
|
|
};
|
|
if (fademode == FADE_OUT && fade < 255) {
|
|
if (fade + fadespeed > 255) {
|
|
fade = 255;
|
|
fademode = FADE_NONE;
|
|
} else
|
|
fade += fadespeed;
|
|
};
|
|
} else fade = 0;
|
|
switch (plr.state)
|
|
{
|
|
case PSTATE_NORMAL:
|
|
if (!(plr.flags & FLAG_CANTMOVE)) {
|
|
if (keyPressed(KEY_GESTURE)) {
|
|
signalMisc(MISC_GESTURE);
|
|
};
|
|
|
|
/* maybe there's a way to "merge" the walking code in a way that reduces
|
|
the redundant animation and flag changes? */
|
|
if (((input_keys & KEY_LEFT) && !(input_keys & KEY_RIGHT)) && plr.hsp > -WALK_SPEEDLIMIT) {
|
|
plr.hsp_sub -= WALK_SPEED;
|
|
plr.animindex = WTH_PLR_WALK_SPR;
|
|
plr.animflimit = WTH_PLR_WALK_LEN;
|
|
if (plr.flags & FLAG_HALTANIM) plr.flags ^= FLAG_HALTANIM;
|
|
if (plr.flags & FLAG_REVERSEANIM) plr.flags ^= FLAG_REVERSEANIM;
|
|
if (plr.hsp > 0) plr.hsp = 0;
|
|
if (!(plr.flags & FLAG_FLIPPED)) plr.flags ^= FLAG_FLIPPED;
|
|
};
|
|
if (((input_keys & KEY_RIGHT) && !(input_keys & KEY_LEFT)) && plr.hsp < WALK_SPEEDLIMIT) {
|
|
plr.hsp_sub += WALK_SPEED;
|
|
plr.animindex = WTH_PLR_WALK_SPR;
|
|
plr.animflimit = WTH_PLR_WALK_LEN;
|
|
if (plr.flags & FLAG_HALTANIM) plr.flags ^= FLAG_HALTANIM;
|
|
if (plr.flags & FLAG_REVERSEANIM) plr.flags ^= FLAG_REVERSEANIM;
|
|
if (plr.hsp < 0) plr.hsp = 0;
|
|
if (plr.flags & FLAG_FLIPPED) plr.flags ^= FLAG_FLIPPED;
|
|
};
|
|
|
|
if ((plr.flags & FLAG_GROUNDED) && ((!(input_keys & KEY_LEFT) && !(input_keys & KEY_RIGHT)) || ((input_keys & KEY_LEFT) && (input_keys & KEY_RIGHT)))) {
|
|
/* if idle, set HSP to 0, AND
|
|
set sprite to idle, check idle animations */
|
|
plr.hsp = 0;
|
|
if (!(plr.flags & FLAG_REVERSEANIM)) {
|
|
if (plr.animindex != WTH_PLR_IDLE_SPR && plr.animindex != WTH_PLR_IDLE1_SPR) {
|
|
plr.animindex = WTH_PLR_IDLE_SPR;
|
|
plr.animflimit = WTH_PLR_IDLE_LEN;
|
|
if (plr.flags & FLAG_HALTANIM) plr.flags ^= FLAG_HALTANIM;
|
|
};
|
|
plr.idleanimtimer++;
|
|
if (plr.idleanimtimer >= 400) {
|
|
plr.animindex = WTH_PLR_IDLE1_SPR;
|
|
plr.animflimit = WTH_PLR_IDLE1_LEN;
|
|
if (!(plr.flags & FLAG_HALTANIM)) plr.flags ^= FLAG_HALTANIM;
|
|
plr.animtimer = 0;
|
|
plr.animframe = 0;
|
|
plr.idleanimtimer = 0;
|
|
};
|
|
} else if (plr.animframe == 0) plr.flags ^= FLAG_REVERSEANIM;
|
|
};
|
|
|
|
if (!(plr.flags & FLAG_NOPHYS)) {
|
|
if (!(plr.flags & FLAG_GROUNDED)) {
|
|
plr.vsp_sub += PRE_GRAVITY;
|
|
}
|
|
if (plr.y > GROUNDLEVEL || plr.y + plr.vsp > GROUNDLEVEL) {
|
|
if (!(plr.flags & FLAG_GROUNDED)) {
|
|
plr.flags |= FLAG_GROUNDED;
|
|
};
|
|
plr.vsp = 0;
|
|
plr.vsp_sub = 0;
|
|
plr.y = GROUNDLEVEL;
|
|
};
|
|
if (plr.vsp_sub > SUBPIXELUNIT_ACCURACY) {
|
|
int16_t calc = (plr.vsp_sub / SUBPIXELUNIT_ACCURACY);
|
|
plr.vsp += calc;
|
|
plr.vsp_sub -= SUBPIXELUNIT_ACCURACY * calc;
|
|
};
|
|
if (plr.vsp_sub < 0) {
|
|
int16_t calc = (plr.vsp_sub / SUBPIXELUNIT_ACCURACY);
|
|
plr.vsp -= calc;
|
|
plr.vsp_sub += SUBPIXELUNIT_ACCURACY * calc;
|
|
};
|
|
|
|
if (plr.hsp_sub > SUBPIXELUNIT_ACCURACY) {
|
|
int16_t calc = (plr.hsp_sub / SUBPIXELUNIT_ACCURACY);
|
|
plr.hsp += calc;
|
|
plr.hsp_sub -= SUBPIXELUNIT_ACCURACY * calc;
|
|
};
|
|
if (plr.hsp_sub < 0) {
|
|
int16_t calc = (plr.hsp_sub / SUBPIXELUNIT_ACCURACY);
|
|
plr.hsp += calc;
|
|
plr.hsp_sub -= SUBPIXELUNIT_ACCURACY * calc; // HOW does this make ANY logical sense to work properly
|
|
};
|
|
plr.x += plr.hsp;
|
|
plr.y += plr.vsp;
|
|
};
|
|
};
|
|
break;
|
|
case PSTATE_CUTSCENE:
|
|
switch (level)
|
|
{
|
|
case ROOM_HOSPITAL:
|
|
if (plr.animframe == WTH_PLR_SPOOKED_LEN) {
|
|
plr.state = PSTATE_NORMAL;
|
|
};
|
|
break;
|
|
};
|
|
break;
|
|
case PSTATE_SLEEPING: default:
|
|
plr.animindex = WTH_PLR_SLEEP_SPR;
|
|
plr.animflimit = WTH_PLR_SLEEP_LEN;
|
|
// TODO: make an animation for getting up instead of making Lukifer jump
|
|
if (keyPressed(KEY_GESTURE)) {
|
|
plr.state = PSTATE_NORMAL;
|
|
plr.animindex = WTH_PLR_WALK_SPR;
|
|
plr.vsp = -3;
|
|
plr.vsp_sub = 0;
|
|
signalPlaySFX(1);
|
|
plr.flags = 0;
|
|
};
|
|
break;
|
|
}
|
|
|
|
// room specific code
|
|
switch (level)
|
|
{
|
|
case ROOM_OUTDOORS:
|
|
if (plr.x >= 1920) {
|
|
int camdiff = cam_x - plr.x;
|
|
changeRoom(ROOM_STREETS, 0);
|
|
cam_x = plr.x + camdiff;
|
|
};
|
|
break;
|
|
case ROOM_STREETS:
|
|
// wrap the player around the level if they go too far
|
|
if (plr.x < -960) {
|
|
plr.x = 2600;
|
|
cam_x += 2600 + 960;
|
|
};
|
|
if (plr.x > 2600) {
|
|
plr.x = -960;
|
|
cam_x -= 2600 + 960;
|
|
};
|
|
break;
|
|
};
|
|
|
|
if (!(GAME_FLAGS & FLAG_LOCKCAM)) {
|
|
if (!(options[0] & WTHOPTS_DEVCAM)) {
|
|
if (plr.x > cam_x + (WIDTH - CAM_BOUNDS)) {
|
|
cam_x += plr.x - (cam_x + (WIDTH - CAM_BOUNDS));
|
|
};
|
|
if (plr.x < cam_x + CAM_BOUNDS) {
|
|
cam_x -= (cam_x + CAM_BOUNDS) - plr.x;
|
|
};
|
|
} else {
|
|
uint8_t speed = (xtrakeys & KEY_ATTACK) ? 6 : 3;
|
|
if (xtrakeys & KEY_RIGHT) cam_x += speed;
|
|
if (xtrakeys & KEY_LEFT) cam_x -= speed;
|
|
if (xtrakeys & KEY_UP) cam_y -= speed;
|
|
if (xtrakeys & KEY_DOWN) cam_y += speed;
|
|
};
|
|
};
|
|
|
|
plr.animtimer++;
|
|
if (plr.animtimer >= ANIM_SPEED && !(plr.flags & FLAG_HALTANIM && (plr.animframe == ((plr.flags & FLAG_REVERSEANIM) ? 0 : plr.animflimit)))) {
|
|
plr.animframe += (plr.flags & FLAG_REVERSEANIM) ? -1 : 1;
|
|
plr.animtimer = 0;
|
|
};
|
|
if (!(plr.flags & FLAG_HALTANIM)) {
|
|
if (plr.animframe > plr.animflimit) plr.animframe = 0;
|
|
}
|
|
if (plr.flags & FLAG_GROUNDED) {
|
|
if (plr.animindex == WTH_PLR_WALK_SPR && plr.animframe == 1 && plr.animflimit == WTH_PLR_WALK_LEN && !(plr.flags & FLAG_STEPDEBOUNCE)) {
|
|
signalPlaySFX(0);
|
|
plr.flags ^= FLAG_STEPDEBOUNCE;
|
|
} else if ((plr.animframe != 1 || plr.animindex != WTH_PLR_WALK_SPR) && (plr.flags & FLAG_STEPDEBOUNCE)) plr.flags ^= FLAG_STEPDEBOUNCE;
|
|
};
|
|
|
|
if (keyPressed(KEY_MENU)) {
|
|
GAME_STATE = GSTATE_PAUSED;
|
|
menu.menuselect = 0;
|
|
menu.menulimit = 2;
|
|
signalMisc(MISC_PAUSEMUSIC);
|
|
};
|
|
|
|
for (uint8_t i = 0; i < INTER_LIMIT; i++) {
|
|
if ((interacts[i].flags & INTER_ACTIVE)) {
|
|
interact_step(&interacts[i]);
|
|
};
|
|
};
|
|
break;
|
|
case GSTATE_PAUSED:
|
|
if (keyPressed(KEY_UP) && menu.menuselect > 0) {
|
|
menu.menuselect--;
|
|
signalPlaySFX(0);
|
|
};
|
|
if (keyPressed(KEY_DOWN) && menu.menuselect < menu.menulimit) {
|
|
menu.menuselect++;
|
|
signalPlaySFX(0);
|
|
};
|
|
if (keyPressed(KEY_GESTURE)) {
|
|
signalPlaySFX(2);
|
|
if (menu.menuindex == 0) {
|
|
switch (menu.menuselect)
|
|
{
|
|
case 0: GAME_STATE = GSTATE_PLAYING; signalMisc(MISC_RESUMEMUSIC);break;
|
|
//case 1: options[0] ^= WTHOPTS_DEVCAM; break;
|
|
case 1: menu.menuindex = 1; menu.menuselect = 0; menu.menulimit = 3; break;
|
|
case 2: // exiting from pause menu to main menu; clear all gamevariables!
|
|
#ifdef WTH_ARTIFACTS
|
|
plr.artifacts = WTH_ARTIFACTS;
|
|
#else
|
|
plr.artifacts = 0;
|
|
#endif
|
|
plr.animframe = plr.animindex = plr.animtimer = 0;
|
|
plr.hsp = plr.vsp = plr.hsp_sub = plr.vsp_sub = 0;
|
|
level = 0;
|
|
signalMisc(MISC_STOPMUSIC);
|
|
plr.flags = 0; // if this causes issues, move it to start()
|
|
start();
|
|
break;
|
|
}
|
|
} else manageOptions();
|
|
};
|
|
break;
|
|
case GSTATE_BOOK:
|
|
if (!(options[0] & WTHOPTS_NOFADE)) {
|
|
if (fademode == FADE_IN && fade > 0) {
|
|
if (fade - fadespeed < 0) {
|
|
fade = 0;
|
|
fademode = FADE_NONE;
|
|
} else
|
|
fade -= fadespeed;
|
|
};
|
|
if (fademode == FADE_OUT && fade < 255) {
|
|
if (fade + fadespeed > 255) {
|
|
fade = 255;
|
|
fademode = FADE_NONE;
|
|
} else
|
|
fade += fadespeed;
|
|
};
|
|
} else fade = 0;
|
|
if (keyPressed(KEY_RIGHT) && (bookpage & 0x0F) < (((bookpage & 0xF0) >> 4 == 0) ? WTH_BOOK_HAMIE_LEN : 15)) {
|
|
bookpage++;
|
|
};
|
|
if (keyPressed(KEY_LEFT) && (bookpage & 0x0F) > 0) {
|
|
bookpage--;
|
|
};
|
|
if (keyPressed(KEY_GESTURE)) {
|
|
GAME_STATE = GSTATE_PLAYING;
|
|
fademode = FADE_IN;
|
|
fadespeed = 3;
|
|
if (plr.flags & FLAG_CANTMOVE) plr.flags ^= FLAG_CANTMOVE;
|
|
signalPlaySFX(11);
|
|
};
|
|
break;
|
|
}
|
|
|
|
input_prevkeys = input_keys;
|
|
input_keys = 0;
|
|
if (options[0] & WTHOPTS_DEVCAM) xtrakeys = 0;
|
|
};
|