PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/engines/kyra/script_tim.cpp

http://github.com/scummvm/scummvm
C++ | 1101 lines | 849 code | 212 blank | 40 comment | 211 complexity | a2ac0bffe9422308fcc96325878a891b MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0
  1. /* ScummVM - Graphic Adventure Engine
  2. *
  3. * ScummVM is the legal property of its developers, whose names
  4. * are too numerous to list here. Please refer to the COPYRIGHT
  5. * file distributed with this source distribution.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. *
  21. */
  22. #include "kyra/script_tim.h"
  23. #include "kyra/resource.h"
  24. #include "kyra/sound.h"
  25. #ifdef ENABLE_LOL
  26. #include "kyra/lol.h"
  27. #include "kyra/screen_lol.h"
  28. #endif // ENABLE_LOL
  29. #include "common/iff_container.h"
  30. #include "common/system.h"
  31. namespace Kyra {
  32. TIMInterpreter::TIMInterpreter(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system) : _vm(engine), _screen(screen_v2), _system(system), _currentTim(0) {
  33. #define COMMAND(x) { &TIMInterpreter::x, #x }
  34. #define COMMAND_UNIMPL() { 0, 0 }
  35. #define cmd_return(n) cmd_return_##n
  36. static const CommandEntry commandProcs[] = {
  37. // 0x00
  38. COMMAND(cmd_initFunc0),
  39. COMMAND(cmd_stopCurFunc),
  40. COMMAND(cmd_initWSA),
  41. COMMAND(cmd_uninitWSA),
  42. // 0x04
  43. COMMAND(cmd_initFunc),
  44. COMMAND(cmd_stopFunc),
  45. COMMAND(cmd_wsaDisplayFrame),
  46. COMMAND(cmd_displayText),
  47. // 0x08
  48. COMMAND(cmd_loadVocFile),
  49. COMMAND(cmd_unloadVocFile),
  50. COMMAND(cmd_playVocFile),
  51. COMMAND_UNIMPL(),
  52. // 0x0C
  53. COMMAND(cmd_loadSoundFile),
  54. COMMAND(cmd_return(1)),
  55. COMMAND(cmd_playMusicTrack),
  56. COMMAND_UNIMPL(),
  57. // 0x10
  58. COMMAND(cmd_return(1)),
  59. COMMAND(cmd_return(1)),
  60. COMMAND_UNIMPL(),
  61. COMMAND_UNIMPL(),
  62. // 0x14
  63. COMMAND(cmd_setLoopIp),
  64. COMMAND(cmd_continueLoop),
  65. COMMAND(cmd_resetLoopIp),
  66. COMMAND(cmd_resetAllRuntimes),
  67. // 0x18
  68. COMMAND(cmd_return(1)),
  69. COMMAND(cmd_execOpcode),
  70. COMMAND(cmd_initFuncNow),
  71. COMMAND(cmd_stopFuncNow),
  72. // 0x1C
  73. COMMAND(cmd_return(1)),
  74. COMMAND(cmd_return(1)),
  75. COMMAND(cmd_return(n1))
  76. };
  77. #undef cmd_return
  78. #undef COMMAND_UNIMPL
  79. #undef COMMAND
  80. _commands = commandProcs;
  81. _commandsSize = ARRAYSIZE(commandProcs);
  82. _langData = 0;
  83. _textDisplayed = false;
  84. _textAreaBuffer = new uint8[320*40];
  85. assert(_textAreaBuffer);
  86. if ((_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().isDemo) && _vm->game() == GI_LOL)
  87. _drawPage2 = 0;
  88. else
  89. _drawPage2 = 8;
  90. _animator = new TimAnimator(0, screen_v2, 0, false);
  91. _palDelayInc = _palDiff = _palDelayAcc = 0;
  92. _abortFlag = 0;
  93. _tim = 0;
  94. }
  95. TIMInterpreter::~TIMInterpreter() {
  96. delete[] _langData;
  97. delete[] _textAreaBuffer;
  98. delete _animator;
  99. }
  100. bool TIMInterpreter::callback(Common::IFFChunk &chunk) {
  101. switch (chunk._type) {
  102. case MKTAG('T','E','X','T'):
  103. _tim->text = new byte[chunk._size];
  104. assert(_tim->text);
  105. if (chunk._stream->read(_tim->text, chunk._size) != chunk._size)
  106. error("Couldn't read TEXT chunk from file '%s'", _filename);
  107. break;
  108. case MKTAG('A','V','T','L'):
  109. _avtlChunkSize = chunk._size >> 1;
  110. _tim->avtl = new uint16[_avtlChunkSize];
  111. assert(_tim->avtl);
  112. if (chunk._stream->read(_tim->avtl, chunk._size) != chunk._size)
  113. error("Couldn't read AVTL chunk from file '%s'", _filename);
  114. for (int i = _avtlChunkSize - 1; i >= 0; --i)
  115. _tim->avtl[i] = READ_LE_UINT16(&_tim->avtl[i]);
  116. break;
  117. default:
  118. warning("Unexpected chunk '%s' of size %d found in file '%s'", tag2str(chunk._type), chunk._size, _filename);
  119. }
  120. return false;
  121. }
  122. TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpcode *> *opcodes) {
  123. if (!_vm->resource()->exists(filename))
  124. return 0;
  125. Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
  126. if (!stream)
  127. error("Couldn't open TIM file '%s'", filename);
  128. _avtlChunkSize = 0;
  129. _filename = filename;
  130. _tim = new TIM;
  131. assert(_tim);
  132. memset(_tim, 0, sizeof(TIM));
  133. _tim->procFunc = -1;
  134. _tim->opcodes = opcodes;
  135. IFFParser iff(*stream);
  136. Common::Functor1Mem<Common::IFFChunk &, bool, TIMInterpreter> c(this, &TIMInterpreter::callback);
  137. iff.parse(c);
  138. if (!_tim->avtl)
  139. error("No AVTL chunk found in file: '%s'", filename);
  140. if (stream->err())
  141. error("Read error while parsing file '%s'", filename);
  142. delete stream;
  143. const int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs;
  144. for (int i = 0; i < num; ++i)
  145. _tim->func[i].avtl = _tim->avtl + _tim->avtl[i];
  146. Common::strlcpy(_tim->filename, filename, 13);
  147. _tim->isLoLOutro = (_vm->game() == GI_LOL) && !scumm_stricmp(filename, "LOLFINAL.TIM");
  148. _tim->lolCharacter = 0;
  149. TIM *r = _tim;
  150. _tim = 0;
  151. return r;
  152. }
  153. void TIMInterpreter::unload(TIM *&tim) const {
  154. if (!tim)
  155. return;
  156. delete[] tim->text;
  157. delete[] tim->avtl;
  158. delete tim;
  159. tim = 0;
  160. }
  161. void TIMInterpreter::setLangData(const char *filename) {
  162. delete[] _langData;
  163. _langData = _vm->resource()->fileData(filename, 0);
  164. }
  165. int TIMInterpreter::exec(TIM *tim, bool loop) {
  166. if (!tim)
  167. return 0;
  168. _currentTim = tim;
  169. if (!_currentTim->func[0].ip) {
  170. _currentTim->func[0].ip = _currentTim->func[0].avtl;
  171. _currentTim->func[0].nextTime = _currentTim->func[0].lastTime = _system->getMillis();
  172. }
  173. do {
  174. update();
  175. for (_currentFunc = 0; _currentFunc < TIM::kCountFuncs; ++_currentFunc) {
  176. TIM::Function &cur = _currentTim->func[_currentFunc];
  177. if (_currentTim->procFunc != -1)
  178. execCommand(28, &_currentTim->procParam);
  179. update();
  180. checkSpeechProgress();
  181. bool running = true;
  182. int cnt = 0;
  183. while (cur.ip && cur.nextTime <= _system->getMillis() && running) {
  184. if (cnt++ > 0) {
  185. if (_currentTim->procFunc != -1)
  186. execCommand(28, &_currentTim->procParam);
  187. update();
  188. }
  189. int8 opcode = int8(cur.ip[2] & 0xFF);
  190. switch (execCommand(opcode, cur.ip + 3)) {
  191. case -1:
  192. loop = false;
  193. running = false;
  194. _currentFunc = 11;
  195. break;
  196. case -2:
  197. running = false;
  198. break;
  199. case -3:
  200. _currentTim->procFunc = _currentFunc;
  201. _currentTim->dlgFunc = -1;
  202. break;
  203. case 22:
  204. cur.loopIp = 0;
  205. break;
  206. default:
  207. break;
  208. }
  209. if (cur.ip) {
  210. cur.ip += cur.ip[0];
  211. cur.lastTime = cur.nextTime;
  212. cur.nextTime += cur.ip[1] * _vm->tickLength();
  213. }
  214. }
  215. }
  216. } while (loop && !_vm->shouldQuit());
  217. return _currentTim->clickedButton;
  218. }
  219. void TIMInterpreter::refreshTimersAfterPause(uint32 elapsedTime) {
  220. if (!_currentTim)
  221. return;
  222. for (int i = 0; i < TIM::kCountFuncs; i++) {
  223. if (_currentTim->func[i].lastTime)
  224. _currentTim->func[i].lastTime += elapsedTime;
  225. if (_currentTim->func[i].nextTime)
  226. _currentTim->func[i].nextTime += elapsedTime;
  227. }
  228. }
  229. void TIMInterpreter::displayText(uint16 textId, int16 flags) {
  230. char *text = getTableEntry(textId);
  231. if (_textDisplayed) {
  232. _screen->copyBlockToPage(0, 0, 160, 320, 40, _textAreaBuffer);
  233. _textDisplayed = false;
  234. }
  235. if (!text)
  236. return;
  237. if (!text[0])
  238. return;
  239. char filename[16];
  240. memset(filename, 0, sizeof(filename));
  241. if (text[0] == '$') {
  242. const char *end = strchr(text+1, '$');
  243. if (end)
  244. memcpy(filename, text+1, end-1-text);
  245. }
  246. const bool sjisMode = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode);
  247. if (filename[0] && (_vm->speechEnabled() || !_vm->gameFlags().isTalkie))
  248. _vm->sound()->voicePlay(filename, 0, 255, 255, !_vm->gameFlags().isTalkie);
  249. if (text[0] == '$')
  250. text = strchr(text + 1, '$') + 1;
  251. if (!_vm->gameFlags().use16ColorMode)
  252. setupTextPalette((flags < 0) ? 1 : flags, 0);
  253. if (flags < 0) {
  254. static const uint8 colorMap[] = { 0x00, 0xF0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  255. _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
  256. _screen->setTextColorMap(colorMap);
  257. _screen->_charWidth = -2;
  258. }
  259. _screen->_charOffset = -4;
  260. _screen->copyRegionToBuffer(0, 0, 160, 320, 40, _textAreaBuffer);
  261. _textDisplayed = true;
  262. char backupChar = 0;
  263. char *str = text;
  264. int heightAdd = 0;
  265. while (str[0] && _vm->textEnabled()) {
  266. char *nextLine = strchr(str, '\r');
  267. backupChar = 0;
  268. if (nextLine) {
  269. backupChar = nextLine[0];
  270. nextLine[0] = '\0';
  271. }
  272. int width = _screen->getTextWidth(str);
  273. if (flags >= 0) {
  274. if (_vm->gameFlags().use16ColorMode) {
  275. static const uint8 colorMap[] = { 0xE1, 0xE1, 0xC1, 0xA1, 0x81, 0x61 };
  276. _screen->printText(str, (320 - width) >> 1, 160 + heightAdd, colorMap[flags], 0x00);
  277. } else {
  278. _screen->printText(str, (320 - width) >> 1, 160 + heightAdd, 0xF0, 0x00);
  279. }
  280. } else {
  281. _screen->printText(str, (320 - width) >> 1, 188, 0xF0, 0x00);
  282. }
  283. heightAdd += _screen->getFontHeight();
  284. str += strlen(str);
  285. if (backupChar) {
  286. nextLine[0] = backupChar;
  287. ++str;
  288. }
  289. }
  290. _screen->_charOffset = 0;
  291. if (flags < 0) {
  292. static const uint8 colorMap[] = { 0x00, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00 };
  293. _screen->setFont(sjisMode ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT);
  294. _screen->setTextColorMap(colorMap);
  295. _screen->_charWidth = 0;
  296. }
  297. }
  298. void TIMInterpreter::displayText(uint16 textId, int16 flags, uint8 color) {
  299. if (!_vm->textEnabled() && !(textId & 0x8000))
  300. return;
  301. char *text = getTableEntry(textId & 0x7FFF);
  302. if (flags > 0)
  303. _screen->copyBlockToPage(0, 0, 0, 320, 40, _textAreaBuffer);
  304. if (flags == 255)
  305. return;
  306. _screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_INTRO_FNT);
  307. static const uint8 colorMap[] = { 0x00, 0xA0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  308. _screen->setTextColorMap(colorMap);
  309. _screen->_charWidth = 0;
  310. if (!_vm->gameFlags().use16ColorMode)
  311. _screen->_charOffset = -4;
  312. if (!flags)
  313. _screen->copyRegionToBuffer(0, 0, 0, 320, 40, _textAreaBuffer);
  314. char backupChar = 0;
  315. char *str = text;
  316. int y = 0;
  317. if (_vm->gameFlags().use16ColorMode) {
  318. if (color == 0xDA)
  319. color = 0xA1;
  320. else if (color == 0xF2)
  321. color = 0xE1;
  322. else if (flags < 0)
  323. color = 0xE1;
  324. else
  325. color = 0xC1;
  326. }
  327. while (str[0]) {
  328. char *nextLine = strchr(str, '\r');
  329. backupChar = 0;
  330. if (nextLine) {
  331. backupChar = nextLine[0];
  332. nextLine[0] = '\0';
  333. }
  334. int width = _screen->getTextWidth(str);
  335. if (flags >= 0)
  336. _screen->printText(str, (320 - width) >> 1, y, color, 0x00);
  337. else
  338. _screen->printText(str, 0, y, color, 0x00);
  339. y += (_vm->gameFlags().use16ColorMode ? 16 : (_screen->getFontHeight() - 4));
  340. str += strlen(str);
  341. if (backupChar) {
  342. nextLine[0] = backupChar;
  343. ++str;
  344. }
  345. }
  346. }
  347. void TIMInterpreter::setupTextPalette(uint index, int fadePalette) {
  348. static const uint16 palTable[] = {
  349. 0x00, 0x00, 0x00,
  350. 0x64, 0x64, 0x64,
  351. 0x61, 0x51, 0x30,
  352. 0x29, 0x48, 0x64,
  353. 0x00, 0x4B, 0x3B,
  354. 0x64, 0x1E, 0x1E,
  355. };
  356. for (int i = 0; i < 15; ++i) {
  357. uint8 *palette = _screen->getPalette(0).getData() + (240 + i) * 3;
  358. uint8 c1 = (((15 - i) << 2) * palTable[index*3+0]) / 100;
  359. uint8 c2 = (((15 - i) << 2) * palTable[index*3+1]) / 100;
  360. uint8 c3 = (((15 - i) << 2) * palTable[index*3+2]) / 100;
  361. palette[0] = c1;
  362. palette[1] = c2;
  363. palette[2] = c3;
  364. }
  365. if (!fadePalette && !_palDiff) {
  366. _screen->setScreenPalette(_screen->getPalette(0));
  367. } else {
  368. _screen->getFadeParams(_screen->getPalette(0), fadePalette, _palDelayInc, _palDiff);
  369. _palDelayAcc = 0;
  370. }
  371. }
  372. int TIMInterpreter::initAnimStruct(int index, const char *filename, int x, int y, int, int offscreenBuffer, uint16 wsaFlags) {
  373. Movie *wsa = 0;
  374. const bool isLoLDemo = _vm->gameFlags().isDemo && _vm->game() == GI_LOL;
  375. if (isLoLDemo || _vm->gameFlags().platform == Common::kPlatformPC98 || _currentTim->isLoLOutro)
  376. _drawPage2 = 0;
  377. else
  378. _drawPage2 = 8;
  379. uint16 wsaOpenFlags = 0;
  380. if (isLoLDemo) {
  381. if (!(wsaFlags & 0x10))
  382. wsaOpenFlags |= 1;
  383. } else {
  384. if (wsaFlags & 0x10)
  385. wsaOpenFlags |= 2;
  386. wsaOpenFlags |= 1;
  387. if (offscreenBuffer == 2)
  388. wsaOpenFlags = 1;
  389. }
  390. Common::String file = Common::String::format("%s.WSA", filename);
  391. if (_vm->resource()->exists(file.c_str())) {
  392. if (isLoLDemo)
  393. wsa = new WSAMovie_v1(_vm);
  394. else
  395. wsa = new WSAMovie_v2(_vm);
  396. assert(wsa);
  397. wsa->open(file.c_str(), wsaOpenFlags, (index == 1) ? &_screen->getPalette(0) : 0);
  398. }
  399. if (wsa && wsa->opened()) {
  400. if (isLoLDemo) {
  401. if (x == -1) {
  402. int16 t = int8(320 - wsa->width());
  403. uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8);
  404. v >>= 1;
  405. x = v;
  406. }
  407. if (y == -1) {
  408. int16 t = int8(200 - wsa->height());
  409. uint8 v = int8(t & 0x00FF) - int8((t & 0xFF00) >> 8);
  410. v >>= 1;
  411. y = v;
  412. }
  413. } else {
  414. if (x == -1)
  415. x = 0;
  416. if (y == -1)
  417. y = 0;
  418. }
  419. if (wsaFlags & 2) {
  420. _screen->fadePalette(_screen->getPalette(1), 15, 0);
  421. _screen->clearPage(_drawPage2);
  422. if (_drawPage2)
  423. _screen->checkedPageUpdate(8, 4);
  424. _screen->updateScreen();
  425. }
  426. if (wsaFlags & 4) {
  427. file = Common::String::format("%s.CPS", filename);
  428. if (_vm->resource()->exists(file.c_str())) {
  429. _screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0));
  430. _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK);
  431. if (_drawPage2)
  432. _screen->checkedPageUpdate(8, 4);
  433. _screen->updateScreen();
  434. }
  435. wsa->displayFrame(0, 0, x, y, 0, 0, 0);
  436. }
  437. if (wsaFlags & 2)
  438. _screen->fadePalette(_screen->getPalette(0), 30, 0);
  439. } else {
  440. if (wsaFlags & 2) {
  441. _screen->fadePalette(_screen->getPalette(1), 15, 0);
  442. _screen->clearPage(_drawPage2);
  443. if (_drawPage2)
  444. _screen->checkedPageUpdate(8, 4);
  445. _screen->updateScreen();
  446. }
  447. file = Common::String::format("%s.CPS", filename);
  448. if (_vm->resource()->exists(file.c_str())) {
  449. _screen->loadBitmap(file.c_str(), 3, 3, &_screen->getPalette(0));
  450. _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, _drawPage2, Screen::CR_NO_P_CHECK);
  451. if (_drawPage2)
  452. _screen->checkedPageUpdate(8, 4);
  453. _screen->updateScreen();
  454. }
  455. if (wsaFlags & 2)
  456. _screen->fadePalette(_screen->getPalette(0), 30, 0);
  457. }
  458. _animator->init(index, wsa, x, y, wsaFlags, 0);
  459. return index + 1;
  460. }
  461. int TIMInterpreter::freeAnimStruct(int index) {
  462. _animator->reset(index, true);
  463. return 1;
  464. }
  465. char *TIMInterpreter::getTableEntry(uint idx) {
  466. if (!_langData)
  467. return 0;
  468. else
  469. return (char *)(_langData + READ_LE_UINT16(_langData + (idx<<1)));
  470. }
  471. const char *TIMInterpreter::getCTableEntry(uint idx) const {
  472. if (!_langData)
  473. return 0;
  474. else
  475. return (const char *)(_langData + READ_LE_UINT16(_langData + (idx<<1)));
  476. }
  477. int TIMInterpreter::execCommand(int cmd, const uint16 *param) {
  478. if (cmd < 0 || cmd >= _commandsSize) {
  479. warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
  480. return 0;
  481. }
  482. if (_commands[cmd].proc == 0) {
  483. warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
  484. return 0;
  485. }
  486. debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param);
  487. return (this->*_commands[cmd].proc)(param);
  488. }
  489. int TIMInterpreter::cmd_initFunc0(const uint16 *param) {
  490. for (int i = 0; i < TIM::kWSASlots; ++i)
  491. memset(&_currentTim->wsa[i], 0, sizeof(TIM::WSASlot));
  492. _currentTim->func[0].ip = _currentTim->func[0].avtl;
  493. _currentTim->func[0].lastTime = _system->getMillis();
  494. return 1;
  495. }
  496. int TIMInterpreter::cmd_stopCurFunc(const uint16 *param) {
  497. if (_currentFunc < TIM::kCountFuncs)
  498. _currentTim->func[_currentFunc].ip = 0;
  499. if (!_currentFunc)
  500. _finished = true;
  501. return -2;
  502. }
  503. void TIMInterpreter::stopAllFuncs(TIM *tim) {
  504. for (int i = 0; i < TIM::kCountFuncs; ++i)
  505. tim->func[i].ip = 0;
  506. }
  507. int TIMInterpreter::cmd_initWSA(const uint16 *param) {
  508. const int index = param[0];
  509. TIM::WSASlot &slot = _currentTim->wsa[index];
  510. slot.x = int16(param[2]);
  511. slot.y = int16(param[3]);
  512. slot.offscreen = param[4];
  513. slot.wsaFlags = param[5];
  514. const char *filename = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[1]<<1)));
  515. slot.anim = initAnimStruct(index, filename, slot.x, slot.y, 10, slot.offscreen, slot.wsaFlags);
  516. return 1;
  517. }
  518. int TIMInterpreter::cmd_uninitWSA(const uint16 *param) {
  519. const int index = param[0];
  520. TIM::WSASlot &slot = _currentTim->wsa[index];
  521. if (!slot.anim)
  522. return 0;
  523. if (slot.offscreen) {
  524. _animator->reset(index, false);
  525. slot.anim = 0;
  526. } else {
  527. //XXX
  528. _animator->reset(index, true);
  529. memset(&slot, 0, sizeof(TIM::WSASlot));
  530. }
  531. return 1;
  532. }
  533. int TIMInterpreter::cmd_initFunc(const uint16 *param) {
  534. uint16 func = *param;
  535. assert(func < TIM::kCountFuncs);
  536. if (_currentTim->func[func].avtl)
  537. _currentTim->func[func].ip = _currentTim->func[func].avtl;
  538. else
  539. _currentTim->func[func].avtl = _currentTim->func[func].ip = _currentTim->avtl + _currentTim->avtl[func];
  540. return 1;
  541. }
  542. int TIMInterpreter::cmd_stopFunc(const uint16 *param) {
  543. uint16 func = *param;
  544. assert(func < TIM::kCountFuncs);
  545. _currentTim->func[func].ip = 0;
  546. return 1;
  547. }
  548. int TIMInterpreter::cmd_wsaDisplayFrame(const uint16 *param) {
  549. _animator->displayFrame(param[0], _drawPage2, param[1]);
  550. return 1;
  551. }
  552. int TIMInterpreter::cmd_displayText(const uint16 *param) {
  553. if (_currentTim->isLoLOutro)
  554. displayText(param[0], param[1], 0xF2);
  555. else
  556. displayText(param[0], param[1]);
  557. return 1;
  558. }
  559. int TIMInterpreter::cmd_loadVocFile(const uint16 *param) {
  560. const int stringId = param[0];
  561. const int index = param[1];
  562. _vocFiles[index] = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (stringId << 1)));
  563. if (index == 2 && _currentTim->isLoLOutro && _vm->gameFlags().isTalkie) {
  564. _vocFiles[index] = "CONGRATA.VOC";
  565. switch (_currentTim->lolCharacter) {
  566. case 0:
  567. _vocFiles[index].setChar('K', 7);
  568. break;
  569. case 1:
  570. _vocFiles[index].setChar('A', 7);
  571. break;
  572. case 2:
  573. _vocFiles[index].setChar('M', 7);
  574. break;
  575. case 3:
  576. _vocFiles[index].setChar('C', 7);
  577. break;
  578. default:
  579. break;
  580. }
  581. }
  582. for (int i = 0; i < 4; ++i)
  583. _vocFiles[index].deleteLastChar();
  584. return 1;
  585. }
  586. int TIMInterpreter::cmd_unloadVocFile(const uint16 *param) {
  587. const int index = param[0];
  588. _vocFiles[index].clear();
  589. return 1;
  590. }
  591. int TIMInterpreter::cmd_playVocFile(const uint16 *param) {
  592. const int index = param[0];
  593. const int volume = (param[1] * 255) / 100;
  594. if (index < ARRAYSIZE(_vocFiles) && !_vocFiles[index].empty())
  595. _vm->sound()->voicePlay(_vocFiles[index].c_str(), 0, volume, 255, true);
  596. else if (index == 7 && !_vm->gameFlags().isTalkie)
  597. _vm->sound()->playTrack(index);
  598. else
  599. _vm->sound()->playSoundEffect(index);
  600. return 1;
  601. }
  602. int TIMInterpreter::cmd_loadSoundFile(const uint16 *param) {
  603. const char *file = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[0]<<1)));
  604. _vm->sound()->loadSoundFile(file);
  605. if (_vm->game() == GI_LOL)
  606. _vm->sound()->loadSfxFile(file);
  607. return 1;
  608. }
  609. int TIMInterpreter::cmd_playMusicTrack(const uint16 *param) {
  610. _vm->sound()->playTrack(param[0]);
  611. return 1;
  612. }
  613. int TIMInterpreter::cmd_setLoopIp(const uint16 *param) {
  614. _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
  615. return 1;
  616. }
  617. int TIMInterpreter::cmd_continueLoop(const uint16 *param) {
  618. TIM::Function &func = _currentTim->func[_currentFunc];
  619. if (!func.loopIp)
  620. return -2;
  621. func.ip = func.loopIp;
  622. uint16 factor = param[0];
  623. if (factor) {
  624. const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000);
  625. uint32 waitTime = (random * factor) / 0x8000;
  626. func.nextTime += waitTime * _vm->tickLength();
  627. }
  628. return -2;
  629. }
  630. int TIMInterpreter::cmd_resetLoopIp(const uint16 *param) {
  631. _currentTim->func[_currentFunc].loopIp = 0;
  632. return 1;
  633. }
  634. int TIMInterpreter::cmd_resetAllRuntimes(const uint16 *param) {
  635. for (int i = 0; i < TIM::kCountFuncs; ++i) {
  636. if (_currentTim->func[i].ip)
  637. _currentTim->func[i].nextTime = _system->getMillis();
  638. }
  639. return 1;
  640. }
  641. int TIMInterpreter::cmd_execOpcode(const uint16 *param) {
  642. const uint16 opcode = *param++;
  643. if (!_currentTim->opcodes) {
  644. warning("Trying to execute TIM opcode %d without opcode list (file '%s')", opcode, _currentTim->filename);
  645. return 0;
  646. }
  647. if (opcode > _currentTim->opcodes->size()) {
  648. warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename);
  649. return 0;
  650. }
  651. if (!(*_currentTim->opcodes)[opcode]->isValid()) {
  652. warning("Calling unimplemented TIM opcode(0x%.02X/%d) from file '%s'", opcode, opcode, _currentTim->filename);
  653. return 0;
  654. }
  655. return (*(*_currentTim->opcodes)[opcode])(_currentTim, param);
  656. }
  657. int TIMInterpreter::cmd_initFuncNow(const uint16 *param) {
  658. uint16 func = *param;
  659. assert(func < TIM::kCountFuncs);
  660. _currentTim->func[func].ip = _currentTim->func[func].avtl;
  661. _currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis();
  662. return 1;
  663. }
  664. int TIMInterpreter::cmd_stopFuncNow(const uint16 *param) {
  665. uint16 func = *param;
  666. assert(func < TIM::kCountFuncs);
  667. _currentTim->func[func].ip = 0;
  668. _currentTim->func[func].lastTime = _currentTim->func[func].nextTime = _system->getMillis();
  669. return 1;
  670. }
  671. // TODO: Consider moving to another file
  672. #ifdef ENABLE_LOL
  673. // LOL version of the TIM interpreter
  674. TIMInterpreter_LoL::TIMInterpreter_LoL(LoLEngine *engine, Screen_v2 *screen_v2, OSystem *system) :
  675. TIMInterpreter(engine, screen_v2, system), _vm(engine) {
  676. #define COMMAND(x) { &TIMInterpreter_LoL::x, #x }
  677. #define COMMAND_UNIMPL() { 0, 0 }
  678. #define cmd_return(n) cmd_return_##n
  679. static const CommandEntry commandProcs[] = {
  680. // 0x00
  681. COMMAND(cmd_initFunc0),
  682. COMMAND(cmd_stopAllFuncs),
  683. COMMAND(cmd_initWSA),
  684. COMMAND(cmd_uninitWSA),
  685. // 0x04
  686. COMMAND(cmd_initFunc),
  687. COMMAND(cmd_stopFunc),
  688. COMMAND(cmd_wsaDisplayFrame),
  689. COMMAND_UNIMPL(),
  690. // 0x08
  691. COMMAND(cmd_loadVocFile),
  692. COMMAND(cmd_unloadVocFile),
  693. COMMAND(cmd_playVocFile),
  694. COMMAND_UNIMPL(),
  695. // 0x0C
  696. COMMAND(cmd_loadSoundFile),
  697. COMMAND(cmd_return(1)),
  698. COMMAND(cmd_playMusicTrack),
  699. COMMAND_UNIMPL(),
  700. // 0x10
  701. COMMAND(cmd_return(1)),
  702. COMMAND(cmd_return(1)),
  703. COMMAND_UNIMPL(),
  704. COMMAND_UNIMPL(),
  705. // 0x14
  706. COMMAND(cmd_setLoopIp),
  707. COMMAND(cmd_continueLoop),
  708. COMMAND(cmd_resetLoopIp),
  709. COMMAND(cmd_resetAllRuntimes),
  710. // 0x18
  711. COMMAND(cmd_return(1)),
  712. COMMAND(cmd_execOpcode),
  713. COMMAND(cmd_initFuncNow),
  714. COMMAND(cmd_stopFuncNow),
  715. // 0x1C
  716. COMMAND(cmd_processDialogue),
  717. COMMAND(cmd_dialogueBox),
  718. COMMAND(cmd_return(n1))
  719. };
  720. #undef cmd_return
  721. #undef COMMAND_UNIMPL
  722. #undef COMMAND
  723. _commands = commandProcs;
  724. _commandsSize = ARRAYSIZE(commandProcs);
  725. _screen = engine->_screen;
  726. delete _animator;
  727. _animator = new TimAnimator(engine, screen_v2, system, true);
  728. _drawPage2 = 0;
  729. }
  730. int TIMInterpreter_LoL::initAnimStruct(int index, const char *filename, int x, int y, int frameDelay, int, uint16 wsaFlags) {
  731. Movie *wsa = 0;
  732. uint16 wsaOpenFlags = 0;
  733. if (wsaFlags & 0x10)
  734. wsaOpenFlags |= 2;
  735. if (wsaFlags & 8)
  736. wsaOpenFlags |= 1;
  737. Common::String file = Common::String::format("%s.WSA", filename);
  738. if (_vm->resource()->exists(file.c_str())) {
  739. wsa = new WSAMovie_v2(_vm);
  740. assert(wsa);
  741. wsa->open(file.c_str(), wsaOpenFlags, &_screen->getPalette(3));
  742. }
  743. if (!_vm->_flags.use16ColorMode) {
  744. if (wsaFlags & 1) {
  745. if (_screen->_fadeFlag != 1)
  746. _screen->fadeClearSceneWindow(10);
  747. _screen->getPalette(3).copy(_screen->getPalette(0), 128, 128);
  748. } else if (wsaFlags & 2) {
  749. _screen->fadeToBlack(10);
  750. }
  751. }
  752. if (wsa && (wsaFlags & 7))
  753. wsa->displayFrame(0, 0, x, y, 0, 0, 0);
  754. if (wsaFlags & 3) {
  755. if (_vm->_flags.use16ColorMode) {
  756. _vm->setPaletteBrightness(_screen->getPalette(0), _vm->_brightness, _vm->_lampEffect);
  757. } else {
  758. _screen->loadSpecialColors(_screen->getPalette(3));
  759. _screen->fadePalette(_screen->getPalette(3), 10);
  760. }
  761. _screen->_fadeFlag = 0;
  762. }
  763. _animator->init(index, wsa, x, y, wsaFlags, frameDelay);
  764. return index + 1;
  765. }
  766. int TIMInterpreter_LoL::freeAnimStruct(int index) {
  767. _animator->reset(index, true);
  768. return 1;
  769. }
  770. void TIMInterpreter_LoL::advanceToOpcode(int opcode) {
  771. TIM::Function *f = &_currentTim->func[_currentTim->dlgFunc];
  772. uint16 len = f->ip[0];
  773. while ((f->ip[2] & 0xFF) != opcode) {
  774. if ((f->ip[2] & 0xFF) == 1) {
  775. f->ip[0] = len;
  776. break;
  777. }
  778. len = f->ip[0];
  779. f->ip += len;
  780. }
  781. f->nextTime = _system->getMillis();
  782. }
  783. void TIMInterpreter_LoL::resetDialogueState(TIM *tim) {
  784. if (!tim)
  785. return;
  786. tim->procFunc = 0;
  787. tim->procParam = _vm->_dialogueNumButtons ? _vm->_dialogueNumButtons : 1;
  788. tim->clickedButton = 0;
  789. tim->dlgFunc = -1;
  790. }
  791. void TIMInterpreter_LoL::update() {
  792. _vm->update();
  793. }
  794. void TIMInterpreter_LoL::checkSpeechProgress() {
  795. if (_vm->speechEnabled() && _currentTim->procParam > 1 && _currentTim->func[_currentFunc].loopIp) {
  796. if (_vm->snd_updateCharacterSpeech() != 2) {
  797. _currentTim->func[_currentFunc].loopIp = 0;
  798. _currentTim->dlgFunc = _currentFunc;
  799. advanceToOpcode(21);
  800. _currentTim->dlgFunc = -1;
  801. _animator->reset(5, false);
  802. }
  803. }
  804. }
  805. char *TIMInterpreter_LoL::getTableString(int id) {
  806. return _vm->getLangString(id);
  807. }
  808. int TIMInterpreter_LoL::execCommand(int cmd, const uint16 *param) {
  809. if (cmd < 0 || cmd >= _commandsSize) {
  810. warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
  811. return 0;
  812. }
  813. if (_commands[cmd].proc == 0) {
  814. warning("Calling unimplemented TIM command %d from file '%s'", cmd, _currentTim->filename);
  815. return 0;
  816. }
  817. debugC(5, kDebugLevelScript, "TIMInterpreter::%s(%p)", _commands[cmd].desc, (const void *)param);
  818. return (this->*_commands[cmd].proc)(param);
  819. }
  820. int TIMInterpreter_LoL::cmd_stopAllFuncs(const uint16 *param) {
  821. while (_currentTim->dlgFunc == -1 && _currentTim->clickedButton == 0 && !_vm->shouldQuit()) {
  822. update();
  823. _currentTim->clickedButton = _vm->processDialogue();
  824. }
  825. for (int i = 0; i < TIM::kCountFuncs; ++i)
  826. _currentTim->func[i].ip = 0;
  827. return -1;
  828. }
  829. int TIMInterpreter_LoL::cmd_setLoopIp(const uint16 *param) {
  830. if (_vm->speechEnabled()) {
  831. if (_vm->snd_updateCharacterSpeech() == 2)
  832. _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
  833. else
  834. advanceToOpcode(21);
  835. } else {
  836. _currentTim->func[_currentFunc].loopIp = _currentTim->func[_currentFunc].ip;
  837. }
  838. return 1;
  839. }
  840. int TIMInterpreter_LoL::cmd_continueLoop(const uint16 *param) {
  841. TIM::Function &func = _currentTim->func[_currentFunc];
  842. if (!func.loopIp)
  843. return -2;
  844. func.ip = func.loopIp;
  845. if (_vm->snd_updateCharacterSpeech() != 2) {
  846. uint16 factor = param[0];
  847. if (factor) {
  848. const uint32 random = _vm->_rnd.getRandomNumberRng(0, 0x8000);
  849. uint32 waitTime = (random * factor) / 0x8000;
  850. func.nextTime += waitTime * _vm->tickLength();
  851. }
  852. }
  853. return -2;
  854. }
  855. int TIMInterpreter_LoL::cmd_processDialogue(const uint16 *param) {
  856. int res = _vm->processDialogue();
  857. if (!res || !_currentTim->procParam)
  858. return res;
  859. _vm->snd_stopSpeech(false);
  860. _currentTim->func[_currentTim->procFunc].loopIp = 0;
  861. _currentTim->dlgFunc = _currentTim->procFunc;
  862. _currentTim->procFunc = -1;
  863. _currentTim->clickedButton = res;
  864. _animator->reset(5, false);
  865. if (_currentTim->procParam)
  866. advanceToOpcode(21);
  867. return res;
  868. }
  869. int TIMInterpreter_LoL::cmd_dialogueBox(const uint16 *param) {
  870. uint16 func = *param;
  871. assert(func < TIM::kCountFuncs);
  872. _currentTim->procParam = func;
  873. _currentTim->clickedButton = 0;
  874. const char *tmpStr[3];
  875. int cnt = 0;
  876. for (int i = 1; i < 4; i++) {
  877. if (param[i] != 0xFFFF) {
  878. tmpStr[i-1] = getTableString(param[i]);
  879. cnt++;
  880. } else {
  881. tmpStr[i-1] = 0;
  882. }
  883. }
  884. _vm->setupDialogueButtons(cnt, tmpStr[0], tmpStr[1], tmpStr[2]);
  885. _vm->gui_notifyButtonListChanged();
  886. return -3;
  887. }
  888. #endif // ENABLE_LOL
  889. } // End of namespace Kyra