/* Starshatter OpenSource Distribution Copyright (c) 1997-2004, Destroyer Studios LLC. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "Destroyer Studios" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SUBSYSTEM: nGenEx.lib FILE: Game.cpp AUTHOR: John DiCamillo */ #include "MemDebug.h" #include "Game.h" #include "Mouse.h" #include "Universe.h" #include "Screen.h" #include "Window.h" #include "EventDispatch.h" #include "Color.h" #include "DataLoader.h" #include "Keyboard.h" #include "Pcx.h" #include "resource.h" #include "Bitmap.h" #include "MachineInfo.h" #include "Video.h" #include "VideoFactory.h" #include "VideoSettings.h" #include "AviFile.h" #include "ContentBundle.h" // +--------------------------------------------------------------------+ FILE* ErrLog = 0; int ErrLine = 0; char ErrBuf[1024]; Game* game = 0; bool Game::active = false; bool Game::paused = false; bool Game::server = false; bool Game::show_mouse = false; DWORD Game::base_game_time = 0; DWORD Game::real_time = 0; DWORD Game::game_time = 0; DWORD Game::time_comp = 1; DWORD Game::frame_number = 0; const int VIDEO_FPS = 30; const double MAX_FRAME_TIME_VIDEO = 1.0 / (double) VIDEO_FPS; const double MAX_FRAME_TIME_NORMAL = 1.0 / 5.0; const double MIN_FRAME_TIME_NORMAL = 1.0 / 60.0; double Game::max_frame_length = MAX_FRAME_TIME_NORMAL; double Game::min_frame_length = MIN_FRAME_TIME_NORMAL; char Game::panicbuf[256]; static LARGE_INTEGER perf_freq; static LARGE_INTEGER perf_cnt1; static LARGE_INTEGER perf_cnt2; // +--------------------------------------------------------------------+ Game::Game() : world(0), video_factory(0), video(0), video_settings(0), soundcard(0), gamma(128), max_tex_size(2048), screen(0), totaltime(0), hInst(0), hwnd(0), frame_rate(0), frame_count(0), frame_count0(0), frame_time(0), frame_time0(0), gui_seconds(0), content(0), status(Game::OK), exit_code(0), window_style(0), avi_file(0) { if (!game) { panicbuf[0] = 0; game = this; ZeroMemory(ErrBuf, 1024); video_settings = new(__FILE__,__LINE__) VideoSettings; is_windowed = false; is_active = false; is_device_lost = false; is_minimized = false; is_maximized = false; ignore_size_change = false; is_device_initialized = false; is_device_restored = false; } else status = TOO_MANY; } Game::~Game() { if (game == this) game = 0; delete content; delete world; delete screen; delete video_factory; delete video; delete soundcard; delete video_settings; delete avi_file; if (status == EXIT) ShowStats(); } // +--------------------------------------------------------------------+ HINSTANCE Game::GetHINST() { if (game) return game->hInst; return 0; } HWND Game::GetHWND() { if (game) return game->hwnd; return 0; } bool Game::IsWindowed() { if (game) return game->is_windowed; return false; } // +--------------------------------------------------------------------+ Text Game::GetText(const char* key) { if (game && game->content && game->content->IsLoaded()) return game->content->GetText(key); return key; } // +--------------------------------------------------------------------+ int Game::GammaLevel() { if (game) return game->gamma; return 0; } void Game::SetGammaLevel(int g) { if (game) { game->gamma = g; if (game->video) game->video->SetGammaLevel(g); } } int Game::MaxTexSize() { if (game && game->video) { int max_vid_size = game->video->MaxTexSize(); return max_vid_size < game->max_tex_size ? max_vid_size : game->max_tex_size; } else if (Video::GetInstance()) { return Video::GetInstance()->MaxTexSize(); } return 256; } int Game::MaxTexAspect() { if (game && game->video) { return game->video->MaxTexAspect(); } else if (Video::GetInstance()) { return Video::GetInstance()->MaxTexAspect(); } return 1; } void Game::SetMaxTexSize(int n) { if (game && n >= 64 && n <= 4096) game->max_tex_size = n; } bool Game::DisplayModeSupported(int w, int h, int bpp) { return game && game->video && game->video->IsModeSupported(w,h,bpp); } double Game::FrameRate() { if (game) return game->frame_rate; return 0; } double Game::FrameTime() { if (game) return game->seconds; return 0; } double Game::GUITime() { if (game) return game->gui_seconds; return 0; } // +--------------------------------------------------------------------+ bool Game::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow) { status = OK; hInst = hi; Print(" Initializing Game\n"); stats.Clear(); if (!InitApplication(hInst)) { // Initialize shared things Panic("Could not initialize the application."); status = INIT_FAILED; } if (status == OK && !video_settings) { Panic("No video settings specified"); status = INIT_FAILED; } if (status == OK) { static int os_version = MachineInfo::GetPlatform(); if (os_version == MachineInfo::OS_WIN95 || os_version == MachineInfo::OS_WIN98) { Panic(" Windows 95 and 98 are no longer supported. Please update to Windows XP or higher."); status = INIT_FAILED; } else if (os_version == MachineInfo::OS_WINNT) { Panic(" D3D not available under WinNT 4"); status = INIT_FAILED; } else if (MachineInfo::GetDirectXVersion() < MachineInfo::DX_9) { Panic(" Insufficient DirectX detected (Dx9 IS REQUIRED)"); status = INIT_FAILED; } Print(" Gamma Level = %d\n", gamma); } if (status == OK) { Print("\n Initializing instance...\n"); // Perform initializations that apply to a specific instance if (!InitInstance(hInst, nCmdShow)) { Panic("Could not initialize the instance."); status = INIT_FAILED; } } if (status == OK) { Print(" Initializing content...\n"); InitContent(); Print(" Initializing game...\n"); if (!InitGame()) { if (!panicbuf[0]) Panic("Could not initialize the game."); status = INIT_FAILED; } } return status == OK; } // +--------------------------------------------------------------------+ bool Game::InitApplication(HINSTANCE hInstance) { WNDCLASS wc; LOGBRUSH brush = { BS_SOLID, RGB(0,0,0), 0 }; if (server) brush.lbColor = RGB(255,255,255); // Fill in window class structure with parameters that // describe the main window. wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = CreateBrushIndirect(&brush); wc.lpszMenuName = app_name; wc.lpszClassName = app_name; // Register the window class and return success/failure code. if (RegisterClass(&wc) == 0) { DWORD err = GetLastError(); if (err == 1410) // class already exists, this is OK return true; else Print("WARNING: Register Window Class: %08x\n", err); } return true; } // +--------------------------------------------------------------------+ bool Game::InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // initialize the game timer: base_game_time = 0; QueryPerformanceFrequency(&perf_freq); QueryPerformanceCounter(&perf_cnt1); // center window on display: int screenx = GetSystemMetrics(SM_CXSCREEN); int screeny = GetSystemMetrics(SM_CYSCREEN); int x_offset = 0; int y_offset = 0; int s_width = 800; int s_height = 600; if (server) { s_width = 320; s_height = 200; } else if (video_settings) { s_width = video_settings->window_width; s_height = video_settings->window_height; } if (s_width < screenx) x_offset = (screenx - s_width) / 2; if (s_height < screeny) y_offset = (screeny - s_height) / 2; // Create a main window for this application instance RECT rctmp; rctmp.left = x_offset; rctmp.top = y_offset; rctmp.right = x_offset + s_width; rctmp.bottom = y_offset + s_height; window_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE; AdjustWindowRect(&rctmp, window_style, 1); hwnd = CreateWindow( app_name, // Class name app_name, // Caption window_style, x_offset, // Position y_offset, rctmp.right - rctmp.left, // Size rctmp.bottom - rctmp.top, 0, // Parent window (no parent) 0, // use class menu hInst, // handle to window instance 0); // no params to pass on // If window could not be created, return "failure" if (!hwnd) { Panic("Could not create window\n"); return false; } Print(" Window created.\n"); // Make the window visible and draw it ShowWindow(hwnd, nCmdShow); // Show the window UpdateWindow(hwnd); // Sends WM_PAINT message // Save window properties window_style = GetWindowLong(hwnd, GWL_STYLE); GetWindowRect(hwnd, &bounds_rect); GetClientRect(hwnd, &client_rect); // Use client area to set video window size video_settings->window_width = client_rect.right - client_rect.left; video_settings->window_height = client_rect.bottom - client_rect.top; Print(" Instance initialized.\n"); return true; } // +--------------------------------------------------------------------+ bool Game::InitVideo() { if (server) return true; // create a video factory, and video object: video_factory = new(__FILE__,__LINE__) VideoFactory(hwnd); if (video_factory) { Print(" Init Video...\n"); Print(" Request %s mode\n", video_settings->GetModeDescription()); video = video_factory->CreateVideo(video_settings); if (video) { if (!video->IsHardware()) { video_factory->DestroyVideo(video); video = 0; Panic("3D Hardware Not Found"); } // save a copy of the device-specific video settings: else if (video->GetVideoSettings()) { *video_settings = *video->GetVideoSettings(); is_windowed = video_settings->IsWindowed(); } } soundcard = video_factory->CreateSoundCard(); } return (video && video->Status() == Video::VIDEO_OK); } // +--------------------------------------------------------------------+ bool Game::ResetVideo() { if (server) return true; if (!video_factory) return InitVideo(); Print(" Reset Video...\n"); Print(" Request %s mode\n", video_settings->GetModeDescription()); delete screen; if (video && !video->Reset(video_settings)) { video_factory->DestroyVideo(video); video = video_factory->CreateVideo(video_settings); } if (!video || video->Status() != Video::VIDEO_OK) { Panic("Could not re-create Video Interface."); return false; } Print(" Re-created video object.\n"); // save a copy of the device-specific video settings: if (video->GetVideoSettings()) { *video_settings = *video->GetVideoSettings(); is_windowed = video_settings->IsWindowed(); } Color::UseVideo(video); screen = new(__FILE__,__LINE__) Screen(video); if (!screen) { Panic("Could not re-create Screen object."); return false; } Print(" Re-created screen object.\n"); if (!screen->SetBackgroundColor(Color::Black)) Print(" WARNING: could not set video background color to Black\n"); screen->ClearAllFrames(true); video->SetGammaLevel(gamma); Print(" Re-established requested video parameters.\n"); Bitmap::CacheUpdate(); Print(" Refreshed texture bitmaps.\n\n"); return true; } // +--------------------------------------------------------------------+ bool Game::ResizeVideo() { if (!video || !video_settings) return false; if (!is_windowed) return false; if (ignore_size_change) return true; HRESULT hr = S_OK; RECT client_old; client_old = client_rect; // Update window properties GetWindowRect(hwnd, &bounds_rect); GetClientRect(hwnd, &client_rect); if (client_old.right - client_old.left != client_rect.right - client_rect.left || client_old.bottom - client_old.top != client_rect.bottom - client_rect.top) { // A new window size will require a new backbuffer // size, so the 3D structures must be changed accordingly. Pause(true); video_settings->is_windowed = true; video_settings->window_width = client_rect.right - client_rect.left; video_settings->window_height = client_rect.bottom - client_rect.top; ::Print("ResizeVideo() %d x %d\n", video_settings->window_width, video_settings->window_height); if (video) { video->Reset(video_settings); } Pause(false); } // save a copy of the device-specific video settings: if (video->GetVideoSettings()) { *video_settings = *video->GetVideoSettings(); is_windowed = video_settings->IsWindowed(); } screen->Resize(video_settings->window_width, video_settings->window_height); return hr == S_OK; } bool Game::ToggleFullscreen() { bool result = false; if (video && video_settings) { Pause(true); ignore_size_change = true; // Toggle the windowed state is_windowed = !is_windowed; video_settings->is_windowed = is_windowed; // Prepare window for windowed/fullscreen change AdjustWindowForChange(); // Reset the 3D device if (!video->Reset(video_settings)) { // reset failed, try to restore... ignore_size_change = false; if (!is_windowed) { // Restore window type to windowed mode is_windowed = !is_windowed; video_settings->is_windowed = is_windowed; AdjustWindowForChange(); SetWindowPos(hwnd, HWND_NOTOPMOST, bounds_rect.left, bounds_rect.top, bounds_rect.right - bounds_rect.left, bounds_rect.bottom - bounds_rect.top, SWP_SHOWWINDOW); } ::Print("Unable to toggle %s fullscreen mode.\n", is_windowed ? "into" : "out of"); } else { ignore_size_change = false; // When moving from fullscreen to windowed mode, it is important to // adjust the window size after resetting the device rather than // beforehand to ensure that you get the window size you want. For // example, when switching from 640x480 fullscreen to windowed with // a 1000x600 window on a 1024x768 desktop, it is impossible to set // the window size to 1000x600 until after the display mode has // changed to 1024x768, because windows cannot be larger than the // desktop. if (is_windowed) { SetWindowPos(hwnd, HWND_NOTOPMOST, bounds_rect.left, bounds_rect.top, bounds_rect.right - bounds_rect.left, bounds_rect.bottom - bounds_rect.top, SWP_SHOWWINDOW); } GetClientRect(hwnd, &client_rect); // Update our copy Pause(false); if (is_windowed) screen->Resize(video_settings->window_width, video_settings->window_height); else screen->Resize(video_settings->fullscreen_mode.width, video_settings->fullscreen_mode.height); result = true; } } return result; } bool Game::AdjustWindowForChange() { if (is_windowed) { // Set windowed-mode style SetWindowLong(hwnd, GWL_STYLE, window_style); if (hmenu != NULL) { SetMenu(hwnd, hmenu); hmenu = NULL; } } else { // Set fullscreen-mode style SetWindowLong(hwnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE); if (hmenu == NULL) { hmenu = GetMenu(hwnd); SetMenu(hwnd, NULL); } } return true; } // +--------------------------------------------------------------------+ bool Game::InitGame() { if (server) { Print(" InitGame() - server mode.\n"); } else { if (!SetupPalette()) { Panic("Could not set up the palette."); return false; } Print(" Palette loaded.\n"); if (!InitVideo() || !video || video->Status() != Video::VIDEO_OK) { if (!panicbuf[0]) Panic("Could not create the Video Interface."); return false; } Print(" Created video object.\n"); Color::UseVideo(video); screen = new(__FILE__,__LINE__) Screen(video); if (!screen) { if (!panicbuf[0]) Panic("Could not create the Screen object."); return false; } Print(" Created screen object.\n"); if (!screen->SetBackgroundColor(Color::Black)) Print(" WARNING: could not set video background color to Black\n"); screen->ClearAllFrames(true); video->SetGammaLevel(gamma); Print(" Established requested video parameters.\n\n"); } return true; } // +--------------------------------------------------------------------+ bool Game::InitContent() { DataLoader* loader = DataLoader::GetLoader(); List bundles; loader->SetDataPath("Content/"); loader->ListFiles("content*", bundles); ListIter iter = bundles; while (++iter) { Text* filename = iter.value(); int n = filename->indexOf('_'); if (n > 0) { Locale::ParseLocale(filename->data() + n); } else { delete content; content = new(__FILE__,__LINE__) ContentBundle("content", 0); } } loader->SetDataPath(0); return true; } void Game::UseLocale(Locale* locale) { if (game) { DataLoader* loader = DataLoader::GetLoader(); loader->SetDataPath("Content/"); delete game->content; game->content = new(__FILE__,__LINE__) ContentBundle("content", locale); loader->SetDataPath(0); } } // +--------------------------------------------------------------------+ bool Game::SetupPalette() { if (LoadPalette(standard_palette, inverse_palette)) { Color::SetPalette(standard_palette, 256, inverse_palette); return true; } return false; } // +--------------------------------------------------------------------+ bool Game::LoadPalette(PALETTEENTRY* pal, BYTE* inv) { char palheader[32]; struct { WORD Version; WORD NumberOfEntries; PALETTEENTRY Entries[256]; } Palette = { 0x300, 256 }; DataLoader* loader = DataLoader::GetLoader(); BYTE* block; char fname[256]; sprintf_s(fname, "%s.pal", palette_name); if (!loader->LoadBuffer(fname, block)) { Print(" Could not open file '%s'\n", fname); return false; } memcpy(&palheader, block, 24); memcpy((char*) Palette.Entries, (block+24), 256*4); for (int i = 0; i < 256; i++) { *pal++ = Palette.Entries[i]; } loader->ReleaseBuffer(block); sprintf_s(fname, "%s.ipl", palette_name); int size = loader->LoadBuffer(fname, block); if (size < 32768) { Print(" Could not open file '%s'\n", fname); return false; } memcpy(inv, block, 32768); loader->ReleaseBuffer(block); return true; } // +--------------------------------------------------------------------+ int Game::Run() { MSG msg; status = RUN; Print("\n"); Print("+====================================================================+\n"); Print("| RUN |\n"); Print("+====================================================================+\n"); // Polling messages from event queue until quit while (status < EXIT) { if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { if (ProfileGameLoop()) WaitMessage(); } } return exit_code ? exit_code : msg.wParam; } // +--------------------------------------------------------------------+ void Game::Exit() { Print("\n\n*** Game::Exit()\n"); status = EXIT; } void Game::Panic(const char* msg) { if (msg) Print("*** PANIC: %s\n", msg); else Print("*** PANIC! ***\n"); if (!msg) msg = "Unspecified fatal error."; sprintf_s(panicbuf, "%s\nThis game will now terminate.", msg); if (game) { game->status = PANIC; } } // +--------------------------------------------------------------------+ void Game::Activate(bool f) { active = f; if (active && video) video->InvalidateCache(); } // +--------------------------------------------------------------------+ void Game::Pause(bool f) { if (f) { if (soundcard) soundcard->Pause(); paused = true; } else { if (soundcard) soundcard->Resume(); paused = false; } } // +--------------------------------------------------------------------+ bool ProfileGameLoop(void) { return game->GameLoop(); } bool Game::GameLoop() { bool wait_for_windows_events = true; if (active && !paused) { if (!server) { // Route Events to EventTargets EventDispatch* ed = EventDispatch::GetInstance(); if (ed) ed->Dispatch(); } UpdateWorld(); GameState(); if (!server) { UpdateScreen(); CollectStats(); } wait_for_windows_events = false; } else if (active && paused) { if (GetKey()=='P') Pause(false); } QueryPerformanceCounter(&perf_cnt2); double freq = (double) (perf_freq.QuadPart); double msec = (double) (perf_cnt2.QuadPart - perf_cnt1.QuadPart); msec /= freq; msec *= 1000.0; if (msec < 1) msec = 1; real_time += (DWORD) msec; frame_number++; Mouse::w = 0; perf_cnt1 = perf_cnt2; return wait_for_windows_events; } // +--------------------------------------------------------------------+ void Game::UpdateWorld() { long new_time = real_time; double delta = new_time - frame_time; gui_seconds = delta * 0.001; seconds = max_frame_length; if (time_comp == 1) { if (delta < max_frame_length * 1000) seconds = delta * 0.001; } else { seconds = time_comp * delta * 0.001; } frame_time = new_time; game_time += (DWORD) (seconds * 1000); if (world) world->ExecFrame(seconds); } // +--------------------------------------------------------------------+ void Game::GameState() { } // +--------------------------------------------------------------------+ void Game::UpdateScreen() { if (!screen || !video) return; if (screen->Refresh()) { if (Keyboard::KeyDown(VK_F12)) { if (Keyboard::KeyDown(VK_SHIFT)) { if (!avi_file) { AVICapture(); // begin capturing SetMaxFrameLength(MAX_FRAME_TIME_VIDEO); } else { delete avi_file; // end capture; avi_file = 0; SetMaxFrameLength(MAX_FRAME_TIME_NORMAL); } } else { if (!avi_file) { ScreenCapture(); } else { delete avi_file; // end capture; avi_file = 0; SetMaxFrameLength(MAX_FRAME_TIME_NORMAL); } } } else if (avi_file) { AVICapture(); // continue capturing... } video->Present(); } else { Panic("Screen refresh failed."); } } // +--------------------------------------------------------------------+ Game* Game::GetInstance() { return game; } Video* Game::GetVideo() { if (game) return game->video; return 0; } Color Game::GetScreenColor() { if (game) return game->screen_color; return Color::Black; } void Game::SetScreenColor(Color c) { if (game) { game->screen_color = c; if (game->screen) game->screen->SetBackgroundColor(c); } } int Game::GetScreenWidth() { if (game && game->video) return game->video->Width(); return 0; } int Game::GetScreenHeight() { if (game && game->video) return game->video->Height(); return 0; } // +--------------------------------------------------------------------+ void Game::ScreenCapture(const char* name) { if (server || !video || !screen) return; static DWORD last_shot = 0; static DWORD shot_num = 1; DataLoader* loader = DataLoader::GetLoader(); char filename[256]; if (!name && (real_time - last_shot) < 1000) return; // try not to overwrite existing screen shots... if (loader) { bool use_file_sys = loader->IsFileSystemEnabled(); loader->UseFileSystem(true); loader->SetDataPath(0); List shot_list; loader->ListFiles("*.PCX", shot_list); loader->UseFileSystem(use_file_sys); for (int i = 0; i < shot_list.size(); i++) { Text* s = shot_list[i]; int n = 0; sscanf_s(s->data()+1, "%d", &n); if (shot_num <= (DWORD) n) shot_num = n+1; } shot_list.destroy(); } if (name) strcpy_s(filename, name); else sprintf_s(filename, "A%d.PCX", shot_num++); //-V576 Bitmap bmp; if (video && video->Capture(bmp)) { PcxImage pcx(bmp.Width(), bmp.Height(), (LPDWORD) bmp.HiPixels()); pcx.Save((char*) filename); } last_shot = real_time; } // +--------------------------------------------------------------------+ void Game::AVICapture(const char* name) { if (server || !video || !screen) return; if (!avi_file) { char filename[256]; Bitmap bmp; DataLoader* loader = DataLoader::GetLoader(); DWORD avi_num = 1; // try not to overwrite existing screen shots... if (loader) { bool use_file_sys = loader->IsFileSystemEnabled(); loader->UseFileSystem(true); loader->SetDataPath(0); List avi_list; loader->ListFiles("*.avi", avi_list); loader->UseFileSystem(use_file_sys); for (int i = 0; i < avi_list.size(); i++) { Text* s = avi_list[i]; int n = 0; sscanf_s(s->data()+1, "%d", &n); if (avi_num <= (DWORD) n) avi_num = n+1; } avi_list.destroy(); } if (name) strcpy_s(filename, name); else sprintf_s(filename, "A%d.avi", avi_num); //-V576 if (video && video->Capture(bmp)) { //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2); avi_file = new(__FILE__,__LINE__) AviFile(filename, Rect(0,0,bmp.Width(),bmp.Height()), VIDEO_FPS); } } else { Bitmap bmp; if (video && video->Capture(bmp)) { //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2); avi_file->AddFrame(bmp); } } } // +--------------------------------------------------------------------+ void Game::CollectStats() { frame_count++; if (!totaltime) totaltime = real_time; if (frame_time - frame_time0 > 200) { frame_rate = (frame_count - frame_count0) * 1000.0 / (frame_time - frame_time0); frame_time0 = frame_time; frame_count0 = frame_count; } if (video) { stats.nframe = video->GetStats().nframe; stats.nverts = video->GetStats().nverts; stats.npolys = video->GetStats().npolys; stats.nlines = video->GetStats().nlines; stats.ncalls += video->GetStats().ncalls; stats.total_verts += stats.nverts; stats.total_polys += stats.npolys; stats.total_lines += stats.nlines; } } // +--------------------------------------------------------------------+ void Game::ShowStats() { if (server) return; totaltime = real_time - totaltime; Print("\n"); Print("Performance Data:\n"); Print("-----------------\n"); Print(" Time: %d msec\n", totaltime); Print(" Frames: %d\n", stats.nframe); Print(" Polys Rendered: %d\n", stats.total_polys); Print(" Lines Rendered: %d\n", stats.total_lines); Print(" Verts Rendered: %d\n", stats.total_verts); Print(" Render Calls: %d\n", stats.ncalls); Print("\n"); Print("Performance Statistics:\n"); Print("-----------------------\n"); Print(" Frames/Second: %.2f\n", (stats.nframe * 1000.0) / totaltime); Print(" Polys/Frame: %.2f\n", (double) stats.total_polys / (double) stats.nframe); Print(" Polys/Call: %.2f\n", (double) stats.total_polys / (double) stats.ncalls); Print(" Polys/Second: %.2f\n", (stats.total_polys * 1000.0) / totaltime); Print(" Lines/Second: %.2f\n", (stats.total_lines * 1000.0) / totaltime); Print(" Verts/Second: %.2f\n", (stats.total_verts * 1000.0) / totaltime); Print("\n"); } // +====================================================================+ // WndProc // +====================================================================+ #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x20A #endif LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM uParam, LPARAM lParam) { switch (message) { case WM_SYSKEYDOWN: if (uParam == VK_TAB || uParam == VK_F4) return DefWindowProc(hwnd, message, uParam, lParam); return 0; case WM_MENUCHAR: return MNC_CLOSE << 16; case WM_ACTIVATEAPP: // Keep track of whether or not the app is in the foreground if (game) game->Activate(uParam?true:false); break; case WM_PAINT: if (!game || !game->OnPaint()) return DefWindowProc(hwnd, message, uParam, lParam); break; case WM_SETCURSOR: if (Game::ShowMouse()) { return DefWindowProc(hwnd, message, uParam, lParam); } else { // hide the windows mouse cursor SetCursor(NULL); return 1; } break; case WM_ENTERSIZEMOVE: // Halt frame movement while the app is sizing or moving if (game) game->Pause(true); break; case WM_SIZE: // Pick up possible changes to window style due to maximize, etc. if (game && game->hwnd != NULL ) { game->window_style = GetWindowLong(game->hwnd, GWL_STYLE ); if (uParam == SIZE_MINIMIZED) { game->Pause(true); // Pause while we're minimized game->is_minimized = true; game->is_maximized = false; } else if (uParam == SIZE_MAXIMIZED) { if (game->is_minimized) game->Pause(false); // Unpause since we're no longer minimized game->is_minimized = false; game->is_maximized = true; game->ResizeVideo(); } else if (uParam == SIZE_RESTORED) { if (game->is_maximized) { game->is_maximized = false; game->ResizeVideo(); } else if (game->is_minimized) { game->Pause(false); // Unpause since we're no longer minimized game->is_minimized = false; game->ResizeVideo(); } else { // If we're neither maximized nor minimized, the window size // is changing by the user dragging the window edges. In this // case, we don't reset the device yet -- we wait until the // user stops dragging, and a WM_EXITSIZEMOVE message comes. } } } break; case WM_EXITSIZEMOVE: if (game) { game->Pause(false); game->ResizeVideo(); } break; case WM_ENTERMENULOOP: if (game) game->Pause(true); break; case WM_EXITMENULOOP: if (game) game->Pause(false); break; /* case WM_HELP: if (game) return game->OnHelp(); break; */ case WM_KEYDOWN: BufferKey(uParam); return 0; case WM_DESTROY: PostQuitMessage(0); break; case WM_MOUSEMOVE: Mouse::x = LOWORD(lParam); Mouse::y = HIWORD(lParam); break; case WM_LBUTTONDOWN: Mouse::l = 1; break; case WM_LBUTTONDBLCLK: Mouse::l = 2; break; case WM_LBUTTONUP: Mouse::l = 0; break; case WM_MBUTTONDOWN: Mouse::m = 1; break; case WM_MBUTTONDBLCLK: Mouse::m = 2; break; case WM_MBUTTONUP: Mouse::m = 0; break; case WM_RBUTTONDOWN: Mouse::r = 1; break; case WM_RBUTTONDBLCLK: Mouse::r = 2; break; case WM_RBUTTONUP: Mouse::r = 0; break; case WM_MOUSEWHEEL: { int w = (int) (uParam >> 16); if (w > 32000) w -= 65536; Mouse::w += w; } break; case WM_CLOSE: if (game) // && game->Server()) game->Exit(); break; default: return DefWindowProc(hwnd, message, uParam, lParam); } return 0; } // +====================================================================+ const int MAX_KEY_BUF = 512; static int vkbuf[MAX_KEY_BUF]; static int vkshiftbuf[MAX_KEY_BUF]; static int vkins = 0; static int vkext = 0; void FlushKeys() { Keyboard::FlushKeys(); vkins = vkext = 0; } void BufferKey(int vkey) { if (vkey < 1) return; int shift = 0; if (GetAsyncKeyState(VK_SHIFT)) shift |= 1; if (GetAsyncKeyState(VK_CONTROL)) shift |= 2; if (GetAsyncKeyState(VK_MENU)) shift |= 4; vkbuf[vkins] = vkey; vkshiftbuf[vkins++] = shift; if (vkins >= MAX_KEY_BUF) vkins = 0; if (vkins == vkext) { vkext++; if (vkext >= MAX_KEY_BUF) vkext = 0; } } int GetKey() { if (vkins == vkext) return 0; int result = vkbuf[vkext++]; if (vkext >= MAX_KEY_BUF) vkext = 0; return result; } int GetKeyPlus(int& key, int& shift) { if (vkins == vkext) return 0; key = vkbuf[vkext]; shift = vkshiftbuf[vkext++]; if (vkext >= MAX_KEY_BUF) vkext = 0; return key; } // +====================================================================+ void Print(const char* fmt, ...) { if (ErrLog) { vsprintf_s(ErrBuf, fmt, (char *)(&fmt+1)); fprintf(ErrLog, ErrBuf); fflush(ErrLog); } } // +====================================================================+ DWORD GetRealTime() { if (game) return Game::RealTime(); return timeGetTime(); } DWORD Game::RealTime() { return real_time; } DWORD Game::GameTime() { return game_time; } DWORD Game::TimeCompression() { return time_comp; } void Game::SetTimeCompression(DWORD comp) { if (comp > 0 && comp <= 100) time_comp = comp; } DWORD Game::Frame() { return frame_number; } void Game::ResetGameTime() { game_time = 0; } void Game::SkipGameTime(double seconds) { if (seconds > 0) game_time += (DWORD) (seconds * 1000); }