WakeToHell/game.h

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;
};