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