SpaceCadetPinballEmscripten/SpaceCadetPinball/winmain.cpp

763 lines
17 KiB
C++
Raw Normal View History

2020-11-05 16:44:34 +01:00
#include "pch.h"
#include "winmain.h"
2020-11-06 14:56:32 +01:00
#include "fullscrn.h"
2020-11-05 16:44:34 +01:00
#include "memory.h"
2020-12-02 18:12:34 +01:00
#include "midi.h"
2020-11-05 16:44:34 +01:00
#include "pinball.h"
#include "options.h"
2020-11-06 14:56:32 +01:00
#include "pb.h"
#include "render.h"
#include "Sound.h"
2020-11-05 16:44:34 +01:00
const double TargetFps = 60, TargetFrameTime = 1000 / TargetFps;
SDL_Window* winmain::MainWindow = nullptr;
SDL_Renderer* winmain::Renderer = nullptr;
ImGuiIO* winmain::ImIO = nullptr;
int winmain::return_value = 0;
int winmain::bQuit = 0;
int winmain::activated;
int winmain::DispFrameRate = 0;
int winmain::DispGRhistory = 0;
int winmain::single_step = 0;
int winmain::has_focus = 1;
int winmain::last_mouse_x;
int winmain::last_mouse_y;
int winmain::mouse_down;
int winmain::no_time_loss;
DWORD winmain::then;
DWORD winmain::now;
bool winmain::restart = false;
gdrv_bitmap8 winmain::gfr_display{};
std::string winmain::DatFileName;
bool winmain::ShowAboutDialog = false;
bool winmain::ShowImGuiDemo = false;
bool winmain::ShowSpriteViewer = false;
bool winmain::LaunchBallEnabled = true;
bool winmain::HighScoresEnabled = true;
bool winmain::DemoActive = false;
char* winmain::BasePath;
std::string winmain::FpsDetails;
2020-11-05 16:44:34 +01:00
uint32_t timeGetTimeAlt()
{
auto now = std::chrono::high_resolution_clock::now();
auto duration = now.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
return static_cast<uint32_t>(millis);
}
int winmain::WinMain(LPCSTR lpCmdLine)
2020-11-05 16:44:34 +01:00
{
restart = false;
bQuit = false;
2020-11-07 16:41:14 +01:00
memory::init(memalloc_failure);
2020-11-05 16:44:34 +01:00
// SDL init
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
2020-11-05 16:44:34 +01:00
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not initialize SDL2", SDL_GetError(), nullptr);
return 1;
}
BasePath = SDL_GetBasePath();
2020-11-05 16:44:34 +01:00
2020-11-06 14:56:32 +01:00
pinball::quickFlag = strstr(lpCmdLine, "-quick") != nullptr;
DatFileName = options::get_string("Pinball Data", pinball::get_rc_string(168, 0));
2020-11-05 16:44:34 +01:00
/*Check for full tilt .dat file and switch to it automatically*/
auto cadetFilePath = pinball::make_path_name("CADET.DAT");
auto cadetDat = fopen(cadetFilePath.c_str(), "r");
if (cadetDat)
{
fclose(cadetDat);
DatFileName = "CADET.DAT";
pb::FullTiltMode = true;
}
// SDL window
SDL_Window* window = SDL_CreateWindow
(
pinball::get_rc_string(38, 0),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
800, 556,
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE
);
MainWindow = window;
if (!window)
2020-11-06 14:56:32 +01:00
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not create window", SDL_GetError(), nullptr);
return 1;
}
SDL_Renderer* renderer = SDL_CreateRenderer
(
window,
-1,
SDL_RENDERER_ACCELERATED
);
Renderer = renderer;
if (!renderer)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not create renderer", SDL_GetError(), window);
return 1;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
// ImGui init
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiSDL::Initialize(renderer, 0, 0);
ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO();
ImIO = &io;
// ImGui_ImplSDL2_Init is private, we are not actually using ImGui OpenGl backend
ImGui_ImplSDL2_InitForOpenGL(window, nullptr);
auto prefPath = SDL_GetPrefPath(nullptr, "SpaceCadetPinball");
auto iniPath = std::string(prefPath) + "imgui_pb.ini";
io.IniFilename = iniPath.c_str();
SDL_free(prefPath);
// PB init from message handler
{
++memory::critical_allocation;
options::init();
auto voiceCount = options::get_int("Voices", 8);
if (Sound::Init(voiceCount))
options::Options.Sounds = 0;
Sound::Activate();
if (!pinball::quickFlag && !midi::music_init())
options::Options.Music = 0;
if (pb::init())
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not load game data",
"The .dat file is missing", window);
return 1;
}
fullscrn::init();
--memory::critical_allocation;
2020-11-06 14:56:32 +01:00
}
pb::reset_table();
pb::firsttime_setup();
if (strstr(lpCmdLine, "-fullscreen"))
{
options::Options.FullScreen = 1;
}
SDL_ShowWindow(window);
fullscrn::set_screen_mode(options::Options.FullScreen);
2020-11-07 16:41:14 +01:00
if (strstr(lpCmdLine, "-demo"))
pb::toggle_demo();
else
pb::replay_level(0);
DWORD updateCounter = 300u, frameCounter = 0, prevTime = 0u;
then = timeGetTimeAlt();
double sdlTimerResMs = 1000.0 / static_cast<double>(SDL_GetPerformanceFrequency());
auto frameStart = static_cast<double>(SDL_GetPerformanceCounter());
2020-11-06 14:56:32 +01:00
while (true)
{
if (!updateCounter)
2020-11-13 17:04:58 +01:00
{
updateCounter = 300;
if (DispFrameRate)
2020-11-13 17:04:58 +01:00
{
auto curTime = timeGetTimeAlt();
if (prevTime)
2020-11-13 17:04:58 +01:00
{
char buf[60];
auto elapsedSec = static_cast<float>(curTime - prevTime) * 0.001f;
snprintf(buf, sizeof buf, "Updates/sec = %02.02f Frames/sec = %02.02f ",
300.0f / elapsedSec, frameCounter / elapsedSec);
SDL_SetWindowTitle(window, buf);
FpsDetails = buf;
frameCounter = 0;
if (DispGRhistory)
{
if (!gfr_display.BmpBufPtr1)
{
auto plt = static_cast<ColorRgba*>(malloc(1024u));
auto pltPtr = &plt[10];
for (int i1 = 0, i2 = 0; i1 < 256 - 10; ++i1, i2 += 8)
{
unsigned char blue = i2, redGreen = i2;
if (i2 > 255)
{
blue = 255;
redGreen = i1;
}
2020-11-13 17:04:58 +01:00
*pltPtr++ = ColorRgba{Rgba{redGreen, redGreen, blue, 0}};
}
gdrv::display_palette(plt);
free(plt);
gdrv::create_bitmap(&gfr_display, 400, 15, 400, false);
}
gdrv::copy_bitmap(&render::vscreen, 300, 10, 0, 30, &gfr_display, 0, 0);
gdrv::fill_bitmap(&gfr_display, 300, 10, 0, 0, 0);
}
}
prevTime = curTime;
}
else
{
prevTime = 0;
2020-11-13 17:04:58 +01:00
}
}
2020-11-06 14:56:32 +01:00
if (!ProcessWindowMessages() || bQuit)
break;
if (has_focus)
{
if (mouse_down)
{
now = timeGetTimeAlt();
if (now - then >= 2)
{
int x, y;
SDL_GetMouseState(&x, &y);
pb::ballset(last_mouse_x - x, y - last_mouse_y);
SDL_WarpMouseInWindow(window, last_mouse_x, last_mouse_y);
}
}
if (!single_step)
{
auto curTime = timeGetTimeAlt();
now = curTime;
if (no_time_loss)
{
then = curTime;
no_time_loss = 0;
}
if (curTime == then)
{
SDL_Delay(8);
}
else if (pb::frame(curTime - then))
{
if (gfr_display.BmpBufPtr1)
{
auto deltaT = now - then + 10;
auto fillChar = static_cast<uint8_t>(deltaT);
if (deltaT > 236)
{
fillChar = 1;
}
gdrv::fill_bitmap(&gfr_display, 1, 10, 300 - updateCounter, 0, fillChar);
}
--updateCounter;
then = now;
}
}
auto frameEnd = static_cast<double>(SDL_GetPerformanceCounter());
auto elapsedMs = (frameEnd - frameStart) * sdlTimerResMs;
if (elapsedMs >= TargetFrameTime)
{
// Keep track of remainder, limited to one frame time.
frameStart = frameEnd - std::min(elapsedMs - TargetFrameTime, TargetFrameTime) / sdlTimerResMs;
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
RenderUi();
SDL_RenderClear(renderer);
render::PresentVScreen();
ImGui::Render();
ImGuiSDL::Render(ImGui::GetDrawData());
SDL_RenderPresent(renderer);
frameCounter++;
}
auto sdlError = SDL_GetError();
if (sdlError[0])
{
SDL_ClearError();
printf("SDL Error: %s\n", sdlError);
}
}
2020-11-06 14:56:32 +01:00
}
gdrv::destroy_bitmap(&gfr_display);
options::uninit();
midi::music_shutdown();
pb::uninit();
Sound::Close();
ImGuiSDL::Deinitialize();
ImGui_ImplSDL2_Shutdown();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
ImGui::DestroyContext();
SDL_Quit();
2020-11-06 14:56:32 +01:00
return return_value;
}
void winmain::RenderUi()
2020-11-06 14:56:32 +01:00
{
// No demo window in release to save space
#ifndef NDEBUG
if (ShowImGuiDemo)
ImGui::ShowDemoWindow(&ShowImGuiDemo);
#endif
if (ImGui::BeginMainMenuBar())
2020-12-02 18:12:34 +01:00
{
if (ImGui::BeginMenu("Game"))
2020-12-02 18:12:34 +01:00
{
if (ImGui::MenuItem("New Game", "F2"))
2020-12-02 18:12:34 +01:00
{
new_game();
2020-12-02 18:12:34 +01:00
}
if (ImGui::MenuItem("Launch Ball", nullptr, false, LaunchBallEnabled))
{
end_pause();
pb::launch_ball();
}
if (ImGui::MenuItem("Pause/ Resume Game", "F3"))
2020-12-02 18:12:34 +01:00
{
pause();
2020-12-02 18:12:34 +01:00
}
ImGui::Separator();
2020-12-02 18:12:34 +01:00
if (ImGui::MenuItem("High Scores...", nullptr, false, HighScoresEnabled))
2020-12-02 18:12:34 +01:00
{
if (!single_step)
pause();
pb::high_scores();
2020-12-02 18:12:34 +01:00
}
if (ImGui::MenuItem("Demo", nullptr, DemoActive))
2020-12-02 18:12:34 +01:00
{
end_pause();
pb::toggle_demo();
2020-12-02 18:12:34 +01:00
}
if (ImGui::MenuItem("Exit"))
{
SDL_Event event{SDL_QUIT};
SDL_PushEvent(&event);
}
ImGui::EndMenu();
2020-12-02 18:12:34 +01:00
}
if (ImGui::BeginMenu("Options"))
2020-12-02 18:12:34 +01:00
{
if (ImGui::MenuItem("Full Screen", "F4", options::Options.FullScreen))
{
options::toggle(Menu1::Full_Screen);
}
if (ImGui::BeginMenu("Select Players"))
{
if (ImGui::MenuItem("1 Player", nullptr, options::Options.Players == 1))
{
options::toggle(Menu1::OnePlayer);
new_game();
}
if (ImGui::MenuItem("2 Players", nullptr, options::Options.Players == 2))
{
options::toggle(Menu1::TwoPlayers);
new_game();
}
if (ImGui::MenuItem("3 Players", nullptr, options::Options.Players == 3))
{
options::toggle(Menu1::ThreePlayers);
new_game();
}
if (ImGui::MenuItem("4 Players", nullptr, options::Options.Players == 4))
{
options::toggle(Menu1::FourPlayers);
new_game();
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Sound", nullptr, options::Options.Sounds))
{
options::toggle(Menu1::Sounds);
}
if (ImGui::MenuItem("Music", nullptr, options::Options.Music))
{
options::toggle(Menu1::Music);
}
ImGui::Separator();
if (ImGui::MenuItem("Player Controls...", "F8"))
2020-12-02 18:12:34 +01:00
{
if (!single_step)
pause();
options::keyboard();
}
if (ImGui::BeginMenu("Table Resolution"))
{
char buffer[20]{};
auto maxResText = pinball::get_rc_string(fullscrn::GetMaxResolution() + 2030, 0);
if (ImGui::MenuItem(maxResText, nullptr, options::Options.Resolution == -1))
{
options::toggle(Menu1::MaximumResolution);
}
for (auto i = 0; i <= fullscrn::GetMaxResolution(); i++)
2020-12-02 18:12:34 +01:00
{
auto& res = fullscrn::resolution_array[i];
snprintf(buffer, sizeof buffer - 1, "%d x %d", res.ScreenWidth, res.ScreenHeight);
if (ImGui::MenuItem(buffer, nullptr, options::Options.Resolution == i))
{
options::toggle(static_cast<Menu1>(static_cast<int>(Menu1::R640x480) + i));
}
2020-12-02 18:12:34 +01:00
}
ImGui::EndMenu();
2020-12-02 18:12:34 +01:00
}
if (ImGui::BeginMenu("Window"))
{
if (ImGui::MenuItem("Uniform Scaling", nullptr, options::Options.UniformScaling))
{
options::toggle(Menu1::WindowUniformScale);
}
if (ImGui::MenuItem("Linear Filtering", nullptr, options::Options.LinearFiltering))
{
options::toggle(Menu1::WindowLinearFilter);
}
ImGui::DragFloat("", &ImIO->FontGlobalScale, 0.005f, 0.8f, 5,
"UI Scale %.2f", ImGuiSliderFlags_AlwaysClamp);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Help"))
{
#ifndef NDEBUG
if (ImGui::MenuItem("ImGui Demo", nullptr, ShowImGuiDemo))
2020-12-02 18:12:34 +01:00
{
ShowImGuiDemo ^= true;
2020-12-02 18:12:34 +01:00
}
#endif
if (ImGui::MenuItem("Sprite Viewer", nullptr, ShowSpriteViewer))
{
if (!ShowSpriteViewer && !single_step)
pause();
ShowSpriteViewer ^= true;
}
ImGui::Separator();
2021-02-09 16:09:44 +01:00
if (ImGui::MenuItem("About Pinball"))
{
if (!single_step)
pause();
ShowAboutDialog = true;
}
ImGui::EndMenu();
}
if (DispFrameRate && !FpsDetails.empty())
if (ImGui::BeginMenu(FpsDetails.c_str()))
ImGui::EndMenu();
ImGui::EndMainMenuBar();
}
a_dialog();
high_score::RenderHighScoreDialog();
if (ShowSpriteViewer)
render::SpriteViewer(&ShowSpriteViewer);
}
int winmain::event_handler(const SDL_Event* event)
{
ImGui_ImplSDL2_ProcessEvent(event);
if (ImIO->WantCaptureMouse)
{
if (mouse_down)
{
mouse_down = 0;
SDL_ShowCursor(SDL_ENABLE);
SDL_SetWindowGrab(MainWindow, SDL_FALSE);
}
switch (event->type)
{
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEWHEEL:
return 1;
default: ;
}
}
if (ImIO->WantCaptureKeyboard)
{
switch (event->type)
{
case SDL_KEYDOWN:
case SDL_KEYUP:
return 1;
default: ;
2020-12-02 18:12:34 +01:00
}
}
switch (event->type)
2020-11-06 14:56:32 +01:00
{
case SDL_QUIT:
end_pause();
bQuit = 1;
fullscrn::shutdown();
return_value = 0;
return 0;
case SDL_KEYUP:
pb::keyup(event->key.keysym.sym);
break;
case SDL_KEYDOWN:
if (!event->key.repeat)
pb::keydown(event->key.keysym.sym);
switch (event->key.keysym.sym)
{
case SDLK_ESCAPE:
if (options::Options.FullScreen)
options::toggle(Menu1::Full_Screen);
SDL_MinimizeWindow(MainWindow);
break;
case SDLK_F2:
new_game();
break;
case SDLK_F3:
pause();
break;
case SDLK_F4:
options::toggle(Menu1::Full_Screen);
break;
case SDLK_F5:
options::toggle(Menu1::Sounds);
break;
case SDLK_F6:
options::toggle(Menu1::Music);
break;
case SDLK_F8:
if (!single_step)
pause();
options::keyboard();
break;
default:
break;
}
if (!pb::cheat_mode)
break;
switch (event->key.keysym.sym)
{
case SDLK_g:
DispGRhistory = 1;
break;
case SDLK_y:
SDL_SetWindowTitle(MainWindow, "Pinball");
DispFrameRate = DispFrameRate == 0;
break;
case SDLK_F1:
pb::frame(10);
break;
case SDLK_F10:
single_step = single_step == 0;
if (single_step == 0)
no_time_loss = 1;
break;
default:
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
switch (event->button.button)
2020-11-06 14:56:32 +01:00
{
case SDL_BUTTON_LEFT:
if (pb::cheat_mode)
2020-11-06 14:56:32 +01:00
{
mouse_down = 1;
last_mouse_x = event->button.x;
last_mouse_y = event->button.y;
SDL_ShowCursor(SDL_DISABLE);
SDL_SetWindowGrab(MainWindow, SDL_TRUE);
2020-11-06 14:56:32 +01:00
}
else
pb::keydown(options::Options.LeftFlipperKey);
break;
case SDL_BUTTON_RIGHT:
if (!pb::cheat_mode)
pb::keydown(options::Options.RightFlipperKey);
break;
case SDL_BUTTON_MIDDLE:
pb::keydown(options::Options.PlungerKey);
break;
default:
break;
2020-11-06 14:56:32 +01:00
}
break;
case SDL_MOUSEBUTTONUP:
switch (event->button.button)
{
case SDL_BUTTON_LEFT:
if (mouse_down)
{
mouse_down = 0;
SDL_ShowCursor(SDL_ENABLE);
SDL_SetWindowGrab(MainWindow, SDL_FALSE);
}
if (!pb::cheat_mode)
pb::keyup(options::Options.LeftFlipperKey);
break;
case SDL_BUTTON_RIGHT:
if (!pb::cheat_mode)
pb::keyup(options::Options.RightFlipperKey);
break;
case SDL_BUTTON_MIDDLE:
pb::keyup(options::Options.PlungerKey);
break;
default:
break;
}
break;
case SDL_WINDOWEVENT:
switch (event->window.event)
{
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT_TAKE_FOCUS:
case SDL_WINDOWEVENT_SHOWN:
activated = 1;
Sound::Activate();
if (options::Options.Music && !single_step)
midi::play_pb_theme(0);
no_time_loss = 1;
has_focus = 1;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
case SDL_WINDOWEVENT_HIDDEN:
activated = 0;
fullscrn::activate(0);
options::Options.FullScreen = 0;
Sound::Deactivate();
midi::music_stop();
has_focus = 0;
pb::loose_focus();
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED:
fullscrn::window_size_changed();
break;
default: ;
}
break;
default: ;
2020-11-06 14:56:32 +01:00
}
return 1;
}
int winmain::ProcessWindowMessages()
{
SDL_Event event;
if (has_focus && !single_step)
2020-11-06 14:56:32 +01:00
{
while (SDL_PollEvent(&event))
{
if (!event_handler(&event))
return 0;
}
return 1;
2020-11-06 14:56:32 +01:00
}
SDL_WaitEvent(&event);
return event_handler(&event);
2020-11-05 16:44:34 +01:00
}
2020-11-07 16:41:14 +01:00
void winmain::memalloc_failure()
2020-11-05 16:44:34 +01:00
{
2020-12-02 18:12:34 +01:00
midi::music_stop();
Sound::Close();
2020-11-05 16:44:34 +01:00
char* caption = pinball::get_rc_string(170, 0);
char* text = pinball::get_rc_string(179, 0);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, MainWindow);
std::exit(1);
2020-11-05 16:44:34 +01:00
}
2020-11-07 16:41:14 +01:00
void winmain::a_dialog()
2020-11-07 16:41:14 +01:00
{
if (ShowAboutDialog == true)
{
ShowAboutDialog = false;
ImGui::OpenPopup("About");
}
2020-11-07 16:41:14 +01:00
bool unused_open = true;
if (ImGui::BeginPopupModal("About", &unused_open, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::TextUnformatted("3D Pinball for Windows - Space Cadet");
ImGui::TextUnformatted("Original game by Cinematronics, Microsoft");
ImGui::Separator();
ImGui::TextUnformatted("Decompiled -> Ported to SDL");
if (ImGui::SmallButton("Project home: https://github.com/k4zmu2a/SpaceCadetPinball"))
{
#if SDL_VERSION_ATLEAST(2, 0, 14)
// Relatively new feature, skip with older SDL
SDL_OpenURL("https://github.com/k4zmu2a/SpaceCadetPinball");
#endif
}
ImGui::Separator();
2020-11-07 16:41:14 +01:00
if (ImGui::Button("Ok"))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
2020-11-07 16:41:14 +01:00
}
2020-12-02 18:12:34 +01:00
void winmain::end_pause()
{
if (single_step)
{
pb::pause_continue();
no_time_loss = 1;
}
}
void winmain::new_game()
{
end_pause();
pb::replay_level(0);
}
void winmain::pause()
{
pb::pause_continue();
no_time_loss = 1;
}
void winmain::Restart()
{
restart = true;
SDL_Event event{SDL_QUIT};
SDL_PushEvent(&event);
}