aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/engine/sprites_lol.cpp
diff options
context:
space:
mode:
authorathrxx2019-01-26 01:31:34 +0100
committerathrxx2019-03-06 20:48:15 +0100
commit1dfdcc7252ac83643cae7a7447c025da2af63843 (patch)
treeb6736d006bf67d5264dd171c336f0915695d1f88 /engines/kyra/engine/sprites_lol.cpp
parent8b53d20b51771680c3d31aa02c0285b7a8be4e85 (diff)
downloadscummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.gz
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.bz2
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.zip
KYRA: cleanup dir
Reorganize all files in sub directories. The file placement isn't as intuitive as it might be for other engines, which is probably the reason why this hasn't been done before.
Diffstat (limited to 'engines/kyra/engine/sprites_lol.cpp')
-rw-r--r--engines/kyra/engine/sprites_lol.cpp1558
1 files changed, 1558 insertions, 0 deletions
diff --git a/engines/kyra/engine/sprites_lol.cpp b/engines/kyra/engine/sprites_lol.cpp
new file mode 100644
index 0000000000..910447c45a
--- /dev/null
+++ b/engines/kyra/engine/sprites_lol.cpp
@@ -0,0 +1,1558 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef ENABLE_LOL
+
+#include "kyra/engine/lol.h"
+#include "kyra/graphics/screen_lol.h"
+
+namespace Kyra {
+
+void LoLEngine::loadMonsterShapes(const char *file, int monsterIndex, int animType) {
+ releaseMonsterShapes(monsterIndex);
+ _screen->loadBitmap(file, 3, 3, 0);
+
+ const uint8 *p = _screen->getCPagePtr(2);
+ const uint8 *ts[16];
+
+ for (int i = 0; i < 16; i++) {
+ ts[i] = _screen->getPtrToShape(p, i);
+
+ bool replaced = false;
+ int pos = monsterIndex << 4;
+
+ for (int ii = 0; ii < i; ii++) {
+ if (ts[i] != ts[ii])
+ continue;
+
+ _monsterShapes[pos + i] = _monsterShapes[pos + ii];
+ replaced = true;
+ break;
+ }
+
+ if (!replaced)
+ _monsterShapes[pos + i] = _screen->makeShapeCopy(p, i);
+
+ int size = _screen->getShapePaletteSize(_monsterShapes[pos + i]) << 3;
+ _monsterPalettes[pos + i] = new uint8[size];
+ memset(_monsterPalettes[pos + i], 0, size);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ for (int ii = 0; ii < 16; ii++) {
+ uint8 **of = &_monsterDecorationShapes[monsterIndex * 192 + i * 48 + ii * 3];
+ int s = (i << 4) + ii + 17;
+ of[0] = _screen->makeShapeCopy(p, s);
+ of[1] = _screen->makeShapeCopy(p, s + 1);
+ of[2] = _screen->makeShapeCopy(p, s + 2);
+ }
+ }
+ _monsterAnimType[monsterIndex] = animType & 0xFF;
+
+ uint8 *palShape = _screen->makeShapeCopy(p, 16);
+
+ _screen->clearPage(3);
+ _screen->drawShape(2, palShape, 0, 0, 0, 0);
+
+ uint8 *tmpPal1 = new uint8[64];
+ uint8 *tmpPal2 = new uint8[256];
+ uint16 *tmpPal3 = new uint16[256];
+ memset(tmpPal1, 0, 64);
+
+ for (int i = 0; i < 64; i++) {
+ tmpPal1[i] = *p;
+ p += 320;
+ }
+
+ p = _screen->getCPagePtr(2);
+
+ for (int i = 0; i < 16; i++) {
+ int pos = (monsterIndex << 4) + i;
+ uint16 sz = MIN(_screen->getShapeSize(_monsterShapes[pos]) - 10, 256);
+ memset(tmpPal2, 0, 256);
+ memcpy(tmpPal2, _monsterShapes[pos] + 10, sz);
+ memset(tmpPal3, 0xFF, 256 * sizeof(uint16));
+ uint8 numCol = *tmpPal2;
+
+ for (int ii = 0; ii < numCol; ii++) {
+ uint8 *cl = (uint8 *)memchr(tmpPal1, tmpPal2[1 + ii], 64);
+ if (!cl)
+ continue;
+ tmpPal3[ii] = (uint16)(cl - tmpPal1);
+ }
+
+ for (int ii = 0; ii < 8; ii++) {
+ memset(tmpPal2, 0, 256);
+ memcpy(tmpPal2, _monsterShapes[pos] + 10, sz);
+ for (int iii = 0; iii < numCol; iii++) {
+ if (tmpPal3[iii] == 0xFFFF)
+ continue;
+ if (p[tmpPal3[iii] * 320 + ii + 1])
+ tmpPal2[1 + iii] = p[tmpPal3[iii] * 320 + ii + 1];
+ }
+ memcpy(_monsterPalettes[pos] + ii * numCol, &tmpPal2[1], numCol);
+ }
+ }
+
+ delete[] tmpPal1;
+ delete[] tmpPal2;
+ delete[] tmpPal3;
+ delete[] palShape;
+}
+
+void LoLEngine::releaseMonsterShapes(int monsterIndex) {
+ for (int i = 0; i < 16; i++) {
+ int pos = (monsterIndex << 4) + i;
+ int pos2 = (monsterIndex << 4) + 16;
+ if (_monsterShapes[pos]) {
+ uint8 *t = _monsterShapes[pos];
+ delete[] _monsterShapes[pos];
+ for (int ii = pos; ii < pos2; ii++) {
+ if (_monsterShapes[ii] == t)
+ _monsterShapes[ii] = 0;
+ }
+ }
+
+ if (_monsterPalettes[pos]) {
+ delete[] _monsterPalettes[pos];
+ _monsterPalettes[pos] = 0;
+ }
+ }
+
+ for (int i = 0; i < 192; i++) {
+ int pos = (monsterIndex * 192) + i;
+ if (_monsterDecorationShapes[pos]) {
+ delete[] _monsterDecorationShapes[pos];
+ _monsterDecorationShapes[pos] = 0;
+ }
+ }
+}
+
+int LoLEngine::deleteMonstersFromBlock(int block) {
+ int i = _levelBlockProperties[block].assignedObjects;
+ int cnt = 0;
+ uint16 next = 0;
+
+ while (i) {
+ next = findObject(i)->nextAssignedObject;
+ if (!(i & 0x8000)) {
+ i = next;
+ continue;
+ }
+
+ LoLMonster *m = &_monsters[i & 0x7FFF];
+
+ cnt++;
+ setMonsterMode(m, 14);
+
+ checkSceneUpdateNeed(m->block);
+
+ placeMonster(m, 0, 0);
+
+ i = next;
+ }
+ return cnt;
+}
+
+void LoLEngine::setMonsterMode(LoLMonster *monster, int mode) {
+ if (monster->mode == 13 && mode != 14)
+ return;
+
+ if (mode == 7) {
+ monster->destX = _partyPosX;
+ monster->destY = _partyPosY;
+ }
+
+ if (monster->mode == 1 && mode == 7) {
+ for (int i = 0; i < 30; i++) {
+ if (monster->mode != 1)
+ continue;
+ monster->mode = mode;
+ monster->fightCurTick = 0;
+ monster->destX = _partyPosX;
+ monster->destY = _partyPosY;
+ setMonsterDirection(monster, calcMonsterDirection(monster->x, monster->y, monster->destX, monster->destY));
+ }
+ } else {
+ monster->mode = mode;
+ monster->fightCurTick = 0;
+ if (mode == 14)
+ monster->hitPoints = 0;
+ if (mode == 13 && (monster->flags & 0x20)) {
+ monster->mode = 0;
+ monsterDropItems(monster);
+ if (_currentLevel != 29)
+ setMonsterMode(monster, 14);
+ runLevelScriptCustom(0x404, -1, monster->id, monster->id, 0, 0);
+ checkSceneUpdateNeed(monster->block);
+ if (monster->mode == 14)
+ placeMonster(monster, 0, 0);
+ }
+ }
+}
+
+bool LoLEngine::updateMonsterAdjustBlocks(LoLMonster *monster) {
+ static const uint8 dims[] = { 0, 13, 9, 3 };
+ if (monster->properties->flags & 8)
+ return true;
+
+ uint16 x1 = (monster->x & 0xFF00) | 0x80;
+ uint16 y1 = (monster->y & 0xFF00) | 0x80;
+ int x2 = _partyPosX;
+ int y2 = _partyPosY;
+
+ uint16 dir = 0;
+ if (monster->properties->flags & 1) {
+ dir = monster->direction >> 1;
+ } else {
+ dir = calcMonsterDirection(x1, y1, x2, y2);
+ if ((monster->properties->flags & 2) && (dir == (monster->direction ^ 4)))
+ return false;
+ dir >>= 1;
+ }
+
+ calcSpriteRelPosition(x1, y1, x2, y2, dir);
+ x2 >>= 8;
+ y2 >>= 8;
+
+ if (y2 < 0 || y2 > 3)
+ return false;
+
+ int t = (x2 < 0) ? -x2 : x2;
+ if (t > y2)
+ return false;
+
+ for (int i = 0; i < 18; i++)
+ _visibleBlocks[i] = &_levelBlockProperties[(monster->block + _dscBlockIndex[dir + i]) & 0x3FF];
+
+ int16 fx1 = 0;
+ int16 fx2 = 0;
+ setLevelShapesDim(x2 + dims[y2], fx1, fx2, 13);
+
+ return fx1 < fx2;
+}
+
+void LoLEngine::placeMonster(LoLMonster *monster, uint16 x, uint16 y) {
+ bool cont = true;
+ int t = monster->block;
+ if (monster->block) {
+ removeAssignedObjectFromBlock(&_levelBlockProperties[t], ((uint16)monster->id) | 0x8000);
+ _levelBlockProperties[t].direction = 5;
+ checkSceneUpdateNeed(t);
+ } else {
+ cont = false;
+ }
+
+ monster->block = calcBlockIndex(x, y);
+
+ if (monster->x != x || monster->y != y) {
+ monster->x = x;
+ monster->y = y;
+ monster->currentSubFrame = (monster->currentSubFrame + 1) & 3;
+ }
+
+ if (monster->block == 0)
+ return;
+
+ assignObjectToBlock(&_levelBlockProperties[monster->block].assignedObjects, ((uint16)monster->id) | 0x8000);
+ _levelBlockProperties[monster->block].direction = 5;
+ checkSceneUpdateNeed(monster->block);
+
+ // WORKAROUND: Some monsters in the white tower have sound id's of 0xFF. This is definitely a bug, since the
+ // last valid track number is 249 and there is no specific handling for 0xFF. Nonetheless this wouldn't
+ // cause problems in the original code, because it just so happens that the invalid memory address points
+ // to an entry in _ingameGMSoundIndex which just so happens to have a value of -1
+ if (monster->properties->sounds[0] == 0 || monster->properties->sounds[0] == 255 || cont == false)
+ return;
+
+ if ((!(monster->properties->flags & 0x100) || ((monster->currentSubFrame & 1) == 0)) && monster->block == t)
+ return;
+
+ if (monster->block != t)
+ runLevelScriptCustom(monster->block, 0x800, -1, monster->id, 0, 0);
+
+ if (_updateFlags & 1)
+ return;
+
+ snd_processEnvironmentalSoundEffect(monster->properties->sounds[0], monster->block);
+}
+
+int LoLEngine::calcMonsterDirection(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
+ int16 r = 0;
+
+ int16 t1 = y1 - y2;
+ if (t1 < 0) {
+ r++;
+ t1 = -t1;
+ }
+
+ r <<= 1;
+
+ int16 t2 = x2 - x1;
+ if (t2 < 0) {
+ r++;
+ t2 = -t2;
+ }
+
+ uint8 f = (t1 > t2) ? 1 : 0;
+
+ if (t2 >= t1)
+ SWAP(t1, t2);
+
+ r = (r << 1) | f;
+
+ t1 = (t1 + 1) >> 1;
+
+ f = (t1 > t2) ? 1 : 0;
+ r = (r << 1) | f;
+
+ static const uint8 retVal[] = { 1, 2, 1, 0, 7, 6, 7, 0, 3, 2, 3, 4, 5, 6, 5, 4};
+ return retVal[r];
+}
+
+void LoLEngine::setMonsterDirection(LoLMonster *monster, int dir) {
+ monster->direction = dir;
+
+ if (!(dir & 1) || ((monster->direction - (monster->facing << 1)) >= 2))
+ monster->facing = monster->direction >> 1;
+
+ checkSceneUpdateNeed(monster->block);
+}
+
+void LoLEngine::monsterDropItems(LoLMonster *monster) {
+ uint16 a = monster->assignedItems;
+ while (a) {
+ uint16 b = a;
+ a = _itemsInPlay[a].nextAssignedObject;
+ setItemPosition(b, monster->x, monster->y, 0, 1);
+ }
+}
+
+int LoLEngine::checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 objectWidth, uint16 testFlag, uint16 wallFlag) {
+ _objectLastDirection = 0;
+ uint16 x2 = 0;
+ uint16 y2 = 0;
+ int xOffs = 0;
+ int yOffs = 0;
+ int flag = 0;
+
+ int r = testBlockPassability(calcBlockIndex(x, y), x, y, objectWidth, testFlag, wallFlag);
+ if (r)
+ return r;
+
+ r = checkBlockOccupiedByParty(x, y, testFlag);
+ if (r)
+ return 4;
+
+ if (x & 0x80) {
+ if (((x & 0xFF) + objectWidth) & 0xFF00) {
+ xOffs = 1;
+ _objectLastDirection = 2;
+ x2 = x + objectWidth;
+
+ r = testBlockPassability(calcBlockIndex(x2, y), x, y, objectWidth, testFlag, wallFlag);
+ if (r)
+ return r;
+
+ r = checkBlockOccupiedByParty(x + xOffs, y, testFlag);
+ if (r)
+ return 4;
+
+ flag = 1;
+ }
+ } else {
+ if (((x & 0xFF) - objectWidth) & 0xFF00) {
+ xOffs = -1;
+ _objectLastDirection = 6;
+ x2 = x - objectWidth;
+
+ r = testBlockPassability(calcBlockIndex(x2, y), x, y, objectWidth, testFlag, wallFlag);
+ if (r)
+ return r;
+
+ r = checkBlockOccupiedByParty(x + xOffs, y, testFlag);
+ if (r)
+ return 4;
+
+ flag = 1;
+ }
+ }
+
+ if (y & 0x80) {
+ if (((y & 0xFF) + objectWidth) & 0xFF00) {
+ yOffs = 1;
+ _objectLastDirection = 4;
+ y2 = y + objectWidth;
+
+ r = testBlockPassability(calcBlockIndex(x, y2), x, y, objectWidth, testFlag, wallFlag);
+ if (r)
+ return r;
+
+ r = checkBlockOccupiedByParty(x, y + yOffs, testFlag);
+ if (r)
+ return 4;
+ flag &= 1;
+ } else {
+ flag = 0;
+ }
+ } else {
+ if (((y & 0xFF) - objectWidth) & 0xFF00) {
+ yOffs = -1;
+ _objectLastDirection = 0;
+ y2 = y - objectWidth;
+
+ r = testBlockPassability(calcBlockIndex(x, y2), x, y, objectWidth, testFlag, wallFlag);
+ if (r)
+ return r;
+
+ r = checkBlockOccupiedByParty(x, y + yOffs, testFlag);
+ if (r)
+ return 4;
+ flag &= 1;
+ } else {
+ flag = 0;
+ }
+ }
+
+ if (!flag)
+ return 0;
+
+ r = testBlockPassability(calcBlockIndex(x2, y2), x, y, objectWidth, testFlag, wallFlag);
+ if (r)
+ return r;
+
+ r = checkBlockOccupiedByParty(x + xOffs, y + yOffs, testFlag);
+ if (r)
+ return 4;
+
+ return 0;
+}
+
+int LoLEngine::testBlockPassability(int block, int x, int y, int objectWidth, int testFlag, int wallFlag) {
+ if (block == _currentBlock)
+ testFlag &= 0xFFFE;
+
+ if (testFlag & 1) {
+ _monsterCurBlock = block;
+ if (testWallFlag(block, -1, wallFlag))
+ return 1;
+ _monsterCurBlock = 0;
+ }
+
+ if (!(testFlag & 2))
+ return 0;
+
+ uint16 obj = _levelBlockProperties[block].assignedObjects;
+ while (obj & 0x8000) {
+ LoLMonster *monster = &_monsters[obj & 0x7FFF];
+
+ if (monster->mode < 13) {
+ int r = checkDrawObjectSpace(x, y, monster->x, monster->y);
+ if ((objectWidth + monster->properties->maxWidth) > r)
+ return 2;
+ }
+
+ obj = findObject(obj)->nextAssignedObject;
+ }
+
+ return 0;
+}
+
+int LoLEngine::calcMonsterSkillLevel(int id, int a) {
+ const uint16 *c = getCharacterOrMonsterStats(id);
+ int r = (a << 8) / c[4];
+
+ if (id & 0x8000) {
+ r = (r * _monsterModifiers2[3 + _monsterDifficulty]) >> 8;
+ } else {
+ if (_characters[id].skillLevels[1] > 7)
+ r = (r - (r >> 1));
+ else if (_characters[id].skillLevels[1] > 3 && _characters[id].skillLevels[1] <= 7)
+ r = (r - (r >> 2));
+ }
+
+ return r;
+}
+
+int LoLEngine::checkBlockOccupiedByParty(int x, int y, int testFlag) {
+ if ((testFlag & 4) && (_currentBlock == calcBlockIndex(x, y)))
+ return 1;
+
+ return 0;
+}
+
+void LoLEngine::drawBlockObjects(int blockArrayIndex) {
+ LevelBlockProperty *l = _visibleBlocks[blockArrayIndex];
+ uint16 s = l->assignedObjects;
+
+ if (l->direction != _currentDirection) {
+ l->drawObjects = 0;
+ l->direction = _currentDirection;
+
+ while (s) {
+ reassignDrawObjects(_currentDirection, s, l, true);
+ s = findObject(s)->nextAssignedObject;
+ }
+ }
+
+ s = l->drawObjects;
+ while (s) {
+ if (s & 0x8000) {
+ s &= 0x7FFF;
+ if (blockArrayIndex < 15)
+ drawMonster(s);
+ s = _monsters[s].nextDrawObject;
+ } else {
+ LoLItem *i = &_itemsInPlay[s];
+ int fx = _sceneItemOffs[s & 7] << 1;
+ int fy = _sceneItemOffs[(s >> 1) & 7] + 5;
+
+ if (i->flyingHeight >= 2 && blockArrayIndex >= 15) {
+ s = i->nextDrawObject;
+ continue;
+ }
+
+ uint8 *shp = 0;
+ uint16 flg = 0;
+
+ if (i->flyingHeight >= 2)
+ fy -= ((i->flyingHeight - 1) * 6);
+
+ if ((_itemProperties[i->itemPropertyIndex].flags & 0x1000) && !(i->shpCurFrame_flg & 0xC000)) {
+ int shpIndex = _itemProperties[i->itemPropertyIndex].flags & 0x800 ? 7 : _itemProperties[i->itemPropertyIndex].shpIndex;
+ int ii = 0;
+ for (; ii < 8; ii++) {
+ if (!_flyingObjects[ii].enable)
+ continue;
+
+ if (_flyingObjects[ii].item == s)
+ break;
+ }
+
+ if (_flyingItemShapes[shpIndex].flipFlags && ((i->x ^ i->y) & 0x20))
+ flg |= 0x20;
+
+ flg |= _flyingItemShapes[shpIndex].drawFlags;
+
+ if (ii != 8) {
+ switch (_currentDirection - (_flyingObjects[ii].direction >> 1) + 3) {
+ case 1:
+ case 5:
+ shpIndex = _flyingItemShapes[shpIndex].shapeFront;
+ break;
+ case 3:
+ shpIndex = _flyingItemShapes[shpIndex].shapeBack;
+ break;
+ case 2:
+ case 6:
+ flg |= 0x10;
+ // fall through
+ case 0:
+ case 4:
+ shpIndex = _flyingItemShapes[shpIndex].shapeLeft;
+ break;
+ default:
+ break;
+ }
+
+ shp = _thrownShapes[shpIndex];
+ }
+
+ if (shp)
+ fy += (shp[2] >> 2);
+
+ } else {
+ shp = (_itemProperties[i->itemPropertyIndex].flags & 0x40) ? _gameShapes[_itemProperties[i->itemPropertyIndex].shpIndex] :
+ _itemShapes[_gameShapeMap[_itemProperties[i->itemPropertyIndex].shpIndex << 1]];
+ }
+
+ if (shp)
+ drawItemOrMonster(shp, 0, i->x, i->y, fx, fy, flg, -1, false);
+ s = i->nextDrawObject;
+ }
+ }
+}
+
+void LoLEngine::drawMonster(uint16 id) {
+ LoLMonster *m = &_monsters[id];
+ int16 flg = _monsterDirFlags[(_currentDirection << 2) + m->facing];
+ int curFrm = getMonsterCurFrame(m, flg & 0xFFEF);
+ uint8 *shp = 0;
+
+ if (curFrm == -1) {
+ shp = _monsterShapes[m->properties->shapeIndex << 4];
+ calcDrawingLayerParameters(m->x + _monsterShiftOffs[m->shiftStep << 1], m->y + _monsterShiftOffs[(m->shiftStep << 1) + 1], _shpDmX, _shpDmY, _dmScaleW, _dmScaleH, shp, 0);
+ } else {
+ int d = m->flags & 7;
+ bool flip = m->properties->flags & 0x200 ? true : false;
+ flg &= 0x10;
+ shp = _monsterShapes[(m->properties->shapeIndex << 4) + curFrm];
+
+ if (m->properties->flags & 0x800)
+ flg |= 0x20;
+
+ uint8 *monsterPalette = d ? _monsterPalettes[(m->properties->shapeIndex << 4) + (curFrm & 0x0F)] + (shp[10] * (d - 1)) : 0;
+ uint8 *brightnessOverlay = drawItemOrMonster(shp, monsterPalette, m->x + _monsterShiftOffs[m->shiftStep << 1], m->y + _monsterShiftOffs[(m->shiftStep << 1) + 1], 0, 0, flg | 1, -1, flip);
+
+ for (int i = 0; i < 4; i++) {
+ int v = m->equipmentShapes[i] - 1;
+ if (v == -1)
+ break;
+
+ uint8 *shp2 = _monsterDecorationShapes[m->properties->shapeIndex * 192 + v * 48 + curFrm * 3];
+ if (!shp2)
+ continue;
+
+ drawDoorOrMonsterEquipment(shp2, 0, _shpDmX, _shpDmY, flg | 1, brightnessOverlay);
+ }
+ }
+
+ if (!m->damageReceived)
+ return;
+
+ int dW = _screen->getShapeScaledWidth(shp, _dmScaleW) >> 1;
+ int dH = _screen->getShapeScaledHeight(shp, _dmScaleH) >> 1;
+
+ int bloodAmount = (m->mode == 13) ? (m->fightCurTick << 1) : (m->properties->hitPoints / (m->damageReceived & 0x7FFF));
+
+ shp = _gameShapes[6];
+
+ int bloodType = m->properties->flags & 0xC000;
+ if (bloodType == 0x4000)
+ bloodType = _flags.use16ColorMode ? 0xBB : 63;
+ else if (bloodType == 0x8000)
+ bloodType = _flags.use16ColorMode ? 0x55 : 15;
+ else if (bloodType == 0xC000)
+ bloodType = _flags.use16ColorMode ? 0x33 : 74;
+ else
+ bloodType = 0;
+
+ uint8 *tbl = new uint8[256];
+ if (bloodType) {
+ for (int i = 0; i < 256; i++) {
+ tbl[i] = i;
+ if (i < 2 || i > 7)
+ continue;
+ tbl[i] += bloodType;
+ }
+ }
+
+ dW += m->hitOffsX;
+ dH += m->hitOffsY;
+
+ bloodAmount = CLIP(bloodAmount, 1, 4);
+
+ int sW = _dmScaleW / bloodAmount;
+ int sH = _dmScaleH / bloodAmount;
+
+ _screen->drawShape(_sceneDrawPage1, shp, _shpDmX + dW, _shpDmY + dH, 13, 0x124, tbl, bloodType ? 1 : 0, sW, sH);
+
+ delete[] tbl;
+}
+
+int LoLEngine::getMonsterCurFrame(LoLMonster *m, uint16 dirFlags) {
+ int tmp = 0;
+ switch (_monsterAnimType[m->properties->shapeIndex]) {
+ case 0:
+ // default
+ if (dirFlags) {
+ return (m->mode == 13) ? -1 : (dirFlags + m->currentSubFrame);
+ } else {
+ if (m->damageReceived)
+ return 12;
+
+ switch (m->mode - 5) {
+ case 0:
+ return (m->properties->flags & 4) ? 13 : 0;
+ case 3:
+ return (m->fightCurTick + 13);
+ case 6:
+ return 14;
+ case 8:
+ return -1;
+ default:
+ return m->currentSubFrame;
+ }
+ }
+ break;
+ case 1:
+ // monsters whose outward appearance reflects the damage they have taken
+ tmp = m->properties->hitPoints;
+ if (_flags.isTalkie)
+ tmp = (tmp * _monsterModifiers1[_monsterDifficulty]) >> 8;
+ if (m->hitPoints > (tmp >> 1))
+ tmp = 0;
+ else if (m->hitPoints > (tmp >> 2))
+ tmp = 4;
+ else
+ tmp = 8;
+
+ switch (m->mode) {
+ case 8:
+ return (m->fightCurTick + tmp);
+ case 11:
+ return 12;
+ case 13:
+ return (m->fightCurTick + 12);
+ default:
+ return tmp;
+ }
+
+ break;
+ case 2:
+ return (m->fightCurTick >= 13) ? 13 : m->fightCurTick;
+ case 3:
+ switch (m->mode) {
+ case 5:
+ return m->damageReceived ? 5 : 6;
+ case 8:
+ return (m->fightCurTick + 6);
+ case 11:
+ return 5;
+ default:
+ return m->damageReceived ? 5 : m->currentSubFrame;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void LoLEngine::reassignDrawObjects(uint16 direction, uint16 itemIndex, LevelBlockProperty *l, bool flag) {
+ if (l->direction != direction) {
+ l->direction = 5;
+ return;
+ }
+
+ LoLObject *newObject = findObject(itemIndex);
+ int r = calcObjectPosition(newObject, direction);
+ uint16 *b = &l->drawObjects;
+ LoLObject *lastObject = 0;
+
+ while (*b) {
+ lastObject = findObject(*b);
+
+ if (flag) {
+ if (calcObjectPosition(lastObject, direction) >= r)
+ break;
+ } else {
+ if (calcObjectPosition(lastObject, direction) > r)
+ break;
+ }
+
+ b = &lastObject->nextDrawObject;
+ }
+
+ newObject->nextDrawObject = *b;
+ *b = itemIndex;
+}
+
+void LoLEngine::redrawSceneItem() {
+ assignVisibleBlocks(_currentBlock, _currentDirection);
+ _screen->fillRect(112, 0, 287, 119, 0);
+
+ static const uint8 sceneClickTileIndex[] = { 13, 16};
+
+ int16 x1 = 0;
+ int16 x2 = 0;
+
+ for (int i = 0; i < 2; i++) {
+ uint8 tile = sceneClickTileIndex[i];
+ setLevelShapesDim(tile, x1, x2, 13);
+ uint16 s = _visibleBlocks[tile]->drawObjects;
+
+ int t = (i << 7) + 1;
+ while (s) {
+ if (s & 0x8000) {
+ s = _monsters[s & 0x7FFF].nextDrawObject;
+ } else {
+ LoLItem *item = &_itemsInPlay[s];
+
+ if (item->shpCurFrame_flg & 0x4000) {
+ if (checkDrawObjectSpace(item->x, item->y, _partyPosX, _partyPosY) < 320) {
+ int fx = _sceneItemOffs[s & 7] << 1;
+ int fy = _sceneItemOffs[(s >> 1) & 7] + 5;
+ if (item->flyingHeight > 1)
+ fy -= ((item->flyingHeight - 1) * 6);
+
+ uint8 *shp = (_itemProperties[item->itemPropertyIndex].flags & 0x40) ? _gameShapes[_itemProperties[item->itemPropertyIndex].shpIndex] :
+ _itemShapes[_gameShapeMap[_itemProperties[item->itemPropertyIndex].shpIndex << 1]];
+
+ drawItemOrMonster(shp, 0, item->x, item->y, fx, fy, 0, t, 0);
+ _screen->updateScreen();
+ }
+ }
+
+ s = item->nextDrawObject;
+ t++;
+ }
+ }
+ }
+}
+
+void LoLEngine::calcSpriteRelPosition(uint16 x1, uint16 y1, int &x2, int &y2, uint16 direction) {
+ int a = x2 - x1;
+ int b = y1 - y2;
+
+ if (direction) {
+ if (direction != 2)
+ SWAP(a, b);
+ if (direction != 3) {
+ a = -a;
+ if (direction != 1)
+ b = -b;
+ } else {
+ b = -b;
+ }
+ }
+
+ x2 = a;
+ y2 = b;
+}
+
+void LoLEngine::drawDoor(uint8 *shape, uint8 *doorPalette, int index, int unk2, int w, int h, int flags) {
+ if (!shape)
+ return;
+
+ uint8 c = _dscDoorY2[(_currentDirection << 5) + unk2];
+ int r = (c / 5) + 5 * _dscDimMap[index];
+ uint16 d = _dscShapeOvlIndex[r];
+ uint16 t = (index << 5) + c;
+
+ _shpDmY = _dscDoorMonsterY[t] + 120;
+
+ if (flags & 1) {
+ // TODO / UNUSED
+ flags |= 1;
+ }
+
+ int u = 0;
+
+ if (flags & 2) {
+ uint8 dimW = _dscDimMap[index];
+ _dmScaleW = _dscDoorMonsterScaleTable[dimW << 1];
+ _dmScaleH = _dscDoorMonsterScaleTable[(dimW << 1) + 1];
+ u = _dscDoor4[dimW];
+ }
+
+ d += 2;
+
+ if (!_dmScaleW || !_dmScaleH)
+ return;
+
+ int s = _screen->getShapeScaledHeight(shape, _dmScaleH) >> 1;
+
+ if (w)
+ w = (w * _dmScaleW) >> 8;
+
+ if (h)
+ h = (h * _dmScaleH) >> 8;
+
+ _shpDmX = _dscDoorMonsterX[t] + w + 200;
+ _shpDmY = _shpDmY + 4 - s + h - u;
+
+ if (d > 7)
+ d = 7;
+
+ if (_flags.use16ColorMode) {
+ uint8 bb = _blockBrightness >> 4;
+ if (d > bb)
+ d -= bb;
+ else
+ d = 0;
+ }
+
+ uint8 *brightnessOverlay = _screen->getLevelOverlay(d);
+ int doorScaledWitdh = _screen->getShapeScaledWidth(shape, _dmScaleW);
+
+ _shpDmX -= (doorScaledWitdh >> 1);
+ _shpDmY -= s;
+
+ drawDoorOrMonsterEquipment(shape, doorPalette, _shpDmX, _shpDmY, flags, brightnessOverlay);
+}
+
+void LoLEngine::drawDoorOrMonsterEquipment(uint8 *shape, uint8 *objectPalette, int x, int y, int flags, const uint8 *brightnessOverlay) {
+ int flg = 0;
+
+ if (flags & 0x10)
+ flg |= 1;
+
+ if (flags & 0x20)
+ flg |= 0x1000;
+
+ if (flags & 0x40)
+ flg |= 2;
+
+ if (flg & 0x1000) {
+ if (objectPalette)
+ _screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x9104, objectPalette, brightnessOverlay, 1, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH);
+ else
+ _screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x1104, brightnessOverlay, 1, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH);
+ } else {
+ if (objectPalette)
+ _screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x8104, objectPalette, brightnessOverlay, 1, _dmScaleW, _dmScaleH);
+ else
+ _screen->drawShape(_sceneDrawPage1, shape, x, y, 13, flg | 0x104, brightnessOverlay, 1, _dmScaleW, _dmScaleH);
+ }
+}
+
+uint8 *LoLEngine::drawItemOrMonster(uint8 *shape, uint8 *monsterPalette, int x, int y, int fineX, int fineY, int flags, int tblValue, bool vflip) {
+ uint8 *ovl2 = 0;
+ uint8 *brightnessOverlay = 0;
+ uint8 tmpOvl[16];
+
+ if (flags & 0x80) {
+ flags &= 0xFF7F;
+ ovl2 = monsterPalette;
+ monsterPalette = 0;
+ } else {
+ ovl2 = _screen->getLevelOverlay(_flags.use16ColorMode ? 5 : 4);
+ }
+
+ int r = calcDrawingLayerParameters(x, y, _shpDmX, _shpDmY, _dmScaleW, _dmScaleH, shape, vflip);
+
+ if (tblValue == -1) {
+ r = 7 - ((r / 3) - 1);
+ r = CLIP(r, 0, 7);
+ if (_flags.use16ColorMode) {
+ uint8 bb = _blockBrightness >> 4;
+ if (r > bb)
+ r -= bb;
+ else
+ r = 0;
+ }
+ brightnessOverlay = _screen->getLevelOverlay(r);
+ } else {
+ memset(tmpOvl + 1, tblValue, 15);
+ tmpOvl[0] = 0;
+ monsterPalette = tmpOvl;
+ brightnessOverlay = _screen->getLevelOverlay(7);
+ }
+
+ int flg = flags & 0x10 ? 1 : 0;
+ if (flags & 0x20)
+ flg |= 0x1000;
+ if (flags & 0x40)
+ flg |= 2;
+
+ if (_flags.use16ColorMode) {
+ if (_currentLevel != 22)
+ flg &= 0xDFFF;
+
+ } else {
+ if (_currentLevel == 22) {
+ if (brightnessOverlay)
+ brightnessOverlay[255] = 0;
+ } else {
+ flg |= 0x2000;
+ }
+ }
+
+ _shpDmX += ((_dmScaleW * fineX) >> 8);
+ _shpDmY += ((_dmScaleH * fineY) >> 8);
+
+ int dH = _screen->getShapeScaledHeight(shape, _dmScaleH) >> 1;
+
+ if (flg & 0x1000) {
+ if (monsterPalette)
+ _screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x8124, monsterPalette, brightnessOverlay, 0, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH, ovl2);
+ else
+ _screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x124, brightnessOverlay, 0, _transparencyTable1, _transparencyTable2, _dmScaleW, _dmScaleH, ovl2);
+ } else {
+ if (monsterPalette)
+ _screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x8124, monsterPalette, brightnessOverlay, 1, _dmScaleW, _dmScaleH, ovl2);
+ else
+ _screen->drawShape(_sceneDrawPage1, shape, _shpDmX, _shpDmY, 13, flg | 0x124, brightnessOverlay, 1, _dmScaleW, _dmScaleH, ovl2);
+ }
+
+ _shpDmX -= (_screen->getShapeScaledWidth(shape, _dmScaleW) >> 1);
+ _shpDmY -= dH;
+
+ return brightnessOverlay;
+}
+
+int LoLEngine::calcDrawingLayerParameters(int x1, int y1, int &x2, int &y2, uint16 &w, uint16 &h, uint8 *shape, int vflip) {
+ calcSpriteRelPosition(_partyPosX, _partyPosY, x1, y1, _currentDirection);
+
+ if (y1 < 0) {
+ w = h = x2 = y2 = 0;
+ return 0;
+ }
+
+ int l = y1 >> 5;
+ y2 = _monsterScaleY[l];
+ x2 = ((_monsterScaleX[l] * x1) >> 8) + 200;
+ w = h = (_shpDmY > 120) ? 0x100 : _monsterScaleWH[_shpDmY - 56];
+
+ if (vflip)
+ // objects aligned to the ceiling (like the "lobsters" in the mines)
+ y2 = ((120 - y2) >> 1) + (_screen->getShapeScaledHeight(shape, _dmScaleH) >> 1);
+ else
+ y2 -= (_screen->getShapeScaledHeight(shape, _dmScaleH) >> 1);
+
+ return l;
+}
+
+void LoLEngine::updateMonster(LoLMonster *monster) {
+ static const uint8 flags[] = { 1, 0, 1, 3, 3, 0, 0, 3, 4, 1, 0, 0, 4, 0, 0 };
+ if (monster->mode > 14)
+ return;
+
+ int f = flags[monster->mode];
+ if ((monster->speedTick++ < monster->properties->speedTotalWaitTicks) && (!(f & 4)))
+ return;
+
+ monster->speedTick = 0;
+
+ if (monster->properties->flags & 0x40) {
+ monster->hitPoints += rollDice(1, 8);
+ if (monster->hitPoints > monster->properties->hitPoints)
+ monster->hitPoints = monster->properties->hitPoints;
+ }
+
+ if (monster->flags & 8) {
+ monster->destX = _partyPosX;
+ monster->destY = _partyPosY;
+ }
+
+ if (f & 2) {
+ if (updateMonsterAdjustBlocks(monster)) {
+ setMonsterMode(monster, 7);
+ f &= 6;
+ }
+ }
+
+ if ((f & 1) && (monster->flags & 0x10))
+ setMonsterMode(monster, 7);
+
+ if ((monster->mode != 11) && (monster->mode != 14)) {
+ if (!(_rnd.getRandomNumber(255) & 3)) {
+ monster->shiftStep = (monster->shiftStep + 1) & 0x0F;
+ checkSceneUpdateNeed(monster->block);
+ }
+ }
+
+ switch (monster->mode) {
+ case 0:
+ case 1:
+ // friendly mode
+ if (monster->flags & 0x10) {
+ for (int i = 0; i < 30; i++) {
+ if (_monsters[i].mode == 1)
+ setMonsterMode(&_monsters[i], 7);
+ }
+ } else if (monster->mode == 1) {
+ moveMonster(monster);
+ }
+ break;
+
+ case 2:
+ moveMonster(monster);
+ break;
+
+ case 3:
+ if (updateMonsterAdjustBlocks(monster))
+ setMonsterMode(monster, 7);
+ for (int i = 0; i < 4; i++) {
+ if (calcNewBlockPosition(monster->block, i) == _currentBlock)
+ setMonsterMode(monster, 7);
+ }
+ break;
+
+ case 4:
+ // straying around not tracing the party
+ moveStrayingMonster(monster);
+ break;
+
+ case 5:
+ // second recovery phase after delivering an attack
+ // monsters will rearrange positions in this phase so as to allow a maximum
+ // number of monsters possible attacking at the same time
+ _partyAwake = true;
+ monster->fightCurTick--;
+ if ((monster->fightCurTick <= 0) || (checkDrawObjectSpace(_partyPosX, _partyPosY, monster->x, monster->y) > 256) || (monster->flags & 8))
+ setMonsterMode(monster, 7);
+ else
+ alignMonsterToParty(monster);
+ break;
+
+ case 6:
+ // same as mode 5, but without rearranging
+ if (--monster->fightCurTick <= 0)
+ setMonsterMode(monster, 7);
+ break;
+
+ case 7:
+ // monster destination is set to current party position
+ // depending on the flag setting this gets updated each round
+ // monster can't change mode before arriving at destination and/or attacking the party
+ if (!chasePartyWithDistanceAttacks(monster))
+ chasePartyWithCloseAttacks(monster);
+ checkSceneUpdateNeed(monster->block);
+ break;
+
+ case 8:
+ // first recovery phase after delivering an attack
+ if (++monster->fightCurTick > 2) {
+ setMonsterMode(monster, 5);
+ monster->fightCurTick = (int8)((((8 << 8) / monster->properties->fightingStats[4]) * _monsterModifiers3[_monsterDifficulty]) >> 8);
+ }
+ checkSceneUpdateNeed(monster->block);
+ break;
+
+ case 9:
+ if (--monster->fightCurTick) {
+ chasePartyWithCloseAttacks(monster);
+ } else {
+ setMonsterMode(monster, 7);
+ monster->flags &= 0xFFF7;
+ }
+ break;
+
+ case 12:
+ checkSceneUpdateNeed(monster->block);
+ if (++monster->fightCurTick > 13)
+ runLevelScriptCustom(0x404, -1, monster->id, monster->id, 0, 0);
+ break;
+
+ case 13:
+ // monster death
+ if (++monster->fightCurTick > 2)
+ killMonster(monster);
+ checkSceneUpdateNeed(monster->block);
+ break;
+
+ case 14:
+ monster->damageReceived = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ if (monster->damageReceived) {
+ if (monster->damageReceived & 0x8000)
+ monster->damageReceived &= 0x7FFF;
+ else
+ monster->damageReceived = 0;
+ checkSceneUpdateNeed(monster->block);
+ }
+
+ monster->flags &= 0xFFEF;
+}
+
+void LoLEngine::moveMonster(LoLMonster *monster) {
+ static const int8 turnPos[] = { 0, 2, 6, 6, 0, 2, 4, 4, 2, 2, 4, 6, 0, 0, 4, 6, 0 };
+ if (monster->x != monster->destX || monster->y != monster->destY) {
+ walkMonster(monster);
+ } else if (monster->direction != monster->destDirection) {
+ int i = (monster->facing << 2) + (monster->destDirection >> 1);
+ setMonsterDirection(monster, turnPos[i]);
+ }
+}
+
+void LoLEngine::walkMonster(LoLMonster *monster) {
+ if (monster->properties->flags & 0x400)
+ return;
+
+ int s = walkMonsterCalcNextStep(monster);
+
+ if (s == -1) {
+ if (walkMonsterCheckDest(monster->x, monster->y, monster, 4) != 1)
+ return;
+
+ _objectLastDirection ^= 4;
+ setMonsterDirection(monster, _objectLastDirection);
+ } else {
+ setMonsterDirection(monster, s);
+ if (monster->numDistAttacks) {
+ if (getBlockDistance(monster->block, _currentBlock) >= 2) {
+ if (checkForPossibleDistanceAttack(monster->block, monster->direction, 3, _currentBlock) != 5) {
+ if (monster->distAttackTick)
+ return;
+ }
+ }
+ }
+ }
+
+ int fx = 0;
+ int fy = 0;
+
+ getNextStepCoords(monster->x, monster->y, fx, fy, (s == -1) ? _objectLastDirection : s);
+ placeMonster(monster, fx, fy);
+}
+
+bool LoLEngine::chasePartyWithDistanceAttacks(LoLMonster *monster) {
+ if (!monster->numDistAttacks)
+ return false;
+
+ if (monster->distAttackTick > 0) {
+ monster->distAttackTick--;
+ return false;
+ }
+
+ int dir = checkForPossibleDistanceAttack(monster->block, monster->facing, 4, _currentBlock);
+ if (dir == 5)
+ return false;
+
+ int s = 0;
+
+ if (monster->flags & 0x10) {
+ s = monster->properties->numDistWeapons ? rollDice(1, monster->properties->numDistWeapons) : 0;
+ } else {
+ s = monster->curDistWeapon++;
+ if (monster->curDistWeapon >= monster->properties->numDistWeapons)
+ monster->curDistWeapon = 0;
+ }
+
+ int flyingObject = monster->properties->distWeapons[s];
+
+ if (flyingObject & 0xC000) {
+ if (getBlockDistance(monster->block, _currentBlock) > 1) {
+ int type = flyingObject & 0x4000 ? 0 : 1;
+ flyingObject = makeItem(flyingObject & 0x3FFF, 0, 0);
+
+ if (flyingObject) {
+ if (!launchObject(type, flyingObject, monster->x, monster->y, 12, dir << 1, -1, monster->id | 0x8000, 0x3F))
+ deleteItem(flyingObject);
+ }
+ }
+ } else if (!(flyingObject & 0x2000)) {
+ if (getBlockDistance(monster->block, _currentBlock) > 1)
+ return false;
+
+ if (flyingObject == 1) {
+ snd_playSoundEffect(147, -1);
+ shakeScene(10, 2, 2, 1);
+
+ for (int i = 0; i < 4; i++) {
+ if (!(_characters[i].flags & 1))
+ continue;
+
+ int item = removeCharacterItem(i, 15);
+ if (item)
+ setItemPosition(item, _partyPosX, _partyPosY, 0, 1);
+
+ inflictDamage(i, 20, 0xFFFF, 0, 2);
+ }
+
+ } else if (flyingObject == 3) {
+ // shriek
+ for (int i = 0; i < 30; i++) {
+ if (getBlockDistance(monster->block, _monsters[i].block) < 7)
+ setMonsterMode(monster, 7);
+ }
+ _txt->printMessage(2, "%s", getLangString(0x401A));
+
+ } else if (flyingObject == 4) {
+ launchMagicViper();
+
+ } else {
+ return false;
+ }
+ }
+
+ if (monster->numDistAttacks != 255)
+ monster->numDistAttacks--;
+
+ monster->distAttackTick = (monster->properties->fightingStats[4] * 8) >> 8;
+
+ return true;
+}
+
+void LoLEngine::chasePartyWithCloseAttacks(LoLMonster *monster) {
+ if (!(monster->flags & 8)) {
+ int dir = calcMonsterDirection(monster->x & 0xFF00, monster->y & 0xFF00, _partyPosX & 0xFF00, _partyPosY & 0xFF00);
+ int x1 = _partyPosX;
+ int y1 = _partyPosY;
+
+ calcSpriteRelPosition(monster->x, monster->y, x1, y1, dir >> 1);
+
+ int t = (x1 < 0) ? -x1 : x1;
+ if (y1 <= 160 && t <= 80) {
+ if ((monster->direction == dir) && (monster->facing == (dir >> 1))) {
+ int dst = getNearestPartyMemberFromPos(monster->x, monster->y);
+ snd_playSoundEffect(monster->properties->sounds[1], -1);
+ int m = monster->id | 0x8000;
+ int hit = battleHitSkillTest(m, dst, 0);
+
+ if (hit) {
+ int mx = calcInflictableDamage(m, dst, hit);
+ int dmg = rollDice(2, mx);
+ inflictDamage(dst, dmg, m, 0, 0);
+ applyMonsterAttackSkill(monster, dst, dmg);
+ }
+
+ setMonsterMode(monster, 8);
+ checkSceneUpdateNeed(monster->block);
+
+ } else {
+ setMonsterDirection(monster, dir);
+ checkSceneUpdateNeed(monster->block);
+ }
+ return;
+ }
+ }
+
+ if (monster->x != monster->destX || monster->y != monster->destY) {
+ walkMonster(monster);
+ } else {
+ setMonsterDirection(monster, monster->destDirection);
+ setMonsterMode(monster, (rollDice(1, 100) <= 50) ? 4 : 3);
+ }
+}
+
+int LoLEngine::walkMonsterCalcNextStep(LoLMonster *monster) {
+ static const int8 walkMonsterTable1[] = { 7, -6, 5, -4, 3, -2, 1, 0 };
+ static const int8 walkMonsterTable2[] = { -7, 6, -5, 4, -3, 2, -1, 0 };
+
+ if (++_monsterStepCounter > 10) {
+ _monsterStepCounter = 0;
+ _monsterStepMode ^= 1;
+ }
+
+ const int8 *tbl = _monsterStepMode ? walkMonsterTable2 : walkMonsterTable1;
+
+ int sx = monster->x;
+ int sy = monster->y;
+ int s = monster->direction;
+ int d = calcMonsterDirection(monster->x, monster->y, monster->destX, monster->destY);
+
+ if (monster->flags & 8)
+ d ^= 4;
+
+ d = (d - s) & 7;
+
+ if (d >= 5)
+ s = (s - 1) & 7;
+ else if (d)
+ s = (s + 1) & 7;
+
+ for (int i = 7; i > -1; i--) {
+ s = (s + tbl[i]) & 7;
+
+ int fx = 0;
+ int fy = 0;
+ getNextStepCoords(sx, sy, fx, fy, s);
+ d = walkMonsterCheckDest(fx, fy, monster, 4);
+
+ if (!d)
+ return s;
+
+ if ((d != 1) || (s & 1) || (!(monster->properties->flags & 0x80)))
+ continue;
+
+ uint8 w = _levelBlockProperties[_monsterCurBlock].walls[(s >> 1) ^ 2];
+
+ if (_wllWallFlags[w] & 0x20) {
+ if (_specialWallTypes[w] == 5) {
+ openCloseDoor(_monsterCurBlock, 1);
+ return -1;
+ }
+ }
+
+ if (_wllWallFlags[w] & 8)
+ return -1;
+ }
+
+ return -1;
+}
+
+int LoLEngine::checkForPossibleDistanceAttack(uint16 monsterBlock, int direction, int distance, uint16 curBlock) {
+ int mdist = getBlockDistance(curBlock, monsterBlock);
+
+ if (mdist > distance)
+ return 5;
+
+ int dir = calcMonsterDirection(monsterBlock & 0x1F, monsterBlock >> 5, curBlock & 0x1F, curBlock >> 5);
+ if ((dir & 1) || (dir != (direction << 1)))
+ return 5;
+
+ if (((monsterBlock & 0x1F) != (curBlock & 0x1F)) && ((monsterBlock & 0xFFE0) != (curBlock & 0xFFE0)))
+ return 5;
+
+ if (distance < 0)
+ return 5;
+
+ int p = monsterBlock;
+
+ for (int i = 0; i < distance; i++) {
+ p = calcNewBlockPosition(p, direction);
+
+ if (p == curBlock)
+ return direction;
+
+ if (_wllWallFlags[_levelBlockProperties[p].walls[direction ^ 2]] & 2)
+ return 5;
+
+ if (_levelBlockProperties[p].assignedObjects & 0x8000)
+ return 5;
+ }
+
+ return 5;
+}
+
+int LoLEngine::walkMonsterCheckDest(int x, int y, LoLMonster *monster, int unk) {
+ uint8 m = monster->mode;
+ monster->mode = 15;
+
+ int objType = checkBlockBeforeObjectPlacement(x, y, monster->properties->maxWidth, 7, monster->properties->flags & 0x1000 ? 32 : unk);
+
+ monster->mode = m;
+ return objType;
+}
+
+void LoLEngine::getNextStepCoords(int16 srcX, int16 srcY, int &newX, int &newY, uint16 direction) {
+ static const int8 stepAdjustX[] = { 0, 32, 32, 32, 0, -32, -32, -32 };
+ static const int8 stepAdjustY[] = { -32, -32, 0, 32, 32, 32, 0, -32 };
+
+ newX = (srcX + stepAdjustX[direction]) & 0x1FFF;
+ newY = (srcY + stepAdjustY[direction]) & 0x1FFF;
+}
+
+void LoLEngine::alignMonsterToParty(LoLMonster *monster) {
+ uint8 mdir = monster->direction >> 1;
+ uint16 mx = monster->x;
+ uint16 my = monster->y;
+ uint16 *pos = (mdir & 1) ? &my : &mx;
+ bool centered = (*pos & 0x7F) == 0;
+
+ bool posFlag = true;
+ if (monster->properties->maxWidth <= 63) {
+ if (centered) {
+ bool r = false;
+
+ if (monster->nextAssignedObject & 0x8000) {
+ r = true;
+ } else {
+ uint16 id = _levelBlockProperties[monster->block].assignedObjects;
+ id = (id & 0x8000) ? (id & 0x7FFF) : 0xFFFF;
+
+ if (id != monster->id) {
+ r = true;
+ } else {
+ for (int i = 0; i < 3; i++) {
+ mdir = (mdir + 1) & 3;
+ id = _levelBlockProperties[calcNewBlockPosition(monster->block, mdir)].assignedObjects;
+ id = (id & 0x8000) ? (id & 0x7FFF) : 0xFFFF;
+ if (id != 0xFFFF) {
+ r = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (r)
+ posFlag = false;
+ } else {
+ posFlag = false;
+ }
+ }
+
+ if (centered && posFlag)
+ return;
+
+ if (posFlag) {
+ if (*pos & 0x80)
+ *pos -= 32;
+ else
+ *pos += 32;
+ } else {
+ if (*pos & 0x80)
+ *pos += 32;
+ else
+ *pos -= 32;
+ }
+
+ if (walkMonsterCheckDest(mx, my, monster, 4))
+ return;
+
+ int fx = _partyPosX;
+ int fy = _partyPosY;
+ calcSpriteRelPosition(mx, my, fx, fy, monster->direction >> 1);
+
+ if (fx < 0)
+ fx = -fx;
+
+ if (fy > 160 || fx > 80)
+ return;
+
+ placeMonster(monster, mx, my);
+}
+
+void LoLEngine::moveStrayingMonster(LoLMonster *monster) {
+ int x = 0;
+ int y = 0;
+
+ if (monster->fightCurTick) {
+ uint8 d = (monster->direction - monster->fightCurTick) & 6;
+ uint8 id = monster->id;
+
+ for (int i = 0; i < 7; i++) {
+ getNextStepCoords(monster->x, monster->y, x, y, d);
+
+ if (!walkMonsterCheckDest(x, y, monster, 4)) {
+ placeMonster(monster, x, y);
+ setMonsterDirection(monster, d);
+ if (!i) {
+ if (++id > 3)
+ monster->fightCurTick = 0;
+ }
+ return;
+ }
+
+ d = (d + monster->fightCurTick) & 6;
+ }
+ setMonsterMode(monster, 3);
+
+ } else {
+ monster->direction &= 6;
+ getNextStepCoords(monster->x, monster->y, x, y, monster->direction);
+ if (!walkMonsterCheckDest(x, y, monster, 4)) {
+ placeMonster(monster, x, y);
+ } else {
+ monster->fightCurTick = _rnd.getRandomBit() ? 2 : -2;
+ monster->direction = (monster->direction + monster->fightCurTick) & 6;
+ }
+ }
+}
+
+void LoLEngine::killMonster(LoLMonster *monster) {
+ setMonsterMode(monster, 14);
+ monsterDropItems(monster);
+ checkSceneUpdateNeed(monster->block);
+
+ uint8 w = _levelBlockProperties[monster->block].walls[0];
+ uint16 f = _levelBlockProperties[monster->block].flags;
+ if (_wllVmpMap[w] == 0 && _wllShapeMap[w] == 0 && !(f & 0x40) && !(monster->properties->flags & 0x1000))
+ _levelBlockProperties[monster->block].flags |= 0x80;
+
+ placeMonster(monster, 0, 0);
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_LOL