Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Game.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: Game.cpp
7  AUTHOR: John DiCamillo
8 
9 */
10 
11 #include "MemDebug.h"
12 #include "Game.h"
13 #include "Mouse.h"
14 #include "Universe.h"
15 #include "Screen.h"
16 #include "Window.h"
17 #include "EventDispatch.h"
18 #include "Color.h"
19 #include "DataLoader.h"
20 #include "Keyboard.h"
21 #include "Pcx.h"
22 #include "Resource.h"
23 #include "Bitmap.h"
24 #include "MachineInfo.h"
25 #include "Video.h"
26 #include "VideoFactory.h"
27 #include "VideoSettings.h"
28 #include "AviFile.h"
29 #include "ContentBundle.h"
30 
31 // +--------------------------------------------------------------------+
32 
33 FILE* ErrLog = 0;
34 int ErrLine = 0;
35 char ErrBuf[256];
36 
37 Game* game = 0;
38 
39 bool Game::active = false;
40 bool Game::paused = false;
41 bool Game::server = false;
42 bool Game::show_mouse = false;
43 DWORD Game::base_game_time = 0;
44 DWORD Game::real_time = 0;
45 DWORD Game::game_time = 0;
46 DWORD Game::time_comp = 1;
47 DWORD Game::frame_number = 0;
48 
49 const int VIDEO_FPS = 30;
50 const double MAX_FRAME_TIME_VIDEO = 1.0 / (double) VIDEO_FPS;
51 const double MAX_FRAME_TIME_NORMAL = 1.0 / 5.0;
52 const double MIN_FRAME_TIME_NORMAL = 1.0 / 60.0;
53 
56 
57 char Game::panicbuf[256];
58 
59 static LARGE_INTEGER perf_freq;
60 static LARGE_INTEGER perf_cnt1;
61 static LARGE_INTEGER perf_cnt2;
62 
63 // +--------------------------------------------------------------------+
64 
66 : world(0), video_factory(0), video(0), video_settings(0), soundcard(0),
67 gamma(128), max_tex_size(2048), screen(0), totaltime(0),
68 hInst(0), hwnd(0), frame_rate(0), frame_count(0), frame_count0(0),
69 frame_time(0), frame_time0(0), gui_seconds(0), content(0),
70 status(Game::OK), exit_code(0), window_style(0), avi_file(0)
71 {
72  if (!game) {
73  panicbuf[0] = 0;
74  game = this;
75  ZeroMemory(ErrBuf, 256);
76 
77  video_settings = new(__FILE__,__LINE__) VideoSettings;
78 
79  is_windowed = false;
80  is_active = false;
81  is_device_lost = false;
82  is_minimized = false;
83  is_maximized = false;
84  ignore_size_change = false;
85  is_device_initialized = false;
86  is_device_restored = false;
87  }
88  else
89  status = TOO_MANY;
90 }
91 
93 {
94  if (game == this)
95  game = 0;
96 
97  delete content;
98  delete world;
99  delete screen;
100  delete video_factory;
101  delete video;
102  delete soundcard;
103  delete video_settings;
104  delete avi_file;
105 
106  if (status == EXIT)
107  ShowStats();
108 }
109 
110 // +--------------------------------------------------------------------+
111 
112 HINSTANCE Game::GetHINST()
113 {
114  if (game)
115  return game->hInst;
116 
117  return 0;
118 }
119 
121 {
122  if (game)
123  return game->hwnd;
124 
125  return 0;
126 }
127 
129 {
130  if (game)
131  return game->is_windowed;
132 
133  return false;
134 }
135 
136 // +--------------------------------------------------------------------+
137 
138 Text
139 Game::GetText(const char* key)
140 {
141  if (game && game->content && game->content->IsLoaded())
142  return game->content->GetText(key);
143 
144  return key;
145 }
146 
147 // +--------------------------------------------------------------------+
148 
149 int
151 {
152  if (game)
153  return game->gamma;
154 
155  return 0;
156 }
157 
158 void
160 {
161  if (game) {
162  game->gamma = g;
163 
164  if (game->video)
165  game->video->SetGammaLevel(g);
166  }
167 }
168 
169 int
171 {
172  if (game && game->video) {
173  int max_vid_size = game->video->MaxTexSize();
174  return max_vid_size < game->max_tex_size ?
175  max_vid_size : game->max_tex_size;
176  }
177  else if (Video::GetInstance()) {
178  return Video::GetInstance()->MaxTexSize();
179  }
180 
181  return 256;
182 }
183 
184 int
186 {
187  if (game && game->video) {
188  return game->video->MaxTexAspect();
189  }
190  else if (Video::GetInstance()) {
191  return Video::GetInstance()->MaxTexAspect();
192  }
193 
194  return 1;
195 }
196 
197 void
199 {
200  if (game && n >= 64 && n <= 4096)
201  game->max_tex_size = n;
202 }
203 
204 bool
205 Game::DisplayModeSupported(int w, int h, int bpp)
206 {
207  return game && game->video && game->video->IsModeSupported(w,h,bpp);
208 }
209 
210 double
212 {
213  if (game)
214  return game->frame_rate;
215 
216  return 0;
217 }
218 
219 double
221 {
222  if (game)
223  return game->seconds;
224 
225  return 0;
226 }
227 
228 double
230 {
231  if (game)
232  return game->gui_seconds;
233 
234  return 0;
235 }
236 
237 // +--------------------------------------------------------------------+
238 
239 bool
240 Game::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow)
241 {
242  status = OK;
243  hInst = hi;
244 
245  Print(" Initializing Game\n");
246 
247  stats.Clear();
248 
249  if (!InitApplication(hInst)) { // Initialize shared things
250  Panic("Could not initialize the application.");
252  }
253 
254  if (status == OK && !video_settings) {
255  Panic("No video settings specified");
257  }
258 
259  if (status == OK) {
260  static int os_version = MachineInfo::GetPlatform();
261 
262  if (os_version == MachineInfo::OS_WIN95 || os_version == MachineInfo::OS_WIN98) {
263  Panic(" Windows 95 and 98 are no longer supported. Please update to Windows XP or higher.");
265  } else if (os_version == MachineInfo::OS_WINNT) {
266  Panic(" D3D not available under WinNT 4");
269  Panic(" Insufficient DirectX detected (Dx9 IS REQUIRED)");
271  }
272 
273  Print(" Gamma Level = %d\n", gamma);
274  }
275 
276  if (status == OK) {
277  Print("\n Initializing instance...\n");
278  // Perform initializations that apply to a specific instance
279  if (!InitInstance(hInst, nCmdShow)) {
280  Panic("Could not initialize the instance.");
282  }
283  }
284 
285  if (status == OK) {
286  Print(" Initializing content...\n");
287  InitContent();
288 
289  Print(" Initializing game...\n");
290  if (!InitGame()) {
291  if (!panicbuf[0])
292  Panic("Could not initialize the game.");
294  }
295  }
296 
297  return status == OK;
298 }
299 
300 // +--------------------------------------------------------------------+
301 
302 bool
303 Game::InitApplication(HINSTANCE hInstance)
304 {
305  WNDCLASS wc;
306  LOGBRUSH brush = { BS_SOLID, RGB(0,0,0), 0 };
307 
308  if (server)
309  brush.lbColor = RGB(255,255,255);
310 
311  // Fill in window class structure with parameters that
312  // describe the main window.
313  wc.style = CS_HREDRAW | CS_VREDRAW;
314  wc.lpfnWndProc = (WNDPROC) WndProc;
315  wc.cbClsExtra = 0;
316  wc.cbWndExtra = 0;
317  wc.hInstance = hInstance;
318  wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100));
319  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
320 
321  wc.hbrBackground = CreateBrushIndirect(&brush);
322  wc.lpszMenuName = app_name;
323  wc.lpszClassName = app_name;
324 
325  // Register the window class and return success/failure code.
326  if (RegisterClass(&wc) == 0) {
327  DWORD err = GetLastError();
328 
329  if (err == 1410) // class already exists, this is OK
330  return true;
331 
332  else
333  Print("WARNING: Register Window Class: %08x\n", err);
334  }
335 
336  return true;
337 }
338 
339 // +--------------------------------------------------------------------+
340 
341 bool
342 Game::InitInstance(HINSTANCE hInstance, int nCmdShow)
343 {
344  hInst = hInstance;
345 
346  // initialize the game timer:
347  base_game_time = 0;
348  QueryPerformanceFrequency(&perf_freq);
349  QueryPerformanceCounter(&perf_cnt1);
350 
351  // center window on display:
352  int screenx = GetSystemMetrics(SM_CXSCREEN);
353  int screeny = GetSystemMetrics(SM_CYSCREEN);
354  int x_offset = 0;
355  int y_offset = 0;
356  int s_width = 800;
357  int s_height = 600;
358 
359  if (server) {
360  s_width = 320;
361  s_height = 200;
362  }
363 
364  else if (video_settings) {
365  s_width = video_settings->window_width;
366  s_height = video_settings->window_height;
367  }
368 
369  if (s_width < screenx)
370  x_offset = (screenx - s_width) / 2;
371 
372  if (s_height < screeny)
373  y_offset = (screeny - s_height) / 2;
374 
375  // Create a main window for this application instance
376  RECT rctmp;
377  rctmp.left = x_offset;
378  rctmp.top = y_offset;
379  rctmp.right = x_offset + s_width;
380  rctmp.bottom = y_offset + s_height;
381 
382  window_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
383  WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE;
384 
385  AdjustWindowRect(&rctmp, window_style, 1);
386 
387  hwnd = CreateWindow(
388  app_name, // Class name
389  app_name, // Caption
390 
391  window_style,
392 
393  x_offset, // Position
394  y_offset,
395 
396  rctmp.right - rctmp.left, // Size
397  rctmp.bottom - rctmp.top,
398 
399  0, // Parent window (no parent)
400  0, // use class menu
401  hInst, // handle to window instance
402  0); // no params to pass on
403 
404  // If window could not be created, return "failure"
405  if (!hwnd) {
406  Panic("Could not create window\n");
407  return false;
408  }
409 
410  Print(" Window created.\n");
411 
412  // Make the window visible and draw it
413  ShowWindow(hwnd, nCmdShow); // Show the window
414  UpdateWindow(hwnd); // Sends WM_PAINT message
415 
416  // Save window properties
417  window_style = GetWindowLong(hwnd, GWL_STYLE);
418  GetWindowRect(hwnd, &bounds_rect);
419  GetClientRect(hwnd, &client_rect);
420 
421  // Use client area to set video window size
424 
425  Print(" Instance initialized.\n");
426  return true;
427 }
428 
429 // +--------------------------------------------------------------------+
430 
431 bool
433 {
434  if (server) return true;
435 
436  // create a video factory, and video object:
437  video_factory = new(__FILE__,__LINE__) VideoFactory(hwnd);
438 
439  if (video_factory) {
440  Print(" Init Video...\n");
441  Print(" Request %s mode\n", video_settings->GetModeDescription());
442 
444 
445  if (video) {
446  if (!video->IsHardware()) {
447  video_factory->DestroyVideo(video);
448  video = 0;
449 
450  Panic("3D Hardware Not Found");
451  }
452 
453  // save a copy of the device-specific video settings:
454  else if (video->GetVideoSettings()) {
455  *video_settings = *video->GetVideoSettings();
457  }
458  }
459 
461  }
462 
463  return (video && video->Status() == Video::VIDEO_OK);
464 }
465 
466 // +--------------------------------------------------------------------+
467 
468 bool
470 {
471  if (server) return true;
472  if (!video_factory) return InitVideo();
473 
474  Print(" Reset Video...\n");
475  Print(" Request %s mode\n", video_settings->GetModeDescription());
476 
477  delete screen;
478 
479  if (video && !video->Reset(video_settings)) {
482  }
483 
484  if (!video || video->Status() != Video::VIDEO_OK) {
485  Panic("Could not re-create Video Interface.");
486  return false;
487  }
488 
489  Print(" Re-created video object.\n");
490 
491  // save a copy of the device-specific video settings:
492  if (video->GetVideoSettings()) {
495  }
496 
498 
499  screen = new(__FILE__,__LINE__) Screen(video);
500  if (!screen) {
501  Panic("Could not re-create Screen object.");
502  return false;
503  }
504 
505  Print(" Re-created screen object.\n");
506 
507  if (!screen->SetBackgroundColor(Color::Black))
508  Print(" WARNING: could not set video background color to Black\n");
509 
510  screen->ClearAllFrames(true);
512 
513  Print(" Re-established requested video parameters.\n");
514 
516  Print(" Refreshed texture bitmaps.\n\n");
517  return true;
518 }
519 
520 // +--------------------------------------------------------------------+
521 
522 bool
524 {
525  if (!video || !video_settings) return false;
526  if (!is_windowed) return false;
527  if (ignore_size_change) return true;
528 
529  HRESULT hr = S_OK;
530  RECT client_old;
531 
532  client_old = client_rect;
533 
534  // Update window properties
535  GetWindowRect(hwnd, &bounds_rect);
536  GetClientRect(hwnd, &client_rect);
537 
538  if (client_old.right - client_old.left !=
539  client_rect.right - client_rect.left ||
540  client_old.bottom - client_old.top !=
541  client_rect.bottom - client_rect.top) {
542 
543  // A new window size will require a new backbuffer
544  // size, so the 3D structures must be changed accordingly.
545  Pause(true);
546 
547  video_settings->is_windowed = true;
550 
551  ::Print("ResizeVideo() %d x %d\n", video_settings->window_width, video_settings->window_height);
552 
553  if (video) {
555  }
556 
557  Pause(false);
558  }
559 
560  // save a copy of the device-specific video settings:
561  if (video->GetVideoSettings()) {
564  }
565 
567 
568  return hr == S_OK;
569 }
570 
571 bool
573 {
574  bool result = false;
575 
576  if (video && video_settings) {
577  Pause(true);
578  ignore_size_change = true;
579 
580  // Toggle the windowed state
583 
584  // Prepare window for windowed/fullscreen change
586 
587  // Reset the 3D device
588  if (!video->Reset(video_settings)) {
589  // reset failed, try to restore...
590  ignore_size_change = false;
591 
592  if (!is_windowed) {
593  // Restore window type to windowed mode
596 
598 
599  SetWindowPos(hwnd,
600  HWND_NOTOPMOST,
601  bounds_rect.left,
602  bounds_rect.top,
603  bounds_rect.right - bounds_rect.left,
604  bounds_rect.bottom - bounds_rect.top,
605  SWP_SHOWWINDOW);
606  }
607 
608  ::Print("Unable to toggle %s fullscreen mode.\n", is_windowed ? "into" : "out of");
609  }
610 
611  else {
612  ignore_size_change = false;
613 
614  // When moving from fullscreen to windowed mode, it is important to
615  // adjust the window size after resetting the device rather than
616  // beforehand to ensure that you get the window size you want. For
617  // example, when switching from 640x480 fullscreen to windowed with
618  // a 1000x600 window on a 1024x768 desktop, it is impossible to set
619  // the window size to 1000x600 until after the display mode has
620  // changed to 1024x768, because windows cannot be larger than the
621  // desktop.
622 
623  if (is_windowed) {
624  SetWindowPos(hwnd,
625  HWND_NOTOPMOST,
626  bounds_rect.left,
627  bounds_rect.top,
628  bounds_rect.right - bounds_rect.left,
629  bounds_rect.bottom - bounds_rect.top,
630  SWP_SHOWWINDOW);
631  }
632 
633  GetClientRect(hwnd, &client_rect); // Update our copy
634  Pause(false);
635 
636  if (is_windowed)
639 
640  else
643 
644  result = true;
645  }
646  }
647 
648  return result;
649 }
650 
651 bool
653 {
654  if (is_windowed) {
655  // Set windowed-mode style
656  SetWindowLong(hwnd, GWL_STYLE, window_style);
657  if (hmenu != NULL) {
658  SetMenu(hwnd, hmenu);
659  hmenu = NULL;
660  }
661  }
662  else {
663  // Set fullscreen-mode style
664  SetWindowLong(hwnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE);
665  if (hmenu == NULL) {
666  hmenu = GetMenu(hwnd);
667  SetMenu(hwnd, NULL);
668  }
669  }
670 
671  return true;
672 }
673 
674 
675 // +--------------------------------------------------------------------+
676 
677 bool
679 {
680  if (server) {
681  Print(" InitGame() - server mode.\n");
682  }
683 
684  else {
685  if (!SetupPalette()) {
686  Panic("Could not set up the palette.");
687  return false;
688  }
689 
690  Print(" Palette loaded.\n");
691 
692  if (!InitVideo() || !video || video->Status() != Video::VIDEO_OK) {
693  if (!panicbuf[0])
694  Panic("Could not create the Video Interface.");
695  return false;
696  }
697 
698  Print(" Created video object.\n");
699 
701 
702  screen = new(__FILE__,__LINE__) Screen(video);
703  if (!screen) {
704  if (!panicbuf[0])
705  Panic("Could not create the Screen object.");
706  return false;
707  }
708 
709  Print(" Created screen object.\n");
710 
712  Print(" WARNING: could not set video background color to Black\n");
713  screen->ClearAllFrames(true);
714 
716 
717  Print(" Established requested video parameters.\n\n");
718  }
719 
720  return true;
721 }
722 
723 
724 // +--------------------------------------------------------------------+
725 
726 bool
728 {
729  DataLoader* loader = DataLoader::GetLoader();
730  List<Text> bundles;
731 
732  loader->SetDataPath("Content/");
733  loader->ListFiles("content*", bundles);
734 
735  ListIter<Text> iter = bundles;
736  while (++iter) {
737  Text* filename = iter.value();
738  int n = filename->indexOf('_');
739 
740  if (n > 0) {
741  Locale::ParseLocale(filename->data() + n);
742  }
743  else {
744  delete content;
745  content = new(__FILE__,__LINE__) ContentBundle("content", 0);
746  }
747  }
748 
749  loader->SetDataPath(0);
750  return true;
751 }
752 
753 void
755 {
756  if (game) {
757  DataLoader* loader = DataLoader::GetLoader();
758  loader->SetDataPath("Content/");
759  delete game->content;
760 
761  game->content = new(__FILE__,__LINE__) ContentBundle("content", locale);
762 
763  loader->SetDataPath(0);
764  }
765 }
766 
767 // +--------------------------------------------------------------------+
768 
769 bool
771 {
774  return true;
775  }
776 
777  return false;
778 }
779 
780 // +--------------------------------------------------------------------+
781 
782 bool
783 Game::LoadPalette(PALETTEENTRY* pal, BYTE* inv)
784 {
785  char palheader[32];
786  struct {
787  WORD Version;
788  WORD NumberOfEntries;
789  PALETTEENTRY Entries[256];
790 
791  } Palette = { 0x300, 256 };
792 
793  DataLoader* loader = DataLoader::GetLoader();
794  BYTE* block;
795  char fname[256];
796  sprintf_s(fname, "%s.pal", palette_name);
797 
798  if (!loader->LoadBuffer(fname, block)) {
799  Print(" Could not open file '%s'\n", fname);
800  return false;
801  }
802 
803  memcpy(&palheader, block, 24);
804  memcpy((char*) Palette.Entries, (block+24), 256*4);
805 
806  for (int i = 0; i < 256; i++) {
807  *pal++ = Palette.Entries[i];
808  }
809 
810  loader->ReleaseBuffer(block);
811 
812  sprintf_s(fname, "%s.ipl", palette_name);
813  int size = loader->LoadBuffer(fname, block);
814  if (size < 32768) {
815  Print(" Could not open file '%s'\n", fname);
816  return false;
817  }
818 
819  memcpy(inv, block, 32768);
820  loader->ReleaseBuffer(block);
821 
822  return true;
823 }
824 
825 // +--------------------------------------------------------------------+
826 
827 int
829 {
830  MSG msg;
831 
832  status = RUN;
833  Print("\n");
834  Print("+====================================================================+\n");
835  Print("| RUN |\n");
836  Print("+====================================================================+\n");
837 
838  // Polling messages from event queue until quit
839  while (status < EXIT) {
840  if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
841  if (msg.message == WM_QUIT)
842  break;
843 
844  TranslateMessage(&msg);
845  DispatchMessage(&msg);
846  }
847  else {
848  if (ProfileGameLoop())
849  WaitMessage();
850  }
851  }
852 
853  return exit_code ? exit_code : msg.wParam;
854 }
855 
856 // +--------------------------------------------------------------------+
857 
858 void
860 {
861  Print("\n\n*** Game::Exit()\n");
862  status = EXIT;
863 }
864 
865 void
866 Game::Panic(const char* msg)
867 {
868  if (msg) Print("*** PANIC: %s\n", msg);
869  else Print("*** PANIC! ***\n");
870 
871  if (!msg) msg = "Unspecified fatal error.";
872  sprintf_s(panicbuf, "%s\nThis game will now terminate.", msg);
873 
874  if (game) {
875  game->status = PANIC;
876  }
877 }
878 
879 // +--------------------------------------------------------------------+
880 
881 void
883 {
884  active = f;
885 
886  if (active && video)
888 }
889 
890 // +--------------------------------------------------------------------+
891 
892 void
893 Game::Pause(bool f)
894 {
895  if (f) {
896  if (soundcard)
897  soundcard->Pause();
898  paused = true;
899  }
900  else {
901  if (soundcard)
902  soundcard->Resume();
903  paused = false;
904  }
905 }
906 
907 // +--------------------------------------------------------------------+
908 
909 bool ProfileGameLoop(void)
910 {
911  return game->GameLoop();
912 }
913 
914 bool
916 {
917  bool wait_for_windows_events = true;
918 
919  if (active && !paused) {
920  if (!server) {
921  // Route Events to EventTargets
923  if (ed)
924  ed->Dispatch();
925  }
926 
927  UpdateWorld();
928  GameState();
929 
930  if (!server) {
931  UpdateScreen();
932  CollectStats();
933  }
934 
935  wait_for_windows_events = false;
936  }
937  else if (active && paused) {
938  if (GetKey()=='P')
939  Pause(false);
940  }
941 
942  QueryPerformanceCounter(&perf_cnt2);
943 
944  double freq = (double) (perf_freq.QuadPart);
945  double msec = (double) (perf_cnt2.QuadPart - perf_cnt1.QuadPart);
946 
947  msec /= freq;
948  msec *= 1000.0;
949 
950  if (msec < 1)
951  msec = 1;
952 
953  real_time += (DWORD) msec;
954 
955  frame_number++;
956  Mouse::w = 0;
957 
958  perf_cnt1 = perf_cnt2;
959 
960  return wait_for_windows_events;
961 }
962 
963 // +--------------------------------------------------------------------+
964 
965 void
967 {
968  long new_time = real_time;
969  double delta = new_time - frame_time;
970  gui_seconds = delta * 0.001;
972 
973  if (time_comp == 1)
974  {
975  if (delta < max_frame_length * 1000)
976  seconds = delta * 0.001;
977  }
978  else
979  {
980  seconds = time_comp * delta * 0.001;
981  }
982 
983  frame_time = new_time;
984  game_time += (DWORD) (seconds * 1000);
985 
986  if (world)
988 }
989 
990 // +--------------------------------------------------------------------+
991 
992 void
994 {
995 }
996 
997 // +--------------------------------------------------------------------+
998 
999 void
1001 {
1002  if (!screen || !video) return;
1003 
1004  if (screen->Refresh()) {
1005  if (Keyboard::KeyDown(VK_F12)) {
1006  if (Keyboard::KeyDown(VK_SHIFT)) {
1007  if (!avi_file) {
1008  AVICapture(); // begin capturing
1010  }
1011  else {
1012  delete avi_file; // end capture;
1013  avi_file = 0;
1015  }
1016  }
1017  else {
1018  if (!avi_file) {
1019  ScreenCapture();
1020  }
1021  else {
1022  delete avi_file; // end capture;
1023  avi_file = 0;
1025  }
1026  }
1027  }
1028  else if (avi_file) {
1029  AVICapture(); // continue capturing...
1030  }
1031 
1032  video->Present();
1033  }
1034  else {
1035  Panic("Screen refresh failed.");
1036  }
1037 }
1038 
1039 // +--------------------------------------------------------------------+
1040 
1041 Game*
1043 {
1044  return game;
1045 }
1046 
1047 Video*
1049 {
1050  if (game)
1051  return game->video;
1052 
1053  return 0;
1054 }
1055 
1056 Color
1058 {
1059  if (game)
1060  return game->screen_color;
1061 
1062  return Color::Black;
1063 }
1064 
1065 void
1067 {
1068  if (game) {
1069  game->screen_color = c;
1070 
1071  if (game->screen)
1072  game->screen->SetBackgroundColor(c);
1073  }
1074 }
1075 
1076 int
1078 {
1079  if (game && game->video)
1080  return game->video->Width();
1081 
1082  return 0;
1083 }
1084 
1085 int
1087 {
1088  if (game && game->video)
1089  return game->video->Height();
1090 
1091  return 0;
1092 }
1093 
1094 // +--------------------------------------------------------------------+
1095 
1096 void
1097 Game::ScreenCapture(const char* name)
1098 {
1099  if (server || !video || !screen) return;
1100 
1101  static DWORD last_shot = 0;
1102  static DWORD shot_num = 1;
1103  DataLoader* loader = DataLoader::GetLoader();
1104  char filename[256];
1105 
1106  if (!name && (real_time - last_shot) < 1000)
1107  return;
1108 
1109  // try not to overwrite existing screen shots...
1110  if (loader) {
1111  bool use_file_sys = loader->IsFileSystemEnabled();
1112  loader->UseFileSystem(true);
1113  loader->SetDataPath(0);
1114  List<Text> shot_list;
1115  loader->ListFiles("*.PCX", shot_list);
1116  loader->UseFileSystem(use_file_sys);
1117 
1118  for (int i = 0; i < shot_list.size(); i++) {
1119  Text* s = shot_list[i];
1120  int n = 0;
1121 
1122  sscanf_s(s->data()+1, "%d", &n);
1123  if (shot_num <= (DWORD) n)
1124  shot_num = n+1;
1125  }
1126 
1127  shot_list.destroy();
1128  }
1129 
1130  if (name)
1131  strcpy_s(filename, name);
1132  else
1133  sprintf_s(filename, "A%d.PCX", shot_num++);
1134 
1135  Bitmap bmp;
1136 
1137  if (video && video->Capture(bmp)) {
1138  PcxImage pcx(bmp.Width(), bmp.Height(), (LPDWORD) bmp.HiPixels());
1139  pcx.Save((char*) filename);
1140  }
1141 
1142  last_shot = real_time;
1143 }
1144 
1145 // +--------------------------------------------------------------------+
1146 
1147 void
1148 Game::AVICapture(const char* name)
1149 {
1150  if (server || !video || !screen) return;
1151 
1152  if (!avi_file) {
1153  char filename[256];
1154  Bitmap bmp;
1155  DataLoader* loader = DataLoader::GetLoader();
1156  DWORD avi_num = 1;
1157 
1158  // try not to overwrite existing screen shots...
1159  if (loader) {
1160  bool use_file_sys = loader->IsFileSystemEnabled();
1161  loader->UseFileSystem(true);
1162  loader->SetDataPath(0);
1163  List<Text> avi_list;
1164  loader->ListFiles("*.avi", avi_list);
1165  loader->UseFileSystem(use_file_sys);
1166 
1167  for (int i = 0; i < avi_list.size(); i++) {
1168  Text* s = avi_list[i];
1169  int n = 0;
1170 
1171  sscanf_s(s->data()+1, "%d", &n);
1172  if (avi_num <= (DWORD) n)
1173  avi_num = n+1;
1174  }
1175 
1176  avi_list.destroy();
1177  }
1178 
1179  if (name)
1180  strcpy_s(filename, name);
1181  else
1182  sprintf_s(filename, "A%d.avi", avi_num);
1183 
1184  if (video && video->Capture(bmp)) {
1185  //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2);
1186  avi_file = new(__FILE__,__LINE__) AviFile(filename, Rect(0,0,bmp.Width(),bmp.Height()), VIDEO_FPS);
1187  }
1188 
1189  }
1190 
1191  else {
1192  Bitmap bmp;
1193 
1194  if (video && video->Capture(bmp)) {
1195  //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2);
1196  avi_file->AddFrame(bmp);
1197  }
1198  }
1199 }
1200 
1201 
1202 
1203 // +--------------------------------------------------------------------+
1204 
1205 void
1207 {
1208  frame_count++;
1209 
1210  if (!totaltime) totaltime = real_time;
1211 
1212  if (frame_time - frame_time0 > 200) {
1216  }
1217 
1218  if (video) {
1224 
1228  }
1229 }
1230 
1231 // +--------------------------------------------------------------------+
1232 
1233 void
1235 {
1236  if (server) return;
1237 
1239 
1240  Print("\n");
1241  Print("Performance Data:\n");
1242  Print("-----------------\n");
1243 
1244  Print(" Time: %d msec\n", totaltime);
1245  Print(" Frames: %d\n", stats.nframe);
1246  Print(" Polys Rendered: %d\n", stats.total_polys);
1247  Print(" Lines Rendered: %d\n", stats.total_lines);
1248  Print(" Verts Rendered: %d\n", stats.total_verts);
1249  Print(" Render Calls: %d\n", stats.ncalls);
1250  Print("\n");
1251 
1252  Print("Performance Statistics:\n");
1253  Print("-----------------------\n");
1254 
1255  Print(" Frames/Second: %.2f\n", (stats.nframe * 1000.0) / totaltime);
1256  Print(" Polys/Frame: %.2f\n", (double) stats.total_polys / (double) stats.nframe);
1257  Print(" Polys/Call: %.2f\n", (double) stats.total_polys / (double) stats.ncalls);
1258  Print(" Polys/Second: %.2f\n", (stats.total_polys * 1000.0) / totaltime);
1259  Print(" Lines/Second: %.2f\n", (stats.total_lines * 1000.0) / totaltime);
1260  Print(" Verts/Second: %.2f\n", (stats.total_verts * 1000.0) / totaltime);
1261 
1262  Print("\n");
1263 }
1264 
1265 // +====================================================================+
1266 // WndProc
1267 // +====================================================================+
1268 
1269 #ifndef WM_MOUSEWHEEL
1270 #define WM_MOUSEWHEEL 0x20A
1271 #endif
1272 
1273 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM uParam, LPARAM lParam)
1274 {
1275  switch (message) {
1276  case WM_SYSKEYDOWN:
1277  if (uParam == VK_TAB || uParam == VK_F4)
1278  return DefWindowProc(hwnd, message, uParam, lParam);
1279 
1280  return 0;
1281 
1282  case WM_MENUCHAR:
1283  return MNC_CLOSE << 16;
1284 
1285  case WM_ACTIVATEAPP:
1286  // Keep track of whether or not the app is in the foreground
1287  if (game)
1288  game->Activate(uParam?true:false);
1289  break;
1290 
1291  case WM_PAINT:
1292  if (!game || !game->OnPaint())
1293  return DefWindowProc(hwnd, message, uParam, lParam);
1294  break;
1295 
1296  case WM_SETCURSOR:
1297  if (Game::ShowMouse()) {
1298  return DefWindowProc(hwnd, message, uParam, lParam);
1299  }
1300  else {
1301  // hide the windows mouse cursor
1302  SetCursor(NULL);
1303  return 1;
1304  }
1305  break;
1306 
1307  case WM_ENTERSIZEMOVE:
1308  // Halt frame movement while the app is sizing or moving
1309  if (game)
1310  game->Pause(true);
1311  break;
1312 
1313  case WM_SIZE:
1314  // Pick up possible changes to window style due to maximize, etc.
1315  if (game && game->hwnd != NULL ) {
1316  game->window_style = GetWindowLong(game->hwnd, GWL_STYLE );
1317 
1318  if (uParam == SIZE_MINIMIZED) {
1319  game->Pause(true); // Pause while we're minimized
1320  game->is_minimized = true;
1321  game->is_maximized = false;
1322  }
1323 
1324  else if (uParam == SIZE_MAXIMIZED) {
1325  if (game->is_minimized)
1326  game->Pause(false); // Unpause since we're no longer minimized
1327 
1328  game->is_minimized = false;
1329  game->is_maximized = true;
1330  game->ResizeVideo();
1331  }
1332 
1333  else if (uParam == SIZE_RESTORED) {
1334  if (game->is_maximized) {
1335  game->is_maximized = false;
1336  game->ResizeVideo();
1337  }
1338 
1339  else if (game->is_minimized) {
1340  game->Pause(false); // Unpause since we're no longer minimized
1341  game->is_minimized = false;
1342  game->ResizeVideo();
1343  }
1344  else {
1345  // If we're neither maximized nor minimized, the window size
1346  // is changing by the user dragging the window edges. In this
1347  // case, we don't reset the device yet -- we wait until the
1348  // user stops dragging, and a WM_EXITSIZEMOVE message comes.
1349  }
1350  }
1351  }
1352  break;
1353 
1354  case WM_EXITSIZEMOVE:
1355  if (game) {
1356  game->Pause(false);
1357  game->ResizeVideo();
1358  }
1359  break;
1360 
1361 
1362  case WM_ENTERMENULOOP:
1363  if (game)
1364  game->Pause(true);
1365  break;
1366 
1367  case WM_EXITMENULOOP:
1368  if (game)
1369  game->Pause(false);
1370  break;
1371 
1372  /*
1373 case WM_HELP:
1374  if (game)
1375  return game->OnHelp();
1376  break;
1377 */
1378 
1379  case WM_KEYDOWN:
1380  BufferKey(uParam);
1381  return 0;
1382 
1383  case WM_DESTROY:
1384  PostQuitMessage(0);
1385  break;
1386 
1387  case WM_MOUSEMOVE:
1388  Mouse::x = LOWORD(lParam);
1389  Mouse::y = HIWORD(lParam);
1390  break;
1391 
1392  case WM_LBUTTONDOWN:
1393  Mouse::l = 1;
1394  break;
1395 
1396  case WM_LBUTTONDBLCLK:
1397  Mouse::l = 2;
1398  break;
1399 
1400  case WM_LBUTTONUP:
1401  Mouse::l = 0;
1402  break;
1403 
1404  case WM_MBUTTONDOWN:
1405  Mouse::m = 1;
1406  break;
1407 
1408  case WM_MBUTTONDBLCLK:
1409  Mouse::m = 2;
1410  break;
1411 
1412  case WM_MBUTTONUP:
1413  Mouse::m = 0;
1414  break;
1415 
1416  case WM_RBUTTONDOWN:
1417  Mouse::r = 1;
1418  break;
1419 
1420  case WM_RBUTTONDBLCLK:
1421  Mouse::r = 2;
1422  break;
1423 
1424  case WM_RBUTTONUP:
1425  Mouse::r = 0;
1426  break;
1427 
1428  case WM_MOUSEWHEEL:
1429  {
1430  int w = (int) (uParam >> 16);
1431  if (w > 32000) w -= 65536;
1432  Mouse::w += w;
1433  }
1434  break;
1435 
1436  case WM_CLOSE:
1437  if (game) // && game->Server())
1438  game->Exit();
1439  break;
1440 
1441  default:
1442  return DefWindowProc(hwnd, message, uParam, lParam);
1443  }
1444 
1445  return 0;
1446 }
1447 
1448 // +====================================================================+
1449 
1450 const int MAX_KEY_BUF = 512;
1451 static int vkbuf[MAX_KEY_BUF];
1452 static int vkshiftbuf[MAX_KEY_BUF];
1453 static int vkins = 0;
1454 static int vkext = 0;
1455 
1456 void
1458 {
1460  vkins = vkext = 0;
1461 }
1462 
1463 void
1464 BufferKey(int vkey)
1465 {
1466  if (vkey < 1) return;
1467 
1468  int shift = 0;
1469 
1470  if (GetAsyncKeyState(VK_SHIFT))
1471  shift |= 1;
1472 
1473  if (GetAsyncKeyState(VK_CONTROL))
1474  shift |= 2;
1475 
1476  if (GetAsyncKeyState(VK_MENU))
1477  shift |= 4;
1478 
1479  vkbuf[vkins] = vkey;
1480  vkshiftbuf[vkins++] = shift;
1481 
1482  if (vkins >= MAX_KEY_BUF)
1483  vkins = 0;
1484 
1485  if (vkins == vkext) {
1486  vkext++;
1487  if (vkext >= MAX_KEY_BUF)
1488  vkext = 0;
1489  }
1490 }
1491 
1492 int
1494 {
1495  if (vkins == vkext) return 0;
1496 
1497  int result = vkbuf[vkext++];
1498  if (vkext >= MAX_KEY_BUF)
1499  vkext = 0;
1500 
1501  return result;
1502 }
1503 
1504 int
1505 GetKeyPlus(int& key, int& shift)
1506 {
1507  if (vkins == vkext) return 0;
1508 
1509  key = vkbuf[vkext];
1510  shift = vkshiftbuf[vkext++];
1511 
1512  if (vkext >= MAX_KEY_BUF)
1513  vkext = 0;
1514 
1515  return key;
1516 }
1517 
1518 // +====================================================================+
1519 
1520 void Print(const char* fmt, ...)
1521 {
1522  if (ErrLog) {
1523  vsprintf_s(ErrBuf, fmt, (char *)(&fmt+1));
1524 
1525  fprintf(ErrLog, ErrBuf);
1526  fflush(ErrLog);
1527  }
1528 }
1529 
1530 // +====================================================================+
1531 
1533 {
1534  if (game)
1535  return Game::RealTime();
1536 
1537  return timeGetTime();
1538 }
1539 
1541 {
1542  return real_time;
1543 }
1544 
1546 {
1547  return game_time;
1548 }
1549 
1551 {
1552  return time_comp;
1553 }
1554 
1556 {
1557  if (comp > 0 && comp <= 100)
1558  time_comp = comp;
1559 }
1560 
1562 {
1563  return frame_number;
1564 }
1565 
1567 {
1568  game_time = 0;
1569 }
1570 
1571 void Game::SkipGameTime(double seconds)
1572 {
1573  if (seconds > 0)
1574  game_time += (DWORD) (seconds * 1000);
1575 }