PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 2ms app.codeStats 1ms

/engines/kyra/lol.cpp

http://github.com/scummvm/scummvm
C++ | 4513 lines | 3520 code | 925 blank | 68 comment | 918 complexity | f942aa9b567e49540d9a3439c93ff8fe 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. #ifdef ENABLE_LOL
  23. #include "kyra/lol.h"
  24. #include "kyra/screen_lol.h"
  25. #include "kyra/resource.h"
  26. #include "kyra/timer.h"
  27. #include "kyra/util.h"
  28. #include "kyra/debugger.h"
  29. #include "kyra/sound.h"
  30. #include "audio/audiostream.h"
  31. #include "common/config-manager.h"
  32. #include "common/system.h"
  33. #include "common/translation.h"
  34. #include "backends/keymapper/keymapper.h"
  35. namespace Kyra {
  36. const char *const LoLEngine::kKeymapName = "lol";
  37. LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraRpgEngine(system, flags) {
  38. _screen = 0;
  39. _gui = 0;
  40. _tim = 0;
  41. _lang = 0;
  42. Common::Language lang = Common::parseLanguage(ConfMan.get("language"));
  43. if (lang == _flags.fanLang && _flags.replacedLang != Common::UNK_LANG)
  44. lang = _flags.replacedLang;
  45. switch (lang) {
  46. case Common::EN_ANY:
  47. case Common::EN_USA:
  48. case Common::EN_GRB:
  49. _lang = 0;
  50. break;
  51. case Common::FR_FRA:
  52. _lang = 1;
  53. break;
  54. case Common::DE_DEU:
  55. _lang = 2;
  56. break;
  57. case Common::JA_JPN:
  58. _lang = 0;
  59. break;
  60. default:
  61. warning("unsupported language, switching back to English");
  62. _lang = 0;
  63. }
  64. _chargenFrameTable = _flags.isTalkie ? _chargenFrameTableTalkie : _chargenFrameTableFloppy;
  65. _chargenWSA = 0;
  66. _lastUsedStringBuffer = 0;
  67. _landsFile = 0;
  68. _levelLangFile = 0;
  69. _lastMusicTrack = -1;
  70. _lastSfxTrack = -1;
  71. _curTlkFile = -1;
  72. _lastSpeaker = _lastSpeechId = _nextSpeechId = _nextSpeaker = -1;
  73. memset(_moneyColumnHeight, 0, sizeof(_moneyColumnHeight));
  74. _credits = 0;
  75. _itemsInPlay = 0;
  76. _itemProperties = 0;
  77. _itemInHand = 0;
  78. memset(_inventory, 0, sizeof(_inventory));
  79. memset(_charStatusFlags, 0, sizeof(_charStatusFlags));
  80. _inventoryCurItem = 0;
  81. _lastCharInventory = -1;
  82. _emcLastItem = -1;
  83. _itemIconShapes = _itemShapes = _gameShapes = _thrownShapes = _effectShapes = _fireballShapes = _healShapes = _healiShapes = 0;
  84. _levelShpList = _levelDatList = 0;
  85. _gameShapeMap = 0;
  86. memset(_monsterAnimType, 0, 3);
  87. _healOverlay = 0;
  88. _swarmSpellStatus = 0;
  89. _ingameMT32SoundIndex = _ingameGMSoundIndex = _ingamePCSpeakerSoundIndex = 0;
  90. _charSelection = -1;
  91. _characters = 0;
  92. _spellProperties = 0;
  93. _selectedSpell = 0;
  94. _updateCharNum = _portraitSpeechAnimMode = _textColorFlag = 0;
  95. _palUpdateTimer = _updatePortraitNext = 0;
  96. _lampStatusTimer = 0xFFFFFFFF;
  97. _weaponsDisabled = false;
  98. _charInventoryUnk = 0;
  99. _lastButtonShape = 0;
  100. _buttonPressTimer = 0;
  101. _selectedCharacter = 0;
  102. _suspendScript = false;
  103. _scriptDirection = 0;
  104. _compassDirectionIndex = -1;
  105. _compassStep = 0;
  106. _smoothScrollModeNormal = 1;
  107. _wllAutomapData = 0;
  108. _sceneXoffset = 112;
  109. _sceneShpDim = 13;
  110. _monsters = 0;
  111. _monsterProperties = 0;
  112. _lvlShapeIndex = 0;
  113. _partyAwake = true;
  114. _transparencyTable2 = 0;
  115. _transparencyTable1 = 0;
  116. _specialGuiShape = 0;
  117. _specialGuiShapeX = _specialGuiShapeY = _specialGuiShapeMirrorFlag = 0;
  118. memset(_characterFaceShapes, 0, sizeof(_characterFaceShapes));
  119. _lampEffect = _brightness = _lampOilStatus = 0;
  120. _lampStatusSuspended = false;
  121. _tempBuffer5120 = 0;
  122. _flyingObjects = 0;
  123. _monsters = 0;
  124. _lastMouseRegion = 0;
  125. _objectLastDirection = 0;
  126. _monsterCurBlock = 0;
  127. _seqWindowX1 = _seqWindowY1 = _seqWindowX2 = _seqWindowY2 = _seqTrigger = 0;
  128. _spsWindowX = _spsWindowY = _spsWindowW = _spsWindowH = 0;
  129. _dscWalls = 0;
  130. _dscOvlMap = 0;
  131. _dscShapeScaleW = 0;
  132. _dscShapeScaleH = 0;
  133. _dscShapeY = 0;
  134. _dscShapeOvlIndex = 0;
  135. _dscDoorMonsterX = _dscDoorMonsterY = 0;
  136. _dscDoor4 = 0;
  137. _ingameSoundList = 0;
  138. _ingameSoundIndex = 0;
  139. _ingameSoundListSize = 0;
  140. _musicTrackMap = 0;
  141. _curMusicTheme = -1;
  142. _curMusicFileExt = 0;
  143. _curMusicFileIndex = -1;
  144. _envSfxUseQueue = false;
  145. _envSfxNumTracksInQueue = 0;
  146. memset(_envSfxQueuedTracks, 0, sizeof(_envSfxQueuedTracks));
  147. memset(_envSfxQueuedBlocks, 0, sizeof(_envSfxQueuedBlocks));
  148. _partyPosX = _partyPosY = 0;
  149. _shpDmX = _shpDmY = _dmScaleW = _dmScaleH = 0;
  150. _floatingCursorControl = _currentFloatingCursor = 0;
  151. memset(_activeTim, 0, sizeof(_activeTim));
  152. memset(&_activeSpell, 0, sizeof(_activeSpell));
  153. _pageBuffer1 = _pageBuffer2 = 0;
  154. memset(_charStatsTemp, 0, sizeof(_charStatsTemp));
  155. _compassBroken = _drainMagic = 0;
  156. _buttonData = 0;
  157. _preserveEvents = false;
  158. _buttonList1 = _buttonList2 = _buttonList3 = _buttonList4 = _buttonList5 = _buttonList6 = _buttonList7 = _buttonList8 = 0;
  159. _mapOverlay = 0;
  160. _automapShapes = 0;
  161. _defaultLegendData = 0;
  162. _mapCursorOverlay = 0;
  163. _lightningProps = 0;
  164. _lightningCurSfx = -1;
  165. _lightningDiv = 0;
  166. _lightningFirstSfx = 0;
  167. _lightningSfxFrame = 0;
  168. _compassTimer = 0;
  169. _scriptCharacterCycle = 0;
  170. _partyDamageFlags = -1;
  171. memset(&_itemScript, 0, sizeof(_itemScript));
  172. }
  173. LoLEngine::~LoLEngine() {
  174. setupPrologueData(false);
  175. releaseTempData();
  176. delete[] _landsFile;
  177. delete[] _levelLangFile;
  178. delete _screen;
  179. _screen = 0;
  180. delete _gui;
  181. _gui = 0;
  182. delete _tim;
  183. _tim = 0;
  184. delete _txt;
  185. _txt = 0;
  186. delete[] _itemsInPlay;
  187. delete[] _itemProperties;
  188. delete[] _characters;
  189. delete[] _pageBuffer1;
  190. delete[] _pageBuffer2;
  191. if (_itemIconShapes) {
  192. for (int i = 0; i < _numItemIconShapes; i++)
  193. delete[] _itemIconShapes[i];
  194. delete[] _itemIconShapes;
  195. }
  196. if (_itemShapes) {
  197. for (int i = 0; i < _numItemShapes; i++)
  198. delete[] _itemShapes[i];
  199. delete[] _itemShapes;
  200. }
  201. if (_gameShapes) {
  202. for (int i = 0; i < _numGameShapes; i++)
  203. delete[] _gameShapes[i];
  204. delete[] _gameShapes;
  205. }
  206. if (_thrownShapes) {
  207. for (int i = 0; i < _numThrownShapes; i++)
  208. delete[] _thrownShapes[i];
  209. delete[] _thrownShapes;
  210. }
  211. if (_effectShapes) {
  212. for (int i = 0; i < _numEffectShapes; i++)
  213. delete[] _effectShapes[i];
  214. delete[] _effectShapes;
  215. }
  216. if (_fireballShapes) {
  217. for (int i = 0; i < _numFireballShapes; i++)
  218. delete[] _fireballShapes[i];
  219. delete[] _fireballShapes;
  220. }
  221. if (_healShapes) {
  222. for (int i = 0; i < _numHealShapes; i++)
  223. delete[] _healShapes[i];
  224. delete[] _healShapes;
  225. }
  226. if (_healiShapes) {
  227. for (int i = 0; i < _numHealiShapes; i++)
  228. delete[] _healiShapes[i];
  229. delete[] _healiShapes;
  230. }
  231. if (_monsterDecorationShapes) {
  232. for (int i = 0; i < 3; i++)
  233. releaseMonsterShapes(i);
  234. delete[] _monsterShapes;
  235. _monsterShapes = 0;
  236. delete[] _monsterPalettes;
  237. _monsterPalettes = 0;
  238. delete[] _monsterDecorationShapes;
  239. _monsterDecorationShapes = 0;
  240. }
  241. for (int i = 0; i < 6; i++) {
  242. delete[] _doorShapes[i];
  243. _doorShapes[i] = 0;
  244. }
  245. releaseDecorations();
  246. delete[] _automapShapes;
  247. for (Common::Array<const TIMOpcode *>::iterator i = _timIntroOpcodes.begin(); i != _timIntroOpcodes.end(); ++i)
  248. delete *i;
  249. _timIntroOpcodes.clear();
  250. for (Common::Array<const TIMOpcode *>::iterator i = _timOutroOpcodes.begin(); i != _timOutroOpcodes.end(); ++i)
  251. delete *i;
  252. _timOutroOpcodes.clear();
  253. for (Common::Array<const TIMOpcode *>::iterator i = _timIngameOpcodes.begin(); i != _timIngameOpcodes.end(); ++i)
  254. delete *i;
  255. _timIngameOpcodes.clear();
  256. delete[] _wllAutomapData;
  257. delete[] _tempBuffer5120;
  258. delete[] _flyingObjects;
  259. delete[] _monsters;
  260. delete[] _monsterProperties;
  261. delete[] _transparencyTable2;
  262. delete[] _transparencyTable1;
  263. delete[] _lightningProps;
  264. delete _lvlShpFileHandle;
  265. if (_ingameSoundList) {
  266. for (int i = 0; i < _ingameSoundListSize; i++)
  267. delete[] _ingameSoundList[i];
  268. delete[] _ingameSoundList;
  269. }
  270. for (int i = 0; i < 3; i++) {
  271. for (int ii = 0; ii < 40; ii++)
  272. delete[] _characterFaceShapes[ii][i];
  273. }
  274. delete[] _healOverlay;
  275. delete[] _defaultLegendData;
  276. delete[] _mapCursorOverlay;
  277. delete[] _mapOverlay;
  278. for (Common::Array<const SpellProc *>::iterator i = _spellProcs.begin(); i != _spellProcs.end(); ++i)
  279. delete *i;
  280. _spellProcs.clear();
  281. for (SpeechList::iterator i = _speechList.begin(); i != _speechList.end(); ++i)
  282. delete *i;
  283. _speechList.clear();
  284. _emc->unload(&_itemScript);
  285. _emc->unload(&_scriptData);
  286. }
  287. Screen *LoLEngine::screen() {
  288. return _screen;
  289. }
  290. GUI *LoLEngine::gui() const {
  291. return _gui;
  292. }
  293. Common::Error LoLEngine::init() {
  294. _screen = new Screen_LoL(this, _system);
  295. assert(_screen);
  296. _screen->setResolution();
  297. _debugger = new Debugger_LoL(this);
  298. assert(_debugger);
  299. KyraEngine_v1::init();
  300. initStaticResource();
  301. _gui = new GUI_LoL(this);
  302. assert(_gui);
  303. _gui->initStaticData();
  304. _txt = new TextDisplayer_LoL(this, _screen);
  305. _screen->setAnimBlockPtr(10000);
  306. _screen->setScreenDim(0);
  307. _pageBuffer1 = new uint8[0xFA00];
  308. memset(_pageBuffer1, 0, 0xFA00);
  309. _pageBuffer2 = new uint8[0xFA00];
  310. memset(_pageBuffer2, 0, 0xFA00);
  311. _itemsInPlay = new LoLItem[400];
  312. memset(_itemsInPlay, 0, sizeof(LoLItem) * 400);
  313. _characters = new LoLCharacter[4];
  314. memset(_characters, 0, sizeof(LoLCharacter) * 4);
  315. if (!_sound->init())
  316. error("Couldn't init sound");
  317. KyraRpgEngine::init();
  318. _wllAutomapData = new uint8[80];
  319. memset(_wllAutomapData, 0, 80);
  320. _monsters = new LoLMonster[30];
  321. memset(_monsters, 0, 30 * sizeof(LoLMonster));
  322. _monsterProperties = new LoLMonsterProperty[5];
  323. memset(_monsterProperties, 0, 5 * sizeof(LoLMonsterProperty));
  324. _tempBuffer5120 = new uint8[5120];
  325. memset(_tempBuffer5120, 0, 5120);
  326. _flyingObjects = new FlyingObject[_numFlyingObjects];
  327. _flyingObjectsPtr = _flyingObjects;
  328. _flyingObjectStructSize = sizeof(FlyingObject);
  329. memset(_flyingObjects, 0, _numFlyingObjects * sizeof(FlyingObject));
  330. memset(_globalScriptVars, 0, sizeof(_globalScriptVars));
  331. _lvlShpFileHandle = 0;
  332. _sceneDrawPage1 = 2;
  333. _sceneDrawPage2 = 6;
  334. _clickedShapeXOffs = 136;
  335. _clickedShapeYOffs = 8;
  336. _clickedSpecialFlag = 0x40;
  337. _monsterShapes = new uint8*[48];
  338. memset(_monsterShapes, 0, 48 * sizeof(uint8 *));
  339. _monsterPalettes = new uint8*[48];
  340. memset(_monsterPalettes, 0, 48 * sizeof(uint8 *));
  341. _monsterDecorationShapes = new uint8*[576];
  342. memset(_monsterDecorationShapes, 0, 576 * sizeof(uint8 *));
  343. memset(&_scriptData, 0, sizeof(EMCData));
  344. _activeMagicMenu = -1;
  345. _automapShapes = new const uint8*[109];
  346. _mapOverlay = new uint8[256];
  347. memset(_availableSpells, -1, 8);
  348. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castSpark));
  349. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castHeal));
  350. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castIce));
  351. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castFireball));
  352. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castHandOfFate));
  353. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castMistOfDoom));
  354. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castLightning));
  355. _spellProcs.push_back(new SpellProc(this, 0));
  356. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castFog));
  357. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castSwarm));
  358. _spellProcs.push_back(new SpellProc(this, 0));
  359. _spellProcs.push_back(new SpellProc(this, 0));
  360. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castVaelansCube));
  361. _spellProcs.push_back(new SpellProc(this, 0));
  362. _spellProcs.push_back(new SpellProc(this, 0));
  363. _spellProcs.push_back(new SpellProc(this, 0));
  364. _spellProcs.push_back(new SpellProc(this, &LoLEngine::castGuardian));
  365. #ifdef ENABLE_KEYMAPPER
  366. _eventMan->getKeymapper()->pushKeymap(kKeymapName, true);
  367. #endif
  368. return Common::kNoError;
  369. }
  370. void LoLEngine::initKeymap() {
  371. #ifdef ENABLE_KEYMAPPER
  372. Common::Keymapper *const mapper = _eventMan->getKeymapper();
  373. // Do not try to recreate same keymap over again
  374. if (mapper->getKeymap(kKeymapName) != 0)
  375. return;
  376. Common::Keymap *const engineKeyMap = new Common::Keymap(kKeymapName);
  377. const Common::KeyActionEntry keyActionEntries[] = {
  378. {Common::KeyState(Common::KEYCODE_F1, Common::ASCII_F1), "AT1", _("Attack 1")},
  379. {Common::KeyState(Common::KEYCODE_F2, Common::ASCII_F2), "AT2", _("Attack 2")},
  380. {Common::KeyState(Common::KEYCODE_F3, Common::ASCII_F3), "AT3", _("Attack 3")},
  381. {Common::KeyState(Common::KEYCODE_UP), "MVF", _("Move Forward")},
  382. {Common::KeyState(Common::KEYCODE_DOWN), "MVB", _("Move Back")},
  383. {Common::KeyState(Common::KEYCODE_LEFT), "SLL", _("Slide Left")},
  384. {Common::KeyState(Common::KEYCODE_RIGHT), "SLR", _("Slide Right")},
  385. {Common::KeyState(Common::KEYCODE_HOME), "TL", _("Turn Left")},
  386. {Common::KeyState(Common::KEYCODE_PAGEUP), "TR", _("Turn Right")},
  387. {Common::KeyState(Common::KEYCODE_r), "RST", _("Rest")},
  388. {Common::KeyState(Common::KEYCODE_o), "OPT", _("Options")},
  389. {Common::KeyState(Common::KEYCODE_SLASH), "SPL", _("Choose Spell")},
  390. {Common::KeyState(), 0, 0}
  391. };
  392. for (const Common::KeyActionEntry *entry = keyActionEntries; entry->id; ++entry) {
  393. Common::Action *const act = new Common::Action(engineKeyMap, entry->id, entry->description);
  394. act->addKeyEvent(entry->ks);
  395. }
  396. mapper->addGameKeymap(engineKeyMap);
  397. #endif
  398. }
  399. void LoLEngine::pauseEngineIntern(bool pause) {
  400. KyraEngine_v1::pauseEngineIntern(pause);
  401. pauseDemoPlayer(pause);
  402. }
  403. Common::Error LoLEngine::go() {
  404. int action = -1;
  405. if (_gameToLoad == -1) {
  406. action = processPrologue();
  407. if (action == -1)
  408. return Common::kNoError;
  409. }
  410. if (_flags.isTalkie && !_flags.isDemo) {
  411. if (!_res->loadFileList("FILEDATA.FDT"))
  412. error("Couldn't load file list: 'FILEDATA.FDT'");
  413. } else if (_pakFileList) {
  414. _res->loadFileList(_pakFileList, _pakFileListSize);
  415. }
  416. // Usually fonts etc. would be setup by the prologue code, if we skip
  417. // the prologue code we need to setup them manually here.
  418. if (_gameToLoad != -1 && action != 3) {
  419. preInit();
  420. _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT);
  421. }
  422. // We have three sound.dat files, one for the intro, one for the
  423. // end sequence and one for ingame, each contained in a different
  424. // PAK file. Therefore a new call to loadSoundFile() is required
  425. // whenever the PAK file configuration changes.
  426. if (_flags.platform == Common::kPlatformPC98)
  427. _sound->loadSoundFile("sound.dat");
  428. _sound->selectAudioResourceSet(kMusicIngame);
  429. if (_flags.platform != Common::kPlatformDOS)
  430. _sound->loadSoundFile(0);
  431. _tim = new TIMInterpreter_LoL(this, _screen, _system);
  432. assert(_tim);
  433. if (shouldQuit())
  434. return Common::kNoError;
  435. startup();
  436. if (action == 0) {
  437. startupNew();
  438. } else if (_gameToLoad != -1) {
  439. // FIXME: Instead of throwing away the error returned by
  440. // loadGameState, we should use it / augment it.
  441. if (loadGameState(_gameToLoad).getCode() != Common::kNoError)
  442. error("Couldn't load game slot %d on startup", _gameToLoad);
  443. _gameToLoad = -1;
  444. }
  445. _screen->_fadeFlag = 3;
  446. _sceneUpdateRequired = true;
  447. enableSysTimer(1);
  448. runLoop();
  449. return Common::kNoError;
  450. }
  451. #pragma mark - Initialization
  452. void LoLEngine::preInit() {
  453. _res->loadPakFile("GENERAL.PAK");
  454. if (_flags.isTalkie)
  455. _res->loadPakFile("STARTUP.PAK");
  456. _screen->loadFont(Screen::FID_9_FNT, "FONT9P.FNT");
  457. _screen->loadFont(Screen::FID_6_FNT, "FONT6P.FNT");
  458. loadTalkFile(0);
  459. Common::String filename;
  460. filename = Common::String::format("LANDS.%s", _languageExt[_lang]);
  461. _res->exists(filename.c_str(), true);
  462. delete[] _landsFile;
  463. _landsFile = _res->fileData(filename.c_str(), 0);
  464. loadItemIconShapes();
  465. }
  466. void LoLEngine::loadItemIconShapes() {
  467. if (_itemIconShapes) {
  468. for (int i = 0; i < _numItemIconShapes; i++)
  469. delete[] _itemIconShapes[i];
  470. delete[] _itemIconShapes;
  471. }
  472. _screen->loadBitmap("ITEMICN.SHP", 3, 3, 0);
  473. const uint8 *shp = _screen->getCPagePtr(3);
  474. _numItemIconShapes = READ_LE_UINT16(shp);
  475. _itemIconShapes = new uint8*[_numItemIconShapes];
  476. for (int i = 0; i < _numItemIconShapes; i++)
  477. _itemIconShapes[i] = _screen->makeShapeCopy(shp, i);
  478. _screen->setMouseCursor(0, 0, _itemIconShapes[0]);
  479. if (!_gameShapes) {
  480. _screen->loadBitmap("GAMESHP.SHP", 3, 3, 0);
  481. shp = _screen->getCPagePtr(3);
  482. _numGameShapes = READ_LE_UINT16(shp);
  483. _gameShapes = new uint8*[_numGameShapes];
  484. for (int i = 0; i < _numGameShapes; i++)
  485. _gameShapes[i] = _screen->makeShapeCopy(shp, i);
  486. }
  487. }
  488. void LoLEngine::setMouseCursorToIcon(int icon) {
  489. _flagsTable[31] |= 0x02;
  490. int i = _itemProperties[_itemsInPlay[_itemInHand].itemPropertyIndex].shpIndex;
  491. if (i == icon)
  492. return;
  493. _screen->setMouseCursor(0, 0, _itemIconShapes[icon]);
  494. }
  495. void LoLEngine::setMouseCursorToItemInHand() {
  496. _flagsTable[31] &= 0xFD;
  497. int o = (_itemInHand == 0) ? 0 : 10;
  498. _screen->setMouseCursor(o, o, getItemIconShapePtr(_itemInHand));
  499. }
  500. void LoLEngine::checkFloatingPointerRegions() {
  501. if (!_floatingCursorsEnabled)
  502. return;
  503. int t = -1;
  504. Common::Point p = getMousePos();
  505. if (!(_updateFlags & 4) & !_floatingCursorControl) {
  506. if (posWithinRect(p.x, p.y, 96, 0, 303, 136)) {
  507. if (!posWithinRect(p.x, p.y, 128, 16, 271, 119)) {
  508. if (posWithinRect(p.x, p.y, 112, 0, 287, 15))
  509. t = 0;
  510. if (posWithinRect(p.x, p.y, 272, 88, 303, 319))
  511. t = 1;
  512. if (posWithinRect(p.x, p.y, 112, 110, 287, 135))
  513. t = 2;
  514. if (posWithinRect(p.x, p.y, 96, 88, 127, 119))
  515. t = 3;
  516. if (posWithinRect(p.x, p.y, 96, 16, 127, 87))
  517. t = 4;
  518. if (posWithinRect(p.x, p.y, 272, 16, 303, 87))
  519. t = 5;
  520. if (t < 4) {
  521. int d = (_currentDirection + t) & 3;
  522. if (!checkBlockPassability(calcNewBlockPosition(_currentBlock, d), d))
  523. t = 6;
  524. }
  525. }
  526. }
  527. }
  528. if (t == _currentFloatingCursor)
  529. return;
  530. if (t == -1) {
  531. setMouseCursorToItemInHand();
  532. } else {
  533. static const uint8 floatingPtrX[] = { 7, 13, 7, 0, 0, 15, 7 };
  534. static const uint8 floatingPtrY[] = { 0, 7, 12, 7, 6, 6, 7 };
  535. _screen->setMouseCursor(floatingPtrX[t], floatingPtrY[t], _gameShapes[10 + t]);
  536. }
  537. _currentFloatingCursor = t;
  538. }
  539. uint8 *LoLEngine::getItemIconShapePtr(int index) {
  540. int ix = _itemProperties[_itemsInPlay[index].itemPropertyIndex].shpIndex;
  541. if (_itemProperties[_itemsInPlay[index].itemPropertyIndex].flags & 0x200)
  542. ix += (_itemsInPlay[index].shpCurFrame_flg & 0x1FFF) - 1;
  543. return _itemIconShapes[ix];
  544. }
  545. int LoLEngine::mainMenu() {
  546. bool hasSave = saveFileLoadable(0);
  547. MainMenu::StaticData data[] = {
  548. // 256 color ASCII mode
  549. {
  550. { 0, 0, 0, 0, 0 },
  551. { 0x01, 0x04, 0x0C, 0x04, 0x00, 0x3D, 0x9F },
  552. { 0x2C, 0x19, 0x48, 0x2C },
  553. Screen::FID_9_FNT, 1
  554. },
  555. // 16 color SJIS mode
  556. {
  557. { 0, 0, 0, 0, 0 },
  558. { 0x01, 0x04, 0x0C, 0x04, 0x00, 0xC1, 0xE1 },
  559. { 0xCC, 0xDD, 0xDD, 0xDD },
  560. Screen::FID_SJIS_FNT, 1
  561. }
  562. };
  563. int dataIndex = _flags.use16ColorMode ? 1 : 0;
  564. if (!_flags.isTalkie)
  565. --data[dataIndex].menuTable[3];
  566. if (hasSave)
  567. ++data[dataIndex].menuTable[3];
  568. static const uint16 mainMenuStrings[4][5] = {
  569. { 0x4248, 0x4249, 0x42DD, 0x424A, 0x0000 },
  570. { 0x4248, 0x4249, 0x42DD, 0x4001, 0x424A },
  571. { 0x4248, 0x4249, 0x424A, 0x0000, 0x0000 },
  572. { 0x4248, 0x4249, 0x4001, 0x424A, 0x0000 }
  573. };
  574. int tableOffs = _flags.isTalkie ? 0 : 2;
  575. for (int i = 0; i < 5; ++i) {
  576. if (hasSave)
  577. data[dataIndex].strings[i] = getLangString(mainMenuStrings[1 + tableOffs][i]);
  578. else
  579. data[dataIndex].strings[i] = getLangString(mainMenuStrings[tableOffs][i]);
  580. }
  581. MainMenu *menu = new MainMenu(this);
  582. assert(menu);
  583. menu->init(data[dataIndex], MainMenu::Animation());
  584. int selection = menu->handle(_flags.isTalkie ? (hasSave ? 19 : 6) : (hasSave ? 6 : 20));
  585. delete menu;
  586. _screen->setScreenDim(0);
  587. if (!_flags.isTalkie && selection >= 2)
  588. selection++;
  589. if (!hasSave && selection == 3)
  590. selection = 4;
  591. return selection;
  592. }
  593. void LoLEngine::startup() {
  594. _screen->clearPage(0);
  595. Palette &pal = _screen->getPalette(0);
  596. _screen->loadBitmap("PLAYFLD.CPS", 3, 3, &pal);
  597. if (_flags.use16ColorMode) {
  598. memset(_screen->_paletteOverlay1, 0, 256);
  599. memset(_screen->_paletteOverlay2, 0, 256);
  600. static const uint8 colTable1[] = { 0x00, 0xEE, 0xCC, 0xFF, 0x44, 0x66, 0x44, 0x88, 0xEE, 0xAA, 0x11, 0xCC, 0xDD, 0xEE, 0x44, 0xCC };
  601. static const uint8 colTable2[] = { 0x00, 0xCC, 0xFF, 0xBB, 0xEE, 0xBB, 0x55, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xFF, 0xCC, 0xDD, 0xBB };
  602. static const uint8 colTable3[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
  603. for (int i = 0; i < 16; i++) {
  604. _screen->_paletteOverlay1[colTable3[i]] = colTable1[i];
  605. _screen->_paletteOverlay2[colTable3[i]] = colTable2[i];
  606. }
  607. } else {
  608. _screen->copyPalette(1, 0);
  609. pal.fill(0, 1, 0x3F);
  610. pal.fill(2, 126, 0x3F);
  611. pal.fill(192, 4, 0x3F);
  612. _screen->generateOverlay(pal, _screen->_paletteOverlay1, 1, 96, 254);
  613. _screen->generateOverlay(pal, _screen->_paletteOverlay2, 144, 65, 254);
  614. _screen->copyPalette(0, 1);
  615. }
  616. _screen->getPalette(1).clear();
  617. _screen->getPalette(2).clear();
  618. loadItemIconShapes();
  619. _screen->setMouseCursor(0, 0, _itemIconShapes[0x85]);
  620. _screen->loadBitmap("ITEMSHP.SHP", 3, 3, 0);
  621. const uint8 *shp = _screen->getCPagePtr(3);
  622. _numItemShapes = READ_LE_UINT16(shp);
  623. _itemShapes = new uint8*[_numItemShapes];
  624. for (int i = 0; i < _numItemShapes; i++)
  625. _itemShapes[i] = _screen->makeShapeCopy(shp, i);
  626. _screen->loadBitmap("THROWN.SHP", 3, 3, 0);
  627. shp = _screen->getCPagePtr(3);
  628. _numThrownShapes = READ_LE_UINT16(shp);
  629. _thrownShapes = new uint8*[_numThrownShapes];
  630. for (int i = 0; i < _numThrownShapes; i++)
  631. _thrownShapes[i] = _screen->makeShapeCopy(shp, i);
  632. _screen->loadBitmap("ICE.SHP", 3, 3, 0);
  633. shp = _screen->getCPagePtr(3);
  634. _numEffectShapes = READ_LE_UINT16(shp);
  635. _effectShapes = new uint8*[_numEffectShapes];
  636. for (int i = 0; i < _numEffectShapes; i++)
  637. _effectShapes[i] = _screen->makeShapeCopy(shp, i);
  638. _screen->loadBitmap("FIREBALL.SHP", 3, 3, 0);
  639. shp = _screen->getCPagePtr(3);
  640. _numFireballShapes = READ_LE_UINT16(shp);
  641. _fireballShapes = new uint8*[_numFireballShapes];
  642. for (int i = 0; i < _numFireballShapes; i++)
  643. _fireballShapes[i] = _screen->makeShapeCopy(shp, i);
  644. _screen->loadBitmap("HEAL.SHP", 3, 3, 0);
  645. shp = _screen->getCPagePtr(3);
  646. _numHealShapes = READ_LE_UINT16(shp);
  647. _healShapes = new uint8*[_numHealShapes];
  648. for (int i = 0; i < _numHealShapes; i++)
  649. _healShapes[i] = _screen->makeShapeCopy(shp, i);
  650. _screen->loadBitmap("HEALI.SHP", 3, 3, 0);
  651. shp = _screen->getCPagePtr(3);
  652. _numHealiShapes = READ_LE_UINT16(shp);
  653. _healiShapes = new uint8*[_numHealiShapes];
  654. for (int i = 0; i < _numHealiShapes; i++)
  655. _healiShapes[i] = _screen->makeShapeCopy(shp, i);
  656. memset(_itemsInPlay, 0, 400 * sizeof(LoLItem));
  657. for (int i = 0; i < 400; i++)
  658. _itemsInPlay[i].shpCurFrame_flg |= 0x8000;
  659. runInitScript("ONETIME.INF", 0);
  660. _emc->load("ITEM.INF", &_itemScript, &_opcodes);
  661. _transparencyTable1 = new uint8[256];
  662. _transparencyTable2 = new uint8[5120];
  663. _loadSuppFilesFlag = 1;
  664. _sound->loadSfxFile("LORESFX");
  665. setMouseCursorToItemInHand();
  666. }
  667. void LoLEngine::startupNew() {
  668. _selectedSpell = 0;
  669. _compassStep = 0;
  670. _compassDirection = _compassDirectionIndex = -1;
  671. _lastMouseRegion = -1;
  672. _currentLevel = 1;
  673. giveCredits(41, 0);
  674. _inventory[0] = makeItem(216, 0, 0);
  675. _inventory[1] = makeItem(217, 0, 0);
  676. _inventory[2] = makeItem(218, 0, 0);
  677. _availableSpells[0] = 0;
  678. setupScreenDims();
  679. Common::fill(_globalScriptVars2, ARRAYEND(_globalScriptVars2), 0x100);
  680. static const int selectIds[] = { -9, -1, -8, -5 };
  681. assert(_charSelection >= 0);
  682. assert(_charSelection < ARRAYSIZE(selectIds));
  683. addCharacter(selectIds[_charSelection]);
  684. gui_enableDefaultPlayfieldButtons();
  685. loadLevel(_currentLevel);
  686. _screen->showMouse();
  687. }
  688. void LoLEngine::runLoop() {
  689. // Initialize debugger since how it should be fully usable
  690. _debugger->initialize();
  691. enableSysTimer(2);
  692. _flagsTable[73] |= 0x08;
  693. while (!shouldQuit()) {
  694. if (_gameToLoad != -1) {
  695. // FIXME: Instead of throwing away the error returned by
  696. // loadGameState, we should use it / augment it.
  697. if (loadGameState(_gameToLoad).getCode() != Common::kNoError)
  698. error("Couldn't load game slot %d", _gameToLoad);
  699. _gameToLoad = -1;
  700. }
  701. if (_nextScriptFunc) {
  702. runLevelScript(_nextScriptFunc, 2);
  703. _nextScriptFunc = 0;
  704. }
  705. _timer->update();
  706. checkFloatingPointerRegions();
  707. gui_updateInput();
  708. update();
  709. if (_sceneUpdateRequired)
  710. gui_drawScene(0);
  711. else
  712. updateEnvironmentalSfx(0);
  713. if (_partyDamageFlags != -1) {
  714. checkForPartyDeath();
  715. _partyDamageFlags = -1;
  716. }
  717. delay(_tickLength);
  718. }
  719. }
  720. void LoLEngine::registerDefaultSettings() {
  721. KyraEngine_v1::registerDefaultSettings();
  722. // Most settings already have sensible defaults. This one, however, is
  723. // specific to the LoL engine.
  724. ConfMan.registerDefault("floating_cursors", false);
  725. ConfMan.registerDefault("smooth_scrolling", true);
  726. ConfMan.registerDefault("monster_difficulty", 1);
  727. }
  728. void LoLEngine::writeSettings() {
  729. ConfMan.setInt("monster_difficulty", _monsterDifficulty);
  730. ConfMan.setBool("floating_cursors", _floatingCursorsEnabled);
  731. ConfMan.setBool("smooth_scrolling", _smoothScrollingEnabled);
  732. switch (_lang) {
  733. case 1:
  734. _flags.lang = Common::FR_FRA;
  735. break;
  736. case 2:
  737. _flags.lang = Common::DE_DEU;
  738. break;
  739. case 0:
  740. default:
  741. if (_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformFMTowns)
  742. _flags.lang = Common::JA_JPN;
  743. else
  744. _flags.lang = Common::EN_ANY;
  745. }
  746. if (_flags.lang == _flags.replacedLang && _flags.fanLang != Common::UNK_LANG)
  747. _flags.lang = _flags.fanLang;
  748. ConfMan.set("language", Common::getLanguageCode(_flags.lang));
  749. KyraEngine_v1::writeSettings();
  750. }
  751. void LoLEngine::readSettings() {
  752. _monsterDifficulty = ConfMan.getInt("monster_difficulty");
  753. if (_monsterDifficulty < 0 || _monsterDifficulty > 2) {
  754. _monsterDifficulty = CLIP(_monsterDifficulty, 0, 2);
  755. warning("LoLEngine: Config file contains invalid difficulty setting.");
  756. }
  757. _smoothScrollingEnabled = ConfMan.getBool("smooth_scrolling");
  758. _floatingCursorsEnabled = ConfMan.getBool("floating_cursors");
  759. KyraEngine_v1::readSettings();
  760. }
  761. void LoLEngine::update() {
  762. updateSequenceBackgroundAnimations();
  763. if (_updateCharNum != -1 && _system->getMillis() > _updatePortraitNext)
  764. updatePortraitSpeechAnim();
  765. if (_flagsTable[31] & 0x08 || !(_updateFlags & 4))
  766. updateLampStatus();
  767. if (_flagsTable[31] & 0x40 && !(_updateFlags & 4) && (_compassDirection == -1 || (_currentDirection << 6) != _compassDirection || _compassStep))
  768. updateCompass();
  769. snd_updateCharacterSpeech();
  770. fadeText();
  771. updateInput();
  772. _screen->updateScreen();
  773. }
  774. #pragma mark - Localization
  775. char *LoLEngine::getLangString(uint16 id) {
  776. if (id == 0xFFFF)
  777. return 0;
  778. uint16 realId = id & 0x3FFF;
  779. uint8 *buffer = 0;
  780. if (id & 0x4000)
  781. buffer = _landsFile;
  782. else
  783. buffer = _levelLangFile;
  784. if (!buffer)
  785. return 0;
  786. char *string = (char *)getTableEntry(buffer, realId);
  787. char *srcBuffer = _stringBuffer[_lastUsedStringBuffer];
  788. if (_flags.lang == Common::JA_JPN) {
  789. decodeSjis(string, srcBuffer);
  790. } else if (_flags.lang == Common::RU_RUS && !_flags.isTalkie) {
  791. decodeCyrillic(string, srcBuffer);
  792. Util::decodeString2(srcBuffer, srcBuffer);
  793. } else {
  794. Util::decodeString1(string, srcBuffer);
  795. Util::decodeString2(srcBuffer, srcBuffer);
  796. }
  797. ++_lastUsedStringBuffer;
  798. _lastUsedStringBuffer %= ARRAYSIZE(_stringBuffer);
  799. return srcBuffer;
  800. }
  801. uint8 *LoLEngine::getTableEntry(uint8 *buffer, uint16 id) {
  802. if (!buffer)
  803. return 0;
  804. return buffer + READ_LE_UINT16(buffer + (id << 1));
  805. }
  806. void LoLEngine::decodeSjis(const char *src, char *dst) {
  807. char s[2];
  808. char d[3];
  809. s[1] = 0;
  810. uint8 cmd = 0;
  811. while ((cmd = *src++) != 0) {
  812. if (cmd == 27) {
  813. cmd = *src++ & 0x7F;
  814. memcpy(dst, src, cmd * 2);
  815. dst += cmd * 2;
  816. src += cmd * 2;
  817. } else {
  818. s[0] = cmd;
  819. int size = Util::decodeString1(s, d);
  820. memcpy(dst, d, size);
  821. dst += size;
  822. }
  823. }
  824. *dst = 0;
  825. }
  826. int LoLEngine::decodeCyrillic(const char *src, char *dst) {
  827. static const uint8 decodeTable1[] = {
  828. 0x20, 0xAE, 0xA5, 0xA0, 0xE2, 0xAD, 0xA8, 0xE0, 0xE1, 0xAB, 0xA2,
  829. 0xA4, 0xAC, 0xAA, 0xE3, 0x2E
  830. };
  831. static const uint8 decodeTable2[] = {
  832. 0xAD, 0xAF, 0xA2, 0xE1, 0xAC, 0xAA, 0x20, 0xA4, 0xAB, 0x20,
  833. 0xE0, 0xE2, 0xA4, 0xA2, 0xA6, 0xAA, 0x20, 0xAD, 0xE2, 0xE0,
  834. 0xAB, 0xAC, 0xE1, 0xA1, 0x20, 0xAC, 0xE1, 0xAA, 0xAB, 0xE0,
  835. 0xE2, 0xAD, 0xAE, 0xEC, 0xA8, 0xA5, 0xA0, 0x20, 0xE0, 0xEB,
  836. 0xAE, 0xA0, 0xA8, 0xA5, 0xEB, 0xEF, 0x20, 0xE3, 0xE2, 0x20,
  837. 0xAD, 0xE7, 0xAB, 0xAC, 0xA5, 0xE0, 0xAE, 0xA0, 0xA5, 0xA8,
  838. 0xE3, 0xEB, 0xEF, 0xAA, 0xE2, 0xEF, 0xA5, 0xEC, 0xAB, 0xAE,
  839. 0xAA, 0xAF, 0xA8, 0xA0, 0xA5, 0xEF, 0xAE, 0xEE, 0xEC, 0xE3,
  840. 0xA0, 0xAE, 0xA5, 0xA8, 0xEB, 0x20, 0xE0, 0xE3, 0xA0, 0xA5,
  841. 0xAE, 0xA8, 0xE3, 0xE1, 0xAD, 0xAB, 0x20, 0xAE, 0xA5, 0xA0,
  842. 0xA8, 0xAD, 0x2E, 0xE3, 0xAE, 0xA0, 0xA8, 0x20, 0xE0, 0xE3,
  843. 0xAB, 0xE1, 0x20, 0xA4, 0xAD, 0xE2, 0xA1, 0xA6, 0xAC, 0xE1,
  844. 0x0D, 0x20, 0x2E, 0x09, 0xA0, 0xA1, 0x9D, 0xA5
  845. };
  846. int size = 0;
  847. uint cChar = 0;
  848. while ((cChar = *src++) != 0) {
  849. if (cChar & 0x80) {
  850. cChar &= 0x7F;
  851. int index = (cChar & 0x78) >> 3;
  852. *dst++ = decodeTable1[index];
  853. ++size;
  854. assert(cChar < sizeof(decodeTable2));
  855. cChar = decodeTable2[cChar];
  856. } else if (cChar >= 0x70) {
  857. cChar = *src++;
  858. } else if (cChar >= 0x30) {
  859. if (cChar < 0x60)
  860. cChar -= 0x30;
  861. cChar |= 0x80;
  862. }
  863. *dst++ = cChar;
  864. ++size;
  865. }
  866. *dst++ = 0;
  867. return size;
  868. }
  869. bool LoLEngine::addCharacter(int id) {
  870. const uint16 *cdf[] = {
  871. _charDefsMan, _charDefsMan, _charDefsMan, _charDefsWoman,
  872. _charDefsMan, _charDefsMan, _charDefsWoman, _charDefsKieran, _charDefsAkshel
  873. };
  874. int numChars = countActiveCharacters();
  875. if (numChars == 4)
  876. return false;
  877. int i = 0;
  878. for (; i < _charDefaultsSize; i++) {
  879. if (_charDefaults[i].id == id) {
  880. memcpy(&_characters[numChars], &_charDefaults[i], sizeof(LoLCharacter));
  881. _characters[numChars].defaultModifiers = cdf[i];
  882. break;
  883. }
  884. }
  885. if (i == _charDefaultsSize)
  886. return false;
  887. loadCharFaceShapes(numChars, id);
  888. _characters[numChars].nextAnimUpdateCountdown = rollDice(1, 12) + 6;
  889. for (i = 0; i < 11; i++) {
  890. if (_characters[numChars].items[i]) {
  891. _characters[numChars].items[i] = makeItem(_characters[numChars].items[i], 0, 0);
  892. runItemScript(numChars, _characters[numChars].items[i], 0x80, 0, 0);
  893. }
  894. }
  895. calcCharPortraitXpos();
  896. if (numChars > 0)
  897. setTemporaryFaceFrame(numChars, 2, 6, 0);
  898. return true;
  899. }
  900. void LoLEngine::setTemporaryFaceFrame(int charNum, int frame, int updateDelay, int redraw) {
  901. _characters[charNum].tempFaceFrame = frame;
  902. if (frame || updateDelay)
  903. setCharacterUpdateEvent(charNum, 6, updateDelay, 1);
  904. if (redraw)
  905. gui_drawCharPortraitWithStats(charNum);
  906. }
  907. void LoLEngine::setTemporaryFaceFrameForAllCharacters(int frame, int updateDelay, int redraw) {
  908. for (int i = 0; i < 4; i++)
  909. setTemporaryFaceFrame(i, frame, updateDelay, 0);
  910. if (redraw)
  911. gui_drawAllCharPortraitsWithStats();
  912. }
  913. void LoLEngine::setCharacterUpdateEvent(int charNum, int updateType, int updateDelay, int overwrite) {
  914. LoLCharacter *l = &_characters[charNum];
  915. for (int i = 0; i < 5; i++) {
  916. if (l->characterUpdateEvents[i] && (!overwrite || l->characterUpdateEvents[i] != updateType))
  917. continue;
  918. l->characterUpdateEvents[i] = updateType;
  919. l->characterUpdateDelay[i] = updateDelay;
  920. _timer->setNextRun(3, _system->getMillis());
  921. _timer->resetNextRun();
  922. _timer->enable(3);
  923. break;
  924. }
  925. }
  926. int LoLEngine::countActiveCharacters() {
  927. int i = 0;
  928. while (_characters[i].flags & 1)
  929. i++;
  930. return i;
  931. }
  932. void LoLEngine::loadCharFaceShapes(int charNum, int id) {
  933. if (id < 0)
  934. id = -id;
  935. Common::String file = Common::String::format("FACE%02d.SHP", id);
  936. _screen->loadBitmap(file.c_str(), 3, 3, 0);
  937. const uint8 *p = _screen->getCPagePtr(3);
  938. for (int i = 0; i < 40; i++) {
  939. delete[] _characterFaceShapes[i][charNum];
  940. _characterFaceShapes[i][charNum] = _screen->makeShapeCopy(p, i);
  941. }
  942. }
  943. void LoLEngine::updatePortraitSpeechAnim() {
  944. int x = 0;
  945. int y = 0;
  946. bool redraw = false;
  947. if (_portraitSpeechAnimMode == 0) {
  948. x = _activeCharsXpos[_updateCharNum];
  949. y = 144;
  950. redraw = true;
  951. } else if (_portraitSpeechAnimMode == 1) {
  952. if (textEnabled()) {
  953. x = 90;
  954. y = 130;
  955. } else {
  956. x = _activeCharsXpos[_updateCharNum];
  957. y = 144;
  958. }
  959. } else if (_portraitSpeechAnimMode == 2) {
  960. if (textEnabled()) {
  961. x = 16;
  962. y = 134;
  963. } else {
  964. x = _activeCharsXpos[_updateCharNum] + 10;
  965. y = 145;
  966. }
  967. }
  968. int f = rollDice(1, 6) - 1;
  969. if (f == _characters[_updateCharNum].curFaceFrame)
  970. f++;
  971. if (f > 5)
  972. f -= 5;
  973. f += 7;
  974. if (speechEnabled()) {
  975. if (snd_updateCharacterSpeech() == 2)
  976. // WORKAROUND for portrait speech animations which would "freeze" in some situations
  977. if (_resetPortraitAfterSpeechAnim == 2)
  978. _resetPortraitAfterSpeechAnim = 1;
  979. else
  980. _updatePortraitSpeechAnimDuration = 2;
  981. else
  982. _updatePortraitSpeechAnimDuration = 1;
  983. } else if (_resetPortraitAfterSpeechAnim == 2) {
  984. _resetPortraitAfterSpeechAnim = 1;
  985. }
  986. _updatePortraitSpeechAnimDuration--;
  987. if (_updatePortraitSpeechAnimDuration) {
  988. setCharFaceFrame(_updateCharNum, f);
  989. if (redraw)
  990. gui_drawCharPortraitWithStats(_updateCharNum);
  991. else
  992. gui_drawCharFaceShape(_updateCharNum, x, y, 0);
  993. _updatePortraitNext = _system->getMillis() + 10 * _tickLength;
  994. } else if (_resetPortraitAfterSpeechAnim != 0) {
  995. faceFrameRefresh(_updateCharNum);
  996. if (redraw) {
  997. gui_drawCharPortraitWithStats(_updateCharNum);
  998. initTextFading(0, 0);
  999. } else {
  1000. gui_drawCharFaceShape(_updateCharNum, x, y, 0);
  1001. }
  1002. _updateCharNum = -1;
  1003. }
  1004. }
  1005. void LoLEngine::stopPortraitSpeechAnim() {
  1006. if (_updateCharNum == -1)
  1007. return;
  1008. _updatePortraitSpeechAnimDuration = 1;
  1009. // WORKAROUND for portrait speech animations which would "freeze" in some situations
  1010. _resetPortraitAfterSpeechAnim = 2;
  1011. updatePortraitSpeechAnim();
  1012. _updatePortraitSpeechAnimDuration = 1;
  1013. _updateCharNum = -1;
  1014. if (!_portraitSpeechAnimMode)
  1015. initTextFading(0, 0);
  1016. }
  1017. void LoLEngine::initTextFading(int textType, int clearField) {
  1018. if (_textColorFlag == textType || !textType) {
  1019. _fadeText = true;
  1020. _palUpdateTimer = _system->getMillis();
  1021. }
  1022. if (!clearField)
  1023. return;
  1024. stopPortraitSpeechAnim();
  1025. if (_needSceneRestore)
  1026. _screen->setScreenDim(_txt->clearDim(3));
  1027. _fadeText = false;
  1028. _timer->disable(11);
  1029. }
  1030. void LoLEngine::setCharFaceFrame(int charNum, int frameNum) {
  1031. _characters[charNum].curFaceFrame = frameNum;
  1032. }
  1033. void LoLEngine::faceFrameRefresh(int charNum) {
  1034. if (_characters[charNum].curFaceFrame == 1)
  1035. setTemporaryFaceFrame(charNum, 0, 0, 0);
  1036. else if (_characters[charNum].curFaceFrame == 6)
  1037. if (_characters[charNum].tempFaceFrame != 5)
  1038. setTemporaryFaceFrame(charNum, 0, 0, 0);
  1039. else
  1040. _characters[charNum].curFaceFrame = 5;
  1041. else
  1042. _characters[charNum].curFaceFrame = 0;
  1043. }
  1044. void LoLEngine::recalcCharacterStats(int charNum) {
  1045. for (int i = 0; i < 5; i++)
  1046. _charStatsTemp[i] = calculateCharacterStats(charNum, i);
  1047. }
  1048. int LoLEngine::calculateCharacterStats(int charNum, int index) {
  1049. if (index == 0) {
  1050. // Might
  1051. int c = 0;
  1052. for (int i = 0; i < 8; i++)
  1053. c += _characters[charNum].itemsMight[i];
  1054. if (c)
  1055. c += _characters[charNum].might;
  1056. else
  1057. c = _characters[charNum].defaultModifiers[8];
  1058. c = (c * _characters[charNum].defaultModifiers[1]) >> 8;
  1059. c = (c * _characters[charNum].totalMightModifier) >> 8;
  1060. return c;
  1061. } else if (index == 1) {
  1062. // Protection
  1063. return calculateProtection(charNum);
  1064. } else if (index > 4) {
  1065. return -1;
  1066. } else {
  1067. // Fighter
  1068. // Rogue
  1069. // Mage
  1070. index -= 2;
  1071. return _characters[charNum].skillLevels[index] + _characters[charNum].skillModifiers[index];
  1072. }
  1073. //return 1;
  1074. }
  1075. int LoLEngine::calculateProtection(int index) {
  1076. int c = 0;
  1077. if (index & 0x8000) {
  1078. // Monster
  1079. index &= 0x7FFF;
  1080. c = (_monsters[index].properties->itemProtection * _monsters[index].properties->fightingStats[2]) >> 8;
  1081. } else {
  1082. // Character
  1083. c = _characters[index].itemProtection + _characters[index].protection;
  1084. c = (c * _characters[index].defaultModifiers[2]) >> 8;
  1085. c = (c * _characters[index].totalProtectionModifier) >> 8;
  1086. }
  1087. return c;
  1088. }
  1089. void LoLEngine::setCharacterMagicOrHitPoints(int charNum, int type, int points, int mode) {
  1090. static const uint16 barData[4][5] = {
  1091. // xPos, bar color, text color, flag, string id
  1092. { 0x27, 0x9A, 0x98, 0x01, 0x4254 },
  1093. { 0x21, 0xA2, 0xA0, 0x00, 0x4253 },
  1094. // 16 color mode
  1095. { 0x27, 0x66, 0x55, 0x01, 0x4254 },
  1096. { 0x21, 0xAA, 0x99, 0x00, 0x4253 }
  1097. };
  1098. if (charNum > 2)
  1099. return;
  1100. LoLCharacter *c = &_characters[charNum];
  1101. if (!(c->flags & 1))
  1102. return;
  1103. int pointsMax = type ? c->magicPointsMax : c->hitPointsMax;
  1104. int pointsCur = type ? c->magicPointsCur : c->hitPointsCur;
  1105. int newVal = (mode == 2) ? (pointsMax + points) : (mode ? (pointsCur + points) : points);
  1106. newVal = CLIP(newVal, 0, pointsMax);
  1107. if (type) {
  1108. c->magicPointsCur = newVal;
  1109. } else {
  1110. c->hitPointsCur = newVal;
  1111. if (c->hitPointsCur < 1)
  1112. c->flags |= 8;
  1113. }
  1114. if (_updateFlags & 2)
  1115. return;
  1116. Screen::FontId cf = _screen->setFont(Screen::FID_6_FNT);
  1117. int cp = _screen->setCurPage(0);
  1118. int s = 8192 / pointsMax;
  1119. pointsMax = (s * pointsMax) >> 8;
  1120. pointsCur = (s * pointsCur) >> 8;
  1121. newVal = (s * newVal) >> 8;
  1122. int newValScl = CLIP(newVal, 0, pointsMax);
  1123. int step = (newVal > pointsCur) ? 2 : -2;
  1124. newVal = CLIP(newVal + step, 0, pointsMax);
  1125. if (_flags.use16ColorMode)
  1126. type += 2;
  1127. if (newVal != pointsCur) {
  1128. step = (newVal >= pointsCur) ? 2 : -2;
  1129. for (int i = pointsCur; i != newVal || newVal != newValScl;) {
  1130. if (ABS(i - newVal) < ABS(step))
  1131. step >>= 1;
  1132. i += step;
  1133. uint32 delayTimer = _system->getMillis() + _tickLength;
  1134. gui_drawLiveMagicBar(barData[type][0] + _activeCharsXpos[charNum], 175, i, 0, pointsMax, 5, 32, barData[type][1], _flags.use16ColorMode ? 0x44 : 1, barData[type][3]);
  1135. _screen->printText(getLangString(barData[type][4]), barData[type][0] + _activeCharsXpos[charNum], 144, barData[type][2], 0);
  1136. _screen->updateScreen();
  1137. if (i == newVal) {
  1138. newVal = newValScl;
  1139. step = -step;
  1140. }
  1141. delayUntil(delayTimer);
  1142. }
  1143. }
  1144. _screen->setFont(cf);
  1145. _screen->setCurPage(cp);
  1146. }
  1147. void LoLEngine::increaseExperience(int charNum, int skill, uint32 points) {
  1148. if (charNum & 0x8000)
  1149. return;
  1150. if (_characters[charNum].flags & 8)
  1151. return;
  1152. _characters[charNum].experiencePts[skill] += points;
  1153. bool loop = true;
  1154. while (loop) {
  1155. if (_characters[charNum].experiencePts[skill] < _expRequirements[_characters[charNum].skillLevels[skill]])
  1156. break;
  1157. _characters[charNum].skillLevels[skill]++;
  1158. _characters[charNum].flags |= (0x200 << skill);
  1159. int inc = 0;
  1160. switch (skill) {
  1161. case 0:
  1162. _txt->printMessage(0x8003, getLangString(0x4023), _characters[charNum].name);
  1163. inc = rollDice(4, 6);
  1164. _characters[charNum].hitPointsCur += inc;
  1165. _characters[charNum].hitPointsMax += inc;
  1166. break;
  1167. case 1:
  1168. _txt->printMessage(0x8003, getLangString(0x4025), _characters[charNum].name);
  1169. inc = rollDice(2, 6);
  1170. _characters[charNum].hitPointsCur += inc;
  1171. _characters[charNum].hitPointsMax += inc;
  1172. break;
  1173. case 2:
  1174. _txt->printMessage(0x8003, getLangString(0x4024), _characters[charNum].name);
  1175. inc = (_characters[charNum].defaultModifiers[6] * (rollDice(1, 8) + 17)) >> 8;
  1176. _characters[charNum].magicPointsCur += inc;
  1177. _characters[charNum].magicPointsMax += inc;
  1178. inc = rollDice(1, 6);
  1179. _characters[charNum].hitPointsCur += inc;
  1180. _characters[charNum].hitPointsMax += inc;
  1181. break;
  1182. default:
  1183. break;
  1184. }
  1185. snd_playSoundEffect(118, -1);
  1186. gui_drawCharPortraitWithStats(charNum);
  1187. }
  1188. }
  1189. void LoLEngine::increaseCharacterHitpoints(int charNum, int points, bool ignoreDeath) {
  1190. if (_characters[charNum].hitPointsCur <= 0 && !ignoreDeath)
  1191. return;
  1192. if (points <= 1)
  1193. points = 1;
  1194. _characters[charNum].hitPointsCur = CLIP<int16>(_characters[charNum].hitPointsCur + points, 1, _characters[charNum].hitPointsMax);
  1195. _characters[charNum].flags &= 0xFFF7;
  1196. }
  1197. void LoLEngine::setupScreenDims() {
  1198. if (textEnabled()) {
  1199. _screen->modifyScreenDim(4, 11, 124, 28, 45);
  1200. _screen->modifyScreenDim(5, 85, 123, 233, 54);
  1201. } else {
  1202. _screen->modifyScreenDim(4, 11, 124, 28, 9);
  1203. _screen->modifyScreenDim(5, 85, 123, 233, 18);
  1204. }
  1205. }
  1206. void LoLEngine::initSceneWindowDialogue(int controlMode) {
  1207. resetPortraitsAndDisableSysTimer();
  1208. gui_prepareForSequence(112, 0, 176, 120, controlMode);
  1209. _updateFlags |= 3;
  1210. _txt->setupField(true);
  1211. _txt->expandField();
  1212. setupScreenDims();
  1213. gui_disableControls(controlMode);
  1214. }
  1215. void LoLEngine::toggleSelectedCharacterFrame(bool mode) {
  1216. if (countActiveCharacters() == 1)
  1217. return;
  1218. int col = mode ? 212 : 1;
  1219. int cp = _screen->setCurPage(0);
  1220. int x = _activeCharsXpos[_selectedCharacter];
  1221. _screen->drawBox(x, 143, x + 65, 176, col);
  1222. _screen->setCurPage(cp);
  1223. }
  1224. void LoLEngine::gui_prepareForSequence(int x, int y, int w, int h, int buttonFlags) {
  1225. setSequenceButtons(x, y, w, h, buttonFlags);
  1226. _seqWindowX1 = x;
  1227. _seqWindowY1 = y;
  1228. _seqWindowX2 = x + w;
  1229. _seqWindowY2 = y + h;
  1230. int mouseOffs = _itemInHand ? 10 : 0;
  1231. _screen->setMouseCursor(mouseOffs, mouseOffs, getItemIconShapePtr(_itemInHand));
  1232. _lastMouseRegion = -1;
  1233. if (w == 320) {
  1234. setLampMode(false);
  1235. _lampStatusSuspended = true;
  1236. }
  1237. }
  1238. void LoLEngine::gui_specialSceneSuspendControls(int controlMode) {
  1239. if (controlMode) {
  1240. _updateFlags |= 4;
  1241. setLampMode(false);
  1242. }
  1243. _updateFlags |= 1;
  1244. _specialSceneFlag = 1;
  1245. _currentControlMode = controlMode;
  1246. calcCharPortraitXpos();
  1247. checkFloatingPointerRegions();
  1248. }
  1249. void LoLEngine::gui_specialSceneRestoreControls(int restoreLamp) {
  1250. if (restoreLamp) {
  1251. _updateFlags &= 0xFFFA;
  1252. resetLampStatus();
  1253. }
  1254. _updateFlags &= 0xFFFE;
  1255. _specialSceneFlag = 0;
  1256. checkFloatingPointerRegions();
  1257. }
  1258. void LoLEngine::restoreAfterSceneWindowDialogue(int redraw) {
  1259. gui_enableControls();
  1260. _txt->setupField(false);
  1261. _updateFlags &= 0xFFDF;
  1262. setDefaultButtonState();
  1263. for (int i = 0; i < 6; i++)
  1264. _tim->freeAnimStruct(i);
  1265. _updateFlags = 0;
  1266. if (redraw) {
  1267. if (_screen->_fadeFlag != 2)
  1268. _screen->fadeClearSceneWindow(10);
  1269. gui_drawPlayField();
  1270. setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect);
  1271. _screen->_fadeFlag = 0;
  1272. }
  1273. _needSceneRestore = 0;
  1274. enableSysTimer(2);
  1275. }
  1276. void LoLEngine::initDialogueSequence(int controlMode, int pageNum) {
  1277. if (controlMode) {
  1278. _timer->disable(11);
  1279. _fadeText = false;
  1280. int cp = _screen->setCurPage(pageNum);
  1281. if (_flags.use16ColorMode) {
  1282. _screen->fillRect(0, 128, 319, 199, 0x44);
  1283. gui_drawBox(0, 129, 320, 71, 0xEE, 0xCC, -1);
  1284. gui_drawBox(1, 130, 318, 69, 0xEE, 0xCC, 0x11);
  1285. } else {
  1286. _screen->fillRect(0, 128, 319, 199, 1);
  1287. gui_drawBox(0, 129, 320, 71, 136, 251, -1);
  1288. gui_drawBox(1, 130, 318, 69, 136, 251, 252);
  1289. }
  1290. _screen->modifyScreenDim(5, 8, 131, 306, 66);
  1291. _screen->modifyScreenDim(4, 1, 133, 38, 60);
  1292. _txt->clearDim(4);
  1293. _updateFlags |= 2;
  1294. _currentControlMode = controlMode;
  1295. calcCharPortraitXpos();
  1296. if (!textEnabled() && (!(controlMode & 2))) {
  1297. int nc = countActiveCharacters();
  1298. for (int i = 0; i < nc; i++) {
  1299. _portraitSpeechAnimMode = 2;
  1300. _updateCharNum = i;
  1301. _screen->drawShape(0, _gameShapes[88], _activeCharsXpos[_updateCharNum] + 8, 142, 0, 0);
  1302. stopPortraitSpeechAnim();
  1303. }
  1304. }
  1305. _screen->setCurPage(cp);
  1306. } else {
  1307. _txt->setupField(true);
  1308. _txt->expandField();
  1309. setupScreenDims();
  1310. _txt->clearDim(4);
  1311. }
  1312. _currentControlMode = controlMode;
  1313. _dialogueField = true;
  1314. }
  1315. void LoLEngine::restoreAfterDialogueSequence(int controlMode) {
  1316. if (!_dialogueField)
  1317. return;
  1318. stopPortraitSpeechAnim();
  1319. _currentControlMode = controlMode;
  1320. calcCharPortraitXpos();
  1321. if (_currentControlMode) {
  1322. _screen->modifyScreenDim(4, 11, 124, 28, 45);
  1323. _screen->modifyScreenDim(5, 85, 123, 233, 54);
  1324. _updateFlags &= 0xFFFD;
  1325. } else {
  1326. const ScreenDim *d = _screen->getScreenDim(5);
  1327. _screen->fillRect(d->sx, d->sy, d->sx + d->w - (_flags.use16ColorMode ? 3 : 2), d->sy + d->h - 2, d->unkA);
  1328. _txt->clearDim(4);
  1329. _txt->setupField(false);
  1330. }
  1331. _dialogueField = false;
  1332. }
  1333. void LoLEngine::resetPortraitsAndDisableSysTimer() {
  1334. _needSceneRestore = 1;
  1335. if (!textEnabled() || (!(_currentControlMode & 2)))
  1336. timerUpdatePortraitAnimations(1);
  1337. disableSysTimer(2);
  1338. }
  1339. void LoLEngine::fadeText() {
  1340. if (!_fadeText)
  1341. return;
  1342. if (_screen->fadeColor(192, 252, (_system->getMillis() - _palUpdateTimer) / _tickLength, 60))
  1343. return;
  1344. if (_needSceneRestore)
  1345. return;
  1346. _screen->setScreenDim(_txt->clearDim(3));
  1347. _timer->disable(11);
  1348. _fadeText = false;
  1349. }
  1350. void LoLEngine::setPaletteBrightness(const Palette &srcPal, int brightness, int modifier) {
  1351. generateBrightnessPalette(srcPal, _screen->getPalette(1), brightness, modifier);
  1352. _screen->fadePalette(_screen->getPalette(1), 5, 0);
  1353. _screen->_fadeFlag = 0;
  1354. }
  1355. void LoLEngine::generateBrightnessPalette(const Palette &src, Palette &dst, int brightness, int16 modifier) {
  1356. dst.copy(src);
  1357. if (_flags.use16ColorMode) {
  1358. if (!brightness)
  1359. modifier = 0;
  1360. else if (modifier < 0 || modifier > 7 || !(_flagsTable[31] & 0x08))
  1361. modifier = 8;
  1362. modifier >>= 1;
  1363. if (modifier)
  1364. modifier--;
  1365. if (modifier > 3)
  1366. modifier = 3;
  1367. _blockBrightness = modifier << 4;
  1368. _sceneUpdateRequired = true;
  1369. } else {
  1370. _screen->loadSpecialColors(dst);
  1371. brightness = (8 - brightness) << 5;
  1372. if (modifier >= 0 && modifier < 8 && (_flagsTable[31] & 0x08)) {
  1373. brightness = 256 - ((((modifier & 0xFFFE) << 5) * (256 - brightness)) >> 8);
  1374. if (brightness < 0)
  1375. brightness = 0;
  1376. }
  1377. for (int i = 0; i < 384; i++) {
  1378. uint16 c = (dst[i] * brightness) >> 8;
  1379. dst[i] = c & 0xFF;
  1380. }
  1381. }
  1382. }
  1383. void LoLEngine::generateFlashPalette(const Palette &src, Palette &dst, int colorFlags) {
  1384. dst.copy(src, 0, 2);
  1385. for (int i = 2; i < 128; i++) {
  1386. for (int ii = 0; ii < 3; ii++) {
  1387. uint8 t = src[i * 3 + ii] & 0x3F;
  1388. if (colorFlags & (1 << ii))
  1389. t += ((0x3F - t) >> 1);
  1390. else
  1391. t -= (t >> 1);
  1392. dst[i * 3 + ii] = t;
  1393. }
  1394. }
  1395. dst.copy(src, 128);
  1396. }
  1397. void LoLEngine::createTransparencyTables() {
  1398. if (_flags.isTalkie || _loadSuppFilesFlag)
  1399. return;
  1400. uint8 *tpal = new uint8[768];
  1401. if (_flags.use16ColorMode) {
  1402. static const uint8 colTbl[] = {
  1403. 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x33, 0x00, 0x44, 0x00, 0x55, 0x00, 0x66, 0x00, 0x77, 0x00,
  1404. 0x88, 0x00, 0x99, 0x00, 0xAA, 0x00, 0xBB, 0x00, 0xCC, 0x00, 0xDD, 0x00, 0xEE, 0x00, 0xFF, 0x00
  1405. };
  1406. memset(tpal, 0xFF, 768);
  1407. _res->loadFileToBuf("LOL.NOL", tpal, 48);
  1408. for (int i = 15; i > -1; i--) {
  1409. int s = colTbl[i << 1] * 3;
  1410. tpal[s] = tpal[i * 3];
  1411. tpal[s + 1] = tpal[i * 3 + 1];
  1412. tpal[s + 2] = tpal[i * 3 + 2];
  1413. tpal[i * 3 + 2] = tpal[i * 3 + 1] = tpal[i * 3] = 0xFF;
  1414. }
  1415. _screen->createTransparencyTablesIntern(colTbl, 16, tpal, tpal, _transparencyTable1, _transparencyTable2, 80);
  1416. } else {
  1417. _res->loadFileToBuf("fxpal.col", tpal, 768);
  1418. _screen->loadBitmap("fxpal.shp", 3, 3, 0);
  1419. const uint8 *shpPal = _screen->getPtrToShape(_screen->getCPagePtr(2), 0) + 11;
  1420. _screen->createTransparencyTablesIntern(shpPal, 20, tpal, _screen->getPalette(1).getData(), _transparencyTable1, _transparencyTable2, 70);
  1421. }
  1422. delete[] tpal;
  1423. _loadSuppFilesFlag = 1;
  1424. }
  1425. void LoLEngine::updateSequenceBackgroundAnimations() {
  1426. if (_updateFlags & 8 || !_tim)
  1427. return;
  1428. if (!_tim->animator())
  1429. return;
  1430. for (int i = 0; i < 6; i++)
  1431. _tim->animator()->update(i);
  1432. }
  1433. void LoLEngine::loadTalkFile(int index) {
  1434. if (index == _curTlkFile)
  1435. return;
  1436. if (_curTlkFile > 0 && index > 0)
  1437. _res->unloadPakFile(Common::String::format("%02d.TLK", _curTlkFile));
  1438. if (index > 0)
  1439. _curTlkFile = index;
  1440. _res->loadPakFile(Common::String::format("%02d.TLK", index));
  1441. }
  1442. int LoLEngine::characterSays(int track, int charId, bool redraw) {
  1443. if (charId == 1) {
  1444. charId = _selectedCharacter;
  1445. } if (charId <= 0) {
  1446. charId = 0;
  1447. } else {
  1448. int i = 0;
  1449. for (; i < 4; i++) {
  1450. if (charId != _characters[i].id || !(_characters[i].flags & 1))
  1451. continue;
  1452. charId = i;
  1453. break;
  1454. }
  1455. if (i == 4)
  1456. return 0;
  1457. }
  1458. bool r = snd_playCharacterSpeech(track, charId, 0);
  1459. if (r && redraw) {
  1460. stopPortraitSpeechAnim();
  1461. _updateCharNum = charId;
  1462. _portraitSpeechAnimMode = 0;
  1463. _resetPortraitAfterSpeechAnim = 1;
  1464. _fadeText = false;
  1465. updatePortraitSpeechAnim();
  1466. }
  1467. return r ? (textEnabled() ? 1 : 0) : 1;
  1468. }
  1469. int LoLEngine::playCharacterScriptChat(int charId, int mode, int restorePortrait, char *str, EMCState *script, const uint16 *paramList, int16 paramIndex) {
  1470. int ch = 0;
  1471. bool skipAnim = false;
  1472. if ((charId == -1) || (!(charId & 0x70)))
  1473. charId = ch = (charId == 1) ? (_selectedCharacter ? _characters[_selectedCharacter].id : 0) : charId;
  1474. else
  1475. charId ^= 0x70;
  1476. stopPortraitSpeechAnim();
  1477. if (charId < 0) {
  1478. charId = ch = _rnd.getRandomNumber(countActiveCharacters() - 1);
  1479. } else if (charId > 0) {
  1480. int i = 0;
  1481. for (; i < 3; i++) {
  1482. if (_characters[i].id != charId || !(_characters[i].flags & 1))
  1483. continue;
  1484. if (charId == ch)
  1485. ch = i;
  1486. charId = i;
  1487. break;
  1488. }
  1489. if (i == 4) {
  1490. if (charId == 8)
  1491. skipAnim = true;
  1492. else
  1493. return 0;
  1494. }
  1495. }
  1496. if (!skipAnim) {
  1497. _updateCharNum = charId;
  1498. _portraitSpeechAnimMode = mode;
  1499. _updatePortraitSpeechAnimDuration = strlen(str) >> 1;
  1500. _resetPortraitAfterSpeechAnim = restorePortrait;
  1501. }
  1502. if (script)
  1503. snd_playCharacterSpeech(script->stack[script->sp + 2], ch, 0);
  1504. else if (paramList)
  1505. snd_playCharacterSpeech(paramList[2], ch, 0);
  1506. if (textEnabled()) {
  1507. if (mode == 0) {
  1508. _txt->printDialogueText(3, str, script, paramList, paramIndex);
  1509. } else if (mode == 1) {
  1510. _txt->clearDim(4);
  1511. _screen->modifyScreenDim(4, 16, 123, 23, 47);
  1512. _txt->printDialogueText(4, str, script, paramList, paramIndex);
  1513. _screen->modifyScreenDim(4, 11, 123, 28, 47);
  1514. } else if (mode == 2) {
  1515. _txt->clearDim(4);
  1516. _screen->modifyScreenDim(4, 9, 133, 30, 60);
  1517. _txt->printDialogueText(4, str, script, paramList, 3);
  1518. _screen->modifyScreenDim(4, 1, 133, 37, 60);
  1519. }
  1520. }
  1521. _fadeText = false;
  1522. if (!skipAnim)
  1523. updatePortraitSpeechAnim();
  1524. return 1;
  1525. }
  1526. void LoLEngine::setupDialogueButtons(int numStr, const char *s1, const char *s2, const char *s3) {
  1527. screen()->setScreenDim(5);
  1528. if (numStr == 1 && speechEnabled()) {
  1529. _dialogueNumButtons = 0;
  1530. _dialogueButtonString[0] = _dialogueButtonString[1] = _dialogueButtonString[2] = 0;
  1531. } else {
  1532. _dialogueNumButtons = numStr;
  1533. _dialogueButtonString[0] = s1;
  1534. _dialogueButtonString[1] = s2;
  1535. _dialogueButtonString[2] = s3;
  1536. _dialogueHighlightedButton = 0;
  1537. const ScreenDim *d = screen()->getScreenDim(5);
  1538. static uint16 posX[3];
  1539. static uint8 posY[3];
  1540. memset(posY, d->sy + d->h - 9, 3);
  1541. _dialogueButtonPosX = posX;
  1542. _dialogueButtonPosY = posY;
  1543. if (numStr == 1) {
  1544. posX[0] = posX[1] = posX[2] = d->sx + d->w - (_dialogueButtonWidth + 3);
  1545. } else {
  1546. int xOffs = d->w / numStr;
  1547. posX[0] = d->sx + (xOffs >> 1) - 37;
  1548. posX[1] = posX[0] + xOffs;
  1549. posX[2] = posX[1] + xOffs;
  1550. }
  1551. drawDialogueButtons();
  1552. }
  1553. if (!shouldQuit())
  1554. removeInputTop();
  1555. }
  1556. void LoLEngine::giveItemToMonster(LoLMonster *monster, Item item) {
  1557. uint16 *c = &monster->assignedItems;
  1558. while (*c)
  1559. c = &_itemsInPlay[*c].nextAssignedObject;
  1560. *c = (uint16)item;
  1561. _itemsInPlay[item].nextAssignedObject = 0;
  1562. }
  1563. const uint16 *LoLEngine::getCharacterOrMonsterStats(int id) {
  1564. return (id & 0x8000) ? (const uint16 *)_monsters[id & 0x7FFF].properties->fightingStats : _characters[id].defaultModifiers;
  1565. }
  1566. uint16 *LoLEngine::getCharacterOrMonsterItemsMight(int id) {
  1567. return (id & 0x8000) ? _monsters[id & 0x7FFF].properties->itemsMight : _characters[id].itemsMight;
  1568. }
  1569. uint16 *LoLEngine::getCharacterOrMonsterProtectionAgainstItems(int id) {
  1570. return (id & 0x8000) ? _monsters[id & 0x7FFF].properties->protectionAgainstItems : _characters[id].protectionAgainstItems;
  1571. }
  1572. void LoLEngine::delay(uint32 millis, bool doUpdate, bool) {
  1573. while (millis && !shouldQuit()) {
  1574. if (doUpdate)
  1575. update();
  1576. else
  1577. updateInput();
  1578. uint32 step = MIN<uint32>(millis, _tickLength);
  1579. _system->delayMillis(step);
  1580. millis -= step;
  1581. }
  1582. }
  1583. const KyraRpgGUISettings *LoLEngine::guiSettings() {
  1584. return &_guiSettings;
  1585. }
  1586. // spells
  1587. int LoLEngine::castSpell(int charNum, int spellType, int spellLevel) {
  1588. _activeSpell.charNum = charNum;
  1589. _activeSpell.spell = spellType;
  1590. _activeSpell.p = &_spellProperties[spellType];
  1591. _activeSpell.level = ABS(spellLevel);
  1592. if ((_spellProperties[spellType].flags & 0x100) && testWallFlag(calcNewBlockPosition(_currentBlock, _currentDirection), _currentDirection, 1)) {
  1593. _txt->printMessage(2, "%s", getLangString(0x4257));
  1594. return 0;
  1595. }
  1596. if (charNum < 0) {
  1597. _activeSpell.charNum = (charNum * -1) - 1;
  1598. if (_spellProcs[spellType]->isValid())
  1599. return (*_spellProcs[spellType])(&_activeSpell);
  1600. } else {
  1601. if (_activeSpell.p->mpRequired[spellLevel] > _characters[charNum].magicPointsCur)
  1602. return 0;
  1603. if (_activeSpell.p->hpRequired[spellLevel] >= _characters[charNum].hitPointsCur)
  1604. return 0;
  1605. setCharacterMagicOrHitPoints(charNum, 1, -_activeSpell.p->mpRequired[spellLevel], 1);
  1606. setCharacterMagicOrHitPoints(charNum, 0, -_activeSpell.p->hpRequired[spellLevel], 1);
  1607. gui_drawCharPortraitWithStats(charNum);
  1608. if (_spellProcs[spellType]->isValid())
  1609. (*_spellProcs[spellType])(&_activeSpell);
  1610. }
  1611. return 1;
  1612. }
  1613. int LoLEngine::castSpark(ActiveSpell *a) {
  1614. processMagicSpark(a->charNum, a->level);
  1615. return 1;
  1616. }
  1617. int LoLEngine::castHeal(ActiveSpell *a) {
  1618. if (a->level < 3)
  1619. processMagicHealSelectTarget();
  1620. else
  1621. processMagicHeal(-1, a->level);
  1622. return 1;
  1623. }
  1624. int LoLEngine::castIce(ActiveSpell *a) {
  1625. processMagicIce(a->charNum, a->level);
  1626. return 1;
  1627. }
  1628. int LoLEngine::castFireball(ActiveSpell *a) {
  1629. processMagicFireball(a->charNum, a->level);
  1630. return 1;
  1631. }
  1632. int LoLEngine::castHandOfFate(ActiveSpell *a) {
  1633. processMagicHandOfFate(a->level);
  1634. return 1;
  1635. }
  1636. int LoLEngine::castMistOfDoom(ActiveSpell *a) {
  1637. processMagicMistOfDoom(a->charNum, a->level);
  1638. return 1;
  1639. }
  1640. int LoLEngine::castLightning(ActiveSpell *a) {
  1641. processMagicLightning(a->charNum, a->level);
  1642. return 1;
  1643. }
  1644. int LoLEngine::castFog(ActiveSpell *a) {
  1645. processMagicFog();
  1646. return 1;
  1647. }
  1648. int LoLEngine::castSwarm(ActiveSpell *a) {
  1649. processMagicSwarm(a->charNum, 10);
  1650. return 1;
  1651. }
  1652. int LoLEngine::castVaelansCube(ActiveSpell *a) {
  1653. return processMagicVaelansCube();
  1654. }
  1655. int LoLEngine::castGuardian(ActiveSpell *a) {
  1656. return processMagicGuardian(a->charNum);
  1657. }
  1658. int LoLEngine::castHealOnSingleCharacter(ActiveSpell *a) {
  1659. processMagicHeal(a->target, a->level);
  1660. return 1;
  1661. }
  1662. int LoLEngine::processMagicSpark(int charNum, int spellLevel) {
  1663. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  1664. _screen->copyPage(0, 12);
  1665. mov->open("spark1.wsa", 0, 0);
  1666. if (!mov->opened())
  1667. error("SPARK: Unable to load SPARK1.WSA");
  1668. snd_playSoundEffect(72, -1);
  1669. playSpellAnimation(mov, 0, 7, 4, _activeCharsXpos[charNum] - 2, 138, 0, 0, 0, 0, false);
  1670. mov->close();
  1671. _screen->copyPage(12, 0);
  1672. _screen->updateScreen();
  1673. uint16 targetBlock = 0;
  1674. int dist = getSpellTargetBlock(_currentBlock, _currentDirection, 4, targetBlock);
  1675. uint16 target = getNearestMonsterFromCharacterForBlock(targetBlock, charNum);
  1676. static const uint8 dmg[] = { 7, 15, 25, 60 };
  1677. if (target != 0xFFFF) {
  1678. inflictMagicalDamage(target, charNum, dmg[spellLevel], 5, 0);
  1679. updateDrawPage2();
  1680. gui_drawScene(0);
  1681. _screen->copyPage(0, 12);
  1682. }
  1683. int numFrames = mov->open("spark2.wsa", 0, 0);
  1684. if (!mov->opened())
  1685. error("SPARK: Unable to load SPARK2.WSA");
  1686. uint16 wX[6];
  1687. uint16 wY[6];
  1688. uint16 wFrames[6];
  1689. const uint16 width = mov->width();
  1690. const uint16 height = mov->height();
  1691. for (int i = 0; i < 6; i++) {
  1692. wX[i] = (_rnd.getRandomNumber(0x7FFF) % 64) + ((176 - width) >> 1) + 80;
  1693. wY[i] = (_rnd.getRandomNumber(0x7FFF) % 32) + ((120 - height) >> 1) - 16;
  1694. wFrames[i] = i << 1;
  1695. }
  1696. for (int i = 0, d = ((spellLevel << 1) + 12); i < d; i++) {
  1697. uint32 delayTimer = _system->getMillis() + 4 * _tickLength;
  1698. _screen->copyPage(12, 2);
  1699. for (int ii = 0; ii <= spellLevel; ii++) {
  1700. if (wFrames[ii] >= i || wFrames[ii] + 13 <= i)
  1701. continue;
  1702. if ((i - wFrames[ii]) == 1)
  1703. snd_playSoundEffect(162, -1);
  1704. mov->displayFrame(((i - wFrames[ii]) + (dist << 4)) % numFrames, 2, wX[ii], wY[ii], 0x5000, _transparencyTable1, _transparencyTable2);
  1705. _screen->copyRegion(wX[ii], wY[ii], wX[ii], wY[ii], width, height, 2, 0, Screen::CR_NO_P_CHECK);
  1706. _screen->updateScreen();
  1707. }
  1708. if (i < d - 1)
  1709. delayUntil(delayTimer);
  1710. }
  1711. mov->close();
  1712. _screen->copyPage(12, 2);
  1713. updateDrawPage2();
  1714. _sceneUpdateRequired = true;
  1715. delete mov;
  1716. return 1;
  1717. }
  1718. int LoLEngine::processMagicHealSelectTarget() {
  1719. _txt->printMessage(0, "%s", getLangString(0x4040));
  1720. gui_resetButtonList();
  1721. gui_setFaceFramesControlButtons(81, 0);
  1722. gui_initButtonsFromList(_buttonList8);
  1723. return 1;
  1724. }
  1725. int LoLEngine::processMagicHeal(int charNum, int spellLevel) {
  1726. if (!_healOverlay) {
  1727. _healOverlay = new uint8[256];
  1728. Palette tpal(256);
  1729. tpal.copy(_screen->getPalette(1));
  1730. if (_flags.use16ColorMode) {
  1731. tpal.fill(16, 240, 0xFF);
  1732. uint8 *dst = tpal.getData();
  1733. for (int i = 1; i < 16; i++) {
  1734. int s = ((i << 4) | i) * 3;
  1735. SWAP(dst[s], dst[i]);
  1736. SWAP(dst[s + 1], dst[i + 1]);
  1737. SWAP(dst[s + 2], dst[i + 2]);
  1738. }
  1739. }
  1740. _screen->generateGrayOverlay(tpal, _healOverlay, 52, 22, 20, 0, 256, true);
  1741. }
  1742. const uint8 *healShpFrames = 0;
  1743. const uint8 *healiShpFrames = 0;
  1744. bool curePoison = false;
  1745. int points = 0;
  1746. if (spellLevel == 0) {
  1747. points = 25;
  1748. healShpFrames = _healShapeFrames;
  1749. healiShpFrames = _healShapeFrames + 32;
  1750. } else if (spellLevel == 1) {
  1751. points = 45;
  1752. healShpFrames = _healShapeFrames + 16;
  1753. healiShpFrames = _healShapeFrames + 48;
  1754. } else if (spellLevel > 3) {
  1755. curePoison = true;
  1756. points = spellLevel;
  1757. healShpFrames = _healShapeFrames + 16;
  1758. healiShpFrames = _healShapeFrames + 64;
  1759. } else {
  1760. curePoison = true;
  1761. points = 10000;
  1762. healShpFrames = _healShapeFrames + 16;
  1763. healiShpFrames = _healShapeFrames + 64;
  1764. }
  1765. int ch = 0;
  1766. int n = 4;
  1767. if (charNum != -1) {
  1768. ch = charNum;
  1769. n = charNum + 1;
  1770. }
  1771. charNum = ch;
  1772. uint16 pX[4];
  1773. uint16 pY = 138;
  1774. uint16 diff[4];
  1775. uint16 pts[4];
  1776. memset(pts, 0, sizeof(pts));
  1777. while (charNum < n) {
  1778. if (!(_characters[charNum].flags & 1)) {
  1779. charNum++;
  1780. continue;
  1781. }
  1782. pX[charNum] = _activeCharsXpos[charNum] - 6;
  1783. _characters[charNum].damageSuffered = 0;
  1784. int dmg = _characters[charNum].hitPointsMax - _characters[charNum].hitPointsCur;
  1785. diff[charNum] = (dmg < points) ? dmg : points;
  1786. _screen->copyRegion(pX[charNum], pY, charNum * 77, 32, 77, 44, 0, 2, Screen::CR_NO_P_CHECK);
  1787. charNum++;
  1788. }
  1789. int cp = _screen->setCurPage(2);
  1790. snd_playSoundEffect(68, -1);
  1791. for (int i = 0; i < 16; i++) {
  1792. uint32 delayTimer = _system->getMillis() + 4 * _tickLength;
  1793. for (charNum = ch; charNum < n; charNum++) {
  1794. if (!(_characters[charNum].flags & 1))
  1795. continue;
  1796. _screen->copyRegion(charNum * 77, 32, pX[charNum], pY, 77, 44, 2, 2, Screen::CR_NO_P_CHECK);
  1797. pts[charNum] &= 0xFF;
  1798. pts[charNum] += ((diff[charNum] << 8) / 16);
  1799. increaseCharacterHitpoints(charNum, pts[charNum] / 256, true);
  1800. gui_drawCharPortraitWithStats(charNum);
  1801. _screen->drawShape(2, _healShapes[healShpFrames[i]], pX[charNum], pY, 0, 0x1000, _transparencyTable1, _transparencyTable2);
  1802. _screen->fillRect(0, 0, 31, 31, 0);
  1803. _screen->drawShape(_screen->_curPage, _healiShapes[healiShpFrames[i]], 0, 0, 0, 0);
  1804. _screen->applyOverlaySpecial(_screen->_curPage, 0, 0, 2, pX[charNum] + 7, pY + 6, 32, 32, 0, 0, _healOverlay);
  1805. _screen->copyRegion(pX[charNum], pY, pX[charNum], pY, 77, 44, 2, 0, Screen::CR_NO_P_CHECK);
  1806. _screen->updateScreen();
  1807. }
  1808. delayUntil(delayTimer);
  1809. }
  1810. for (charNum = ch; charNum < n; charNum++) {
  1811. if (!(_characters[charNum].flags & 1))
  1812. continue;
  1813. _screen->copyRegion(charNum * 77, 32, pX[charNum], pY, 77, 44, 2, 2, Screen::CR_NO_P_CHECK);
  1814. if (curePoison)
  1815. removeCharacterEffects(&_characters[charNum], 4, 4);
  1816. gui_drawCharPortraitWithStats(charNum);
  1817. _screen->copyRegion(pX[charNum], pY, pX[charNum], pY, 77, 44, 2, 0, Screen::CR_NO_P_CHECK);
  1818. _screen->updateScreen();
  1819. }
  1820. _screen->setCurPage(cp);
  1821. updateDrawPage2();
  1822. return 1;
  1823. }
  1824. int LoLEngine::processMagicIce(int charNum, int spellLevel) {
  1825. int cp = _screen->setCurPage(2);
  1826. disableSysTimer(2);
  1827. gui_drawScene(0);
  1828. _screen->copyPage(0, 12);
  1829. Palette tpal(256), swampCol(256);
  1830. if (_currentLevel == 11 && !(_flagsTable[52] & 0x04)) {
  1831. uint8 *sc = _screen->getPalette(0).getData();
  1832. uint8 *dc = _screen->getPalette(2).getData();
  1833. for (int i = 1; i < (_screen->getPalette(0).getNumColors() * 3); i++)
  1834. SWAP(sc[i], dc[i]);
  1835. _flagsTable[52] |= 0x04;
  1836. static const uint8 freezeTimes[] = { 20, 28, 40, 60 };
  1837. setCharacterUpdateEvent(charNum, 8, freezeTimes[spellLevel], 1);
  1838. }
  1839. Palette s(256);
  1840. s.copy(_screen->getPalette(1));
  1841. if (_flags.use16ColorMode) {
  1842. _screen->loadPalette("LOLICE.NOL", swampCol);
  1843. for (int i = 1; i < 16; i++) {
  1844. uint16 v = (s[i * 3] + s[i * 3 + 1] + s[i * 3 + 2]) / 3;
  1845. tpal[i * 3] = 0;
  1846. tpal[i * 3 + 1] = v;
  1847. tpal[i * 3 + 2] = v << 1;
  1848. if (tpal[i * 3 + 2] > 29)
  1849. tpal[i * 3 + 2] = 29;
  1850. }
  1851. } else {
  1852. _screen->loadPalette("SWAMPICE.COL", swampCol);
  1853. tpal.copy(s, 128);
  1854. swampCol.copy(s, 128);
  1855. for (int i = 1; i < 128; i++) {
  1856. tpal[i * 3] = 0;
  1857. uint16 v = (s[i * 3] + s[i * 3 + 1] + s[i * 3 + 2]) / 3;
  1858. tpal[i * 3 + 1] = v;
  1859. tpal[i * 3 + 2] = v << 1;
  1860. if (tpal[i * 3 + 2] > 0x3F)
  1861. tpal[i * 3 + 2] = 0x3F;
  1862. }
  1863. }
  1864. generateBrightnessPalette(tpal, tpal, _brightness, _lampEffect);
  1865. generateBrightnessPalette(swampCol, swampCol, _brightness, _lampEffect);
  1866. swampCol[0] = swampCol[1] = swampCol[2] = tpal[0] = tpal[1] = tpal[2] = 0;
  1867. generateBrightnessPalette(_screen->getPalette(0), s, _brightness, _lampEffect);
  1868. int sX = 112;
  1869. int sY = 0;
  1870. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  1871. if (spellLevel == 0) {
  1872. sX = 0;
  1873. } if (spellLevel == 1 || spellLevel == 2) {
  1874. mov->open("SNOW.WSA", 1, 0);
  1875. if (!mov->opened())
  1876. error("Ice: Unable to load snow.wsa");
  1877. } if (spellLevel == 3) {
  1878. mov->open("ICE.WSA", 1, 0);
  1879. if (!mov->opened())
  1880. error("Ice: Unable to load ice.wsa");
  1881. sX = 136;
  1882. sY = 12;
  1883. }
  1884. snd_playSoundEffect(71, -1);
  1885. playSpellAnimation(0, 0, 0, 2, 0, 0, 0, s.getData(), tpal.getData(), 40, false);
  1886. _screen->timedPaletteFadeStep(s.getData(), tpal.getData(), _system->getMillis(), _tickLength);
  1887. if (mov->opened()) {
  1888. int r = true;
  1889. if (spellLevel > 2) {
  1890. _levelBlockProperties[calcNewBlockPosition(_currentBlock, _currentDirection)].flags |= 0x10;
  1891. snd_playSoundEffect(165, -1);
  1892. r = false;
  1893. };
  1894. playSpellAnimation(mov, 0, mov->frames(), 2, sX, sY, 0, 0, 0, 0, r);
  1895. mov->close();
  1896. }
  1897. delete mov;
  1898. static const uint8 snowDamage[] = { 10, 20, 30, 55 };
  1899. static const uint8 iceDamageMax[] = {1, 2, 15, 20, 35};
  1900. static const uint8 iceDamageMin[] = {10, 10, 3, 4, 4};
  1901. static const uint8 iceDamageAdd[] = {5, 10, 30, 10, 10};
  1902. bool breakWall = false;
  1903. if (spellLevel < 3) {
  1904. inflictMagicalDamageForBlock(calcNewBlockPosition(_currentBlock, _currentDirection), charNum, snowDamage[spellLevel], 3);
  1905. } else {
  1906. uint16 o = _levelBlockProperties[calcNewBlockPosition(_currentBlock, _currentDirection)].assignedObjects;
  1907. while (o & 0x8000) {
  1908. int might = rollDice(iceDamageMin[spellLevel], iceDamageMax[spellLevel]) + iceDamageAdd[spellLevel];
  1909. int dmg = calcInflictableDamagePerItem(charNum, 0, might, 3, 2);
  1910. LoLMonster *m = &_monsters[o & 0x7FFF];
  1911. if (m->hitPoints <= dmg) {
  1912. increaseExperience(charNum, 2, m->hitPoints);
  1913. o = m->nextAssignedObject;
  1914. if (m->flags & 0x20) {
  1915. m->mode = 0;
  1916. monsterDropItems(m);
  1917. if (_currentLevel != 29)
  1918. setMonsterMode(m, 14);
  1919. runLevelScriptCustom(0x404, -1, o, o, 0, 0);
  1920. checkSceneUpdateNeed(m->block);
  1921. if (m->mode != 14)
  1922. placeMonster(m, 0, 0);
  1923. } else {
  1924. killMonster(m);
  1925. }
  1926. } else {
  1927. breakWall = true;
  1928. inflictDamage(o, dmg, charNum, 2, 3);
  1929. m->damageReceived = 0;
  1930. o = m->nextAssignedObject;
  1931. }
  1932. if (m->flags & 0x20)
  1933. break;
  1934. }
  1935. }
  1936. updateDrawPage2();
  1937. gui_drawScene(0);
  1938. enableSysTimer(2);
  1939. if (_currentLevel != 11)
  1940. generateBrightnessPalette(_screen->getPalette(0), swampCol, _brightness, _lampEffect);
  1941. playSpellAnimation(0, 0, 0, 2, 0, 0, 0, tpal.getData(), swampCol.getData(), 40, 0);
  1942. _screen->timedPaletteFadeStep(tpal.getData(), swampCol.getData(), _system->getMillis(), _tickLength);
  1943. if (breakWall)
  1944. breakIceWall(tpal.getData(), swampCol.getData());
  1945. _screen->setCurPage(cp);
  1946. return 1;
  1947. }
  1948. int LoLEngine::processMagicFireball(int charNum, int spellLevel) {
  1949. int fbCnt = 0;
  1950. int d = 1;
  1951. if (spellLevel == 0) {
  1952. fbCnt = 4;
  1953. } else if (spellLevel == 1) {
  1954. fbCnt = 5;
  1955. } else if (spellLevel == 2) {
  1956. fbCnt = 6;
  1957. } else if (spellLevel == 3) {
  1958. d = 0;
  1959. fbCnt = 5;
  1960. }
  1961. int drawPage1 = 2;
  1962. int drawPage2 = 4;
  1963. int bl = _currentBlock;
  1964. int fireballItem = makeItem(9, 0, 0);
  1965. int i = 0;
  1966. for (; i < 3; i++) {
  1967. runLevelScriptCustom(bl, 0x200, -1, fireballItem, 0, 0);
  1968. uint16 o = _levelBlockProperties[bl].assignedObjects;
  1969. if ((o & 0x8000) || (_wllWallFlags[_levelBlockProperties[bl].walls[_currentDirection ^ 2]] & 7)) {
  1970. while (o & 0x8000) {
  1971. static const uint8 fireballDamage[] = { 20, 40, 80, 100 };
  1972. int dmg = calcInflictableDamagePerItem(charNum, o, fireballDamage[spellLevel], 4, 1);
  1973. LoLMonster *m = &_monsters[o & 0x7FFF];
  1974. o = m->nextAssignedObject;
  1975. _envSfxUseQueue = true;
  1976. inflictDamage(m->id | 0x8000, dmg, charNum, 2, 4);
  1977. _envSfxUseQueue = false;
  1978. }
  1979. break;
  1980. }
  1981. bl = calcNewBlockPosition(bl, _currentDirection);
  1982. }
  1983. d += i;
  1984. if (d > 3)
  1985. d = 3;
  1986. deleteItem(fireballItem);
  1987. snd_playSoundEffect(69, -1);
  1988. int cp = _screen->setCurPage(2);
  1989. _screen->copyPage(0, 12);
  1990. int fireBallWH = (d << 4) * -1;
  1991. int numFireballs = 1;
  1992. if (fbCnt > 3)
  1993. numFireballs = fbCnt - 3;
  1994. FireballState *fireballState[3];
  1995. memset(&fireballState, 0, sizeof(fireballState));
  1996. for (i = 0; i < numFireballs; i++)
  1997. fireballState[i] = new FireballState(i);
  1998. _screen->copyPage(12, drawPage1);
  1999. for (i = 0; i < numFireballs;) {
  2000. _screen->setCurPage(drawPage1);
  2001. uint32 ctime = _system->getMillis();
  2002. for (int ii = 0; ii < MIN(fbCnt, 3); ii++) {
  2003. FireballState *fb = fireballState[ii];
  2004. if (!fb)
  2005. continue;
  2006. if (!fb->active)
  2007. continue;
  2008. static const int8 finShpIndex1[] = { 5, 6, 7, 7, 6, 5 };
  2009. static const int8 finShpIndex2[] = { -1, 1, 2, 3, 4, -1 };
  2010. uint8 *shp = fb->finalize ? _fireballShapes[finShpIndex1[fb->finProgress]] : _fireballShapes[0];
  2011. int fX = (((fb->progress * _fireBallCoords[fb->tblIndex & 0xFF]) >> 16) + fb->destX) - ((fb->progress / 8 + shp[3] + fireBallWH) >> 1);
  2012. int fY = (((fb->progress * _fireBallCoords[(fb->tblIndex + 64) & 0xFF]) >> 16) + fb->destY) - ((fb->progress / 8 + shp[2] + fireBallWH) >> 1);
  2013. int sW = ((fb->progress / 8 + shp[3] + fireBallWH) << 8) / shp[3];
  2014. int sH = ((fb->progress / 8 + shp[2] + fireBallWH) << 8) / shp[2];
  2015. if (fb->finalize) {
  2016. if (_flags.use16ColorMode)
  2017. _screen->drawShape(_screen->_curPage, shp, fX, fY, 0, 4, sW, sH);
  2018. else
  2019. _screen->drawShape(_screen->_curPage, shp, fX, fY, 0, 0x1004, _transparencyTable1, _transparencyTable2, sW, sH);
  2020. if (finShpIndex2[fb->finProgress] != -1) {
  2021. shp = _fireballShapes[finShpIndex2[fb->finProgress]];
  2022. fX = (((fb->progress * _fireBallCoords[fb->tblIndex & 0xFF]) >> 16) + fb->destX) - ((fb->progress / 8 + shp[3] + fireBallWH) >> 1);
  2023. fY = (((fb->progress * _fireBallCoords[(fb->tblIndex + 64) & 0xFF]) >> 16) + fb->destY) - ((fb->progress / 8 + shp[2] + fireBallWH) >> 1);
  2024. sW = ((fb->progress / 8 + shp[3] + fireBallWH) << 8) / shp[3];
  2025. sH = ((fb->progress / 8 + shp[2] + fireBallWH) << 8) / shp[2];
  2026. _screen->drawShape(_screen->_curPage, shp, fX, fY, 0, 4, sW, sH);
  2027. }
  2028. } else {
  2029. if (_flags.use16ColorMode)
  2030. _screen->drawShape(_screen->_curPage, shp, fX, fY, 0, 4, sW, sH);
  2031. else
  2032. _screen->drawShape(_screen->_curPage, shp, fX, fY, 0, 0x1004, _transparencyTable1, _transparencyTable2, sW, sH);
  2033. }
  2034. if (fb->finalize) {
  2035. if (++fb->finProgress >= 6) {
  2036. fb->active = false;
  2037. i++;
  2038. }
  2039. } else {
  2040. if (fb->step < 40)
  2041. fb->step += 2;
  2042. else
  2043. fb->step = 40;
  2044. if (fb->progress < fb->step) {
  2045. if (ii < 1) {
  2046. fb->progress = fb->step = fb->finProgress = 0;
  2047. fb->finalize = true;
  2048. } else {
  2049. fb->active = false;
  2050. i++;
  2051. }
  2052. static const uint8 fireballSfx[] = { 98, 167, 167, 168 };
  2053. snd_playSoundEffect(fireballSfx[d], -1);
  2054. } else {
  2055. fb->progress -= fb->step;
  2056. }
  2057. }
  2058. }
  2059. int del = _tickLength - (_system->getMillis() - ctime);
  2060. if (del > 0)
  2061. delay(del);
  2062. _screen->checkedPageUpdate(drawPage1, drawPage2);
  2063. _screen->updateScreen();
  2064. SWAP(drawPage1, drawPage2);
  2065. _screen->copyPage(12, drawPage1);
  2066. }
  2067. for (i = 0; i < numFireballs; i++)
  2068. delete fireballState[i];
  2069. _screen->setCurPage(cp);
  2070. _screen->copyPage(12, 0);
  2071. _screen->updateScreen();
  2072. updateDrawPage2();
  2073. snd_playQueuedEffects();
  2074. runLevelScriptCustom(bl, 0x20, charNum, 3, 0, 0);
  2075. return 1;
  2076. }
  2077. int LoLEngine::processMagicHandOfFate(int spellLevel) {
  2078. int cp = _screen->setCurPage(2);
  2079. _screen->copyPage(0, 12);
  2080. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2081. mov->open("hand.wsa", 1, 0);
  2082. if (!mov->opened())
  2083. error("Hand: Unable to load HAND.WSA");
  2084. static const uint8 frames[] = { 17, 26, 11, 16, 27, 35, 27, 35, 0, 75 };
  2085. snd_playSoundEffect(173, -1);
  2086. playSpellAnimation(mov, 0, 10, 3, 112, 0, 0, 0, 0, 0, false);
  2087. snd_playSoundEffect(151, -1);
  2088. playSpellAnimation(mov, frames[spellLevel * 2] , frames[spellLevel * 2 + 1], 3, 112, 0, 0, 0, 0, 0, false);
  2089. snd_playSoundEffect(18, -1);
  2090. playSpellAnimation(mov, 10, 0, 3, 112, 0, 0, 0, 0, 0, false);
  2091. mov->close();
  2092. delete mov;
  2093. _screen->setCurPage(cp);
  2094. _screen->copyPage(12, 2);
  2095. gui_drawScene(2);
  2096. if (spellLevel < 2) {
  2097. uint16 b1 = calcNewBlockPosition(_currentBlock, _currentDirection);
  2098. uint16 b2 = calcNewBlockPosition(b1, _currentDirection);
  2099. if (!testWallFlag(b2, 0, 4)) {
  2100. if (!(_levelBlockProperties[b2].assignedObjects & 0x8000)) {
  2101. checkSceneUpdateNeed(b1);
  2102. uint16 dir = (_currentDirection << 1);
  2103. uint16 o = _levelBlockProperties[b1].assignedObjects;
  2104. while (o & 0x8000) {
  2105. uint16 o2 = o;
  2106. LoLMonster *m = &_monsters[o & 0x7FFF];
  2107. o = findObject(o)->nextAssignedObject;
  2108. int nX = 0;
  2109. int nY = 0;
  2110. getNextStepCoords(m->x, m->y, nX, nY, dir);
  2111. for (int i = 0; i < 7; i++)
  2112. getNextStepCoords(nX, nY, nX, nY, dir);
  2113. placeMonster(m, nX, nY);
  2114. runLevelScriptCustom(b2, 0x800, -1, o2, 0, 0);
  2115. }
  2116. }
  2117. }
  2118. } else {
  2119. uint16 b1 = calcNewBlockPosition(_currentBlock, _currentDirection);
  2120. checkSceneUpdateNeed(b1);
  2121. static const uint16 damage[] = { 75, 125, 175 };
  2122. uint16 o = _levelBlockProperties[b1].assignedObjects;
  2123. while (o & 0x8000) {
  2124. uint16 t = o;
  2125. o = findObject(o)->nextAssignedObject;
  2126. // This might be a bug in the original code, but using
  2127. // the hand of fate spell won't give any experience points
  2128. int dmg = calcInflictableDamagePerItem(-1, t, damage[spellLevel - 2], 0x80, 1);
  2129. inflictDamage(t, dmg, 0xFFFF, 3, 0x80);
  2130. }
  2131. }
  2132. if (_currentLevel == 29)
  2133. _screen->copyPage(12, 2);
  2134. _screen->copyPage(2, 0);
  2135. _screen->updateScreen();
  2136. gui_drawScene(2);
  2137. updateDrawPage2();
  2138. return 1;
  2139. }
  2140. int LoLEngine::processMagicMistOfDoom(int charNum, int spellLevel) {
  2141. static const uint8 mistDamage[] = { 30, 70, 110, 200 };
  2142. _envSfxUseQueue = true;
  2143. inflictMagicalDamageForBlock(calcNewBlockPosition(_currentBlock, _currentDirection), charNum, mistDamage[spellLevel], 0x80);
  2144. _envSfxUseQueue = false;
  2145. int cp = _screen->setCurPage(2);
  2146. _screen->copyPage(0, 2);
  2147. gui_drawScene(2);
  2148. _screen->copyPage(2, 12);
  2149. snd_playSoundEffect(155, -1);
  2150. Common::String wsafile = Common::String::format("mists%0d.wsa", spellLevel + 1);
  2151. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2152. mov->open(wsafile.c_str(), 1, 0);
  2153. if (!mov->opened())
  2154. error("Mist: Unable to load %s", wsafile.c_str());
  2155. snd_playSoundEffect(_mistAnimData[spellLevel].sound, -1);
  2156. playSpellAnimation(mov, _mistAnimData[spellLevel].part1First, _mistAnimData[spellLevel].part1Last, 7, 112, 0, 0, 0, 0, 0, false);
  2157. playSpellAnimation(mov, _mistAnimData[spellLevel].part2First, _mistAnimData[spellLevel].part2Last, 14, 112, 0, 0, 0, 0, 0, false);
  2158. mov->close();
  2159. delete mov;
  2160. _screen->setCurPage(cp);
  2161. _screen->copyPage(12, 0);
  2162. updateDrawPage2();
  2163. snd_playQueuedEffects();
  2164. return 1;
  2165. }
  2166. int LoLEngine::processMagicLightning(int charNum, int spellLevel) {
  2167. _screen->hideMouse();
  2168. _screen->copyPage(0, 2);
  2169. gui_drawScene(2);
  2170. _screen->copyPage(2, 12);
  2171. _lightningCurSfx = _lightningProps[spellLevel].sfxId;
  2172. _lightningDiv = _lightningProps[spellLevel].frameDiv;
  2173. _lightningFirstSfx = 0;
  2174. Common::String wsafile = Common::String::format("litning%d.wsa", spellLevel + 1);
  2175. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2176. mov->open(wsafile.c_str(), 1, 0);
  2177. if (!mov->opened())
  2178. error("Litning: Unable to load %s", wsafile.c_str());
  2179. for (int i = 0; i < 4; i++)
  2180. playSpellAnimation(mov, 0, _lightningProps[spellLevel].lastFrame, 3, 93, 0, &LoLEngine::callbackProcessMagicLightning, 0, 0, 0, false);
  2181. mov->close();
  2182. delete mov;
  2183. _screen->setScreenPalette(_screen->getPalette(1));
  2184. _screen->copyPage(12, 2);
  2185. _screen->copyPage(12, 0);
  2186. updateDrawPage2();
  2187. static const uint8 lightningDamage[] = { 18, 35, 50, 72 };
  2188. inflictMagicalDamageForBlock(calcNewBlockPosition(_currentBlock, _currentDirection), charNum, lightningDamage[spellLevel], 5);
  2189. _sceneUpdateRequired = true;
  2190. gui_drawScene(0);
  2191. _screen->showMouse();
  2192. return 1;
  2193. }
  2194. int LoLEngine::processMagicFog() {
  2195. int cp = _screen->setCurPage(2);
  2196. _screen->copyPage(0, 12);
  2197. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2198. int numFrames = mov->open("fog.wsa", 0, 0);
  2199. if (!mov->opened())
  2200. error("Fog: Unable to load fog.wsa");
  2201. snd_playSoundEffect(145, -1);
  2202. for (int curFrame = 0; curFrame < numFrames; curFrame++) {
  2203. uint32 delayTimer = _system->getMillis() + 3 * _tickLength;
  2204. _screen->copyPage(12, 2);
  2205. mov->displayFrame(curFrame % numFrames, 2, 112, 0, 0x5000, _transparencyTable1, _transparencyTable2);
  2206. _screen->copyRegion(112, 0, 112, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK);
  2207. _screen->updateScreen();
  2208. delayUntil(delayTimer);
  2209. }
  2210. mov->close();
  2211. delete mov;
  2212. _screen->copyPage(12, 2);
  2213. _screen->setCurPage(cp);
  2214. updateDrawPage2();
  2215. uint16 o = _levelBlockProperties[calcNewBlockPosition(_currentBlock, _currentDirection)].assignedObjects;
  2216. while (o & 0x8000) {
  2217. inflictMagicalDamage(o, -1, 15, 6, 0);
  2218. o = _monsters[o & 0x7FFF].nextAssignedObject;
  2219. }
  2220. gui_drawScene(0);
  2221. return 1;
  2222. }
  2223. int LoLEngine::processMagicSwarm(int charNum, int damage) {
  2224. createTransparencyTables();
  2225. int cp = _screen->setCurPage(2);
  2226. _screen->copyPage(0, 12);
  2227. snd_playSoundEffect(74, -1);
  2228. uint16 destIds[6];
  2229. uint8 destModes[6];
  2230. int8 destTicks[6];
  2231. memset(destIds, 0, sizeof(destIds));
  2232. memset(destModes, 8, sizeof(destModes));
  2233. memset(destTicks, 0, sizeof(destTicks));
  2234. int t = 0;
  2235. uint16 o = _levelBlockProperties[calcNewBlockPosition(_currentBlock, _currentDirection)].assignedObjects;
  2236. while (o & 0x8000) {
  2237. o &= 0x7FFF;
  2238. if (_monsters[o].mode != 13) {
  2239. destIds[t++] = o;
  2240. if (!(_monsters[o].flags & 0x2000)) {
  2241. _envSfxUseQueue = true;
  2242. inflictMagicalDamage(o | 0x8000, charNum, damage, 0, 0);
  2243. _envSfxUseQueue = false;
  2244. _monsters[o].flags &= 0xFFEF;
  2245. }
  2246. }
  2247. o = _monsters[o].nextAssignedObject;
  2248. }
  2249. for (int i = 0; i < t; i++) {
  2250. SWAP(destModes[i], _monsters[destIds[i]].mode);
  2251. SWAP(destTicks[i], _monsters[destIds[i]].fightCurTick);
  2252. }
  2253. gui_drawScene(_screen->_curPage);
  2254. _screen->copyRegion(112, 0, 112, 0, 176, 120, _screen->_curPage, 7);
  2255. for (int i = 0; i < t; i++) {
  2256. _monsters[destIds[i]].mode = destModes[i];
  2257. _monsters[destIds[i]].fightCurTick = destTicks[i];
  2258. }
  2259. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2260. mov->open("swarm.wsa", 0, 0);
  2261. if (!mov->opened())
  2262. error("Swarm: Unable to load SWARM.WSA");
  2263. _screen->hideMouse();
  2264. playSpellAnimation(mov, 0, 37, 2, 0, 0, 0, 0, 0, 0, false);
  2265. playSpellAnimation(mov, 38, 41, 8, 0, 0, &LoLEngine::callbackProcessMagicSwarm, 0, 0, 0, false);
  2266. _screen->showMouse();
  2267. mov->close();
  2268. _screen->copyPage(12, 0);
  2269. _screen->updateScreen();
  2270. updateDrawPage2();
  2271. snd_playQueuedEffects();
  2272. _screen->setCurPage(cp);
  2273. delete mov;
  2274. return 1;
  2275. }
  2276. int LoLEngine::processMagicVaelansCube() {
  2277. uint8 *sp1 = _screen->getPalette(1).getData();
  2278. int len = _screen->getPalette(1).getNumColors() * 3;
  2279. uint8 *tmpPal1 = new uint8[len];
  2280. uint8 *tmpPal2 = new uint8[len];
  2281. memcpy(tmpPal1, sp1, len);
  2282. memcpy(tmpPal2, sp1, len);
  2283. if (_flags.use16ColorMode) {
  2284. for (int i = 0; i < 16; i++) {
  2285. uint16 a = sp1[i * 3 + 1] + 16;
  2286. tmpPal2[i * 3 + 1] = (a > 58) ? 58 : a;
  2287. tmpPal2[i * 3] = sp1[i * 3];
  2288. a = sp1[i * 3 + 2] + 16;
  2289. tmpPal2[i * 3 + 2] = (a > 63) ? 63 : a;
  2290. }
  2291. } else {
  2292. for (int i = 0; i < 128; i++) {
  2293. uint16 a = sp1[i * 3] + 16;
  2294. tmpPal2[i * 3] = (a > 60) ? 60 : a;
  2295. tmpPal2[i * 3 + 1] = sp1[i * 3 + 1];
  2296. a = sp1[i * 3 + 2] + 19;
  2297. tmpPal2[i * 3 + 2] = (a > 60) ? 60 : a;
  2298. }
  2299. }
  2300. snd_playSoundEffect(146, -1);
  2301. uint32 ctime = _system->getMillis();
  2302. uint32 endTime = _system->getMillis() + 70 * _tickLength;
  2303. while (_system->getMillis() < endTime) {
  2304. _screen->timedPaletteFadeStep(tmpPal1, tmpPal2, _system->getMillis() - ctime, 70 * _tickLength);
  2305. updateInput();
  2306. }
  2307. uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection);
  2308. uint8 s = _levelBlockProperties[bl].walls[_currentDirection ^ 2];
  2309. uint8 flg = _wllWallFlags[s];
  2310. int res = (s == 47 && (_currentLevel == 17 || _currentLevel == 24)) ? 1 : 0;
  2311. if ((_wllVmpMap[s] == 1 || _wllVmpMap[s] == 2) && (!(flg & 1)) && (_currentLevel != 22)) {
  2312. memset(_levelBlockProperties[bl].walls, 0, 4);
  2313. gui_drawScene(0);
  2314. res = 1;
  2315. }
  2316. uint16 o = _levelBlockProperties[bl].assignedObjects;
  2317. while (o & 0x8000) {
  2318. LoLMonster *m = &_monsters[o & 0x7FFF];
  2319. if (m->properties->flags & 0x1000) {
  2320. inflictDamage(o, 100, 0xFFFF, 0, 0x80);
  2321. res = 1;
  2322. }
  2323. o = m->nextAssignedObject;
  2324. }
  2325. ctime = _system->getMillis();
  2326. endTime = _system->getMillis() + 70 * _tickLength;
  2327. while (_system->getMillis() < endTime) {
  2328. _screen->timedPaletteFadeStep(tmpPal2, tmpPal1, _system->getMillis() - ctime, 70 * _tickLength);
  2329. updateInput();
  2330. }
  2331. delete[] tmpPal1;
  2332. delete[] tmpPal2;
  2333. return res;
  2334. }
  2335. int LoLEngine::processMagicGuardian(int charNum) {
  2336. int cp = _screen->setCurPage(2);
  2337. _screen->copyPage(0, 2);
  2338. _screen->copyPage(2, 12);
  2339. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2340. mov->open("guardian.wsa", 0, 0);
  2341. if (!mov->opened())
  2342. error("Guardian: Unable to load guardian.wsa");
  2343. snd_playSoundEffect(156, -1);
  2344. playSpellAnimation(mov, 0, 37, 2, 112, 0, 0, 0, 0, 0, false);
  2345. _screen->copyPage(2, 12);
  2346. uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection);
  2347. int res = (_levelBlockProperties[bl].assignedObjects & 0x8000) ? 1 : 0;
  2348. inflictMagicalDamageForBlock(bl, charNum, 200, 0x80);
  2349. _screen->copyPage(12, 2);
  2350. updateDrawPage2();
  2351. gui_drawScene(2);
  2352. _screen->copyPage(2, 12);
  2353. snd_playSoundEffect(176, -1);
  2354. playSpellAnimation(mov, 38, 48, 8, 112, 0, 0, 0, 0, 0, false);
  2355. mov->close();
  2356. delete mov;
  2357. _screen->setCurPage(cp);
  2358. gui_drawPlayField();
  2359. updateDrawPage2();
  2360. return res;
  2361. }
  2362. void LoLEngine::callbackProcessMagicSwarm(WSAMovie_v2 *mov, int x, int y) {
  2363. if (_swarmSpellStatus)
  2364. _screen->copyRegion(112, 0, 112, 0, 176, 120, 6, _screen->_curPage);
  2365. _swarmSpellStatus ^= 1;
  2366. }
  2367. void LoLEngine::callbackProcessMagicLightning(WSAMovie_v2 *mov, int x, int y) {
  2368. if (_lightningDiv == 2)
  2369. shakeScene(1, 2, 3, 0);
  2370. const Palette &p1 = _screen->getPalette(1);
  2371. if (_lightningSfxFrame % _lightningDiv) {
  2372. _screen->setScreenPalette(p1);
  2373. } else {
  2374. Palette tpal(p1.getNumColors());
  2375. tpal.copy(p1);
  2376. int start = 6;
  2377. int end = 384;
  2378. if (_flags.use16ColorMode) {
  2379. start = 3;
  2380. end = 48;
  2381. }
  2382. for (int i = start; i < end; i++) {
  2383. uint16 v = (tpal[i] * 120) / 64;
  2384. tpal[i] = (v < 64) ? v : 63;
  2385. }
  2386. _screen->setScreenPalette(tpal);
  2387. }
  2388. if (_lightningDiv == 2) {
  2389. if (!_lightningFirstSfx) {
  2390. snd_playSoundEffect(_lightningCurSfx, -1);
  2391. _lightningFirstSfx = 1;
  2392. }
  2393. } else {
  2394. if (!(_lightningSfxFrame & 7))
  2395. snd_playSoundEffect(_lightningCurSfx, -1);
  2396. }
  2397. _lightningSfxFrame++;
  2398. }
  2399. void LoLEngine::drinkBezelCup(int numUses, int charNum) {
  2400. createTransparencyTables();
  2401. int cp = _screen->setCurPage(2);
  2402. snd_playSoundEffect(73, -1);
  2403. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2404. mov->open("bezel.wsa", 0, 0);
  2405. if (!mov->opened())
  2406. error("Bezel: Unable to load bezel.wsa");
  2407. int x = _activeCharsXpos[charNum] - 11;
  2408. int y = 124;
  2409. int w = mov->width();
  2410. int h = mov->height();
  2411. _screen->copyRegion(x, y, 0, 0, w, h, 0, 2, Screen::CR_NO_P_CHECK);
  2412. static const uint8 bezelAnimData[] = { 0, 26, 20, 27, 61, 55, 62, 92, 86, 93, 131, 125 };
  2413. int frm = bezelAnimData[numUses * 3];
  2414. int hpDiff = _characters[charNum].hitPointsMax - _characters[charNum].hitPointsCur;
  2415. uint16 step = 0;
  2416. do {
  2417. step = (step & 0xFF) + (hpDiff * 256) / (bezelAnimData[numUses * 3 + 1]);
  2418. increaseCharacterHitpoints(charNum, step / 256, true);
  2419. gui_drawCharPortraitWithStats(charNum);
  2420. uint32 etime = _system->getMillis() + 4 * _tickLength;
  2421. _screen->copyRegion(0, 0, x, y, w, h, 2, 2, Screen::CR_NO_P_CHECK);
  2422. mov->displayFrame(frm, 2, x, y, _flags.use16ColorMode ? 0x4000 : 0x5000, _transparencyTable1, _transparencyTable2);
  2423. _screen->copyRegion(x, y, x, y, w, h, 2, 0, Screen::CR_NO_P_CHECK);
  2424. _screen->updateScreen();
  2425. delayUntil(etime);
  2426. } while (++frm < bezelAnimData[numUses * 3 + 1]);
  2427. _characters[charNum].hitPointsCur = _characters[charNum].hitPointsMax;
  2428. _screen->copyRegion(0, 0, x, y, w, h, 2, 2, Screen::CR_NO_P_CHECK);
  2429. removeCharacterEffects(&_characters[charNum], 4, 4);
  2430. gui_drawCharPortraitWithStats(charNum);
  2431. _screen->copyRegion(x, y, x, y, w, h, 2, 0, Screen::CR_NO_P_CHECK);
  2432. _screen->updateScreen();
  2433. mov->close();
  2434. delete mov;
  2435. _screen->setCurPage(cp);
  2436. }
  2437. void LoLEngine::addSpellToScroll(int spell, int charNum) {
  2438. bool assigned = false;
  2439. int slot = 0;
  2440. for (int i = 0; i < 7; i++) {
  2441. if (!assigned && _availableSpells[i] == -1) {
  2442. assigned = true;
  2443. slot = i;
  2444. }
  2445. if (_availableSpells[i] == spell) {
  2446. _txt->printMessage(2, "%s", getLangString(0x42D0));
  2447. return;
  2448. }
  2449. }
  2450. if (spell > 1)
  2451. transferSpellToScollAnimation(charNum, spell, slot - 1);
  2452. _availableSpells[slot] = spell;
  2453. gui_enableDefaultPlayfieldButtons();
  2454. }
  2455. void LoLEngine::transferSpellToScollAnimation(int charNum, int spell, int slot) {
  2456. int cX = 16 + _activeCharsXpos[charNum];
  2457. if (slot != 1) {
  2458. _screen->loadBitmap("playfld.cps", 3, 3, 0);
  2459. _screen->copyRegion(8, 0, 216, 0, 96, 120, 3, 3, Screen::CR_NO_P_CHECK);
  2460. _screen->copyPage(3, 10);
  2461. for (int i = 0; i < 9; i++) {
  2462. int h = (slot + 1) * 9 + i + 1;
  2463. uint32 delayTimer = _system->getMillis() + _tickLength;
  2464. _screen->copyPage(10, 3);
  2465. _screen->copyRegion(216, 0, 8, 0, 96, 120, 3, 3, Screen::CR_NO_P_CHECK);
  2466. _screen->copyRegion(112, 0, 12, 0, 87, 15, 2, 2, Screen::CR_NO_P_CHECK);
  2467. _screen->copyRegion(201, 1, 17, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK);
  2468. _screen->copyRegion(208, 1, 89, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK);
  2469. int cp = _screen->setCurPage(2);
  2470. _screen->fillRect(21, 15, 89, h + 15, _flags.use16ColorMode ? 0xBB : 206);
  2471. _screen->copyRegion(112, 16, 12, h + 15, 87, 14, 2, 2, Screen::CR_NO_P_CHECK);
  2472. int y = 15;
  2473. Screen::FontId of = _screen->setFont(Screen::FID_9_FNT);
  2474. for (int ii = 0; ii < 7; ii++) {
  2475. if (_availableSpells[ii] == -1)
  2476. continue;
  2477. uint8 col = (ii == _selectedSpell) ? 132 : 1;
  2478. if (_flags.use16ColorMode)
  2479. col = (ii == _selectedSpell) ? 0x88 : 0x44;
  2480. _screen->fprintString("%s", 24, y, col, 0, 0, getLangString(_spellProperties[_availableSpells[ii]].spellNameCode));
  2481. y += 9;
  2482. }
  2483. _screen->setFont(of);
  2484. _screen->setCurPage(cp);
  2485. _screen->copyRegion(8, 0, 8, 0, 96, 120, 3, 0, Screen::CR_NO_P_CHECK);
  2486. _screen->updateScreen();
  2487. delayUntil(delayTimer);
  2488. }
  2489. }
  2490. _screen->hideMouse();
  2491. _screen->copyPage(0, 12);
  2492. int vX = _updateSpellBookCoords[slot << 1] + 32;
  2493. int vY = _updateSpellBookCoords[(slot << 1) + 1] + 5;
  2494. Common::String wsaFile = Common::String::format("write%0d", spell);
  2495. if (_flags.isTalkie)
  2496. wsaFile += (_lang == 1) ? 'f' : (_lang == 0 ? 'e' : 'g');
  2497. wsaFile += ".wsa";
  2498. snd_playSoundEffect(_updateSpellBookAnimData[(spell << 2) + 3], -1);
  2499. snd_playSoundEffect(95, -1);
  2500. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  2501. mov->open("getspell.wsa", 0, 0);
  2502. if (!mov->opened())
  2503. error("SpellBook: Unable to load getspell anim");
  2504. snd_playSoundEffect(128, -1);
  2505. playSpellAnimation(mov, 0, 25, 5, _activeCharsXpos[charNum], 148, 0, 0, 0, 0, true);
  2506. snd_playSoundEffect(128, -1);
  2507. playSpellAnimation(mov, 26, 52, 5, _activeCharsXpos[charNum], 148, 0, 0, 0, 0, true);
  2508. for (int i = 16; i > 0; i--) {
  2509. uint32 delayTimer = _system->getMillis() + _tickLength;
  2510. _screen->copyPage(12, 2);
  2511. int wsaX = vX + (((((cX - vX) << 8) / 16) * i) >> 8) - 16;
  2512. int wsaY = vY + (((((160 - vY) << 8) / 16) * i) >> 8) - 16;
  2513. mov->displayFrame(51, 2, wsaX, wsaY, 0x5000, _transparencyTable1, _transparencyTable2);
  2514. _screen->copyRegion(wsaX, wsaY, wsaX, wsaY, mov->width() + 48, mov->height() + 48, 2, 0, Screen::CR_NO_P_CHECK);
  2515. _screen->updateScreen();
  2516. delayUntil(delayTimer);
  2517. }
  2518. mov->close();
  2519. mov->open("spellexp.wsa", 0, 0);
  2520. if (!mov->opened())
  2521. error("SpellBook: Unable to load spellexp anim");
  2522. snd_playSoundEffect(168, -1);
  2523. playSpellAnimation(mov, 0, 8, 3, vX - 44, vY - 38, 0, 0, 0, 0, true);
  2524. mov->close();
  2525. mov->open("writing.wsa", 0, 0);
  2526. if (!mov->opened())
  2527. error("SpellBook: Unable to load writing anim");
  2528. playSpellAnimation(mov, 0, 6, 5, _updateSpellBookCoords[slot << 1], _updateSpellBookCoords[(slot << 1) + 1], 0, 0, 0, 0, false);
  2529. mov->close();
  2530. mov->open(wsaFile.c_str(), 0, 0);
  2531. if (!mov->opened())
  2532. error("SpellBook: Unable to load spellbook anim");
  2533. snd_playSoundEffect(_updateSpellBookAnimData[(spell << 2) + 3], -1);
  2534. playSpellAnimation(mov, _updateSpellBookAnimData[(spell << 2) + 1], _updateSpellBookAnimData[(spell << 2) + 2], _updateSpellBookAnimData[spell << 2], _updateSpellBookCoords[slot << 1], _updateSpellBookCoords[(slot << 1) + 1], 0, 0, 0, 0, false);
  2535. mov->close();
  2536. gui_drawScene(2);
  2537. updateDrawPage2();
  2538. _screen->showMouse();
  2539. delete mov;
  2540. }
  2541. void LoLEngine::playSpellAnimation(WSAMovie_v2 *mov, int firstFrame, int lastFrame, int frameDelay, int x, int y, SpellProcCallback callback, uint8 *pal1, uint8 *pal2, int fadeDelay, bool restoreScreen) {
  2542. int w = 0;
  2543. int h = 0;
  2544. if (mov) {
  2545. w = mov->width();
  2546. h = mov->height();
  2547. }
  2548. int w2 = w;
  2549. int h2 = h;
  2550. uint32 startTime = _system->getMillis();
  2551. if (x < 0)
  2552. w2 += x;
  2553. if (y < 0)
  2554. h2 += y;
  2555. int dir = lastFrame >= firstFrame ? 1 : -1;
  2556. int curFrame = firstFrame;
  2557. bool fin = false;
  2558. while (!fin) {
  2559. uint32 delayTimer = _system->getMillis() + _tickLength * frameDelay;
  2560. if (mov || callback)
  2561. _screen->copyPage(12, 2);
  2562. if (callback)
  2563. (this->*callback)(mov, x, y);
  2564. if (mov)
  2565. mov->displayFrame(curFrame % mov->frames(), 2, x, y, _flags.use16ColorMode ? 0x4000 : 0x5000, _transparencyTable1, _transparencyTable2);
  2566. if (mov || callback) {
  2567. _screen->copyRegion(x, y, x, y, w2, h2, 2, 0, Screen::CR_NO_P_CHECK);
  2568. _screen->updateScreen();
  2569. }
  2570. uint32 tm = _system->getMillis();
  2571. uint32 del = (delayTimer > tm) ? (delayTimer - tm) : 0;
  2572. do {
  2573. uint32 step = del > _tickLength ? _tickLength : del;
  2574. if (!pal1 || !pal2) {
  2575. if (del) {
  2576. delay(step);
  2577. del -= step;
  2578. } else {
  2579. updateInput();
  2580. }
  2581. continue;
  2582. }
  2583. if (!_screen->timedPaletteFadeStep(pal1, pal2, _system->getMillis() - startTime, _tickLength * fadeDelay) && !mov)
  2584. return;
  2585. if (del) {
  2586. delay(step);
  2587. del -= step;
  2588. } else {
  2589. updateInput();
  2590. }
  2591. } while (del);
  2592. if (!mov)
  2593. continue;
  2594. curFrame += dir;
  2595. if ((dir > 0 && curFrame >= lastFrame) || (dir < 0 && curFrame < lastFrame))
  2596. fin = true;
  2597. }
  2598. if (restoreScreen && (mov || callback)) {
  2599. _screen->copyPage(12, 2);
  2600. _screen->copyRegion(x, y, x, y, w2, h2, 2, 0, Screen::CR_NO_P_CHECK);
  2601. _screen->updateScreen();
  2602. }
  2603. }
  2604. int LoLEngine::checkMagic(int charNum, int spellNum, int spellLevel) {
  2605. if (_spellProperties[spellNum].mpRequired[spellLevel] > _characters[charNum].magicPointsCur) {
  2606. if (characterSays(0x4043, _characters[charNum].id, true))
  2607. _txt->printMessage(6, getLangString(0x4043), _characters[charNum].name);
  2608. return 1;
  2609. } else if (_spellProperties[spellNum].hpRequired[spellLevel] >= _characters[charNum].hitPointsCur) {
  2610. _txt->printMessage(2, getLangString(0x4179), _characters[charNum].name);
  2611. return 1;
  2612. }
  2613. return 0;
  2614. }
  2615. int LoLEngine::getSpellTargetBlock(int currentBlock, int direction, int maxDistance, uint16 &targetBlock) {
  2616. targetBlock = 0xFFFF;
  2617. uint16 c = calcNewBlockPosition(currentBlock, direction);
  2618. int i = 0;
  2619. for (; i < maxDistance; i++) {
  2620. if (_levelBlockProperties[currentBlock].assignedObjects & 0x8000) {
  2621. targetBlock = currentBlock;
  2622. return i;
  2623. }
  2624. if (_wllWallFlags[_levelBlockProperties[c].walls[direction ^ 2]] & 7) {
  2625. targetBlock = c;
  2626. return i;
  2627. }
  2628. currentBlock = c;
  2629. c = calcNewBlockPosition(currentBlock, direction);
  2630. }
  2631. return i;
  2632. }
  2633. void LoLEngine::inflictMagicalDamage(int target, int attacker, int damage, int index, int hitType) {
  2634. hitType = hitType ? 1 : 2;
  2635. damage = calcInflictableDamagePerItem(attacker, target, damage, index, hitType);
  2636. inflictDamage(target, damage, attacker, 2, index);
  2637. }
  2638. void LoLEngine::inflictMagicalDamageForBlock(int block, int attacker, int damage, int index) {
  2639. uint16 o = _levelBlockProperties[block].assignedObjects;
  2640. while (o & 0x8000) {
  2641. inflictDamage(o, calcInflictableDamagePerItem(attacker, o, damage, index, 2), attacker, 2, index);
  2642. if ((_monsters[o & 0x7FFF].flags & 0x20) && (_currentLevel != 22))
  2643. break;
  2644. o = _monsters[o & 0x7FFF].nextAssignedObject;
  2645. }
  2646. }
  2647. // fight
  2648. int LoLEngine::battleHitSkillTest(int16 attacker, int16 target, int skill) {
  2649. if (target == -1)
  2650. return 0;
  2651. if (attacker == -1)
  2652. return 1;
  2653. if (target & 0x8000) {
  2654. if (_monsters[target & 0x7FFF].mode >= 13)
  2655. return 0;
  2656. }
  2657. uint16 hitChanceModifier = 0;
  2658. uint16 evadeChanceModifier = 0;
  2659. int sk = 0;
  2660. if (attacker & 0x8000) {
  2661. hitChanceModifier = _monsters[target & 0x7FFF].properties->fightingStats[0];
  2662. sk = 100 - _monsters[target & 0x7FFF].properties->skillLevel;
  2663. } else {
  2664. hitChanceModifier = _characters[attacker].defaultModifiers[0];
  2665. int8 m = _characters[attacker].skillModifiers[skill];
  2666. if (skill == 1)
  2667. m *= 3;
  2668. sk = 100 - (_characters[attacker].skillLevels[skill] + m);
  2669. }
  2670. if (target & 0x8000) {
  2671. evadeChanceModifier = _monsters[target & 0x7FFF].properties->fightingStats[3];
  2672. if (_monsterModifiers4)
  2673. evadeChanceModifier = (evadeChanceModifier * _monsterModifiers4[_monsterDifficulty]) >> 8;
  2674. _monsters[target & 0x7FFF].flags |= 0x10;
  2675. } else {
  2676. evadeChanceModifier = _characters[target].defaultModifiers[3];
  2677. }
  2678. int r = rollDice(1, 100);
  2679. if (r >= sk)
  2680. return 2;
  2681. uint16 v = (evadeChanceModifier << 8) / hitChanceModifier;
  2682. if (r < v)
  2683. return 0;
  2684. return 1;
  2685. }
  2686. int LoLEngine::calcInflictableDamage(int16 attacker, int16 target, int hitType) {
  2687. const uint16 *s = getCharacterOrMonsterItemsMight(attacker);
  2688. // The original code looks somewhat like the commented out part of the next line.
  2689. // In the end the value is always set to zero. I do not know whether this is done on purpose or not.
  2690. // It might be a bug in the original code.
  2691. int res = 0/*attacker & 0x8000 ? 0 : _characters[attacker].might*/;
  2692. for (int i = 0; i < 8; i++)
  2693. res += calcInflictableDamagePerItem(attacker, target, s[i], i, hitType);
  2694. return res;
  2695. }
  2696. int LoLEngine::inflictDamage(uint16 target, int damage, uint16 attacker, int skill, int flags) {
  2697. LoLMonster *m = 0;
  2698. LoLCharacter *c = 0;
  2699. if (target & 0x8000) {
  2700. m = &_monsters[target & 0x7FFF];
  2701. if (m->mode >= 13)
  2702. return 0;
  2703. if (damage > 0) {
  2704. m->hitPoints -= damage;
  2705. m->damageReceived = 0x8000 | damage;
  2706. m->flags |= 0x10;
  2707. m->hitOffsX = rollDice(1, 24);
  2708. m->hitOffsX -= 12;
  2709. m->hitOffsY = rollDice(1, 24);
  2710. m->hitOffsY -= 12;
  2711. m->hitPoints = CLIP<int16>(m->hitPoints, 0, m->properties->hitPoints);
  2712. if (!(attacker & 0x8000))
  2713. applyMonsterDefenseSkill(m, attacker, flags, skill, damage);
  2714. snd_queueEnvironmentalSoundEffect(m->properties->sounds[2], m->block);
  2715. checkSceneUpdateNeed(m->block);
  2716. if (m->hitPoints <= 0) {
  2717. m->hitPoints = 0;
  2718. if (!(attacker & 0x8000))
  2719. increaseExperience(attacker, skill, m->properties->hitPoints);
  2720. setMonsterMode(m, 13);
  2721. }
  2722. } else {
  2723. m->hitPoints -= damage;
  2724. m->hitPoints = CLIP<int16>(m->hitPoints, 1, m->properties->hitPoints);
  2725. }
  2726. } else {
  2727. if (target > 3) {
  2728. // WORKAROUND for script bug
  2729. int i = 0;
  2730. for (; i < 4; i++) {
  2731. if (_characters[i].id == target) {
  2732. target = i;
  2733. break;
  2734. }
  2735. }
  2736. if (i == 4)
  2737. return 0;
  2738. }
  2739. c = &_characters[target];
  2740. if (!(c->flags & 1) || (c->flags & 8))
  2741. return 0;
  2742. if (!(c->flags & 0x1000))
  2743. snd_playSoundEffect(c->screamSfx, -1);
  2744. setTemporaryFaceFrame(target, 6, 4, 0);
  2745. // check for equipped cloud ring
  2746. if (flags == 4 && itemEquipped(target, 229))
  2747. damage >>= 2;
  2748. setCharacterMagicOrHitPoints(target, 0, -damage, 1);
  2749. if (c->hitPointsCur <= 0) {
  2750. characterHitpointsZero(target, flags);
  2751. } else {
  2752. _characters[target].damageSuffered = damage;
  2753. setCharacterUpdateEvent(target, 2, 4, 1);
  2754. }
  2755. gui_drawCharPortraitWithStats(target);
  2756. }
  2757. if (!(attacker & 0x8000)) {
  2758. if (!skill)
  2759. _characters[attacker].weaponHit = damage;
  2760. increaseExperience(attacker, skill, damage);
  2761. }
  2762. return damage;
  2763. }
  2764. void LoLEngine::characterHitpointsZero(int16 charNum, int flags) {
  2765. LoLCharacter *c = &_characters[charNum];
  2766. c->hitPointsCur = 0;
  2767. c->flags |= 8;
  2768. removeCharacterEffects(c, 1, 5);
  2769. _partyDamageFlags = flags;
  2770. }
  2771. void LoLEngine::removeCharacterEffects(LoLCharacter *c, int first, int last) {
  2772. for (int i = first; i <= last; i++) {
  2773. switch (i - 1) {
  2774. case 0:
  2775. c->flags &= 0xFFFB;
  2776. c->weaponHit = 0;
  2777. break;
  2778. case 1:
  2779. c->damageSuffered = 0;
  2780. break;
  2781. case 2:
  2782. c->flags &= 0xFFBF;
  2783. break;
  2784. case 3:
  2785. c->flags &= 0xFF7F;
  2786. break;
  2787. case 4:
  2788. c->flags &= 0xFEFF;
  2789. break;
  2790. case 6:
  2791. c->flags &= 0xEFFF;
  2792. break;
  2793. default:
  2794. break;
  2795. }
  2796. for (int ii = 0; ii < 5; ii++) {
  2797. if (i != c->characterUpdateEvents[ii])
  2798. continue;
  2799. c->characterUpdateEvents[ii] = 0;
  2800. c->characterUpdateDelay[ii] = 0;
  2801. }
  2802. }
  2803. _timer->enable(3);
  2804. }
  2805. int LoLEngine::calcInflictableDamagePerItem(int16 attacker, int16 target, uint16 itemMight, int index, int hitType) {
  2806. int dmg = (attacker == -1) ? 0x100 : getCharacterOrMonsterStats(attacker)[1];
  2807. const uint16 *st_t = getCharacterOrMonsterProtectionAgainstItems(target);
  2808. dmg = (dmg * itemMight) >> 8;
  2809. if (!dmg)
  2810. return 0;
  2811. if (!(attacker & 0x8000)) {
  2812. dmg = (dmg * _characters[attacker].totalMightModifier) >> 8;
  2813. if (!dmg)
  2814. return 0;
  2815. }
  2816. int d = (index & 0x80) ? st_t[7] : st_t[index];
  2817. int r = (dmg * ABS(d)) >> 8;
  2818. dmg = d < 0 ? -r : r;
  2819. if (hitType == 2 || !dmg)
  2820. return (dmg == 1) ? 2 : dmg;
  2821. int p = (calculateProtection(target) << 7) / dmg;
  2822. if (p > 217)
  2823. p = 217;
  2824. d = 256 - p;
  2825. r = (dmg * ABS(d)) >> 8;
  2826. dmg = d < 0 ? -r : r;
  2827. return (dmg < 2) ? 2 : dmg;
  2828. }
  2829. void LoLEngine::checkForPartyDeath() {
  2830. Button b;
  2831. b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE;
  2832. b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01;
  2833. for (int i = 0; i < 4; i++) {
  2834. if (!(_characters[i].flags & 1) || _characters[i].hitPointsCur <= 0)
  2835. continue;
  2836. return;
  2837. }
  2838. if (_weaponsDisabled)
  2839. clickedExitCharInventory(&b);
  2840. gui_drawAllCharPortraitsWithStats();
  2841. if (_partyDamageFlags & 0x40) {
  2842. _screen->fadeToBlack(40);
  2843. for (int i = 0; i < 4; i++) {
  2844. if (_characters[i].flags & 1)
  2845. increaseCharacterHitpoints(i, 1, true);
  2846. }
  2847. gui_drawAllCharPortraitsWithStats();
  2848. _screen->fadeToPalette1(40);
  2849. } else {
  2850. if (!_flags.use16ColorMode)
  2851. _screen->fadeClearSceneWindow(10);
  2852. restoreAfterSpecialScene(0, 1, 1, 0);
  2853. snd_playTrack(325);
  2854. stopPortraitSpeechAnim();
  2855. initTextFading(0, 1);
  2856. setMouseCursorToIcon(0);
  2857. _updateFlags |= 4;
  2858. setLampMode(true);
  2859. disableSysTimer(2);
  2860. _gui->runMenu(_gui->_deathMenu);
  2861. setMouseCursorToItemInHand();
  2862. _updateFlags &= 0xFFFB;
  2863. resetLampStatus();
  2864. gui_enableDefaultPlayfieldButtons();
  2865. enableSysTimer(2);
  2866. updateDrawPage2();
  2867. }
  2868. }
  2869. void LoLEngine::applyMonsterAttackSkill(LoLMonster *monster, int16 target, int16 damage) {
  2870. if (rollDice(1, 100) > monster->properties->attackSkillChance)
  2871. return;
  2872. int t = 0;
  2873. switch (monster->properties->attackSkillType - 1) {
  2874. case 0:
  2875. t = removeCharacterItem(target, 0x7FF);
  2876. if (t) {
  2877. giveItemToMonster(monster, t);
  2878. if (characterSays(0x4019, _characters[target].id, true))
  2879. _txt->printMessage(6, "%s", getLangString(0x4019));
  2880. }
  2881. break;
  2882. case 1:
  2883. // poison character
  2884. paralyzePoisonCharacter(target, 0x80, 0x88, 100, 1);
  2885. break;
  2886. case 2:
  2887. t = removeCharacterItem(target, 0x20);
  2888. if (t) {
  2889. deleteItem(t);
  2890. if (characterSays(0x401B, _characters[target].id, true))
  2891. _txt->printMessage(6, "%s", getLangString(0x401B));
  2892. }
  2893. break;
  2894. case 3:
  2895. t = removeCharacterItem(target, 0x0F);
  2896. if (t) {
  2897. if (characterSays(0x401E, _characters[target].id, true))
  2898. _txt->printMessage(6, getLangString(0x401E), _characters[target].name);
  2899. setItemPosition(t, monster->x, monster->y, 0, 1);
  2900. }
  2901. break;
  2902. case 5:
  2903. if (_characters[target].magicPointsCur <= 0)
  2904. return;
  2905. monster->hitPoints += _characters[target].magicPointsCur;
  2906. _characters[target].magicPointsCur = 0;
  2907. gui_drawCharPortraitWithStats(target);
  2908. if (characterSays(0x4020, _characters[target].id, true))
  2909. _txt->printMessage(6, getLangString(0x4020), _characters[target].name);
  2910. break;
  2911. case 7:
  2912. stunCharacter(target);
  2913. break;
  2914. case 8:
  2915. monster->hitPoints += damage;
  2916. if (monster->hitPoints > monster->properties->hitPoints)
  2917. monster->hitPoints = monster->properties->hitPoints;
  2918. break;
  2919. case 9:
  2920. // paralyze party (spider web)
  2921. paralyzePoisonAllCharacters(0x40, 0x48, 100);
  2922. break;
  2923. default:
  2924. break;
  2925. }
  2926. }
  2927. void LoLEngine::applyMonsterDefenseSkill(LoLMonster *monster, int16 attacker, int flags, int skill, int damage) {
  2928. if (rollDice(1, 100) > monster->properties->defenseSkillChance)
  2929. return;
  2930. int itm = 0;
  2931. switch (monster->properties->defenseSkillType - 1) {
  2932. case 0:
  2933. case 1:
  2934. if ((flags & 0x3F) == 2 || skill)
  2935. return;
  2936. for (int i = 0; i < 3; i++) {
  2937. itm = _characters[attacker].items[i];
  2938. if (!itm)
  2939. continue;
  2940. if ((_itemProperties[_itemsInPlay[itm].itemPropertyIndex].protection & 0x3F) != flags)
  2941. continue;
  2942. removeCharacterItem(attacker, 0x7FFF);
  2943. if (monster->properties->defenseSkillType == 1) {
  2944. giveItemToMonster(monster, itm);
  2945. if (characterSays(0x401C, _characters[attacker].id, true))
  2946. _txt->printMessage(6, "%s", getLangString(0x401C));
  2947. } else {
  2948. deleteItem(itm);
  2949. if (characterSays(0x401D, _characters[attacker].id, true))
  2950. _txt->printMessage(6, "%s", getLangString(0x401D));
  2951. }
  2952. }
  2953. break;
  2954. case 2:
  2955. if (!(flags & 0x80))
  2956. return;
  2957. monster->flags |= 8;
  2958. monster->direction = calcMonsterDirection(monster->x, monster->y, _partyPosX, _partyPosY) ^ 4;
  2959. setMonsterMode(monster, 9);
  2960. monster->fightCurTick = 30;
  2961. break;
  2962. case 3:
  2963. if (flags != 3)
  2964. return;
  2965. monster->hitPoints += damage;
  2966. if (monster->hitPoints > monster->properties->hitPoints)
  2967. monster->hitPoints = monster->properties->hitPoints;
  2968. break;
  2969. case 4:
  2970. if (!(flags & 0x80))
  2971. return;
  2972. monster->hitPoints += damage;
  2973. if (monster->hitPoints > monster->properties->hitPoints)
  2974. monster->hitPoints = monster->properties->hitPoints;
  2975. break;
  2976. case 5:
  2977. if ((flags & 0x84) == 0x84)
  2978. monster->numDistAttacks++;
  2979. break;
  2980. default:
  2981. break;
  2982. }
  2983. }
  2984. int LoLEngine::removeCharacterItem(int charNum, int itemFlags) {
  2985. for (int i = 0; i < 11; i++) {
  2986. int s = _characters[charNum].items[i];
  2987. if (!((1 << i) & itemFlags) || !s)
  2988. continue;
  2989. _characters[charNum].items[i] = 0;
  2990. runItemScript(charNum, s, 0x100, 0, 0);
  2991. return s;
  2992. }
  2993. return 0;
  2994. }
  2995. int LoLEngine::paralyzePoisonCharacter(int charNum, int typeFlag, int immunityFlags, int hitChance, int redraw) {
  2996. if (!(_characters[charNum].flags & 1) || (_characters[charNum].flags & immunityFlags))
  2997. return 0;
  2998. if (rollDice(1, 100) > hitChance)
  2999. return 0;
  3000. int r = 0;
  3001. if (typeFlag == 0x40) {
  3002. _characters[charNum].flags |= 0x40;
  3003. setCharacterUpdateEvent(charNum, 3, 3600, 1);
  3004. r = 1;
  3005. // check for bezel ring
  3006. } else if (typeFlag == 0x80 && !itemEquipped(charNum, 225)) {
  3007. _characters[charNum].flags |= 0x80;
  3008. setCharacterUpdateEvent(charNum, 4, 10, 1);
  3009. if (characterSays(0x4021, _characters[charNum].id, true))
  3010. _txt->printMessage(6, getLangString(0x4021), _characters[charNum].name);
  3011. r = 1;
  3012. } else if (typeFlag == 0x1000) {
  3013. _characters[charNum].flags |= 0x1000;
  3014. setCharacterUpdateEvent(charNum, 7, 120, 1);
  3015. r = 1;
  3016. }
  3017. if (r && redraw)
  3018. gui_drawCharPortraitWithStats(charNum);
  3019. return r;
  3020. }
  3021. void LoLEngine::paralyzePoisonAllCharacters(int typeFlag, int immunityFlags, int hitChance) {
  3022. bool r = false;
  3023. for (int i = 0; i < 4; i++) {
  3024. if (paralyzePoisonCharacter(i, typeFlag, immunityFlags, hitChance, 0))
  3025. r = true;
  3026. }
  3027. if (r)
  3028. gui_drawAllCharPortraitsWithStats();
  3029. }
  3030. void LoLEngine::stunCharacter(int charNum) {
  3031. if (!(_characters[charNum].flags & 1) || (_characters[charNum].flags & 0x108))
  3032. return;
  3033. _characters[charNum].flags |= 0x100;
  3034. setCharacterUpdateEvent(charNum, 5, 20, 1);
  3035. gui_drawCharPortraitWithStats(charNum);
  3036. _txt->printMessage(6, getLangString(0x4026), _characters[charNum].name);
  3037. }
  3038. void LoLEngine::restoreSwampPalette() {
  3039. _flagsTable[52] &= 0xFB;
  3040. if (_currentLevel != 11)
  3041. return;
  3042. uint8 *s = _screen->getPalette(2).getData();
  3043. uint8 *d = _screen->getPalette(0).getData();
  3044. uint8 *d2 = _screen->getPalette(1).getData();
  3045. for (int i = 1; i < (_screen->getPalette(0).getNumColors() * 3); i++)
  3046. SWAP(s[i], d[i]);
  3047. generateBrightnessPalette(_screen->getPalette(0), _screen->getPalette(1), _brightness, _lampEffect);
  3048. _screen->loadSpecialColors(_screen->getPalette(2));
  3049. _screen->loadSpecialColors(_screen->getPalette(1));
  3050. playSpellAnimation(0, 0, 0, 2, 0, 0, 0, s, d2, 40, 0);
  3051. }
  3052. void LoLEngine::launchMagicViper() {
  3053. _partyAwake = true;
  3054. int d = 0;
  3055. for (uint16 b = _currentBlock; d < 3; d++) {
  3056. uint16 o = _levelBlockProperties[b].assignedObjects;
  3057. if (o & 0x8000)
  3058. break;
  3059. b = calcNewBlockPosition(b, _currentDirection);
  3060. if (_wllWallFlags[_levelBlockProperties[b].walls[_currentDirection ^ 2]] & 7)
  3061. break;
  3062. }
  3063. _screen->copyPage(0, 12);
  3064. snd_playSoundEffect(148, -1);
  3065. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  3066. int numFrames = mov->open("viper.wsa", 1, 0);
  3067. if (!mov->opened())
  3068. error("Viper: Unable to load viper.wsa");
  3069. static const uint8 viperAnimData[] = { 15, 25, 20, 10, 25, 20, 5, 25, 20, 0, 25, 20 };
  3070. const uint8 *v = &viperAnimData[d * 3];
  3071. int frm = v[0];
  3072. for (bool running = true; running;) {
  3073. uint32 etime = _system->getMillis() + 5 * _tickLength;
  3074. _screen->copyPage(12, 2);
  3075. if (frm == v[2])
  3076. snd_playSoundEffect(172, -1);
  3077. mov->displayFrame(frm++ % numFrames, 2, 112, 0, 0x5000, _transparencyTable1, _transparencyTable2);
  3078. _screen->copyRegion(112, 0, 112, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK);
  3079. _screen->updateScreen();
  3080. delayUntil(etime);
  3081. if (frm > v[1])
  3082. running = false;
  3083. }
  3084. mov->close();
  3085. delete mov;
  3086. _screen->copyPage(12, 0);
  3087. _screen->copyPage(12, 2);
  3088. int t = rollDice(1, 4);
  3089. for (int i = 0; i < 4; i++) {
  3090. if (!(_characters[i].flags & 1)) {
  3091. t = t % 4;
  3092. continue;
  3093. }
  3094. inflictDamage(t, _currentLevel + 10, 0x8000, 2, 0x86);
  3095. }
  3096. }
  3097. void LoLEngine::breakIceWall(uint8 *pal1, uint8 *pal2) {
  3098. _screen->hideMouse();
  3099. uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection);
  3100. _levelBlockProperties[bl].flags &= 0xEF;
  3101. _screen->copyPage(0, 2);
  3102. gui_drawScene(2);
  3103. _screen->copyPage(2, 10);
  3104. WSAMovie_v2 *mov = new WSAMovie_v2(this);
  3105. int numFrames = mov->open("shatter.wsa", 1, 0);
  3106. if (!mov->opened())
  3107. error("Shatter: Unable to load shatter.wsa");
  3108. snd_playSoundEffect(166, -1);
  3109. playSpellAnimation(mov, 0, numFrames, 1, 58, 0, 0, pal1, pal2, 20, true);
  3110. mov->close();
  3111. delete mov;
  3112. _screen->copyPage(10, 0);
  3113. updateDrawPage2();
  3114. gui_drawScene(0);
  3115. _screen->showMouse();
  3116. }
  3117. uint16 LoLEngine::getNearestMonsterFromCharacter(int charNum) {
  3118. return getNearestMonsterFromCharacterForBlock(calcNewBlockPosition(_currentBlock, _currentDirection), charNum);
  3119. }
  3120. uint16 LoLEngine::getNearestMonsterFromCharacterForBlock(uint16 block, int charNum) {
  3121. uint16 cX = 0;
  3122. uint16 cY = 0;
  3123. uint16 id = 0xFFFF;
  3124. int minDist = 0x7FFF;
  3125. if (block == 0xFFFF)
  3126. return id;
  3127. calcCoordinatesForSingleCharacter(charNum, cX, cY);
  3128. int o = _levelBlockProperties[block].assignedObjects;
  3129. while (o & 0x8000) {
  3130. LoLMonster *m = &_monsters[o & 0x7FFF];
  3131. if (m->mode >= 13) {
  3132. o = m->nextAssignedObject;
  3133. continue;
  3134. }
  3135. int d = ABS(cX - m->x) + ABS(cY - m->y);
  3136. if (d < minDist) {
  3137. minDist = d;
  3138. id = o;
  3139. }
  3140. o = m->nextAssignedObject;
  3141. }
  3142. return id;
  3143. }
  3144. uint16 LoLEngine::getNearestMonsterFromPos(int x, int y) {
  3145. uint16 id = 0xFFFF;
  3146. int minDist = 0x7FFF;
  3147. for (int i = 0; i < 30; i++) {
  3148. if (_monsters[i].mode > 13)
  3149. continue;
  3150. int d = ABS(x - _monsters[i].x) + ABS(y - _monsters[i].y);
  3151. if (d < minDist) {
  3152. minDist = d;
  3153. id = 0x8000 | i;
  3154. }
  3155. }
  3156. return id;
  3157. }
  3158. uint16 LoLEngine::getNearestPartyMemberFromPos(int x, int y) {
  3159. uint16 id = 0xFFFF;
  3160. int minDist = 0x7FFF;
  3161. for (int i = 0; i < 4; i++) {
  3162. if (!(_characters[i].flags & 1) || _characters[i].hitPointsCur <= 0)
  3163. continue;
  3164. uint16 charX = 0;
  3165. uint16 charY = 0;
  3166. calcCoordinatesForSingleCharacter(i, charX, charY);
  3167. int d = ABS(x - charX) + ABS(y - charY);
  3168. if (d < minDist) {
  3169. minDist = d;
  3170. id = i;
  3171. }
  3172. }
  3173. return id;
  3174. }
  3175. // magic atlas
  3176. void LoLEngine::displayAutomap() {
  3177. snd_playSoundEffect(105, -1);
  3178. gui_toggleButtonDisplayMode(_flags.isTalkie ? 78 : 76, 1);
  3179. _currentMapLevel = _currentLevel;
  3180. uint8 *tmpWll = new uint8[80];
  3181. memcpy(tmpWll, _wllAutomapData, 80);
  3182. _screen->loadBitmap("parch.cps", 2, 2, &_screen->getPalette(3));
  3183. _screen->loadBitmap("autobut.shp", 3, 5, 0);
  3184. const uint8 *shp = _screen->getCPagePtr(5);
  3185. for (int i = 0; i < 109; i++)
  3186. _automapShapes[i] = _screen->getPtrToShape(shp, i + 11);
  3187. if (_flags.use16ColorMode) {
  3188. static const uint8 ovlSrc[] = { 0x00, 0xEE, 0xCC, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0x22, 0x11, 0xDD, 0xEE, 0xCC };
  3189. memset(_mapOverlay, 0, 256);
  3190. for (int i = 0; i < 16; i++)
  3191. _mapOverlay[(i << 4) | i] = ovlSrc[i];
  3192. } else
  3193. _screen->generateGrayOverlay(_screen->getPalette(3), _mapOverlay, 52, 0, 0, 0, 256, false);
  3194. _screen->loadFont(Screen::FID_9_FNT, "FONT9PN.FNT");
  3195. _screen->loadFont(Screen::FID_6_FNT, "FONT6PN.FNT");
  3196. for (int i = 0; i < 11; i++)
  3197. _defaultLegendData[i].enable = false;
  3198. disableSysTimer(2);
  3199. generateTempData();
  3200. resetItems(1);
  3201. disableMonsters();
  3202. bool exitAutomap = false;
  3203. _mapUpdateNeeded = false;
  3204. restoreBlockTempData(_currentMapLevel);
  3205. loadMapLegendData(_currentMapLevel);
  3206. _screen->fadeToBlack(10);
  3207. drawMapPage(2);
  3208. _screen->copyPage(2, 0);
  3209. _screen->updateScreen();
  3210. _screen->fadePalette(_screen->getPalette(3), 10);
  3211. uint32 delayTimer = _system->getMillis() + 8 * _tickLength;
  3212. while (!exitAutomap && !shouldQuit()) {
  3213. if (_mapUpdateNeeded) {
  3214. drawMapPage(2);
  3215. _screen->copyPage(2, 0);
  3216. _screen->updateScreen();
  3217. _mapUpdateNeeded = false;
  3218. }
  3219. if (_system->getMillis() >= delayTimer) {
  3220. redrawMapCursor();
  3221. delayTimer = _system->getMillis() + 8 * _tickLength;
  3222. }
  3223. int f = checkInput(0) & 0xFF;
  3224. removeInputTop();
  3225. if (f) {
  3226. exitAutomap = automapProcessButtons(f);
  3227. gui_notifyButtonListChanged();
  3228. }
  3229. if (f == _keyMap[Common::KEYCODE_c]) {
  3230. for (int i = 0; i < 1024; i++)
  3231. _levelBlockProperties[i].flags |= 7;
  3232. _mapUpdateNeeded = true;
  3233. } else if (f == _keyMap[Common::KEYCODE_ESCAPE]) {
  3234. exitAutomap = true;
  3235. }
  3236. delay(_tickLength);
  3237. }
  3238. _screen->loadFont(Screen::FID_9_FNT, "FONT9P.FNT");
  3239. _screen->loadFont(Screen::FID_6_FNT, "FONT6P.FNT");
  3240. if (_flags.use16ColorMode)
  3241. _screen->clearPage(2);
  3242. _screen->fadeToBlack(10);
  3243. loadLevelWallData(_currentLevel, false);
  3244. memcpy(_wllAutomapData, tmpWll, 80);
  3245. delete[] tmpWll;
  3246. restoreBlockTempData(_currentLevel);
  3247. addLevelItems();
  3248. gui_notifyButtonListChanged();
  3249. enableSysTimer(2);
  3250. }
  3251. void LoLEngine::updateAutoMap(uint16 block) {
  3252. if (!(_flagsTable[31] & 0x10))
  3253. return;
  3254. _levelBlockProperties[block].flags |= 7;
  3255. uint16 x = block & 0x1F;
  3256. uint16 y = block >> 5;
  3257. updateAutoMapIntern(block, x, y, -1, -1);
  3258. updateAutoMapIntern(block, x, y, 1, -1);
  3259. updateAutoMapIntern(block, x, y, -1, 1);
  3260. updateAutoMapIntern(block, x, y, 1, 1);
  3261. updateAutoMapIntern(block, x, y, 0, -1);
  3262. updateAutoMapIntern(block, x, y, 0, 1);
  3263. updateAutoMapIntern(block, x, y, -1, 0);
  3264. updateAutoMapIntern(block, x, y, 1, 0);
  3265. }
  3266. bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOffs, int16 yOffs) {
  3267. static const int16 blockPosTable[] = { 1, -1, 3, 2, -1, 0, -1, 0, 1, -32, 0, 32 };
  3268. x += xOffs;
  3269. y += yOffs;
  3270. if ((x & 0xFFE0) || (y & 0xFFE0))
  3271. return false;
  3272. xOffs++;
  3273. yOffs++;
  3274. int16 fx = blockPosTable[xOffs];
  3275. uint16 b = block + blockPosTable[6 + xOffs];
  3276. if (fx != -1) {
  3277. if (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xC0)
  3278. return false;
  3279. }
  3280. int16 fy = blockPosTable[3 + yOffs];
  3281. b = block + blockPosTable[9 + yOffs];
  3282. if (fy != -1) {
  3283. if (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xC0)
  3284. return false;
  3285. }
  3286. b = block + blockPosTable[6 + xOffs] + blockPosTable[9 + yOffs];
  3287. if ((fx != -1) && (fy != -1) && (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xC0) && (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xC0))
  3288. return false;
  3289. _levelBlockProperties[b].flags |= 7;
  3290. return true;
  3291. }
  3292. void LoLEngine::loadMapLegendData(int level) {
  3293. uint16 *legendData = (uint16 *)_tempBuffer5120;
  3294. for (int i = 0; i < 32; i++) {
  3295. legendData[i * 6] = 0xFFFF;
  3296. legendData[i * 6 + 5] = 0xFFFF;
  3297. }
  3298. Common::String file = Common::String::format("level%d.xxx", level);
  3299. uint32 size = 0;
  3300. uint8 *data = _res->fileData(file.c_str(), &size);
  3301. uint8 *pos = data;
  3302. size = MIN<uint32>(size / 12, 32);
  3303. for (uint32 i = 0; i < size; i++) {
  3304. uint16 *l = &legendData[i * 6];
  3305. l[3] = READ_LE_UINT16(pos);
  3306. pos += 2;
  3307. l[4] = READ_LE_UINT16(pos);
  3308. pos += 2;
  3309. l[5] = READ_LE_UINT16(pos);
  3310. pos += 2;
  3311. l[0] = READ_LE_UINT16(pos);
  3312. pos += 2;
  3313. l[1] = READ_LE_UINT16(pos);
  3314. pos += 2;
  3315. l[2] = READ_LE_UINT16(pos);
  3316. pos += 2;
  3317. }
  3318. delete[] data;
  3319. }
  3320. void LoLEngine::drawMapPage(int pageNum) {
  3321. // WORKAROUND for French version. The text does not always properly fit the screen there.
  3322. const int8 xOffset = (_lang == 1) ? -2 : 0;
  3323. if (_flags.use16ColorMode)
  3324. _screen->clearPage(pageNum);
  3325. for (int i = 0; i < 2; i++) {
  3326. _screen->loadBitmap("parch.cps", pageNum, pageNum, &_screen->getPalette(3));
  3327. if (_lang == 1)
  3328. _screen->copyRegion(236, 16, 236 + xOffset, 16, -xOffset, 1, pageNum, pageNum, Screen::CR_NO_P_CHECK);
  3329. int cp = _screen->setCurPage(pageNum);
  3330. Screen::FontId of = _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT);
  3331. _screen->printText(getLangString(_autoMapStrings[_currentMapLevel]), 236 + xOffset, 8, 1, 0);
  3332. uint16 blX = mapGetStartPosX();
  3333. uint16 bl = (mapGetStartPosY() << 5) + blX;
  3334. int sx = _automapTopLeftX;
  3335. int sy = _automapTopLeftY;
  3336. for (; bl < 1024; bl++) {
  3337. uint8 *w = _levelBlockProperties[bl].walls;
  3338. if ((_levelBlockProperties[bl].flags & 7) == 7 && (!(_wllAutomapData[w[0]] & 0xC0)) && (!(_wllAutomapData[w[2]] & 0xC0)) && (!(_wllAutomapData[w[1]] & 0xC0)) && (!(_wllAutomapData[w[3]] & 0xC0))) {
  3339. uint16 b0 = calcNewBlockPosition(bl, 0);
  3340. uint16 b2 = calcNewBlockPosition(bl, 2);
  3341. uint16 b1 = calcNewBlockPosition(bl, 1);
  3342. uint16 b3 = calcNewBlockPosition(bl, 3);
  3343. uint8 w02 = _levelBlockProperties[b0].walls[2];
  3344. uint8 w20 = _levelBlockProperties[b2].walls[0];
  3345. uint8 w13 = _levelBlockProperties[b1].walls[3];
  3346. uint8 w31 = _levelBlockProperties[b3].walls[1];
  3347. // draw block
  3348. _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy, _screen->_curPage, sx, sy, 7, 6, 0, _mapOverlay);
  3349. // draw north wall
  3350. drawMapBlockWall(b3, w31, sx, sy, 3);
  3351. drawMapShape(w31, sx, sy, 3);
  3352. if (_wllAutomapData[w31] & 0xC0)
  3353. _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy, _screen->_curPage, sx, sy, 1, 6, 0, _mapOverlay);
  3354. // draw west wall
  3355. drawMapBlockWall(b1, w13, sx, sy, 1);
  3356. drawMapShape(w13, sx, sy, 1);
  3357. if (_wllAutomapData[w13] & 0xC0)
  3358. _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx + 6, sy, _screen->_curPage, sx + 6, sy, 1, 6, 0, _mapOverlay);
  3359. // draw east wall
  3360. drawMapBlockWall(b0, w02, sx, sy, 0);
  3361. drawMapShape(w02, sx, sy, 0);
  3362. if (_wllAutomapData[w02] & 0xC0)
  3363. _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy, _screen->_curPage, sx, sy, 7, 1, 0, _mapOverlay);
  3364. //draw south wall
  3365. drawMapBlockWall(b2, w20, sx, sy, 2);
  3366. drawMapShape(w20, sx, sy, 2);
  3367. if (_wllAutomapData[w20] & 0xC0)
  3368. _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy + 5, _screen->_curPage, sx, sy + 5, 7, 1, 0, _mapOverlay);
  3369. }
  3370. sx += 7;
  3371. if (bl % 32 == 31) {
  3372. sx = _automapTopLeftX;
  3373. sy += 6;
  3374. bl += blX;
  3375. }
  3376. }
  3377. _screen->setFont(of);
  3378. _screen->setCurPage(cp);
  3379. of = _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
  3380. int tY = 0;
  3381. sx = mapGetStartPosX();
  3382. sy = mapGetStartPosY();
  3383. uint16 *legendData = (uint16 *)_tempBuffer5120;
  3384. uint8 yOffset = _flags.use16ColorMode ? 4 : 0;
  3385. for (int ii = 0; ii < 32; ii++) {
  3386. uint16 *l = &legendData[ii * 6];
  3387. if (l[0] == 0xFFFF)
  3388. break;
  3389. uint16 cbl = l[0] + (l[1] << 5);
  3390. if ((_levelBlockProperties[cbl].flags & 7) != 7)
  3391. continue;
  3392. if (l[2] == 0xFFFF)
  3393. continue;
  3394. printMapText(l[2], 244 + xOffset, (tY << 3) + 22 + yOffset);
  3395. if (l[5] == 0xFFFF) {
  3396. tY++;
  3397. continue;
  3398. }
  3399. uint16 cbl2 = l[3] + (l[4] << 5);
  3400. _levelBlockProperties[cbl2].flags |= 7;
  3401. _screen->drawShape(2, _automapShapes[l[5] << 2], (l[3] - sx) * 7 + _automapTopLeftX - 3, (l[4] - sy) * 6 + _automapTopLeftY - 3, 0, 0);
  3402. _screen->drawShape(2, _automapShapes[l[5] << 2], 231 + xOffset, (tY << 3) + 19 + yOffset, 0, 0);
  3403. tY++;
  3404. }
  3405. cp = _screen->setCurPage(pageNum);
  3406. for (int ii = 0; ii < 11; ii++) {
  3407. if (!_defaultLegendData[ii].enable)
  3408. continue;
  3409. _screen->copyBlockAndApplyOverlay(_screen->_curPage, 235, (tY << 3) + 21 + yOffset, _screen->_curPage, 235 + xOffset, (tY << 3) + 21 + yOffset, 7, 6, 0, _mapOverlay);
  3410. _screen->drawShape(_screen->_curPage, _automapShapes[_defaultLegendData[ii].shapeIndex << 2], 232 + xOffset, (tY << 3) + 18 + yOffset + _defaultLegendData[ii].y, 0, 0);
  3411. printMapText(_defaultLegendData[ii].stringId, 244 + xOffset, (tY << 3) + 22 + yOffset);
  3412. tY++;
  3413. }
  3414. _screen->setFont(of);
  3415. _screen->setCurPage(cp);
  3416. }
  3417. printMapExitButtonText();
  3418. }
  3419. bool LoLEngine::automapProcessButtons(int inputFlag) {
  3420. int r = -1;
  3421. if (inputFlag == _keyMap[Common::KEYCODE_RIGHT] || inputFlag == _keyMap[Common::KEYCODE_KP6]) {
  3422. r = 0;
  3423. } else if (inputFlag == _keyMap[Common::KEYCODE_LEFT] || inputFlag == _keyMap[Common::KEYCODE_KP4]) {
  3424. r = 1;
  3425. } else if (inputFlag == 199) {
  3426. if (posWithinRect(_mouseX, _mouseY, 252, 175, 273, 200))
  3427. r = 0;
  3428. else if (posWithinRect(_mouseX, _mouseY, 231, 175, 252, 200))
  3429. r = 1;
  3430. else if (posWithinRect(_mouseX, _mouseY, 275, 175, 315, 197))
  3431. r = 2;
  3432. printMapExitButtonText();
  3433. while (inputFlag == 199 || inputFlag == 200) {
  3434. inputFlag = checkInput(0, false);
  3435. removeInputTop();
  3436. delay(_tickLength);
  3437. }
  3438. } else {
  3439. return false;
  3440. }
  3441. if (r == 0) {
  3442. automapForwardButton();
  3443. printMapExitButtonText();
  3444. } else if (r == 1) {
  3445. automapBackButton();
  3446. printMapExitButtonText();
  3447. } if (r == 2) {
  3448. return true;
  3449. }
  3450. return false;
  3451. }
  3452. void LoLEngine::automapForwardButton() {
  3453. int i = _currentMapLevel + 1;
  3454. while (!(_hasTempDataFlags & (1 << (i - 1))))
  3455. i = (i + 1) & 0x1F;
  3456. if (i == _currentMapLevel)
  3457. return;
  3458. for (int l = 0; l < 11; l++)
  3459. _defaultLegendData[l].enable = false;
  3460. _currentMapLevel = i;
  3461. loadLevelWallData(i, false);
  3462. restoreBlockTempData(i);
  3463. loadMapLegendData(i);
  3464. _mapUpdateNeeded = true;
  3465. }
  3466. void LoLEngine::automapBackButton() {
  3467. int i = _currentMapLevel - 1;
  3468. while (!(_hasTempDataFlags & (1 << (i - 1))))
  3469. i = (i - 1) & 0x1F;
  3470. if (i == _currentMapLevel)
  3471. return;
  3472. for (int l = 0; l < 11; l++)
  3473. _defaultLegendData[l].enable = false;
  3474. _currentMapLevel = i;
  3475. loadLevelWallData(i, false);
  3476. restoreBlockTempData(i);
  3477. loadMapLegendData(i);
  3478. _mapUpdateNeeded = true;
  3479. }
  3480. void LoLEngine::redrawMapCursor() {
  3481. int sx = mapGetStartPosX();
  3482. int sy = mapGetStartPosY();
  3483. if (_currentLevel != _currentMapLevel)
  3484. return;
  3485. int cx = _automapTopLeftX + (((_currentBlock - sx) % 32) * 7);
  3486. int cy = _automapTopLeftY + (((_currentBlock - (sy << 5)) / 32) * 6);
  3487. if (_flags.use16ColorMode) {
  3488. _screen->drawShape(0, _automapShapes[48 + _currentDirection], cx - 3, cy - 2, 0, 0);
  3489. } else {
  3490. _screen->fillRect(0, 0, 16, 16, 0, 2);
  3491. _screen->drawShape(2, _automapShapes[48 + _currentDirection], 0, 0, 0, 0);
  3492. _screen->copyRegion(cx, cy, cx, cy, 16, 16, 2, 0);
  3493. _screen->copyBlockAndApplyOverlay(2, 0, 0, 0, cx - 3, cy - 2, 16, 16, 0, _mapCursorOverlay);
  3494. _mapCursorOverlay[24] = _mapCursorOverlay[1];
  3495. for (int i = 1; i < 24; i++)
  3496. _mapCursorOverlay[i] = _mapCursorOverlay[i + 1];
  3497. }
  3498. _screen->updateScreen();
  3499. }
  3500. void LoLEngine::drawMapBlockWall(uint16 block, uint8 wall, int x, int y, int direction) {
  3501. if (((1 << direction) & _levelBlockProperties[block].flags) || ((_wllAutomapData[wall] & 0x1F) != 13))
  3502. return;
  3503. int cp = _screen->_curPage;
  3504. _screen->copyBlockAndApplyOverlay(cp, x + _mapCoords[0][direction], y + _mapCoords[1][direction], cp, x + _mapCoords[0][direction], y + _mapCoords[1][direction], _mapCoords[2][direction], _mapCoords[3][direction], 0, _mapOverlay);
  3505. _screen->copyBlockAndApplyOverlay(cp, x + _mapCoords[4][direction], y + _mapCoords[5][direction], cp, x + _mapCoords[4][direction], y + _mapCoords[5][direction], _mapCoords[8][direction], _mapCoords[9][direction], 0, _mapOverlay);
  3506. _screen->copyBlockAndApplyOverlay(cp, x + _mapCoords[6][direction], y + _mapCoords[7][direction], cp, x + _mapCoords[6][direction], y + _mapCoords[7][direction], _mapCoords[8][direction], _mapCoords[9][direction], 0, _mapOverlay);
  3507. }
  3508. void LoLEngine::drawMapShape(uint8 wall, int x, int y, int direction) {
  3509. int l = _wllAutomapData[wall] & 0x1F;
  3510. if (l == 0x1F)
  3511. return;
  3512. _screen->drawShape(_screen->_curPage, _automapShapes[(l << 2) + direction], x + _mapCoords[10][direction] - 2, y + _mapCoords[11][direction] - 2, 0, 0);
  3513. mapIncludeLegendData(l);
  3514. }
  3515. int LoLEngine::mapGetStartPosX() {
  3516. int c = 0;
  3517. int a = 32;
  3518. do {
  3519. for (a = 0; a < 32; a++) {
  3520. if (_levelBlockProperties[(a << 5) + c].flags)
  3521. break;
  3522. }
  3523. if (a == 32)
  3524. c++;
  3525. } while (c < 32 && a == 32);
  3526. int d = 31;
  3527. a = 32;
  3528. do {
  3529. for (a = 0; a < 32; a++) {
  3530. if (_levelBlockProperties[(a << 5) + d].flags)
  3531. break;
  3532. }
  3533. if (a == 32)
  3534. d--;
  3535. } while (d > 0 && a == 32);
  3536. _automapTopLeftX = (d > c) ? ((32 - (d - c)) >> 1) * 7 + 5 : 5;
  3537. return (d > c) ? c : 0;
  3538. }
  3539. int LoLEngine::mapGetStartPosY() {
  3540. int c = 0;
  3541. int a = 32;
  3542. do {
  3543. for (a = 0; a < 32; a++) {
  3544. if (_levelBlockProperties[(c << 5) + a].flags)
  3545. break;
  3546. }
  3547. if (a == 32)
  3548. c++;
  3549. } while (c < 32 && a == 32);
  3550. int d = 31;
  3551. a = 32;
  3552. do {
  3553. for (a = 0; a < 32; a++) {
  3554. if (_levelBlockProperties[(d << 5) + a].flags)
  3555. break;
  3556. }
  3557. if (a == 32)
  3558. d--;
  3559. } while (d > 0 && a == 32);
  3560. _automapTopLeftY = (d > c) ? ((32 - (d - c)) >> 1) * 6 + 4 : 4;
  3561. return (d > c) ? c : 0;
  3562. }
  3563. void LoLEngine::mapIncludeLegendData(int type) {
  3564. type &= 0x7F;
  3565. for (int i = 0; i < 11; i++) {
  3566. if (_defaultLegendData[i].shapeIndex != type)
  3567. continue;
  3568. _defaultLegendData[i].enable = true;
  3569. return;
  3570. }
  3571. }
  3572. void LoLEngine::printMapText(uint16 stringId, int x, int y) {
  3573. int cp = _screen->setCurPage(2);
  3574. if (_flags.use16ColorMode)
  3575. _screen->printText(getLangString(stringId), x & ~3, y & ~7, 1, 0);
  3576. else
  3577. _screen->printText(getLangString(stringId), x, y, 239, 0);
  3578. _screen->setCurPage(cp);
  3579. }
  3580. void LoLEngine::printMapExitButtonText() {
  3581. int cp = _screen->setCurPage(2);
  3582. Screen::FontId of = _screen->setFont(Screen::FID_9_FNT);
  3583. _screen->fprintString("%s", 295, 182, _flags.use16ColorMode ? 0xBB : 172, 0, 5, getLangString(0x4033));
  3584. _screen->setFont(of);
  3585. _screen->setCurPage(cp);
  3586. }
  3587. } // End of namespace Kyra
  3588. #endif // ENABLE_LOL