Skip to content

Instantly share code, notes, and snippets.

@Myvar
Created April 15, 2024 12:13
Show Gist options
  • Save Myvar/d0aaf3c34baee8aafc5f5d080b44abc1 to your computer and use it in GitHub Desktop.
Save Myvar/d0aaf3c34baee8aafc5f5d080b44abc1 to your computer and use it in GitHub Desktop.
Nasty Hackky code
//stuff like this nastyness
extern boolean isInMenu;
extern boolean isInDialog;
#ifdef SDL_PATHS
#include <unistd.h>
#endif
#include <limits.h>
#include <SDL_image.h>
#include <SDL_gamecontroller.h>
#include "platform.h"
#include "tiles.h"
#include "GlobalsBase.h"
#define PAUSE_BETWEEN_EVENT_POLLING 36L // 17
#define MAX_REMAPS 128
struct keypair
{
char from;
char to;
};
static struct keypair remapping[MAX_REMAPS];
static size_t nremaps = 0;
static enum graphicsModes showGraphics = TEXT_GRAPHICS;
static SDL_GameController *controller = NULL;
static rogueEvent lastEvent;
static void sdlfatal(char *file, int line)
{
fprintf(stderr, "Fatal SDL error (%s:%d): %s\n", file, line, SDL_GetError());
exit(EXIT_STATUS_FAILURE_PLATFORM_ERROR);
}
static void imgfatal(char *file, int line)
{
fprintf(stderr, "Fatal SDL_image error (%s:%d): %s\n", file, line, IMG_GetError());
exit(EXIT_STATUS_FAILURE_PLATFORM_ERROR);
}
/*
If the key is to be processed, returns true and updates event. False
otherwise. This function only listens for keypresses which do not produce
corresponding TextInputEvents.
*/
static boolean eventFromKey(rogueEvent *event, SDL_Keycode key)
{
event->param1 = -1;
switch (key)
{
case SDLK_ESCAPE:
event->param1 = ESCAPE_KEY;
return true;
case SDLK_UP:
event->param1 = UP_ARROW;
return true;
case SDLK_DOWN:
event->param1 = DOWN_ARROW;
return true;
case SDLK_RIGHT:
event->param1 = RIGHT_ARROW;
return true;
case SDLK_LEFT:
event->param1 = LEFT_ARROW;
return true;
case SDLK_RETURN:
case SDLK_KP_ENTER:
event->param1 = RETURN_KEY;
return true;
case SDLK_BACKSPACE:
event->param1 = DELETE_KEY;
return true;
case SDLK_TAB:
event->param1 = TAB_KEY;
return true;
case SDLK_PRINTSCREEN:
event->param1 = PRINTSCREEN_KEY;
return true;
}
/*
Only process keypad events when we're holding a modifier, as there is no
TextInputEvent then.
*/
if (event->shiftKey || event->controlKey)
{
switch (key)
{
case SDLK_KP_0:
event->param1 = NUMPAD_0;
return true;
case SDLK_KP_1:
event->param1 = NUMPAD_1;
return true;
case SDLK_KP_2:
event->param1 = NUMPAD_2;
return true;
case SDLK_KP_3:
event->param1 = NUMPAD_3;
return true;
case SDLK_KP_4:
event->param1 = NUMPAD_4;
return true;
case SDLK_KP_5:
event->param1 = NUMPAD_5;
return true;
case SDLK_KP_6:
event->param1 = NUMPAD_6;
return true;
case SDLK_KP_7:
event->param1 = NUMPAD_7;
return true;
case SDLK_KP_8:
event->param1 = NUMPAD_8;
return true;
case SDLK_KP_9:
event->param1 = NUMPAD_9;
return true;
}
}
// Ctrl+letter doesn't give a TextInputEvent
if (event->controlKey && key >= SDLK_a && key <= SDLK_z)
{
event->param1 = 'a' + (key - SDLK_a);
if (event->shiftKey)
event->param1 -= 'a' - 'A';
return true;
}
return false;
}
static boolean _modifierHeld(int mod)
{
SDL_Keymod km = SDL_GetModState();
return mod == 0 && (km & (KMOD_LSHIFT | KMOD_RSHIFT)) || mod == 1 && (km & (KMOD_LCTRL | KMOD_RCTRL));
}
static char applyRemaps(char c)
{
for (size_t i = 0; i < nremaps; i++)
{
if (remapping[i].from == c)
return remapping[i].to;
}
return c;
}
int prevxDir = 0;
int prevyDir = 0;
int toggle = 0;
int mouseX = 0;
int mouseY = 0;
/*
If an event is available, returns true and updates returnEvent. Otherwise
it returns false and an error event. This function also processes
platform-specific inputs/behaviours.
*/
static boolean pollBrogueEvent(rogueEvent *returnEvent, boolean textInput)
{
static int mx = 0, my = 0;
returnEvent->eventType = EVENT_ERROR;
returnEvent->shiftKey = _modifierHeld(0);
returnEvent->controlKey = _modifierHeld(1);
SDL_Event event;
boolean ret = false;
// ~ for (int i=0; i < 100 && SDL_PollEvent(&event); i++) {
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
// the player clicked the X button!
SDL_Quit();
int statusCode = quitImmediately();
exit(statusCode);
}
else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED)
{
resizeWindow(event.window.data1, event.window.data2);
}
else if (event.type == SDL_KEYDOWN)
{
SDL_Keycode key = event.key.keysym.sym;
if (key == SDLK_PAGEUP)
{
resizeWindow(max(windowWidth * 11 / 10, windowWidth + 1), max(windowHeight * 11 / 10, windowHeight + 1));
continue;
}
else if (key == SDLK_PAGEDOWN)
{
fullScreen = false;
resizeWindow(max(windowWidth * 10 / 11, 1), max(windowHeight * 10 / 11, 1));
continue;
}
else if (key == SDLK_F11 || key == SDLK_F12 || key == SDLK_RETURN && (SDL_GetModState() & KMOD_ALT))
{
fullScreen = !fullScreen;
resizeWindow(-1, -1); // Reset to starting resolution
continue;
}
if (eventFromKey(returnEvent, key))
{
returnEvent->eventType = KEYSTROKE;
return true;
}
}
else if (event.type == SDL_TEXTINPUT && (unsigned char)(event.text.text[0]) < 0x80)
{
/*
It's difficult/impossible to check what characters are on the
shifts of keys. So to detect '&', '>' etc. reliably we need to
listen for text input events as well as keydowns. This results
in hybrid keyboard code, where Brogue KEYSTROKEs can come from
different SDL events.
*/
char c = event.text.text[0];
if (!textInput)
{
c = applyRemaps(c);
if (c == '=' || c == '+')
{
resizeWindow(max(windowWidth * 11 / 10, windowWidth + 1), max(windowHeight * 11 / 10, windowHeight + 1));
}
else if (c == '-')
{
fullScreen = false;
resizeWindow(max(windowWidth * 10 / 11, 1), max(windowHeight * 10 / 11, 1));
}
}
returnEvent->eventType = KEYSTROKE;
returnEvent->param1 = c;
// ~ printf("textinput %s\n", event.text.text);
return true;
}
else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP)
{
if (event.button.button == SDL_BUTTON_LEFT || event.button.button == SDL_BUTTON_RIGHT)
{
if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT)
{
returnEvent->eventType = MOUSE_DOWN;
}
else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT)
{
returnEvent->eventType = MOUSE_UP;
}
else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT)
{
returnEvent->eventType = RIGHT_MOUSE_DOWN;
}
else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_RIGHT)
{
returnEvent->eventType = RIGHT_MOUSE_UP;
}
returnEvent->param1 = event.button.x * COLS / windowWidth;
returnEvent->param2 = event.button.y * ROWS / windowHeight;
return true;
}
}
else if (event.type == SDL_MOUSEMOTION)
{
// We don't want to return on a mouse motion event, because only the last
// in the queue is important. That's why we just set ret=true
int xcell = event.motion.x * COLS / windowWidth,
ycell = event.motion.y * ROWS / windowHeight;
if (xcell != mx || ycell != my)
{
returnEvent->eventType = MOUSE_ENTERED_CELL;
returnEvent->param1 = xcell;
returnEvent->param2 = ycell;
mx = xcell;
my = ycell;
ret = true;
}
}
else if (event.type == SDL_CONTROLLERAXISMOTION)
{
int xDir = 0;
int yDir = 0;
const int JOYSTICK_DEAD_ZONE = 8000;
// Motion on controller 0
// X axis motion
if (event.jaxis.axis == 0)
{
// Left of dead zone
if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
{
xDir = -1;
}
// Right of dead zone
else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
{
xDir = 1;
}
else
{
xDir = 0;
}
}
// Y axis motion
else if (event.jaxis.axis == 1)
{
// Below of dead zone
if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
{
yDir = -1;
}
// Above of dead zone
else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
{
yDir = 1;
}
else
{
yDir = 0;
}
}
int speed = 30;
mouseX += xDir * speed;
mouseY += yDir * speed;
if (mouseX < 0)
{
mouseX = 0;
}
if (mouseX > windowWidth)
{
mouseX = windowWidth;
}
if (mouseY < 0)
{
mouseY = 0;
}
if (mouseY > windowHeight)
{
mouseY = windowHeight;
}
// int xcell = mouseX * COLS / windowWidth,
// ycell = mouseY * ROWS / windowHeight;
// returnEvent->eventType = MOUSE_ENTERED_CELL;
// returnEvent->param1 = xcell;
// returnEvent->param2 = ycell;
// ret = true;
if (event.jaxis.axis == 5)
{
returnEvent->eventType = KEYSTROKE;
returnEvent->param1 = RETURN_KEY;
return true;
}
prevxDir = xDir;
prevyDir = yDir;
}
else if (event.type == SDL_CONTROLLERBUTTONDOWN)
{
returnEvent->eventType = KEYSTROKE;
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{
toggle = 1;
returnEvent->param1 = 'n';
return true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
{
returnEvent->param1 = toggle ? 'y' : LEFT_ARROW;
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
returnEvent->param1 = toggle ? 'n' : RIGHT_ARROW;
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
{
returnEvent->param1 = toggle ? 'u' : UP_ARROW;
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
{
returnEvent->param1 = toggle ? 'b' : DOWN_ARROW;
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_A)
{
if (toggle)
{
returnEvent->eventType = MOUSE_DOWN;
int xcell = mouseX * COLS / windowWidth,
ycell = mouseY * ROWS / windowHeight;
returnEvent->param1 = xcell;
returnEvent->param2 = ycell;
}
else
{
if(isInMenu) {
returnEvent->param1 = 'a';
} else {
returnEvent->param1 = 'x';
}
}
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_Y)
{
returnEvent->param1 = 't';
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_B)
{
if(isInMenu) {
returnEvent->param1 = ESCAPE_KEY;
ret = true;
} else {
returnEvent->param1 = 'z';
ret = true;
}
} else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_START)
{
returnEvent->param1 = 'i';
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
{
returnEvent->param1 = 'y';
ret = true;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK)
{
returnEvent->param1 = '>';
ret = true;
}
}
else if (event.type == SDL_CONTROLLERBUTTONUP)
{
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER)
{
toggle = 0;
}
else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_A)
{
if (toggle)
{
returnEvent->eventType = MOUSE_UP;
int xcell = mouseX * COLS / windowWidth,
ycell = mouseY * ROWS / windowHeight;
returnEvent->param1 = xcell;
returnEvent->param2 = ycell;
return true;
}
}
}
else if (event.type == SDL_CONTROLLERDEVICEADDED)
{
printf("device added\n");
}
else if (event.type == SDL_CONTROLLERDEVICEREMOVED)
{
printf("device removed\n");
}
}
return ret;
}
static void _gameLoop()
{
#ifdef SDL_PATHS
char *path = SDL_GetBasePath();
if (path)
{
fprintf(stderr, "Base path: %s\n", path);
path[strlen(path) - 1] = '\0'; // remove trailing separator
strcpy(dataDirectory, path);
}
else
{
fprintf(stderr, "Failed to find the path to the application\n");
exit(EXIT_STATUS_FAILURE_PLATFORM_ERROR);
}
free(path);
path = SDL_GetPrefPath("Brogue", "Brogue CE");
if (!path || chdir(path) != 0)
{
fprintf(stderr, "Failed to find or change to the save directory\n");
exit(EXIT_STATUS_FAILURE_PLATFORM_ERROR);
}
fprintf(stderr, "Save path: %s\n", path);
free(path);
#endif
SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0)
sdlfatal(__FILE__, __LINE__);
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
imgfatal(__FILE__, __LINE__);
initTiles();
lastEvent.eventType = EVENT_ERROR;
resizeWindow(windowWidth, windowHeight);
int nJoysticks = SDL_NumJoysticks();
int nController = nJoysticks;
printf("Starting: num of joys: %d, num of cont: %d ...\n", nJoysticks, nController);
int nGameControllers = 0;
for (int i = 0; i < nJoysticks - 1; i++)
{
printf("Found Controller: [%d] %s\n", i, SDL_GameControllerNameForIndex(i));
if (SDL_IsGameController(i))
{
controller = SDL_GameControllerOpen(i);
break;
}
}
int statusCode = rogueMain();
SDL_GameControllerClose(controller);
SDL_Quit();
exit(statusCode);
}
static long lastDelayTime = 0;
// Like SDL_Delay, but reduces the delay if time has passed since the last delay
static void _delayUpTo(short ms)
{
long curTime = SDL_GetTicks();
long timeDiff = curTime - lastDelayTime;
ms -= timeDiff;
if (ms > 0)
{
SDL_Delay(ms);
} // else delaying further would go past the time we want to delay until
lastDelayTime = SDL_GetTicks();
}
static boolean _pauseForMilliseconds(short ms, PauseBehavior behavior)
{
updateScreen();
_delayUpTo(ms);
if (lastEvent.eventType != EVENT_ERROR && (lastEvent.eventType != MOUSE_ENTERED_CELL || behavior.interuptForMouseMove))
{
return true; // SDL already gave us an interrupting event to process
}
return pollBrogueEvent(&lastEvent, false) // ask SDL for a new event if one is available
&& lastEvent.eventType != EVENT_ERROR // and check if it is interrupting
&& (lastEvent.eventType != MOUSE_ENTERED_CELL || behavior.interuptForMouseMove);
}
static void _nextKeyOrMouseEvent(rogueEvent *returnEvent, boolean textInput, boolean colorsDance)
{
updateScreen();
if (lastEvent.eventType != EVENT_ERROR)
{
*returnEvent = lastEvent;
lastEvent.eventType = EVENT_ERROR;
return;
}
while (true)
{
if (colorsDance)
{
shuffleTerrainColors(3, true);
commitDraws();
}
updateScreen();
if (pollBrogueEvent(returnEvent, textInput))
break;
_delayUpTo(PAUSE_BETWEEN_EVENT_POLLING);
}
}
/*
Returns the index of the sprite representing the given glyph. Sprites <256 are
from the text font sheet, 256+ are from the tiles sheet.
*/
static int fontIndex(enum displayGlyph glyph)
{
// These are the only non-ASCII glyphs which always come from the font sheet
if (glyph == G_UP_ARROW)
return 0x90;
if (glyph == G_DOWN_ARROW)
return 0x91;
if (glyph < 128)
{
// ASCII characters map directly
return glyph;
}
else if (showGraphics == TILES_GRAPHICS || (showGraphics == HYBRID_GRAPHICS && isEnvironmentGlyph(glyph)))
{
// Tile glyphs have sprite indices starting at 256
// -2 to disregard the up and down arrow glyphs
return glyph + 128 - 2;
}
else
{
unsigned int code = glyphToUnicode(glyph);
switch (code)
{
case U_MIDDLE_DOT:
return 0x80;
case U_FOUR_DOTS:
return 0x81;
case U_DIAMOND:
return 0x82;
case U_FLIPPED_V:
return 0x83;
case U_ARIES:
return 0x84;
case U_ESZETT:
return 0xdf;
case U_ANKH:
return 0x85;
case U_MUSIC_NOTE:
return 0x86;
case U_CIRCLE:
return 0x87;
case U_LIGHTNING_BOLT:
return 0x99;
case U_FILLED_CIRCLE:
return 0x89;
case U_NEUTER:
return 0x8a;
case U_U_ACUTE:
return 0xda;
case U_CURRENCY:
return 0xa4;
case U_UP_ARROW:
return 0x90;
case U_DOWN_ARROW:
return 0x91;
case U_LEFT_ARROW:
return 0x92;
case U_RIGHT_ARROW:
return 0x93;
case U_OMEGA:
return 0x96;
case U_CIRCLE_BARS:
return 0x8c;
case U_FILLED_CIRCLE_BARS:
return 0x8d;
case U_LEFT_TRIANGLE:
return 0x8e;
default:
brogueAssert(code < 256);
return code;
}
}
}
static void _plotChar(
enum displayGlyph inputChar,
short x, short y,
short foreRed, short foreGreen, short foreBlue,
short backRed, short backGreen, short backBlue)
{
updateTile(y, x, fontIndex(inputChar),
foreRed, foreGreen, foreBlue,
backRed, backGreen, backBlue);
}
static void _remap(const char *from, const char *to)
{
if (nremaps < MAX_REMAPS)
{
remapping[nremaps].from = from[0];
remapping[nremaps].to = to[0];
nremaps++;
}
}
/*
* Take screenshot in current working directory (ScreenshotN.png)
*/
static boolean _takeScreenshot()
{
SDL_Surface *screenshot = captureScreen();
if (!screenshot)
return false;
// choose filename
char screenshotFilepath[BROGUE_FILENAME_MAX];
getAvailableFilePath(screenshotFilepath, "Screenshot", SCREENSHOT_SUFFIX);
strcat(screenshotFilepath, SCREENSHOT_SUFFIX);
// save to PNG
IMG_SavePNG(screenshot, screenshotFilepath);
SDL_FreeSurface(screenshot);
return true;
}
static enum graphicsModes _setGraphicsMode(enum graphicsModes mode)
{
showGraphics = mode;
refreshScreen();
updateScreen();
return mode;
}
struct brogueConsole sdlConsole = {
_gameLoop,
_pauseForMilliseconds,
_nextKeyOrMouseEvent,
_plotChar,
_remap,
_modifierHeld,
NULL,
_takeScreenshot,
_setGraphicsMode};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment