scummvm/engines/kyra/items_lol.cpp
Florian Kagerer 09f77eb464 LOL: - fix invalid mem access
- also extend LordHoto's recent item code changes to LOL

svn-id: r53740
2010-10-23 16:01:40 +00:00

542 lines
13 KiB
C++

/* 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