void ScummEngine::setOwnerOf(int obj, int owner) { ScriptSlot *ss; // In Sam & Max this is necessary, or you won't get your stuff back // from the Lost and Found tent after riding the Cone of Tragedy. But // it probably applies to all V6+ games. See bugs #493153 and #907113. // FT disassembly is checked, behaviour is correct. [sev] int arg = (_game.version >= 6) ? obj : 0; // WORKAROUND for bug #1917981: Game crash when finishing Indy3 demo. // Script 94 tries to empty the inventory but does so in a bogus way. // This causes it to try to remove object 0 from the inventory. if (_game.id == GID_PASS && obj == 0 && vm.slot[_currentScript].number == 94) return; assert(obj > 0); if (owner == 0) { clearOwnerOf(obj); // FIXME: See bug #1535358 and many others. Essentially, the following // code, while matching disasm of various versions of the SCUMM engine, // is total bullocks, and leads to odd crashes due to out-of-bounds // array (read) access. Three "famous" crashes were caused by this: // Monkey Island 1: Using meat with flower // FOA: Using ribcage with another item // DOTT: Using stamp with contract // // The bad code: // if (ss->where == WIO_INVENTORY && _inventory[ss->number] == obj) { // That check makes no sense at all: _inventory only contains 80 items, // which are in the order the player picked up items. We can only // guess that the SCUMM coders meant to write // if (ss->where == WIO_INVENTORY && ss->number == obj) { // which would ensure that an object script that nukes itself gets // stopped. Alas, we can't just make that change, since it could // lead to new regressions. // Another fix would be to completely remove this check, which should // not cause much problems, since it'll only succeed by pure chance. // // For now we follow a more defensive route: We perform the check // if ss->number is small enough. ss = &vm.slot[_currentScript]; if (ss->where == WIO_INVENTORY) { if (ss->number < _numInventory && _inventory[ss->number] == obj) { error("Odd setOwnerOf case #1: Please report to Fingolfin where you encountered this"); putOwner(obj, 0); runInventoryScript(arg); stopObjectCode(); return; } if (ss->number == obj) error("Odd setOwnerOf case #2: Please report to Fingolfin where you encountered this"); } } putOwner(obj, owner); runInventoryScript(arg); }
void ScummEngine_v0::o_pickupObject() { int obj = fetchScriptByte(); if (!obj) obj = _cmdObject; /* Don't take an object twice */ if (whereIsObject(obj) == WIO_INVENTORY) return; addObjectToInventory(obj, _roomResource); markObjectRectAsDirty(obj); putOwner(obj, VAR(VAR_EGO)); putState(obj, getState(obj) | kObjectState_08 | kObjectStateUntouchable); clearDrawObjectQueue(); runInventoryScript(1); }
void ScummEngine_v70he::o70_pickupObject() { int obj, room; room = pop(); obj = pop(); if (room == 0) room = getObjectRoom(obj); addObjectToInventory(obj, room); putOwner(obj, VAR(VAR_EGO)); if (_game.heversion <= 70) { putClass(obj, kObjectClassUntouchable, 1); putState(obj, 1); markObjectRectAsDirty(obj); clearDrawObjectQueue(); } runInventoryScript(obj); /* Difference */ }
void ScummEngine_v4::o4_pickupObject() { int obj = getVarOrDirectWord(PARAM_1); if (obj < 1) { error("pickupObjectOld received invalid index %d (script %d)", obj, vm.slot[_currentScript].number); } if (getObjectIndex(obj) == -1) return; if (whereIsObject(obj) == WIO_INVENTORY) // Don't take an object twice return; // debug(0, "adding %d from %d to inventoryOld", obj, _currentRoom); addObjectToInventory(obj, _roomResource); markObjectRectAsDirty(obj); putOwner(obj, VAR(VAR_EGO)); putClass(obj, kObjectClassUntouchable, 1); putState(obj, 1); clearDrawObjectQueue(); runInventoryScript(1); }