/* 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. * * $URL$ * $Id$ * */ #ifdef ENABLE_LOL #include "kyra/lol.h" #include "kyra/screen_lol.h" namespace Kyra { 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; Item ii = i; while (ii && !t) { t = testUnkItemFlags(ii); if (t) break; else ii = _itemsInPlay[ii - 1].nextAssignedObject; } if (t) { cnt = diff; r = i; } } Item slot = i; if (cnt) { slot = r; if (testUnkItemFlags(r)) { if (_itemsInPlay[r].nextAssignedObject) _itemsInPlay[_itemsInPlay[r].nextAssignedObject].level = _itemsInPlay[r].level; deleteItem(r); slot = r; } else { int ii = _itemsInPlay[slot].nextAssignedObject; while (ii) { if (testUnkItemFlags(ii)) { _itemsInPlay[slot].nextAssignedObject = _itemsInPlay[ii].nextAssignedObject; deleteItem(ii); slot = ii; break; } else { slot = ii; } ii = _itemsInPlay[slot].nextAssignedObject; } } } memset(&_itemsInPlay[slot], 0, sizeof(ItemInPlay)); _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(); } _inventory[pos] = itemIndex; gui_drawInventory(); return true; } bool LoLEngine::testUnkItemFlags(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(ItemInPlay)); _itemsInPlay[itemIndex].shpCurFrame_flg |= 0x8000; } ItemInPlay *LoLEngine::findObject(uint16 index) { if (index & 0x8000) return (ItemInPlay *)&_monsters[index & 0x7fff]; else return &_itemsInPlay[index]; } 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 b) { 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 (b) _itemsInPlay[item].shpCurFrame_flg |= 0x4000; else _itemsInPlay[item].shpCurFrame_flg &= 0xbfff; assignItemToBlock(&_levelBlockProperties[block].assignedObjects, item); reassignDrawObjects(_currentDirection, item, &_levelBlockProperties[block], false); if (b) 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 objectOnNextBlock) { int cx = x; int cy = y; uint16 block = calcBlockIndex(t->x, t->y); removeAssignedObjectFromBlock(&_levelBlockProperties[block], t->item); removeDrawObjectFromBlock(&_levelBlockProperties[block], t->item); if (objectOnNextBlock == 1) { cx = t->x; cy = t->y; } if (t->objectType == 0 || t->objectType == 1) { objectFlightProcessHits(t, cx, cy, objectOnNextBlock); 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 objectOnNextBlock) { uint16 r = 0; if (objectOnNextBlock == 1) { runLevelScriptCustom(calcNewBlockPosition(_itemsInPlay[t->item].block, t->direction >> 1), 0x8000, -1, t->item, 0, 0); } else if (objectOnNextBlock == 2) { if (_itemProperties[_itemsInPlay[t->item].itemPropertyIndex].flags & 0x4000) { int o = _levelBlockProperties[_itemsInPlay[t->item].block].assignedObjects; while (o & 0x8000) { ItemInPlay *i = findObject(o); o = i->nextAssignedObject; runItemScript(t->attackerId, t->item, 0x8000, o, 0); } } else { r = getNearestMonsterFromPos(x, y); runItemScript(t->attackerId, t->item, 0x8000, r, 0); } } else if (objectOnNextBlock == 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 { r = getNearestPartyMemberFromPos(x, y); runItemScript(t->attackerId, t->item, 0x8000, r, 0); } } } void LoLEngine::updateFlyingObject(FlyingObject *t) { int x = 0; int y = 0; getNextStepCoords(t->x, t->y, x, y, t->direction); // WORKAROUND: The next line seems to be bugged in the original code. I have fixed it in a way that at least seems to work fine. int objectOnNextBlock = checkBlockBeforeObjectPlacement(x, y, _itemProperties[_itemsInPlay[t->item].itemPropertyIndex].flags & 0x4000 ? 127 : 63, t->flags, t->wallFlags); if (objectOnNextBlock) { endObjectFlight(t, x, y, objectOnNextBlock); } else { if (--t->distance) { processObjectFlight(t, x, y); } else { endObjectFlight(t, x, y, 8); } } } void LoLEngine::assignItemToBlock(uint16 *assignedBlockObjects, int id) { while (*assignedBlockObjects & 0x8000) { ItemInPlay *tmp = findObject(*assignedBlockObjects); assignedBlockObjects = &tmp->nextAssignedObject; } ItemInPlay *newObject = findObject(id); newObject->nextAssignedObject = *assignedBlockObjects; newObject->level = -1; *assignedBlockObjects = id; } int LoLEngine::checkDrawObjectSpace(int itemX, int itemY, int partyX, int partyY) { int a = itemX - partyX; if (a < 0) a = -a; int b = itemY - partyY; if (b < 0) b = -b; return a + b; } int LoLEngine::checkSceneForItems(uint16 *blockDrawObjects, int colour) { while (*blockDrawObjects) { if (!(*blockDrawObjects & 0x8000)) { if (!--colour) return *blockDrawObjects; } ItemInPlay *i = findObject(*blockDrawObjects); blockDrawObjects = &i->nextDrawObject; } return -1; } } // End of namespace Kyra #endif // ENABLE_LOL