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_EOB
24 
25 #include "kyra/engine/eobcommon.h"
26 #include "kyra/script/script_eob.h"
27 #include "kyra/resource/resource.h"
28 #include "kyra/engine/timer.h"
29 
30 #include "common/system.h"
31 
32 
33 namespace Kyra {
34 
loadMonsterShapes(const char * filename,int monsterIndex,bool hasDecorations,int encodeTableIndex)35 void EoBCoreEngine::loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) {
36 	_screen->loadShapeSetBitmap(filename, 3, 3);
37 	const uint16 *enc = &_encodeMonsterShpTable[encodeTableIndex << 2];
38 
39 	for (int i = 0; i < 6; i++, enc += 4)
40 		_monsterShapes[monsterIndex + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaMappingDefault);
41 
42 	generateMonsterPalettes(filename, monsterIndex);
43 
44 	if (hasDecorations) {
45 		Common::SeekableReadStream *s = _res->createReadStream(Common::String::format("%s.DCR", filename));
46 		if (s)
47 			loadMonsterDecoration(s, monsterIndex);
48 		delete s;
49 	}
50 
51 	_screen->_curPage = 0;
52 }
53 
releaseMonsterShapes(int first,int num)54 void EoBCoreEngine::releaseMonsterShapes(int first, int num) {
55 	for (int i = first; i < first + num; i++) {
56 		delete[] _monsterShapes[i];
57 		_monsterShapes[i] = 0;
58 		delete[] _monsterDecorations[i].shp;
59 		_monsterDecorations[i].shp = 0;
60 	}
61 }
62 
loadActiveMonsterData(const uint8 * data,int level)63 const uint8 *EoBCoreEngine::loadActiveMonsterData(const uint8 *data, int level) {
64 	static const uint8 intervals[4] = { 35, 30, 25, 0 };
65 	for (uint8 p = *data++; p != 0xFF; p = *data++) {
66 		uint8 v = *data++;
67 		if (_flags.platform == Common::kPlatformSegaCD) {
68 			assert(v < ARRAYSIZE(intervals));
69 			v = intervals[v];
70 		}
71 		_timer->setCountdown(0x20 + (p << 1), v);
72 		_timer->setCountdown(0x21 + (p << 1), v);
73 	}
74 
75 	uint32 ct = _system->getMillis();
76 	for (int i = 0x20; i < 0x24; i++) {
77 		int32 del = _timer->getDelay(i);
78 		_timer->setNextRun(i, (i & 1) ? ct + (del >> 1) * _tickLength : ct + del * _tickLength);
79 	}
80 	_timer->resetNextRun();
81 
82 	if (_hasTempDataFlags & (1 << (level - 1)))
83 		return data + 420;
84 
85 	memset(_monsters, 0, 30 * sizeof(EoBMonsterInPlay));
86 
87 	for (int i = 0; i < 30; i++, data += 14) {
88 		if (*data == 0xFF)
89 			continue;
90 
91 		initMonster(data[0], data[1], READ_LE_UINT16(&data[2]), data[4], (int8)data[5], data[6], data[7], data[8], data[9], READ_LE_UINT16(&data[10]), READ_LE_UINT16(&data[12]));
92 		_monsters[data[0]].flags |= 0x40;
93 	}
94 
95 	return data;
96 }
97 
initMonster(int index,int unit,uint16 block,int pos,int dir,int type,int shpIndex,int mode,int i,int randItem,int fixedItem)98 void EoBCoreEngine::initMonster(int index, int unit, uint16 block, int pos, int dir, int type, int shpIndex, int mode, int i, int randItem, int fixedItem) {
99 	EoBMonsterInPlay *m = &_monsters[index];
100 	EoBMonsterProperty *p = &_monsterProps[type];
101 	memset(m, 0, sizeof(EoBMonsterInPlay));
102 
103 	if (!block)
104 		return;
105 
106 	unit <<= 1;
107 	if (index & 1)
108 		unit++;
109 
110 	m->stepsTillRemoteAttack = _flags.gameID == GI_EOB2 ? rollDice(1, 3, 0) : 5;
111 	m->type = type;
112 	m->numRemoteAttacks = p->numRemoteAttacks;
113 	m->curRemoteWeapon = 0;
114 	m->unit = unit;
115 	m->pos = pos;
116 	m->shpIndex = shpIndex;
117 	m->mode = mode;
118 	m->spellStatusLeft = i;
119 	m->dir = dir;
120 	m->palette = _flags.gameID == GI_EOB2 ? (index % 3) : 0;
121 	m->hitPointsCur = m->hitPointsMax = _flags.gameID == GI_EOB2 ? rollDice(p->hpDcTimes, p->hpDcPips, p->hpDcBase) : (p->level == -1 ? rollDice(1, 4, 0) : rollDice(p->level, 8, 0));
122 	m->randItem = randItem;
123 	m->fixedItem = fixedItem;
124 	m->sub = _currentSub;
125 
126 	placeMonster(m, block, dir);
127 }
128 
placeMonster(EoBMonsterInPlay * m,uint16 block,int dir)129 void EoBCoreEngine::placeMonster(EoBMonsterInPlay *m, uint16 block, int dir) {
130 	if (block != 0xFFFF) {
131 		checkSceneUpdateNeed(m->block);
132 		if (_levelBlockProperties[m->block].flags & 7) {
133 			_levelBlockProperties[m->block].flags--;
134 			if (_flags.gameID == GI_EOB2)
135 				runLevelScript(m->block, 0x400);
136 		}
137 		m->block = block;
138 		_levelBlockProperties[block].flags++;
139 		if (_flags.gameID == GI_EOB2)
140 			runLevelScript(m->block, 0x200);
141 	}
142 
143 	if (dir != -1) {
144 		m->dir = dir;
145 		block = m->block;
146 	}
147 
148 	checkSceneUpdateNeed(block);
149 }
150 
killMonster(EoBMonsterInPlay * m,bool giveExperience)151 void EoBCoreEngine::killMonster(EoBMonsterInPlay *m, bool giveExperience) {
152 	m->hitPointsCur = -1;
153 	int pos = (m->pos == 4) ? rollDice(1, 4, -1) : m->pos;
154 
155 	if (m->randItem) {
156 		if (rollDice(1, _flags.gameID == GI_EOB1 ? 2 : 10, 0) == 1)
157 			setItemPosition((Item *)&_levelBlockProperties[m->block & 0x3FF].drawObjects, m->block, duplicateItem(m->randItem), pos);
158 	}
159 
160 	if (m->fixedItem)
161 		setItemPosition((Item *)&_levelBlockProperties[m->block & 0x3FF].drawObjects, m->block, duplicateItem(m->fixedItem), pos);
162 
163 	if (giveExperience)
164 		increasePartyExperience(_monsterProps[m->type].experience);
165 
166 	if (_totalEnemiesKilled < 0xFFFF)
167 		_totalEnemiesKilled++;
168 
169 	if (killMonsterExtra(m)) {
170 		placeMonster(m, 0, -1);
171 
172 		if (m->mode == 8)
173 			updateAttackingMonsterFlags();
174 	}
175 }
176 
countSpecificMonsters(int type)177 int EoBCoreEngine::countSpecificMonsters(int type) {
178 	int res = 0;
179 	for (int i = 0; i < 30; i++) {
180 		if (_monsters[i].type != type || _monsters[i].sub != _currentSub || _monsters[i].hitPointsCur < 0)
181 			continue;
182 		res++;
183 	}
184 	return res;
185 }
186 
updateAttackingMonsterFlags()187 void EoBCoreEngine::updateAttackingMonsterFlags() {
188 	EoBMonsterInPlay *m2 = 0;
189 	for (EoBMonsterInPlay *m = _monsters; m < &_monsters[30]; m++) {
190 		if (m->mode != 8)
191 			continue;
192 		m->mode = 0;
193 		m->dest = _currentBlock;
194 		m2 = m;
195 	}
196 
197 	if (!m2)
198 		return;
199 
200 	if (m2->type == 7)
201 		setScriptFlags(4);
202 
203 	if (m2->type == 12)
204 		setScriptFlags(0x800);
205 }
206 
getMonstersOnBlockPositions(uint16 block)207 const int8 *EoBCoreEngine::getMonstersOnBlockPositions(uint16 block) {
208 	memset(_monsterBlockPosArray, -1, sizeof(_monsterBlockPosArray));
209 	for (int8 i = 0; i < 30; i++) {
210 		if (_monsters[i].block != block)
211 			continue;
212 		assert(_monsters[i].pos < sizeof(_monsterBlockPosArray));
213 		_monsterBlockPosArray[_monsters[i].pos] = i;
214 	}
215 	return _monsterBlockPosArray;
216 }
217 
getClosestMonster(int charIndex,int block)218 int EoBCoreEngine::getClosestMonster(int charIndex, int block) {
219 	const int8 *pos = getMonstersOnBlockPositions(block);
220 	if (pos[4] != -1)
221 		return pos[4];
222 
223 	const uint8 *p = &_monsterProximityTable[(_currentDirection << 3) + ((charIndex & 1) << 2)];
224 	for (int i = 0; i < 4; i++) {
225 		if (pos[p[i]] != -1)
226 			return pos[p[i]];
227 	}
228 	return -1;
229 }
230 
blockHasMonsters(uint16 block)231 bool EoBCoreEngine::blockHasMonsters(uint16 block) {
232 	return _levelBlockProperties[block].flags & 7 ? true : false;
233 }
234 
isMonsterOnPos(EoBMonsterInPlay * m,uint16 block,int pos,int checkPos4)235 bool EoBCoreEngine::isMonsterOnPos(EoBMonsterInPlay *m, uint16 block, int pos, int checkPos4) {
236 	return (m->block == block && (m->pos == pos || (m->pos == 4 && checkPos4))) ? true : false;
237 }
238 
findBlockMonsters(uint16 block,int pos,int dir,int blockDamage,int singleTargetCheckAdjacent)239 const int16 *EoBCoreEngine::findBlockMonsters(uint16 block, int pos, int dir, int blockDamage, int singleTargetCheckAdjacent) {
240 	static const uint8 cpos4[] = { 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1 };
241 	int include4 = (pos < 4) ? cpos4[(dir << 2) + pos] : 1;
242 	int16 *dst = _foundMonstersArray;
243 
244 	if (blockDamage) {
245 		for (int i = 0; i < 30; i++) {
246 			if (_monsters[i].block == block && (_monsters[i].pos != 4 || include4))
247 				*dst++ = i;
248 		}
249 
250 	} else if (singleTargetCheckAdjacent) {
251 		int16 r = -1;
252 		int f = 5;
253 
254 		for (int i = 0; i < 30; i++) {
255 			const uint8 *tbl = &_findBlockMonstersTable[(dir << 4) + (pos << 2)];
256 
257 			if (_monsters[i].block != block)
258 				continue;
259 
260 			if (_monsters[i].pos == pos) {
261 				r = i;
262 				break;
263 			}
264 
265 			for (int ii = 0; ii < 4; ii++) {
266 				if (_monsters[i].pos == tbl[ii] && ii < f) {
267 					f = ii;
268 					r = i;
269 				}
270 			}
271 		}
272 
273 		*dst++ = r;
274 
275 	} else {
276 		for (int i = 0; i < 30; i++) {
277 			if (isMonsterOnPos(&_monsters[i], block, pos, include4))
278 				*dst++ = i;
279 		}
280 	}
281 
282 	*dst = -1;
283 	return _foundMonstersArray;
284 }
285 
drawBlockObject(int flipped,int page,const uint8 * shape,int x,int y,int sd,uint8 * ovl)286 void EoBCoreEngine::drawBlockObject(int flipped, int page, const uint8 *shape, int x, int y, int sd, uint8 *ovl) {
287 	const ScreenDim *d = _screen->getScreenDim(sd);
288 	if (_flags.gameID == GI_EOB1)
289 		x &= ~1;
290 
291 	_screen->drawShape(page, shape, x - (d->sx << 3) + _shapeShakeOffsetX, y - d->sy + _shapeShakeOffsetY, sd, flipped | (ovl ? 2 : 0), ovl);
292 }
293 
drawMonsterShape(const uint8 * shape,int x,int y,int flipped,int flags,int palIndex)294 void EoBCoreEngine::drawMonsterShape(const uint8 *shape, int x, int y, int flipped, int flags, int palIndex) {
295 	uint8 *ovl = 0;
296 
297 	if (flags & 2)
298 		ovl = _monsterFlashOverlay;
299 	else if (_flags.gameID == GI_EOB2 && flags & 0x20)
300 		ovl = _monsterStoneOverlay;
301 	else if (palIndex != -1 && _flags.platform != Common::kPlatformAmiga)
302 		ovl = _monsterPalettes[palIndex];
303 
304 	drawBlockObject(flipped, 2, shape, x, y, 5, ovl);
305 }
306 
flashMonsterShape(EoBMonsterInPlay * m)307 void EoBCoreEngine::flashMonsterShape(EoBMonsterInPlay *m) {
308 	disableSysTimer(2);
309 	_flashShapeTimer = 0;
310 	drawScene(1);
311 	m->flags &= 0xFD;
312 	_flashShapeTimer  = _system->getMillis() + _tickLength;
313 	enableSysTimer(2);
314 
315 	_sceneUpdateRequired = true;
316 }
317 
updateAllMonsterShapes()318 void EoBCoreEngine::updateAllMonsterShapes() {
319 	drawScene(1);
320 	bool updateShp = false;
321 
322 	for (EoBMonsterInPlay *m = _monsters; m < &_monsters[30]; m++) {
323 		if (m->flags & 2) {
324 			m->flags &= ~2;
325 			updateShp = true;
326 			if (m->hitPointsCur <= 0)
327 				killMonster(m, true);
328 		}
329 	}
330 
331 	if (updateShp) {
332 		_sceneUpdateRequired = true;
333 		_flashShapeTimer = _system->getMillis() + _tickLength;
334 	} else {
335 		_sceneUpdateRequired = false;
336 	}
337 	_preventMonsterFlash = false;
338 }
339 
drawBlockItems(int index)340 void EoBCoreEngine::drawBlockItems(int index) {
341 	uint16 o = _visibleBlocks[index]->drawObjects;
342 	uint8 w = _visibleBlocks[index]->walls[_sceneDrawVarDown];
343 	uint8 flg = (index == 16) ? 0x80 : _wllWallFlags[w];
344 
345 	if (_wllVmpMap[w] && !(flg & 0x80))
346 		return;
347 
348 	uint16 o2 = o = _items[o].next;
349 	static const int8 itemPosYNiche[] = { 0x25, 0x31, 0x38, 0x00 };
350 	static const int8 itemPosFin[] = { 0, -2, 1, -1, 2, 0, 1, -1 };
351 	int tile2 = 0;
352 
353 	for (bool loop = true; o != o2 || loop; ) {
354 		EoBItem *itm = &_items[o];
355 		if (itm->pos == 8 || itm->pos < 4) {
356 			tile2 = -1;
357 
358 			uint8 ps = (itm->pos == 8) ? 4 : _dscItemPosIndex[(_currentDirection << 2) + (itm->pos & 3)];
359 			uint16 wo = (index * 5 + ps) << 1;
360 			int x = _dscShapeCoords[wo] + 88;
361 			int y = 0;
362 
363 			if (itm->pos == 8) {
364 				x = _dscItemShpX[index];
365 
366 				if (_flags.gameID == Common::kPlatformSegaCD && _currentLevel == 12 && (_currentBlock & 0x1F) < 17 && (_currentBlock >> 5) < 20) {
367 					if (index == 8)
368 						x += 20;
369 					else if (index == 10)
370 						x -= 20;
371 				}
372 
373 				y = itemPosYNiche[_dscDimMap[index]];
374 				ps = 0;
375 			} else {
376 				y = _dscShapeCoords[wo + 1] + 124;
377 			}
378 
379 			int8 scaleSteps = (int8)_dscItemScaleIndex[(_dscDimMap[index] << 2) + ps];
380 			if ((flg & 8) && ps < 2 && scaleSteps) {
381 				tile2 = _dscItemTileIndex[index];
382 				if (tile2 != -1)
383 					setLevelShapesDim(tile2, _shpDmX1, _shpDmX2, 5);
384 				y -= 4;
385 			}
386 
387 			if (scaleSteps >= 0) {
388 				const uint8 *shp = 0;
389 				if (_flags.gameID == GI_EOB2 || scaleSteps == 0)
390 					shp = _screen->scaleShape(_dscItemShapeMap[itm->icon] < _numLargeItemShapes ? _largeItemShapes[_dscItemShapeMap[itm->icon]] : (_dscItemShapeMap[itm->icon] < 15 ? 0 : _smallItemShapes[_dscItemShapeMap[itm->icon] - 15]), scaleSteps);
391 				else
392 					shp = _dscItemShapeMap[itm->icon] < _numLargeItemShapes ? _largeItemShapesScl[scaleSteps - 1][_dscItemShapeMap[itm->icon]] : (_dscItemShapeMap[itm->icon] < 15 ? 0 : _smallItemShapesScl[scaleSteps - 1][_dscItemShapeMap[itm->icon] - 15]);
393 
394 				x = x + (itemPosFin[o & 7] << 1) - ((shp[2] << 3) >> 1);
395 				y -= shp[1];
396 
397 				if (itm->pos != 8)
398 					y += itemPosFin[(o >> 1) & 7];
399 
400 				drawBlockObject(0, 2, shp, x, y, 5);
401 				_screen->setShapeFadingLevel(0);
402 			}
403 		}
404 
405 		o = itm->next;
406 		loop = false;
407 		if (tile2 != -1)
408 			setLevelShapesDim(index, _shpDmX1, _shpDmX2, 5);
409 	}
410 }
411 
drawDoor(int index)412 void EoBCoreEngine::drawDoor(int index) {
413 	int s = _visibleBlocks[index]->walls[_sceneDrawVarDown];
414 
415 	if (_flags.gameID == GI_EOB1 && s == 0x85)
416 		s = 0;
417 
418 	if (s >= _dscDoorShpIndexSize)
419 		return;
420 
421 	int type = _dscDoorShpIndex[s];
422 	int d = _dscDimMap[index];
423 	int w = _dscShapeCoords[(index * 5 + 4) << 1];
424 
425 	int x = 88 + w;
426 	int y = 0;
427 
428 	int16 y1 = 0;
429 	int16 y2 = 0;
430 	setDoorShapeDim(index, y1, y2, 5);
431 	drawDoorIntern(type, index, x, y, w, s, d, y1, y2);
432 	drawLevelModifyScreenDim(5, _shpDmX1, 0, _shpDmX2, 15);
433 }
434 
drawMonsters(int index)435 void EoBCoreEngine::drawMonsters(int index) {
436 	static const uint8 distMap[] = { 2, 1, 0, 4 };
437 	// SEGA: 2, 1, 0, 3 ??
438 	static const uint8 yAdd[] = { 20, 12, 4, 4, 2, 0, 0 };
439 
440 	int blockDistance = distMap[_dscDimMap[index]];
441 
442 	uint16 bl = _visibleBlockIndex[index];
443 	if (!bl)
444 		return;
445 
446 	int drawObjDirIndex = _currentDirection * 5;
447 	int cDirOffs = _currentDirection << 2;
448 
449 	EoBMonsterInPlay *drawObj[5];
450 	memset(drawObj, 0, 5 * sizeof(EoBMonsterInPlay *));
451 
452 	for (int i = 0; i < 30; i++) {
453 		if (_monsters[i].block != bl)
454 			continue;
455 		drawObj[_drawObjPosIndex[drawObjDirIndex + _monsters[i].pos]] = &_monsters[i];
456 	}
457 
458 	for (int i = 0; i < 5; i++) {
459 		EoBMonsterInPlay *d = drawObj[i];
460 		if (!d)
461 			continue;
462 
463 		EoBMonsterProperty *p = &_monsterProps[d->type];
464 
465 		if (_flags.gameID == GI_EOB2 && (p->capsFlags & 0x100) && !(_partyEffectFlags & 0x220) && !(d->flags & 2))
466 			continue;
467 
468 		int f = (d->animStep << 4) + cDirOffs + d->dir;
469 		f = (p->capsFlags & 2) ? _monsterFrmOffsTable1[f] : _monsterFrmOffsTable2[f];
470 
471 		if (!blockDistance && d->curAttackFrame < 0)
472 			f = d->curAttackFrame + 7;
473 
474 		int subFrame = ABS(f);
475 		int shpIndex = d->shpIndex ? 18 : 0;
476 		int palIndex = d->palette ? ((((shpIndex == 18) ? subFrame + 5 : subFrame - 1) << 1) + (d->palette - 1)) : -1;
477 		int shpOffs = subFrame + shpIndex - 1;
478 		int xAdd2 = 0;
479 		int yAdd2 = 0;
480 
481 		int v30 = (subFrame == 1 || subFrame > 3) ? 1 : 0;
482 		int posOffs = (d->pos == 4) ? 4 : _dscItemPosIndex[cDirOffs + d->pos];
483 		int posIndex = (index * 5 + posOffs) << 1;
484 
485 		if (_flags.platform == Common::kPlatformSegaCD) {
486 			if (d->curAttackFrame < 0)
487 				subFrame = 5;
488 			else if (subFrame >= 3)
489 				subFrame--;
490 
491 			if (d->animType != subFrame) {
492 				d->animType = subFrame;
493 				d->animProgress = 0;
494 			}
495 		} else if (d->curAttackFrame < 0) {
496 			d->curAttackFrame++;
497 		}
498 
499 		for (int numFrames = 1; numFrames; --numFrames) {
500 			if (_flags.platform == Common::kPlatformSegaCD) {
501 				int temp = 0;
502 				const uint8 *frm = _staticres->loadRawData(kEoB1MonsterAnimFrames00 + d->type * 5 + subFrame - 1, temp) + ((d->animProgress++) << 2);
503 				shpOffs = shpIndex + (frm[0] & 0x3F);
504 				numFrames += ((frm[0] >> 6) & 1);
505 				xAdd2 = (int8)frm[1];
506 				yAdd2 = (int8)frm[2];
507 				if (frm[4] == 0xFE) {
508 					d->animProgress = 0;
509 				} else if (frm[4] == 0xFF) {
510 					d->curAttackFrame = 0;
511 					d->animType = 0;
512 				}
513 			}
514 
515 			const uint8 *shp = _screen->scaleShape(_monsterShapes[shpOffs], blockDistance);
516 
517 			int x = _dscShapeCoords[posIndex] + 88;
518 			int y = _dscShapeCoords[posIndex + 1] + 127;
519 
520 			if (p->u30 == 1) {
521 				if (v30) {
522 					if (_flags.gameID == GI_EOB2)
523 						posIndex = ((posIndex >> 1) - posOffs) << 1;
524 					y = _dscShapeCoords[posIndex + 1] + 127 + yAdd[blockDistance + ((posOffs == 4 || _flags.gameID == GI_EOB1) ? 0 : 3)];
525 				} else {
526 					if (_flags.gameID == GI_EOB2)
527 						posIndex = ((posIndex >> 1) - posOffs + 4) << 1;
528 					x = _dscShapeCoords[posIndex] + 88;
529 				}
530 			}
531 
532 			int w = shp[2] << 3;
533 			int h = shp[1];
534 
535 			x = x - (w >> 1) + (d->idleAnimState >> 4) + xAdd2;
536 			y = y - h + (d->idleAnimState & 0x0F) + yAdd2;
537 
538 			drawMonsterShape(shp, x, y, f >= 0 ? 0 : 1, d->flags, palIndex);
539 
540 			if (_flags.gameID == GI_EOB2) {
541 				for (int ii = 0; ii < 3; ii++) {
542 					if (!p->decorations[ii])
543 						continue;
544 
545 					SpriteDecoration *dcr = &_monsterDecorations[(p->decorations[ii] - 1) * 6 + subFrame + shpIndex - 1];
546 
547 					if (!dcr->shp)
548 						continue;
549 
550 					shp = _screen->scaleShape(dcr->shp, blockDistance);
551 					int dx = dcr->x;
552 					int dy = dcr->y;
553 
554 					for (int iii = 0; iii < blockDistance; iii++) {
555 						dx = (dx << 1) / 3;
556 						dy = (dy << 1) / 3;
557 					}
558 
559 					drawMonsterShape(shp, x + ((f < 0) ? (w - dx - (shp[2] << 3)) : dx), y + dy, f >= 0 ? 0 : 1, d->flags, -1);
560 				}
561 			}
562 			_screen->setShapeFadingLevel(0);
563 		}
564 	}
565 }
566 
drawWallOfForce(int index)567 void EoBCoreEngine::drawWallOfForce(int index) {
568 	int d = _dscDimMap[index];
569 	assert(d < 3);
570 	int dH = _wallOfForceDsNumH[d];
571 	int dW = _wallOfForceDsNumW[d];
572 	int y = _wallOfForceDsY[d];
573 	int shpId = _wallOfForceShpId[d] + _teleporterPulse;
574 	int h = _wallOfForceShapes[shpId][1];
575 	int w = _wallOfForceShapes[shpId][2] << 3;
576 
577 	for (int i = 0; i < dH; i++) {
578 		int x = _wallOfForceDsX[index];
579 		for (int ii = 0; ii < dW; ii++) {
580 			drawBlockObject(0, 2, _wallOfForceShapes[shpId], x, y, 5);
581 			x += w;
582 		}
583 		y += h;
584 		shpId ^= 1;
585 	}
586 }
587 
drawFlyingObjects(int index)588 void EoBCoreEngine::drawFlyingObjects(int index) {
589 	LevelBlockProperty *bl = _visibleBlocks[index];
590 	int blockIndex = _visibleBlockIndex[index];
591 	int w = bl->walls[_sceneDrawVarDown];
592 
593 	if (_wllVmpMap[w] && !(_wllWallFlags[w] & 0x80))
594 		return;
595 
596 	EoBFlyingObject *drawObj[5];
597 	memset(drawObj, 0, 5 * sizeof(EoBFlyingObject *));
598 
599 	for (int i = 0; i < 10; i++) {
600 		if (!_flyingObjects[i].enable || blockIndex != _flyingObjects[i].curBlock)
601 			continue;
602 		drawObj[_drawObjPosIndex[_currentDirection * 5 + (_flyingObjects[i].curPos & 3)]] = &_flyingObjects[i];
603 	}
604 
605 	for (int i = 0; i < 5; i++) {
606 		EoBFlyingObject *fo = drawObj[i];
607 		if (!fo)
608 			continue;
609 
610 		int p = _dscItemPosIndex[(_currentDirection << 2) + (fo->curPos & 3)];
611 		int x = _dscShapeCoords[(index * 5 + p) << 1] + 88;
612 		int y = 39;
613 
614 		int sclValue = _flightObjSclIndex[(index << 2) + p];
615 		int flipped = 0;
616 
617 		if (sclValue < 0) {
618 			_screen->setShapeFadingLevel(0);
619 			continue;
620 		}
621 
622 		const uint8 *shp = 0;
623 		bool noFade = false;
624 
625 		if (fo->enable == 1) {
626 			int shpIx = _dscItemShapeMap[_items[fo->item].icon];
627 			int dirOffs = (fo->direction == _currentDirection) ? 0 : ((fo->direction == (_currentDirection ^ 2)) ? 1 : -1);
628 
629 			if (dirOffs == -1 || _flightObjShpMap[shpIx] == -1) {
630 				if (_flags.gameID == GI_EOB2 || sclValue == 0)
631 					shp = shpIx < _numLargeItemShapes ? _largeItemShapes[shpIx] : (shpIx < 15 ? 0 : _smallItemShapes[shpIx - 15]);
632 				else
633 					shp = shpIx < _numLargeItemShapes ? _largeItemShapesScl[sclValue - 1][shpIx] : (shpIx < 15 ? 0 : _smallItemShapesScl[sclValue - 1][shpIx - 15]);
634 				flipped = fo->direction == ((_currentDirection + 1) & 3) ? 1 : 0;
635 			} else {
636 				if (_flags.gameID == GI_EOB2 || sclValue == 0)
637 					shp = (_flightObjShpMap[shpIx] + dirOffs) < _numThrownItemShapes ? _thrownItemShapes[_flightObjShpMap[shpIx] + dirOffs] : _spellShapes[_flightObjShpMap[shpIx - _numThrownItemShapes] + dirOffs];
638 				else
639 					shp = (_flightObjShpMap[shpIx] + dirOffs) < _numThrownItemShapes ? _thrownItemShapesScl[sclValue - 1][_flightObjShpMap[shpIx] + dirOffs] : 0;
640 				flipped = _flightObjFlipIndex[(fo->direction << 2) + (fo->curPos & 3)];
641 			}
642 
643 		} else {
644 			noFade = true;
645 			if (_flags.gameID == GI_EOB2 || sclValue == 0)
646 				shp = (fo->objectType < _numThrownItemShapes) ? _thrownItemShapes[fo->objectType] : _spellShapes[fo->objectType - _numThrownItemShapes];
647 			else
648 				shp = (fo->objectType < _numThrownItemShapes) ? _thrownItemShapesScl[sclValue - 1][fo->objectType] : 0;
649 			flipped = _flightObjFlipIndex[(fo->direction << 2) + (fo->curPos & 3)];
650 
651 			if (fo->flags & 0x40) {
652 				x = _dscShapeCoords[(index * 5 + 4) << 1] + 88;
653 				y = 44;
654 			}
655 		}
656 
657 		assert(shp);
658 
659 		if (_flags.gameID == GI_EOB2 || sclValue == 0)
660 			shp = _screen->scaleShape(shp, sclValue);
661 
662 		if (noFade) {
663 			_screen->setShapeFadingLevel(0);
664 			noFade = false;
665 		}
666 
667 		x -= (shp[2] << 2);
668 		y -= (y == 44 ? (shp[1] >> 1) : shp[1]);
669 
670 		drawBlockObject(flipped, 2, shp, x, y, 5);
671 		_screen->setShapeFadingLevel(0);
672 	}
673 }
674 
drawTeleporter(int index)675 void EoBCoreEngine::drawTeleporter(int index) {
676 	static const uint8 telprtX[] = { 0x28, 0x1C, 0x12 };
677 	static const uint8 telprtY[] = { 0x0D, 0x15, 0x1A };
678 
679 	int t = 2 - _dscDimMap[index];
680 	if (t < 0)
681 		return;
682 
683 	int16 x1 = _dscItemShpX[index] - telprtX[t];
684 	int16 y1 = telprtY[t];
685 
686 	for (int i = 0; i < 2; i++) {
687 		int16 x2 = 0;
688 		int16 y2 = 0;
689 		int d = (t << 1) + i;
690 		if (!d)
691 			x2 = y2 = -4;
692 
693 		const uint8 *shp = _teleporterShapes[d ^ _teleporterPulse];
694 
695 		for (int ii = 0; ii < 13; ii++)
696 			drawBlockObject(0, 2, shp, x1 + x2 + _teleporterShapeCoords[d * 26 + ii * 2], y1 + y2 + _teleporterShapeCoords[d * 26 + ii * 2 + 1], 5);
697 	}
698 }
699 
updateMonsters(int unit)700 void EoBCoreEngine::updateMonsters(int unit) {
701 	for (int i = 0; i < 30; i++) {
702 		EoBMonsterInPlay *m = &_monsters[i];
703 		if (m->unit == unit) {
704 			if (m->hitPointsCur <= 0 || m->flags & 0x20)
705 				continue;
706 			if (m->directionChanged) {
707 				m->directionChanged = 0;
708 				continue;
709 			}
710 
711 			updateMonsterDest(m);
712 
713 			if (m->mode > 0)
714 				updateMonsterAttackMode(m);
715 
716 			switch (m->mode) {
717 			case 0:
718 				updateMoveMonster(m);
719 				break;
720 			case 1:
721 				updateMonsterFollowPath(m, 2);
722 				break;
723 			case 2:
724 				updateMonsterFollowPath(m, -1);
725 				break;
726 			case 3:
727 				updateMonsterFollowPath(m, 1);
728 				break;
729 			case 5:
730 				updateMonstersStraying(m, -1);
731 				break;
732 			case 6:
733 				updateMonstersStraying(m, 1);
734 				break;
735 			case 7:
736 			case 10:
737 				updateMonstersSpellStatus(m);
738 				break;
739 			default:
740 				break;
741 			}
742 
743 			if (m->mode != 4 && m->mode != 7 && m->mode != 8)
744 				m->animStep ^= 1;
745 
746 			if (_monsterProps[m->type].u30 == 1)
747 				setBlockMonsterDirection(m->block, m->dir);
748 		}
749 	}
750 	checkFlyingObjects();
751 }
752 
updateMonsterDest(EoBMonsterInPlay * m)753 void EoBCoreEngine::updateMonsterDest(EoBMonsterInPlay *m) {
754 	if (m->mode >= 7 && m->mode <= 10)
755 		return;
756 	int dist = getBlockDistance(m->block, _currentBlock);
757 	if (dist >= 4)
758 		return;
759 
760 	int s = getNextMonsterDirection(m->block, _currentBlock) - (m->dir << 1) - 3;
761 
762 	if (s < 0)
763 		s += 8;
764 
765 	if (s <= 2 && dist >= 2)
766 		return;
767 
768 	m->mode = 0;
769 	m->dest = _currentBlock;
770 }
771 
updateMonsterAttackMode(EoBMonsterInPlay * m)772 void EoBCoreEngine::updateMonsterAttackMode(EoBMonsterInPlay *m) {
773 	if (!(m->flags & 1) || m->mode == 10)
774 		return;
775 	if (m->mode == 8) {
776 		turnFriendlyMonstersHostile();
777 		return;
778 	}
779 	m->mode = 0;
780 	m->dest = _currentBlock;
781 }
782 
updateAllMonsterDests()783 void EoBCoreEngine::updateAllMonsterDests() {
784 	for (int i = 0; i < 30; i++)
785 		updateMonsterDest(&_monsters[i]);
786 }
787 
turnFriendlyMonstersHostile()788 void EoBCoreEngine::turnFriendlyMonstersHostile() {
789 	EoBMonsterInPlay *m = 0;
790 	for (int i = 0; i < 30; i++) {
791 		if (_monsters[i].mode == 8) {
792 			_monsters[i].mode = 0;
793 			_monsters[i].dest = _currentBlock;
794 			m = &_monsters[i];
795 		}
796 	}
797 
798 	if (m) {
799 		if (m->type == 7)
800 			setScriptFlags(0x40000);
801 		else if (m->type == 12)
802 			setScriptFlags(0x8000000);
803 	}
804 }
805 
getNextMonsterDirection(int curBlock,int destBlock)806 int EoBCoreEngine::getNextMonsterDirection(int curBlock, int destBlock) {
807 	uint8 c = destBlock % 32;
808 	uint8 d = destBlock / 32;
809 	uint8 e = curBlock % 32;
810 	uint8 f = curBlock / 32;
811 
812 	int r = 0;
813 
814 	int s1 = (_flags.platform == Common::kPlatformAmiga) ? (d - f) : (f - d);
815 	int d1 = ABS(s1);
816 	s1 <<= 1;
817 	int s2 = c - e;
818 	int d2 = ABS(s2);
819 	s2 <<= 1;
820 
821 	if (s1 >= d2)
822 		r |= 8;
823 	if (-s1 >= d2)
824 		r |= 4;
825 	if (s2 >= d1)
826 		r |= 2;
827 	if (-s2 >= d1)
828 		r |= 1;
829 
830 	return _monsterDirChangeTable[r];
831 }
832 
getNextMonsterPos(EoBMonsterInPlay * m,int block)833 int EoBCoreEngine::getNextMonsterPos(EoBMonsterInPlay *m, int block) {
834 	if ((_flags.gameID == GI_EOB1 && _monsterProps[m->type].u30 != 0) || (_flags.gameID == GI_EOB2 && _monsterProps[m->type].u30 == 2))
835 		return -1;
836 	int d = findFreeMonsterPos(block, _monsterProps[m->type].u30);
837 	if (d < 0)
838 		return -1;
839 
840 	int dir = m->dir;
841 	if (_flags.gameID == GI_EOB2) {
842 		if (_monsterProps[m->type].u30 == 1) {
843 			if (d == 9)
844 				return -1;
845 
846 			int v = _monsterCloseAttUnkTable[d];
847 			if (v != -1)
848 				//////
849 				m->dir = 0;
850 			return v;
851 		}
852 	} else {
853 		dir &= 1;
854 	}
855 
856 	for (int i = 0; i < 4; i++) {
857 		int v = m->dir ^ _monsterCloseAttPosTable2[(dir << 2) + i];
858 		if (!(d & (1 << v)))
859 			return v;
860 	}
861 
862 	return -1;
863 }
864 
findFreeMonsterPos(int block,int size)865 int EoBCoreEngine::findFreeMonsterPos(int block, int size) {
866 	int nm = _levelBlockProperties[block].flags & 7;
867 	if (nm == 4)
868 		return -2;
869 
870 	int res = 0;
871 
872 	for (int i = 0; i < 30; i++) {
873 		EoBMonsterInPlay *m = &_monsters[i];
874 		if (m->block != block)
875 			continue;
876 		if (_monsterProps[m->type].u30 != size)
877 			return -1;
878 
879 		if (m->pos == 4 && !(_flags.gameID == GI_EOB2 && m->flags & 0x20))
880 			m->pos = (_flags.gameID == GI_EOB2 && _monsterProps[m->type].u30 == 1) ? 0 : _monsterCloseAttPosTable1[m->dir];
881 
882 		res |= (1 << m->pos);
883 		if (--nm == 0)
884 			break;
885 	}
886 
887 	return res;
888 }
889 
updateMoveMonster(EoBMonsterInPlay * m)890 void EoBCoreEngine::updateMoveMonster(EoBMonsterInPlay *m) {
891 	EoBMonsterProperty *p = &_monsterProps[m->type];
892 	int d = getNextMonsterDirection(m->block, _currentBlock);
893 
894 	if ((_flags.gameID == GI_EOB2) && (p->capsFlags & 0x800) && !(d & 1))
895 		d >>= 1;
896 	else
897 		d = m->dir;
898 
899 	d = calcNewBlockPosition(m->block, d);
900 
901 	if (m->dest == d && _currentBlock != d) {
902 		m->mode = rollDice(1, 2, -1) + 5;
903 		return;
904 	}
905 
906 	if (updateMonsterTryDistanceAttack(m))
907 		return;
908 
909 	if (updateMonsterTryCloseAttack(m, d))
910 		return;
911 
912 	m->curAttackFrame = 0;
913 	walkMonster(m, m->dest);
914 
915 	if (p->capsFlags & 8)
916 		updateMonsterTryCloseAttack(m, -1);
917 }
918 
updateMonsterTryDistanceAttack(EoBMonsterInPlay * m)919 bool EoBCoreEngine::updateMonsterTryDistanceAttack(EoBMonsterInPlay *m) {
920 	EoBMonsterProperty *p = &_monsterProps[m->type];
921 	if (!m->numRemoteAttacks || ((_flags.gameID == GI_EOB1) && !(p->capsFlags & 0x40)))
922 		return false;
923 
924 	if ((_flags.gameID == GI_EOB1 && m->stepsTillRemoteAttack < 5) || (_flags.gameID == GI_EOB2 && (rollDice(1, 3) > m->stepsTillRemoteAttack))) {
925 		m->stepsTillRemoteAttack++;
926 		return false;
927 	}
928 
929 	if (getBlockDistance(m->block, _currentBlock) > 3 || getNextMonsterDirection(m->block, _currentBlock) != (m->dir << 1))
930 		return false;
931 
932 	int d = m->dir;
933 	int bl = calcNewBlockPosition(m->block, d);
934 
935 	while (bl != _currentBlock) {
936 		if (!(_wllWallFlags[_levelBlockProperties[bl].walls[d ^ 2]] & 3) || (_levelBlockProperties[bl].flags & 7))
937 			return false;
938 		bl = calcNewBlockPosition(bl, d);
939 	}
940 
941 	Item itm = 0;
942 	if (_flags.gameID == GI_EOB1) {
943 		switch (m->type - 4) {
944 		case 0:
945 			launchMagicObject(-1, 9, m->block, m->pos, m->dir);
946 			snd_processEnvironmentalSoundEffect(31, m->block);
947 			break;
948 		case 10:
949 			launchMagicObject(-1, _enemyMageSpellList[m->numRemoteAttacks], m->block, m->pos, m->dir);
950 			snd_processEnvironmentalSoundEffect(_enemyMageSfx[m->numRemoteAttacks], m->block);
951 			break;
952 		case 11:
953 			itm = duplicateItem(60);
954 			if (itm) {
955 				if (!launchObject(-1, itm, m->block, m->pos, m->dir, _items[itm].type))
956 					_items[itm].block = -1;
957 			}
958 			break;
959 		case 12:
960 			launchMagicObject(-1, 0, m->block, m->pos, m->dir);
961 			snd_processEnvironmentalSoundEffect(85, m->block);
962 			break;
963 		case 13:
964 			snd_processEnvironmentalSoundEffect(83, m->block);
965 			_txt->printMessage(_monsterSpecAttStrings[1]);
966 			for (int i = 0; i < 6; i++)
967 				statusAttack(i, 4, _monsterSpecAttStrings[2], 1, 5, 9, 1);
968 			break;
969 		case 17:
970 			d = rollDice(1, 4, -1);
971 			if (d >= 3) {
972 				for (int i = 0; i < 6; i++) {
973 					if (!testCharacter(i, 3))
974 						continue;
975 					_txt->printMessage(_monsterSpecAttStrings[0], -1, _characters[i].name);
976 					inflictCharacterDamage(i, rollDice(2, 8, 1));
977 				}
978 				snd_processEnvironmentalSoundEffect(108, m->block);
979 			} else {
980 				launchMagicObject(-1, _beholderSpellList[d], m->block, m->pos, m->dir);
981 				snd_processEnvironmentalSoundEffect(_beholderSfx[d], m->block);
982 			}
983 			break;
984 		default:
985 			break;
986 		}
987 
988 	} else {
989 		int cw = 0;
990 		if (p->remoteWeaponChangeMode == 1) {
991 			cw = m->curRemoteWeapon++;
992 			if (m->curRemoteWeapon == p->numRemoteWeapons)
993 				m->curRemoteWeapon = 0;
994 		} else if (p->remoteWeaponChangeMode == 2) {
995 			cw = rollDice(1, p->numRemoteWeapons, -1);
996 		}
997 
998 		int s = p->remoteWeapons[cw];
999 		if (s >= 0) {
1000 			if (s < 20) {
1001 				monsterSpellCast(m, s);
1002 			} else if (s == 20) {
1003 				if (_flags.platform == Common::kPlatformAmiga)
1004 					snd_processEnvironmentalSoundEffect(39, _currentBlock + 1);
1005 				else
1006 					snd_processEnvironmentalSoundEffect(103, m->block);
1007 				_txt->printMessage(_monsterSpecAttStrings[0]);
1008 				for (int i = 0; i < 6; i++)
1009 					statusAttack(i, 4, _monsterSpecAttStrings[1], 1, 5, 9, 1);
1010 			}
1011 		} else {
1012 			itm = duplicateItem(-s);
1013 			if (itm) {
1014 				if (!launchObject(-1, itm, m->block, m->pos, m->dir, _items[itm].type))
1015 					_items[itm].block = -1;
1016 			}
1017 		}
1018 	}
1019 
1020 	if (m->numRemoteAttacks != 255)
1021 		m->numRemoteAttacks--;
1022 	m->stepsTillRemoteAttack = 0;
1023 	return true;
1024 }
1025 
updateMonsterTryCloseAttack(EoBMonsterInPlay * m,int block)1026 bool EoBCoreEngine::updateMonsterTryCloseAttack(EoBMonsterInPlay *m, int block) {
1027 	if (block == -1)
1028 		block = calcNewBlockPosition(m->block, m->dir);
1029 
1030 	if (block != _currentBlock)
1031 		return false;
1032 
1033 	int r = (m->pos == 4 || (_flags.gameID == GI_EOB2 && _monsterProps[m->type].u30 == 1)) ? 1 : _monsterCloseAttChkTable1[(m->dir << 2) + m->pos];
1034 
1035 	if (r) {
1036 		m->flags ^= 4;
1037 		if (!(m->flags & 4))
1038 			return true;
1039 
1040 		bool facing = (m->block == _visibleBlockIndex[13]);
1041 
1042 		if (facing) {
1043 			disableSysTimer(2);
1044 			if ((_flags.platform == Common::kPlatformSegaCD) == (m->type != 4))
1045 				snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
1046 
1047 			_flashShapeTimer = _flashShapeTimerIntv0;
1048 			m->curAttackFrame = -2;
1049 
1050 			for (int i = 0; i < 16 && m->curAttackFrame < 0; ++i) {
1051 				if (m->type != 4 && m->curAttackFrame == -1)
1052 					snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
1053 				if (_flags.platform == Common::kPlatformSegaCD && _partyResting) {
1054 					// The original SegaCD code does not update the monsters during resting. I presume to
1055 					// avoid graphical issues which I try to fix here...
1056 					setLevelPalettes(_currentLevel);
1057 					_screen->sega_selectPalette(-1, 2, true);
1058 					gui_setupPlayFieldHelperPages(true);
1059 					gui_drawAllCharPortraitsWithStats();
1060 					_partyResting = false;
1061 				}
1062 				drawScene(1);
1063 				_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv1;
1064 			}
1065 
1066 		} else {
1067 			snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
1068 		}
1069 
1070 		monsterCloseAttack(m);
1071 
1072 		if (facing) {
1073 			m->curAttackFrame = 0;
1074 			m->animStep ^= 1;
1075 			_sceneUpdateRequired = 1;
1076 			enableSysTimer(2);
1077 			_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv2;
1078 		}
1079 	} else {
1080 		int b = m->block;
1081 		if ((_levelBlockProperties[b].flags & 7) == 1) {
1082 			m->pos = 4;
1083 		} else {
1084 			b = getNextMonsterPos(m, b);
1085 			if (b >= 0)
1086 				m->pos = b;
1087 		}
1088 		checkSceneUpdateNeed(m->block);
1089 	}
1090 
1091 	return true;
1092 }
1093 
walkMonster(EoBMonsterInPlay * m,int destBlock)1094 void EoBCoreEngine::walkMonster(EoBMonsterInPlay *m, int destBlock) {
1095 	if (++_monsterStepCounter > 10) {
1096 		_monsterStepCounter = 0;
1097 		_monsterStepMode ^= 1;
1098 	}
1099 
1100 	const int8 *tbl = _monsterStepMode ? _monsterStepTable3 : _monsterStepTable2;
1101 
1102 	int s = m->dir << 1;
1103 	int b = m->block;
1104 	int d = getNextMonsterDirection(b, destBlock);
1105 	if (d == -1)
1106 		return;
1107 
1108 	if (m->flags & 8) {
1109 		// Interestingly, the fear spell in EOB 1 does not expire.
1110 		// I don't know whether this is intended or not.
1111 		if (_flags.gameID == GI_EOB1) {
1112 			d ^= 4;
1113 		} else if (m->spellStatusLeft > 0) {
1114 			if (--m->spellStatusLeft == 0)
1115 				m->flags &= ~8;
1116 			else
1117 				d ^= 4;
1118 		}
1119 	}
1120 
1121 	int d2 = (d - s) & 7;
1122 
1123 	if (_flags.gameID == GI_EOB1) {
1124 		if ((b + _monsterStepTable0[d >> 1] == _currentBlock) && !(d & 1)) {
1125 			if (d2 >= 5)
1126 				s = m->dir - 1;
1127 			else if (d2 != 0)
1128 				s = m->dir + 1;
1129 			walkMonsterNextStep(m, -1, s & 3);
1130 			return;
1131 		}
1132 	} else if (_flags.gameID == GI_EOB2) {
1133 		if (b + _monsterStepTable0[d] == destBlock) {
1134 			if (d & 1) {
1135 				int e = _monsterStepTable1[((d - 1) << 1) + m->dir];
1136 				if (e && (!(_monsterProps[m->type].capsFlags & 0x200) || (rollDice(1, 4) < 4))) {
1137 					if (walkMonsterNextStep(m, b + e, -1))
1138 						return;
1139 				}
1140 			} else {
1141 				walkMonsterNextStep(m, -1, d >> 1);
1142 				return;
1143 			}
1144 		}
1145 	}
1146 
1147 	if (d2) {
1148 		if (d2 >= 5)
1149 			s -= (1 + ((d & 1) ^ 1));
1150 		else
1151 			s += (1 + ((d & 1) ^ 1));
1152 		s &= 7;
1153 	}
1154 
1155 	for (int i = 7; i > -1; i--) {
1156 		s = (s + tbl[i]) & 7;
1157 		uint16 b2 = (s & 1) ? 0 : calcNewBlockPosition(b, s >> 1);
1158 		if (!b2)
1159 			continue;
1160 		if (walkMonsterNextStep(m, b2, s >> 1))
1161 			return;
1162 	}
1163 }
1164 
walkMonsterNextStep(EoBMonsterInPlay * m,int destBlock,int direction)1165 bool EoBCoreEngine::walkMonsterNextStep(EoBMonsterInPlay *m, int destBlock, int direction) {
1166 	EoBMonsterProperty *p = &_monsterProps[m->type];
1167 	int obl = m->block;
1168 
1169 	if (destBlock != m->block && destBlock != -1) {
1170 		if (m->flags & 8) {
1171 			if (getBlockDistance(destBlock, _currentBlock) < getBlockDistance(m->block, _currentBlock))
1172 				return false;
1173 		}
1174 
1175 		if (destBlock == _currentBlock)
1176 			return false;
1177 
1178 		if (direction == -1)
1179 			direction = m->dir;
1180 
1181 		LevelBlockProperty *l = &_levelBlockProperties[destBlock];
1182 		uint8 w = l->walls[direction ^ 2];
1183 
1184 		if (!(_wllWallFlags[w] & 4)) {
1185 			if (_flags.gameID == GI_EOB1 || !(p->capsFlags & 0x1000) || _wllShapeMap[w] != -1)
1186 				return false;
1187 
1188 			if (_wllWallFlags[w] & 0x20) {
1189 				if (p->capsFlags & 4 && m->type == 1)
1190 					l->walls[direction] = l->walls[direction ^ 2] = 72;
1191 				else
1192 					openDoor(destBlock);
1193 			}
1194 
1195 			if (direction != -1) {
1196 				m->dir = direction;
1197 				checkSceneUpdateNeed(m->block);
1198 			}
1199 			return true;
1200 		}
1201 
1202 		if ((l->flags & 7) && destBlock) {
1203 			int pos = getNextMonsterPos(m, destBlock);
1204 			if (pos == -1)
1205 				return false;
1206 			m->pos = pos;
1207 		}
1208 
1209 		placeMonster(m, destBlock, direction);
1210 		direction = -1;
1211 	}
1212 
1213 	if (direction != -1)
1214 		m->dir = direction;
1215 
1216 	checkSceneUpdateNeed(obl);
1217 	uint16 prioFlag = (_flags.platform == Common::kPlatformSegaCD) ? 0x2000 : 0;
1218 	if (!_partyResting && p->sound2 > 0)
1219 		snd_processEnvironmentalSoundEffect(p->sound2 | prioFlag, m->block);
1220 
1221 	return true;
1222 }
1223 
updateMonsterFollowPath(EoBMonsterInPlay * m,int turnSteps)1224 void EoBCoreEngine::updateMonsterFollowPath(EoBMonsterInPlay *m, int turnSteps) {
1225 	if (!walkMonsterNextStep(m, calcNewBlockPosition(m->block, m->dir), -1)) {
1226 		m->dir = (m->dir + turnSteps) & 3;
1227 		walkMonsterNextStep(m, -1, m->dir);
1228 	}
1229 }
1230 
updateMonstersStraying(EoBMonsterInPlay * m,int a)1231 void EoBCoreEngine::updateMonstersStraying(EoBMonsterInPlay *m, int a) {
1232 	if (m->stray >= 0) {
1233 		if (m->stray == 0)
1234 			updateMonsterFollowPath(m, -a);
1235 
1236 		int8 d = (m->dir + a) & 3;
1237 		uint16 bl = calcNewBlockPosition(m->block, d);
1238 		uint8 flg = _wllWallFlags[_levelBlockProperties[bl].walls[_dscBlockMap[d]]] & 4;
1239 
1240 		if (m->stray == 0) {
1241 			if (!flg)
1242 				m->stray = -1;
1243 			return;
1244 		}
1245 
1246 		if (flg) {
1247 			walkMonsterNextStep(m, -1, d);
1248 			m->stray = -1;
1249 			return;
1250 		}
1251 	}
1252 
1253 	if (walkMonsterNextStep(m, calcNewBlockPosition(m->block, m->dir), -1)) {
1254 		m->stray = 1;
1255 	} else {
1256 		walkMonsterNextStep(m, -1, (m->dir - a) & 3);
1257 		m->stray = 0;
1258 	}
1259 }
1260 
updateMonstersSpellStatus(EoBMonsterInPlay * m)1261 void EoBCoreEngine::updateMonstersSpellStatus(EoBMonsterInPlay *m) {
1262 	if (m->spellStatusLeft) {
1263 		if (!--m->spellStatusLeft)
1264 			m->mode = 0;
1265 	}
1266 }
1267 
setBlockMonsterDirection(int block,int dir)1268 void EoBCoreEngine::setBlockMonsterDirection(int block, int dir) {
1269 	for (int i = 0; i < 30; i++) {
1270 		if (_monsters[i].block != block || _monsters[i].dir == dir)
1271 			continue;
1272 		_monsters[i].dir = dir;
1273 		_monsters[i].directionChanged = 1;
1274 	}
1275 }
1276 
1277 } // End of namespace Kyra
1278 
1279 #endif // ENABLE_EOB
1280