WakeToHell/main_allegro.c
2025-04-06 14:52:44 -04:00

437 lines
16 KiB
C

/*
Allegro5 frontend for the game
Licensed under CC0, public domain, 2023-2024
Made by blitzdoughnuts
*/
#include <stdio.h>
#include <stdint.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>
#undef main
#include "game.h" // keep in mind, MAIN can still access variables from the game!
#ifndef EDITMODE
#define EDITMODE 0
#endif
// for aspect correction
#define CAM_VERTOFFSET ((HEIGHT - 270) / 4)
// lookup table for font offsets, to offset the character afterwards to the left by this amount
static const uint8_t fontOffsets_left[90] = {
21, 12, 3, 11, 7, 14, 22, 20, 20, 4, 4, 21, 1, 20, 11, // 33-47
8, 10, 6, 11, 10, 8, 9, 6, 13, 7, 5, 23, 21, 5, 24, 6, 24, // 48-64
0, 7, 7, 7, 4, 7, 4, 0, 0, 0, 9, 7, 0, 9, 4, 9, 5, 11, 9, 6, 6, 10, 3, 3, 9, 5, // 64-96 (A-Z)
12, 12, 12, 16, 3, 21, // 97-102
14, 16, 13, 16, 14, 15, 12, 11, 23, // 103-122 (a-z)
18, 18, 21, 3, 12, 12, 15, 10, 12, 13, 13, 13, 15, 5, 13, 13, 13 // 123-139
};
static const char str_savegamename[] = "WTH_SaveGame.bin";
ALLEGRO_SAMPLE *music[3];
ALLEGRO_SAMPLE *sfx[12];
ALLEGRO_BITMAP *plrsprites[WTH_PLR_SPRCOUNT * 2];
ALLEGRO_BITMAP *sprites[WTH_SPRCOUNT];
#ifdef WTH_DEBUG
ALLEGRO_BITMAP *DEBUG_sprites[1];
#endif
ALLEGRO_DISPLAY *window;
uint8_t renderFlags; // flags to tell Allegro5 what to draw, modified by signalDraw()
uint8_t running;
void drawSprite(ALLEGRO_BITMAP *sprite, int x, int y) {
al_draw_bitmap(sprite, (float)x, (float)y, 0);
};
FILE *DaSave;
#if WTH_SDL_DEMOSUPPORT == 1
FILE *DaDemo;
uint8_t demoStatus;
uint8_t demowrite[1];
uint32_t demoPointer;
static const char str_wintitle[] = WINTITLE; // it's only here to cut down redundancies
#endif
void drawRepeatingSprite(ALLEGRO_BITMAP *sprite, int x, int y) {
};
void drawSpriteSheeted(ALLEGRO_BITMAP *sprite, int x, int y, int frame, int x_sprdist, int y_sprdist, uint8_t flipped) { // supports only purely horizontal sheets
al_draw_scaled_bitmap(sprite, x_sprdist * frame, 0, x_sprdist, y_sprdist, x, y, x_sprdist, y_sprdist, flipped);
};
void drawTextString(const char *stuff, int x, int y, uint8_t color) {
int16_t _x = x;
for (uint8_t i = 0; i < 255; i++) {
if (stuff[i] == '\0') break; // terminator character? then get outta there before stuff gets nasty
if (_x < -27 || _x > 27 + WIDTH) return;
};
};
void signalMisc(uint8_t signal) {
};
#define WTH_OPTIONTEXT_X 30
#define WTH_OPTIONTEXT_Y 40
void drawOptions() {
// drawRepeatingSprite has no functionality for VERTICAL tiling... fuck it, we ball
for (uint16_t i = 0; i < HEIGHT; i += 64) {
drawRepeatingSprite(sprites[WTH_SPR_OPTMENU], 0, i);
};
drawTextString("Fade", WTH_OPTIONTEXT_X,WTH_OPTIONTEXT_Y, menu.menuselect == 0 ? 1 : 0);
drawTextString((options[0] & WTHOPTS_NOFADE) ? str_off : str_on, 320, WTH_OPTIONTEXT_Y, menu.menuselect == 0 ? 1 : 0);
drawTextString("Freecam", WTH_OPTIONTEXT_X,WTH_OPTIONTEXT_Y + 32, menu.menuselect == 1 ? 1 : 0);
drawTextString((options[0] & WTHOPTS_DEVCAM) ? str_on : str_off, 320, WTH_OPTIONTEXT_Y + 32, menu.menuselect == 1 ? 1 : 0);
drawTextString("Music", WTH_OPTIONTEXT_X,WTH_OPTIONTEXT_Y + 64, menu.menuselect == 2 ? 1 : 0);
drawTextString((options[0] & WTHOPTS_DOSONG) ? str_on : str_off, 320, WTH_OPTIONTEXT_Y + 64, menu.menuselect == 2 ? 1 : 0);
drawTextString(str_exit, 32,HEIGHT - 48, menu.menuselect == 3 ? 1 : 0);
};
void signalDraw(uint8_t index) {
switch (index)
{
case DRAW_PLRUPARROW:
renderFlags ^= 1;
break;
case DRAW_PLRDNARROW:
renderFlags ^= 2;
break;
};
};
// inline the simple stuff
static inline void signalPlaySFX(uint8_t signal) {
#ifndef WTHOPTS_SDL_NOMIXER
//Mix_PlayChannel(-1, sfx[signal], 0);
#endif
};
static inline void signalPlayMUS(uint8_t signal) {
#ifndef WTHOPTS_SDL_NOMIXER
//if (options[0] & WTHOPTS_DOSONG) Mix_PlayMusic(music[signal], -1);
#endif
};
void saveGame() {
DaSave = fopen(str_savegamename,"w");
fwrite(&options, 1, 2, DaSave);
fclose(DaSave);
};
void loadGame() {
if (!(DaSave = fopen(str_savegamename,"r"))) {
options[0] = DEFAULT_OPTIONS;
return;
};
fread(&options, 1, 2, DaSave);
fclose(DaSave);
};
void draw() {
switch (GAME_STATE)
{
case GSTATE_MENU:
//SDL_FillRect(winsurf, NULL, 400);
switch (menu.menuindex)
{
case 0:
drawSprite(sprites[WTH_SPR_MENU], 0, 0);
drawSpriteSheeted(sprites[WTH_SPR_MENUBUTTONS], 47, 147, (menu.menuselect == 0) ? 1 : 0, 99, 23, 0);
drawSpriteSheeted(sprites[WTH_SPR_MENUBUTTONS], 47, 170, (menu.menuselect == 1) ? 3 : 2, 99, 23, 0);
drawSpriteSheeted(sprites[WTH_SPR_MENUBUTTONS], 47, 193, (menu.menuselect == 2) ? 5 : 4, 99, 23, 0);
#ifdef WTH_FANCY
drawTextString("Fancy :)", 0, HEIGHT - 48, 0);
#endif
break;
case 1: case MINDEX_XOPTIONS:
drawOptions();
break;
}
drawTextString(VERSION_NUMBER, 0, 0, 0);
break;
case GSTATE_PLAYING:
// draw background
switch (level)
{
case ROOM_TEST: case ROOM_STREETS:
drawRepeatingSprite(sprites[WTH_SPR_BG_STREET], -cam_x, -cam_y);
break;
case ROOM_HOUSE:
drawSprite(sprites[WTH_SPR_BG_HOUSE], -456 - cam_x - 75, -cam_y);
break;
case ROOM_OUTDOORS:
drawRepeatingSprite(sprites[WTH_SPR_BG_OUTDOORS], -cam_x / 2, -cam_y);
break;
case ROOM_HOSPITAL:
drawSprite(sprites[WTH_SPR_BG_HOSPITAL], -45 - cam_x, -cam_y);
break;
case ROOM_H_PATIENTS:
drawSprite(sprites[WTH_SPR_BG_HOS_PATIENTS], -30 - cam_x, -cam_y);
break;
case ROOM_H_HAMIE:
drawSprite(sprites[WTH_SPR_BG_HOS_HAMIE], -45 - cam_x, -cam_y);
break;
case ROOM_H_RICHIE:
drawSprite(sprites[WTH_SPR_BG_HOS_RICHIE], -45 - cam_x, -cam_y);
break;
case ROOM_BAR:
drawSprite(sprites[WTH_SPR_BG_BAR], -250 - cam_x, -cam_y);
break;
};
for (uint8_t i = 0; i < interacts_count; i++) {
if (!(interacts[i].flags & INTER_ACTIVE)) continue;
switch (interacts[i].objID)
{
case 255:
drawSpriteSheeted(sprites[WTH_SPR_YOU], (interacts[i].x - 75) - cam_x, (interacts[i].y - 100) - cam_y, interacts[i].vars[1], 150, 150, (plr.x < interacts[i].x) ? 1 : 0);
break;
case INTERTYPE_COAT:
drawSpriteSheeted(sprites[WTH_SPR_COATRACK], interacts[i].x - 35 - cam_x, (interacts[i].y - 100) - cam_y, (plr.flags & FLAG_HASCOAT) ? 1 : 0, 70, 100, 0);
break;
case INTERTYPE_DECOR:
drawSprite(sprites[interacts[i].vars[0]], interacts[i].x - cam_x, interacts[i].y - cam_y);
break;
case INTERTYPE_ARTIFACT:
drawSpriteSheeted(sprites[WTH_SPR_ARTIFACTS], (interacts[i].x - 32) - cam_x, (interacts[i].y - 32) - cam_y, interacts[i].vars[0], 64, 64, 0);
break;
case INTERTYPE_DOOR:
drawSpriteSheeted(sprites[WTH_SPR_DOOR], (interacts[i].x - 75) - cam_x, (interacts[i].y - 100) - cam_y, ((interacts[i].flags & 1 << 2) || interacts[i].vars[1]) ? 1 : 0, 150, 150, 0);
break;
case INTERTYPE_STAIRS:
drawSpriteSheeted(sprites[WTH_SPR_STAIRS], (interacts[i].x - 75) - cam_x, (interacts[i].y - 100) - cam_y, 0, 150, 150, 0);
break;
case INTERTYPE_BOOK:
drawSpriteSheeted(sprites[WTH_SPR_BOOK_HAMIE], (interacts[i].x - 32) - cam_x, (interacts[i].y - 32) - cam_y, ((interacts[i].flags & 1 << 2) || interacts[i].vars[1]) ? 1 : 0, 64, 64, 0);
break;
#ifdef WTH_DEBUG
case INTERTYPE_GESTURE:
drawSprite(DEBUG_sprites[0], (interacts[i].x - 75) - cam_x, (interacts[i].y - 100) - cam_y);
break;
#endif
}
};
// draw player
uint8_t plrFrame = plr.animframe;
if (plr.animindex == WTH_PLR_SPOOKED_SPR) {
switch (plr.animframe)
{
case 2: case 3: // stall Lukifer's initial shock for 3 frames
plrFrame = 1;
break;
case 4: case 5: case 6: case 7: // cue turnaround
plrFrame -= 3;
break;
case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: // line boil Lukifer turned around
plrFrame = 4 + (plr.animframe & 1);
break;
case 21: case 22: // Lukifer reacts in unamusement
plrFrame -= 16;
break;
case 23: case 24: case 25: case 26: case 27: case 28: // loop unamusement
plrFrame = 7;
break;
case 29: case 30: // go back to turning right
plrFrame -= 21;
break;
};
};
drawSpriteSheeted(plrsprites[(plr.flags & FLAG_HASCOAT) ? plr.animindex : plr.animindex + WTH_PLR_SPRCOUNT], (plr.x - 75) - cam_x, (plr.y - 100) - cam_y, plrFrame, 150, 150, (plr.flags & FLAG_FLIPPED) ? 1 : 0);
if (renderFlags & 1) drawSprite(sprites[WTH_SPR_UPARROW], plr.x - 20 - cam_x, plr.y - 150 - cam_y);
if (renderFlags & 2) drawSprite(sprites[WTH_SPR_UPARROW], plr.x - 20 - cam_x, plr.y + 25 - cam_y);
#ifdef WTH_DEBUG
switch (level)
{
case ROOM_TEST: drawTextString("Test Room", 0, 0, 0); break;
case ROOM_HOUSE: drawTextString("House", 0, 0, 0); break;
case ROOM_OUTDOORS: drawTextString("Outside of House", 0, 0, 0); break;
case ROOM_STREETS: drawTextString("Endless Sidewalk", 0, 0, 0); break;
case ROOM_HOSPITAL: drawTextString("Hospital Lobby", 0, 0, 0); break;
case ROOM_H_PATIENTS: drawTextString("Hospital Patient Lobby", 0, 0, 0); break;
case ROOM_H_HAMIE: drawTextString("Hamie's Room", 0, 0, 0); break;
case ROOM_H_RICHIE: drawTextString("Richie's Room", 0, 0, 0); break;
case ROOM_H_DARK: drawTextString("Dark Room", 0, 0, 0); break;
case ROOM_YOU: drawTextString("Dark Room?", 0, 0, 0); break;
case ROOM_BAR: drawTextString("Bar", 0, 0, 0); break;
};
#endif
break;
case GSTATE_PAUSED:
//SDL_FillRect(winsurf, NULL, 400);
switch (menu.menuindex)
{
case 0:
if (plr.artifacts & 1 << ARTIFACT_BADGE)
drawSpriteSheeted(sprites[WTH_SPR_ARTIFACTS], 248, 96, 0, 64, 64, 0);
if (plr.artifacts & 1 << ARTIFACT_MIRROR)
drawSpriteSheeted(sprites[WTH_SPR_ARTIFACTS], 312, 96, 1, 64, 64, 0);
if (plr.artifacts & 1 << ARTIFACT_DONUT)
drawSpriteSheeted(sprites[WTH_SPR_ARTIFACTS], 248, 144, 2, 64, 64, 0);
if (plr.artifacts & 1 << ARTIFACT_KNIFE)
drawSpriteSheeted(sprites[WTH_SPR_ARTIFACTS], 312, 144, 3, 64, 64, 0);
if (plr.flags & FLAG_HASCOAT)
drawSpriteSheeted(sprites[WTH_SPR_COATRACK], 376, 108, 0, 100, 100, 0);
if (GAME_FLAGS & FLAG_HAMIEBOOK) // TODO: Proper sprite for Hamie's book
drawTextString("Read Hamie's book", 312, 200, 0);
drawTextString("PAUSED",80, 64, 0);
drawTextString("Resume", 80, 120, menu.menuselect == 0 ? 1 : 0);
drawTextString("Options", 80, 152, menu.menuselect == 1 ? 1 : 0);
drawTextString(str_exit, 80, 184, menu.menuselect == 2 ? 1 : 0);
break;
case 1:
drawOptions();
break;
}
break;
case GSTATE_BOOK:
drawSpriteSheeted(sprites[WTH_SPR_READ_HAMIE], 0, 0, bookpage & 0x0F, 480, 270, 0);
break;
};
renderFlags = 0;
};
uint8_t *keystates;
void keys() {
};
int main(int argc, char *argv[]) {
al_init();
al_init_image_addon();
al_init_acodec_addon();
al_init_primitives_addon();
window = al_create_display(WIDTH, HEIGHT);
al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW);
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__APPLE__)
puts("WARNING: You are running Wake to Hell under a malware OS. Please consider switching to a less harmful system.");
FILE *malwareOSWarning;
fopen(malwareOSWarning, "WARNING.txt");
fprintf(malwareOSWarning, "You are running Wake to Hell under a malware OS. Please consider switching to a less harmful system.");
fclose(malwareOSWarning);
#endif
#ifndef WTH_NOSOUND
if (!al_install_audio()) {
puts("The game couldn't find a usable audio system. Check your audio devices!");
} else {
music[MUS_NMARE] = al_load_audio_stream(MUSF_NMARE, 64, 512);
music[MUS_BAR] = al_load_audio_stream(MUSF_BAR, 64, 512);
music[MUS_CRC] = al_load_audio_stream(MUSF_CRC, 64, 512);
sfx[0] = al_load_sample("sfx/step.wav");
sfx[1] = al_load_sample("sfx/jump.wav");
sfx[2] = al_load_sample("sfx/slam.wav");
sfx[3] = al_load_sample("sfx/coatpickup.wav");
sfx[4] = al_load_sample("sfx/dooropen.wav");
sfx[5] = al_load_sample("sfx/doorclose.wav");
sfx[6] = al_load_sample("sfx/artifact_badge.wav");
sfx[7] = al_load_sample("sfx/artifact_mirror.wav");
sfx[8] = al_load_sample("sfx/artifact_donut.wav");
sfx[9] = al_load_sample("sfx/artifact_knife.wav");
sfx[10] = al_load_sample("sfx/paperopen.wav");
sfx[11] = al_load_sample("sfx/paperclose.wav");
};
#endif
// COAT
plrsprites[WTH_PLR_IDLE_SPR] = al_load_bitmap("sprites/plr_idle.png");
plrsprites[WTH_PLR_WALK_SPR] = al_load_bitmap("sprites/plr_walk.png");
plrsprites[WTH_PLR_DOOR_SPR] = al_load_bitmap("sprites/plr_enterdoor.png");
plrsprites[WTH_PLR_SLEEP_SPR] = al_load_bitmap("sprites/plr_asleep.png");
plrsprites[WTH_PLR_IDLE1_SPR] = al_load_bitmap("sprites/plr_idle1.png");
plrsprites[WTH_PLR_UPSTAIR1_SPR] = al_load_bitmap("sprites/plr_enterdoor.png");
plrsprites[WTH_PLR_UPSTAIR2_SPR] = al_load_bitmap("sprites/plr_upstairs2.png");
plrsprites[WTH_PLR_DNSTAIR1_SPR] = al_load_bitmap("sprites/plr_idle1.png");
plrsprites[WTH_PLR_DNSTAIR2_SPR] = al_load_bitmap("sprites/plr_walk.png");
plrsprites[WTH_PLR_SPOOKED_SPR] = al_load_bitmap("sprites/plr_surprise.png");
// NO COAT
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_IDLE_SPR] = al_load_bitmap("sprites/plr_idle_coatless.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_WALK_SPR] = al_load_bitmap("sprites/plr_walk_coatless.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_DOOR_SPR] = al_load_bitmap("sprites/plr_enterdoor_coatless.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_SLEEP_SPR] = al_load_bitmap("sprites/plr_asleep.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_IDLE1_SPR] = al_load_bitmap("sprites/plr_idle1_coatless.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_UPSTAIR1_SPR] = al_load_bitmap("sprites/plr_enterdoor_coatless.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_UPSTAIR2_SPR] = al_load_bitmap("sprites/plr_upstairs2.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_DNSTAIR1_SPR] = al_load_bitmap("sprites/plr_idle1.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_DNSTAIR2_SPR] = al_load_bitmap("sprites/plr_walk_coatless.png");
plrsprites[WTH_PLR_SPRCOUNT + WTH_PLR_SPOOKED_SPR] = al_load_bitmap("sprites/plr_surprise.png");
sprites[WTH_SPR_MENU] = al_load_bitmap("sprites/MENU.png");
sprites[WTH_SPR_FONTMAP] = al_load_bitmap("sprites/fontmap.png");
sprites[WTH_SPR_MENUBUTTONS] = al_load_bitmap("sprites/MENUBUTTONS.png");
sprites[WTH_SPR_UPARROW] = al_load_bitmap("sprites/uparrow.png");
sprites[WTH_SPR_OPTMENU] = al_load_bitmap("sprites/OPTIONSMENU.png");
sprites[WTH_SPR_BG_STREET] = al_load_bitmap("sprites/bg.png");
sprites[WTH_SPR_BG_HOUSE] = al_load_bitmap("sprites/bg_house.png");
sprites[WTH_SPR_BG_OUTDOORS] = al_load_bitmap("sprites/bg_outdoors.png");
sprites[WTH_SPR_BG_HOSPITAL] = al_load_bitmap("sprites/bg_hospital.png");
sprites[WTH_SPR_COATRACK] = al_load_bitmap("sprites/coatrack.png");
sprites[WTH_SPR_DOOR] = al_load_bitmap("sprites/door.png");
sprites[WTH_SPR_ARTIFACTS] = al_load_bitmap("sprites/artifacts.png");
sprites[WTH_SPR_YOU] = al_load_bitmap("sprites/you_idle.png");
sprites[WTH_SPR_DECOR_BED] = al_load_bitmap("sprites/bed.png");
sprites[WTH_SPR_DECOR_MAILBOX] = al_load_bitmap("sprites/decor_mailbox.png");
sprites[WTH_SPR_DECOR_HATRACK] = al_load_bitmap("sprites/decor_hatrack.png");
sprites[WTH_SPR_BOOK_HAMIE] = al_load_bitmap("sprites/book.png");
sprites[WTH_SPR_READ_HAMIE] = al_load_bitmap("sprites/read_hamie.png");
if (!al_install_keyboard()) {
puts("ALLEGRO: Couldn't install keyboard!");
return 1;
};
int running = 1;
ALLEGRO_EVENT_QUEUE *queue = al_create_event_queue();
ALLEGRO_EVENT event;
ALLEGRO_KEYBOARD_STATE keyboard;
al_register_event_source(queue, al_get_keyboard_event_source());
// to-do: figure out what the hell is causing keyboard inputs to crash the game
start();
while (running) {
if (!al_is_event_queue_empty(queue)) {
while (al_get_next_event(queue, &event)) {
};
};
#if (HEIGHT != 270)
cam.y = -CAM_VERTOFFSET;
#endif
draw();
al_get_keyboard_state(&keyboard);
if (al_key_down(&keyboard, ALLEGRO_KEY_Z)) input_keys ^= WTH_KEY_GESTURE;
if (al_key_down(&keyboard, ALLEGRO_KEY_UP)) input_keys ^= WTH_KEY_UP;
if (al_key_down(&keyboard, ALLEGRO_KEY_DOWN)) input_keys ^= WTH_KEY_DOWN;
if (al_key_down(&keyboard, ALLEGRO_KEY_LEFT)) input_keys ^= WTH_KEY_LEFT;
if (al_key_down(&keyboard, ALLEGRO_KEY_RIGHT)) input_keys ^= WTH_KEY_RIGHT;
if (al_key_down(&keyboard, ALLEGRO_KEY_ESCAPE)) input_keys ^= WTH_KEY_MENU;
step();
al_flip_display();
}
printf("Exiting game...\n");
return 0;
};