/* * * Singe 2 * Copyright (C) 2019 Scott Duensing * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include "thirdparty/uthash.h" #include "thirdparty/manymouse/manymouse.h" #include "util.h" #include "videoPlayer.h" #include "singe.h" #define AUDIO_MAX_VOLUME 63 #define MAX_TITLE_LENGTH 1024 #define MAX_MICE 128 #define SCROLLWHEEL_DISPLAY_TICKS 100 #define NOMOUSE -1 #define KEYBD_ARRAY_SIZE 15 #define MOUSE_ARRAY_SIZE 6 typedef struct MouseS { int x; int y; int relx; int rely; char name[64]; bool connected; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" Uint32 buttons; #pragma GCC diagnostic pop Uint32 scrolluptick; Uint32 scrolldowntick; Uint32 scrolllefttick; Uint32 scrollrighttick; } MouseT; typedef struct SpriteS { int id; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" SDL_Surface *surface; #pragma GCC diagnostic pop UT_hash_handle hh; } SpriteT; typedef struct SoundS { int id; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" Mix_Chunk *chunk; #pragma GCC diagnostic pop UT_hash_handle hh; } SoundT; typedef struct FontS { int id; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" TTF_Font *font; #pragma GCC diagnostic pop UT_hash_handle hh; } FontT; enum { SWITCH_UP, SWITCH_LEFT, SWITCH_DOWN, SWITCH_RIGHT, SWITCH_START1, SWITCH_START2, SWITCH_BUTTON1, SWITCH_BUTTON2, SWITCH_BUTTON3, SWITCH_COIN1, SWITCH_COIN2, SWITCH_SKILL1, SWITCH_SKILL2, SWITCH_SKILL3, SWITCH_SERVICE, SWITCH_TEST, SWITCH_RESET, SWITCH_SCREENSHOT, SWITCH_QUIT, SWITCH_PAUSE, SWITCH_CONSOLE, SWITCH_TILT, SWITCH_MOUSE_SCROLL_UP, SWITCH_MOUSE_SCROLL_DOWN, SWITCH_MOUSE_DISCONNECT, SWITCH_COUNT }; enum { KEYBD_NORMAL = 0, KEYBD_FULL }; enum { MOUSE_SINGLE = 100, MOUSE_MANY = 200 }; enum { LDP_ERROR = 0, LDP_SEARCHING, LDP_STOPPED, LDP_PLAYING, LDP_PAUSE, }; // Command line options char *_confVideoFile = NULL; char *_confScriptFile = NULL; char *_confDataDir = NULL; bool _confStretchVideo = false; bool _confNoMouse = false; bool _confNoStats = false; bool _confNoSound = false; bool _confFullScreen = false; bool _confFullScreenWindow = false; int _confVolumeVldp = 100; int _confVolumeNonVldp = 100; int _confScaleFactor = 100; int _confXResolution = -1; int _confYResolution = -1; // Other globals static MouseT _mice[MAX_MICE]; static lua_State *_luaContext = NULL; static SDL_Color _colorForeground = { 255, 255, 255, 255 }; static SDL_Color _colorBackground = { 0, 0, 0, 0 }; static SDL_Surface *_overlay = NULL; static SDL_Window *_window = NULL; static SDL_Renderer *_renderer = NULL; static SDL_Texture *_videoTexture = NULL; static int _nextSpriteId = 0; static int _nextSoundId = 0; static int _nextFontId = 0; static int _effectsVolume = AUDIO_MAX_VOLUME; static int _keyboardMode = KEYBD_NORMAL; static int _videoHandle = -1; static int _fontQuality = 1; static int _mouseMode = MOUSE_SINGLE; static int _mouseCount = 0; static double _overlayScaleX = 1; // Difference between overlay and video static double _overlayScaleY = 1; // Difference between overlay and video static bool _pauseState = false; // by RDG2010 static bool _pauseEnabled = true; // by RDG2010 static bool _refreshDisplay = false; static bool _running = true; static bool _discStopped = true; static bool _mouseEnabled = true; static SpriteT *_spriteList = NULL; static SoundT *_soundList = NULL; static FontT *_fontList = NULL; static FontT *_fontCurrent = NULL; static void *_pixelBuffer = NULL; static int _keyDefs[SWITCH_COUNT][2] = { { SDLK_UP, SDLK_KP_8 }, // Up { SDLK_LEFT, SDLK_KP_4 }, // Left { SDLK_DOWN, SDLK_KP_2 }, // Down { SDLK_RIGHT, SDLK_KP_6 }, // Right { SDLK_1, 0 }, // 1 player start { SDLK_2, 0 }, // 2 player start { SDLK_SPACE, SDLK_LCTRL }, // Action button 1 { SDLK_LALT, 0 }, // Action button 2 { SDLK_LSHIFT, 0 }, // Action button 3 { SDLK_5, SDLK_c }, // Coin chute left { SDLK_6, 0 }, // Coin chute right { SDLK_KP_DIVIDE, 0 }, // Skill easy { SDLK_KP_MULTIPLY, 0 }, // Skill medium { SDLK_KP_MINUS, 0 }, // Skill hard { SDLK_9, 0 }, // Service coin { SDLK_F2, 0 }, // Test mode { SDLK_F3, 0 }, // Reset cpu { SDLK_F12, SDLK_F11 }, // Take screenshot { SDLK_ESCAPE, SDLK_q }, // Quit { SDLK_p, 0 }, // Pause game { SDLK_BACKQUOTE, 0 }, // Toggle console { SDLK_t, 0 } // Tilt/Slam switch }; static int _fullKeybdDefs[] = { SDLK_BACKSPACE, SDLK_TAB, SDLK_RETURN, SDLK_PAUSE, SDLK_SPACE, SDLK_QUOTE, SDLK_COMMA, SDLK_SEMICOLON, SDLK_EQUALS, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_BACKSLASH, SDLK_SLASH, SDLK_DELETE, SDLK_PERIOD }; int apiColorBackground(lua_State *L); int apiColorForeground(lua_State *L); int apiDaphneGetHeight(lua_State *L); int apiDaphneGetWidth(lua_State *L); int apiDaphneScreenshot(lua_State *L); int apiDebugPrint(lua_State *L); int apiDiscAudio(lua_State *L); int apiDiscChangeSpeed(lua_State *L); int apiDiscGetFrame(lua_State *L); int apiDiscPause(lua_State *L); int apiDiscPauseAtFrame(lua_State *L); int apiDiscPlay(lua_State *L); int apiDiscSearch(lua_State *L); int apiDiscSearchBlanking(lua_State *L); int apiDiscSetFps(lua_State *L); int apiDiscSkipBackward(lua_State *L); int apiDiscSkipBlanking(lua_State *L); int apiDiscSkipForward(lua_State *L); int apiDiscSkipToFrame(lua_State *L); int apiDiscStepBackward(lua_State *L); int apiDiscStepForward(lua_State *L); int apiDiscStop(lua_State *L); int apiFontLoad(lua_State *L); int apiFontPrint(lua_State *L); int apiFontQuality(lua_State *L); int apiFontSelect(lua_State *L); int apiFontToSprite(lua_State *L); int apiOverlayClear(lua_State *L); int apiOverlayGetHeight(lua_State *L); int apiOverlayGetWidth(lua_State *L); int apiOverlayPrint(lua_State *L); int apiSoundLoad(lua_State *L); int apiSoundPlay(lua_State *L); int apiSoundPause(lua_State *L); int apiSoundResume(lua_State *L); int apiSoundIsPlaying(lua_State *L); int apiSoundStop(lua_State *L); int apiSoundSetVolume(lua_State *L); int apiSoundGetVolume(lua_State *L); int apiSoundFullStop(lua_State *L); int apiSpriteDraw(lua_State *L); int apiSpriteGetHeight(lua_State *L); int apiSpriteGetWidth(lua_State *L); int apiSpriteLoad(lua_State *L); int apiVldpGetHeight(lua_State *L); int apiVldpGetPixel(lua_State *L); int apiVldpGetWidth(lua_State *L); int apiVldpVerbose(lua_State *L); int apiKeyboardGetMode(lua_State *L); int apiKeyboardSetMode(lua_State *L); int apiMouseEnable(lua_State *L); int apiMouseDisable(lua_State *L); int apiMouseSetMode(lua_State *L); int apiMouseHowMany(lua_State *L); int apiDiscGetState(lua_State *L); int apiSingeGetPauseFlag(lua_State *L); int apiSingeSetPauseFlag(lua_State *L); int apiSingeEnablePauseKey(lua_State *L); int apiSingeDisablePauseKey(lua_State *L); int apiSingeQuit(lua_State *L); int apiSingeVersion(lua_State *L); int apiSingeSetGameName(lua_State *L); int apiSingeGetScriptPath(lua_State *L); void callLua(const char *func, const char *sig, ...); void channelFinished(int channel); void luaDie(lua_State *L, char *method, char *fmt, ...); int luaError(lua_State *L); void luaTrace(lua_State *L, char *method, char *fmt, ...); void processKey(bool down, int keysym); int apiColorBackground(lua_State *L) { int n = lua_gettop(L); double d = 0; bool result = false; if ((n == 3) || (n == 4)) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); _colorBackground.r = (byte)d; d = lua_tonumber(L, 2); _colorBackground.g = (byte)d; d = lua_tonumber(L, 3); _colorBackground.b = (byte)d; if (n == 3) { _colorBackground.a = (byte)255; } else { if (lua_isnumber(L, 4)) { d = lua_tonumber(L, 4); _colorBackground.a = (byte)d; } else { _colorBackground.a = (byte)255; } } result = true; } } } } if (result) { luaTrace(L, "colorBackground", "%d %d %d %d", _colorBackground.r, _colorBackground.g, _colorBackground.b, _colorBackground.a); } else { luaTrace(L, "colorBackground", "Failed!"); } return 0; } int apiColorForeground(lua_State *L) { int n = lua_gettop(L); bool result = false; double d = 0; if ((n == 3) || (n == 4)) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); _colorForeground.r = (byte)d; d = lua_tonumber(L, 2); _colorForeground.g = (byte)d; d = lua_tonumber(L, 3); _colorForeground.b = (byte)d; if (n == 3) { _colorForeground.a = (byte)255; } else { if (lua_isnumber(L, 4)) { d = lua_tonumber(L, 4); _colorForeground.a = (byte)d; } else { _colorForeground.a = (byte)255; } } result = true; } } } } if (result) { luaTrace(L, "colorForeground", "%d %d %d %d", _colorForeground.r, _colorForeground.g, _colorForeground.b, _colorForeground.a); } else { luaTrace(L, "colorForeground", "Failed!"); } return 0; } int apiDaphneGetHeight(lua_State *L) { int y; SDL_GetWindowSize(_window, NULL, &y); luaTrace(L, "daphneGetHeight", "%d", y); lua_pushinteger(L, y); return 1; } int apiDaphneGetWidth(lua_State *L) { int x; SDL_GetWindowSize(_window, &x, NULL); luaTrace(L, "daphneGetWidth", "%d", x); lua_pushinteger(L, x); return 1; } int apiDaphneScreenshot(lua_State *L) { int x = 0; int y = 0; Uint32 format = 0; char filename[128]; void *pixels = NULL; SDL_Surface *surface = NULL; SDL_Texture *texture = NULL; (void)L; while (x <= 999) { snprintf(filename, 128, "singe%03d", x); if (!utilFileExists(filename)) break; x++; } if (x > 999) luaDie(L, "daphneScreenshot", "Seriously? You have 1000 screenshots in this folder? Remove some."); luaTrace(L, "daphneScreenshot", "%s", filename); texture = SDL_GetRenderTarget(_renderer); if (SDL_QueryTexture(texture, &format, NULL, &x, &y) < 0) luaDie(L, "daphneScreenshot", "%s", SDL_GetError()); pixels = malloc((size_t)x * (size_t)y * SDL_BYTESPERPIXEL(format)); if (!pixels) luaDie(L, "daphneScreenshot", "Unable to allocate screenshot."); if (SDL_RenderReadPixels(_renderer, NULL, format, pixels, (Uint16)x * SDL_BYTESPERPIXEL(format)) < 0) luaDie(L, "daphneScreenshot", "%s", SDL_GetError()); surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, x, y, SDL_BITSPERPIXEL(format), (Uint16)x * SDL_BYTESPERPIXEL(format), format); if (!surface) luaDie(L, "daphneScreenshot", "%s", SDL_GetError()); if (IMG_SavePNG(surface, filename) < 0) luaDie(L, "daphneScreenshot", "%s", IMG_GetError()); SDL_FreeSurface(surface); free(pixels); return 0; } int apiDebugPrint(lua_State *L) { int n = lua_gettop(L); if (n == 1) { if (lua_isstring(L, 1)) { luaTrace(L, "DebugPrint", "%s", lua_tostring(L, 1)); utilSay("%s", lua_tostring(L, 1)); } } return 0; } int apiDiscAudio(lua_State *L) { int n = lua_gettop(L); int channel = 0; int left = 0; int right = 0; bool onOff = false; bool result = false; double d = 0; if (n == 2) { if (lua_isnumber(L, 1)) { if (lua_isboolean(L, 2)) { d = lua_tonumber(L, 1); channel = (int)d; d = lua_tonumber(L, 2); onOff = (bool)d; if ((channel == 1) && onOff) left = _confVolumeVldp; if ((channel == 2) && onOff) right = _confVolumeVldp; videoSetVolume(_videoHandle, left, right); result = true; } } } if (result) { luaTrace(L, "discAudio", "%d %d", left, right); } else { luaTrace(L, "discAudio", "Failed!"); } return 0; } int apiDiscChangeSpeed(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discChangeSpeed", "Unimplemented"); return 0; } int apiDiscGetFrame(lua_State *L) { int frame = 0; if (!_discStopped) { frame = videoGetFrame(_videoHandle); } luaTrace(L, "discGetFrame", "%d", frame); lua_pushinteger(L, frame); return 1; } int apiDiscPause(lua_State *L) { (void)L; if (!_discStopped) { videoPause(_videoHandle); luaTrace(L, "discPause", ""); } else { luaTrace(L, "discPause", "Failed! Disc is stopped."); } return 0; } int apiDiscPauseAtFrame(lua_State *L) { int n = lua_gettop(L); int frame = 0; bool result = false; double d = 0; if (!_discStopped) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int)d; videoSeek(_videoHandle, frame); videoPause(_videoHandle); result = true; } } } else { luaTrace(L, "discPauseAtFrame", "Failed! Disc is stopped."); return 0; } if (result) { luaTrace(L, "discPauseAtFrame", "%d", frame); } else { luaTrace(L, "discPauseAtFrame", "Failed!"); } return 0; } int apiDiscPlay(lua_State *L) { (void)L; videoPlay(_videoHandle); _discStopped = false; luaTrace(L, "discPlay", ""); return 0; } int apiDiscSearch(lua_State *L) { int n = lua_gettop(L); int frame = 0; bool result = false; double d = 0; if (!_discStopped) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int)d; videoSeek(_videoHandle, frame); result = true; } } } else { luaTrace(L, "discSearch", "Failed! Disc is stopped."); return 0; } if (result) { luaTrace(L, "discSearch", "%d", frame); } else { luaTrace(L, "discSearch", "Failed!"); } return 0; } int apiDiscSearchBlanking(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discSearchBlanking", "Unimplemented"); return 0; } int apiDiscSetFps(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discSetFPS", "Unimplemented"); return 0; } int apiDiscSkipBackward(lua_State *L) { int n = lua_gettop(L); int frame = 0; bool result = false; double d = 0; if (!_discStopped) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = videoGetFrame(_videoHandle) - (int)d; videoSeek(_videoHandle, frame); result = true; } } } else { luaTrace(L, "discSkipBackward", "Failed! Disc is stopped."); return 0; } if (result) { luaTrace(L, "discSkipBackward", "%d", frame); } else { luaTrace(L, "discSkipBackward", "Failed!"); } return 0; } int apiDiscSkipBlanking(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discSkipBlanking", "Unimplemented"); return 0; } int apiDiscSkipForward(lua_State *L) { int n = lua_gettop(L); int frame = 0; bool result = false; double d = 0; if (!_discStopped) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = videoGetFrame(_videoHandle) + (int)d; videoSeek(_videoHandle, frame); result = true; } } } else { luaTrace(L, "discSkipForward", "Failed! Disc is stopped."); return 0; } if (result) { luaTrace(L, "discSkipForward", "%d", frame); } else { luaTrace(L, "discSkipForward", "Failed!"); } return 0; } int apiDiscSkipToFrame(lua_State *L) { // Not really sure what this is, so just seek to a new frame. luaTrace(L, "discSkipToFrame", "Forwarding to discSearch."); return apiDiscSearch(L); } int apiDiscStepBackward(lua_State *L) { int frame = 0; (void)L; if (!_discStopped) { frame = videoGetFrame(_videoHandle) - 1; videoSeek(_videoHandle, frame); luaTrace(L, "discStepBackward", "%d", frame); } else { luaTrace(L, "discStepBackward", "Failed! Disc is stopped."); } return 0; } int apiDiscStepForward(lua_State *L) { int frame = 0; (void)L; if (!_discStopped) { frame = videoGetFrame(_videoHandle) + 1; videoSeek(_videoHandle, frame); luaTrace(L, "discStepForward", "%d", frame); } else { luaTrace(L, "discStepForward", "Failed! Disc is stopped."); } return 0; } int apiDiscStop(lua_State *L) { (void)L; if (!_discStopped) { videoPause(_videoHandle); _discStopped = true; _refreshDisplay = true; luaTrace(L, "discStop", ""); } else { luaTrace(L, "discStop", "Failed! Disc is stopped."); } return 0; } int apiFontLoad(lua_State *L) { int n = lua_gettop(L); int result = -1; int points = 0; const char *name = NULL; double d = 0; FontT *font = NULL; if (n == 2) { if (lua_isstring(L, 1)) { if (lua_isnumber(L, 2)) { name = lua_tostring(L, 1); d = lua_tonumber(L, 2); points = (int)d; font = (FontT *)calloc(1, sizeof(FontT)); if (!font) luaDie(L, "fontLoad", "Unable to allocate new font."); // Load this font. font->font = TTF_OpenFont(name, points); if (!font->font) luaDie(L, "fontLoad", "%s", TTF_GetError()); // Make it the current font and mark it as loaded. font->id = _nextFontId; result = _nextFontId++; _fontCurrent = font; HASH_ADD_INT(_fontList, id, font); } } } if (result >= 0) { luaTrace(L, "fontLoad", "%s %d", name, result); } else { luaTrace(L, "fontLoad", "Failed!"); } lua_pushnumber(L, result); return 1; } int apiFontPrint(lua_State *L) { int n = lua_gettop(L); const char *message = NULL; double d = 0; SDL_Surface *textSurface = NULL; SDL_Rect dest; if (n == 3) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isstring(L, 3)) { if (_fontCurrent) { d = lua_tonumber(L, 1); dest.x = (int)d; d = lua_tonumber(L, 2); dest.y = (int)d; textSurface = NULL; message = lua_tostring(L, 3); switch (_fontQuality) { case 1: textSurface = TTF_RenderText_Solid(_fontCurrent->font, message, _colorForeground); break; case 2: textSurface = TTF_RenderText_Shaded(_fontCurrent->font, message, _colorForeground, _colorBackground); break; case 3: textSurface = TTF_RenderText_Blended(_fontCurrent->font, message, _colorForeground); break; } if (!textSurface) { luaDie(L, "fontPrint", "Font surface is null!"); } else { SDL_SetSurfaceBlendMode(textSurface, SDL_BLENDMODE_BLEND); // Transparent index is 0 SDL_SetColorKey(textSurface, true, 0); dest.w = textSurface->w; dest.h = textSurface->h; SDL_BlitSurface(textSurface, NULL, _overlay, &dest); SDL_FreeSurface(textSurface); } } } } } } if (textSurface) { luaTrace(L, "fontPrint", "%s", message); } else { luaTrace(L, "fontPrint", "Failed!"); } return 0; } int apiFontQuality(lua_State *L) { int n = lua_gettop(L); bool result = false; double d = 0; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); _fontQuality = (int)d; result = true; } } if (result) { luaTrace(L, "fontQuality", "%d", _fontQuality); } else { luaTrace(L, "fontQuality", "Failed!"); } return 0; } int apiFontSelect(lua_State *L) { int n = lua_gettop(L); int id = -1; double d = 0; bool result = false; FontT *font = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int)d; HASH_FIND_INT(_fontList, &id, font); if (!font) luaDie(L, "fontSelect", "No font at index %d in apiSpriteGetWidth.", id); _fontCurrent = font; result = true; } } if (result) { luaTrace(L, "fontSelect", "%d", _fontCurrent->id); } else { luaTrace(L, "fontSelect", "Failed!"); } return 0; } int apiFontToSprite(lua_State *L) { int n = lua_gettop(L); int result = -1; const char *message = NULL; SpriteT *sprite = NULL; if (n == 1) { if (lua_isstring(L, 1)) { if (_fontCurrent) { // Create spirte sprite = (SpriteT *)calloc(1, sizeof(SpriteT)); if (!sprite) luaDie(L, "fontToSprite", "Unable to allocate new text sprite."); message = lua_tostring(L, 1); switch (_fontQuality) { case 1: sprite->surface = TTF_RenderText_Solid(_fontCurrent->font, message, _colorForeground); break; case 2: sprite->surface = TTF_RenderText_Shaded(_fontCurrent->font, message, _colorForeground, _colorBackground); break; case 3: sprite->surface = TTF_RenderText_Blended(_fontCurrent->font, message, _colorForeground); break; } if (!sprite->surface) { luaDie(L, "fontToSprite", "Font surface is null!"); } else { SDL_SetColorKey(sprite->surface, true, 0); //SDL_SetSurfaceAlphaMod(sprite->surface, 255); sprite->id = _nextSpriteId; result = _nextSpriteId++; HASH_ADD_INT(_spriteList, id, sprite); } } } } if (sprite->surface) { luaTrace(L, "fontToSprite", "%d %s", result, message); } else { luaTrace(L, "fontToSprite", "Failed!"); } lua_pushinteger(L, result); return 1; } int apiOverlayClear(lua_State *L) { (void)L; SDL_FillRect(_overlay, NULL, SDL_MapRGBA(_overlay->format, _colorBackground.r, _colorBackground.g, _colorBackground.b, _colorBackground.a)); luaTrace(L, "overlayClear", ""); return 0; } int apiOverlayGetHeight(lua_State *L) { luaTrace(L, "overlayGetWidth", "%d", _overlay->h); lua_pushinteger(L, _overlay->h); return 1; } int apiOverlayGetWidth(lua_State *L) { luaTrace(L, "overlayGetWidth", "%d", _overlay->w); lua_pushinteger(L, _overlay->w); return 1; } int apiOverlayPrint(lua_State *L) { int n = lua_gettop(L); if (n == 3) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { //***TODO*** g_pSingeIn->draw_string((char *)lua_tostring(L, 3), lua_tonumber(L, 1), lua_tonumber(L, 2), g_se_surface); } } } } return 0; } int apiSoundLoad(lua_State *L) { int n = lua_gettop(L); int result = -1; const char *name = NULL; SoundT *sound = NULL; if (n == 1) { if (lua_isstring(L, 1)) { sound = (SoundT *)calloc(1, sizeof(SoundT)); if (!sound) luaDie(L, "soundLoad", "Unable to allocate new sound."); name = lua_tostring(L, 1); sound->chunk = Mix_LoadWAV(name); if (!sound->chunk) luaDie(L, "soundLoad", "%s", Mix_GetError()); sound->id = _nextSoundId; result = _nextSoundId++; HASH_ADD_INT(_soundList, id, sound); } } if (sound) { luaTrace(L, "soundLoad", "%d %s", result, name); } else { luaTrace(L, "soundLoad", "Failed!"); } lua_pushnumber(L, result); return 1; } int apiSoundPlay(lua_State *L) { int n = lua_gettop(L); int result = -1; int id = -1; double d = 0; SoundT *sound = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int)d; // Get our sound structure HASH_FIND_INT(_soundList, &id, sound); if (!sound) luaDie(L, "soundPlay", "No sound at index %d in apiSoundPlay.", id); // Play it result = Mix_PlayChannel(-1, sound->chunk, 0); Mix_Volume(result, _effectsVolume * 2); } } if (sound) { luaTrace(L, "soundPlay", "%d", result); } else { luaTrace(L, "soundPlay", "Failed!"); } lua_pushnumber(L, result); return 1; } int apiSoundPause(lua_State *L) { // Instructs Daphne to pause a given sample from playing. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // soundPause(thisHandle) // // Function returns true if sample was paused, false otherwise. // // --rdg int n = lua_gettop(L); int channel = -1; double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int)d; Mix_Pause(channel); result = true; } } if (result) { luaTrace(L, "soundPause", "%d", channel); } else { luaTrace(L, "soundPause", "Failed!"); } lua_pushboolean(L, result); return 1; } int apiSoundResume(lua_State *L) { // Instructs Daphne to unpause a sound that was previously paused. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // soundPause(thisHandle) // soundResume(thisHandle) // // Function returns true if sample was unpaused, false otherwise. // // --rdg int n = lua_gettop(L); int channel = -1; double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int)d; Mix_Resume(channel); result = true; } } if (result) { luaTrace(L, "soundResume", "%d", channel); } else { luaTrace(L, "soundResume", "Failed!"); } lua_pushboolean(L, result); return 1; } int apiSoundIsPlaying(lua_State *L) { // Checks to see if a certain sound has finished playing. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // if (soundIsPlaying(thisSound)) then do something ... end // // Function returns true if sample is still playing, false otherwise. // // --rdg int n = lua_gettop(L); int channel = -1; double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int)d; result = (bool)Mix_Playing(channel); } } if (result) { luaTrace(L, "soundIsPlaying", "%d %d", channel, result); } else { luaTrace(L, "soundIsPlaying", "Failed!"); } lua_pushboolean(L, result); return 1; } int apiSoundStop(lua_State *L) { // Instructs Daphne to end a sound early. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // soundStop(thisHandle) // // Function returns true if sample was stopped, false otherwise. // NOTE: thisHandle will be invalidated as a result of this function. // Lua doesn't do variables by reference, so it is // up to the user to keep track of sound handles on the lua script. // // --rdg int n = lua_gettop(L); int channel = -1; double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int)d; Mix_HaltChannel(channel); result = true; } } if (result) { luaTrace(L, "soundStop", "%d", channel); } else { luaTrace(L, "soundStop", "Failed!"); } lua_pushboolean(L, result); return 1; } int apiSoundSetVolume(lua_State *L) { // Allows manipulation of sample volume. // Valid values range from 0 to 63 // e.g. lua code, // // soundSetVolume(32) // // Function returns nothing. // // --rdg int n = lua_gettop(L); int thisValue = 0; double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); thisValue = (int)d; if (thisValue >= 0 && thisValue <= AUDIO_MAX_VOLUME) { _effectsVolume = thisValue; Mix_Volume(-1, _effectsVolume * 2); } else { luaDie(L, "soundSetVolume", "Invalid sound volume value."); } } } if (result) { luaTrace(L, "soundSetVolume", "%d", _effectsVolume); } else { luaTrace(L, "soundSetVolume", "Failed!"); } lua_pushboolean(L, result); return 0; } int apiSoundGetVolume(lua_State *L) { // Returns the current sample volume value. // e.g. lua code, // // local iVolume = soundGetVolume() // // Function returns an integer value ranging from 0 to 63. // // --rdg luaTrace(L, "soundGetVolume", "%d", _effectsVolume); lua_pushinteger(L, _effectsVolume); return 1; } int apiSoundFullStop(lua_State *L) { // Clears the audio queue of any samples actively playing. // No parameters needed. Function returns nothing. // e.g. lua code, // // soundFullStop() (void)L; luaTrace(L, "soundFullStop", ""); Mix_HaltChannel(-1); return 0; } int apiSpriteDraw(lua_State *L) { int n = lua_gettop(L); int id = -1; double d = 0; SpriteT *sprite = NULL; SDL_Rect dest; if (n == 3) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); dest.x = (int)d; d = lua_tonumber(L, 2); dest.y = (int)d; d = lua_tonumber(L, 3); id = (int)d; HASH_FIND_INT(_spriteList, &id, sprite); if (!sprite) luaDie(L, "spriteDraw", "No sprite at index %d in apiSpriteGetWidth.", id); dest.w = sprite->surface->w; dest.h = sprite->surface->h; SDL_BlitSurface(sprite->surface, NULL, _overlay, &dest); } } } } if (id >= 0) { luaTrace(L, "spriteDraw", "%d %d %d %d %d", id, dest.x, dest.y, dest.w, dest.h); } else { luaTrace(L, "spriteDraw", "Failed!"); } return 0; } int apiSpriteGetHeight(lua_State *L) { int n = lua_gettop(L); int result = -1; int id = -1; double d; SpriteT *sprite = NULL; if (n == 1) { if (lua_isstring(L, 1)) { d = lua_tonumber(L, 1); id = (int)d; // Get our sprite structure HASH_FIND_INT(_spriteList, &id, sprite); if (!sprite) luaDie(L, "spriteGetHeight", "No sprite at index %d in apiSpriteGetWidth.", id); result = sprite->surface->h; } } if (result >= 0) { luaTrace(L, "spriteGetHeight", "%d", result); } else { luaTrace(L, "spriteGetHeight", "Failed!"); } lua_pushinteger(L, result); return 1; } int apiSpriteGetWidth(lua_State *L) { int n = lua_gettop(L); int result = -1; int id = -1; double d; SpriteT *sprite = NULL; lua_Debug ar; lua_getstack(L, 1, &ar); lua_getinfo(L, "nSl", &ar); if (n == 1) { if (lua_isstring(L, 1)) { d = lua_tonumber(L, 1); id = (int)d; // Get our sprite structure HASH_FIND_INT(_spriteList, &id, sprite); if (!sprite) luaDie(L, "spriteGetWidth", "No sprite at index %d in apiSpriteGetWidth.", id); result = sprite->surface->w; } } if (result >= 0) { luaTrace(L, "spriteGetWidth", "%d", result); } else { luaTrace(L, "spriteGetWidth", "Failed!"); } lua_pushinteger(L, result); return 1; } int apiSpriteLoad(lua_State *L) { int n = lua_gettop(L); int result = -1; const char *name = NULL; SpriteT *sprite = NULL; if (n == 1) { if (lua_isstring(L, 1)) { sprite = (SpriteT *)calloc(1, sizeof(SpriteT)); if (!sprite) luaDie(L, "spriteLoad", "Unable to allocate new sprite."); name = lua_tostring(L, 1); sprite->surface = IMG_Load(name); if (!sprite->surface) luaDie(L, "spriteLoad", "%s", IMG_GetError()); SDL_SetColorKey(sprite->surface, true, 0); sprite->id = _nextSpriteId; result = _nextSpriteId++; HASH_ADD_INT(_spriteList, id, sprite); } } if (sprite->surface) { luaTrace(L, "spriteLoad", "%d %s", result, name); } else { luaTrace(L, "spriteLoad", "Failed!"); } lua_pushnumber(L, result); return 1; } int apiVldpGetHeight(lua_State *L) { int height = videoGetHeight(_videoHandle); luaTrace(L, "vldpGetHeight", "%d", height); lua_pushinteger(L, height); return 1; } int apiVldpGetPixel(lua_State *L) { int n = lua_gettop(L); double d = 0; bool result = false; byte pixel[SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_BGRA32)]; SDL_Rect rect; if (n == 2) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { rect.h = 1; rect.w = 1; d = lua_tonumber(L, 1); rect.x = (int)(d / _overlayScaleX); d = lua_tonumber(L, 2); rect.y = (int)(d / _overlayScaleY); if (SDL_SetRenderTarget(_renderer, _videoTexture) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError()); if (SDL_RenderReadPixels(_renderer, &rect, SDL_PIXELFORMAT_BGRA32, pixel, SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_BGRA32) * videoGetWidth(_videoHandle)) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError()); if (SDL_SetRenderTarget(_renderer, NULL) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError()); result = true; } } } if (result) { luaTrace(L, "vldpGetPixel", "%d %d %d %d %d", rect.x, rect.y, pixel[2], pixel[1], pixel[0]); lua_pushinteger(L, (int)pixel[2]); // R lua_pushinteger(L, (int)pixel[1]); // G lua_pushinteger(L, (int)pixel[0]); // B } else { luaTrace(L, "vldpGetPixel", "Failed!"); lua_pushinteger(L, -1); lua_pushinteger(L, -1); lua_pushinteger(L, -1); } return 3; } int apiVldpGetWidth(lua_State *L) { int width = videoGetWidth(_videoHandle); luaTrace(L, "vldpGetHeight", "%d", width); lua_pushinteger(L, width); return 1; } int apiVldpVerbose(lua_State *L) { /* * Enables/Disables writing of VLDP playback activity to daphne_log.txt * Enabled by default. */ (void)L; //***REMOVED*** luaTrace(L, "vldpVerbose", "Unimplemented"); return 0; } int apiKeyboardGetMode(lua_State *L) { luaTrace(L, "keyboardGetMode", "%d", _keyboardMode); lua_pushinteger(L, _keyboardMode); return 1; } int apiKeyboardSetMode(lua_State *L) { bool result = false; /* * Singe can scan keyboard input in two ways: * * MODE_NORMAL - Singe will only check for keys defined * in daphne.ini. This is the default behavior. * * MODE_FULL - Singe will scan the keyboard for most keypresses. * */ int n = lua_gettop(L); double d = 0; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); _keyboardMode = (int)d; result = true; } } if (result) { luaTrace(L, "keyboardSetMode", "%d", _keyboardMode); } else { luaTrace(L, "keyboardSetMode", "Failed!"); } return 0; } int apiMouseEnable(lua_State *L) { // Enables mouse monitoring (void)L; luaTrace(L, "mouseEnable", "%d", _confNoMouse); _mouseEnabled = (bool)!_confNoMouse; return 0; } int apiMouseDisable(lua_State *L) { // Disables mouse monitoring (void)L; luaTrace(L, "mouseDisable", ""); _mouseEnabled = false; return 0; } int apiMouseSetMode(lua_State *L) { // Sets the scanning mode for mouse input. // Can be one of two values: // // SINGLE_MOUSE = 100 // MANY_MOUSE = 200 // // Be sure to add these constant declarations to your framework.singe // By default Singe starts in single mouse mode. // Use this command if you need to scan multiple mice. // e.g. lua code, // // mouseSetMode(MANY_MOUSE) // // Function returns TRUE is mode set was successful, FALSE otherwise. // // --rdg int n = lua_gettop(L); double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); _mouseMode = (int)d; result = true; } } if (result) { luaTrace(L, "mouseSetMode", "%d", _mouseMode); } else { luaTrace(L, "mouseSetMode", "Failed!"); } lua_pushboolean(L, result); return 1; } int apiMouseHowMany(lua_State *L) { luaTrace(L, "mouseHowMany", "%d", _mouseCount); lua_pushinteger(L, _mouseCount); return 1; } int apiDiscGetState(lua_State *L) { int isPlaying = -1; /* * Returns the status of the vldp * Values returned are * based on the following enumeration (found in ldp.h). * * LDP_ERROR = 0 * LDP_SEARCHING = 1 * LDP_STOPPED = 2 * LDP_PLAYING = 3 * LDP_PAUSE = 4 * */ // Our player isn't as sophisticated as the one in Daphne isPlaying = videoIsPlaying(_videoHandle); luaTrace(L, "discGetState", "%s", _discStopped ? "Stopped" : (isPlaying ? "Playing" : "Paused")); lua_pushinteger(L, _discStopped ? LDP_STOPPED : (isPlaying ? LDP_PLAYING : LDP_PAUSE)); return 1; } int apiSingeGetPauseFlag(lua_State *L) { /* * This function returns g_pause_state's value to the lua script. * * Sometimes game logic pauses the game (which implies pausing video playback). * When implementing a pause state it is possible for the player * to resume playblack at moments where the game is not intended to. * Boolean g_pause state is an internal variable that keeps track * of this. It's set to true whenever sep_pre_pause is called. * It's set to false whenever sep_pre_play or sep_skip_to_frame is called. * * A lua programmer can use this to prevent resuming playback accidentally. */ luaTrace(L, "singeGetPauseFlag", "%d", _pauseState); lua_pushboolean(L, _pauseState); return 1; } int apiSingeSetPauseFlag(lua_State *L) { int n = lua_gettop(L); bool result = false; if (n == 1) { if (lua_isboolean(L, 1)) { _pauseState = (bool)lua_toboolean(L, 1); result = true; } } if (result) { luaTrace(L, "singeSetPauseFlag", "%d", _pauseState); } else { luaTrace(L, "singeSetPauseFlag", "Failed!"); } return 0; } int apiSingeEnablePauseKey(lua_State *L) { (void)L; luaTrace(L, "singeEnablePauseKey", ""); _pauseEnabled = true; return 0; } int apiSingeDisablePauseKey(lua_State *L) { (void)L; luaTrace(L, "singeDisablePauseKey", ""); _pauseEnabled = false; return 0; } int apiSingeQuit(lua_State *L) { (void)L; luaTrace(L, "singeQuit", ""); _running = false; return 0; } int apiSingeVersion(lua_State *L) { luaTrace(L, "singeVersion", "%f", SINGE_VERSION); lua_pushnumber(L, SINGE_VERSION); return 1; } int apiSingeSetGameName(lua_State *L) { // Adds the name of the singe game to the window's title bar. // Valid value is a string no longer than 25 characters. // e.g. lua code, // // singeSetGameName("My FMV game") // // Function returns nothing. // // --rdg int n = lua_gettop(L); bool result = false; char thisName[MAX_TITLE_LENGTH]; if (n == 1) { if (lua_isstring(L, 1)) { sprintf(thisName,"%.40s", lua_tostring(L, 1)); // Need a better way to do this... SDL_SetWindowTitle(_window, thisName); result = true; } } if (result) { luaTrace(L, "singeSetGameName", "%s", thisName); } else { luaTrace(L, "singeSetGameName", "Failed!"); } lua_pushboolean(L, result); return 0; } int apiSingeGetScriptPath(lua_State *L) { // Returns the path to the singe script. // e.g. lua code, // // sGameDirectory = singeGetScriptPath() // luaTrace(L, "singeSetScriptPath", "%s", _confScriptFile); lua_pushstring(L, _confScriptFile); return 1; } void callLua(const char *func, const char *sig, ...) { va_list vl; bool done = false; int narg; int nres; int popCount; double d; const int top = lua_gettop(_luaContext); va_start(vl, sig); // Get Function lua_getglobal(_luaContext, func); if (!lua_isfunction(_luaContext, -1)) { // Function does not exist. Bail. lua_settop(_luaContext, top); return; } utilTrace("%s", func); // Push Arguments narg = 0; while ((*sig) && (!done)) { switch (*sig++) { case 'd': // Double lua_pushnumber(_luaContext, va_arg(vl, double)); break; case 'i': // Int lua_pushinteger(_luaContext, va_arg(vl, int)); break; case 's': // String lua_pushstring(_luaContext, va_arg(vl, char *)); break; case '>': done = true; break; default: utilDie("Invalid argument option (%c)", *(sig - 1)); } if (!done) { narg++; luaL_checkstack(_luaContext, 1, "Too many arguments"); } } // Do the call popCount = nres = (int)strlen(sig); // Number of expected results if (lua_pcall(_luaContext, narg, nres, 0) != 0) { utilDie("Error executing function '%s': %s", func, lua_tostring(_luaContext, -1)); } // Retrieve results nres = -nres; // Stack index of first result while (*sig) { switch (*sig++) { case 'd': // Double if (!lua_isnumber(_luaContext, nres)) { utilDie("Wrong result type"); } *va_arg(vl, double *) = lua_tonumber(_luaContext, nres); break; case 'i': // Int if (!lua_isnumber(_luaContext, nres)) { utilDie("Wrong result type"); } d = lua_tonumber(_luaContext, nres); *va_arg(vl, int *) = (int)d; break; case 's': // String if (!lua_isstring(_luaContext, nres)) { utilDie("Wrong result type"); } *va_arg(vl, const char **) = lua_tostring(_luaContext, nres); break; default: utilDie("Invalid option (%c)", *(sig - 1)); } nres++; } va_end(vl); if (popCount > 0) { lua_pop(_luaContext, popCount); } } void channelFinished(int channel) { callLua("onSoundCompleted", "i", channel); } void luaDie(lua_State *L, char *method, char *fmt, ...) { va_list args; lua_Debug ar; char *string1 = NULL; char *string2 = NULL; lua_getstack(L, 1, &ar); lua_getinfo(L, "nSl", &ar); string1 = utilCreateString("%d:%s: ", ar.currentline, method); if (!string1) utilDie("Unable to allocate first trace string."); va_start(args, fmt); string2 = utilCreateStringVArgs(fmt, args); if (!string2) utilDie("Unable to allocate second trace string."); va_end(args); utilTrace("%s%s", string1, string2); utilDie("%s%s", string1, string2); // Can't free strings - we never get here. } int luaError(lua_State *L) { lua_Debug ar; int level = 0; utilSay("Singe has panicked! Very bad!"); utilSay("Error: %s", lua_tostring(L, -1)); utilSay("Stack trace:"); while (lua_getstack(L, level, &ar) != 0) { lua_getinfo(L, "nSl", &ar); utilSay(" %d: function `%s' at line %d %s", level, ar.name, ar.currentline, ar.short_src); level++; } utilSay("Trace complete."); return 0; } void luaTrace(lua_State *L, char *method, char *fmt, ...) { va_list args; lua_Debug ar; char *string1 = NULL; char *string2 = NULL; if (utilTraceFile) { lua_getstack(L, 1, &ar); lua_getinfo(L, "nSl", &ar); string1 = utilCreateString("%d:%s: ", ar.currentline, method); if (!string1) utilDie("Unable to allocate first trace string."); va_start(args, fmt); string2 = utilCreateStringVArgs(fmt, args); if (!string2) utilDie("Unable to allocate second trace string."); va_end(args); utilSay("%s%s", string1, string2); utilTrace("%s%s", string1, string2); free(string2); free(string1); } } void processKey(bool down, int keysym) { int move; if (_keyboardMode == KEYBD_NORMAL) { // Daphne keys for (move=0; movetoggle_game_pause(); } if ((move == SWITCH_QUIT) || (keysym == SDLK_ESCAPE)) { _running = false; } if (move == SWITCH_SCREENSHOT) { //***TODO*** g_ldp->request_screenshot(); } } if (move != SWITCH_PAUSE) { callLua(down ? "onInputPressed" : "onInputReleased", "ii", move, NOMOUSE); } } } } else { //***TODO*** Why is RDG filtering keys? // Full keyboard if (keysym >= SDLK_a && keysym <= SDLK_z) callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE); // check to see if key is a number on the top row of the keyboard (not keypad) else if (keysym >= SDLK_MINUS && keysym <= SDLK_9) callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE); // numeric keypad keys else if (keysym >= SDLK_KP_0 && keysym <= SDLK_KP_EQUALS) callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE); // arrow keys and insert, delete, home, end, pgup, pgdown else if (keysym >= SDLK_UP && keysym <= SDLK_PAGEDOWN) callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE); // function keys else if (keysym >= SDLK_F1 && keysym <= SDLK_F15) callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE); // Key state modifier keys (left and right ctrls, alts) else if (keysym >= SDLK_NUMLOCKCLEAR && keysym <= SDLK_LGUI) callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE); // International keys /* else if (keysym >= SDLK_WORLD_0 && keysym <= SDLK_WORLD_95) callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE); */ else { /* * SDLK_BACKSPACE, SDLK_TAB, SDLK_RETURN, SDLK_PAUSE, * SDLK_SPACE, SDLK_QUOTE, SDLK_COMMA, SDLK_SEMICOLON, * SDLK_EQUALS, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, * SDLK_BACKSLASH, SDLK_SLASH, SDLK_DELETE, SDLK_PERIOD }; */ for (move=0; move MAX_MICE) { _mouseCount = MAX_MICE; } memset(_mice, 0, sizeof(_mice)); _mouseEnabled = (bool)!_confNoMouse; for (x=0; x<_mouseCount; x++) { strncpy(_mice[x].name, ManyMouse_DeviceName((unsigned)x), sizeof(_mice[x].name)); _mice[x].name[sizeof(_mice[x].name) - 1] = 0; _mice[x].x = (int)(videoGetWidth(_videoHandle) * _overlayScaleX); _mice[x].y = (int)(videoGetHeight(_videoHandle) * _overlayScaleY); } SDL_SetWindowGrab(_window, SDL_TRUE); SDL_ShowCursor(SDL_DISABLE); // Set volume _effectsVolume = (int)((float)AUDIO_MAX_VOLUME * (float)_confVolumeNonVldp * (float)0.01); Mix_Volume(-1, _effectsVolume * 2); // Let us know when sounds end Mix_ChannelFinished(channelFinished); // Start video paused videoPlay(_videoHandle); videoPause(_videoHandle); _discStopped = false; // Start script if (luaL_dofile(_luaContext, _confScriptFile) != 0) utilDie("Error compiling script: %s", lua_tostring(_luaContext, -1)); // Game Loop while (_running) { // SDL Event Loop while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: processKey(true, event.key.keysym.sym); //***TODO*** RDG used "g" to re-grab/re-scan for mice break; case SDL_KEYUP: processKey(false, event.key.keysym.sym); break; case SDL_MOUSEMOTION: if ((_mouseEnabled) && (_mouseMode == MOUSE_SINGLE)) { x = (int)(event.motion.x * _overlayScaleX); y = (int)(event.motion.y * _overlayScaleY); xr = (int)(event.motion.xrel * _overlayScaleX); yr = (int)(event.motion.yrel * _overlayScaleY); callLua("onMouseMoved", "iiiii", x, y, xr, yr, NOMOUSE); } break; case SDL_MOUSEBUTTONDOWN: for (x=0; x= (unsigned)_mouseCount) continue; mouse = &_mice[mouseEvent.device]; switch (mouseEvent.type) { case MANYMOUSE_EVENT_RELMOTION: switch (mouseEvent.item) { case 0: mouse->relx += mouseEvent.value; break; case 1: mouse->rely += mouseEvent.value; break; } // Clamp to video size x = videoGetWidth(_videoHandle); y = videoGetHeight(_videoHandle); if (mouse->relx < 0) mouse->relx = 0; if (mouse->relx >= x) mouse->relx = x - 1; if (mouse->rely < 0) mouse->rely = 0; if (mouse->rely >= y) mouse->rely = y - 1; x = (int)(mouse->x * _overlayScaleX); y = (int)(mouse->y * _overlayScaleY); xr = (int)(mouse->relx * _overlayScaleX); yr = (int)(mouse->relx * _overlayScaleY); callLua("onMouseMoved", "iiiii", x, y, xr, yr, mouseEvent.device); break; case MANYMOUSE_EVENT_ABSMOTION: switch (mouseEvent.item) { case 0: mouse->x += mouseEvent.value; break; case 1: mouse->y += mouseEvent.value; break; } x = (int)(mouse->x * _overlayScaleX); y = (int)(mouse->y * _overlayScaleY); xr = (int)(mouse->relx * _overlayScaleX); yr = (int)(mouse->relx * _overlayScaleY); utilSay("Mouse %d: Absolute %d, %d", mouseEvent.device, x, y); callLua("onMouseMoved", "iiiii", x, y, xr, yr, mouseEvent.device); break; case MANYMOUSE_EVENT_BUTTON: if (mouseEvent.item < 32) { if (mouseEvent.value == 1) { // Button pressed callLua("onInputPressed", "ii", mouseButtonMap[mouseEvent.item], mouseEvent.device); mouse->buttons |= (1 << mouseEvent.item); } else { // Button released callLua("onInputReleased", "ii", mouseButtonMap[mouseEvent.item], mouseEvent.device); mouse->buttons &= ~(1 << mouseEvent.item); } } break; case MANYMOUSE_EVENT_SCROLL: if (mouseEvent.item == 0) { if (mouseEvent.value > 0) { // Scroll up callLua("onInputPressed", "ii", SWITCH_MOUSE_SCROLL_UP, mouseEvent.device); callLua("onInputReleased", "ii", SWITCH_MOUSE_SCROLL_UP, mouseEvent.device); } else { // Scroll down callLua("onInputPressed", "ii", SWITCH_MOUSE_SCROLL_DOWN, mouseEvent.device); callLua("onInputReleased", "ii", SWITCH_MOUSE_SCROLL_DOWN, mouseEvent.device); } } break; case MANYMOUSE_EVENT_DISCONNECT: mouse->connected = false; callLua("onInputPressed", "ii", SWITCH_MOUSE_DISCONNECT, mouseEvent.device); callLua("onInputReleased", "ii", SWITCH_MOUSE_DISCONNECT, mouseEvent.device); break; case MANYMOUSE_EVENT_MAX: // We don't use this break; } } } // Call game code callLua("onOverlayUpdate", ">i", &intReturn); if (intReturn == 1) { _refreshDisplay = true; } // Update video thisFrame = videoUpdate(_videoHandle, &_videoTexture); if ((thisFrame != lastFrame) && (thisFrame>= 0)) { lastFrame = thisFrame; _refreshDisplay = true; } // Update display if (_refreshDisplay) { //***TODO*** Handle overlay and blank disk frames SDL_RenderCopy(_renderer, _videoTexture, NULL, NULL); //makeZeroTransparent(_overlay); overlayTexture = SDL_CreateTextureFromSurface(_renderer, _overlay); if (!overlayTexture) utilDie("%s", SDL_GetError()); if (!_confStretchVideo) { SDL_RenderSetLogicalSize(renderer, videoGetWidth(_videoHandle), videoGetHeight(_videoHandle)); } SDL_RenderCopy(_renderer, overlayTexture, NULL, NULL); SDL_DestroyTexture(overlayTexture); overlayTexture = NULL; SDL_RenderPresent(_renderer); _refreshDisplay = false; } } // End game callLua("onShutdown", ""); // Stop all sounds Mix_HaltChannel(-1); Mix_ChannelFinished(NULL); // Stop Lua lua_close(_luaContext); // Free overlay SDL_FreeSurface(_overlay); // Unload fonts HASH_ITER(hh, _fontList, font, fontTemp) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" HASH_DEL(_fontList, font); #pragma GCC diagnostic pop TTF_CloseFont(font->font); free(font); } // Unload sounds HASH_ITER(hh, _soundList, sound, soundTemp) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" HASH_DEL(_soundList, sound); #pragma GCC diagnostic pop Mix_FreeChunk(sound->chunk); free(sound); } // Unload sprites HASH_ITER(hh, _spriteList, sprite, spriteTemp) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" HASH_DEL(_spriteList, sprite); #pragma GCC diagnostic pop SDL_FreeSurface(sprite->surface); free(sprite); } // Unload video videoUnload(_videoHandle); // Stop mice SDL_ShowCursor(SDL_ENABLE); ManyMouse_Quit(); }