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 
28 namespace Kyra {
29 
loadMonsterShapes(const char * file,int monsterIndex,int animType)30 void LoLEngine::loadMonsterShapes(const char *file, int monsterIndex, int animType) {
31 	releaseMonsterShapes(monsterIndex);
32 	_screen->loadBitmap(file, 3, 3, 0);
33 
34 	const uint8 *p = _screen->getCPagePtr(2);
35 	const uint8 *ts[16];
36 
37 	for (int i = 0; i < 16; i++) {
38 		ts[i] = _screen->getPtrToShape(p, i);
39 
40 		bool replaced = false;
41 		int pos = monsterIndex << 4;
42 
43 		for (int ii = 0; ii < i; ii++) {
44 			if (ts[i] != ts[ii])
45 				continue;
46 
47 			_monsterShapes[pos + i] = _monsterShapes[pos + ii];
48 			replaced = true;
49 			break;
50 		}
51 
52 		if (!replaced)
53 			_monsterShapes[pos + i] = _screen->makeShapeCopy(p, i);
54 
55 		int size = _screen->getShapePaletteSize(_monsterShapes[pos + i]) << 3;
56 		_monsterPalettes[pos + i] = new uint8[size];
57 		memset(_monsterPalettes[pos + i], 0, size);
58 	}
59 
60 	for (int i = 0; i < 4; i++) {
61 		for (int ii = 0; ii < 16; ii++) {
62 			uint8 **of = &_monsterDecorationShapes[monsterIndex * 192 + i * 48 + ii * 3];
63 			int s = (i << 4) + ii + 17;
64 			of[0] = _screen->makeShapeCopy(p, s);
65 			of[1] = _screen->makeShapeCopy(p, s + 1);
66 			of[2] = _screen->makeShapeCopy(p, s + 2);
67 		}
68 	}
69 	_monsterAnimType[monsterIndex] = animType & 0xFF;
70 
71 	uint8 *palShape = _screen->makeShapeCopy(p, 16);
72 
73 	_screen->clearPage(3);
74 	_screen->drawShape(2, palShape, 0, 0, 0, 0);
75 
76 	uint8 *tmpPal1 = new uint8[64];
77 	uint8 *tmpPal2 = new uint8[256];
78 	uint16 *tmpPal3 = new uint16[256];
79 	memset(tmpPal1, 0, 64);
80 
81 	for (int i = 0; i < 64; i++) {
82 		tmpPal1[i] = *p;
83 		p += 320;
84 	}
85 
86 	p = _screen->getCPagePtr(2);
87 
88 	for (int i = 0; i < 16; i++) {
89 		int pos = (monsterIndex << 4) + i;
90 		uint16 sz = MIN(_screen->getShapeSize(_monsterShapes[pos]) - 10, 256);
91 		memset(tmpPal2, 0, 256);
92 		memcpy(tmpPal2, _monsterShapes[pos] + 10, sz);
93 		memset(tmpPal3, 0xFF, 256 * sizeof(uint16));
94 		uint8 numCol = *tmpPal2;
95 
96 		for (int ii = 0; ii < numCol; ii++) {
97 			uint8 *cl = (uint8 *)memchr(tmpPal1, tmpPal2[1 + ii], 64);
98 			if (!cl)
99 				continue;
100 			tmpPal3[ii] = (uint16)(cl - tmpPal1);
101 		}
102 
103 		for (int ii = 0; ii < 8; ii++) {
104 			memset(tmpPal2, 0, 256);
105 			memcpy(tmpPal2, _monsterShapes[pos] + 10, sz);
106 			for (int iii = 0; iii < numCol; iii++) {
107 				if (tmpPal3[iii] == 0xFFFF)
108 					continue;
109 				if (p[tmpPal3[iii] * 320 + ii + 1])
110 					tmpPal2[1 + iii] = p[tmpPal3[iii] * 320 + ii + 1];
111 			}
112 			memcpy(_monsterPalettes[pos] + ii * numCol, &tmpPal2[1], numCol);
113 		}
114 	}
115 
116 	delete[] tmpPal1;
117 	delete[] tmpPal2;
118 	delete[] tmpPal3;
119 	delete[] palShape;
120 }
121 
releaseMonsterShapes(int monsterIndex)122 void LoLEngine::releaseMonsterShapes(int monsterIndex) {
123 	for (int i = 0; i < 16; i++) {
124 		int pos = (monsterIndex << 4) + i;
125 		int pos2 = (monsterIndex << 4) + 16;
126 		if (_monsterShapes[pos]) {
127 			uint8 *t = _monsterShapes[pos];
128 			delete[] _monsterShapes[pos];
129 			for (int ii = pos; ii < pos2; ii++) {
130 				if (_monsterShapes[ii] == t)
131 					_monsterShapes[ii] = 0;
132 			}
133 		}
134 
135 		if (_monsterPalettes[pos]) {
136 			delete[] _monsterPalettes[pos];
137 			_monsterPalettes[pos] = 0;
138 		}
139 	}
140 
141 	for (int i = 0; i < 192; i++) {
142 		int pos = (monsterIndex * 192) + i;
143 		if (_monsterDecorationShapes[pos]) {
144 			delete[] _monsterDecorationShapes[pos];
145 			_monsterDecorationShapes[pos] = 0;
146 		}
147 	}
148 }
149 
deleteMonstersFromBlock(int block)150 int LoLEngine::deleteMonstersFromBlock(int block) {
151 	int i = _levelBlockProperties[block].assignedObjects;
152 	int cnt = 0;
153 	uint16 next = 0;
154 
155 	while (i) {
156 		next = findObject(i)->nextAssignedObject;
157 		if (!(i & 0x8000)) {
158 			i = next;
159 			continue;
160 		}
161 
162 		LoLMonster *m = &_monsters[i & 0x7FFF];
163 
164 		cnt++;
165 		setMonsterMode(m, 14);
166 
167 		checkSceneUpdateNeed(m->block);
168 
169 		placeMonster(m, 0, 0);
170 
171 		i = next;
172 	}
173 	return cnt;
174 }
175 
setMonsterMode(LoLMonster * monster,int mode)176 void LoLEngine::setMonsterMode(LoLMonster *monster, int mode) {
177 	if (monster->mode == 13 && mode != 14)
178 		return;
179 
180 	if (mode == 7) {
181 		monster->destX = _partyPosX;
182 		monster->destY = _partyPosY;
183 	}
184 
185 	if (monster->mode == 1 && mode == 7) {
186 		for (int i = 0; i < 30; i++) {
187 			if (monster->mode != 1)
188 				continue;
189 			monster->mode = mode;
190 			monster->fightCurTick = 0;
191 			monster->destX = _partyPosX;
192 			monster->destY = _partyPosY;
193 			setMonsterDirection(monster, calcMonsterDirection(monster->x, monster->y, monster->destX, monster->destY));
194 		}
195 	} else {
196 		monster->mode = mode;
197 		monster->fightCurTick = 0;
198 		if (mode == 14)
199 			monster->hitPoints = 0;
200 		if (mode == 13 && (monster->flags & 0x20)) {
201 			monster->mode = 0;
202 			monsterDropItems(monster);
203 			if (_currentLevel != 29)
204 				setMonsterMode(monster, 14);
205 			runLevelScriptCustom(0x404, -1, monster->id, monster->id, 0, 0);
206 			checkSceneUpdateNeed(monster->block);
207 			if (monster->mode == 14)
208 				placeMonster(monster, 0, 0);
209 		}
210 	}
211 }
212 
updateMonsterAdjustBlocks(LoLMonster * monster)213 bool LoLEngine::updateMonsterAdjustBlocks(LoLMonster *monster) {
214 	static const uint8 dims[] = { 0, 13, 9, 3 };
215 	if (monster->properties->flags & 8)
216 		return true;
217 
218 	uint16 x1 = (monster->x & 0xFF00) | 0x80;
219 	uint16 y1 = (monster->y & 0xFF00) | 0x80;
220 	int x2 = _partyPosX;
221 	int y2 = _partyPosY;
222 
223 	uint16 dir = 0;
224 	if (monster->properties->flags & 1) {
225 		dir = monster->direction >> 1;
226 	} else {
227 		dir = calcMonsterDirection(x1, y1, x2, y2);
228 		if ((monster->properties->flags & 2) && (dir == (monster->direction ^ 4)))
229 			return false;
230 		dir >>= 1;
231 	}
232 
233 	calcSpriteRelPosition(x1, y1, x2, y2, dir);
234 	x2 >>= 8;
235 	y2 >>= 8;
236 
237 	if (y2 < 0 || y2 > 3)
238 		return false;
239 
240 	int t = (x2 < 0) ? -x2 : x2;
241 	if (t > y2)
242 		return false;
243 
244 	for (int i = 0; i < 18; i++)
245 		_visibleBlocks[i] = &_levelBlockProperties[(monster->block + _dscBlockIndex[dir + i]) & 0x3FF];
246 
247 	int16 fx1 = 0;
248 	int16 fx2 = 0;
249 	setLevelShapesDim(x2 + dims[y2], fx1, fx2, 13);
250 
251 	return fx1 < fx2;
252 }
253 
placeMonster(LoLMonster * monster,uint16 x,uint16 y)254 void LoLEngine::placeMonster(LoLMonster *monster, uint16 x, uint16 y) {
255 	bool cont = true;
256 	int t = monster->block;
257 	if (monster->block) {
258 		removeAssignedObjectFromBlock(&_levelBlockProperties[t], ((uint16)monster->id) | 0x8000);
259 		_levelBlockProperties[t].direction = 5;
260 		checkSceneUpdateNeed(t);
261 	} else {
262 		cont = false;
263 	}
264 
265 	monster->block = calcBlockIndex(x, y);
266 
267 	if (monster->x != x || monster->y != y) {
268 		monster->x = x;
269 		monster->y = y;
270 		monster->currentSubFrame = (monster->currentSubFrame + 1) & 3;
271 	}
272 
273 	if (monster->block == 0)
274 		return;
275 
276 	assignObjectToBlock(&_levelBlockProperties[monster->block].assignedObjects, ((uint16)monster->id) | 0x8000);
277 	_levelBlockProperties[monster->block].direction = 5;
278 	checkSceneUpdateNeed(monster->block);
279 
280 	// WORKAROUND: Some monsters in the white tower have sound id's of 0xFF. This is definitely a bug, since the
281 	// last valid track number is 249 and there is no specific handling for 0xFF. Nonetheless this wouldn't
282 	// cause problems in the original code, because it just so happens that the invalid memory address points
283 	// to an entry in _ingameGMSoundIndex which just so happens to have a value of -1
284 	if (monster->properties->sounds[0] == 0 || monster->properties->sounds[0] == 255 || cont == false)
285 		return;
286 
287 	if ((!(monster->properties->flags & 0x100) || ((monster->currentSubFrame & 1) == 0)) && monster->block == t)
288 		return;
289 
290 	if (monster->block != t)
291 		runLevelScriptCustom(monster->block, 0x800, -1, monster->id, 0, 0);
292 
293 	if (_updateFlags & 1)
294 		return;
295 
296 	snd_processEnvironmentalSoundEffect(monster->properties->sounds[0], monster->block);
297 }
298 
calcMonsterDirection(uint16 x1,uint16 y1,uint16 x2,uint16 y2)299 int LoLEngine::calcMonsterDirection(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
300 	int16 r = 0;
301 
302 	int16 t1 = y1 - y2;
303 	if (t1 < 0) {
304 		r++;
305 		t1 = -t1;
306 	}
307 
308 	r <<= 1;
309 
310 	int16 t2 = x2 - x1;
311 	if (t2 < 0) {
312 		r++;
313 		t2 = -t2;
314 	}
315 
316 	uint8 f = (t1 > t2) ? 1 : 0;
317 
318 	if (t2 >= t1)
319 		SWAP(t1, t2);
320 
321 	r = (r << 1) | f;
322 
323 	t1 = (t1 + 1) >> 1;
324 
325 	f = (t1 > t2) ? 1 : 0;
326 	r = (r << 1) | f;
327 
328 	static const uint8 retVal[] = { 1, 2, 1, 0, 7, 6, 7, 0, 3, 2, 3, 4, 5, 6, 5, 4};
329 	return retVal[r];
330 }
331 
setMonsterDirection(LoLMonster * monster,int dir)332 void LoLEngine::setMonsterDirection(LoLMonster *monster, int dir) {
333 	monster->direction = dir;
334 
335 	if (!(dir & 1) || ((monster->direction - (monster->facing << 1)) >= 2))
336 		monster->facing = monster->direction >> 1;
337 
338 	checkSceneUpdateNeed(monster->block);
339 }
340 
monsterDropItems(LoLMonster * monster)341 void LoLEngine::monsterDropItems(LoLMonster *monster) {
342 	uint16 a = monster->assignedItems;
343 	while (a) {
344 		uint16 b = a;
345 		a = _itemsInPlay[a].nextAssignedObject;
346 		setItemPosition(b, monster->x, monster->y, 0, 1);
347 	}
348 }
349 
checkBlockBeforeObjectPlacement(uint16 x,uint16 y,uint16 objectWidth,uint16 testFlag,uint16 wallFlag)350 int LoLEngine::checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 objectWidth, uint16 testFlag, uint16 wallFlag) {
351 	_objectLastDirection = 0;
352 	uint16 x2 = 0;
353 	uint16 y2 = 0;
354 	int xOffs = 0;
355 	int yOffs = 0;
356 	int flag = 0;
357 
358 	int r = testBlockPassability(calcBlockIndex(x, y), x, y, objectWidth, testFlag, wallFlag);
359 	if (r)
360 		return r;
361 
362 	r = checkBlockOccupiedByParty(x, y, testFlag);
363 	if (r)
364 		return 4;
365 
366 	if (x & 0x80) {
367 		if (((x & 0xFF) + objectWidth) & 0xFF00) {
368 			xOffs = 1;
369 			_objectLastDirection = 2;
370 			x2 = x + objectWidth;
371 
372 			r = testBlockPassability(calcBlockIndex(x2, y), x, y, objectWidth, testFlag, wallFlag);
373 			if (r)
374 				return r;
375 
376 			r = checkBlockOccupiedByParty(x + xOffs, y, testFlag);
377 			if (r)
378 				return 4;
379 
380 			flag = 1;
381 		}
382 	} else {
383 		if (((x & 0xFF) - objectWidth) & 0xFF00) {
384 			xOffs = -1;
385 			_objectLastDirection = 6;
386 			x2 = x - objectWidth;
387 
388 			r = testBlockPassability(calcBlockIndex(x2, y), x, y, objectWidth, testFlag, wallFlag);
389 			if (r)
390 				return r;
391 
392 			r = checkBlockOccupiedByParty(x + xOffs, y, testFlag);
393 			if (r)
394 				return 4;
395 
396 			flag = 1;
397 		}
398 	}
399 
400 	if (y & 0x80) {
401 		if (((y & 0xFF) + objectWidth) & 0xFF00) {
402 			yOffs = 1;
403 			_objectLastDirection = 4;
404 			y2 = y + objectWidth;
405 
406 			r = testBlockPassability(calcBlockIndex(x, y2), x, y, objectWidth, testFlag, wallFlag);
407 			if (r)
408 				return r;
409 
410 			r = checkBlockOccupiedByParty(x, y + yOffs, testFlag);
411 			if (r)
412 				return 4;
413 			flag &= 1;
414 		} else {
415 			flag = 0;
416 		}
417 	} else {
418 		if (((y & 0xFF) - objectWidth) & 0xFF00) {
419 			yOffs = -1;
420 			_objectLastDirection = 0;
421 			y2 = y - objectWidth;
422 
423 			r = testBlockPassability(calcBlockIndex(x, y2), x, y, objectWidth, testFlag, wallFlag);
424 			if (r)
425 				return r;
426 
427 			r = checkBlockOccupiedByParty(x, y + yOffs, testFlag);
428 			if (r)
429 				return 4;
430 			flag &= 1;
431 		} else {
432 			flag = 0;
433 		}
434 	}
435 
436 	if (!flag)
437 		return 0;
438 
439 	r = testBlockPassability(calcBlockIndex(x2, y2), x, y, objectWidth, testFlag, wallFlag);
440 	if (r)
441 		return r;
442 
443 	r = checkBlockOccupiedByParty(x + xOffs, y + yOffs, testFlag);
444 	if (r)
445 		return 4;
446 
447 	return 0;
448 }
449 
testBlockPassability(int block,int x,int y,int objectWidth,int testFlag,int wallFlag)450 int LoLEngine::testBlockPassability(int block, int x, int y, int objectWidth, int testFlag, int wallFlag) {
451 	if (block == _currentBlock)
452 		testFlag &= 0xFFFE;
453 
454 	if (testFlag & 1) {
455 		_monsterCurBlock = block;
456 		if (testWallFlag(block, -1, wallFlag))
457 			return 1;
458 		_monsterCurBlock = 0;
459 	}
460 
461 	if (!(testFlag & 2))
462 		return 0;
463 
464 	uint16 obj = _levelBlockProperties[block].assignedObjects;
465 	while (obj & 0x8000) {
466 		LoLMonster *monster = &_monsters[obj & 0x7FFF];
467 
468 		if (monster->mode < 13) {
469 			int r = checkDrawObjectSpace(x, y, monster->x, monster->y);
470 			if ((objectWidth + monster->properties->maxWidth) > r)
471 				return 2;
472 		}
473 
474 		obj = findObject(obj)->nextAssignedObject;
475 	}
476 
477 	return 0;
478 }
479 
calcMonsterSkillLevel(int id,int a)480 int LoLEngine::calcMonsterSkillLevel(int id, int a) {
481 	const uint16 *c = getCharacterOrMonsterStats(id);
482 	int r = (a << 8) / c[4];
483 
484 	if (id & 0x8000) {
485 		r = (r * _monsterModifiers2[_monsterDifficulty]) >> 8;
486 	} else {
487 		if (_characters[id].skillLevels[1] > 7)
488 			r = (r - (r >> 1));
489 		else if (_characters[id].skillLevels[1] > 3 && _characters[id].skillLevels[1] <= 7)
490 			r = (r - (r >> 2));
491 	}
492 
493 	return r;
494 }
495 
checkBlockOccupiedByParty(int x,int y,int testFlag)496 int LoLEngine::checkBlockOccupiedByParty(int x, int y, int testFlag) {
497 	if ((testFlag & 4) && (_currentBlock == calcBlockIndex(x, y)))
498 		return 1;
499 
500 	return 0;
501 }
502 
drawBlockObjects(int blockArrayIndex)503 void LoLEngine::drawBlockObjects(int blockArrayIndex) {
504 	LevelBlockProperty *l = _visibleBlocks[blockArrayIndex];
505 	uint16 s = l->assignedObjects;
506 
507 	if (l->direction != _currentDirection) {
508 		l->drawObjects = 0;
509 		l->direction = _currentDirection;
510 
511 		while (s) {
512 			reassignDrawObjects(_currentDirection, s, l, true);
513 			s = findObject(s)->nextAssignedObject;
514 		}
515 	}
516 
517 	s = l->drawObjects;
518 	while (s) {
519 		if (s & 0x8000) {
520 			s &= 0x7FFF;
521 			if (blockArrayIndex < 15)
522 				drawMonster(s);
523 			s = _monsters[s].nextDrawObject;
524 		} else {
525 			LoLItem *i = &_itemsInPlay[s];
526 			int fx = _sceneItemOffs[s & 7] << 1;
527 			int fy = _sceneItemOffs[(s >> 1) & 7] + 5;
528 
529 			if (i->flyingHeight >= 2 && blockArrayIndex >= 15) {
530 				s = i->nextDrawObject;
531 				continue;
532 			}
533 
534 			uint8 *shp = 0;
535 			uint16 flg = 0;
536 
537 			if (i->flyingHeight >= 2)
538 				fy -= ((i->flyingHeight - 1) * 6);
539 
540 			if ((_itemProperties[i->itemPropertyIndex].flags & 0x1000) && !(i->shpCurFrame_flg & 0xC000)) {
541 				int shpIndex = _itemProperties[i->itemPropertyIndex].flags & 0x800 ? 7 : _itemProperties[i->itemPropertyIndex].shpIndex;
542 				int ii = 0;
543 				for (; ii < 8; ii++) {
544 					if (!_flyingObjects[ii].enable)
545 						continue;
546 
547 					if (_flyingObjects[ii].item == s)
548 						break;
549 				}
550 
551 				if (_flyingItemShapes[shpIndex].flipFlags && ((i->x ^ i->y) & 0x20))
552 					flg |= 0x20;
553 
554 				flg |= _flyingItemShapes[shpIndex].drawFlags;
555 
556 				if (ii != 8) {
557 					switch (_currentDirection - (_flyingObjects[ii].direction >> 1) + 3) {
558 					case 1:
559 					case 5:
560 						shpIndex = _flyingItemShapes[shpIndex].shapeFront;
561 						break;
562 					case 3:
563 						shpIndex = _flyingItemShapes[shpIndex].shapeBack;
564 						break;
565 					case 2:
566 					case 6:
567 						flg |= 0x10;
568 						// fall through
569 					case 0:
570 					case 4:
571 						shpIndex = _flyingItemShapes[shpIndex].shapeLeft;
572 						break;
573 					default:
574 						break;
575 					}
576 
577 					shp = _thrownShapes[shpIndex];
578 				}
579 
580 				if (shp)
581 					fy += (shp[2] >> 2);
582 
583 			} else {
584 				shp = (_itemProperties[i->itemPropertyIndex].flags & 0x40) ? _gameShapes[_itemProperties[i->itemPropertyIndex].shpIndex] :
585 				      _itemShapes[_gameShapeMap[_itemProperties[i->itemPropertyIndex].shpIndex << 1]];
586 			}
587 
588 			if (shp)
589 				drawItemOrMonster(shp, 0, i->x, i->y, fx, fy, flg, -1, false);
590 			s = i->nextDrawObject;
591 		}
592 	}
593 }
594 
drawMonster(uint16 id)595 void LoLEngine::drawMonster(uint16 id) {
596 	LoLMonster *m = &_monsters[id];
597 	int16 flg = _monsterDirFlags[(_currentDirection << 2) + m->facing];
598 	int curFrm = getMonsterCurFrame(m, flg & 0xFFEF);
599 	uint8 *shp = 0;
600 
601 	if (curFrm == -1) {
602 		shp = _monsterShapes[m->properties->shapeIndex << 4];
603 		calcDrawingLayerParameters(m->x + _monsterShiftOffs[m->shiftStep << 1], m->y + _monsterShiftOffs[(m->shiftStep << 1) + 1], _shpDmX, _shpDmY, _dmScaleW, _dmScaleH, shp, 0);
604 	} else {
605 		int d = m->flags & 7;
606 		bool flip = m->properties->flags & 0x200 ? true : false;
607 		flg &= 0x10;
608 		shp = _monsterShapes[(m->properties->shapeIndex << 4) + curFrm];
609 
610 		if (m->properties->flags & 0x800)
611 			flg |= 0x20;
612 
613 		uint8 *monsterPalette = d ? _monsterPalettes[(m->properties->shapeIndex << 4) + (curFrm & 0x0F)] + (shp[10] * (d - 1)) : 0;
614 		uint8 *brightnessOverlay = drawItemOrMonster(shp, monsterPalette, m->x + _monsterShiftOffs[m->shiftStep << 1], m->y + _monsterShiftOffs[(m->shiftStep << 1) + 1], 0, 0, flg | 1, -1, flip);
615 
616 		for (int i = 0; i < 4; i++) {
617 			int v = m->equipmentShapes[i] - 1;
618 			if (v == -1)
619 				break;
620 
621 			uint8 *shp2 = _monsterDecorationShapes[m->properties->shapeIndex * 192 + v * 48 + curFrm * 3];
622 			if (!shp2)
623 				continue;
624 
625 			drawDoorOrMonsterEquipment(shp2, 0, _shpDmX, _shpDmY, flg | 1, brightnessOverlay);
626 		}
627 	}
628 
629 	if (!m->damageReceived)
630 		return;
631 
632 	int dW = _screen->getShapeScaledWidth(shp, _dmScaleW) >> 1;
633 	int dH = _screen->getShapeScaledHeight(shp, _dmScaleH) >> 1;
634 
635 	int bloodAmount = (m->mode == 13) ? (m->fightCurTick << 1) : (m->properties->hitPoints / (m->damageReceived & 0x7FFF));
636 
637 	shp = _gameShapes[6];
638 
639 	int bloodType = m->properties->flags & 0xC000;
640 	if (bloodType == 0x4000)
641 		bloodType = _flags.use16ColorMode ? 0xBB : 63;
642 	else if (bloodType == 0x8000)
643 		bloodType = _flags.use16ColorMode ? 0x55 : 15;
644 	else if (bloodType == 0xC000)
645 		bloodType = _flags.use16ColorMode ? 0x33 : 74;
646 	else
647 		bloodType = 0;
648 
649 	uint8 *tbl = new uint8[256];
650 	if (bloodType) {
651 		for (int i = 0; i < 256; i++) {
652 			tbl[i] = i;
653 			if (i < 2 || i > 7)
654 				continue;
655 			tbl[i] += bloodType;
656 		}
657 	}
658 
659 	dW += m->hitOffsX;
660 	dH += m->hitOffsY;
661 
662 	bloodAmount = CLIP(bloodAmount, 1, 4);
663 
664 	int sW = _dmScaleW / bloodAmount;
665 	int sH = _dmScaleH / bloodAmount;
666 
667 	_screen->drawShape(_sceneDrawPage1, shp, _shpDmX + dW, _shpDmY + dH, 13, 0x124, tbl, bloodType ? 1 : 0, sW, sH);
668 
669 	delete[] tbl;
670 }
671 
getMonsterCurFrame(LoLMonster * m,uint16 dirFlags)672 int LoLEngine::getMonsterCurFrame(LoLMonster *m, uint16 dirFlags) {
673 	int tmp = 0;
674 	switch (_monsterAnimType[m->properties->shapeIndex]) {
675 	case 0:
676 		// default
677 		if (dirFlags) {
678 			return (m->mode == 13) ? -1 : (dirFlags + m->currentSubFrame);
679 		} else {
680 			if (m->damageReceived)
681 				return 12;
682 
683 			switch (m->mode - 5) {
684 			case 0:
685 				return (m->properties->flags & 4) ? 13 : 0;
686 			case 3:
687 				return (m->fightCurTick + 13);
688 			case 6:
689 				return 14;
690 			case 8:
691 				return -1;
692 			default:
693 				return m->currentSubFrame;
694 			}
695 		}
696 		break;
697 	case 1:
698 		// monsters whose outward appearance reflects the damage they have taken
699 		tmp = m->properties->hitPoints;
700 		if (_flags.isTalkie)
701 			tmp = (tmp * _monsterModifiers1[_monsterDifficulty]) >> 8;
702 		if (m->hitPoints > (tmp >> 1))
703 			tmp = 0;
704 		else if (m->hitPoints > (tmp >> 2))
705 			tmp = 4;
706 		else
707 			tmp = 8;
708 
709 		switch (m->mode) {
710 		case 8:
711 			return (m->fightCurTick + tmp);
712 		case 11:
713 			return 12;
714 		case 13:
715 			return (m->fightCurTick + 12);
716 		default:
717 			return tmp;
718 		}
719 
720 		break;
721 	case 2:
722 		return (m->fightCurTick >= 13) ? 13 : m->fightCurTick;
723 	case 3:
724 		switch (m->mode) {
725 		case 5:
726 			return m->damageReceived ? 5 : 6;
727 		case 8:
728 			return (m->fightCurTick + 6);
729 		case 11:
730 			return 5;
731 		default:
732 			return m->damageReceived ? 5 : m->currentSubFrame;
733 		}
734 
735 		break;
736 	default:
737 		break;
738 	}
739 
740 	return 0;
741 }
742 
reassignDrawObjects(uint16 direction,uint16 itemIndex,LevelBlockProperty * l,bool flag)743 void LoLEngine::reassignDrawObjects(uint16 direction, uint16 itemIndex, LevelBlockProperty *l, bool flag) {
744 	if (l->direction != direction) {
745 		l->direction = 5;
746 		return;
747 	}
748 
749 	LoLObject *newObject = findObject(itemIndex);
750 	int r = calcObjectPosition(newObject, direction);
751 	uint16 *b = &l->drawObjects;
752 	LoLObject *lastObject = 0;
753 
754 	while (*b) {
755 		lastObject = findObject(*b);
756 
757 		if (flag) {
758 			if (calcObjectPosition(lastObject, direction) >= r)
759 				break;
760 		} else {
761 			if (calcObjectPosition(lastObject, direction) > r)
762 				break;
763 		}
764 
765 		b = &lastObject->nextDrawObject;
766 	}
767 
768 	newObject->nextDrawObject = *b;
769 	*b = itemIndex;
770 }
771 
redrawSceneItem()772 void LoLEngine::redrawSceneItem() {
773 	assignVisibleBlocks(_currentBlock, _currentDirection);
774 	_screen->fillRect(112, 0, 287, 119, 0);
775 
776 	static const uint8 sceneClickTileIndex[] = { 13, 16};
777 
778 	int16 x1 = 0;
779 	int16 x2 = 0;
780 
781 	for (int i = 0; i < 2; i++) {
782 		uint8 tile = sceneClickTileIndex[i];
783 		setLevelShapesDim(tile, x1, x2, 13);
784 		uint16 s = _visibleBlocks[tile]->drawObjects;
785 
786 		int t = (i << 7) + 1;
787 		while (s) {
788 			if (s & 0x8000) {
789 				s = _monsters[s & 0x7FFF].nextDrawObject;
790 			} else {
791 				LoLItem *item = &_itemsInPlay[s];
792 
793 				if (item->shpCurFrame_flg & 0x4000) {
794 					if (checkDrawObjectSpace(item->x, item->y, _partyPosX, _partyPosY) < 320) {
795 						int fx = _sceneItemOffs[s & 7] << 1;
796 						int fy = _sceneItemOffs[(s >> 1) & 7] + 5;
797 						if (item->flyingHeight > 1)
798 							fy -= ((item->flyingHeight - 1) * 6);
799 
800 						uint8 *shp = (_itemProperties[item->itemPropertyIndex].flags & 0x40) ? _gameShapes[_itemProperties[item->itemPropertyIndex].shpIndex] :
801 						             _itemShapes[_gameShapeMap[_itemProperties[item->itemPropertyIndex].shpIndex << 1]];
802 
803 						drawItemOrMonster(shp, 0, item->x, item->y, fx, fy, 0, t, 0);
804 						_screen->updateScreen();
805 					}
806 				}
807 
808 				s = item->nextDrawObject;
809 				t++;
810 			}
811 		}
812 	}
813 }
814 
calcSpriteRelPosition(uint16 x1,uint16 y1,int & x2,int & y2,uint16 direction)815 void LoLEngine::calcSpriteRelPosition(uint16 x1, uint16 y1, int &x2, int &y2, uint16 direction) {
816 	int a = x2 - x1;
817 	int b = y1 - y2;
818 
819 	if (direction) {
820 		if (direction != 2)
821 			SWAP(a, b);
822 		if (direction != 3) {
823 			a = -a;
824 			if (direction != 1)
825 				b = -b;
826 		} else {
827 			b = -b;
828 		}
829 	}
830 
831 	x2 = a;
832 	y2 = b;
833 }
834 
drawDoor(uint8 * shape,uint8 * doorPalette,int index,int unk2,int w,int h,int flags)835 void LoLEngine::drawDoor(uint8 *shape, uint8 *doorPalette, int index, int unk2, int w, int h, int flags) {
836 	if (!shape)
837 		return;
838 
839 	uint8 c = _dscDoorY2[(_currentDirection << 5) + unk2];
840 	int r = (c / 5) + 5 * _dscDimMap[index];
841 	uint16 d = _dscShapeOvlIndex[r];
842 	uint16 t = (index << 5) + c;
843 
844 	_shpDmY = _dscDoorMonsterY[t] + 120;
845 
846 	if (flags & 1) {
847 		// TODO / UNUSED
848 		flags |= 1;
849 	}
850 
851 	int u = 0;
852 
853 	if (flags & 2) {
854 		uint8 dimW = _dscDimMap[index];
855 		_dmScaleW = _dscDoorMonsterScaleTable[dimW << 1];
856 		_dmScaleH = _dscDoorMonsterScaleTable[(dimW << 1) + 1];
857 		u = _dscDoor4[dimW];
858 	}
859 
860 	d += 2;
861 
862 	if (!_dmScaleW || !_dmScaleH)
863 		return;
864 
865 	int s = _screen->getShapeScaledHeight(shape, _dmScaleH) >> 1;
866 
867 	if (w)
868 		w = (w * _dmScaleW) >> 8;
869 
870 	if (h)
871 		h = (h * _dmScaleH) >> 8;
872 
873 	_shpDmX = _dscDoorMonsterX[t] + w + 200;
874 	_shpDmY = _shpDmY + 4 - s + h - u;
875 
876 	if (d > 7)
877 		d = 7;
878 
879 	if (_flags.use16ColorMode) {
880 		uint8 bb = _blockBrightness >> 4;
881 		if (d > bb)
882 			d -= bb;
883 		else
884 			d = 0;
885 	}
886 
887 	uint8 *brightnessOverlay = _screen->getLevelOverlay(d);
888 	int doorScaledWitdh = _screen->getShapeScaledWidth(shape, _dmScaleW);
889 
890 	_shpDmX -= (doorScaledWitdh >> 1);
891 	_shpDmY -= s;
892 
893 	drawDoorOrMonsterEquipment(shape, doorPalette, _shpDmX, _shpDmY, flags, brightnessOverlay);
894 }
895 
drawDoorOrMonsterEquipment(uint8 * shape,uint8 * objectPalette,int x,int y,int flags,const uint8 * brightnessOverlay)896 void LoLEngine::drawDoorOrMonsterEquipment(uint8 *shape, uint8 *objectPalette, int x, int y, int flags, const uint8 *brightnessOverlay) {
897 	int flg = 0;
898 
899 	if (flags & 0x10)
900 		flg |= 1;
901 
902 	if (flags & 0x20)
903 		flg |= 0x1000;
904 
905 	if (flags & 0x40)
906 		flg |= 2;
907 
908 	if (flg & 0x1000) {
909 		if (objectPalette)
910 			_screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x9104, objectPalette, brightnessOverlay, 1, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH);
911 		else
912 			_screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x1104, brightnessOverlay, 1, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH);
913 	} else {
914 		if (objectPalette)
915 			_screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x8104, objectPalette, brightnessOverlay, 1, _dmScaleW, _dmScaleH);
916 		else
917 			_screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x104, brightnessOverlay, 1, _dmScaleW, _dmScaleH);
918 	}
919 }
920 
drawItemOrMonster(uint8 * shape,uint8 * monsterPalette,int x,int y,int fineX,int fineY,int flags,int tblValue,bool vflip)921 uint8 *LoLEngine::drawItemOrMonster(uint8 *shape, uint8 *monsterPalette, int x, int y, int fineX, int fineY, int flags, int tblValue, bool vflip) {
922 	uint8 *ovl2 = 0;
923 	uint8 *brightnessOverlay = 0;
924 	uint8 tmpOvl[16];
925 
926 	if (flags & 0x80) {
927 		flags &= 0xFF7F;
928 		ovl2 = monsterPalette;
929 		monsterPalette = 0;
930 	} else {
931 		ovl2 = _screen->getLevelOverlay(_flags.use16ColorMode ? 5 : 4);
932 	}
933 
934 	int r = calcDrawingLayerParameters(x, y, _shpDmX, _shpDmY, _dmScaleW, _dmScaleH, shape, vflip);
935 
936 	if (tblValue == -1) {
937 		r = 7 - ((r / 3) - 1);
938 		r = CLIP(r, 0, 7);
939 		if (_flags.use16ColorMode) {
940 			uint8 bb = _blockBrightness >> 4;
941 			if (r > bb)
942 				r -= bb;
943 			else
944 				r = 0;
945 		}
946 		brightnessOverlay = _screen->getLevelOverlay(r);
947 	} else {
948 		memset(tmpOvl + 1, tblValue, 15);
949 		tmpOvl[0] = 0;
950 		monsterPalette = tmpOvl;
951 		brightnessOverlay = _screen->getLevelOverlay(7);
952 	}
953 
954 	int flg = flags & 0x10 ? 1 : 0;
955 	if (flags & 0x20)
956 		flg |= 0x1000;
957 	if (flags & 0x40)
958 		flg |= 2;
959 
960 	if (_flags.use16ColorMode) {
961 		if (_currentLevel != 22)
962 			flg &= 0xDFFF;
963 
964 	} else {
965 		if (_currentLevel == 22) {
966 			if (brightnessOverlay)
967 				brightnessOverlay[255] = 0;
968 		} else {
969 			flg |= 0x2000;
970 		}
971 	}
972 
973 	_shpDmX += ((_dmScaleW * fineX) >> 8);
974 	_shpDmY += ((_dmScaleH * fineY) >> 8);
975 
976 	int dH = _screen->getShapeScaledHeight(shape, _dmScaleH) >> 1;
977 
978 	if (flg & 0x1000) {
979 		if (monsterPalette)
980 			_screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x8124, monsterPalette, brightnessOverlay, 0, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH, ovl2);
981 		else
982 			_screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x124, brightnessOverlay, 0, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH, ovl2);
983 	} else {
984 		if (monsterPalette)
985 			_screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x8124, monsterPalette, brightnessOverlay, 1, _dmScaleW, _dmScaleH, ovl2);
986 		else
987 			_screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x124, brightnessOverlay, 1, _dmScaleW, _dmScaleH, ovl2);
988 	}
989 
990 	_shpDmX -= (_screen->getShapeScaledWidth(shape, _dmScaleW) >> 1);
991 	_shpDmY -= dH;
992 
993 	return brightnessOverlay;
994 }
995 
calcDrawingLayerParameters(int x1,int y1,int & x2,int & y2,uint16 & w,uint16 & h,uint8 * shape,int vflip)996 int LoLEngine::calcDrawingLayerParameters(int x1, int y1, int &x2, int &y2, uint16 &w, uint16 &h, uint8 *shape, int vflip) {
997 	calcSpriteRelPosition(_partyPosX, _partyPosY, x1, y1, _currentDirection);
998 
999 	if (y1 < 0) {
1000 		w = h = x2 = y2 = 0;
1001 		return 0;
1002 	}
1003 
1004 	int l = y1 >> 5;
1005 	y2 = _monsterScaleY[l];
1006 	x2 = ((_monsterScaleX[l] * x1) >> 8) + 200;
1007 	w = h = (_shpDmY > 120) ? 0x100 : _monsterScaleWH[_shpDmY - 56];
1008 
1009 	if (vflip)
1010 		// objects aligned to the ceiling (like the "lobsters" in the mines)
1011 		y2 = ((120 - y2) >> 1) + (_screen->getShapeScaledHeight(shape, _dmScaleH) >> 1);
1012 	else
1013 		y2 -= (_screen->getShapeScaledHeight(shape, _dmScaleH) >> 1);
1014 
1015 	return l;
1016 }
1017 
updateMonster(LoLMonster * monster)1018 void LoLEngine::updateMonster(LoLMonster *monster) {
1019 	static const uint8 flags[] = { 1, 0, 1, 3, 3, 0, 0, 3, 4, 1, 0, 0, 4, 0, 0 };
1020 	if (monster->mode > 14)
1021 		return;
1022 
1023 	int f = flags[monster->mode];
1024 	if ((monster->speedTick++ < monster->properties->speedTotalWaitTicks) && (!(f & 4)))
1025 		return;
1026 
1027 	monster->speedTick = 0;
1028 
1029 	if (monster->properties->flags & 0x40) {
1030 		monster->hitPoints += rollDice(1, 8);
1031 		if (monster->hitPoints > monster->properties->hitPoints)
1032 			monster->hitPoints = monster->properties->hitPoints;
1033 	}
1034 
1035 	if (monster->flags & 8) {
1036 		monster->destX = _partyPosX;
1037 		monster->destY = _partyPosY;
1038 	}
1039 
1040 	if (f & 2) {
1041 		if (updateMonsterAdjustBlocks(monster)) {
1042 			setMonsterMode(monster, 7);
1043 			f &= 6;
1044 		}
1045 	}
1046 
1047 	if ((f & 1) && (monster->flags & 0x10))
1048 		setMonsterMode(monster, 7);
1049 
1050 	if ((monster->mode != 11) && (monster->mode != 14)) {
1051 		if (!(_rnd.getRandomNumber(255) & 3)) {
1052 			monster->shiftStep = (monster->shiftStep + 1) & 0x0F;
1053 			checkSceneUpdateNeed(monster->block);
1054 		}
1055 	}
1056 
1057 	switch (monster->mode) {
1058 	case 0:
1059 	case 1:
1060 		// friendly mode
1061 		if (monster->flags & 0x10) {
1062 			for (int i = 0; i < 30; i++) {
1063 				if (_monsters[i].mode == 1)
1064 					setMonsterMode(&_monsters[i], 7);
1065 			}
1066 		} else if (monster->mode == 1) {
1067 			moveMonster(monster);
1068 		}
1069 		break;
1070 
1071 	case 2:
1072 		moveMonster(monster);
1073 		break;
1074 
1075 	case 3:
1076 		if (updateMonsterAdjustBlocks(monster))
1077 			setMonsterMode(monster, 7);
1078 		for (int i = 0; i < 4; i++) {
1079 			if (calcNewBlockPosition(monster->block, i) == _currentBlock)
1080 				setMonsterMode(monster, 7);
1081 		}
1082 		break;
1083 
1084 	case 4:
1085 		// straying around not tracing the party
1086 		moveStrayingMonster(monster);
1087 		break;
1088 
1089 	case 5:
1090 		// second recovery phase after delivering an attack
1091 		// monsters will rearrange positions in this phase so as to allow a maximum
1092 		// number of monsters possible attacking at the same time
1093 		_partyAwake = true;
1094 		monster->fightCurTick--;
1095 		if ((monster->fightCurTick <= 0) || (checkDrawObjectSpace(_partyPosX, _partyPosY, monster->x, monster->y) > 256) || (monster->flags & 8))
1096 			setMonsterMode(monster, 7);
1097 		else
1098 			alignMonsterToParty(monster);
1099 		break;
1100 
1101 	case 6:
1102 		// same as mode 5, but without rearranging
1103 		if (--monster->fightCurTick <= 0)
1104 			setMonsterMode(monster, 7);
1105 		break;
1106 
1107 	case 7:
1108 		// monster destination is set to current party position
1109 		// depending on the flag setting this gets updated each round
1110 		// monster can't change mode before arriving at destination and/or attacking the party
1111 		if (!chasePartyWithDistanceAttacks(monster))
1112 			chasePartyWithCloseAttacks(monster);
1113 		checkSceneUpdateNeed(monster->block);
1114 		break;
1115 
1116 	case 8:
1117 		// first recovery phase after delivering an attack
1118 		if (++monster->fightCurTick > 2) {
1119 			setMonsterMode(monster, 5);
1120 			monster->fightCurTick = (int8)((((8 << 8) / monster->properties->fightingStats[4]) * _monsterModifiers3[_monsterDifficulty]) >> 8);
1121 		}
1122 		checkSceneUpdateNeed(monster->block);
1123 		break;
1124 
1125 	case 9:
1126 		if (--monster->fightCurTick) {
1127 			chasePartyWithCloseAttacks(monster);
1128 		} else {
1129 			setMonsterMode(monster, 7);
1130 			monster->flags &= 0xFFF7;
1131 		}
1132 		break;
1133 
1134 	case 12:
1135 		checkSceneUpdateNeed(monster->block);
1136 		if (++monster->fightCurTick > 13)
1137 			runLevelScriptCustom(0x404, -1, monster->id, monster->id, 0, 0);
1138 		break;
1139 
1140 	case 13:
1141 		// monster death
1142 		if (++monster->fightCurTick > 2)
1143 			killMonster(monster);
1144 		checkSceneUpdateNeed(monster->block);
1145 		break;
1146 
1147 	case 14:
1148 		monster->damageReceived = 0;
1149 		break;
1150 
1151 	default:
1152 		break;
1153 	}
1154 
1155 	if (monster->damageReceived) {
1156 		if (monster->damageReceived & 0x8000)
1157 			monster->damageReceived &= 0x7FFF;
1158 		else
1159 			monster->damageReceived = 0;
1160 		checkSceneUpdateNeed(monster->block);
1161 	}
1162 
1163 	monster->flags &= 0xFFEF;
1164 }
1165 
moveMonster(LoLMonster * monster)1166 void LoLEngine::moveMonster(LoLMonster *monster) {
1167 	static const int8 turnPos[] = { 0, 2, 6, 6, 0, 2, 4, 4, 2, 2, 4, 6, 0, 0, 4, 6, 0 };
1168 	if (monster->x != monster->destX || monster->y != monster->destY) {
1169 		walkMonster(monster);
1170 	} else if (monster->direction != monster->destDirection) {
1171 		int i = (monster->facing << 2) + (monster->destDirection >> 1);
1172 		setMonsterDirection(monster, turnPos[i]);
1173 	}
1174 }
1175 
walkMonster(LoLMonster * monster)1176 void LoLEngine::walkMonster(LoLMonster *monster) {
1177 	if (monster->properties->flags & 0x400)
1178 		return;
1179 
1180 	int s = walkMonsterCalcNextStep(monster);
1181 
1182 	if (s == -1) {
1183 		if (walkMonsterCheckDest(monster->x, monster->y, monster, 4) != 1)
1184 			return;
1185 
1186 		_objectLastDirection ^= 4;
1187 		setMonsterDirection(monster, _objectLastDirection);
1188 	} else {
1189 		setMonsterDirection(monster, s);
1190 		if (monster->numDistAttacks) {
1191 			if (getBlockDistance(monster->block, _currentBlock) >= 2) {
1192 				if (checkForPossibleDistanceAttack(monster->block, monster->direction, 3, _currentBlock) != 5) {
1193 					if (monster->distAttackTick)
1194 						return;
1195 				}
1196 			}
1197 		}
1198 	}
1199 
1200 	int fx = 0;
1201 	int fy = 0;
1202 
1203 	getNextStepCoords(monster->x, monster->y, fx, fy, (s == -1) ? _objectLastDirection : s);
1204 	placeMonster(monster, fx, fy);
1205 }
1206 
chasePartyWithDistanceAttacks(LoLMonster * monster)1207 bool LoLEngine::chasePartyWithDistanceAttacks(LoLMonster *monster) {
1208 	if (!monster->numDistAttacks)
1209 		return false;
1210 
1211 	if (monster->distAttackTick > 0) {
1212 		monster->distAttackTick--;
1213 		return false;
1214 	}
1215 
1216 	int dir = checkForPossibleDistanceAttack(monster->block, monster->facing, 4, _currentBlock);
1217 	if (dir == 5)
1218 		return false;
1219 
1220 	int s = 0;
1221 
1222 	if (monster->flags & 0x10) {
1223 		s = monster->properties->numDistWeapons ? rollDice(1, monster->properties->numDistWeapons) : 0;
1224 	} else {
1225 		s = monster->curDistWeapon++;
1226 		if (monster->curDistWeapon >= monster->properties->numDistWeapons)
1227 			monster->curDistWeapon = 0;
1228 	}
1229 
1230 	int flyingObject = monster->properties->distWeapons[s];
1231 
1232 	if (flyingObject & 0xC000) {
1233 		if (getBlockDistance(monster->block, _currentBlock) > 1) {
1234 			int type = flyingObject & 0x4000 ? 0 : 1;
1235 			flyingObject = makeItem(flyingObject & 0x3FFF, 0, 0);
1236 
1237 			if (flyingObject) {
1238 				if (!launchObject(type, flyingObject, monster->x, monster->y, 12, dir << 1, -1, monster->id | 0x8000, 0x3F))
1239 					deleteItem(flyingObject);
1240 			}
1241 		}
1242 	} else if (!(flyingObject & 0x2000)) {
1243 		if (getBlockDistance(monster->block, _currentBlock) > 1)
1244 			return false;
1245 
1246 		if (flyingObject == 1) {
1247 			snd_playSoundEffect(147, -1);
1248 			shakeScene(10, 2, 2, 1);
1249 
1250 			for (int i = 0; i < 4; i++) {
1251 				if (!(_characters[i].flags & 1))
1252 					continue;
1253 
1254 				int item = removeCharacterItem(i, 15);
1255 				if (item)
1256 					setItemPosition(item, _partyPosX, _partyPosY, 0, 1);
1257 
1258 				inflictDamage(i, 20, 0xFFFF, 0, 2);
1259 			}
1260 
1261 		} else if (flyingObject == 3) {
1262 			// shriek
1263 			for (int i = 0; i < 30; i++) {
1264 				if (getBlockDistance(monster->block, _monsters[i].block) < 7)
1265 					setMonsterMode(monster, 7);
1266 			}
1267 			_txt->printMessage(2, "%s", getLangString(0x401A));
1268 
1269 		} else if (flyingObject == 4) {
1270 			launchMagicViper();
1271 
1272 		} else {
1273 			return false;
1274 		}
1275 	}
1276 
1277 	if (monster->numDistAttacks != 255)
1278 		monster->numDistAttacks--;
1279 
1280 	monster->distAttackTick = (monster->properties->fightingStats[4] * 8) >> 8;
1281 
1282 	return true;
1283 }
1284 
chasePartyWithCloseAttacks(LoLMonster * monster)1285 void LoLEngine::chasePartyWithCloseAttacks(LoLMonster *monster) {
1286 	if (!(monster->flags & 8)) {
1287 		int dir = calcMonsterDirection(monster->x & 0xFF00, monster->y & 0xFF00, _partyPosX & 0xFF00, _partyPosY & 0xFF00);
1288 		int x1 = _partyPosX;
1289 		int y1 = _partyPosY;
1290 
1291 		calcSpriteRelPosition(monster->x, monster->y, x1, y1, dir >> 1);
1292 
1293 		int t = (x1 < 0) ? -x1 : x1;
1294 		if (y1 <= 160 && t <= 80) {
1295 			if ((monster->direction == dir) && (monster->facing == (dir >> 1))) {
1296 				int dst = getNearestPartyMemberFromPos(monster->x, monster->y);
1297 				snd_playSoundEffect(monster->properties->sounds[1], -1);
1298 				int m = monster->id | 0x8000;
1299 				int hit = battleHitSkillTest(m, dst, 0);
1300 
1301 				if (hit) {
1302 					int mx = calcInflictableDamage(m, dst, hit);
1303 					int dmg = rollDice(2, mx);
1304 					inflictDamage(dst, dmg, m, 0, 0);
1305 					applyMonsterAttackSkill(monster, dst, dmg);
1306 				}
1307 
1308 				setMonsterMode(monster, 8);
1309 				checkSceneUpdateNeed(monster->block);
1310 
1311 			} else {
1312 				setMonsterDirection(monster, dir);
1313 				checkSceneUpdateNeed(monster->block);
1314 			}
1315 			return;
1316 		}
1317 	}
1318 
1319 	if (monster->x != monster->destX || monster->y != monster->destY) {
1320 		walkMonster(monster);
1321 	} else {
1322 		setMonsterDirection(monster, monster->destDirection);
1323 		setMonsterMode(monster, (rollDice(1, 100) <= 50) ? 4 : 3);
1324 	}
1325 }
1326 
walkMonsterCalcNextStep(LoLMonster * monster)1327 int LoLEngine::walkMonsterCalcNextStep(LoLMonster *monster) {
1328 	static const int8 walkMonsterTable1[] = { 7, -6, 5, -4, 3, -2, 1, 0 };
1329 	static const int8 walkMonsterTable2[] = { -7, 6, -5, 4, -3, 2, -1, 0 };
1330 
1331 	if (++_monsterStepCounter > 10) {
1332 		_monsterStepCounter = 0;
1333 		_monsterStepMode ^= 1;
1334 	}
1335 
1336 	const int8 *tbl = _monsterStepMode ? walkMonsterTable2 : walkMonsterTable1;
1337 
1338 	int sx = monster->x;
1339 	int sy = monster->y;
1340 	int s = monster->direction;
1341 	int d = calcMonsterDirection(monster->x, monster->y, monster->destX, monster->destY);
1342 
1343 	if (monster->flags & 8)
1344 		d ^= 4;
1345 
1346 	d = (d - s) & 7;
1347 
1348 	if (d >= 5)
1349 		s = (s - 1) & 7;
1350 	else if (d)
1351 		s = (s + 1) & 7;
1352 
1353 	for (int i = 7; i > -1; i--) {
1354 		s = (s + tbl[i]) & 7;
1355 
1356 		int fx = 0;
1357 		int fy = 0;
1358 		getNextStepCoords(sx, sy, fx, fy, s);
1359 		d = walkMonsterCheckDest(fx, fy, monster, 4);
1360 
1361 		if (!d)
1362 			return s;
1363 
1364 		if ((d != 1) || (s & 1) || (!(monster->properties->flags & 0x80)))
1365 			continue;
1366 
1367 		uint8 w = _levelBlockProperties[_monsterCurBlock].walls[(s >> 1) ^ 2];
1368 
1369 		if (_wllWallFlags[w] & 0x20) {
1370 			if (_specialWallTypes[w] == 5) {
1371 				openCloseDoor(_monsterCurBlock, 1);
1372 				return -1;
1373 			}
1374 		}
1375 
1376 		if (_wllWallFlags[w] & 8)
1377 			return -1;
1378 	}
1379 
1380 	return -1;
1381 }
1382 
checkForPossibleDistanceAttack(uint16 monsterBlock,int direction,int distance,uint16 curBlock)1383 int LoLEngine::checkForPossibleDistanceAttack(uint16 monsterBlock, int direction, int distance, uint16 curBlock) {
1384 	int mdist = getBlockDistance(curBlock, monsterBlock);
1385 
1386 	if (mdist > distance)
1387 		return 5;
1388 
1389 	int dir = calcMonsterDirection(monsterBlock & 0x1F, monsterBlock >> 5, curBlock & 0x1F, curBlock >> 5);
1390 	if ((dir & 1) || (dir != (direction << 1)))
1391 		return 5;
1392 
1393 	if (((monsterBlock & 0x1F) != (curBlock & 0x1F)) && ((monsterBlock & 0xFFE0) != (curBlock & 0xFFE0)))
1394 		return 5;
1395 
1396 	if (distance < 0)
1397 		return 5;
1398 
1399 	int p = monsterBlock;
1400 
1401 	for (int i = 0; i < distance; i++) {
1402 		p = calcNewBlockPosition(p, direction);
1403 
1404 		if (p == curBlock)
1405 			return direction;
1406 
1407 		if (_wllWallFlags[_levelBlockProperties[p].walls[direction ^ 2]] & 2)
1408 			return 5;
1409 
1410 		if (_levelBlockProperties[p].assignedObjects & 0x8000)
1411 			return 5;
1412 	}
1413 
1414 	return 5;
1415 }
1416 
walkMonsterCheckDest(int x,int y,LoLMonster * monster,int unk)1417 int LoLEngine::walkMonsterCheckDest(int x, int y, LoLMonster *monster, int unk) {
1418 	uint8 m = monster->mode;
1419 	monster->mode = 15;
1420 
1421 	int objType = checkBlockBeforeObjectPlacement(x, y, monster->properties->maxWidth, 7, monster->properties->flags & 0x1000 ? 32 : unk);
1422 
1423 	monster->mode = m;
1424 	return objType;
1425 }
1426 
getNextStepCoords(int16 srcX,int16 srcY,int & newX,int & newY,uint16 direction)1427 void LoLEngine::getNextStepCoords(int16 srcX, int16 srcY, int &newX, int &newY, uint16 direction) {
1428 	static const int8 stepAdjustX[] = { 0, 32, 32, 32, 0, -32, -32, -32 };
1429 	static const int8 stepAdjustY[] = { -32, -32, 0, 32, 32, 32, 0, -32 };
1430 
1431 	newX = (srcX + stepAdjustX[direction]) & 0x1FFF;
1432 	newY = (srcY + stepAdjustY[direction]) & 0x1FFF;
1433 }
1434 
alignMonsterToParty(LoLMonster * monster)1435 void LoLEngine::alignMonsterToParty(LoLMonster *monster) {
1436 	uint8 mdir = monster->direction >> 1;
1437 	uint16 mx = monster->x;
1438 	uint16 my = monster->y;
1439 	uint16 *pos = (mdir & 1) ? &my : &mx;
1440 	bool centered = (*pos & 0x7F) == 0;
1441 
1442 	bool posFlag = true;
1443 	if (monster->properties->maxWidth <= 63) {
1444 		if (centered) {
1445 			bool r = false;
1446 
1447 			if (monster->nextAssignedObject & 0x8000) {
1448 				r = true;
1449 			} else {
1450 				uint16 id = _levelBlockProperties[monster->block].assignedObjects;
1451 				id = (id & 0x8000) ? (id & 0x7FFF) : 0xFFFF;
1452 
1453 				if (id != monster->id) {
1454 					r = true;
1455 				} else {
1456 					for (int i = 0; i < 3; i++) {
1457 						mdir = (mdir + 1) & 3;
1458 						id = _levelBlockProperties[calcNewBlockPosition(monster->block, mdir)].assignedObjects;
1459 						id = (id & 0x8000) ? (id & 0x7FFF) : 0xFFFF;
1460 						if (id != 0xFFFF) {
1461 							r = true;
1462 							break;
1463 						}
1464 					}
1465 				}
1466 			}
1467 
1468 			if (r)
1469 				posFlag = false;
1470 		} else {
1471 			posFlag = false;
1472 		}
1473 	}
1474 
1475 	if (centered && posFlag)
1476 		return;
1477 
1478 	if (posFlag) {
1479 		if (*pos & 0x80)
1480 			*pos -= 32;
1481 		else
1482 			*pos += 32;
1483 	} else {
1484 		if (*pos & 0x80)
1485 			*pos += 32;
1486 		else
1487 			*pos -= 32;
1488 	}
1489 
1490 	if (walkMonsterCheckDest(mx, my, monster, 4))
1491 		return;
1492 
1493 	int fx = _partyPosX;
1494 	int fy = _partyPosY;
1495 	calcSpriteRelPosition(mx, my, fx, fy, monster->direction >> 1);
1496 
1497 	if (fx < 0)
1498 		fx = -fx;
1499 
1500 	if (fy > 160 || fx > 80)
1501 		return;
1502 
1503 	placeMonster(monster, mx, my);
1504 }
1505 
moveStrayingMonster(LoLMonster * monster)1506 void LoLEngine::moveStrayingMonster(LoLMonster *monster) {
1507 	int x = 0;
1508 	int y = 0;
1509 
1510 	if (monster->fightCurTick) {
1511 		uint8 d = (monster->direction - monster->fightCurTick) & 6;
1512 		uint8 id = monster->id;
1513 
1514 		for (int i = 0; i < 7; i++) {
1515 			getNextStepCoords(monster->x, monster->y, x, y, d);
1516 
1517 			if (!walkMonsterCheckDest(x, y, monster, 4)) {
1518 				placeMonster(monster, x, y);
1519 				setMonsterDirection(monster, d);
1520 				if (!i) {
1521 					if (++id > 3)
1522 						monster->fightCurTick = 0;
1523 				}
1524 				return;
1525 			}
1526 
1527 			d = (d + monster->fightCurTick) & 6;
1528 		}
1529 		setMonsterMode(monster, 3);
1530 
1531 	} else {
1532 		monster->direction &= 6;
1533 		getNextStepCoords(monster->x, monster->y, x, y, monster->direction);
1534 		if (!walkMonsterCheckDest(x, y, monster, 4)) {
1535 			placeMonster(monster, x, y);
1536 		} else {
1537 			monster->fightCurTick = _rnd.getRandomBit() ? 2 : -2;
1538 			monster->direction = (monster->direction + monster->fightCurTick) & 6;
1539 		}
1540 	}
1541 }
1542 
killMonster(LoLMonster * monster)1543 void LoLEngine::killMonster(LoLMonster *monster) {
1544 	setMonsterMode(monster, 14);
1545 	monsterDropItems(monster);
1546 	checkSceneUpdateNeed(monster->block);
1547 
1548 	uint8 w = _levelBlockProperties[monster->block].walls[0];
1549 	uint16 f = _levelBlockProperties[monster->block].flags;
1550 	if (_wllVmpMap[w] == 0 && _wllShapeMap[w] == 0 && !(f & 0x40) && !(monster->properties->flags & 0x1000))
1551 		_levelBlockProperties[monster->block].flags |= 0x80;
1552 
1553 	placeMonster(monster, 0, 0);
1554 }
1555 
1556 } // End of namespace Kyra
1557 
1558 #endif // ENABLE_LOL
1559