credits.cpp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. // credits.cpp
  19. // Project: Postal
  20. //
  21. // This module deals with displaying the credits.
  22. //
  23. // History:
  24. // 12/05/96 MJR Started.
  25. //
  26. // 12/19/96 JMI Scope error in Credits(). Loop was waiting for terminators
  27. // one of which was lKey. The lKey was being set via a call
  28. // to rspGetKey(&lKey). But there were two lKeys. One inside
  29. // and one outside the loop. The one on the outside was
  30. // evaluated and the one on the inside was being set to the
  31. // current key.
  32. //
  33. // 02/17/97 JMI Making this obey rspGetQuitStatus().
  34. //
  35. // 07/05/97 MJR Changed to RSP_BLACK_INDEX instead of 0.
  36. //
  37. // 07/16/97 BRH Changed the credits screen to the "What's in the
  38. // release version of postal preview" screen for the
  39. // demo. Took out blah blah blah, yada yada yada.
  40. //
  41. // 08/11/97 JRD Transforming this module into a device for scolling text
  42. // which is intended to be used both by credits and by story.
  43. // It wil operate similar to cutscene in that all of it's
  44. // assets and memory usage is assumed temporary. It will still
  45. // use the shell sak for assets.
  46. //
  47. // 08/11/97 JRD Created a class for the purpose of accessing generic files
  48. // from within a sak directory. Used to read in credits metafile.
  49. //
  50. // 08/20/97 BRH Added music to credits. Unfortunately, I hardwired the music in
  51. // the function ScrollPage instead of Credits(), so the mustic will
  52. // play in any part of the game where text is scrolled. Man, was I
  53. // "stupid" when I did that.
  54. //
  55. // 08/21/97 JMI Changed call to Update() to UpdateSystem() and occurrences
  56. // of rspUpdateDisplay() to UpdateDisplay().
  57. //
  58. // 08/22/97 JRD Attempted to make the credits locking safe.
  59. //
  60. // 08/22/97 JMI Changed calls to UpdateDisplay() back to rspUpdateDisplay()
  61. // since we no longer need UpdateDisplay() now that we are
  62. // using rspLock/Unlock* functions properly.
  63. //
  64. // 08/26/97 JRD Moved the credit music into the credits function from the
  65. // text scrolling function.
  66. //
  67. // 08/27/97 JRD Moved end sound out of Scroll Page. Made Scroll page not clear screen
  68. // at end. Added special credit ending.
  69. //
  70. // 08/27/97 JRD Removed text dependency on windows colors. Made it so if credits
  71. // are aborted, ending is not shown.
  72. //
  73. // 08/28/97 JRD Altered Credits() to allow varying the resources so
  74. // different music could play in the final scene than
  75. // in the normal credits.
  76. //
  77. // 09/08/97 JRD Had Bill add rspGetQuitStaus() to the list of keys and
  78. // mouse buttons that abort the credits. Boy, did I look
  79. // "stupid" when someone hit Alt-F4 during the ending
  80. // sequence only to get stuck on the credits. Thanks for
  81. // fixing that Bill, I owe you another trip to Las Vegas.
  82. //
  83. // 09/17/97 BRH Added in a conditional compile optionfor the credits so
  84. // the shareware version will show the coming soon screen
  85. // instead of the credits.
  86. //
  87. // 06/24/01 MJR Changed from obsolete macro to SHOW_EXIT_SCREEN as a way
  88. // to control what gets displayed when the player exits from
  89. // the game. Also renamed macro to EXIT_BG and changed the
  90. // filename to "exit.bmp".
  91. //
  92. ////////////////////////////////////////////////////////////////////////////////
  93. // SCROLL PAGE HACK: CURRENTLY USES COLOR 244 = {128,0,0}, 245 = {255,0,0}
  94. #include "RSPiX.h"
  95. #include "credits.h"
  96. #include "game.h"
  97. #include "update.h"
  98. #include "SampleMaster.h"
  99. #include "CompileOptions.h"
  100. #include "ORANGE/Parse/SimpleBatch.h"
  101. #include "GREEN/Mix/MixBuf.h"
  102. // try to hook the sound scaling tables for the palette
  103. ////////////////////////////////////////////////////////////////////////////////
  104. // Macros/types/etc.
  105. ////////////////////////////////////////////////////////////////////////////////
  106. #define EXIT_BG "credits/exit.bmp"
  107. // default credits parameters... (serve as emergency backups...)
  108. char g_szBackground[256] = "credits/pile640.bmp";
  109. char g_szCredits[256] = "credits/credits.txt";
  110. SampleMasterID* g_psmidMusic = &g_smidCreditsMusak;
  111. #define BG_X (g_pimScreenBuf->m_sWidth / 2 - pimBackground->m_sWidth / 2)
  112. #define BG_Y (g_pimScreenBuf->m_sHeight / 2 - pimBackground->m_sHeight / 2)
  113. #define CREDIT_TIME 30000
  114. #define LEFT_X 200
  115. #define SPACE_Y 30
  116. #define MUSAK_START_TIME 0
  117. #define MUSAK_END_TIME 0
  118. #define MIN_SCOLL_FRAME_MILLI 14 // Cap at 40 fps
  119. extern int wideScreenWidth;
  120. ////////////////////////////////////////////////////////////////////////////////
  121. // Variables/data
  122. ////////////////////////////////////////////////////////////////////////////////
  123. static SampleMaster::SoundInstance ms_siMusak;
  124. ////////////////////////////////////////////////////////////////////////////////
  125. // Function prototypes
  126. ////////////////////////////////////////////////////////////////////////////////
  127. ////////////////////////////////////////////////////////////////////////////////
  128. // Forward Declarations
  129. ////////////////////////////////////////////////////////////////////////////////
  130. #define MAX_BACKGROUNDS 10
  131. class CBackgroundChange
  132. {
  133. public:
  134. RImage* m_pimPrevBackground;
  135. RImage* m_pimNewBackground;
  136. short m_sImageCacheIndex;
  137. char m_szNewName[64]; // resource name
  138. bool m_bActive;
  139. // All times in milliseconds...
  140. long m_lActivationTime; // also delay time
  141. long m_lFadeOutTime; // also in delta time form
  142. long m_lBlackTime; // also in delta time form
  143. long m_lFadeInTime; // also in delta time form
  144. UCHAR m_TransitionPalette[1024];
  145. typedef enum { Inactive,FadeOut,Black,FadeIn } BackState;
  146. BackState m_eState;
  147. typedef enum { OnEnter,OnExit } ActivationType;
  148. ActivationType m_eType;
  149. //--------------------------------------------------------
  150. void Clear()
  151. {
  152. m_pimPrevBackground = m_pimNewBackground = NULL;
  153. m_szNewName[0] = 0;
  154. m_bActive = false;
  155. m_lActivationTime = m_lFadeOutTime = m_lBlackTime = m_lFadeInTime = 0;
  156. m_eState = Inactive;
  157. m_sImageCacheIndex = -1;
  158. m_eType = OnEnter;
  159. }
  160. CBackgroundChange()
  161. {
  162. Clear();
  163. }
  164. ~CBackgroundChange()
  165. {
  166. Clear();
  167. }
  168. //--------------------------------------------------------
  169. void Activate(RImage* pimCurBack,RImage* pBackgrounds[])
  170. {
  171. // set current times as delta from current:
  172. m_lActivationTime += rspGetMilliseconds();
  173. m_lFadeOutTime += m_lActivationTime;
  174. m_lBlackTime += m_lFadeOutTime;
  175. m_lFadeInTime += m_lBlackTime;
  176. m_pimPrevBackground = pimCurBack;
  177. m_pimNewBackground = pBackgrounds[m_sImageCacheIndex];
  178. if (!m_pimNewBackground) m_pimNewBackground = pimCurBack;
  179. m_eState = FadeOut;
  180. }
  181. };
  182. class CTextPhrase
  183. {
  184. public:
  185. short m_sColorIndex;
  186. short m_sFontSize;
  187. short m_sLocalX;
  188. short m_sLocalY;
  189. typedef enum {Left, Right, Center} Justify;
  190. Justify m_eJust;
  191. char m_szText[128];
  192. CTextPhrase* m_pNext;
  193. CTextPhrase* m_pPrev;
  194. //------------------------------
  195. CTextPhrase()
  196. {
  197. m_pNext = m_pPrev = NULL;
  198. m_sFontSize = 0; // means use previous font size
  199. }
  200. ~CTextPhrase()
  201. {
  202. m_pNext = m_pPrev = NULL;;
  203. }
  204. };
  205. // Will actually create an image of itself
  206. //
  207. class CTextChunk
  208. {
  209. public:
  210. long m_lNumPhrases;
  211. CTextPhrase m_tHead; // bookends
  212. CTextPhrase m_tTail;
  213. short m_sGlobalTopY;
  214. short m_sGlobalBottomY;
  215. RImage* m_pimCache;
  216. CTextChunk* m_pNext;
  217. CTextChunk* m_pPrev;
  218. CBackgroundChange* m_pChangeBackground;
  219. //--------------------------
  220. CTextChunk()
  221. {
  222. m_lNumPhrases = 0;
  223. m_tHead.m_pNext = &m_tTail;
  224. m_tTail.m_pPrev = &m_tHead;
  225. m_sGlobalBottomY = -1;
  226. m_pimCache = NULL;
  227. m_pChangeBackground = NULL;
  228. }
  229. ~CTextChunk()
  230. {
  231. CTextPhrase* pCur = m_tHead.m_pNext;
  232. while (pCur != &m_tTail)
  233. {
  234. CTextPhrase* pNext = pCur->m_pNext;
  235. delete pCur;
  236. pCur = pNext;
  237. }
  238. if (m_pimCache)
  239. {
  240. delete m_pimCache;
  241. m_pimCache = NULL;
  242. }
  243. }
  244. //--------------------------
  245. // Must insert at tail to maintain order
  246. void AddPhrase(CTextPhrase* pNew)
  247. {
  248. m_lNumPhrases++;
  249. pNew->m_pNext = &m_tTail;
  250. pNew->m_pPrev = m_tTail.m_pPrev;
  251. m_tTail.m_pPrev->m_pNext = pNew;
  252. m_tTail.m_pPrev = pNew;
  253. }
  254. // WILL NOT DELETE!
  255. void RemovePhrase(CTextPhrase* pGone)
  256. {
  257. m_lNumPhrases--;
  258. pGone->m_pPrev->m_pNext = pGone->m_pNext;
  259. pGone->m_pNext->m_pPrev = pGone->m_pPrev;
  260. pGone->m_pNext = pGone->m_pPrev = NULL;
  261. }
  262. //------------------------------------
  263. void RenderChunk(short sW,RPrint* pPrint)
  264. {
  265. if (m_pimCache)
  266. {
  267. //TRACE("Already rendered\n");// legal!
  268. return;
  269. }
  270. m_pimCache = new RImage;
  271. m_pimCache->CreateImage(sW,m_sGlobalBottomY - m_sGlobalTopY+1,RImage::BMP8);
  272. CTextPhrase* pCur = m_tHead.m_pNext;
  273. pPrint->SetDestination(m_pimCache);
  274. while (pCur != &m_tTail)
  275. {
  276. //---------------------------------
  277. pPrint->SetColor(pCur->m_sColorIndex);
  278. // HARD CODED shadow colors (postal specific:)
  279. if (pCur->m_sColorIndex == 245) pPrint->SetColor(pCur->m_sColorIndex, 0 , 244);
  280. else pPrint->SetColor(pCur->m_sColorIndex, 0 , 243); // until we have comand support
  281. if (pCur->m_sFontSize) pPrint->SetFont(pCur->m_sFontSize);
  282. short sTabX = pCur->m_sLocalX;
  283. if (sTabX < 0) sTabX += sW; // from the right...
  284. // each justification needed to select the RPrint cell to use:
  285. // RPrint doesn't seem to be justifying correctly...
  286. // I will attempt some of my own:
  287. int wideScreenOffset = (wideScreenWidth - 640);
  288. switch (pCur->m_eJust)
  289. {
  290. short sRadius;
  291. //--------------------------- Left Justify
  292. case CTextPhrase::Left:
  293. pPrint->SetJustifyLeft();
  294. pPrint->SetColumn(0 ,0,sW,m_pimCache->m_sHeight);
  295. pPrint->print(sTabX + wideScreenOffset/2,pCur->m_sLocalY,"%s",pCur->m_szText);
  296. break;
  297. //--------------------------- Right Justify
  298. case CTextPhrase::Right:
  299. pPrint->SetJustifyRight();
  300. pPrint->SetColumn(0,0,sTabX + 1 + wideScreenOffset/2,m_pimCache->m_sHeight);
  301. // trick print into thinking it's a new line!
  302. pPrint->print(-1,pCur->m_sLocalY,"%s",pCur->m_szText);
  303. break;
  304. //--------------------------- Center Justify (about tab)
  305. case CTextPhrase::Center:
  306. pPrint->SetJustifyCenter();
  307. sRadius = MIN(sTabX,short(sW - sTabX));
  308. pPrint->SetColumn(sTabX - sRadius + wideScreenOffset,0,
  309. sRadius<<1, m_pimCache->m_sHeight);
  310. // trick print into thinking it's a new line!
  311. pPrint->print(-1,pCur->m_sLocalY,"%s",pCur->m_szText);
  312. break;
  313. }
  314. // undo justification:
  315. pPrint->SetColumn(0,0,sW,m_pimCache->m_sHeight);
  316. //---------------------------------
  317. pCur = pCur->m_pNext;
  318. }
  319. // Now compress it for speed!
  320. m_pimCache->Convert(RImage::FSPR8); // for testing.
  321. }
  322. void FreeChunk()
  323. {
  324. // Keep 'em for now...
  325. if (m_pimCache) delete m_pimCache;
  326. m_pimCache = NULL;
  327. }
  328. };
  329. char* pct = g_szCredits;
  330. extern void SetAll();
  331. // The highest level:
  332. class CScrollMaster
  333. {
  334. public:
  335. long m_lGlobalHeight;
  336. long m_lCurrentTopY;
  337. long m_lCurrentBottomY;
  338. long m_lTotalChunks;
  339. CTextChunk* m_pTopActiveChunk;
  340. CTextChunk* m_pBottomActiveChunk;
  341. long m_lActivationTime;
  342. RRect m_rDisplay;
  343. double m_dScrollRate; // screens/sec
  344. CTextChunk m_cHead; // bookends:
  345. CTextChunk m_cTail;
  346. short m_sNumBackgrounds;
  347. char m_szBackgroundNames[MAX_BACKGROUNDS][64];
  348. RImage* m_pimBackgrounds[MAX_BACKGROUNDS];
  349. CBackgroundChange* m_pCurSceneChange;
  350. //--------------------------------------
  351. CScrollMaster()
  352. {
  353. m_lGlobalHeight = 0;
  354. m_lCurrentBottomY = 0;
  355. m_lTotalChunks = 0;
  356. m_pTopActiveChunk = NULL;
  357. m_pBottomActiveChunk = NULL;
  358. m_lActivationTime = 0;
  359. m_cHead.m_pNext = &m_cTail;
  360. m_cTail.m_pPrev = &m_cHead;
  361. m_rDisplay = RRect(0,40,wideScreenWidth,360);
  362. m_dScrollRate = 0.1; // 100 seconds per screen
  363. m_sNumBackgrounds = 0;
  364. m_pCurSceneChange = NULL;
  365. }
  366. ~CScrollMaster()
  367. {
  368. CTextChunk* pCur = m_cHead.m_pNext;
  369. while (pCur != &m_cTail)
  370. {
  371. CTextChunk* pNext = pCur->m_pNext;
  372. delete pCur;
  373. pCur = pNext;
  374. }
  375. // release all of the backgrounds:
  376. short i;
  377. for (i = 0; i < m_sNumBackgrounds;i++)
  378. {
  379. if (m_pimBackgrounds[i]) g_resmgrShell.Release(m_pimBackgrounds[i]);
  380. m_pimBackgrounds[i] = NULL;
  381. }
  382. }
  383. void Configure(double dScrollRate,RRect* prWindow = NULL)
  384. {
  385. if (dScrollRate > 0.0) m_dScrollRate = dScrollRate;
  386. if (prWindow) m_rDisplay = *prWindow;
  387. }
  388. //--------------------------------
  389. void AddChunk(CTextChunk* pNew)
  390. {
  391. m_lTotalChunks++;
  392. pNew->m_pNext = &m_cTail;
  393. pNew->m_pPrev = m_cTail.m_pPrev;
  394. m_cTail.m_pPrev->m_pNext = pNew;
  395. m_cTail.m_pPrev = pNew;
  396. m_lGlobalHeight = pNew->m_sGlobalBottomY + 1;
  397. }
  398. //--------------------------------
  399. void Start(RPrint* pPrint)
  400. {
  401. // Try to load all the extra bmps from memory:
  402. short i;
  403. for (i=0; i < m_sNumBackgrounds;i++)
  404. {
  405. if (rspGetResource(&g_resmgrShell,m_szBackgroundNames[i],&m_pimBackgrounds[i])
  406. != SUCCESS)
  407. {
  408. TRACE("Couldn't load resource %s\n",m_szBackgroundNames[i]);
  409. m_pimBackgrounds[i] = NULL;
  410. }
  411. }
  412. // Activate first chunk:
  413. ASSERT(m_lTotalChunks >= 1);
  414. m_pTopActiveChunk = m_pBottomActiveChunk = m_cHead.m_pNext;
  415. m_pBottomActiveChunk->RenderChunk(m_rDisplay.sW,pPrint);
  416. m_pBottomActiveChunk = m_pBottomActiveChunk->m_pNext; // chunk 2
  417. // ATTEMPT TO LOAD ALL THE BACKGROUND RESOURCES FOR SCENE CHANGES!
  418. m_lActivationTime = rspGetMilliseconds();
  419. }
  420. // will return FAILURE if scroll is over!
  421. short Update(RPrint* pPrint)
  422. {
  423. // calculate current global scroll location:
  424. m_lCurrentBottomY = long(double(rspGetMilliseconds() - m_lActivationTime)
  425. * m_dScrollRate * m_rDisplay.sH / 1000.0);
  426. m_lCurrentTopY = (m_lCurrentBottomY - m_rDisplay.sH);
  427. if (m_lCurrentTopY > m_lGlobalHeight) return FAILURE; // scroll over!
  428. // see if we need to activate a new chunk:
  429. if (m_pBottomActiveChunk != &m_cTail)
  430. {
  431. if (m_pBottomActiveChunk->m_sGlobalTopY <= m_lCurrentBottomY)
  432. {
  433. m_pBottomActiveChunk->RenderChunk(m_rDisplay.sW,pPrint);
  434. m_pBottomActiveChunk = m_pBottomActiveChunk->m_pNext;
  435. // CHECK for scene change
  436. }
  437. }
  438. // see if we need to deactivate a new chunk:
  439. if (m_pTopActiveChunk != &m_cTail)
  440. {
  441. if (m_pTopActiveChunk->m_sGlobalBottomY <= m_lCurrentTopY)
  442. {
  443. m_pTopActiveChunk->FreeChunk();
  444. m_pTopActiveChunk = m_pTopActiveChunk->m_pNext;
  445. // CHECK for exit scene change
  446. }
  447. }
  448. return SUCCESS;
  449. }
  450. // will overlay the text onto your bitmap with the designated clip rectangle:
  451. // NOTE: script coordinates are relative to upper left clipping corner...
  452. void Render(RImage* pimDst)
  453. {
  454. // Draw each chunk separately:
  455. CTextChunk* pCur;
  456. // all active chunks should be pre-rendered:
  457. for (pCur = m_pTopActiveChunk; pCur != m_pBottomActiveChunk->m_pNext;
  458. pCur = pCur->m_pNext)
  459. {
  460. if (pCur != &m_cTail)
  461. {
  462. // Draw the pre-rendered chunk...
  463. if (pCur->m_pimCache)
  464. {
  465. rspBlit(pCur->m_pimCache,pimDst,m_rDisplay.sX,
  466. pCur->m_sGlobalTopY - m_lCurrentTopY
  467. + m_rDisplay.sY,&m_rDisplay);
  468. }
  469. }
  470. }
  471. }
  472. //-------------------------------------------------------------
  473. // returns the index number for the resource bmp:
  474. short AddBackground(char* pszName)
  475. {
  476. ASSERT(pszName);
  477. ASSERT(m_sNumBackgrounds < MAX_BACKGROUNDS);
  478. strcpy(m_szBackgroundNames[m_sNumBackgrounds],pszName);
  479. m_pimBackgrounds[m_sNumBackgrounds] = NULL;
  480. return m_sNumBackgrounds++;
  481. }
  482. };
  483. extern short sLoaded;
  484. ////////////////////////////////////////////////////////////////////////////////
  485. // This is cheesy, but right now I'm using a global stream to load into.
  486. ////////////////////////////////////////////////////////////////////////////////
  487. //CScrollMaster* gpCurStream = NULL;
  488. // For Res managing an ANSI file:
  489. class CFileTextInput
  490. {
  491. public:
  492. RBatch m_bf;
  493. //-----------------------
  494. CFileTextInput()
  495. {
  496. m_pStream = NULL;
  497. };
  498. ~CFileTextInput()
  499. {
  500. m_bf.clear(); // don't let it close the file!
  501. if (m_pStream) delete m_pStream;
  502. };
  503. //-----------------------
  504. short ParseTextInput(FILE* fp);
  505. //-----------------------
  506. short Load(RFile* pFile) // so res manager can hook it!
  507. {
  508. FILE* fp = pFile->m_fs;
  509. //--------------------- do my load:
  510. ParseTextInput(fp);
  511. return SUCCESS;
  512. }
  513. short Load(FILE* fp) // so res manager can hook it!
  514. {
  515. //--------------------- do my load:
  516. ParseTextInput(fp);
  517. return SUCCESS;
  518. }
  519. CScrollMaster* m_pStream;
  520. };
  521. ////////////////////////////////////////////////////////////////////////////////
  522. // Tokenizing the script file into absolute strips:
  523. ////////////////////////////////////////////////////////////////////////////////
  524. ////////////////////////////////////////////////////////////////////////////////
  525. // ASSUME A VALID FILE STREAM
  526. //
  527. // This will parse and tokenize the incoming text...
  528. // It will not actually render it.
  529. //
  530. ////////////////////////////////////////////////////////////////////////////////
  531. short CFileTextInput::ParseTextInput(FILE* fp)
  532. {
  533. if (m_pStream)
  534. {
  535. TRACE("CFileTextInput::ParseTextInput: Stream in progress.\n");
  536. return FAILURE;
  537. }
  538. else
  539. m_pStream = new CScrollMaster; // needs to be freed!
  540. // We need to be a little low level so we can force feed a
  541. // file pointer to it.
  542. m_bf.clear();
  543. m_bf.m_fp = fp;
  544. m_bf.configure(" \t,;=({[",";/)}]",'`','/');
  545. char* pszToken;
  546. //gpCurStream = new CTextStream;
  547. short sGlobalY = 0;
  548. short sLocalY = 0;
  549. short sCurStripTop = 0;
  550. short sMaxH = 0;
  551. bool bNewChunk = true;
  552. short sCurFontSize = 0;
  553. short sCurTabX = 0;
  554. CTextPhrase::Justify eCurJust = CTextPhrase::Left;
  555. short sCurColor = 255;
  556. CTextChunk* pChunk = new CTextChunk;
  557. CTextPhrase* pCurPhrase = new CTextPhrase;
  558. pChunk->m_sGlobalTopY = sCurStripTop;
  559. pChunk->m_sGlobalBottomY = sCurStripTop;
  560. if (sLoaded) SetAll();
  561. while ((pszToken = m_bf.NextToken()) != NULL)
  562. {
  563. //TRACE("TOKEN = '%s'\n",pszToken);
  564. //-----------------------------------------------------
  565. if (!strcmp(pszToken,"color"))
  566. {
  567. sCurColor = atoi(m_bf.NextToken());
  568. continue;
  569. }
  570. //-----------------------------------------------------
  571. if (!strcmp(pszToken,"size"))
  572. {
  573. pCurPhrase->m_sFontSize = 0;
  574. short sNewSize = atoi(m_bf.NextToken());
  575. // If we are starting a new chunk, we MUST give a font
  576. // size!
  577. if (bNewChunk || (sCurFontSize != sNewSize))
  578. {
  579. pCurPhrase->m_sFontSize = sNewSize;
  580. }
  581. sCurFontSize = sNewSize;
  582. bNewChunk = false;
  583. sMaxH = MAX(sMaxH,short(sLocalY + sNewSize));
  584. continue;
  585. }
  586. //-----------------------------------------------------
  587. if (!strcmp(pszToken,"back"))
  588. {
  589. sLocalY -= atoi(m_bf.NextToken());
  590. if (sLocalY < 0)
  591. {
  592. TRACE("ERROR - you backed up above the strip!\n");
  593. sLocalY = 0;
  594. }
  595. continue;
  596. }
  597. //-----------------------------------------------------
  598. if (!strcmp(pszToken+1,"tab"))
  599. {
  600. pCurPhrase->m_sLocalX = sCurTabX = atoi(m_bf.NextToken());
  601. switch (*pszToken)
  602. {
  603. case 'l': eCurJust = pCurPhrase->m_eJust = CTextPhrase::Left; break;
  604. case 'r': eCurJust = pCurPhrase->m_eJust = CTextPhrase::Right; break;
  605. case 'c': eCurJust = pCurPhrase->m_eJust = CTextPhrase::Center; break;
  606. }
  607. continue;
  608. }
  609. //-----------------------------------------------------
  610. if (!strcmp(pszToken,";")) // means new line (& should benew phrase)
  611. {
  612. sLocalY += sCurFontSize;
  613. sMaxH = MAX(sMaxH,short(sLocalY + sCurFontSize));
  614. continue;
  615. }
  616. //-----------------------------------------------------
  617. if (!strcmp(pszToken,"end")) // if in the sak, no EOF!
  618. {
  619. break; // done
  620. }
  621. //-----------------------------------------------------
  622. if (!strcmp(pszToken,"skip")) // signals a new chunk!
  623. {
  624. //TRACE("New chunk!\n");
  625. bNewChunk = true;
  626. sMaxH = MAX(sMaxH,short(sLocalY + sCurFontSize));
  627. // Figure out how big chunk was:
  628. sGlobalY += MAX(sMaxH,sLocalY);
  629. pChunk->m_sGlobalBottomY = sGlobalY - 1;
  630. if (pChunk->m_sGlobalBottomY > pChunk->m_sGlobalTopY)
  631. {
  632. m_pStream->AddChunk(pChunk);
  633. }
  634. // create a new chunk
  635. short sVal = atoi(m_bf.NextToken());
  636. sGlobalY += sVal;
  637. pChunk = new CTextChunk;
  638. pChunk->m_sGlobalTopY = pChunk->m_sGlobalBottomY = sGlobalY;
  639. sMaxH = 0; // resetting stuff like this!
  640. sLocalY = 0;
  641. continue;
  642. }
  643. //-----------------------------------------------------
  644. if (!strcmp(pszToken,"scene")) // describe a scene change:
  645. {
  646. // first, parse through it all, then validate the information:
  647. // Good standards: (in milliseconds)
  648. short sDelay = 0,sFadeInTime = 1000,sFadeOutTime = 1000;
  649. short sBlackTime = 0, sOnExit = FALSE;
  650. char szName[64] = {0,};
  651. // The order doesn't matter - go until ')'
  652. pszToken = m_bf.NextToken();
  653. while (strcmp(pszToken,")")) // until we hit a right parenthesis
  654. {
  655. if (!strcmp(pszToken,"delay"))
  656. {
  657. sDelay = atoi(m_bf.NextToken());
  658. }
  659. else if (!strcmp(pszToken,"black"))
  660. {
  661. sBlackTime = atoi(m_bf.NextToken());
  662. }
  663. else if (!strcmp(pszToken,"out"))
  664. {
  665. sFadeOutTime = atoi(m_bf.NextToken());
  666. }
  667. else if (!strcmp(pszToken,"in"))
  668. {
  669. sFadeInTime = atoi(m_bf.NextToken());
  670. }
  671. else if (!strcmp(pszToken,"exit"))
  672. {
  673. sOnExit = TRUE;
  674. }
  675. else if (!strcmp(pszToken,"name"))
  676. {
  677. strcpy(szName,m_bf.NextToken());
  678. }
  679. pszToken = m_bf.NextToken();
  680. }
  681. //........... Now put the data into a useful form
  682. // and log it:
  683. CBackgroundChange* pNew = new CBackgroundChange;
  684. pNew->m_lActivationTime = sDelay;
  685. pNew->m_lFadeOutTime = sFadeOutTime;
  686. pNew->m_lBlackTime = sBlackTime;
  687. pNew->m_lFadeInTime = sFadeInTime;
  688. strcpy(pNew->m_szNewName,szName);
  689. if (sOnExit) pNew->m_eType = CBackgroundChange::OnExit;
  690. // Log into the stream:
  691. pNew->m_sImageCacheIndex = m_pStream->AddBackground(szName);
  692. // Add into chunk:
  693. ASSERT(!pChunk->m_pChangeBackground);
  694. pChunk->m_pChangeBackground = pNew;
  695. continue;
  696. }
  697. //-----------------------------------------------------
  698. // Assume default case is text to be printed, which
  699. // also signals the creation of a new phrase...
  700. //-----------------------------------------------------
  701. if (*pCurPhrase->m_szText)
  702. {
  703. TRACE("Error - already text\n");
  704. }
  705. // Set latest states
  706. pCurPhrase->m_sLocalY = sLocalY;
  707. pCurPhrase->m_sColorIndex = sCurColor;
  708. strcpy(pCurPhrase->m_szText,pszToken);
  709. // Create a brand new phrase:
  710. pChunk->AddPhrase(pCurPhrase);
  711. CTextPhrase* pNewPhrase = new CTextPhrase;
  712. *pNewPhrase = *pCurPhrase; // struct copy
  713. *pNewPhrase->m_szText = 0;
  714. pCurPhrase = pNewPhrase;
  715. continue;
  716. }
  717. if (pChunk)
  718. {
  719. // Figure out how big the chunk was
  720. sGlobalY += MAX(sMaxH,sLocalY);
  721. pChunk->m_sGlobalBottomY = sGlobalY - 1;
  722. m_pStream->AddChunk(pChunk);
  723. }
  724. if (pCurPhrase)
  725. {
  726. delete pCurPhrase;
  727. pCurPhrase = NULL;
  728. }
  729. return SUCCESS;
  730. }
  731. ////////////////////////////////////////////////////////////////////////////////
  732. //
  733. // General scrolling text screens: Returns FAILURE if assets couldn't load
  734. // OR returns FAILURE if use aborts!
  735. //
  736. // All resources are assumed from the shell sak.
  737. //
  738. ////////////////////////////////////////////////////////////////////////////////
  739. short ScrollPage(char* pszBackground,char* pszScrollScript,double dScrollRate,RRect *prWindow)
  740. {
  741. // Try to load resources:
  742. short sResult;
  743. rspLockBuffer();
  744. // clear background BEFORE doing a palette swap:
  745. rspRect(RSP_BLACK_INDEX,g_pimScreenBuf,0,0,g_pimScreenBuf->m_sWidth,
  746. g_pimScreenBuf->m_sHeight);
  747. rspUnlockBuffer();
  748. rspUpdateDisplay();
  749. // Load background
  750. RImage* pimBackground;
  751. if (rspGetResource(&g_resmgrShell, pszBackground, &pimBackground) != SUCCESS)
  752. {
  753. TRACE("ScrollPage: Couldn't load background %s\n",pszBackground);
  754. return FAILURE; // couldn't load background
  755. }
  756. // Set palette
  757. ASSERT(pimBackground->m_pPalette != NULL);
  758. ASSERT(pimBackground->m_pPalette->m_type == RPal::PDIB);
  759. rspSetPaletteEntries(
  760. 0, //10,
  761. 256, // 236,
  762. pimBackground->m_pPalette->Red(0), //10
  763. pimBackground->m_pPalette->Green(0), //10
  764. pimBackground->m_pPalette->Blue(0), //10
  765. pimBackground->m_pPalette->m_sPalEntrySize);
  766. rspUpdatePalette();
  767. // Load text script:
  768. CFileTextInput* pScript = NULL; // must free it myself!
  769. // Try to override with our own file, because I'm too lazy to figure out how to regenerate .sak files.
  770. FILE *nonsak = fopen(FindCorrectFile("res/credits.txt", "rb"), "rb");
  771. if (nonsak != NULL)
  772. {
  773. pScript = new CFileTextInput;
  774. pScript->Load(nonsak);
  775. }
  776. if (pScript == NULL)
  777. {
  778. sResult = rspGetResource(&g_resmgrShell, pszScrollScript, &pScript);
  779. if (sResult != SUCCESS)
  780. {
  781. // try another sak:
  782. sResult = rspGetResource(&g_resmgrSamples, pszScrollScript, &pScript);
  783. if (sResult != SUCCESS)
  784. {
  785. TRACE("ScrollPage: Couldn't load script %s\n",pszScrollScript);
  786. g_resmgrShell.Release(pimBackground); // dom't need it anymore
  787. return FAILURE; // couldn't load background
  788. }
  789. }
  790. }
  791. // Set standard font...
  792. RPrint print;
  793. print.SetFont(24, &g_fontPostal);
  794. print.SetColor(255, 0 , 0);
  795. print.SetEffectAbs(RPrint::SHADOW_X,1);
  796. print.SetEffectAbs(RPrint::SHADOW_Y,1);
  797. // Clear mouse and keyboard events
  798. rspClearKeyEvents();
  799. rspClearMouseEvents();
  800. // Clear screen
  801. rspLockBuffer();
  802. rspRect(RSP_BLACK_INDEX, g_pimScreenBuf, 0, 0, g_pimScreenBuf->m_sWidth, g_pimScreenBuf->m_sHeight);
  803. rspUnlockBuffer();
  804. pScript->m_pStream->Configure(dScrollRate,prWindow);
  805. pScript->m_pStream->Start(&print);
  806. // Wait until user input
  807. long lKey = 0;
  808. short sButtons = 0;
  809. short sJoyPress = 0;
  810. //****************** STATISTICAL ANALYSIS!
  811. /*
  812. long lTimeCount[256] = {0,}; // bucket sort
  813. */
  814. long lRunningTime,lPrevTime;
  815. lRunningTime = lPrevTime = rspGetMilliseconds();
  816. while ( (pScript->m_pStream->Update(&print) == SUCCESS) && !lKey && !sButtons && !(rspGetQuitStatus()) && !sJoyPress)
  817. {
  818. // Show title image.
  819. rspLockBuffer();
  820. rspBlit(
  821. pimBackground,
  822. g_pimScreenBuf,
  823. 0,0,
  824. BG_X ,//+ (rspGetMilliseconds()/100)&15,
  825. BG_Y,
  826. pimBackground->m_sWidth,
  827. pimBackground->m_sHeight);
  828. pScript->m_pStream->Render(g_pimScreenBuf);
  829. rspUnlockBuffer();
  830. rspUpdateDisplay();
  831. UpdateSystem();
  832. // Get key and mouse button inputs
  833. rspGetMouse(NULL, NULL, &sButtons);
  834. rspGetKey(&lKey);
  835. sJoyPress = IsXInputButtonPressed();
  836. // Clear mouse events to avoid overflowing the queue
  837. rspClearMouseEvents();
  838. // Restrict the frame rate to MAX_SCOLL_FRAME_MILLI
  839. while ( (lRunningTime - lPrevTime) < MIN_SCOLL_FRAME_MILLI)
  840. {
  841. lRunningTime = rspGetMilliseconds();
  842. }
  843. //*******************8 Update timing statistics:
  844. //lTimeCount[lRunningTime - lPrevTime]++;
  845. lPrevTime = lRunningTime;
  846. }
  847. //************* Report statistics
  848. /*
  849. FILE* fp = fopen("speed.out","a");
  850. for (short i=0;i < 256;i++) fprintf(fp,"%hd = %ld\n",i,lTimeCount[i]);
  851. */
  852. //------------------------------------------------------------------------------
  853. // palette fade out ? ...
  854. // Free resources:
  855. g_resmgrShell.Release(pimBackground);
  856. if (nonsak != NULL)
  857. {
  858. delete pScript;
  859. fclose(nonsak);
  860. }
  861. else
  862. {
  863. g_resmgrShell.Release(pScript);
  864. }
  865. // Clean Up:
  866. // Clear mouse and keyboard events
  867. rspClearKeyEvents();
  868. rspClearMouseEvents();
  869. // DO NOT Clear screen!
  870. /*
  871. rspLockBuffer();
  872. rspRect(RSP_BLACK_INDEX, g_pimScreenBuf, 0, 0, g_pimScreenBuf->m_sWidth, g_pimScreenBuf->m_sHeight);
  873. rspUnlockBuffer();
  874. */
  875. rspUpdateDisplay();
  876. if (lKey || sButtons || sJoyPress || rspGetQuitStatus()) return FAILURE;
  877. return SUCCESS;
  878. }
  879. ////////////////////////////////////////////////////////////////////////////////
  880. //
  881. // Display the credits - now with variable input
  882. //
  883. ////////////////////////////////////////////////////////////////////////////////
  884. // Returns 0 if successfull, non-zero otherwise
  885. short Credits(SampleMasterID* pMusic,
  886. char* pszBackground,
  887. char* pszCredits)
  888. {
  889. short sResult = 0;
  890. #ifdef SHOW_EXIT_SCREEN
  891. RImage* pimBackground;
  892. sResult = rspGetResource(&g_resmgrShell, EXIT_BG, &pimBackground);
  893. if (sResult == 0)
  894. {
  895. // Set palette
  896. ASSERT(pimBackground->m_pPalette != NULL);
  897. ASSERT(pimBackground->m_pPalette->m_type == RPal::PDIB);
  898. rspSetPaletteEntries(
  899. 0, //10,
  900. 256, // 236,
  901. pimBackground->m_pPalette->Red(0), //10
  902. pimBackground->m_pPalette->Green(0), //10
  903. pimBackground->m_pPalette->Blue(0), //10
  904. pimBackground->m_pPalette->m_sPalEntrySize);
  905. rspUpdatePalette();
  906. // Show title image.
  907. rspLockBuffer();
  908. rspBlit(
  909. pimBackground,
  910. g_pimScreenBuf,
  911. 0, 0,
  912. BG_X,
  913. BG_Y,
  914. pimBackground->m_sWidth,
  915. pimBackground->m_sHeight);
  916. rspUnlockBuffer();
  917. }
  918. rspUpdateDisplay();
  919. // Wait a while or until user input
  920. long lKey = 0;
  921. short sButtons = 0;
  922. do {
  923. UpdateSystem();
  924. // Get key and mouse button inputs
  925. short sButtons;
  926. rspGetMouse(NULL, NULL, &sButtons);
  927. rspGetKey(&lKey);
  928. // Clear mouse events to avoid overflowing the queue
  929. rspClearMouseEvents();
  930. } while (!lKey && !sButtons && rspGetQuitStatus() == FALSE);
  931. // Release bg
  932. g_resmgrShell.Release(pimBackground);
  933. #else
  934. SampleMasterID* psmidMusic;
  935. char szBackground[256];
  936. char szCredits[256];
  937. // Set defaults:
  938. psmidMusic = g_psmidMusic;
  939. strcpy(szBackground,g_szBackground);
  940. strcpy(szCredits,g_szCredits);
  941. // Set input parameters:
  942. if (pMusic) psmidMusic = pMusic;
  943. if (pszBackground) strcpy(szBackground,pszBackground);
  944. if (pszCredits) strcpy(szCredits,pszCredits);
  945. // Start credits music
  946. PlaySample( // Returns nothing.
  947. // Does not fail.
  948. *psmidMusic, // In: Identifier of sample you want played.
  949. SampleMaster::Unspecified, // In: Sound Volume Category for user adjustment
  950. 255, // In: Initial Sound Volume (0 - 255)
  951. &ms_siMusak, // Out: Handle for adjusting sound volume
  952. NULL, // Out: Sample duration in ms, if not NULL.
  953. MUSAK_START_TIME, // In: Where to loop back to in milliseconds.
  954. // -1 indicates no looping (unless m_sLoop is
  955. // explicitly set).
  956. MUSAK_END_TIME, // In: Where to loop back from in milliseconds.
  957. // In: If less than 1, the end + lLoopEndTime is used.
  958. true); // In: Call ReleaseAndPurge rather than Release after playing
  959. //------------------------------------------------------------------------------
  960. // Begin scrolling loop....
  961. RRect rect(0,80,wideScreenWidth,320);
  962. if (ScrollPage(szBackground,szCredits,0.12,&rect) != SUCCESS)
  963. {
  964. // USER aborted!
  965. // Stop the musak
  966. if (ms_siMusak)
  967. {
  968. // Don't just allow it to finish.
  969. StopLoopingSample(ms_siMusak);
  970. // Cut it off.
  971. AbortSample(ms_siMusak);
  972. // Clear.
  973. ms_siMusak = 0;
  974. // Play final sample that completes the cut off sound. ***
  975. }
  976. return SUCCESS;
  977. }
  978. // fade out the musak: cross fade the laughter
  979. SampleMaster::SoundInstance siLaughter;
  980. UnlockAchievement(ACHIEVEMENT_WATCH_ALL_CREDITS);
  981. // Start silent laughter looping:
  982. PlaySample(
  983. g_smidDemonLaugh2,
  984. SampleMaster::Unspecified,
  985. 0,
  986. &siLaughter,
  987. NULL,
  988. 0,
  989. 0,
  990. true);
  991. long lTimeToFade = 15000;
  992. long lStartTime = rspGetMilliseconds();
  993. // Copy the palette:
  994. UCHAR PaletteCopy[256 * 3] = {0,};
  995. rspGetPaletteEntries(10,236,PaletteCopy+30,PaletteCopy + 31,PaletteCopy + 32,3);
  996. //====================================================================
  997. long lCurTime;
  998. while ( (lCurTime = rspGetMilliseconds() - lStartTime) < lTimeToFade)
  999. {
  1000. short sByteLevel = short((255.0 * lCurTime) / lTimeToFade);
  1001. if (sByteLevel > 255) sByteLevel = 255;
  1002. // Update the sound volumes:
  1003. SetInstanceVolume(ms_siMusak,255 - sByteLevel);
  1004. SetInstanceVolume(siLaughter,sByteLevel);
  1005. // Update the Palette:
  1006. short i=0;
  1007. short sCurPal = 10 * 3;
  1008. for (i=10; i < 246; i++, sCurPal += 3)
  1009. {
  1010. rspSetPaletteEntry( i,
  1011. (PaletteCopy[sCurPal + 0] * (256 - sByteLevel)) / 256,
  1012. (PaletteCopy[sCurPal + 1] * (256 - sByteLevel)) / 256,
  1013. (PaletteCopy[sCurPal + 2] * (256 - sByteLevel)) / 256
  1014. );
  1015. }
  1016. rspUpdatePalette();
  1017. UpdateSystem();
  1018. }
  1019. //====================================================================
  1020. // Stop the musak
  1021. if (ms_siMusak)
  1022. {
  1023. // Don't just allow it to finish.
  1024. StopLoopingSample(ms_siMusak);
  1025. }
  1026. // Stop the musak
  1027. if (siLaughter)
  1028. {
  1029. // Don't just allow it to finish.
  1030. StopLoopingSample(siLaughter);
  1031. }
  1032. // Stop the musak
  1033. if (ms_siMusak)
  1034. {
  1035. // Cut it off.
  1036. AbortSample(ms_siMusak);
  1037. // Clear.
  1038. ms_siMusak = 0;
  1039. // Play final sample that completes the cut off sound. ***
  1040. }
  1041. if (siLaughter)
  1042. {
  1043. while (IsSamplePlaying(siLaughter) == true)
  1044. {
  1045. UpdateSystem();
  1046. }
  1047. }
  1048. //===========================================================================
  1049. // Stop the musak
  1050. if (siLaughter)
  1051. {
  1052. // Cut it off.
  1053. AbortSample(siLaughter);
  1054. // Clear.
  1055. siLaughter = 0;
  1056. // Play final sample that completes the cut off sound. ***
  1057. }
  1058. #endif
  1059. return SUCCESS;
  1060. }
  1061. ////////////////////////////////////////////////////////////////////////////////
  1062. // EOF
  1063. ////////////////////////////////////////////////////////////////////////////////