aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/engine/items_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/items_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/items_lol.cpp')
-rw-r--r--engines/kyra/engine/items_lol.cpp594
1 files changed, 594 insertions, 0 deletions
diff --git a/engines/kyra/engine/items_lol.cpp b/engines/kyra/engine/items_lol.cpp
new file mode 100644
index 0000000000..446650d6e1
--- /dev/null
+++ b/engines/kyra/engine/items_lol.cpp
@@ -0,0 +1,594 @@
+/* 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 {
+
+LoLObject *LoLEngine::findObject(uint16 index) {
+ if (index & 0x8000)
+ return &_monsters[index & 0x7FFF];
+ else
+ return &_itemsInPlay[index];
+}
+
+int LoLEngine::calcObjectPosition(LoLObject *i, uint16 direction) {
+ int x = i->x;
+ int y = i->y;
+
+ calcSpriteRelPosition(_partyPosX, _partyPosY, x, y, direction);
+
+ if (y < 0)
+ y = 0;
+
+ int res = (i->flyingHeight << 12);
+ res |= (4095 - y);
+
+ return res;
+}
+
+void LoLEngine::removeAssignedObjectFromBlock(LevelBlockProperty *l, uint16 id) {
+ uint16 *blockItemIndex = &l->assignedObjects;
+ LoLObject *i = 0;
+
+ while (*blockItemIndex) {
+ if (*blockItemIndex == id) {
+ i = findObject(id);
+ *blockItemIndex = i->nextAssignedObject;
+ i->nextAssignedObject = 0;
+ return;
+ }
+
+ i = findObject(*blockItemIndex);
+ blockItemIndex = &i->nextAssignedObject;
+ }
+}
+
+void LoLEngine::removeDrawObjectFromBlock(LevelBlockProperty *l, uint16 id) {
+ uint16 *blockItemIndex = &l->drawObjects;
+ LoLObject *i = 0;
+
+ while (*blockItemIndex) {
+ if (*blockItemIndex == id) {
+ i = findObject(id);
+ *blockItemIndex = i->nextDrawObject;
+ i->nextDrawObject = 0;
+ return;
+ }
+
+ i = findObject(*blockItemIndex);
+ blockItemIndex = &i->nextDrawObject;
+ }
+}
+
+void LoLEngine::assignObjectToBlock(uint16 *assignedBlockObjects, uint16 id) {
+ LoLObject *t = findObject(id);
+ t->nextAssignedObject = *assignedBlockObjects;
+ *assignedBlockObjects = id;
+}
+
+void LoLEngine::giveCredits(int credits, int redraw) {
+ if (redraw)
+ snd_playSoundEffect(101, -1);
+
+ int t = credits / 30;
+ if (!t)
+ t = 1;
+
+ int cnt = 0;
+
+ while (credits) {
+ if (t > credits)
+ t = credits;
+
+ if (_credits < 60 && t > 0) {
+ cnt = 0;
+
+ do {
+ if (_credits < 60) {
+ int d = _stashSetupData[_credits % 12] - _credits / 12;
+ if (d < 0)
+ d += 5;
+ _moneyColumnHeight[d]++;
+ }
+ _credits++;
+ } while (++cnt < t);
+ } else if (_credits >= 60) {
+ _credits += t;
+ }
+
+ if (redraw) {
+ gui_drawMoneyBox(6);
+ if (credits)
+ delay(_tickLength, 1);
+ }
+ credits -= t;
+ }
+}
+
+void LoLEngine::takeCredits(int credits, int redraw) {
+ if (redraw)
+ snd_playSoundEffect(101, -1);
+
+ if (credits > _credits)
+ credits = _credits;
+
+ int t = credits / 30;
+ if (!t)
+ t = 1;
+
+ int cnt = 0;
+
+ while (credits && _credits > 0) {
+ if (t > credits)
+ t = credits;
+
+ if (_credits - t < 60 && t > 0) {
+ cnt = 0;
+
+ do {
+ if (--_credits < 60) {
+ int d = _stashSetupData[_credits % 12] - _credits / 12;
+ if (d < 0)
+ d += 5;
+ _moneyColumnHeight[d]--;
+ }
+ } while (++cnt < t);
+ } else if (_credits - t >= 60) {
+ _credits -= t;
+ }
+
+ if (redraw) {
+ gui_drawMoneyBox(6);
+ if (credits)
+ delay(_tickLength, 1);
+ }
+ credits -= t;
+ }
+}
+
+Item LoLEngine::makeItem(int itemType, int curFrame, int flags) {
+ int cnt = 0;
+ int r = 0;
+ Item i = 1;
+
+ for (; i < 400; i++) {
+ if (_itemsInPlay[i].shpCurFrame_flg & 0x8000) {
+ cnt = 0;
+ break;
+ }
+
+ if (_itemsInPlay[i].level < 1 || _itemsInPlay[i].level > 29 || _itemsInPlay[i].level == _currentLevel)
+ continue;
+
+ int diff = ABS(_currentLevel - _itemsInPlay[i].level);
+
+ if (diff <= cnt)
+ continue;
+
+ bool t = false;
+ for (Item ii = i; ii && !t; ii = _itemsInPlay[ii].nextAssignedObject)
+ t = isItemMoveable(ii);
+
+ if (t) {
+ cnt = diff;
+ r = i;
+ }
+ }
+
+ Item slot = i;
+ if (cnt) {
+ slot = 0;
+ if (isItemMoveable(r)) {
+ if (_itemsInPlay[r].nextAssignedObject)
+ _itemsInPlay[_itemsInPlay[r].nextAssignedObject].level = _itemsInPlay[r].level;
+ deleteItem(r);
+ slot = r;
+ } else {
+ for (uint16 ii = _itemsInPlay[r].nextAssignedObject; ii; ii = _itemsInPlay[ii].nextAssignedObject) {
+ if (!isItemMoveable(ii))
+ continue;
+ _itemsInPlay[r].nextAssignedObject = _itemsInPlay[ii].nextAssignedObject;
+ deleteItem(ii);
+ slot = ii;
+ break;
+ }
+ }
+ }
+
+ memset(&_itemsInPlay[slot], 0, sizeof(LoLItem));
+
+ _itemsInPlay[slot].itemPropertyIndex = itemType;
+ _itemsInPlay[slot].shpCurFrame_flg = (curFrame & 0x1FFF) | flags;
+ _itemsInPlay[slot].level = -1;
+
+ return slot;
+}
+
+void LoLEngine::placeMoveLevelItem(Item itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight) {
+ calcCoordinates(_itemsInPlay[itemIndex].x, _itemsInPlay[itemIndex].y, block, xOffs, yOffs);
+
+ if (_itemsInPlay[itemIndex].block)
+ removeLevelItem(itemIndex, _itemsInPlay[itemIndex].block);
+
+ if (_currentLevel == level) {
+ setItemPosition(itemIndex, _itemsInPlay[itemIndex].x, _itemsInPlay[itemIndex].y, flyingHeight, 1);
+ } else {
+ _itemsInPlay[itemIndex].level = level;
+ _itemsInPlay[itemIndex].block = block;
+ _itemsInPlay[itemIndex].flyingHeight = flyingHeight;
+ _itemsInPlay[itemIndex].shpCurFrame_flg |= 0x4000;
+ }
+}
+
+bool LoLEngine::addItemToInventory(Item itemIndex) {
+ int pos = 0;
+ int i = 0;
+
+ for (; i < 48; i++) {
+ pos = _inventoryCurItem + i;
+ if (pos > 47)
+ pos -= 48;
+
+ if (!_inventory[pos])
+ break;
+ }
+
+ if (i == 48)
+ return false;
+
+ while ((_inventoryCurItem > pos) || ((_inventoryCurItem + 9) <= pos)) {
+ if (++_inventoryCurItem > 47)
+ _inventoryCurItem -= 48;
+ gui_drawInventory();
+ }
+
+ assert(pos >= 0 && pos < 48);
+ _inventory[pos] = itemIndex;
+ gui_drawInventory();
+
+ return true;
+}
+
+bool LoLEngine::isItemMoveable(Item itemIndex) {
+ if (!(_itemsInPlay[itemIndex].shpCurFrame_flg & 0x4000))
+ return false;
+
+ if (_itemProperties[_itemsInPlay[itemIndex].itemPropertyIndex].flags & 4)
+ return false;
+
+ return true;
+
+}
+
+void LoLEngine::deleteItem(Item itemIndex) {
+ memset(&_itemsInPlay[itemIndex], 0, sizeof(LoLItem));
+ _itemsInPlay[itemIndex].shpCurFrame_flg |= 0x8000;
+}
+
+void LoLEngine::runItemScript(int charNum, Item item, int flags, int next, int reg4) {
+ EMCState scriptState;
+ memset(&scriptState, 0, sizeof(EMCState));
+
+ uint8 func = item ? _itemProperties[_itemsInPlay[item].itemPropertyIndex].itemScriptFunc : 3;
+ if (func == 0xFF)
+ return;
+
+ _emc->init(&scriptState, &_itemScript);
+ _emc->start(&scriptState, func);
+
+ scriptState.regs[0] = flags;
+ scriptState.regs[1] = charNum;
+ scriptState.regs[2] = item;
+ scriptState.regs[3] = next;
+ scriptState.regs[4] = reg4;
+
+ if (_emc->isValid(&scriptState)) {
+ if (*(scriptState.ip - 1) & flags) {
+ while (_emc->isValid(&scriptState))
+ _emc->run(&scriptState);
+ }
+ }
+}
+
+void LoLEngine::setHandItem(Item itemIndex) {
+ if (itemIndex && _itemProperties[_itemsInPlay[itemIndex].itemPropertyIndex].flags & 0x80) {
+ runItemScript(-1, itemIndex, 0x400, 0, 0);
+ if (_itemsInPlay[itemIndex].shpCurFrame_flg & 0x8000)
+ itemIndex = 0;
+ }
+
+ int mouseOffs = 0;
+
+ if (itemIndex && !(_flagsTable[31] & 0x02)) {
+ mouseOffs = 10;
+ if (!_currentControlMode || textEnabled())
+ _txt->printMessage(0, getLangString(0x403E), getLangString(_itemProperties[_itemsInPlay[itemIndex].itemPropertyIndex].nameStringId));
+ }
+
+ _itemInHand = itemIndex;
+ _screen->setMouseCursor(mouseOffs, mouseOffs, getItemIconShapePtr(itemIndex));
+}
+
+bool LoLEngine::itemEquipped(int charNum, uint16 itemType) {
+ if (charNum < 0 || charNum > 3)
+ return false;
+
+ if (!(_characters[charNum].flags & 1))
+ return false;
+
+ for (int i = 0; i < 11; i++) {
+ if (!_characters[charNum].items[i])
+ continue;
+
+ if (_itemsInPlay[_characters[charNum].items[i]].itemPropertyIndex == itemType)
+ return true;
+ }
+
+ return false;
+}
+
+void LoLEngine::setItemPosition(Item item, uint16 x, uint16 y, int flyingHeight, int moveable) {
+ if (!flyingHeight) {
+ x = (x & 0xFFC0) | 0x40;
+ y = (y & 0xFFC0) | 0x40;
+ }
+
+ uint16 block = calcBlockIndex(x, y);
+ _itemsInPlay[item].x = x;
+ _itemsInPlay[item].y = y;
+ _itemsInPlay[item].block = block;
+ _itemsInPlay[item].flyingHeight = flyingHeight;
+
+ if (moveable)
+ _itemsInPlay[item].shpCurFrame_flg |= 0x4000;
+ else
+ _itemsInPlay[item].shpCurFrame_flg &= 0xBFFF;
+
+
+ assignItemToBlock(&_levelBlockProperties[block].assignedObjects, item);
+ reassignDrawObjects(_currentDirection, item, &_levelBlockProperties[block], false);
+
+ if (moveable)
+ runLevelScriptCustom(block, 0x80, -1, item, 0, 0);
+
+ checkSceneUpdateNeed(block);
+}
+
+void LoLEngine::removeLevelItem(Item item, int block) {
+ removeAssignedObjectFromBlock(&_levelBlockProperties[block], item);
+ removeDrawObjectFromBlock(&_levelBlockProperties[block], item);
+ runLevelScriptCustom(block, 0x100, -1, item, 0, 0);
+ _itemsInPlay[item].block = 0;
+ _itemsInPlay[item].level = 0;
+}
+
+bool LoLEngine::launchObject(int objectType, Item item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c) {
+ int sp = checkDrawObjectSpace(_partyPosX, _partyPosY, startX, startY);
+ FlyingObject *t = _flyingObjects;
+ int slot = -1;
+ int i = 0;
+
+ for (; i < 8; i++) {
+ if (!t->enable) {
+ sp = -1;
+ break;
+ }
+
+ int csp = checkDrawObjectSpace(_partyPosX, _partyPosY, t->x, t->y);
+ if (csp > sp) {
+ sp = csp;
+ slot = i;
+ }
+ t++;
+ }
+
+ if (sp != -1 && slot != -1) {
+ i = slot;
+
+ t = &_flyingObjects[i];
+ endObjectFlight(t, startX, startY, 8);
+ }
+
+ if (i == 8)
+ return false;
+
+ t->enable = 1;
+ t->objectType = objectType;
+ t->item = item;
+ t->x = startX;
+ t->y = startY;
+ t->flyingHeight = flyingHeight;
+ t->direction = direction;
+ t->distance = 255;
+ t->attackerId = attackerId;
+ t->flags = 7;
+ t->wallFlags = 2;
+ t->c = c;
+
+ if (attackerId != -1) {
+ if (attackerId & 0x8000) {
+ t->flags &= 0xFD;
+ } else {
+ t->flags &= 0xFB;
+ increaseExperience(attackerId, 1, 2);
+ }
+ }
+
+ updateObjectFlightPosition(t);
+
+ return true;
+}
+
+void LoLEngine::endObjectFlight(FlyingObject *t, int x, int y, int collisionType) {
+ int cx = x;
+ int cy = y;
+ uint16 block = calcBlockIndex(t->x, t->y);
+ removeAssignedObjectFromBlock(&_levelBlockProperties[block], t->item);
+ removeDrawObjectFromBlock(&_levelBlockProperties[block], t->item);
+
+ if (collisionType == 1) {
+ cx = t->x;
+ cy = t->y;
+ }
+
+ if (t->objectType == 0 || t->objectType == 1) {
+ objectFlightProcessHits(t, cx, cy, collisionType);
+ t->x = (cx & 0xFFC0) | 0x40;
+ t->y = (cy & 0xFFC0) | 0x40;
+ t->flyingHeight = 0;
+ updateObjectFlightPosition(t);
+ }
+
+ t->enable = 0;
+}
+
+void LoLEngine::processObjectFlight(FlyingObject *t, int x, int y) {
+ int bl = calcBlockIndex(t->x, t->y);
+ LevelBlockProperty *l = &_levelBlockProperties[bl];
+ removeAssignedObjectFromBlock(l, t->item);
+ removeDrawObjectFromBlock(l, t->item);
+ t->x = x;
+ t->y = y;
+ updateObjectFlightPosition(t);
+ checkSceneUpdateNeed(bl);
+}
+
+void LoLEngine::updateObjectFlightPosition(FlyingObject *t) {
+ if (t->objectType == 0) {
+ setItemPosition(t->item, t->x, t->y, t->flyingHeight, (t->flyingHeight == 0) ? 1 : 0);
+ } else if (t->objectType == 1) {
+ if (t->flyingHeight == 0) {
+ deleteItem(t->item);
+ checkSceneUpdateNeed(calcBlockIndex(t->x, t->y));
+ } else {
+ setItemPosition(t->item, t->x, t->y, t->flyingHeight, 0);
+ }
+ }
+}
+
+void LoLEngine::objectFlightProcessHits(FlyingObject *t, int x, int y, int collisionType) {
+ if (collisionType == 1) {
+ runLevelScriptCustom(calcNewBlockPosition(_itemsInPlay[t->item].block, t->direction >> 1), 0x8000, -1, t->item, 0, 0);
+
+ } else if (collisionType == 2) {
+ if (_itemProperties[_itemsInPlay[t->item].itemPropertyIndex].flags & 0x4000) {
+ uint16 obj = _levelBlockProperties[_itemsInPlay[t->item].block].assignedObjects;
+ while (obj & 0x8000) {
+ runItemScript(t->attackerId, t->item, 0x8000, obj, 0);
+ obj = findObject(obj)->nextAssignedObject;
+ }
+
+ } else {
+ runItemScript(t->attackerId, t->item, 0x8000, getNearestMonsterFromPos(x, y), 0);
+ }
+
+ } else if (collisionType == 4) {
+ _partyAwake = true;
+ if (_itemProperties[_itemsInPlay[t->item].itemPropertyIndex].flags & 0x4000) {
+ for (int i = 0; i < 4; i++) {
+ if (_characters[i].flags & 1)
+ runItemScript(t->attackerId, t->item, 0x8000, i, 0);
+ }
+ } else {
+ runItemScript(t->attackerId, t->item, 0x8000, getNearestPartyMemberFromPos(x, y), 0);
+ }
+ }
+}
+
+void LoLEngine::updateFlyingObject(FlyingObject *t) {
+ int x = 0;
+ int y = 0;
+ getNextStepCoords(t->x, t->y, x, y, t->direction);
+ /* WORKAROUND:
+ Large fireballs cast by the "birds" in white tower level 2 and by the "wraith knights" in castle cimmeria
+ level 1 (or possible other objects with flag 0x4000) could not fly through corridors in ScummVM and would
+ be terminated prematurely. The original code (all versions) involuntarily circumvents this via a bug in the
+ next line of code.
+ The original checks for _itemProperties[t->item].flags instead of _itemProperties[_itemsInPlay[t->item].itemPropertyIndex].flags.
+ This leads to more or less unpredictable object widths. The large fireballs will usually get a width of 63
+ instead of 256 making them work just fine in the original.
+
+ I have fixed this by setting an object width of 63 of here. This produces results faithful to the original
+ at least.
+
+ Other methods of working around this issue don't make too much sense. An object with a width of 256
+ could never fly through corridors, since 256 is also the width of a block. Aligning the fireballs to the
+ middle of a block (or making the monsters align to the middle before casting them) wouldn't help here
+ (and wouldn't be faithful to the original either).
+ */
+ int collisionType = checkBlockBeforeObjectPlacement(x, y, /*_itemProperties[_itemsInPlay[t->item].itemPropertyIndex].flags & 0x4000 ? 256 :*/ 63, t->flags, t->wallFlags);
+ if (collisionType) {
+ endObjectFlight(t, x, y, collisionType);
+ } else {
+ if (--t->distance) {
+ processObjectFlight(t, x, y);
+ } else {
+ endObjectFlight(t, x, y, 8);
+ }
+ }
+}
+
+void LoLEngine::assignItemToBlock(uint16 *assignedBlockObjects, int id) {
+ while (*assignedBlockObjects & 0x8000) {
+ LoLObject *tmp = findObject(*assignedBlockObjects);
+ assignedBlockObjects = &tmp->nextAssignedObject;
+ }
+
+ LoLObject *newObject = findObject(id);
+ newObject->nextAssignedObject = *assignedBlockObjects;
+ ((LoLItem *)newObject)->level = -1;
+ *assignedBlockObjects = id;
+}
+
+int LoLEngine::checkDrawObjectSpace(int x1, int y1, int x2, int y2) {
+ int dx = x1 - x2;
+ if (dx < 0)
+ dx = -dx;
+
+ int dy = y1 - y2;
+ if (dy < 0)
+ dy = -dy;
+
+ return dx + dy;
+}
+
+int LoLEngine::checkSceneForItems(uint16 *blockDrawObjects, int color) {
+ while (*blockDrawObjects) {
+ if (!(*blockDrawObjects & 0x8000)) {
+ if (!--color)
+ return *blockDrawObjects;
+ }
+
+ LoLObject *i = findObject(*blockDrawObjects);
+ blockDrawObjects = &i->nextDrawObject;
+ }
+
+ return -1;
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_LOL