/src/C4GraphicsSystem.cpp

https://bitbucket.org/randrian/openclonk2 · C++ · 953 lines · 698 code · 84 blank · 171 comment · 181 complexity · bf2f6730bb8ea7f8994ef14389436a10 MD5 · raw file

  1. /*
  2. * OpenClonk, http://www.openclonk.org
  3. *
  4. * Copyright (c) 1998-2000, 2004, 2008 Matthes Bender
  5. * Copyright (c) 2001-2003, 2005-2009 Sven Eberhardt
  6. * Copyright (c) 2001 Michael K?ser
  7. * Copyright (c) 2005-2006, 2008 G?nther Brammer
  8. * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
  9. *
  10. * Portions might be copyrighted by other authors who have contributed
  11. * to OpenClonk.
  12. *
  13. * Permission to use, copy, modify, and/or distribute this software for any
  14. * purpose with or without fee is hereby granted, provided that the above
  15. * copyright notice and this permission notice appear in all copies.
  16. * See isc_license.txt for full license and disclaimer.
  17. *
  18. * "Clonk" is a registered trademark of Matthes Bender.
  19. * See clonk_trademark_license.txt for full license.
  20. */
  21. /* Operates viewports, message board and draws the game */
  22. #include <C4Include.h>
  23. #include <C4GraphicsSystem.h>
  24. #ifndef BIG_C4INCLUDE
  25. #include <C4Viewport.h>
  26. #include <C4Application.h>
  27. #include <C4Console.h>
  28. #include <C4Random.h>
  29. #include <C4SurfaceFile.h>
  30. #include <C4FullScreen.h>
  31. #include <C4Gui.h>
  32. #include <C4LoaderScreen.h>
  33. #include <C4Player.h>
  34. #include <C4SoundSystem.h>
  35. #include <C4MouseControl.h>
  36. #include <C4GraphicsResource.h>
  37. #include <C4Landscape.h>
  38. #include <C4Network2.h>
  39. #include <C4Game.h>
  40. #include <C4PlayerList.h>
  41. #include <C4GameObjects.h>
  42. #endif
  43. #include <StdPNG.h>
  44. C4GraphicsSystem::C4GraphicsSystem()
  45. {
  46. fViewportClassRegistered=false;
  47. Default();
  48. }
  49. C4GraphicsSystem::~C4GraphicsSystem()
  50. {
  51. Clear();
  52. }
  53. #ifdef _WIN32
  54. LRESULT APIENTRY ViewportWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  55. BOOL C4GraphicsSystem::RegisterViewportClass(HINSTANCE hInst)
  56. {
  57. // register landscape viewport class
  58. WNDCLASSEX WndClass;
  59. WndClass.cbSize=sizeof(WNDCLASSEX);
  60. WndClass.style = CS_DBLCLKS | CS_BYTEALIGNCLIENT;
  61. WndClass.lpfnWndProc = ViewportWinProc;
  62. WndClass.cbClsExtra = 0;
  63. WndClass.cbWndExtra = 0;
  64. WndClass.hInstance = hInst;
  65. WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
  66. WndClass.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
  67. WndClass.lpszMenuName = NULL;
  68. WndClass.lpszClassName = C4ViewportClassName;
  69. WndClass.hIcon = LoadIcon (hInst, MAKEINTRESOURCE (IDI_01_C4S) );
  70. WndClass.hIconSm = LoadIcon (hInst, MAKEINTRESOURCE (IDI_01_C4S) );
  71. if (!RegisterClassEx(&WndClass)) return FALSE;
  72. // register GUI dialog class
  73. return C4GUI::Dialog::RegisterWindowClass(hInst);
  74. }
  75. #endif
  76. bool C4GraphicsSystem::Init()
  77. {
  78. #ifdef _WIN32
  79. // Register viewport class
  80. if (!fViewportClassRegistered)
  81. if (!RegisterViewportClass(Application.GetInstance()))
  82. return false;
  83. fViewportClassRegistered=true;
  84. #endif
  85. // Init video module
  86. if (Config.Graphics.VideoModule)
  87. Video.Init(Application.DDraw->lpBack);
  88. // Success
  89. return true;
  90. }
  91. void C4GraphicsSystem::Clear()
  92. {
  93. // Clear message board
  94. MessageBoard.Clear();
  95. // Clear upper board
  96. UpperBoard.Clear();
  97. // clear loader
  98. if (pLoaderScreen) { delete pLoaderScreen; pLoaderScreen=NULL; }
  99. // Close viewports
  100. C4Viewport *next;
  101. while (FirstViewport)
  102. {
  103. next=FirstViewport->Next;
  104. delete FirstViewport;
  105. FirstViewport=next;
  106. }
  107. FirstViewport=NULL;
  108. // Clear video system
  109. Video.Clear();
  110. // No debug stuff
  111. DeactivateDebugOutput();
  112. }
  113. bool C4GraphicsSystem::SetPalette()
  114. {
  115. // Set primary palette by game palette
  116. if (!Application.DDraw->SetPrimaryPalette(::GraphicsResource.GamePalette, ::GraphicsResource.AlphaPalette)) return false;
  117. return true;
  118. }
  119. extern int32_t iLastControlSize,iPacketDelay;
  120. extern int32_t ControlQueueSize,ControlQueueDataSize;
  121. int32_t ScreenTick=0, ScreenRate=1;
  122. bool C4GraphicsSystem::StartDrawing()
  123. {
  124. // only if ddraw is ready
  125. if (!Application.DDraw) return false;
  126. if (!Application.DDraw->Active) return false;
  127. // only if application is active or windowed (if config allows)
  128. if (!Application.Active && (Application.isFullScreen || !Config.Graphics.RenderInactiveEM)) return false;
  129. // drawing OK
  130. return true;
  131. }
  132. void C4GraphicsSystem::FinishDrawing()
  133. {
  134. if (Application.isFullScreen) Application.DDraw->PageFlip();
  135. }
  136. void C4GraphicsSystem::Execute()
  137. {
  138. // activity check
  139. if (!StartDrawing()) return;
  140. bool fBGDrawn = false;
  141. // If lobby running, message board only (page flip done by startup message board)
  142. if (!::pGUI || !::pGUI->HasFullscreenDialog(true)) // allow for message board behind GUI
  143. if(::Network.isLobbyActive() || !Game.IsRunning)
  144. if (Application.isFullScreen)
  145. {
  146. // Message board
  147. if (iRedrawBackground) ClearFullscreenBackground();
  148. MessageBoard.Execute();
  149. if (!::pGUI || !C4GUI::IsActive())
  150. { FinishDrawing(); return; }
  151. fBGDrawn = true;
  152. }
  153. // fullscreen GUI?
  154. if (Application.isFullScreen && ::pGUI && C4GUI::IsActive() && (::pGUI->HasFullscreenDialog(false) || !Game.IsRunning))
  155. {
  156. if (!fBGDrawn && iRedrawBackground) ClearFullscreenBackground();
  157. ::pGUI->Render(!fBGDrawn);
  158. FinishDrawing();
  159. return;
  160. }
  161. // Fixed screen rate in old network
  162. ScreenRate = 1;
  163. // Background redraw
  164. if (Application.isFullScreen)
  165. if (iRedrawBackground)
  166. DrawFullscreenBackground();
  167. // Screen rate skip frame draw
  168. ScreenTick++; if (ScreenTick>=ScreenRate) ScreenTick=0;
  169. // Reset object audibility
  170. ::Objects.ResetAudibility();
  171. // some hack to ensure the mouse is drawn after a dialog close and before any
  172. // movement messages
  173. if (::pGUI && !C4GUI::IsActive())
  174. SetMouseInGUI(false, false);
  175. // Viewports
  176. for (C4Viewport *cvp=FirstViewport; cvp; cvp=cvp->Next)
  177. cvp->Execute();
  178. if (Application.isFullScreen)
  179. {
  180. // Upper board
  181. UpperBoard.Execute();
  182. // Message board
  183. MessageBoard.Execute();
  184. // Help & Messages
  185. DrawHelp();
  186. DrawHoldMessages();
  187. DrawFlashMessage();
  188. }
  189. // InGame-GUI
  190. if (::pGUI && C4GUI::IsActive())
  191. {
  192. ::pGUI->Render(false);
  193. }
  194. // Palette update
  195. if (fSetPalette) { SetPalette(); /*SetDarkColorTable();*/ fSetPalette=false; }
  196. // gamma update
  197. if (fSetGamma)
  198. {
  199. ApplyGamma();
  200. fSetGamma=false;
  201. }
  202. // Video record & status (fullsrceen)
  203. if (Application.isFullScreen)
  204. Video.Execute();
  205. // done
  206. FinishDrawing();
  207. }
  208. bool C4GraphicsSystem::CloseViewport(C4Viewport * cvp)
  209. {
  210. if (!cvp) return false;
  211. /*C4Viewport *next,*prev=NULL;
  212. for (C4Viewport *cvp2=FirstViewport; cvp2; cvp2=next)
  213. {
  214. next=cvp2->Next;
  215. if (cvp2 == cvp)
  216. {
  217. delete cvp;
  218. StartSoundEffect("CloseViewport");
  219. if (prev) prev->Next=next;
  220. else FirstViewport=next;
  221. }
  222. else
  223. prev=cvp2;
  224. }*/
  225. // Chop the start of the chain off
  226. if (FirstViewport == cvp)
  227. {
  228. FirstViewport = cvp->Next;
  229. delete cvp;
  230. StartSoundEffect("CloseViewport");
  231. }
  232. // Take out of the chain
  233. else for (C4Viewport * prev = FirstViewport; prev; prev = prev->Next)
  234. {
  235. if (prev->Next == cvp)
  236. {
  237. prev->Next = cvp->Next;
  238. delete cvp;
  239. StartSoundEffect("CloseViewport");
  240. }
  241. }
  242. // Recalculate viewports
  243. RecalculateViewports();
  244. // Done
  245. return true;
  246. }
  247. #ifdef _WIN32
  248. C4Viewport* C4GraphicsSystem::GetViewport(HWND hwnd)
  249. {
  250. for (C4Viewport *cvp=FirstViewport; cvp; cvp=cvp->Next)
  251. if (cvp->pWindow->hWindow==hwnd)
  252. return cvp;
  253. return NULL;
  254. }
  255. #endif
  256. bool C4GraphicsSystem::CreateViewport(int32_t iPlayer, bool fSilent)
  257. {
  258. // Create and init new viewport, add to viewport list
  259. int32_t iLastCount = GetViewportCount();
  260. C4Viewport *nvp = new C4Viewport;
  261. bool fOkay = false;
  262. if (Application.isFullScreen)
  263. fOkay = nvp->Init(iPlayer, false);
  264. else
  265. fOkay = nvp->Init(&Console,&Application,iPlayer);
  266. if (!fOkay) { delete nvp; return false; }
  267. C4Viewport *pLast;
  268. for (pLast=FirstViewport; pLast && pLast->Next; pLast=pLast->Next) {}
  269. if (pLast) pLast->Next=nvp; else FirstViewport=nvp;
  270. // Recalculate viewports
  271. RecalculateViewports();
  272. // Viewports start off at centered position
  273. nvp->CenterPosition();
  274. // Action sound
  275. if (GetViewportCount()!=iLastCount) if (!fSilent)
  276. StartSoundEffect("CloseViewport");
  277. return true;
  278. }
  279. void C4GraphicsSystem::ClearPointers(C4Object *pObj)
  280. {
  281. for (C4Viewport *cvp=FirstViewport; cvp; cvp=cvp->Next)
  282. cvp->ClearPointers(pObj);
  283. }
  284. void C4GraphicsSystem::Default()
  285. {
  286. UpperBoard.Default();
  287. MessageBoard.Default();
  288. FirstViewport=NULL;
  289. InvalidateBg();
  290. ViewportArea.Default();
  291. ShowVertices=false;
  292. ShowAction=false;
  293. ShowCommand=false;
  294. ShowEntrance=false;
  295. ShowPathfinder=false;
  296. ShowNetstatus=false;
  297. ShowSolidMask=false;
  298. ShowHelp=false;
  299. FlashMessageText[0]=0;
  300. FlashMessageTime=0; FlashMessageX=FlashMessageY=0;
  301. fSetPalette=false;
  302. Video.Default();
  303. for (int32_t iRamp=0; iRamp<3*C4MaxGammaRamps; iRamp+=3)
  304. { dwGamma[iRamp+0]=0; dwGamma[iRamp+1]=0x808080; dwGamma[iRamp+2]=0xffffff; }
  305. fSetGamma=false;
  306. pLoaderScreen=NULL;
  307. }
  308. void C4GraphicsSystem::DrawFullscreenBackground()
  309. {
  310. for (int i=0, iNum=BackgroundAreas.GetCount(); i<iNum; ++i)
  311. {
  312. const C4Rect &rc = BackgroundAreas.Get(i);
  313. Application.DDraw->BlitSurfaceTile(::GraphicsResource.fctBackground.Surface,Application.DDraw->lpBack,rc.x,rc.y,rc.Wdt,rc.Hgt,-rc.x,-rc.y);
  314. }
  315. --iRedrawBackground;
  316. }
  317. void C4GraphicsSystem::ClearFullscreenBackground()
  318. {
  319. Application.DDraw->FillBG(0);
  320. --iRedrawBackground;
  321. }
  322. void OnSurfaceRestore()
  323. {
  324. ::GraphicsSystem.InvalidateBg();
  325. }
  326. bool C4GraphicsSystem::InitLoaderScreen(const char *szLoaderSpec, bool fDrawBlackScreenFirst)
  327. {
  328. // create new loader; overwrite current only if successful
  329. C4LoaderScreen *pNewLoader = new C4LoaderScreen();
  330. pNewLoader->SetBlackScreen(fDrawBlackScreenFirst);
  331. if (!pNewLoader->Init(szLoaderSpec)) { delete pNewLoader; return false; }
  332. if (pLoaderScreen) delete pLoaderScreen;
  333. pLoaderScreen = pNewLoader;
  334. // apply user gamma for loader
  335. ApplyGamma();
  336. // done, success
  337. return true;
  338. }
  339. void C4GraphicsSystem::EnableLoaderDrawing()
  340. {
  341. // reset black screen loader flag
  342. if (pLoaderScreen) pLoaderScreen->SetBlackScreen(false);
  343. }
  344. bool C4GraphicsSystem::CloseViewport(int32_t iPlayer, bool fSilent)
  345. {
  346. // Close all matching viewports
  347. int32_t iLastCount = GetViewportCount();
  348. C4Viewport *next,*prev=NULL;
  349. for (C4Viewport *cvp=FirstViewport; cvp; cvp=next)
  350. {
  351. next=cvp->Next;
  352. if (cvp->Player==iPlayer || (iPlayer==NO_OWNER && cvp->fIsNoOwnerViewport))
  353. {
  354. delete cvp;
  355. if (prev) prev->Next=next;
  356. else FirstViewport=next;
  357. }
  358. else
  359. prev=cvp;
  360. }
  361. // Recalculate viewports
  362. RecalculateViewports();
  363. // Action sound
  364. if (GetViewportCount()!=iLastCount) if (!fSilent)
  365. StartSoundEffect("CloseViewport");
  366. return true;
  367. }
  368. void C4GraphicsSystem::RecalculateViewports()
  369. {
  370. // Fullscreen only
  371. if (!Application.isFullScreen) return;
  372. // Sort viewports
  373. SortViewportsByPlayerControl();
  374. // Viewport area
  375. int32_t iBorderTop = 0, iBorderBottom = 0;
  376. if (Config.Graphics.UpperBoard)
  377. iBorderTop = C4UpperBoardHeight;
  378. iBorderBottom = MessageBoard.Output.Hgt;
  379. ViewportArea.Set(Application.DDraw->lpBack,0,iBorderTop, C4GUI::GetScreenWdt(), C4GUI::GetScreenHgt()-iBorderTop-iBorderBottom);
  380. // Redraw flag
  381. InvalidateBg();
  382. #ifdef _WIN32
  383. // reset mouse clipping
  384. ClipCursor(NULL);
  385. #else
  386. // StdWindow handles this.
  387. #endif
  388. // reset GUI dlg pos
  389. if (::pGUI)
  390. ::pGUI->SetPreferredDlgRect(C4Rect(ViewportArea.X, ViewportArea.Y, ViewportArea.Wdt, ViewportArea.Hgt));
  391. // fullscreen background: First, cover all of screen
  392. BackgroundAreas.Clear();
  393. BackgroundAreas.AddRect(C4Rect(ViewportArea.X, ViewportArea.Y, ViewportArea.Wdt, ViewportArea.Hgt));
  394. // Viewports
  395. C4Viewport *cvp;
  396. int32_t iViews = 0;
  397. for (cvp=FirstViewport; cvp; cvp=cvp->Next) iViews++;
  398. if (!iViews) return;
  399. int32_t iViewsH = (int32_t) sqrt(float(iViews));
  400. int32_t iViewsX = iViews / iViewsH;
  401. int32_t iViewsL = iViews % iViewsH;
  402. int32_t cViewH,cViewX,ciViewsX;
  403. int32_t cViewWdt,cViewHgt,cOffWdt,cOffHgt,cOffX,cOffY;
  404. cvp=FirstViewport;
  405. for (cViewH=0; cViewH<iViewsH; cViewH++)
  406. {
  407. ciViewsX = iViewsX; if (cViewH<iViewsL) ciViewsX++;
  408. for (cViewX=0; cViewX<ciViewsX; cViewX++)
  409. {
  410. cViewWdt = ViewportArea.Wdt/ciViewsX;
  411. cViewHgt = ViewportArea.Hgt/iViewsH;
  412. cOffX = ViewportArea.X;
  413. cOffY = ViewportArea.Y;
  414. cOffWdt = cOffHgt = 0;
  415. if (ciViewsX * cViewWdt < ViewportArea.Wdt)
  416. cOffX = (ViewportArea.Wdt - ciViewsX * cViewWdt) / 2;
  417. if (iViewsH * cViewHgt < ViewportArea.Hgt)
  418. cOffY = (ViewportArea.Hgt - iViewsH * cViewHgt) / 2 + ViewportArea.Y;
  419. if (Config.Graphics.SplitscreenDividers)
  420. {
  421. if (cViewX < ciViewsX - 1) cOffWdt=4;
  422. if (cViewH < iViewsH - 1) cOffHgt=4;
  423. }
  424. int32_t coViewWdt=cViewWdt-cOffWdt;
  425. int32_t coViewHgt=cViewHgt-cOffHgt;
  426. C4Rect rcOut(cOffX+cViewX*cViewWdt, cOffY+cViewH*cViewHgt, coViewWdt, coViewHgt);
  427. cvp->SetOutputSize(rcOut.x,rcOut.y,rcOut.x,rcOut.y,rcOut.Wdt,rcOut.Hgt);
  428. cvp=cvp->Next;
  429. // clip down area avaiable for background drawing
  430. BackgroundAreas.ClipByRect(rcOut);
  431. }
  432. }
  433. }
  434. int32_t C4GraphicsSystem::GetViewportCount()
  435. {
  436. int32_t iResult = 0;
  437. for (C4Viewport *cvp=FirstViewport; cvp; cvp=cvp->Next) iResult++;
  438. return iResult;
  439. }
  440. C4Viewport* C4GraphicsSystem::GetViewport(int32_t iPlayer)
  441. {
  442. for (C4Viewport *cvp=FirstViewport; cvp; cvp=cvp->Next)
  443. if (cvp->Player==iPlayer || (iPlayer==NO_OWNER && cvp->fIsNoOwnerViewport))
  444. return cvp;
  445. return NULL;
  446. }
  447. int32_t LayoutOrder(int32_t iControl)
  448. {
  449. // Convert keyboard control index to keyboard layout order
  450. switch (iControl)
  451. {
  452. case C4P_Control_Keyboard1: return 0;
  453. case C4P_Control_Keyboard2: return 3;
  454. case C4P_Control_Keyboard3: return 1;
  455. case C4P_Control_Keyboard4: return 2;
  456. }
  457. return iControl;
  458. }
  459. void C4GraphicsSystem::SortViewportsByPlayerControl()
  460. {
  461. // Sort
  462. bool fSorted;
  463. C4Player *pPlr1,*pPlr2;
  464. C4Viewport *pView,*pNext,*pPrev;
  465. do
  466. {
  467. fSorted = true;
  468. for (pPrev=NULL,pView=FirstViewport; pView && (pNext = pView->Next); pView=pNext)
  469. {
  470. // Get players
  471. pPlr1 = ::Players.Get(pView->Player);
  472. pPlr2 = ::Players.Get(pNext->Player);
  473. // Swap order
  474. if (pPlr1 && pPlr2 && ( LayoutOrder(pPlr1->Control) > LayoutOrder(pPlr2->Control) ))
  475. {
  476. if (pPrev) pPrev->Next = pNext; else FirstViewport = pNext;
  477. pView->Next = pNext->Next;
  478. pNext->Next = pView;
  479. pPrev = pNext;
  480. pNext = pView;
  481. fSorted = false;
  482. }
  483. // Don't swap
  484. else
  485. {
  486. pPrev = pView;
  487. }
  488. }
  489. }
  490. while (!fSorted);
  491. }
  492. void C4GraphicsSystem::MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP)
  493. {
  494. // pass on to GUI
  495. // Special: Don't pass if dragging and button is not upped
  496. if (::pGUI && ::pGUI->IsActive() && !::MouseControl.IsDragging())
  497. {
  498. bool fResult = ::pGUI->MouseInput(iButton, iX, iY, dwKeyParam, NULL, pVP);
  499. if (::pGUI && ::pGUI->HasMouseFocus()) { SetMouseInGUI(true, true); return; }
  500. // non-exclusive GUI: inform mouse-control about GUI-result
  501. SetMouseInGUI(fResult, true);
  502. // abort if GUI processed it
  503. if (fResult) return;
  504. }
  505. else
  506. // no GUI: mouse is not in GUI
  507. SetMouseInGUI(false, true);
  508. // mouse control enabled?
  509. if (!::MouseControl.IsActive())
  510. {
  511. // enable mouse in GUI, if a mouse-only-dlg is displayed
  512. if (::pGUI && ::pGUI->GetMouseControlledDialogCount())
  513. SetMouseInGUI(true, true);
  514. return;
  515. }
  516. // Pass on to mouse controlled viewport
  517. MouseMoveToViewport(iButton, iX, iY, dwKeyParam);
  518. }
  519. void C4GraphicsSystem::MouseMoveToViewport(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
  520. {
  521. // Pass on to mouse controlled viewport
  522. for (C4Viewport *cvp=FirstViewport; cvp; cvp=cvp->Next)
  523. if (::MouseControl.IsViewport(cvp))
  524. ::MouseControl.Move( iButton,
  525. BoundBy<int32_t>(iX-cvp->OutX,0,cvp->ViewWdt-1),
  526. BoundBy<int32_t>(iY-cvp->OutY,0,cvp->ViewHgt-1),
  527. dwKeyParam );
  528. }
  529. void C4GraphicsSystem::SetMouseInGUI(bool fInGUI, bool fByMouse)
  530. {
  531. // inform mouse control and GUI
  532. if (::pGUI)
  533. {
  534. ::pGUI->Mouse.SetOwnedMouse(fInGUI);
  535. // initial movement to ensure mouse control pos is correct
  536. if (!::MouseControl.IsMouseOwned() && !fInGUI && !fByMouse)
  537. {
  538. ::MouseControl.SetOwnedMouse(true);
  539. MouseMoveToViewport(C4MC_Button_None, int32_t(::pGUI->Mouse.x*C4GUI::GetZoom()), int32_t(::pGUI->Mouse.y*C4GUI::GetZoom()), ::pGUI->Mouse.dwKeys);
  540. }
  541. }
  542. ::MouseControl.SetOwnedMouse(!fInGUI);
  543. }
  544. bool C4GraphicsSystem::SaveScreenshot(bool fSaveAll)
  545. {
  546. // Filename
  547. char szFilename[_MAX_PATH+1];
  548. int32_t iScreenshotIndex=1;
  549. const char *strFilePath = NULL;
  550. do
  551. sprintf(szFilename,"Screenshot%03i.png",iScreenshotIndex++);
  552. while (FileExists(strFilePath = Config.AtScreenshotPath(szFilename)));
  553. bool fSuccess=DoSaveScreenshot(fSaveAll, strFilePath);
  554. // log if successful/where it has been stored
  555. if (!fSuccess)
  556. LogF(LoadResStr("IDS_PRC_SCREENSHOTERROR"), Config.AtUserDataRelativePath(Config.AtScreenshotPath(szFilename)));
  557. else
  558. LogF(LoadResStr("IDS_PRC_SCREENSHOT"), Config.AtUserDataRelativePath(Config.AtScreenshotPath(szFilename)));
  559. // return success
  560. return !!fSuccess;
  561. }
  562. bool C4GraphicsSystem::DoSaveScreenshot(bool fSaveAll, const char *szFilename)
  563. {
  564. // Fullscreen only
  565. if (!Application.isFullScreen) return false;
  566. // back surface must be present
  567. if (!Application.DDraw->lpBack) return false;
  568. // save landscape
  569. if (fSaveAll)
  570. {
  571. // get viewport to draw in
  572. C4Viewport *pVP=GetFirstViewport(); if (!pVP) return false;
  573. // create image large enough to hold the landcape
  574. CPNGFile png; int32_t lWdt=GBackWdt,lHgt=GBackHgt;
  575. if (!png.Create(lWdt, lHgt, false)) return false;
  576. // get backbuffer size
  577. int32_t bkWdt=C4GUI::GetScreenWdt(), bkHgt=C4GUI::GetScreenHgt();
  578. if (!bkWdt || !bkHgt) return false;
  579. // facet for blitting
  580. C4TargetFacet bkFct;
  581. // mark background to be redrawn
  582. InvalidateBg();
  583. // backup and clear sky parallaxity
  584. int32_t iParX=::Landscape.Sky.ParX; ::Landscape.Sky.ParX=10;
  585. int32_t iParY=::Landscape.Sky.ParY; ::Landscape.Sky.ParY=10;
  586. // temporarily change viewport player
  587. int32_t iVpPlr=pVP->Player; pVP->Player=NO_OWNER;
  588. // blit all tiles needed
  589. for (int32_t iY=0; iY<lHgt; iY+=bkHgt) for (int32_t iX=0; iX<lWdt; iX+=bkWdt)
  590. {
  591. // get max width/height
  592. int32_t bkWdt2=bkWdt,bkHgt2=bkHgt;
  593. if (iX+bkWdt2>lWdt) bkWdt2-=iX+bkWdt2-lWdt;
  594. if (iY+bkHgt2>lHgt) bkHgt2-=iY+bkHgt2-lHgt;
  595. // update facet
  596. bkFct.Set(Application.DDraw->lpBack, 0, 0, bkWdt2, bkHgt2, iX, iY);
  597. // draw there
  598. pVP->Draw(bkFct, false);
  599. // render
  600. Application.DDraw->PageFlip(); Application.DDraw->PageFlip();
  601. // get output (locking primary!)
  602. if (Application.DDraw->lpBack->Lock())
  603. {
  604. // transfer each pixel - slooow...
  605. for (int32_t iY2=0; iY2<bkHgt2; ++iY2)
  606. for (int32_t iX2=0; iX2<bkWdt2; ++iX2)
  607. png.SetPix(iX+iX2, iY+iY2, Application.DDraw->ApplyGammaTo(Application.DDraw->lpBack->GetPixDw(iX2, iY2, false)));
  608. // done; unlock
  609. Application.DDraw->lpBack->Unlock();
  610. }
  611. }
  612. // restore viewport player
  613. pVP->Player=iVpPlr;
  614. // restore parallaxity
  615. ::Landscape.Sky.ParX=iParX;
  616. ::Landscape.Sky.ParY=iParY;
  617. // save!
  618. return png.Save(szFilename);
  619. }
  620. // Save primary surface
  621. return Application.DDraw->lpBack->SavePNG(szFilename, false, true, false);
  622. }
  623. void C4GraphicsSystem::DeactivateDebugOutput()
  624. {
  625. ShowVertices=false;
  626. ShowAction=false;
  627. ShowCommand=false;
  628. ShowEntrance=false;
  629. ShowPathfinder=false; // allow pathfinder! - why this??
  630. ShowSolidMask=false;
  631. ShowNetstatus=false;
  632. }
  633. void C4GraphicsSystem::DrawHoldMessages()
  634. {
  635. if (Application.isFullScreen && Game.HaltCount)
  636. {
  637. Application.DDraw->TextOut("Pause", ::GraphicsResource.FontRegular,1.0,
  638. Application.DDraw->lpBack, C4GUI::GetScreenWdt()/2,
  639. C4GUI::GetScreenHgt()/2 - ::GraphicsResource.FontRegular.iLineHgt*2,
  640. CStdDDraw::DEFAULT_MESSAGE_COLOR, ACenter);
  641. ::GraphicsSystem.OverwriteBg();
  642. }
  643. }
  644. BYTE FindPaletteColor(BYTE *bypPalette, int32_t iRed, int32_t iGreen, int32_t iBlue)
  645. {
  646. int32_t iClosest=0;
  647. for (int32_t iColor=1; iColor<256; iColor++)
  648. if (Abs(bypPalette[iColor*3+0]-iRed)+Abs(bypPalette[iColor*3+1]-iGreen)+Abs(bypPalette[iColor*3+2]-iBlue)
  649. < Abs(bypPalette[iClosest*3+0]-iRed)+Abs(bypPalette[iClosest*3+1]-iGreen)+Abs(bypPalette[iClosest*3+2]-iBlue) )
  650. iClosest = iColor;
  651. return iClosest;
  652. }
  653. void C4GraphicsSystem::SetDarkColorTable()
  654. {
  655. const int32_t iDarkening=80;
  656. // Using GamePalette
  657. BYTE *bypPalette = ::GraphicsResource.GamePalette;
  658. for (int32_t iColor=0; iColor<256; iColor++)
  659. DarkColorTable[iColor]=FindPaletteColor(bypPalette,Max<int32_t>(bypPalette[iColor*3+0]-iDarkening,0),Max<int32_t>(bypPalette[iColor*3+1]-iDarkening,0),Max<int32_t>(bypPalette[iColor*3+2]-iDarkening,0));
  660. }
  661. void C4GraphicsSystem::FlashMessage(const char *szMessage)
  662. {
  663. // Store message
  664. SCopy(szMessage, FlashMessageText, C4MaxTitle);
  665. // Calculate message time
  666. FlashMessageTime = SLen(FlashMessageText) * 2;
  667. // Initial position
  668. FlashMessageX = -1;
  669. FlashMessageY = 10;
  670. // Upper board active: stay below upper board
  671. if (Config.Graphics.UpperBoard)
  672. FlashMessageY += C4UpperBoardHeight;
  673. // More than one viewport: try to stay below portraits etc.
  674. if (GetViewportCount() > 1)
  675. FlashMessageY += 64;
  676. // New flash message: redraw background (might be drawing one message on top of another)
  677. InvalidateBg();
  678. }
  679. void C4GraphicsSystem::FlashMessageOnOff(const char *strWhat, bool fOn)
  680. {
  681. StdStrBuf strMessage;
  682. strMessage.Format("%s: %s", strWhat, LoadResStr(fOn ? "IDS_CTL_ON" : "IDS_CTL_OFF"));
  683. FlashMessage(strMessage.getData());
  684. }
  685. void C4GraphicsSystem::DrawFlashMessage()
  686. {
  687. if (!FlashMessageTime) return;
  688. if (!Application.isFullScreen) return;
  689. Application.DDraw->TextOut(FlashMessageText, ::GraphicsResource.FontRegular, 1.0, Application.DDraw->lpBack,
  690. (FlashMessageX==-1) ? C4GUI::GetScreenWdt()/2 : FlashMessageX,
  691. (FlashMessageY==-1) ? C4GUI::GetScreenHgt()/2 : FlashMessageY,
  692. CStdDDraw::DEFAULT_MESSAGE_COLOR,
  693. (FlashMessageX==-1) ? ACenter : ALeft);
  694. FlashMessageTime--;
  695. // Flash message timed out: redraw background
  696. if (!FlashMessageTime) InvalidateBg();
  697. }
  698. void C4GraphicsSystem::DrawHelp()
  699. {
  700. if (!ShowHelp) return;
  701. if (!Application.isFullScreen) return;
  702. int32_t iX = ViewportArea.X, iY = ViewportArea.Y;
  703. int32_t iWdt = ViewportArea.Wdt;
  704. StdStrBuf strText;
  705. // left coloumn
  706. strText.AppendFormat("[%s]\n\n", LoadResStr("IDS_CTL_GAMEFUNCTIONS"));
  707. // main functions
  708. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("ToggleShowHelp").getData(), LoadResStr("IDS_CON_HELP"));
  709. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("MusicToggle").getData(), LoadResStr("IDS_CTL_MUSIC"));
  710. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("NetClientListDlgToggle").getData(), LoadResStr("IDS_DLG_NETWORK"));
  711. // messages
  712. StdCopyStrBuf strAltChatKey(GetKeyboardInputName("ChatOpen", false, 0));
  713. strText.AppendFormat("\n<c ffff00>%s/%s</c> - %s\n", GetKeyboardInputName("ChatOpen", false, 1).getData(), strAltChatKey.getData(), LoadResStr("IDS_CTL_SENDMESSAGE"));
  714. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("MsgBoardScrollUp").getData(), LoadResStr("IDS_CTL_MESSAGEBOARDBACK"));
  715. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("MsgBoardScrollDown").getData(), LoadResStr("IDS_CTL_MESSAGEBOARDFORWARD"));
  716. // irc chat
  717. strText.AppendFormat("\n<c ffff00>%s</c> - %s\n", GetKeyboardInputName("ToggleChat").getData(), LoadResStr("IDS_CTL_IRCCHAT"));
  718. // scoreboard
  719. strText.AppendFormat("\n<c ffff00>%s</c> - %s\n", GetKeyboardInputName("ScoreboardToggle").getData(), LoadResStr("IDS_CTL_SCOREBOARD"));
  720. // screenshots
  721. strText.AppendFormat("\n<c ffff00>%s</c> - %s\n", GetKeyboardInputName("Screenshot").getData(), LoadResStr("IDS_CTL_SCREENSHOT"));
  722. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("ScreenshotEx").getData(), LoadResStr("IDS_CTL_SCREENSHOTEX"));
  723. Application.DDraw->TextOut(strText.getData(), ::GraphicsResource.FontRegular, 1.0, Application.DDraw->lpBack,
  724. iX + 128, iY + 64, CStdDDraw::DEFAULT_MESSAGE_COLOR, ALeft);
  725. // right coloumn
  726. strText.Clear();
  727. // game speed
  728. strText.AppendFormat("\n\n<c ffff00>%s</c> - %s\n", GetKeyboardInputName("GameSpeedUp").getData(), LoadResStr("IDS_CTL_GAMESPEEDUP"));
  729. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("GameSlowDown").getData(), LoadResStr("IDS_CTL_GAMESPEEDDOWN"));
  730. // debug
  731. strText.AppendFormat("\n\n[%s]\n\n", "Debug");
  732. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgModeToggle").getData(), LoadResStr("IDS_CTL_DEBUGMODE"));
  733. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowVtxToggle").getData(), "Entrance+Vertices");
  734. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowActionToggle").getData(), "Actions/Commands/Pathfinder");
  735. strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowSolidMaskToggle").getData(), "SolidMasks");
  736. Application.DDraw->TextOut(strText.getData(), ::GraphicsResource.FontRegular, 1.0, Application.DDraw->lpBack,
  737. iX + iWdt/2 + 64, iY + 64, CStdDDraw::DEFAULT_MESSAGE_COLOR, ALeft);
  738. }
  739. int32_t C4GraphicsSystem::GetAudibility(int32_t iX, int32_t iY, int32_t *iPan, int32_t iAudibilityRadius)
  740. {
  741. // default audibility radius
  742. if (!iAudibilityRadius) iAudibilityRadius = C4AudibilityRadius;
  743. // Accumulate audibility by viewports
  744. int32_t iAudible=0; *iPan = 0;
  745. for (C4Viewport *cvp=FirstViewport; cvp; cvp=cvp->Next)
  746. {
  747. iAudible = Max( iAudible,
  748. BoundBy<int32_t>(100-100*Distance(cvp->ViewX+cvp->ViewWdt/2,cvp->ViewY+cvp->ViewHgt/2,iX,iY)/C4AudibilityRadius,0,100) );
  749. *iPan += (iX-(cvp->ViewX+cvp->ViewWdt/2)) / 5;
  750. }
  751. *iPan = BoundBy<int32_t>(*iPan, -100, 100);
  752. return iAudible;
  753. }
  754. void C4GraphicsSystem::SetGamma(DWORD dwClr1, DWORD dwClr2, DWORD dwClr3, int32_t iRampIndex)
  755. {
  756. // No gamma effects
  757. if (Config.Graphics.DisableGamma) return;
  758. if (iRampIndex < 0 || iRampIndex >= C4MaxGammaRamps) return;
  759. // turn ramp index into array offset
  760. iRampIndex*=3;
  761. // set array members
  762. dwGamma[iRampIndex+0]=dwClr1;
  763. dwGamma[iRampIndex+1]=dwClr2;
  764. dwGamma[iRampIndex+2]=dwClr3;
  765. // mark gamma ramp to be recalculated
  766. fSetGamma=true;
  767. }
  768. void C4GraphicsSystem::ApplyGamma()
  769. {
  770. // No gamma effects
  771. if (Config.Graphics.DisableGamma) return;
  772. // calculate color channels by adding the difference between the gamma ramps to their normals
  773. int32_t ChanOff[3];
  774. DWORD Gamma[3];
  775. const int32_t DefChanVal[3] = { 0x00, 0x80, 0xff };
  776. // calc offset for curve points
  777. for (int32_t iCurve=0; iCurve<3; ++iCurve)
  778. {
  779. ZeroMemory(ChanOff, sizeof(int32_t)*3);
  780. // ...channels...
  781. for (int32_t iChan=0; iChan<3; ++iChan)
  782. // ...ramps...
  783. for (int32_t iRamp=0; iRamp<C4MaxGammaRamps; ++iRamp)
  784. // add offset
  785. ChanOff[iChan]+=(int32_t) BYTE(dwGamma[iRamp*3+iCurve]>>(16-iChan*8)) - DefChanVal[iCurve];
  786. // calc curve point
  787. Gamma[iCurve]=C4RGB(BoundBy<int32_t>(DefChanVal[iCurve]+ChanOff[0], 0, 255), BoundBy<int32_t>(DefChanVal[iCurve]+ChanOff[1], 0, 255), BoundBy<int32_t>(DefChanVal[iCurve]+ChanOff[2], 0, 255));
  788. }
  789. // set gamma
  790. Application.DDraw->SetGamma(Gamma[0], Gamma[1], Gamma[2]);
  791. }
  792. bool C4GraphicsSystem::ToggleShowNetStatus()
  793. {
  794. ShowNetstatus = !ShowNetstatus;
  795. return true;
  796. }
  797. bool C4GraphicsSystem::ToggleShowVertices()
  798. {
  799. if (!Game.DebugMode && !Console.Active) { FlashMessage(LoadResStr("IDS_MSG_NODEBUGMODE")); return false; }
  800. Toggle(ShowVertices);
  801. Toggle(ShowEntrance); // vertices and entrance now toggled together
  802. FlashMessageOnOff("Entrance+Vertices", ShowVertices || ShowEntrance);
  803. return true;
  804. }
  805. bool C4GraphicsSystem::ToggleShowAction()
  806. {
  807. if (!Game.DebugMode && !Console.Active) { FlashMessage(LoadResStr("IDS_MSG_NODEBUGMODE")); return false; }
  808. if (!(ShowAction || ShowCommand || ShowPathfinder))
  809. { ShowAction = true; FlashMessage("Actions"); }
  810. else if (ShowAction)
  811. { ShowAction = false; ShowCommand = true; FlashMessage("Commands"); }
  812. else if (ShowCommand)
  813. { ShowCommand = false; ShowPathfinder = true; FlashMessage("Pathfinder"); }
  814. else if (ShowPathfinder)
  815. { ShowPathfinder = false; FlashMessageOnOff("Actions/Commands/Pathfinder", false); }
  816. return true;
  817. }
  818. bool C4GraphicsSystem::ToggleShowSolidMask()
  819. {
  820. if (!Game.DebugMode && !Console.Active) { FlashMessage(LoadResStr("IDS_MSG_NODEBUGMODE")); return false; }
  821. Toggle(ShowSolidMask);
  822. FlashMessageOnOff("SolidMasks", !!ShowSolidMask);
  823. return true;
  824. }
  825. bool C4GraphicsSystem::ToggleShowHelp()
  826. {
  827. Toggle(ShowHelp);
  828. // Turned off? Invalidate background.
  829. if (!ShowHelp) InvalidateBg();
  830. return true;
  831. }
  832. bool C4GraphicsSystem::ViewportNextPlayer()
  833. {
  834. // safety: switch valid?
  835. if ((!Game.C4S.Head.Film || !Game.C4S.Head.Replay) && !::GraphicsSystem.GetViewport(NO_OWNER)) return false;
  836. // do switch then
  837. C4Viewport *vp = GetFirstViewport();
  838. if (!vp) return false;
  839. vp->NextPlayer();
  840. return true;
  841. }
  842. bool C4GraphicsSystem::FreeScroll(C4Vec2D vScrollBy)
  843. {
  844. // safety: move valid?
  845. if ((!Game.C4S.Head.Replay || !Game.C4S.Head.Film) && !::GraphicsSystem.GetViewport(NO_OWNER)) return false;
  846. C4Viewport *vp = GetFirstViewport();
  847. if (!vp) return false;
  848. // move then (old static code crap...)
  849. static int32_t vp_vx=0; static int32_t vp_vy=0; static int32_t vp_vf=0;
  850. int32_t dx=vScrollBy.x; int32_t dy=vScrollBy.y;
  851. if (Game.FrameCounter-vp_vf < 5)
  852. { dx += vp_vx; dy += vp_vy; }
  853. vp_vx=dx; vp_vy=dy; vp_vf=Game.FrameCounter;
  854. vp->ViewX+=dx; vp->ViewY+=dy;
  855. return true;
  856. }
  857. bool C4GraphicsSystem::ViewportZoomOut()
  858. {
  859. for (C4Viewport *vp = FirstViewport; vp; vp = vp->Next) vp->ChangeZoom(1.0f/C4GFX_ZoomStep);
  860. if (FirstViewport) FlashMessage(FormatString("%s: %f", "[!]Zoom", (float)FirstViewport->ZoomTarget).getData());
  861. return true;
  862. }
  863. bool C4GraphicsSystem::ViewportZoomIn()
  864. {
  865. for (C4Viewport *vp = FirstViewport; vp; vp = vp->Next) vp->ChangeZoom(C4GFX_ZoomStep);
  866. if (FirstViewport) FlashMessage(FormatString("%s: %f", "[!]Zoom", (float)FirstViewport->ZoomTarget).getData());
  867. return true;
  868. }
  869. C4GraphicsSystem GraphicsSystem;