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 
23 #ifdef ENABLE_LOL
24 
25 #include "kyra/engine/lol.h"
26 #include "kyra/graphics/screen_lol.h"
27 #include "kyra/resource/resource.h"
28 #include "kyra/engine/timer.h"
29 
30 #include "common/endian.h"
31 #include "common/system.h"
32 
33 namespace Kyra {
34 
loadLevel(int index)35 void LoLEngine::loadLevel(int index) {
36 	_flagsTable[73] |= 0x08;
37 	setMouseCursorToIcon(0x85);
38 	_nextScriptFunc = 0;
39 
40 	snd_stopMusic();
41 
42 	stopPortraitSpeechAnim();
43 
44 	for (int i = 0; i < 400; i++) {
45 		delete[] _levelDecorationShapes[i];
46 		_levelDecorationShapes[i] = 0;
47 	}
48 	_emc->unload(&_scriptData);
49 
50 	resetItems(1);
51 	disableMonsters();
52 	resetBlockProperties();
53 
54 	releaseMonsterShapes(0);
55 	releaseMonsterShapes(1);
56 
57 	for (int i = 0x50; i < 0x53; i++)
58 		_timer->disable(i);
59 
60 	_currentLevel = index;
61 	_updateFlags = 0;
62 
63 	setDefaultButtonState();
64 
65 	loadTalkFile(index);
66 
67 	loadLevelWallData(index, true);
68 	_loadLevelFlag = 1;
69 
70 	Common::String filename = Common::String::format("LEVEL%d.INI", index);
71 
72 	int f = _hasTempDataFlags & (1 << (index - 1));
73 
74 	runInitScript(filename.c_str(), f ? 0 : 1);
75 
76 	if (f)
77 		restoreBlockTempData(index);
78 
79 	filename = Common::String::format("LEVEL%d.INF", index);
80 	runInfScript(filename.c_str());
81 
82 	addLevelItems();
83 	deleteMonstersFromBlock(_currentBlock);
84 
85 	if (!_flags.use16ColorMode)
86 		_screen->generateGrayOverlay(_screen->getPalette(0), _screen->_grayOverlay, 32, 16, 0, 0, 128, true);
87 
88 	_sceneDefaultUpdate = 0;
89 	if (_screen->_fadeFlag == 3)
90 		_screen->fadeToBlack(10);
91 
92 	gui_drawPlayField();
93 
94 	setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect);
95 	setMouseCursorToItemInHand();
96 
97 	if (_flags.use16ColorMode)
98 		_screen->fadeToPalette1(10);
99 
100 	snd_playTrack(_curMusicTheme);
101 }
102 
addLevelItems()103 void LoLEngine::addLevelItems() {
104 	for (int i = 0; i < 400; i++) {
105 		if (_itemsInPlay[i].level != _currentLevel)
106 			continue;
107 
108 		assignBlockItem(&_levelBlockProperties[_itemsInPlay[i].block], i);
109 
110 		_levelBlockProperties[_itemsInPlay[i].block].direction = 5;
111 		_itemsInPlay[i].nextDrawObject = 0;
112 	}
113 }
114 
assignBlockItem(LevelBlockProperty * l,uint16 item)115 void LoLEngine::assignBlockItem(LevelBlockProperty *l, uint16 item) {
116 	uint16 *index = &l->assignedObjects;
117 	LoLObject *tmp = 0;
118 
119 	while (*index & 0x8000) {
120 		tmp = findObject(*index);
121 		index = &tmp->nextAssignedObject;
122 	}
123 
124 	tmp = findObject(item);
125 	((LoLItem *)tmp)->level = -1;
126 
127 	uint16 ix = *index;
128 
129 	if (ix == item)
130 		return;
131 
132 	*index = item;
133 	index = &tmp->nextAssignedObject;
134 
135 	while (*index) {
136 		tmp = findObject(*index);
137 		index = &tmp->nextAssignedObject;
138 	}
139 
140 	*index = ix;
141 }
142 
loadLevelWallData(int index,bool mapShapes)143 void LoLEngine::loadLevelWallData(int index, bool mapShapes) {
144 	Common::String filename = Common::String::format("LEVEL%d.WLL", index);
145 
146 	uint32 size;
147 	uint8 *file = _res->fileData(filename.c_str(), &size);
148 
149 	uint16 c = READ_LE_UINT16(file);
150 	loadLevelShpDat(_levelShpList[c], _levelDatList[c], false);
151 
152 	uint8 *d = file + 2;
153 	size = (size - 2) / 12;
154 	for (uint32 i = 0; i < size; i++) {
155 		c = READ_LE_UINT16(d);
156 		d += 2;
157 		_wllVmpMap[c] = *d;
158 		d += 2;
159 
160 		if (mapShapes) {
161 			int16 sh = (int16) READ_LE_UINT16(d);
162 			if (sh > 0)
163 				_wllShapeMap[c] = assignLevelDecorationShapes(sh);
164 			else
165 				_wllShapeMap[c] = *d;
166 		}
167 		d += 2;
168 		_specialWallTypes[c] = *d;
169 		d += 2;
170 		_wllWallFlags[c] = *d;
171 		d += 2;
172 		_wllAutomapData[c] = *d;
173 		d += 2;
174 	}
175 
176 	delete[] file;
177 
178 	delete _lvlShpFileHandle;
179 	_lvlShpFileHandle = 0;
180 }
181 
assignLevelDecorationShapes(int index)182 int LoLEngine::assignLevelDecorationShapes(int index) {
183 	uint16 *p1 = (uint16 *)_tempBuffer5120;
184 	uint16 *p2 = (uint16 *)(_tempBuffer5120 + 4000);
185 
186 	uint16 r = p2[index];
187 	if (r)
188 		return r;
189 
190 	uint16 o = _mappedDecorationsCount++;
191 
192 	memcpy(&_levelDecorationProperties[o], &_levelDecorationData[index], sizeof(LevelDecorationProperty));
193 
194 	for (int i = 0; i < 10; i++) {
195 		uint16 t = _levelDecorationProperties[o].shapeIndex[i];
196 		if (t == 0xFFFF)
197 			continue;
198 
199 		uint16 pv = p1[t];
200 		if (pv) {
201 			_levelDecorationProperties[o].shapeIndex[i] = pv;
202 		} else {
203 			releaseDecorations(_lvlShapeIndex, 1);
204 			_levelDecorationShapes[_lvlShapeIndex] = getLevelDecorationShapes(t);
205 			p1[t] = _lvlShapeIndex;
206 			_levelDecorationProperties[o].shapeIndex[i] = _lvlShapeIndex++;
207 		}
208 	}
209 
210 	p2[index] = o;
211 	if (_levelDecorationProperties[o].next)
212 		_levelDecorationProperties[o].next = assignLevelDecorationShapes(_levelDecorationProperties[o].next);
213 
214 	return o;
215 }
216 
getLevelDecorationShapes(int shapeIndex)217 uint8 *LoLEngine::getLevelDecorationShapes(int shapeIndex) {
218 	if (_decorationCount <= shapeIndex)
219 		return 0;
220 
221 	_lvlShpFileHandle->seek(shapeIndex * 4 + 2, SEEK_SET);
222 	uint32 offs = _lvlShpFileHandle->readUint32LE() + 2;
223 	_lvlShpFileHandle->seek(offs, SEEK_SET);
224 
225 	uint8 tmp[16];
226 	_lvlShpFileHandle->read(tmp, 16);
227 	uint16 size = _screen->getShapeSize(tmp);
228 
229 	_lvlShpFileHandle->seek(offs, SEEK_SET);
230 	uint8 *res = new uint8[size];
231 	_lvlShpFileHandle->read(res, size);
232 
233 	return res;
234 }
235 
releaseDecorations(int first,int num)236 void LoLEngine::releaseDecorations(int first, int num) {
237 	for (int i = first; i < (first + num); i++) {
238 		delete[] _levelDecorationShapes[i];
239 		_levelDecorationShapes[i] = 0;
240 	}
241 }
242 
loadBlockProperties(const char * cmzFile)243 void LoLEngine::loadBlockProperties(const char *cmzFile) {
244 	memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty));
245 	_screen->loadBitmap(cmzFile, 2, 2, 0);
246 	const uint8 *h = _screen->getCPagePtr(2);
247 	uint16 len = READ_LE_UINT16(&h[4]);
248 	const uint8 *p = h + 6;
249 
250 	for (int i = 0; i < 1024; i++) {
251 		for (int ii = 0; ii < 4; ii++)
252 			_levelBlockProperties[i].walls[ii] = p[i * len + ii];
253 
254 		_levelBlockProperties[i].direction = 5;
255 
256 		if (_wllAutomapData[_levelBlockProperties[i].walls[0]] == 17) {
257 			_levelBlockProperties[i].flags &= 0xEF;
258 			_levelBlockProperties[i].flags |= 0x20;
259 		}
260 	}
261 }
262 
getBlockFileData(int levelIndex)263 const uint8 *LoLEngine::getBlockFileData(int levelIndex) {
264 	_screen->loadBitmap(Common::String::format("LEVEL%d.CMZ", levelIndex).c_str(), 15, 15, 0);
265 	return _screen->getCPagePtr(14);
266 }
267 
loadLevelShpDat(const char * shpFile,const char * datFile,bool flag)268 void LoLEngine::loadLevelShpDat(const char *shpFile, const char *datFile, bool flag) {
269 	memset(_tempBuffer5120, 0, 5120);
270 
271 	_lvlShpFileHandle = _res->createReadStream(shpFile);
272 	_decorationCount = _lvlShpFileHandle->readUint16LE();
273 
274 	Common::SeekableReadStream *s = _res->createReadStream(datFile);
275 
276 	_levelDecorationDataSize = s->readUint16LE();
277 	delete[] _levelDecorationData;
278 	_levelDecorationData = new LevelDecorationProperty[_levelDecorationDataSize];
279 	for (int i = 0; i < _levelDecorationDataSize; i++) {
280 		LevelDecorationProperty *l = &_levelDecorationData[i];
281 		for (int ii = 0; ii < 10; ii++)
282 			l->shapeIndex[ii] = s->readUint16LE();
283 		for (int ii = 0; ii < 10; ii++)
284 			l->scaleFlag[ii] = s->readByte();
285 		for (int ii = 0; ii < 10; ii++)
286 			l->shapeX[ii] = s->readSint16LE();
287 		for (int ii = 0; ii < 10; ii++)
288 			l->shapeY[ii] = s->readSint16LE();
289 		l->next = s->readByte();
290 		l->flags = s->readByte();
291 	}
292 
293 	delete s;
294 
295 	if (!flag) {
296 		_mappedDecorationsCount = 1;
297 		_lvlShapeIndex = 1;
298 	}
299 }
300 
loadLevelGraphics(const char * file,int specialColor,int weight,int vcnLen,int vmpLen,const char * palFile)301 void LoLEngine::loadLevelGraphics(const char *file, int specialColor, int weight, int vcnLen, int vmpLen, const char *palFile) {
302 	if (file) {
303 		_lastSpecialColor = specialColor;
304 		_lastSpecialColorWeight = weight;
305 		strcpy(_lastBlockDataFile, file);
306 		if (palFile)
307 			_lastOverridePalFile = palFile;
308 		else
309 			_lastOverridePalFile.clear();
310 	}
311 
312 	if (_flags.use16ColorMode) {
313 		if (_lastSpecialColor == 1)
314 			_lastSpecialColor = 0x44;
315 		else if (_lastSpecialColor == 0x66)
316 			_lastSpecialColor = scumm_stricmp(_lastBlockDataFile, "YVEL2") ? 0xCC : 0x44;
317 		else if (_lastSpecialColor == 0x6B)
318 			_lastSpecialColor = 0xCC;
319 		else
320 			_lastSpecialColor = 0x44;
321 	}
322 
323 	Common::String fname;
324 	const uint8 *v = 0;
325 	int tlen = 0;
326 
327 	if (_flags.use16ColorMode) {
328 		fname = Common::String::format("%s.VCF", _lastBlockDataFile);
329 		_screen->loadBitmap(fname.c_str(), 3, 3, 0);
330 		v = _screen->getCPagePtr(2);
331 		tlen = READ_LE_UINT16(v) << 5;
332 		v += 2;
333 
334 		delete[] _vcfBlocks;
335 		_vcfBlocks = new uint8[tlen];
336 
337 		memcpy(_vcfBlocks, v, tlen);
338 	}
339 
340 	fname = Common::String::format("%s.VCN", _lastBlockDataFile);
341 	_screen->loadBitmap(fname.c_str(), 3, 3, 0);
342 	v = _screen->getCPagePtr(2);
343 	tlen = READ_LE_UINT16(v);
344 	v += 2;
345 
346 	if (vcnLen == -1)
347 		vcnLen = tlen << 5;
348 
349 	delete[] _vcnBlocks;
350 	_vcnBlocks = new uint8[vcnLen];
351 
352 	if (!_flags.use16ColorMode) {
353 		delete[] _vcnShift;
354 		_vcnShift = new uint8[tlen];
355 
356 		memcpy(_vcnShift, v, tlen);
357 		v += tlen;
358 
359 		memcpy(_vcnColTable, v, 128);
360 		v += 128;
361 
362 		if (!_lastOverridePalFile.empty()) {
363 			_res->loadFileToBuf(_lastOverridePalFile.c_str(), _screen->getPalette(0).getData(), 384);
364 		} else {
365 			_screen->getPalette(0).copy(v, 0, 128);
366 		}
367 
368 		v += 384;
369 	}
370 
371 	if (_currentLevel == 11) {
372 		if (_flags.use16ColorMode) {
373 			_screen->loadPalette("LOLICE.NOL", _screen->getPalette(2));
374 
375 		} else {
376 			_screen->loadPalette("SWAMPICE.COL", _screen->getPalette(2));
377 			_screen->getPalette(2).copy(_screen->getPalette(0), 128);
378 		}
379 
380 		if (_flagsTable[52] & 0x04) {
381 			uint8 *pal0 = _screen->getPalette(0).getData();
382 			uint8 *pal2 = _screen->getPalette(2).getData();
383 			for (int i = 1; i < _screen->getPalette(0).getNumColors() * 3; i++)
384 				SWAP(pal0[i], pal2[i]);
385 		}
386 	}
387 
388 	memcpy(_vcnBlocks, v, vcnLen);
389 	v += vcnLen;
390 
391 	fname = Common::String::format("%s.VMP", _lastBlockDataFile);
392 	_screen->loadBitmap(fname.c_str(), 3, 3, 0);
393 	v = _screen->getCPagePtr(2);
394 
395 	if (vmpLen == -1)
396 		vmpLen = READ_LE_UINT16(v);
397 	v += 2;
398 
399 	delete[] _vmpPtr;
400 	_vmpPtr = new uint16[vmpLen];
401 
402 	for (int i = 0; i < vmpLen; i++)
403 		_vmpPtr[i] = READ_LE_UINT16(&v[i << 1]);
404 
405 	Palette tpal(256);
406 	if (_flags.use16ColorMode) {
407 		uint8 *dst = tpal.getData();
408 		_res->loadFileToBuf("LOL.NOL", dst, 48);
409 		for (int i = 1; i < 16; i++) {
410 			int s = ((i << 4) | i) * 3;
411 			SWAP(dst[s], dst[i * 3]);
412 			SWAP(dst[s + 1], dst[i * 3 + 1]);
413 			SWAP(dst[s + 2], dst[i * 3 + 2]);
414 		}
415 	} else {
416 		tpal.copy(_screen->getPalette(0));
417 	}
418 
419 	for (int i = 0; i < 7; i++) {
420 		weight = 100 - (i * _lastSpecialColorWeight);
421 		weight = (weight > 0) ? (weight * 255) / 100 : 0;
422 		_screen->generateOverlay(tpal, _screen->getLevelOverlay(i), _lastSpecialColor, weight);
423 
424 		int l = _flags.use16ColorMode ? 256 : 128;
425 		uint8 *levelOverlay = _screen->getLevelOverlay(i);
426 		for (int ii = 0; ii < l; ii++) {
427 			if (levelOverlay[ii] == 255)
428 				levelOverlay[ii] = 0;
429 		}
430 
431 		for (int ii = l; ii < 256; ii++)
432 			levelOverlay[ii] = ii & 0xFF;
433 	}
434 
435 	uint8 *levelOverlay = _screen->getLevelOverlay(7);
436 	for (int i = 0; i < 256; i++)
437 		levelOverlay[i] = i & 0xFF;
438 
439 	if (_flags.use16ColorMode) {
440 		_screen->getLevelOverlay(6)[0xEE] = 0xEE;
441 		if (_lastSpecialColor == 0x44)
442 			_screen->getLevelOverlay(5)[0xEE] = 0xEE;
443 
444 		for (int i = 0; i < 7; i++)
445 			memcpy(_screen->getLevelOverlay(i), _screen->getLevelOverlay(i + 1), 256);
446 
447 		_screen->loadPalette("LOL.NOL", _screen->getPalette(0));
448 
449 		for (int i = 0; i < 8; i++) {
450 			uint8 *pl = _screen->getLevelOverlay(7 - i);
451 			for (int ii = 0; ii < 16; ii++)
452 				_vcnColTable[(i << 4) + ii] = pl[(ii << 4) | ii];
453 		}
454 	}
455 
456 	_loadSuppFilesFlag = 0;
457 	generateBrightnessPalette(_screen->getPalette(0), _screen->getPalette(1), _brightness, _lampEffect);
458 
459 	if (_flags.isTalkie) {
460 		Common::SeekableReadStream *s = _res->createReadStream(Common::String::format("LEVEL%.02d.TLC", _currentLevel));
461 		s->read(_transparencyTable1, 256);
462 		s->read(_transparencyTable2, 5120);
463 		delete s;
464 	} else {
465 		createTransparencyTables();
466 	}
467 
468 	_loadSuppFilesFlag = 1;
469 }
470 
resetItems(int flag)471 void LoLEngine::resetItems(int flag) {
472 	for (int i = 0; i < 1024; i++) {
473 		_levelBlockProperties[i].direction = 5;
474 		uint16 id = _levelBlockProperties[i].assignedObjects;
475 		LoLObject *r = 0;
476 
477 		while (id & 0x8000) {
478 			r = findObject(id);
479 			id = r->nextAssignedObject;
480 		}
481 
482 		if (!id)
483 			continue;
484 
485 		LoLItem *it = &_itemsInPlay[id];
486 		it->level = _currentLevel;
487 		it->block = i;
488 		if (r)
489 			r->nextAssignedObject = 0;
490 	}
491 
492 	if (flag)
493 		memset(_flyingObjects, 0, 8 * sizeof(FlyingObject));
494 }
495 
disableMonsters()496 void LoLEngine::disableMonsters() {
497 	memset(_monsters, 0, 30 * sizeof(LoLMonster));
498 	for (int i = 0; i < 30; i++)
499 		_monsters[i].mode = 0x10;
500 }
501 
resetBlockProperties()502 void LoLEngine::resetBlockProperties() {
503 	for (int i = 0; i < 1024; i++) {
504 		LevelBlockProperty *l = &_levelBlockProperties[i];
505 		if (l->flags & 0x10) {
506 			l->flags &= 0xEF;
507 			if (testWallInvisibility(i, 0) && testWallInvisibility(i, 1))
508 				l->flags |= 0x40;
509 		} else {
510 			if (l->flags & 0x40)
511 				l->flags &= 0xBF;
512 			else if (l->flags & 0x80)
513 				l->flags &= 0x7F;
514 		}
515 	}
516 }
517 
testWallFlag(int block,int direction,int flag)518 bool LoLEngine::testWallFlag(int block, int direction, int flag) {
519 	if (_levelBlockProperties[block].flags & 0x10)
520 		return true;
521 
522 	if (direction != -1)
523 		return (_wllWallFlags[_levelBlockProperties[block].walls[direction ^ 2]] & flag) ? true : false;
524 
525 	for (int i = 0; i < 4; i++) {
526 		if (_wllWallFlags[_levelBlockProperties[block].walls[i]] & flag)
527 			return true;
528 	}
529 
530 	return false;
531 }
532 
testWallInvisibility(int block,int direction)533 bool LoLEngine::testWallInvisibility(int block, int direction) {
534 	uint8 w = _levelBlockProperties[block].walls[direction];
535 	if (_wllVmpMap[w] || _wllShapeMap[w] || _levelBlockProperties[block].flags & 0x80)
536 		return false;
537 	return true;
538 }
539 
resetLampStatus()540 void LoLEngine::resetLampStatus() {
541 	_flagsTable[31] |= 0x04;
542 	_lampEffect = -1;
543 	updateLampStatus();
544 }
545 
setLampMode(bool lampOn)546 void LoLEngine::setLampMode(bool lampOn) {
547 	_flagsTable[31] &= 0xFB;
548 	if (!(_flagsTable[31] & 0x08) || !lampOn)
549 		return;
550 
551 	_screen->drawShape(0, _gameShapes[_flags.isTalkie ? 43 : 41], 291, 56, 0, 0);
552 	_lampEffect = 8;
553 }
554 
updateLampStatus()555 void LoLEngine::updateLampStatus() {
556 	int8 newLampEffect = 0;
557 	uint8 tmpOilStatus = 0;
558 
559 	if ((_updateFlags & 4) || !(_flagsTable[31] & 0x08))
560 		return;
561 
562 	if (!_brightness || !_lampOilStatus) {
563 		newLampEffect = 8;
564 		if (newLampEffect != _lampEffect && _screen->_fadeFlag == 0)
565 			setPaletteBrightness(_screen->getPalette(0), _brightness, newLampEffect);
566 	} else {
567 		tmpOilStatus = (_lampOilStatus < 100) ? _lampOilStatus : 100;
568 		newLampEffect = (3 - ((tmpOilStatus - 1) / 25)) << 1;
569 
570 		if (_lampEffect == -1) {
571 			if (_screen->_fadeFlag == 0)
572 				setPaletteBrightness(_screen->getPalette(0), _brightness, newLampEffect);
573 			_lampStatusTimer = _system->getMillis() + (10 + rollDice(1, 30)) * _tickLength;
574 		} else {
575 			if ((_lampEffect & 0xFE) == (newLampEffect & 0xFE)) {
576 				if (_system->getMillis() <= _lampStatusTimer) {
577 					newLampEffect = _lampEffect;
578 				} else {
579 					newLampEffect = _lampEffect ^ 1;
580 					_lampStatusTimer = _system->getMillis() + (10 + rollDice(1, 30)) * _tickLength;
581 				}
582 			} else {
583 				if (_screen->_fadeFlag == 0)
584 					setPaletteBrightness(_screen->getPalette(0), _brightness, newLampEffect);
585 			}
586 		}
587 	}
588 
589 	if (newLampEffect == _lampEffect)
590 		return;
591 
592 	_screen->hideMouse();
593 
594 	_screen->drawShape(_screen->_curPage, _gameShapes[(_flags.isTalkie ? 35 : 33) + newLampEffect], 291, 56, 0, 0);
595 	_screen->showMouse();
596 
597 	_lampEffect = newLampEffect;
598 }
599 
updateCompass()600 void LoLEngine::updateCompass() {
601 	if (!(_flagsTable[31] & 0x40) || (_updateFlags & 4))
602 		return;
603 
604 	if (_compassDirection == -1) {
605 		_compassStep = 0;
606 		gui_drawCompass();
607 		return;
608 	}
609 
610 	if (_compassTimer >= _system->getMillis())
611 		return;
612 
613 	if ((_currentDirection << 6) == _compassDirection && (!_compassStep))
614 		return;
615 
616 	_compassTimer = _system->getMillis() + 3 * _tickLength;
617 	int dir = _compassStep >= 0 ? 1 : -1;
618 	if (_compassStep)
619 		_compassStep -= (((ABS(_compassStep) >> 4) + 2) * dir);
620 
621 	int16 diff = _compassBroken ? (int8(_rnd.getRandomNumber(255)) - _compassDirection) : (_currentDirection << 6) - _compassDirection;
622 	if (diff <= -128)
623 		diff += 256;
624 	if (diff >= 128)
625 		diff -= 256;
626 
627 	diff >>= 2;
628 	_compassStep += diff;
629 	_compassStep = CLIP(_compassStep, -24, 24);
630 	_compassDirection += _compassStep;
631 
632 	if (_compassDirection < 0)
633 		_compassDirection += 256;
634 	if (_compassDirection > 255)
635 		_compassDirection -= 256;
636 
637 	if (((((_compassDirection + 3) & 0xFD) >> 6) == _currentDirection) && (_compassStep < 2) && (ABS(diff) < 4)) {
638 		_compassDirection = _currentDirection << 6;
639 		_compassStep = 0;
640 	}
641 
642 	gui_drawCompass();
643 }
644 
moveParty(uint16 direction,int unk1,int unk2,int buttonShape)645 void LoLEngine::moveParty(uint16 direction, int unk1, int unk2, int buttonShape) {
646 	if (buttonShape)
647 		gui_toggleButtonDisplayMode(buttonShape, 1);
648 
649 	uint16 opos = _currentBlock;
650 	uint16 npos = calcNewBlockPosition(_currentBlock, direction);
651 
652 	if (!checkBlockPassability(npos, direction)) {
653 		notifyBlockNotPassable(unk2 ? 0 : 1);
654 		gui_toggleButtonDisplayMode(buttonShape, 0);
655 		return;
656 	}
657 
658 	_scriptDirection = direction;
659 	_currentBlock = npos;
660 	_sceneDefaultUpdate = 1;
661 
662 	calcCoordinates(_partyPosX, _partyPosY, _currentBlock, 0x80, 0x80);
663 	_flagsTable[73] &= 0xFD;
664 
665 	runLevelScript(opos, 4);
666 	runLevelScript(npos, 1);
667 
668 	if (!(_flagsTable[73] & 0x02)) {
669 		initTextFading(2, 0);
670 
671 		if (_sceneDefaultUpdate) {
672 			switch (unk2) {
673 			case 0:
674 				movePartySmoothScrollUp(2);
675 				break;
676 			case 1:
677 				movePartySmoothScrollDown(2);
678 				break;
679 			case 2:
680 				movePartySmoothScrollLeft(1);
681 				break;
682 			case 3:
683 				movePartySmoothScrollRight(1);
684 				break;
685 			default:
686 				break;
687 			}
688 		} else {
689 			gui_drawScene(0);
690 		}
691 
692 		gui_toggleButtonDisplayMode(buttonShape, 0);
693 
694 		if (npos == _currentBlock) {
695 			runLevelScript(opos, 8);
696 			runLevelScript(npos, 2);
697 
698 			if (_levelBlockProperties[npos].walls[0] == 0x1A)
699 				memset(_levelBlockProperties[npos].walls, 0, 4);
700 		}
701 	}
702 
703 	updateAutoMap(_currentBlock);
704 }
705 
calcBlockIndex(uint16 x,uint16 y)706 uint16 LoLEngine::calcBlockIndex(uint16 x, uint16 y) {
707 	return (((y & 0xFF00) >> 3) | (x >> 8)) & 0x3FF;
708 }
709 
calcCoordinates(uint16 & x,uint16 & y,int block,uint16 xOffs,uint16 yOffs)710 void LoLEngine::calcCoordinates(uint16 &x, uint16 &y, int block, uint16 xOffs, uint16 yOffs) {
711 	x = (block & 0x1F) << 8 | xOffs;
712 	y = ((block & 0xFFE0) << 3) | yOffs;
713 }
714 
calcCoordinatesForSingleCharacter(int charNum,uint16 & x,uint16 & y)715 void LoLEngine::calcCoordinatesForSingleCharacter(int charNum, uint16 &x, uint16 &y) {
716 	static const uint8 xOffsets[] = {  0x80, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x40, 0x80, 0xC0 };
717 	int c = countActiveCharacters();
718 	if (!c)
719 		return;
720 
721 	c = (c - 1) * 3 + charNum;
722 
723 	x = xOffsets[c];
724 	y = 0x80;
725 
726 	calcCoordinatesAddDirectionOffset(x, y, _currentDirection);
727 
728 	x |= (_partyPosX & 0xFF00);
729 	y |= (_partyPosY & 0xFF00);
730 }
731 
calcCoordinatesAddDirectionOffset(uint16 & x,uint16 & y,int direction)732 void LoLEngine::calcCoordinatesAddDirectionOffset(uint16 &x, uint16 &y, int direction) {
733 	if (!direction)
734 		return;
735 
736 	int tx = x;
737 	int ty = y;
738 
739 	if (direction & 1)
740 		SWAP(tx, ty);
741 
742 	if (direction != 1)
743 		ty = (ty - 256) * -1;
744 
745 	if (direction != 3) {
746 		tx = (tx - 256) * -1;
747 	}
748 
749 	x = tx;
750 	y = ty;
751 }
752 
checkBlockPassability(uint16 block,uint16 direction)753 bool LoLEngine::checkBlockPassability(uint16 block, uint16 direction) {
754 	if (testWallFlag(block, direction, 1))
755 		return false;
756 
757 	uint16 d = _levelBlockProperties[block].assignedObjects;
758 
759 	while (d) {
760 		if (d & 0x8000)
761 			return false;
762 		d = findObject(d)->nextAssignedObject;
763 	}
764 
765 	return true;
766 }
767 
notifyBlockNotPassable(int scrollFlag)768 void LoLEngine::notifyBlockNotPassable(int scrollFlag) {
769 	if (scrollFlag)
770 		movePartySmoothScrollBlocked(2);
771 
772 	snd_stopSpeech(true);
773 	_txt->printMessage(0x8002, "%s", getLangString(0x403F));
774 	snd_playSoundEffect(19, -1);
775 }
776 
clickedDoorSwitch(uint16 block,uint16 direction)777 int LoLEngine::clickedDoorSwitch(uint16 block, uint16 direction) {
778 	uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]];
779 	if (!clickedShape(v))
780 		return 0;
781 
782 	snd_playSoundEffect(78, -1);
783 	_blockDoor = 0;
784 	runLevelScript(block, 0x40);
785 
786 	if (!_blockDoor) {
787 		delay(15 * _tickLength);
788 		processDoorSwitch(block, 0);
789 	}
790 
791 	return 1;
792 }
793 
clickedNiche(uint16 block,uint16 direction)794 int LoLEngine::clickedNiche(uint16 block, uint16 direction) {
795 	uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]];
796 	if (!clickedShape(v) || !_itemInHand)
797 		return 0;
798 
799 	uint16 x = 0x80;
800 	uint16 y = 0xFF;
801 	calcCoordinatesAddDirectionOffset(x, y, _currentDirection);
802 	calcCoordinates(x, y, block, x, y);
803 	setItemPosition(_itemInHand, x, y, 8, 1);
804 	setHandItem(0);
805 	return 1;
806 }
807 
movePartySmoothScrollBlocked(int speed)808 void LoLEngine::movePartySmoothScrollBlocked(int speed) {
809 	if (!_smoothScrollingEnabled || (_smoothScrollingEnabled && _needSceneRestore))
810 		return;
811 
812 	_screen->backupSceneWindow(_sceneDrawPage2 == 2 ? 2 : 6, 6);
813 
814 	for (int i = 0; i < 2; i++) {
815 		uint32 delayTimer = _system->getMillis() + speed * _tickLength;
816 		_screen->smoothScrollZoomStepTop(6, 2, _scrollXTop[i], _scrollYTop[i]);
817 		_screen->smoothScrollZoomStepBottom(6, 2, _scrollXBottom[i], _scrollYBottom[i]);
818 		_screen->restoreSceneWindow(2, 0);
819 		_screen->updateScreen();
820 		fadeText();
821 		delayUntil(delayTimer);
822 		if (!_smoothScrollModeNormal)
823 			i++;
824 	}
825 
826 	for (int i = 2; i; i--) {
827 		uint32 delayTimer = _system->getMillis() + speed * _tickLength;
828 		_screen->smoothScrollZoomStepTop(6, 2, _scrollXTop[i], _scrollYTop[i]);
829 		_screen->smoothScrollZoomStepBottom(6, 2, _scrollXBottom[i], _scrollYBottom[i]);
830 		_screen->restoreSceneWindow(2, 0);
831 		_screen->updateScreen();
832 		fadeText();
833 		delayUntil(delayTimer);
834 		if (!_smoothScrollModeNormal)
835 			i++;
836 	}
837 
838 	if (_sceneDefaultUpdate != 2) {
839 		_screen->restoreSceneWindow(6, 0);
840 		_screen->updateScreen();
841 	}
842 
843 	updateDrawPage2();
844 }
845 
movePartySmoothScrollUp(int speed)846 void LoLEngine::movePartySmoothScrollUp(int speed) {
847 	if (!_smoothScrollingEnabled || (_smoothScrollingEnabled && _needSceneRestore))
848 		return;
849 
850 	int d = 0;
851 
852 	if (_sceneDrawPage2 == 2) {
853 		d = smoothScrollDrawSpecialGuiShape(6);
854 		gui_drawScene(6);
855 		_screen->backupSceneWindow(6, 12);
856 		_screen->backupSceneWindow(2, 6);
857 	} else {
858 		d = smoothScrollDrawSpecialGuiShape(2);
859 		gui_drawScene(2);
860 		_screen->backupSceneWindow(2, 12);
861 		_screen->backupSceneWindow(6, 6);
862 	}
863 
864 	for (int i = 0; i < 5; i++) {
865 		uint32 delayTimer = _system->getMillis() + speed * _tickLength;
866 		_screen->smoothScrollZoomStepTop(6, 2, _scrollXTop[i], _scrollYTop[i]);
867 		_screen->smoothScrollZoomStepBottom(6, 2, _scrollXBottom[i], _scrollYBottom[i]);
868 
869 		if (d)
870 			_screen->copyGuiShapeToSurface(14, 2);
871 
872 		_screen->restoreSceneWindow(2, 0);
873 		_screen->updateScreen();
874 		fadeText();
875 		delayUntil(delayTimer);
876 		if (!_smoothScrollModeNormal)
877 			i++;
878 	}
879 
880 	if (d)
881 		_screen->copyGuiShapeToSurface(14, 12);
882 
883 	if (_sceneDefaultUpdate != 2) {
884 		_screen->restoreSceneWindow(12, 0);
885 		_screen->updateScreen();
886 	}
887 
888 	updateDrawPage2();
889 }
890 
movePartySmoothScrollDown(int speed)891 void LoLEngine::movePartySmoothScrollDown(int speed) {
892 	if (!_smoothScrollingEnabled)
893 		return;
894 
895 	int d = smoothScrollDrawSpecialGuiShape(2);
896 	gui_drawScene(2);
897 	_screen->backupSceneWindow(2, 6);
898 
899 	for (int i = 4; i >= 0; i--) {
900 		uint32 delayTimer = _system->getMillis() + speed * _tickLength;
901 		_screen->smoothScrollZoomStepTop(6, 2, _scrollXTop[i], _scrollYTop[i]);
902 		_screen->smoothScrollZoomStepBottom(6, 2, _scrollXBottom[i], _scrollYBottom[i]);
903 
904 		if (d)
905 			_screen->copyGuiShapeToSurface(14, 2);
906 
907 		_screen->restoreSceneWindow(2, 0);
908 		_screen->updateScreen();
909 		fadeText();
910 		delayUntil(delayTimer);
911 		if (!_smoothScrollModeNormal)
912 			i++;
913 	}
914 
915 	if (d)
916 		_screen->copyGuiShapeToSurface(14, 12);
917 
918 	if (_sceneDefaultUpdate != 2) {
919 		_screen->restoreSceneWindow(6, 0);
920 		_screen->updateScreen();
921 	}
922 
923 	updateDrawPage2();
924 }
925 
movePartySmoothScrollLeft(int speed)926 void LoLEngine::movePartySmoothScrollLeft(int speed) {
927 	if (!_smoothScrollingEnabled)
928 		return;
929 
930 	speed <<= 1;
931 
932 	gui_drawScene(_sceneDrawPage1);
933 
934 	for (int i = 88, d = 88; i > 22; i -= 22, d += 22) {
935 		uint32 delayTimer = _system->getMillis() + speed * _tickLength;
936 		_screen->smoothScrollHorizontalStep(_sceneDrawPage2, 66, d, i);
937 		_screen->copyRegion(112 + i, 0, 112, 0, d, 120, _sceneDrawPage1, _sceneDrawPage2, Screen::CR_NO_P_CHECK);
938 		_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage2, 0, Screen::CR_NO_P_CHECK);
939 		_screen->updateScreen();
940 		fadeText();
941 		delayUntil(delayTimer);
942 	}
943 
944 	if (_sceneDefaultUpdate != 2) {
945 		_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
946 		_screen->updateScreen();
947 	}
948 
949 	SWAP(_sceneDrawPage1, _sceneDrawPage2);
950 }
951 
movePartySmoothScrollRight(int speed)952 void LoLEngine::movePartySmoothScrollRight(int speed) {
953 	if (!_smoothScrollingEnabled)
954 		return;
955 
956 	speed <<= 1;
957 
958 	gui_drawScene(_sceneDrawPage1);
959 
960 	uint32 delayTimer = _system->getMillis() + speed * _tickLength;
961 	_screen->copyRegion(112, 0, 222, 0, 66, 120, _sceneDrawPage1, _sceneDrawPage2, Screen::CR_NO_P_CHECK);
962 	_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage2, 0, Screen::CR_NO_P_CHECK);
963 	_screen->updateScreen();
964 	fadeText();
965 	delayUntil(delayTimer);
966 
967 	delayTimer = _system->getMillis() + speed * _tickLength;
968 	_screen->smoothScrollHorizontalStep(_sceneDrawPage2, 22, 0, 66);
969 	_screen->copyRegion(112, 0, 200, 0, 88, 120, _sceneDrawPage1, _sceneDrawPage2, Screen::CR_NO_P_CHECK);
970 	_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage2, 0, Screen::CR_NO_P_CHECK);
971 	_screen->updateScreen();
972 	fadeText();
973 	delayUntil(delayTimer);
974 
975 	delayTimer = _system->getMillis() + speed * _tickLength;
976 	_screen->smoothScrollHorizontalStep(_sceneDrawPage2, 44, 0, 22);
977 	_screen->copyRegion(112, 0, 178, 0, 110, 120, _sceneDrawPage1, _sceneDrawPage2, Screen::CR_NO_P_CHECK);
978 	_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage2, 0, Screen::CR_NO_P_CHECK);
979 	_screen->updateScreen();
980 	fadeText();
981 	delayUntil(delayTimer);
982 
983 	if (_sceneDefaultUpdate != 2) {
984 		_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
985 		_screen->updateScreen();
986 	}
987 
988 	SWAP(_sceneDrawPage1, _sceneDrawPage2);
989 }
990 
movePartySmoothScrollTurnLeft(int speed)991 void LoLEngine::movePartySmoothScrollTurnLeft(int speed) {
992 	if (!_smoothScrollingEnabled)
993 		return;
994 
995 	speed <<= 1;
996 
997 	int d = smoothScrollDrawSpecialGuiShape(_sceneDrawPage1);
998 	gui_drawScene(_sceneDrawPage1);
999 	int dp = _sceneDrawPage2 == 2 ? _sceneDrawPage2 : _sceneDrawPage1;
1000 
1001 	uint32 delayTimer = _system->getMillis() + speed * _tickLength;
1002 	_screen->smoothScrollTurnStep1(_sceneDrawPage1, _sceneDrawPage2, dp);
1003 	if (d)
1004 		_screen->copyGuiShapeToSurface(14, dp);
1005 	_screen->restoreSceneWindow(dp, 0);
1006 	_screen->updateScreen();
1007 	fadeText();
1008 	delayUntil(delayTimer);
1009 
1010 	delayTimer = _system->getMillis() + speed * _tickLength;
1011 	_screen->smoothScrollTurnStep2(_sceneDrawPage1, _sceneDrawPage2, dp);
1012 	if (d)
1013 		_screen->copyGuiShapeToSurface(14, dp);
1014 	_screen->restoreSceneWindow(dp, 0);
1015 	_screen->updateScreen();
1016 	fadeText();
1017 	delayUntil(delayTimer);
1018 
1019 	delayTimer = _system->getMillis() + speed * _tickLength;
1020 	_screen->smoothScrollTurnStep3(_sceneDrawPage1, _sceneDrawPage2, dp);
1021 	if (d)
1022 		_screen->copyGuiShapeToSurface(14, dp);
1023 	_screen->restoreSceneWindow(dp, 0);
1024 	_screen->updateScreen();
1025 	fadeText();
1026 	delayUntil(delayTimer);
1027 
1028 	if (_sceneDefaultUpdate != 2) {
1029 		drawSpecialGuiShape(_sceneDrawPage1);
1030 		_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
1031 		_screen->updateScreen();
1032 	}
1033 }
1034 
movePartySmoothScrollTurnRight(int speed)1035 void LoLEngine::movePartySmoothScrollTurnRight(int speed) {
1036 	if (!_smoothScrollingEnabled)
1037 		return;
1038 
1039 	speed <<= 1;
1040 
1041 	int d = smoothScrollDrawSpecialGuiShape(_sceneDrawPage1);
1042 	gui_drawScene(_sceneDrawPage1);
1043 	int dp = _sceneDrawPage2 == 2 ? _sceneDrawPage2 : _sceneDrawPage1;
1044 
1045 	uint32 delayTimer = _system->getMillis() + speed * _tickLength;
1046 	_screen->smoothScrollTurnStep3(_sceneDrawPage2, _sceneDrawPage1, dp);
1047 	if (d)
1048 		_screen->copyGuiShapeToSurface(14, dp);
1049 	_screen->restoreSceneWindow(dp, 0);
1050 	_screen->updateScreen();
1051 	fadeText();
1052 	delayUntil(delayTimer);
1053 
1054 	delayTimer = _system->getMillis() + speed * _tickLength;
1055 	_screen->smoothScrollTurnStep2(_sceneDrawPage2, _sceneDrawPage1, dp);
1056 	if (d)
1057 		_screen->copyGuiShapeToSurface(14, dp);
1058 	_screen->restoreSceneWindow(dp, 0);
1059 	_screen->updateScreen();
1060 	fadeText();
1061 	delayUntil(delayTimer);
1062 
1063 	delayTimer = _system->getMillis() + speed * _tickLength;
1064 	_screen->smoothScrollTurnStep1(_sceneDrawPage2, _sceneDrawPage1, dp);
1065 	if (d)
1066 		_screen->copyGuiShapeToSurface(14, dp);
1067 	_screen->restoreSceneWindow(dp, 0);
1068 	_screen->updateScreen();
1069 	fadeText();
1070 	delayUntil(delayTimer);
1071 
1072 	if (_sceneDefaultUpdate != 2) {
1073 		drawSpecialGuiShape(_sceneDrawPage1);
1074 		_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
1075 		_screen->updateScreen();
1076 	}
1077 }
1078 
pitDropScroll(int numSteps)1079 void LoLEngine::pitDropScroll(int numSteps) {
1080 	_screen->copyRegionSpecial(0, 320, 200, 112, 0, 6, 176, 120, 0, 0, 176, 120, 0);
1081 	uint32 etime = 0;
1082 
1083 	for (int i = 0; i < numSteps; i++) {
1084 		etime = _system->getMillis() + _tickLength;
1085 		int ys = ((30720 / numSteps) * i) >> 8;
1086 		_screen->copyRegionSpecial(6, 176, 120, 0, ys, 0, 320, 200, 112, 0, 176, 120 - ys, 0);
1087 		_screen->copyRegionSpecial(2, 320, 200, 112, 0, 0, 320, 200, 112, 120 - ys, 176, ys, 0);
1088 		_screen->updateScreen();
1089 
1090 		delayUntil(etime);
1091 	}
1092 
1093 	etime = _system->getMillis() + _tickLength;
1094 
1095 	_screen->copyRegionSpecial(2, 320, 200, 112, 0, 0, 320, 200, 112, 0, 176, 120, 0);
1096 	_screen->updateScreen();
1097 
1098 	delayUntil(etime);
1099 
1100 	updateDrawPage2();
1101 }
1102 
shakeScene(int duration,int width,int height,int restore)1103 void LoLEngine::shakeScene(int duration, int width, int height, int restore) {
1104 	_screen->copyRegion(112, 0, 112, 0, 176, 120, 0, 6, Screen::CR_NO_P_CHECK);
1105 	uint32 endTime = _system->getMillis() + duration * _tickLength;
1106 
1107 	while (endTime > _system->getMillis()) {
1108 		uint32 delayTimer = _system->getMillis() + 2 * _tickLength;
1109 
1110 		int s1 = width ? (_rnd.getRandomNumber(255) % (width << 1)) - width : 0;
1111 		int s2 = height ? (_rnd.getRandomNumber(255) % (height << 1)) - height : 0;
1112 
1113 		int x1, y1, x2, y2, w, h;
1114 		if (s1 >= 0) {
1115 			x1 = 112;
1116 			x2 = 112 + s1;
1117 			w = 176 - s1;
1118 		} else {
1119 			x1 = 112 - s1;
1120 			x2 = 112;
1121 			w = 176 + s1;
1122 		}
1123 
1124 		if (s2 >= 0) {
1125 			y1 = 0;
1126 			y2 = s2;
1127 			h = 120 - s2;
1128 		} else {
1129 			y1 = -s2;
1130 			y2 = 0;
1131 			h = 120 + s2;
1132 		}
1133 
1134 		_screen->copyRegion(x1, y1, x2, y2, w, h, 6, 0, Screen::CR_NO_P_CHECK);
1135 		_screen->updateScreen();
1136 
1137 		delayUntil(delayTimer);
1138 	}
1139 
1140 	if (restore) {
1141 		_screen->copyRegion(112, 0, 112, 0, 176, 120, 6, 0, Screen::CR_NO_P_CHECK);
1142 		_screen->updateScreen();
1143 		updateDrawPage2();
1144 	}
1145 }
1146 
processGasExplosion(int soundId)1147 void LoLEngine::processGasExplosion(int soundId) {
1148 	int cp = _screen->setCurPage(2);
1149 	_screen->copyPage(0, 12);
1150 
1151 	static const uint8 sounds[] = { 0x62, 0xA7, 0xA7, 0xA8 };
1152 	snd_playSoundEffect(sounds[soundId], -1);
1153 
1154 	uint16 targetBlock = 0;
1155 	int dist = getSpellTargetBlock(_currentBlock, _currentDirection, 3, targetBlock);
1156 
1157 	uint8 *p1 = _screen->getPalette(1).getData();
1158 	uint8 *p2 = _screen->getPalette(3).getData();
1159 
1160 	if (dist) {
1161 		WSAMovie_v2 *mov = new WSAMovie_v2(this);
1162 		Common::String file = Common::String::format("gasexp%0d.wsa", dist);
1163 		mov->open(file.c_str(), 1, 0);
1164 		if (!mov->opened())
1165 			error("Gas: Unable to load gasexp.wsa");
1166 
1167 		playSpellAnimation(mov, 0, 6, 1, (176 - mov->width()) / 2 + 112, (120 - mov->height()) / 2, 0, 0, 0, 0, false);
1168 
1169 		mov->close();
1170 		delete mov;
1171 
1172 	} else {
1173 		memcpy(p2, p1, 768);
1174 
1175 		for (int i = 1; i < 128; i++)
1176 			p2[i * 3] = 0x3F;
1177 
1178 		uint32 ctime = _system->getMillis();
1179 		while (_screen->timedPaletteFadeStep(_screen->getPalette(0).getData(), p2, _system->getMillis() - ctime, 10))
1180 			updateInput();
1181 
1182 		ctime = _system->getMillis();
1183 		while (_screen->timedPaletteFadeStep(p2, _screen->getPalette(0).getData(), _system->getMillis() - ctime, 50))
1184 			updateInput();
1185 	}
1186 
1187 	_screen->copyPage(12, 2);
1188 	_screen->setCurPage(cp);
1189 
1190 	updateDrawPage2();
1191 	_sceneUpdateRequired = true;
1192 	gui_drawScene(0);
1193 }
1194 
smoothScrollDrawSpecialGuiShape(int pageNum)1195 int LoLEngine::smoothScrollDrawSpecialGuiShape(int pageNum) {
1196 	if (!_specialGuiShape)
1197 		return 0;
1198 
1199 	_screen->clearGuiShapeMemory(pageNum);
1200 	_screen->drawShape(pageNum, _specialGuiShape, _specialGuiShapeX, _specialGuiShapeY, 2, 0);
1201 	_screen->copyGuiShapeFromSceneBackupBuffer(pageNum, 14);
1202 	return 1;
1203 }
1204 
drawScene(int pageNum)1205 void LoLEngine::drawScene(int pageNum) {
1206 	if (pageNum && pageNum != _sceneDrawPage1) {
1207 		SWAP(_sceneDrawPage1, _sceneDrawPage2);
1208 		updateDrawPage2();
1209 	}
1210 
1211 	if (pageNum && pageNum != _sceneDrawPage1) {
1212 		SWAP(_sceneDrawPage1, _sceneDrawPage2);
1213 		updateDrawPage2();
1214 	}
1215 
1216 	generateBlockDrawingBuffer();
1217 	drawVcnBlocks();
1218 	drawSceneShapes();
1219 
1220 	if (!pageNum) {
1221 		drawSpecialGuiShape(_sceneDrawPage1);
1222 		_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage1, _sceneDrawPage2, Screen::CR_NO_P_CHECK);
1223 		_screen->copyRegion(112, 0, 112, 0, 176, 120, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
1224 		_screen->updateScreen();
1225 		SWAP(_sceneDrawPage1, _sceneDrawPage2);
1226 	}
1227 
1228 	updateEnvironmentalSfx(0);
1229 	gui_drawCompass();
1230 
1231 	_sceneUpdateRequired = false;
1232 }
1233 
1234 
setWallType(int block,int wall,int val)1235 void LoLEngine::setWallType(int block, int wall, int val) {
1236 	if (wall == -1) {
1237 		for (int i = 0; i < 4; i++)
1238 			_levelBlockProperties[block].walls[i] = val;
1239 		if (_wllAutomapData[val] == 17) {
1240 			_levelBlockProperties[block].flags &= 0xEF;
1241 			_levelBlockProperties[block].flags |= 0x20;
1242 		} else {
1243 			_levelBlockProperties[block].flags &= 0xDF;
1244 		}
1245 	} else {
1246 		_levelBlockProperties[block].walls[wall] = val;
1247 	}
1248 
1249 	checkSceneUpdateNeed(block);
1250 }
1251 
updateDrawPage2()1252 void LoLEngine::updateDrawPage2() {
1253 	_screen->copyRegion(112, 0, 112, 0, 176, 120, 0, _sceneDrawPage2, Screen::CR_NO_P_CHECK);
1254 }
1255 
prepareSpecialScene(int fieldType,int hasDialogue,int suspendGui,int allowSceneUpdate,int controlMode,int fadeFlag)1256 void LoLEngine::prepareSpecialScene(int fieldType, int hasDialogue, int suspendGui, int allowSceneUpdate, int controlMode, int fadeFlag) {
1257 	resetPortraitsAndDisableSysTimer();
1258 	if (fieldType) {
1259 		if (suspendGui)
1260 			gui_specialSceneSuspendControls(1);
1261 
1262 		if (!allowSceneUpdate)
1263 			_sceneDefaultUpdate = 0;
1264 
1265 		if (hasDialogue)
1266 			initDialogueSequence(fieldType, 0);
1267 
1268 		if (fadeFlag) {
1269 			if (_flags.use16ColorMode)
1270 				setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect);
1271 			else
1272 				_screen->fadePalette(_screen->getPalette(3), 10);
1273 			_screen->_fadeFlag = 0;
1274 		}
1275 
1276 		setSpecialSceneButtons(0, 0, 320, 130, controlMode);
1277 
1278 	} else {
1279 		if (suspendGui)
1280 			gui_specialSceneSuspendControls(0);
1281 
1282 		if (!allowSceneUpdate)
1283 			_sceneDefaultUpdate = 0;
1284 
1285 		gui_disableControls(controlMode);
1286 
1287 		if (fadeFlag) {
1288 			if (_flags.use16ColorMode) {
1289 				setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect);
1290 			} else {
1291 				_screen->getPalette(3).copy(_screen->getPalette(0), 128);
1292 				_screen->loadSpecialColors(_screen->getPalette(3));
1293 				_screen->fadePalette(_screen->getPalette(3), 10);
1294 			}
1295 			_screen->_fadeFlag = 0;
1296 		}
1297 
1298 		if (hasDialogue)
1299 			initDialogueSequence(fieldType, 0);
1300 
1301 		setSpecialSceneButtons(112, 0, 176, 120, controlMode);
1302 	}
1303 }
1304 
restoreAfterSpecialScene(int fadeFlag,int redrawPlayField,int releaseTimScripts,int sceneUpdateMode)1305 int LoLEngine::restoreAfterSpecialScene(int fadeFlag, int redrawPlayField, int releaseTimScripts, int sceneUpdateMode) {
1306 	if (!_needSceneRestore)
1307 		return 0;
1308 
1309 	_needSceneRestore = 0;
1310 	enableSysTimer(2);
1311 
1312 	if (_dialogueField)
1313 		restoreAfterDialogueSequence(_currentControlMode);
1314 
1315 	if (_specialSceneFlag)
1316 		gui_specialSceneRestoreControls(_currentControlMode);
1317 
1318 	int l = _currentControlMode;
1319 	_currentControlMode = 0;
1320 
1321 	gui_specialSceneRestoreButtons();
1322 	calcCharPortraitXpos();
1323 
1324 	_currentControlMode = l;
1325 
1326 	if (releaseTimScripts) {
1327 		for (int i = 0; i < TIM::kWSASlots; i++)
1328 			_tim->freeAnimStruct(i);
1329 
1330 		for (int i = 0; i < 10; i++)
1331 			_tim->unload(_activeTim[i]);
1332 	}
1333 
1334 	gui_enableControls();
1335 
1336 	if (fadeFlag) {
1337 		if ((_screen->_fadeFlag != 1 && _screen->_fadeFlag != 2) || (_screen->_fadeFlag == 1 && _currentControlMode)) {
1338 			if (_currentControlMode)
1339 				_screen->fadeToBlack(10);
1340 			else
1341 				_screen->fadeClearSceneWindow(10);
1342 		}
1343 
1344 		_currentControlMode = 0;
1345 		calcCharPortraitXpos();
1346 
1347 		if (redrawPlayField)
1348 			gui_drawPlayField();
1349 
1350 		setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect);
1351 
1352 	} else {
1353 		_currentControlMode = 0;
1354 		calcCharPortraitXpos();
1355 
1356 		if (redrawPlayField)
1357 			gui_drawPlayField();
1358 	}
1359 
1360 	_sceneDefaultUpdate = sceneUpdateMode;
1361 	return 1;
1362 }
1363 
setSequenceButtons(int x,int y,int w,int h,int enableFlags)1364 void LoLEngine::setSequenceButtons(int x, int y, int w, int h, int enableFlags) {
1365 	gui_enableSequenceButtons(x, y, w, h, enableFlags);
1366 	_seqWindowX1 = x;
1367 	_seqWindowY1 = y;
1368 	_seqWindowX2 = x + w;
1369 	_seqWindowY2 = y + h;
1370 	int offs = _itemInHand ? 10 : 0;
1371 	_screen->setMouseCursor(offs, offs, getItemIconShapePtr(_itemInHand));
1372 	_currentFloatingCursor = -1;
1373 	if (w == 320) {
1374 		setLampMode(0);
1375 		_lampStatusSuspended = true;
1376 	}
1377 }
1378 
setSpecialSceneButtons(int x,int y,int w,int h,int enableFlags)1379 void LoLEngine::setSpecialSceneButtons(int x, int y, int w, int h, int enableFlags) {
1380 	gui_enableSequenceButtons(x, y, w, h, enableFlags);
1381 	_spsWindowX = x;
1382 	_spsWindowY = y;
1383 	_spsWindowW = w;
1384 	_spsWindowH = h;
1385 }
1386 
setDefaultButtonState()1387 void LoLEngine::setDefaultButtonState() {
1388 	gui_enableDefaultPlayfieldButtons();
1389 	_seqWindowX1 = _seqWindowY1 = _seqWindowX2 = _seqWindowY2 = _seqTrigger = 0;
1390 	if (_lampStatusSuspended)
1391 		resetLampStatus();
1392 	_lampStatusSuspended = false;
1393 }
1394 
drawSceneShapes(int)1395 void LoLEngine::drawSceneShapes(int) {
1396 	for (int i = 0; i < 18; i++) {
1397 		uint8 t = _dscTileIndex[i];
1398 		uint8 s = _visibleBlocks[t]->walls[_sceneDrawVarDown];
1399 
1400 		_shpDmX1 = 0;
1401 		_shpDmX2 = 0;
1402 
1403 		int16 dimY1 = 0;
1404 		int16 dimY2 = 0;
1405 
1406 		setLevelShapesDim(t, _shpDmX1, _shpDmX2, _sceneShpDim);
1407 
1408 		if (_shpDmX2 <= _shpDmX1)
1409 			continue;
1410 
1411 		drawDecorations(t);
1412 
1413 		uint16 w = _wllWallFlags[s];
1414 
1415 		if (t == 16)
1416 			w |= 0x80;
1417 
1418 		drawBlockEffects(t, 0);
1419 
1420 		if (_visibleBlocks[t]->assignedObjects && (w & 0x80))
1421 			drawBlockObjects(t);
1422 
1423 		drawBlockEffects(t, 1);
1424 
1425 		if (!(w & 8))
1426 			continue;
1427 
1428 		uint16 v = 20 * (s - (s < 23 ? _dscDoorScaleOffs[s] : 0));
1429 		if (v > 80)
1430 			v = 80;
1431 
1432 		setDoorShapeDim(t, dimY1, dimY2, _sceneShpDim);
1433 		drawDoor(_doorShapes[(s < 23 ? _dscDoorShpIndex[s] : 0)], 0, t, 10, 0, -v, 2);
1434 		setLevelShapesDim(t, dimY1, dimY2, _sceneShpDim);
1435 	}
1436 }
1437 
drawDecorations(int index)1438 void LoLEngine::drawDecorations(int index) {
1439 	for (int i = 1; i >= 0; i--) {
1440 		int s = index * 2 + i;
1441 		uint16 scaleW = _dscShapeScaleW[s];
1442 		uint16 scaleH = _dscShapeScaleH[s];
1443 		int8 ix = _dscShapeIndex[s];
1444 		uint8 shpIx = ABS(ix);
1445 		uint8 ovlIndex = _dscShapeOvlIndex[4 + _dscDimMap[index] * 5] + 2;
1446 		if (ovlIndex > 7)
1447 			ovlIndex = 7;
1448 
1449 		if (!scaleW || !scaleH)
1450 			continue;
1451 
1452 		uint8 d = (_currentDirection + _dscWalls[s]) & 3;
1453 		int8 l = _wllShapeMap[_visibleBlocks[index]->walls[d]];
1454 
1455 		uint8 *shapeData = 0;
1456 
1457 		int x = 0;
1458 		int y = 0;
1459 		int flags = 0;
1460 
1461 		while (l > 0) {
1462 			if ((_levelDecorationProperties[l].flags & 8) && index != 3 && index != 9 && index != 13) {
1463 				l = _levelDecorationProperties[l].next;
1464 				continue;
1465 			}
1466 
1467 			if (_dscOvlMap[shpIx] == 1 && ((_levelDecorationProperties[l].flags & 2) || ((_levelDecorationProperties[l].flags & 4) && _wllProcessFlag)))
1468 				ix = -ix;
1469 
1470 			int xOffs = 0;
1471 			int yOffs = 0;
1472 			uint8 *ovl = 0;
1473 
1474 			if (_levelDecorationProperties[l].scaleFlag[shpIx] & 1) {
1475 				xOffs = _levelDecorationProperties[l].shapeX[shpIx];
1476 				yOffs = _levelDecorationProperties[l].shapeY[shpIx];
1477 				shpIx = _dscOvlMap[shpIx];
1478 				int ov = ovlIndex;
1479 				if (_flags.use16ColorMode) {
1480 					uint8 bb = _blockBrightness >> 4;
1481 					if (ov > bb)
1482 						ov -= bb;
1483 					else
1484 						ov = 0;
1485 				}
1486 				ovl = _screen->getLevelOverlay(ov);
1487 			} else if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xFFFF) {
1488 				scaleW = scaleH = 0x100;
1489 				int ov = 7;
1490 				if (_flags.use16ColorMode) {
1491 					uint8 bb = _blockBrightness >> 4;
1492 					if (ov > bb)
1493 						ov -= bb;
1494 					else
1495 						ov = 0;
1496 				}
1497 				ovl = _screen->getLevelOverlay(ov);
1498 			}
1499 
1500 			if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xFFFF) {
1501 				shapeData = _levelDecorationShapes[_levelDecorationProperties[l].shapeIndex[shpIx]];
1502 				if (shapeData) {
1503 					if (ix < 0) {
1504 						x = _dscShapeX[s] + xOffs + ((_levelDecorationProperties[l].shapeX[shpIx] * scaleW) >> 8);
1505 						if (ix == _dscShapeIndex[s]) {
1506 							x = _dscShapeX[s] - ((_levelDecorationProperties[l].shapeX[shpIx] * scaleW) >> 8) -
1507 							     _screen->getShapeScaledWidth(shapeData, scaleW) - xOffs;
1508 						}
1509 						flags = 0x105;
1510 					} else {
1511 						x = _dscShapeX[s] + xOffs + ((_levelDecorationProperties[l].shapeX[shpIx] * scaleW) >> 8);
1512 						flags = 0x104;
1513 					}
1514 
1515 					y = _dscShapeY[s] + yOffs + ((_levelDecorationProperties[l].shapeY[shpIx] * scaleH) >> 8);
1516 					_screen->drawShape(_sceneDrawPage1, shapeData, x + 112, y, _sceneShpDim, flags, ovl, 1, scaleW, scaleH);
1517 
1518 					if ((_levelDecorationProperties[l].flags & 1) && shpIx < 4) {
1519 						//draw shadow
1520 						x += (_screen->getShapeScaledWidth(shapeData, scaleW));
1521 						flags ^= 1;
1522 						_screen->drawShape(_sceneDrawPage1, shapeData, x + 112, y, _sceneShpDim, flags, ovl, 1, scaleW, scaleH);
1523 					}
1524 				}
1525 			}
1526 
1527 			l = _levelDecorationProperties[l].next;
1528 			shpIx = (_dscShapeIndex[s] < 0) ? -_dscShapeIndex[s] : _dscShapeIndex[s];
1529 		}
1530 	}
1531 }
1532 
drawBlockEffects(int index,int type)1533 void LoLEngine::drawBlockEffects(int index, int type) {
1534 	static const uint16 yOffs[] = { 0xFF, 0xFF, 0x80, 0x80 };
1535 	uint8 flg = _visibleBlocks[index]->flags;
1536 	// flags: 0x10 = ice wall, 0x20 = teleporter, 0x40 = blue slime spot, 0x80 = blood spot
1537 	if (!(flg & 0xF0))
1538 		return;
1539 
1540 	type = (type == 0) ? 2 : 0;
1541 
1542 	for (int i = 0; i < 2; i++, type++) {
1543 		if (!((0x10 << type) & flg))
1544 			continue;
1545 
1546 		uint16 x = 0x80;
1547 		uint16 y = yOffs[type];
1548 		uint16 drawFlag = (type == 3) ? 0x80 : 0x20;
1549 		uint8 *ovl = (type == 3) ? _screen->_grayOverlay : 0;
1550 
1551 		if (_flags.use16ColorMode) {
1552 			ovl = 0;
1553 			drawFlag = (type == 0 || type == 3) ? 0 : 0x20;
1554 		}
1555 
1556 		calcCoordinatesAddDirectionOffset(x, y, _currentDirection);
1557 
1558 		x |= ((_visibleBlockIndex[index] & 0x1F) << 8);
1559 		y |= ((_visibleBlockIndex[index] & 0xFFE0) << 3);
1560 
1561 		drawItemOrMonster(_effectShapes[type], ovl, x, y, 0, (type == 1) ? -20 : 0, drawFlag, -1, false);
1562 	}
1563 }
1564 
drawSpecialGuiShape(int pageNum)1565 void LoLEngine::drawSpecialGuiShape(int pageNum) {
1566 	if (!_specialGuiShape)
1567 		return;
1568 
1569 	_screen->drawShape(pageNum, _specialGuiShape, _specialGuiShapeX, _specialGuiShapeY, 2, 0);
1570 
1571 	if (_specialGuiShapeMirrorFlag & 1)
1572 		_screen->drawShape(pageNum, _specialGuiShape, _specialGuiShapeX + _specialGuiShape[3], _specialGuiShapeY, 2, 1);
1573 }
1574 
1575 } // End of namespace Kyra
1576 
1577 #endif // ENABLE_LOL
1578