#include #define SDL_MAIN_USE_CALLBACKS #include #include "game.h" #include "maps.h" #define MAXFPS (60) static yeti_t yeti; static u16 backbuffer[YETI_FRAMEBUFFER_WIDTH * YETI_FRAMEBUFFER_HEIGHT]; static SDL_Window *window = NULL; static SDL_Renderer *renderer = NULL; static SDL_Texture *texture = NULL; static bool focused = true; static Sint64 now = 0, then = 0; static int requested_map = -1; static int screenshot_idx = 0; static void die(const char *fmt, ...) { static char error[1024]; va_list ap; va_start(ap, fmt); SDL_vsnprintf(error, sizeof(error), fmt, ap); va_end(ap); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, error); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", error, NULL); exit(1); } static void dump_textures(void) { int i; SDL_Palette *pal; pal = SDL_CreatePalette(256); for (i = 0; i < pal->ncolors; i++) { pal->colors[i].r = palette[i][0]; pal->colors[i].g = palette[i][1]; pal->colors[i].b = palette[i][2]; pal->colors[i].a = 255; } for (i = 0; i < TEXTURE_MAX; i++) { char filename[256]; SDL_Surface *surf; surf = SDL_CreateSurfaceFrom(TEXTURE_WIDTH, TEXTURE_HEIGHT, SDL_PIXELFORMAT_INDEX8, textures[i], TEXTURE_WIDTH * sizeof(u8)); SDL_SetSurfacePalette(surf, pal); SDL_FlipSurface(surf, SDL_FLIP_VERTICAL); SDL_snprintf(filename, sizeof(filename), "texture%03d.bmp", i); SDL_SaveBMP(surf, filename); SDL_DestroySurface(surf); } SDL_DestroyPalette(pal); } SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { Uint32 format; if (!SDL_Init(SDL_INIT_VIDEO)) die(SDL_GetError()); window = SDL_CreateWindow(YETI_STR_CAPTION, YETI_FRAMEBUFFER_WIDTH, YETI_FRAMEBUFFER_HEIGHT, SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED); if (!window) die(SDL_GetError()); SDL_SetWindowRelativeMouseMode(window, focused); SDL_SetWindowMinimumSize(window, YETI_FRAMEBUFFER_WIDTH, YETI_FRAMEBUFFER_HEIGHT); renderer = SDL_CreateRenderer(window, NULL); if (!renderer) die(SDL_GetError()); SDL_SetRenderVSync(renderer, 1); SDL_SetRenderLogicalPresentation(renderer, YETI_FRAMEBUFFER_WIDTH, YETI_FRAMEBUFFER_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_RenderPresent(renderer); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_XBGR1555, SDL_TEXTUREACCESS_STREAMING, YETI_FRAMEBUFFER_WIDTH, YETI_FRAMEBUFFER_HEIGHT); if (!texture) die(SDL_GetError()); SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST); yeti_init(&yeti, backbuffer, backbuffer, textures, palette); yeti_init_lua(&yeti, YETI_GAMMA); game_init(&yeti); /* get requested map */ if (argc > 1) { requested_map = SDL_atoi(argv[1]); SDL_Log("Loading map %d", requested_map); game_load_map(&yeti, maps[requested_map]); } else { game_load_map(&yeti, &map_e1m1); } game_goto(&yeti.game, GAME_MODE_PLAY); /* start counting time */ then = SDL_GetTicks(); return SDL_APP_CONTINUE; } void do_frame(Sint64 dt) { static Sint64 frame_delta = 1000; const bool *keys = SDL_GetKeyboardState(NULL); yeti.keyboard.state.up = keys[SDL_SCANCODE_UP] || keys[SDL_SCANCODE_W]; yeti.keyboard.state.down = keys[SDL_SCANCODE_DOWN] || keys[SDL_SCANCODE_S]; yeti.keyboard.state.left = keys[SDL_SCANCODE_LEFT]; yeti.keyboard.state.right = keys[SDL_SCANCODE_RIGHT]; yeti.keyboard.state.a = keys[SDL_SCANCODE_RCTRL]; yeti.keyboard.state.b = keys[SDL_SCANCODE_SPACE]; yeti.keyboard.state.l = keys[SDL_SCANCODE_A]; yeti.keyboard.state.r = keys[SDL_SCANCODE_Z]; yeti.keyboard.state.select = keys[SDL_SCANCODE_ESCAPE]; frame_delta += dt; if (frame_delta > (1000 / MAXFPS)) { game_loop(&yeti); frame_delta = 0; } } SDL_AppResult SDL_AppIterate(void *appstate) { void *pixels; int pitch; /* speed limiter */ now = SDL_GetTicks(); do_frame(now - then); then = now; if (SDL_LockTexture(texture, NULL, &pixels, &pitch)) { SDL_memcpy(pixels, backbuffer, sizeof(backbuffer)); SDL_UnlockTexture(texture); } SDL_RenderClear(renderer); SDL_RenderTexture(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); return SDL_APP_CONTINUE; } static bool file_exists(const char *filename) { SDL_IOStream *io = SDL_IOFromFile(filename, "rb"); if (io) { SDL_CloseIO(io); return true; } return false; } SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { switch (event->type) { case SDL_EVENT_QUIT: return SDL_APP_SUCCESS; case SDL_EVENT_KEY_DOWN: if (event->key.scancode == SDL_SCANCODE_ESCAPE) { focused = !focused; SDL_SetWindowRelativeMouseMode(window, focused); } else if (event->key.scancode == SDL_SCANCODE_F12) { SDL_Surface *screen; char filename[256]; screen = SDL_CreateSurfaceFrom(YETI_FRAMEBUFFER_WIDTH, YETI_FRAMEBUFFER_HEIGHT, SDL_PIXELFORMAT_XBGR1555, backbuffer, YETI_FRAMEBUFFER_WIDTH * sizeof(u16)); if (!screen) die("%s", SDL_GetError()); SDL_snprintf(filename, sizeof(filename), "yeti%03d.bmp", screenshot_idx++); while (file_exists(filename)) SDL_snprintf(filename, sizeof(filename), "yeti%03d.bmp", screenshot_idx++); SDL_SaveBMP(screen, filename); SDL_Log("Saved screenshot %s", filename); SDL_DestroySurface(screen); } break; case SDL_EVENT_MOUSE_MOTION: if (focused) { yeti.camera->p += fl2f(event->motion.yrel); yeti.camera->t += fl2f(event->motion.xrel); } break; } return SDL_APP_CONTINUE; } void SDL_AppQuit(void *appstate, SDL_AppResult result) { if (window) SDL_DestroyWindow(window); if (renderer) SDL_DestroyRenderer(renderer); if (texture) SDL_DestroyTexture(texture); SDL_Quit(); }