diff options
32 files changed, 1620 insertions, 1477 deletions
| diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index eb23c30ebe..dee601b273 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -42,6 +42,14 @@ namespace Scumm {  byte Actor::kInvalidBox = 0; +static const byte v0ActorTalkArray[0x19] = { +	0x00, 0x06, 0x06, 0x06, 0x06, +	0x06, 0x06, 0x00, 0x46, 0x06, +	0x06, 0x06, 0x06, 0xFF, 0xFF, +	0x06, 0xC0, 0x06, 0x06, 0x00, +	0xC0, 0xC0, 0x00, 0x06, 0x06 +}; +  Actor::Actor(ScummEngine *scumm, int id) :  	_vm(scumm), _number(id) {  	assert(_vm != 0); @@ -167,6 +175,21 @@ void Actor_v2::initActor(int mode) {  	_talkStopFrame = 4;  } +void Actor_v0::initActor(int mode) { +	Actor_v2::initActor(mode); + +	_costCommandNew = 0xFF; +	_costCommand = 0xFF; +	_miscflags = 0; +	_speaking = 0; + +	_animFrameRepeat = 0; +	for (int i = 0; i < 8; ++i) { +		_limbFrameRepeatNew[i] = 0; +		_limbFrameRepeat[i] = 0; +		_limb_flipped[i] = false; +	} +}  void Actor::setBox(int box) {  	_walkbox = box; @@ -226,12 +249,9 @@ void Actor::stopActorMoving() {  	if (_walkScript)  		_vm->stopScript(_walkScript); -	// V0 Games will walk on the spot if the actor is stopped mid-walk -	// So we must set the stand still frame -	if (_vm->_game.version == 0) -		startWalkAnim(3, -1); -  	_moving = 0; +	if (_vm->_game.version == 0) +		setDirection(_facing);  }  void Actor::setActorWalkSpeed(uint newSpeedX, uint newSpeedY) { @@ -286,7 +306,7 @@ int Actor::calcMovementFactor(const Common::Point& next) {  		deltaYFactor = 0;  	} -	if ((uint) ABS((int)(deltaXFactor >> 16)) > _speedx) { +	if ((uint) ABS(deltaXFactor) > (_speedx << 16))	{  		deltaXFactor = _speedx << 16;  		if (diffX < 0)  			deltaXFactor = -deltaXFactor; @@ -319,6 +339,9 @@ int Actor::actorWalkStep() {  	int distX, distY;  	int nextFacing; +	if (_vm->_game.version == 0) +		((Actor_v0 *)this)->_animFrameRepeat = -1; +  	_needRedraw = true;  	nextFacing = updateActorDirection(true); @@ -327,6 +350,10 @@ int Actor::actorWalkStep() {  			startWalkAnim(1, nextFacing);  		}  		_moving |= MF_IN_LEG; + +		// V0: Don't move during the turn +		if (_vm->_game.version == 0) +			return 0;  	}  	if (_walkbox != _walkdata.curbox && _vm->checkXYInBoxBounds(_walkdata.curbox, _pos.x, _pos.y)) { @@ -361,6 +388,10 @@ int Actor::actorWalkStep() {  		_moving &= ~MF_IN_LEG;  		return 0;  	} + +	if (_vm->_game.version == 0) +		((Actor_v0 *)this)->animateActor(newDirToOldDir(_facing)); +  	return 1;  } @@ -536,23 +567,101 @@ void Actor::walkActor() {  	calcMovementFactor(_walkdata.dest);  } +bool Actor_v2::checkWalkboxesHaveDirectPath(Common::Point &foundPath) { +	// only MM v0 supports walking in direct line between walkboxes. +	// MM v1 already does not support it anymore. +	return false; +} + +bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End,  +	const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result)  +{ +	const Common::Point v1 = line1End - line1Start; // line1(n1) = line1Start + n1 * v1 +	const Common::Point v2 = line2End - line2Start; // line2(n2) = line2Start + n2 * v2 +	 +	double det = v2.x * v1.y - v1.x * v2.y; +	if (det == 0) +		return false; + +	double n1 = ((double)v2.x * (line2Start.y - line1Start.y) -  +		         (double)v2.y * (line2Start.x - line1Start.x)) / det; +	double n2 = ((double)v1.x * (line2Start.y - line1Start.y) -  +		         (double)v1.y * (line2Start.x - line1Start.x)) / det; + +	// both coefficients have to be in [0, 1], otherwise the intersection is +	// not inside of at least one of the two line segments +	if (n1 < 0.0 || n1 > 1.0 || n2 < 0.0 || n2 > 1.0) +		return false; + +	result.x = line1Start.x + (int)(n1 * v1.x); +	result.y = line1Start.y + (int)(n1 * v1.y); +	return true; +} + +/* + * MM v0 allows the actor to walk in a direct line between boxes to the target  + * if actor and target share a horizontal or vertical corridor. + * If such a corridor is found the actor is not forced to go horizontally or  + * vertically from one box to the next but can also walk diagonally. + * + * Note: the original v0 interpreter sets the target destination for diagonal  + * walking only once and then rechecks whenever the actor reaches a new box if the + * walk destination is still suitable for the current box.  + * ScummVM does not perform such a check, so it is possible to leave the walkboxes  + * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water)  + * or the medical room (actor walks over examination table). + * To solve this we intersect the new walk destination with the actor's walkbox borders, + * so a recheck is done when the actor leaves his box. This is done by the + * intersectLineSegments() routine calls. + */ +bool Actor_v0::checkWalkboxesHaveDirectPath(Common::Point &foundPath) { +	BoxCoords boxCoords = _vm->getBoxCoordinates(_walkbox); +	BoxCoords curBoxCoords = _vm->getBoxCoordinates(_walkdata.curbox); + +	// check if next walkbox is left or right to actor's box +	if (boxCoords.ll.x > curBoxCoords.lr.x || boxCoords.lr.x < curBoxCoords.ll.x) { +		// determine horizontal corridor gates +		int gateUpper = MAX(boxCoords.ul.y, curBoxCoords.ul.y); +		int gateLower = MIN(boxCoords.ll.y, curBoxCoords.ll.y); + +		// check if actor and target are in the same horizontal corridor between the boxes +		if ((_pos.y >= gateUpper && _pos.y <= gateLower) && +			(_walkdata.dest.y >= gateUpper && _walkdata.dest.y <= gateLower)) { +			if (boxCoords.ll.x > curBoxCoords.lr.x) // next box is left +				return intersectLineSegments(_pos, _walkdata.dest, boxCoords.ll, boxCoords.ul, foundPath); +			else // next box is right +				return intersectLineSegments(_pos, _walkdata.dest, boxCoords.lr, boxCoords.ur, foundPath); +		} +	// check if next walkbox is above or below actor's box +	} else if (boxCoords.ul.y > curBoxCoords.ll.y || boxCoords.ll.y < curBoxCoords.ul.y) { +		// determine vertical corridor gates +		int gateLeft = MAX(boxCoords.ll.x, curBoxCoords.ll.x); +		int gateRight = MIN(boxCoords.lr.x, curBoxCoords.lr.x); + +		// check if actor and target are in the same vertical corridor between the boxes +		if ((_pos.x >= gateLeft && _pos.x <= gateRight) && +			(_walkdata.dest.x >= gateLeft && _walkdata.dest.x <= gateRight)) { +			if (boxCoords.ul.y > curBoxCoords.ll.y) // next box is above +				return intersectLineSegments(_pos, _walkdata.dest, boxCoords.ul, boxCoords.ur, foundPath); +			else // next box is below +				return intersectLineSegments(_pos, _walkdata.dest, boxCoords.ll, boxCoords.lr, foundPath); +		} +	} + +	return false; +} +  void Actor_v2::walkActor() {  	Common::Point foundPath, tmp;  	int new_dir, next_box;  	if (_moving & MF_TURN) {  		new_dir = updateActorDirection(false); -		// FIXME: is this correct?  		if (_facing != new_dir) { - -			// Actor never stops walking when an object has been selected without this -			if (_vm->_game.version ==0) -				_moving = 0; -  			setDirection(new_dir); - -		} else +		} else {  			_moving = 0; +		}  		return;  	} @@ -588,14 +697,7 @@ void Actor_v2::walkActor() {  				_walkdata.curbox = next_box; -				// WORKAROUND: The route of the meteor landing in the introduction isn't correct. -				// MM V0 in contrast to MM V2 uses two walkboxes instead of just one. Hence a route -				// from walkbox 1 to 0 is calculated first. This causes the meteor to fly on a -				// horizontal line to walkbox 0 then vertically to the ground. -				// To fix this problem, the box-to-box routing has been disabled in room 33. -				if (_vm->_game.version == 0 && _vm->_currentRoom == 33) { -					foundPath = _walkdata.dest; -				} else { +				if (!checkWalkboxesHaveDirectPath(foundPath)) {  					getClosestPtOnBox(_vm->getBoxCoordinates(_walkdata.curbox), _pos.x, _pos.y, tmp.x, tmp.y);  					getClosestPtOnBox(_vm->getBoxCoordinates(_walkbox), tmp.x, tmp.y, foundPath.x, foundPath.y);  				} @@ -781,7 +883,7 @@ int Actor::remapDirection(int dir, bool is_walking) {  			return 180;  		} -		// MM C64 stores flags as a part of the mask +		// MM v0 stores flags as a part of the mask  		if (_vm->_game.version == 0) {  			mask = _vm->getMaskFromBox(_walkbox);  			// face the wall if climbing/descending a ladder @@ -857,16 +959,6 @@ void Actor::setDirection(int direction) {  	if (_costume == 0)  		return; -	// V0 MM -	if (_vm->_game.version == 0) { -		if (_moving) -			_vm->_costumeLoader->costumeDecodeData(this, _walkFrame, 0); -		else -			_vm->_costumeLoader->costumeDecodeData(this, _standFrame, 0); -		_needRedraw = true; -		return; -	} -  	// Update the costume for the new direction (and mark the actor for redraw)  	aMask = 0x8000;  	for (i = 0; i < 16; i++, aMask >>= 1) { @@ -879,6 +971,34 @@ void Actor::setDirection(int direction) {  	_needRedraw = true;  } +void Actor_v0::setDirection(int direction) { +	int dir = newDirToOldDir( direction ); +	int res = 0; + +	switch (dir) { +		case 0: +			res = 4;	// Left +			break; + +		case 1: +			res = 5;	// Right +			break; + +		case 2: +			res = 6;	// Face Away +			break; + +		default: +			res = 7;	// Face Camera +			break; +	} +	 +	_animFrameRepeat = -1; +	animateActor(res); +	if (_moving) +		animateCostume(); +} +  void Actor::faceToObject(int obj) {  	int x2, y2, dir; @@ -963,6 +1083,10 @@ void Actor::putActor(int dstX, int dstY, int newRoom) {  		if (isInCurrentRoom())  			showActor();  	} + +	// V0 always sets the actor to face the camera upon entering a room +	if (_vm->_game.version == 0) +		setDirection(oldDirToNewDir(2));  }  static bool inBoxQuickReject(const BoxCoords &box, int x, int y, int threshold) { @@ -1062,16 +1186,11 @@ static int checkXYInBoxBounds(int boxnum, int x, int y, int &destX, int &destY)  	// yDist must be divided by 4, as we are using 8x2 pixels  	// blocks for actor coordinates).  	int xDist = ABS(x - destX); -	int yDist; +	int yDist = ABS(y - destY) / 4;  	int dist; -	// MM C64: This fixes the trunk bug (#3070065), as well -	// as the fruit bowl, however im not sure if its -	// the proper solution or not. -	if( g_scumm->_game.version == 0 ) -		yDist = ABS(y - destY); -	else -		yDist = ABS(y - destY) / 4; +	if (g_scumm->_game.version == 0) +		xDist *= 2;  	if (xDist < yDist)  		dist = (xDist >> 1) + yDist; @@ -1090,7 +1209,9 @@ AdjustBoxResult Actor_v2::adjustXYToBeInBox(const int dstX, const int dstY) {  	int numBoxes = _vm->getNumBoxes() - 1;  	int bestDist = 0xFF; -	for (int box = numBoxes; box >= 0; box--) { +	for (int i = 0; i <= numBoxes; i++) { +		// MM v0 prioritizes lower boxes, other engines higher boxes +		int box = (_vm->_game.version == 0 ? i : numBoxes - i);  		int foundX, foundY;  		int flags = _vm->getBoxFlags(box);  		if ((flags & kBoxInvisible) && !((flags & kBoxPlayerOnly) && !isPlayer())) @@ -1286,8 +1407,24 @@ void Actor::showActor() {  	_vm->ensureResourceLoaded(rtCostume, _costume);  	if (_vm->_game.version == 0) { +		Actor_v0 *a = ((Actor_v0 *)this); +		 +		a->_costCommand = a->_costCommandNew = 0xFF; + +		for (int i = 0; i < 8; ++i) { +			a->_limbFrameRepeat[i] = 0; +			a->_limbFrameRepeatNew[i] = 0; +		} +  		_cost.reset(); + +		a->_animFrameRepeat = 1; +		a->_speaking = 0; +  		startAnimActor(_standFrame); +		_visible = true; +		return; +  	} else if (_vm->_game.version <= 2) {  		_cost.reset();  		startAnimActor(_standFrame); @@ -1323,23 +1460,23 @@ static const byte v0ActorSounds[24] = {  	0x06, // Bernard  	0x06, // Wendy  	0x00, // Jeff -	0x46, // ??? +	0x46, // Radiation Suit  	0x06, // Dr Fred  	0x06, // Nurse Edna  	0x06, // Weird Ed  	0x06, // Dead Cousin Ted  	0xFF, // Purple Tentacle  	0xFF, // Green Tentacle -	0x06, // Meteor -	0xC0, // Plant -	0x06, // ??? -	0x06, // ??? -	0x00, // ??? -	0xC0, // ??? -	0xC0, // ??? -	0x00, // ??? -	0x06, // Sandy -	0x06, // ??? +	0x06, // Meteor police +	0xC0, // Meteor +	0x06, // Mark Eteer +	0x06, // Talkshow Host +	0x00, // Plant +	0xC0, // Meteor Radiation +	0xC0, // Edsel (small, outro) +	0x00, // Meteor (small, intro) +	0x06, // Sandy (Lab) +	0x06, // Sandy (Cut-Scene)  };  /* Used in Scumm v5 only. Play sounds associated with actors */ @@ -1355,7 +1492,10 @@ void ScummEngine::playActorSounds() {  			} else {  				sound = _actors[i]->_sound[0];  			} -			_sound->addSoundToQueue(sound); +			// fast mode will flood the queue with walk sounds +			if (!_fastMode) { +				_sound->addSoundToQueue(sound); +			}  			for (j = 1; j < _numActors; j++) {  				_actors[j]->_cost.soundCounter = 0;  			} @@ -1462,6 +1602,18 @@ void ScummEngine::processActors() {  				}  			}  		} +	} else if (_game.version == 0) { +		for (int j = 0; j < numactors; ++j) { +			for (int i = 0; i < numactors; ++i) { +				// Note: the plant is handled different in v0, the y value is not used. +				// In v1/2 this is done by the actor's elevation instead. +				int sc_actor1 = (_sortedActors[j]->_number == 19 ? 0 : _sortedActors[j]->getPos().y); +				int sc_actor2 = (_sortedActors[i]->_number == 19 ? 0 : _sortedActors[i]->getPos().y); +				if (sc_actor1 < sc_actor2) { +					SWAP(_sortedActors[i], _sortedActors[j]); +				} +			} +		}  	} else {  		for (int j = 0; j < numactors; ++j) {  			for (int i = 0; i < numactors; ++i) { @@ -1479,11 +1631,26 @@ void ScummEngine::processActors() {  	for (Actor** ac = _sortedActors; ac != end; ++ac) {  		Actor* a = *ac; -		// V0 MM: 0x057B  		if (_game.version == 0) { -			ActorC64 *A = (ActorC64*) a; -			if ((A->_speaking & 1)) -				A->_speaking ^= 0xFE; +			// 0x057B +			Actor_v0 *a0 = (Actor_v0*) a; +			if (a0->_speaking & 1) +				a0->_speaking ^= 0xFE; + +			// 0x22B5 +			if (a0->_miscflags & kActorMiscFlagHide) +				continue; + +			// Sound +			if (a0->_moving  && _currentRoom != 1 && _currentRoom != 44) { +				if (a0->_cost.soundPos == 0) +					a0->_cost.soundCounter++; + +				// Is this the correct location? +				// 0x073C +				if (v0ActorTalkArray[a0->_number] & 0x3F) +					a0->_cost.soundPos = (a0->_cost.soundPos + 1) % 3; +			}  		}  		// Draw and animate the actors, except those w/o a costume.  		// Note: We could 'optimize' this a little bit by only putting @@ -1779,6 +1946,25 @@ void Actor::startAnimActor(int f) {  	}  } +void Actor_v0::startAnimActor(int f) { +	if (f == _talkStartFrame) { +		if (v0ActorTalkArray[_number] & 0x40) +			return; + +		_speaking = 1; +		return; +	} + +	if (f == _talkStopFrame) { + +		_speaking = 0; +		return; +	} + +	if (f == _standFrame) +		setDirection(_facing); +} +  void Actor::animateActor(int anim) {  	int cmd, dir; @@ -1841,6 +2027,47 @@ void Actor::animateCostume() {  	}  } +void Actor_v0::limbFrameCheck(int limb) { +	if (_cost.frame[limb] == 0xFFFF) +		return; + +	if (_cost.start[limb] == _cost.frame[limb]) +		return; + +	// 0x25A4 +	_cost.start[limb] = _cost.frame[limb]; + +	_limbFrameRepeat[limb] = _limbFrameRepeatNew[limb]; + +	// 0x25C3 +	_cost.active[limb] = ((V0CostumeLoader*)_vm->_costumeLoader)->getFrame(this, limb); +	_cost.curpos[limb] = 0; + +	_needRedraw = true; +} + +void Actor_v0::animateCostume() { +	speakCheck(); + +	if (_vm->_costumeLoader->increaseAnims(this)) +		_needRedraw = true; +} + +void Actor_v0::speakCheck() { +	if (v0ActorTalkArray[_number] & 0x80) +		return; +	 +	int cmd = newDirToOldDir(_facing); + +	if (_speaking & 0x80) +		cmd += 0x0C; +	else +		cmd += 0x10; + +	_animFrameRepeat = -1; +	animateActor(cmd); +} +  #ifdef ENABLE_SCUMM_7_8  void Actor::animateLimb(int limb, int f) {  	// This methods is very similiar to animateCostume(). @@ -1994,7 +2221,7 @@ void ScummEngine::setTalkingActor(int i) {  		VAR(VAR_TALK_ACTOR) = i;  } -static const int c64MMActorTalkColor[25] = { +static const int v0MMActorTalkColor[25] = {  	1, 7, 2, 14, 8, 15, 3, 7, 7, 15, 1, 13, 1, 4, 5, 5, 4, 3, 1, 5, 1, 1, 1, 1, 7  };  static const int v1MMActorTalkColor[25] = { @@ -2006,7 +2233,7 @@ void ScummEngine::resetV1ActorTalkColor() {  	for (i = 1; i < _numActors; i++) {  		if (_game.version == 0) { -			_actors[i]->_talkColor = c64MMActorTalkColor[i]; +			_actors[i]->_talkColor = v0MMActorTalkColor[i];  		} else {  			_actors[i]->_talkColor = v1MMActorTalkColor[i];  		} @@ -2295,22 +2522,23 @@ static const char *const v0ActorNames_English[25] = {  	"Bernard",  	"Wendy",  	"Jeff", -	"", +	"", // Radiation Suit  	"Dr Fred",  	"Nurse Edna",  	"Weird Ed",  	"Dead Cousin Ted",  	"Purple Tentacle",  	"Green Tentacle", +	"", // Meteor Police  	"Meteor", -	"", -	"", -	"", +	"", // Mark Eteer +	"", // Talkshow Host  	"Plant", -	"", -	"", -	"", -	"Sandy" +	"", // Meteor Radiation +	"", // Edsel (small, outro) +	"", // Meteor (small, intro) +	"Sandy", // (Lab) +	"", // Sandy (Cut-Scene)  };  static const char *const v0ActorNames_German[25] = { @@ -2328,15 +2556,16 @@ static const char *const v0ActorNames_German[25] = {  	"Ted",  	"Lila Tentakel",  	"Gr<nes Tentakel", -	"Meteor",  	"", +	"Meteor",  	"",  	"",  	"Pflanze",  	"",  	"",  	"", -	"Sandy" +	"Sandy", +	"",  };  const byte *Actor::getActorName() { @@ -2467,6 +2696,8 @@ bool Actor::isPlayer() {  }  bool Actor_v2::isPlayer() { +	// isPlayer() is not supported by v0 +	assert(_vm->_game.version != 0);  	return _vm->VAR(42) <= _number && _number <= _vm->VAR(43);  } @@ -2621,16 +2852,63 @@ void ScummEngine_v71he::queueAuxEntry(int actorNum, int subIndex) {  }  #endif +void Actor_v0::animateActor(int anim) { +	int dir = -1; + +	switch (anim) { +		case 0x00: +		case 0x04: +			dir = 0; +			break; + +		case 0x01: +		case 0x05: +			dir = 1; +			break; + +		case 0x02: +		case 0x06: +			dir = 2; +			break; + +		case 0x03: +		case 0x07: +			dir = 3; +			break; + +		default: +			break; +	} + +	if (isInCurrentRoom()) { + +		_costCommandNew = anim; +		_vm->_costumeLoader->costumeDecodeData(this, 0, 0); +		 +		if (dir == -1) +			return; + +		_facing = normalizeAngle(oldDirToNewDir(dir)); + +	} else { + +		if (anim > 4 && anim <= 7) +			_facing = normalizeAngle(oldDirToNewDir(dir)); +	} +} -void ActorC64::saveLoadWithSerializer(Serializer *ser) { +void Actor_v0::saveLoadWithSerializer(Serializer *ser) {  	Actor::saveLoadWithSerializer(ser);  	static const SaveLoadEntry actorEntries[] = { -		MKLINE(ActorC64, _costCommand, sleByte, VER(84)), -		MKLINE(ActorC64, _costFrame, sleByte, VER(84)), -		MKLINE(ActorC64, _miscflags, sleByte, VER(84)), -		MKLINE(ActorC64, _speaking, sleByte, VER(84)), -		MKLINE(ActorC64, _speakingPrev, sleByte, VER(84)), +		MKLINE(Actor_v0, _costCommand, sleByte, VER(84)), +		MK_OBSOLETE(Actor_v0, _costFrame, sleByte, VER(84), VER(88)), +		MKLINE(Actor_v0, _miscflags, sleByte, VER(84)), +		MKLINE(Actor_v0, _speaking, sleByte, VER(84)), +		MK_OBSOLETE(Actor_v0, _speakingPrev, sleByte, VER(84), VER(88)), +		MKLINE(Actor_v0, _animFrameRepeat, sleByte, VER(89)), +		MKARRAY(Actor_v0, _limbFrameRepeatNew[0], sleInt8, 8, VER(89)), +		MKARRAY(Actor_v0, _limbFrameRepeat[0], sleInt8, 8, VER(89)),  		MKEND()  	}; diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h index 1584d0a78b..0ed239d005 100644 --- a/engines/scumm/actor.h +++ b/engines/scumm/actor.h @@ -202,13 +202,13 @@ public:  	void adjustActorPos();  	virtual AdjustBoxResult adjustXYToBeInBox(int dstX, int dstY); -	void setDirection(int direction); +	virtual void setDirection(int direction);  	void faceToObject(int obj);  	void turnToDirection(int newdir);  	virtual void walkActor();  	void drawActorCostume(bool hitTestMode = false);  	virtual void prepareDrawActorCostume(BaseCostumeRenderer *bcr); -	void animateCostume(); +	virtual void animateCostume();  	virtual void setActorCostume(int c);  	void animateLimb(int limb, int f); @@ -222,7 +222,7 @@ protected:  	void startWalkAnim(int cmd, int angle);  public:  	void runActorTalkScript(int f); -	void startAnimActor(int frame); +	virtual void startAnimActor(int frame);  	void remapActorPalette(int r_fact, int g_fact, int b_fact, int threshold);  	void remapActorPaletteColor(int slot, int color); @@ -333,33 +333,53 @@ public:  protected:  	virtual bool isPlayer();  	virtual void prepareDrawActorCostume(BaseCostumeRenderer *bcr); +	virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath);  }; -class ActorC64 : public Actor_v2 { +enum ActorV0MiscFlags { +	kActorMiscFlagStrong    = 0x01, // Kid is strong (Hunk-O-Matic used) +	kActorMiscFlagGTFriend  = 0x02, // Kid is green tentacle's friend (recording contract) +	kActorMiscFlagWatchedTV = 0x04, // Kid knows publisher's address (watched TV) +	kActorMiscFlagEdsEnemy  = 0x08, // Kid is not Weird Ed's friend +	kActorMiscFlag_10       = 0x10, // ??? +	kActorMiscFlag_20       = 0x20, // ??? +	kActorMiscFlagFreeze    = 0x40, // Stop moving +	kActorMiscFlagHide      = 0x80  // Kid is invisible (dead or in radiation suit) +}; + +class Actor_v0 : public Actor_v2 {  public: -	byte _costCommand, _costFrame; -	byte _miscflags; // 0x1: strong, 0x8: Ed's enemy, 0x40: stop moving, 0x80: hide(dead/radiation suit) -	byte _speaking, _speakingPrev; +	byte _costCommandNew; +	byte _costCommand; +	byte _miscflags; +	byte _speaking; + +	int8 _animFrameRepeat; +	int8 _limbFrameRepeatNew[8]; +	int8 _limbFrameRepeat[8]; + +	bool _limb_flipped[8];  public: -	ActorC64(ScummEngine *scumm, int id) : Actor_v2(scumm, id) { -		 _costCommand = 0; -		 _costFrame = 0; -		 _speaking = 0; -		 _speakingPrev = 0; -	} -	virtual void initActor(int mode) { -		Actor_v2::initActor(mode); -		if (mode == -1) { -			_miscflags = 0; -		} -	} +	Actor_v0(ScummEngine *scumm, int id) : Actor_v2(scumm, id) {} + +	virtual void initActor(int mode); +	virtual void animateActor(int anim); +	virtual void animateCostume(); + +	void limbFrameCheck(int limb); + +	void speakCheck(); +	virtual void setDirection(int direction); +	void startAnimActor(int f);  	// Used by the save/load system:  	virtual void saveLoadWithSerializer(Serializer *ser);  protected: - +	bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End,  +		const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result); +	virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath);  }; diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp index 64d4d7422c..f6d2a18f38 100644 --- a/engines/scumm/boxes.cpp +++ b/engines/scumm/boxes.cpp @@ -42,7 +42,7 @@ struct Box {				/* Internal walkbox file format */  			byte y1;  			byte y2;  			byte mask; -		} c64; +		} v0;  		struct {  			byte uy; @@ -181,7 +181,7 @@ byte ScummEngine::getMaskFromBox(int box) {  	if (_game.version == 8)  		return (byte) FROM_LE_32(ptr->v8.mask);  	else if (_game.version == 0) -		return ptr->c64.mask; +		return ptr->v0.mask;  	else if (_game.version <= 2)  		return ptr->v2.mask;  	else @@ -479,7 +479,7 @@ Box *ScummEngine::getBoxBaseAddr(int box) {  	assertRange(0, box, ptr[0] - 1, "box");  	if (_game.version == 0) -		return (Box *)(ptr + box * SIZEOF_BOX_C64 + 1); +		return (Box *)(ptr + box * SIZEOF_BOX_V0 + 1);  	else if (_game.version <= 2)  		return (Box *)(ptr + box * SIZEOF_BOX_V2 + 1);  	else if (_game.version == 3) @@ -602,19 +602,19 @@ BoxCoords ScummEngine::getBoxCoordinates(int boxnum) {  			SWAP(box->ll, box->lr);  		}  	} else if (_game.version == 0) { -		box->ul.x = bp->c64.x1; -		box->ul.y = bp->c64.y1; -		box->ur.x = bp->c64.x2; -		box->ur.y = bp->c64.y1; +		box->ul.x = bp->v0.x1; +		box->ul.y = bp->v0.y1; +		box->ur.x = bp->v0.x2; +		box->ur.y = bp->v0.y1; -		box->ll.x = bp->c64.x1; -		box->ll.y = bp->c64.y2; -		box->lr.x = bp->c64.x2; -		box->lr.y = bp->c64.y2; +		box->ll.x = bp->v0.x1; +		box->ll.y = bp->v0.y2; +		box->lr.x = bp->v0.x2; +		box->lr.y = bp->v0.y2; -		if ((bp->c64.mask & 0x88) == 0x88) { +		if ((bp->v0.mask & 0x88) == 0x88) {  			// walkbox for (right/left) corner -			if (bp->c64.mask & 0x04) +			if (bp->v0.mask & 0x04)  				box->ur = box->ul;  			else  				box->ul = box->ur; diff --git a/engines/scumm/boxes.h b/engines/scumm/boxes.h index e554aea1b5..345d6a9d36 100644 --- a/engines/scumm/boxes.h +++ b/engines/scumm/boxes.h @@ -27,7 +27,7 @@  namespace Scumm { -#define SIZEOF_BOX_C64 5 +#define SIZEOF_BOX_V0 5  #define SIZEOF_BOX_V2 8  #define SIZEOF_BOX_V3 18  #define SIZEOF_BOX 20 diff --git a/engines/scumm/charset-fontdata.cpp b/engines/scumm/charset-fontdata.cpp index 29465584f8..16193f5503 100644 --- a/engines/scumm/charset-fontdata.cpp +++ b/engines/scumm/charset-fontdata.cpp @@ -420,7 +420,7 @@ static const byte germanCharsetDataV2[] = {  	126, 10,  }; -// German C64 MM. +// German v0 MM.  static const byte germanCharsetDataV0[] = {  	36, 11,  	42, 12, diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp index eb3cc3262c..2bbf3b3801 100644 --- a/engines/scumm/costume.cpp +++ b/engines/scumm/costume.cpp @@ -72,14 +72,6 @@ static const int v1MMNESLookup[25] = {  	0x17, 0x00, 0x01, 0x05, 0x16  }; -static const byte v0ActorTalkArray[0x19] = { -	0x00, 0x06, 0x06, 0x06, 0x06, -	0x06, 0x06, 0x00, 0x46, 0x06, -	0x06, 0x06, 0x06, 0xFF, 0xFF, -	0x06, 0xC0, 0x06, 0x06, 0x00, -	0xC0, 0xC0, 0x00, 0x06, 0x06 -}; -  byte ClassicCostumeRenderer::mainRoutine(int xmoveCur, int ymoveCur) {  	int i, skip = 0;  	byte drawFlag = 1; @@ -1171,7 +1163,7 @@ byte NESCostumeLoader::increaseAnim(Actor *a, int slot) {  	return (a->_cost.curpos[slot] != oldframe);  } -static const byte actorColorsMMC64[25] = { +static const byte actorV0Colors[25] = {  	0, 7, 2, 6, 9, 1, 3, 7, 7, 1, 1, 9, 1, 4, 5, 5, 4, 1, 0, 5, 4, 2, 2, 7, 7  }; @@ -1186,24 +1178,25 @@ static const byte actorColorsMMC64[25] = {  			dst[p + 1] = palette[pcolor]; \  	} -byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) { -	if (limb >= 8) -		return 0; +byte V0CostumeRenderer::drawLimb(const Actor *a, int limb) { +	const Actor_v0* a0 = (const Actor_v0 *)a; -	if (a->_cost.start[limb] == 0xFFFF) +	if (limb >= 8)  		return 0;  	if (limb == 0) {  		_draw_top = 200;  		_draw_bottom = 0;  	} - -	bool flipped = (a->_cost.start[limb] & 0x80) != 0; -	byte frameStart = _loaded._frameOffsets[a->_cost.frame[limb]]; -	byte frame = _loaded._frameOffsets[frameStart + a->_cost.curpos[limb]]; -	if (frame == 0xFF) +	 +	// Invalid current position? +	if (a->_cost.curpos[limb] == 0xFFFF)  		return 0; +	_loaded.loadCostume(a->_costume); +	byte frame = _loaded._frameOffsets[a->_cost.curpos[limb] + a->_cost.active[limb]]; + +	// Get the frame ptr  	byte ptrLow = _loaded._baseptr[frame];  	byte ptrHigh = ptrLow + _loaded._dataOffsets[4];  	int frameOffset = (_loaded._baseptr[ptrHigh] << 8) + _loaded._baseptr[ptrLow + 2];			// 0x23EF / 0x2400 @@ -1214,7 +1207,7 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {  	byte palette[4] = { 0, 0, 0, 0 };  	if (_vm->getCurrentLights() & LIGHTMODE_actor_use_colors) {  		palette[1] = 10; -		palette[2] = actorColorsMMC64[_actorID]; +		palette[2] = actorV0Colors[_actorID];  	} else {  		palette[2] = 11;  		palette[3] = 11; @@ -1231,7 +1224,7 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {  	if (!width || !height)  		return 0; -	int xpos = _actorX + (flipped ? -1 : +1) * (offsetX * 8 - a->_width / 2); +	int xpos = _actorX + (a0->_limb_flipped[limb] ? -1 : +1) * (offsetX * 8 - a->_width / 2);  	// +1 as we appear to be 1 pixel away from the original interpreter  	int ypos = _actorY - offsetY + 1; @@ -1241,13 +1234,13 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {  			byte color = data[y * width + x];  			byte pcolor; -			int destX = xpos + (flipped ? -(x + 1) : x) * 8; +			int destX = xpos + (a0->_limb_flipped[limb] ? -(x + 1) : x) * 8;  			int destY = ypos + y;  			if (destY >= 0 && destY < _out.h && destX >= 0 && destX < _out.w) {  				byte *dst = (byte *)_out.pixels + destY * _out.pitch + destX;  				byte *mask = _vm->getMaskBuffer(0, destY, _zbuf); -				if (flipped) { +				if (a0->_limb_flipped[limb]) {  					LINE(0, 0); LINE(2, 2); LINE(4, 4); LINE(6, 6);  				} else {  					LINE(6, 0); LINE(4, 2); LINE(2, 4); LINE(0, 6); @@ -1258,7 +1251,7 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {  	_draw_top = MIN(_draw_top, ypos);  	_draw_bottom = MAX(_draw_bottom, ypos + height); -	if (flipped) +	if (a0->_limb_flipped[limb])  		_vm->markRectAsDirty(kMainVirtScreen, xpos - (width * 8), xpos, ypos, ypos + height, _actorID);  	else  		_vm->markRectAsDirty(kMainVirtScreen, xpos, xpos + (width * 8), ypos, ypos + height, _actorID); @@ -1268,11 +1261,11 @@ byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {  #undef LINE  #undef MASK_AT -void C64CostumeRenderer::setCostume(int costume, int shadow) { +void V0CostumeRenderer::setCostume(int costume, int shadow) {  	_loaded.loadCostume(costume);  } -void C64CostumeLoader::loadCostume(int id) { +void V0CostumeLoader::loadCostume(int id) {  	const byte *ptr = _vm->getResourceAddress(rtCostume, id);  	_id = id; @@ -1282,166 +1275,128 @@ void C64CostumeLoader::loadCostume(int id) {  	_numColors = 0;  	_numAnim = 0;  	_mirror = 0; -	_palette = &actorColorsMMC64[id]; +	_palette = &actorV0Colors[id];  	_frameOffsets = _baseptr + READ_LE_UINT16(ptr + 5);  	_dataOffsets = ptr;  	_animCmds = _baseptr + READ_LE_UINT16(ptr + 7); - -	_maxHeight = 0;  } -void C64CostumeLoader::frameUpdate(ActorC64 *a, int cmd ) { -	byte limbFrames = 0; +void V0CostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) { +	Actor_v0 *a0 = (Actor_v0 *)a; + +	if (!a->_costume) +		return; + +	loadCostume(a->_costume); + +	// Invalid costume command? +	if (a0->_costCommandNew == 0xFF || (a0->_costCommand == a0->_costCommandNew)) +		return; + +	a0->_costCommand = a0->_costCommandNew; + +	int cmd = a0->_costCommand; +	byte limbFrameNumber = 0;  	// Each costume-command has 8 limbs  (0x2622)  	cmd <<= 3; -	for (int limb = 0, pos = 0; limb < 8; ++limb, pos = 0) { -		// get a limb frames ptr from the costume command -		limbFrames = ((_animCmds + cmd)[limb]); - -		// Dont change limb if entry is invalid -		if (limbFrames == 0xFF) -			continue; +	for (int limb = 0; limb < 8; ++limb) { -		// Has limb frames ptr changed since last update? -		if (a->_cost.start[limb] == limbFrames) -			continue; +		// get the frame number for the beginning of the costume command +		limbFrameNumber = ((_animCmds + cmd)[limb]); -		// Set new limb command addresses -		a->_cost.start[limb]	= limbFrames; -		a->_cost.frame[limb]	= _frameOffsets[limb] + (limbFrames & 0x7f);			// limb animation-frames ptr +		// Is this limb flipped? +		if (limbFrameNumber & 0x80) { -		// Get first entry of a limbs' frames -		byte frameStart = _frameOffsets[ a->_cost.frame[limb]]; +			// Invalid frame? +			if (limbFrameNumber == 0xFF) +				continue; -		// Loop each frame in this limb until we reach the end marker -		while (pos != 0xFF) {	// This is just so we dont overflow -			byte frame = _frameOffsets[frameStart + pos]; +			// Store the limb frame number (clear the flipped status) +			a->_cost.frame[limb] = (limbFrameNumber & 0x7f); -			// Each animation-frame until we find end -			if (frame == 0xFF) -				break; +			if (a0->_limb_flipped[limb] != true) +				a->_cost.start[limb] = 0xFFFF; -			byte ptrLow = _baseptr[frame]; -			byte ptrHigh = ptrLow + _dataOffsets[4]; -			int  frameOffset = (_baseptr[ptrHigh] << 8) + _baseptr[ptrLow + 2];			// 0x23EF / 0x2400 +			a0->_limb_flipped[limb] = true; -			const byte *data = _baseptr + frameOffset; +		} else { +			//Store the limb frame number +			a->_cost.frame[limb] = limbFrameNumber; -			if (data[3] > _maxHeight) -				_maxHeight = data[3] + 1; +			if (a0->_limb_flipped[limb] != false) +				a->_cost.start[limb] = 0xFFFF; -			++pos; +			a0->_limb_flipped[limb] = false;  		} -		// Set ending position of limb frames -		a->_cost.end[limb] = pos - 1; -		a->_cost.curpos[limb] = 0; -	} -} - -// based on 0x2BCA, doesn't match disassembly because 'oldDir' variable -// is not the same value as stored in the original interpreter -int C64CostumeLoader::dirToDirStop(int oldDir) { -	switch (oldDir) { -		case 0: -			return 4;	// Left -		case 1: -			return 5;	// Right -		case 2: -			return 6;	// Face Camera -		case 3: -			return 7;	// Face Away +		// Set the repeat value +		a0->_limbFrameRepeatNew[limb] = a0->_animFrameRepeat;  	} -	// shouldnt' be reached -	return 4;  } -void C64CostumeLoader::actorSpeak(ActorC64 *a, int &cmd) { -	if (v0ActorTalkArray[a->_number] & 0x80) -		return; +byte V0CostumeLoader::getFrame(Actor *a, int limb) { +	loadCostume(a->_costume); -	if ((a->_speaking & 0x80)) -		cmd += 0x0C; -	else -		cmd += 0x10; +	// Get the frame number for the current limb / Command +	return _frameOffsets[_frameOffsets[limb] + a->_cost.start[limb]];  } -void C64CostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) { -	ActorC64 *A = (ActorC64 *)a; -	int dir = newDirToOldDir(a->getFacing()); -	int command = dir; - -	loadCostume(a->_costume); - -	// Enable/Disable speaking flag -	if (frame == a->_talkStartFrame) { -		if (v0ActorTalkArray[a->_number] & 0x40) -			return; +byte V0CostumeLoader::increaseAnims(Actor *a) { +	Actor_v0 *a0 = (Actor_v0 *)a; +	int i; +	byte r = 0; -		A->_speaking = 1; -		return; -	} -	if (frame == a->_talkStopFrame) { -		A->_speaking = 0; -		return; +	for (i = 0; i != 8; i++) { +		a0->limbFrameCheck(i); +		r += increaseAnim(a, i);  	} +	return r; +} -	// Different command for stand frame -	if (frame == a->_standFrame) -		command = dirToDirStop(dir); - -	// Update the limb frames -	frameUpdate(A, command); - -	// Keep current command/frame mode -	A->_costCommand = dir; -	A->_costFrame = frame; +byte V0CostumeLoader::increaseAnim(Actor *a, int limb) { +	Actor_v0 *a0 = (Actor_v0 *)a; +	const uint16 limbPrevious = a->_cost.curpos[limb]++; -	// Update 'speaking' frames? -	if (A->_speaking) { -		command = dir;	// Incase standing frame was set as cmd -		actorSpeak(A, command); +	loadCostume(a->_costume); -		// Update the limb speak frames -		frameUpdate(A, command); -	} -} +	// 0x2543 +	byte frame = _frameOffsets[a->_cost.curpos[limb] + a->_cost.active[limb]]; -byte C64CostumeLoader::increaseAnims(Actor *a) { -	ActorC64 *A = (ActorC64 *)a; +	// Is this frame invalid? +	if (frame == 0xFF) { -	// check if the actor speak flag has changed since last frame increase -	if (A->_speaking != A->_speakingPrev) { -		int cmd = A->_costCommand; -		A->_speakingPrev = A->_speaking; +		// Repeat timer has reached 0? +		if (a0->_limbFrameRepeat[limb] == 0) { -		actorSpeak(A, cmd); +			// Use the previous frame +			--a0->_cost.curpos[limb]; -		// Update the limb frames -		frameUpdate(A, cmd); -	} +			// Reset the comstume command +			a0->_costCommandNew = 0xFF; +			a0->_costCommand = 0xFF; +			 +			// Set the frame/start to invalid +			a0->_cost.frame[limb] = 0xFFFF; +			a0->_cost.start[limb] = 0xFFFF; -	if (A->_moving  && _vm->_currentRoom != 1 && _vm->_currentRoom != 44) { -		if (a->_cost.soundPos == 0) -			a->_cost.soundCounter++; +		} else { -		// Is this the correct location? -		// 0x073C -		if (v0ActorTalkArray[a->_number] & 0x3F) -			a->_cost.soundPos = (a->_cost.soundPos + 1) % 3; -	} +			// Repeat timer enabled? +			if (a0->_limbFrameRepeat[limb] != -1) +				--a0->_limbFrameRepeat[limb]; -	// increase each frame pos -	for (int limb = 0; limb < 8; ++limb) { -		if (a->_cost.curpos[limb] < a->_cost.end[limb]) -			a->_cost.curpos[limb]++; -		else +			// No, restart at frame 0  			a->_cost.curpos[limb] = 0; +		}  	} +	// Limb frame has changed? +	if (limbPrevious == a->_cost.curpos[limb]) +		return 0; +  	return 1;  } diff --git a/engines/scumm/costume.h b/engines/scumm/costume.h index 3acf2a1f6c..4a21692ddb 100644 --- a/engines/scumm/costume.h +++ b/engines/scumm/costume.h @@ -67,20 +67,16 @@ protected:  	byte increaseAnim(Actor *a, int slot);  }; -class C64CostumeLoader : public ClassicCostumeLoader { +class V0CostumeLoader : public ClassicCostumeLoader {  public: -	C64CostumeLoader(ScummEngine *vm) : ClassicCostumeLoader(vm) {} +	V0CostumeLoader(ScummEngine *vm) : ClassicCostumeLoader(vm) {}  	void loadCostume(int id);  	void costumeDecodeData(Actor *a, int frame, uint usemask);  	byte increaseAnims(Actor *a); - -	int _maxHeight; +	byte getFrame(Actor *a, int limb);  protected: -	void actorSpeak(ActorC64 *a, int &cmd); -	int dirToDirStop(int oldDir); -	void frameUpdate(ActorC64 *A, int cmd); - +	byte increaseAnim(Actor *a, int limb);  };  class ClassicCostumeRenderer : public BaseCostumeRenderer { @@ -135,12 +131,12 @@ public:  };  #endif -class C64CostumeRenderer : public BaseCostumeRenderer { +class V0CostumeRenderer : public BaseCostumeRenderer {  protected: -	C64CostumeLoader _loaded; +	V0CostumeLoader _loaded;  public: -	C64CostumeRenderer(ScummEngine *vm) : BaseCostumeRenderer(vm), _loaded(vm) {} +	V0CostumeRenderer(ScummEngine *vm) : BaseCostumeRenderer(vm), _loaded(vm) {}  	void setPalette(uint16 *palette) {}  	void setFacing(const Actor *a) {} diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index 54f7fea97b..edcf2e6fea 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -382,7 +382,8 @@ bool ScummDebugger::Cmd_Actor(int argc, const char **argv) {  			DebugPrintf("Actor[%d].costume = %d\n", actnum, a->_costume);  		}  	} else if (!strcmp(argv[2], "name")) { -		DebugPrintf("Name of actor %d: %s\n", actnum, _vm->getObjOrActorName(actnum)); +		DebugPrintf("Name of actor %d: %s\n", actnum,  +			_vm->getObjOrActorName(_vm->actorToObj(actnum)));  	} else if (!strcmp(argv[2], "condmask")) {  		if (argc > 3) {  			a->_heCondMask = value; @@ -427,9 +428,10 @@ bool ScummDebugger::Cmd_PrintObjects(int argc, const char **argv) {  		o = &(_vm->_objs[i]);  		if (o->obj_nr == 0)  			continue; +		int classData = (_vm->_game.version != 0 ? _vm->_classData[o->obj_nr] : 0);  		DebugPrintf("|%4d|%4d|%4d|%5d|%6d|%5d|%2d|$%08x|\n",  				o->obj_nr, o->x_pos, o->y_pos, o->width, o->height, o->state, -				o->fl_object_index, _vm->_classData[o->obj_nr]); +				o->fl_object_index, classData);  	}  	DebugPrintf("\n"); @@ -446,7 +448,7 @@ bool ScummDebugger::Cmd_Object(int argc, const char **argv) {  	}  	obj = atoi(argv[1]); -	if (obj >= _vm->_numGlobalObjects) { +	if (_vm->_game.version != 0 && obj >= _vm->_numGlobalObjects) {  		DebugPrintf("Object %d is out of range (range: 1 - %d)\n", obj, _vm->_numGlobalObjects);  		return true;  	} diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index a22aa1802f..bc6cfc761e 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -26,6 +26,7 @@  #include "scumm/he/intern_he.h"  #endif  #include "scumm/resource.h" +#include "scumm/scumm_v0.h"  #include "scumm/scumm_v5.h"  #include "scumm/scumm_v6.h"  #include "scumm/usage_bits.h" @@ -239,7 +240,7 @@ GdiPCEngine::~GdiPCEngine() {  #endif  GdiV1::GdiV1(ScummEngine *vm) : Gdi(vm) { -	memset(&_C64, 0, sizeof(_C64)); +	memset(&_V1, 0, sizeof(_V1));  }  GdiV2::GdiV2(ScummEngine *vm) : Gdi(vm) { @@ -296,17 +297,17 @@ void GdiPCEngine::loadTiles(byte *roomptr) {  void GdiV1::roomChanged(byte *roomptr) {  	for (int i = 0; i < 4; i++){ -		_C64.colors[i] = roomptr[6 + i]; +		_V1.colors[i] = roomptr[6 + i];  	} -	decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 10), _C64.charMap, 2048); -	decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 12), _C64.picMap, roomptr[4] * roomptr[5]); -	decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 14), _C64.colorMap, roomptr[4] * roomptr[5]); -	decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 16), _C64.maskMap, roomptr[4] * roomptr[5]); +	decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 10), _V1.charMap, 2048); +	decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 12), _V1.picMap, roomptr[4] * roomptr[5]); +	decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 14), _V1.colorMap, roomptr[4] * roomptr[5]); +	decodeV1Gfx(roomptr + READ_LE_UINT16(roomptr + 16), _V1.maskMap, roomptr[4] * roomptr[5]);  	// Read the mask data. The 16bit length value seems to always be 8 too big.  	// See bug #1837375 for details on this.  	const byte *maskPtr = roomptr + READ_LE_UINT16(roomptr + 18); -	decodeC64Gfx(maskPtr + 2, _C64.maskChar, READ_LE_UINT16(maskPtr) - 8); +	decodeV1Gfx(maskPtr + 2, _V1.maskChar, READ_LE_UINT16(maskPtr) - 8);  	_objectMode = true;  } @@ -1487,15 +1488,17 @@ void ScummEngine_v5::drawFlashlight() {  	_flashlight.isDrawn = true;  } -// V0 Maniac doesn't have a ScummVar for VAR_CURRENT_LIGHTS, and just uses -// an internal variable. Emulate this to prevent overwriting script vars... -// And V6 games do not use the "lights" at all. There, the whole screen is -// always visible, and actors are always colored, so we fake the correct -// light value for it. +int ScummEngine_v0::getCurrentLights() const { +	// V0 Maniac doesn't have a ScummVar for VAR_CURRENT_LIGHTS, and just uses +	// an internal variable. Emulate this to prevent overwriting script vars... +	// And V6 games do not use the "lights" at all. There, the whole screen is +	// always visible, and actors are always colored, so we fake the correct +	// light value for it. +	return _currentLights; +} +  int ScummEngine::getCurrentLights() const { -	if (_game.id == GID_MANIAC && _game.version == 0) -		return _currentLights; -	else if (_game.version >= 6) +	if (_game.version >= 6)  		return LIGHTMODE_room_lights_on | LIGHTMODE_actor_use_colors;  	else  		return VAR(VAR_CURRENT_LIGHTS); @@ -1538,7 +1541,7 @@ void GdiV1::prepareDrawBitmap(const byte *ptr, VirtScreen *vs,  					const int x, const int y, const int width, const int height,  	                int stripnr, int numstrip) {  	if (_objectMode) { -		decodeC64Gfx(ptr, _C64.objectMap, (width / 8) * (height / 8) * 3); +		decodeV1Gfx(ptr, _V1.objectMap, (width / 8) * (height / 8) * 3);  	}  } @@ -1925,9 +1928,9 @@ bool GdiPCEngine::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const in  bool GdiV1::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const int width, const int height,  					int stripnr, const byte *smap_ptr) {  	if (_objectMode) -		drawStripC64Object(dstPtr, vs->pitch, stripnr, width, height); +		drawStripV1Object(dstPtr, vs->pitch, stripnr, width, height);  	else -		drawStripC64Background(dstPtr, vs->pitch, stripnr, height); +		drawStripV1Background(dstPtr, vs->pitch, stripnr, height);  	return false;  } @@ -2068,7 +2071,7 @@ void GdiV1::decodeMask(int x, int y, const int width, const int height,  	                int stripnr, int numzbuf, const byte *zplane_list[9],  	                bool transpStrip, byte flag) {  	byte *mask_ptr = getMaskBuffer(x, y, 1); -	drawStripC64Mask(mask_ptr, stripnr, width, height); +	drawStripV1Mask(mask_ptr, stripnr, width, height);  }  void GdiV2::decodeMask(int x, int y, const int width, const int height, @@ -3086,67 +3089,67 @@ void GdiPCEngine::drawStripPCEngineMask(byte *dst, int stripnr, int top, int hei  }  #endif -void GdiV1::drawStripC64Background(byte *dst, int dstPitch, int stripnr, int height) { +void GdiV1::drawStripV1Background(byte *dst, int dstPitch, int stripnr, int height) {  	int charIdx;  	height /= 8;  	for (int y = 0; y < height; y++) { -		_C64.colors[3] = (_C64.colorMap[y + stripnr * height] & 7); +		_V1.colors[3] = (_V1.colorMap[y + stripnr * height] & 7);  		// Check for room color change in V1 zak  		if (_roomPalette[0] == 255) { -			_C64.colors[2] = _roomPalette[2]; -			_C64.colors[1] = _roomPalette[1]; +			_V1.colors[2] = _roomPalette[2]; +			_V1.colors[1] = _roomPalette[1];  		} -		charIdx = _C64.picMap[y + stripnr * height] * 8; +		charIdx = _V1.picMap[y + stripnr * height] * 8;  		for (int i = 0; i < 8; i++) { -			byte c = _C64.charMap[charIdx + i]; -			dst[0] = dst[1] = _C64.colors[(c >> 6) & 3]; -			dst[2] = dst[3] = _C64.colors[(c >> 4) & 3]; -			dst[4] = dst[5] = _C64.colors[(c >> 2) & 3]; -			dst[6] = dst[7] = _C64.colors[(c >> 0) & 3]; +			byte c = _V1.charMap[charIdx + i]; +			dst[0] = dst[1] = _V1.colors[(c >> 6) & 3]; +			dst[2] = dst[3] = _V1.colors[(c >> 4) & 3]; +			dst[4] = dst[5] = _V1.colors[(c >> 2) & 3]; +			dst[6] = dst[7] = _V1.colors[(c >> 0) & 3];  			dst += dstPitch;  		}  	}  } -void GdiV1::drawStripC64Object(byte *dst, int dstPitch, int stripnr, int width, int height) { +void GdiV1::drawStripV1Object(byte *dst, int dstPitch, int stripnr, int width, int height) {  	int charIdx;  	height /= 8;  	width /= 8;  	for (int y = 0; y < height; y++) { -		_C64.colors[3] = (_C64.objectMap[(y + height) * width + stripnr] & 7); -		charIdx = _C64.objectMap[y * width + stripnr] * 8; +		_V1.colors[3] = (_V1.objectMap[(y + height) * width + stripnr] & 7); +		charIdx = _V1.objectMap[y * width + stripnr] * 8;  		for (int i = 0; i < 8; i++) { -			byte c = _C64.charMap[charIdx + i]; -			dst[0] = dst[1] = _C64.colors[(c >> 6) & 3]; -			dst[2] = dst[3] = _C64.colors[(c >> 4) & 3]; -			dst[4] = dst[5] = _C64.colors[(c >> 2) & 3]; -			dst[6] = dst[7] = _C64.colors[(c >> 0) & 3]; +			byte c = _V1.charMap[charIdx + i]; +			dst[0] = dst[1] = _V1.colors[(c >> 6) & 3]; +			dst[2] = dst[3] = _V1.colors[(c >> 4) & 3]; +			dst[4] = dst[5] = _V1.colors[(c >> 2) & 3]; +			dst[6] = dst[7] = _V1.colors[(c >> 0) & 3];  			dst += dstPitch;  		}  	}  } -void GdiV1::drawStripC64Mask(byte *dst, int stripnr, int width, int height) const { +void GdiV1::drawStripV1Mask(byte *dst, int stripnr, int width, int height) const {  	int maskIdx;  	height /= 8;  	width /= 8;  	for (int y = 0; y < height; y++) {  		if (_objectMode) -			maskIdx = _C64.objectMap[(y + 2 * height) * width + stripnr] * 8; +			maskIdx = _V1.objectMap[(y + 2 * height) * width + stripnr] * 8;  		else -			maskIdx = _C64.maskMap[y + stripnr * height] * 8; +			maskIdx = _V1.maskMap[y + stripnr * height] * 8;  		for (int i = 0; i < 8; i++) { -			byte c = _C64.maskChar[maskIdx + i]; +			byte c = _V1.maskChar[maskIdx + i]; -			// V1/C64 masks are inverted compared to what ScummVM expects +			// V1/V0 masks are inverted compared to what ScummVM expects  			*dst = c ^ 0xFF;  			dst += _numStrips;  		}  	}  } -void GdiV1::decodeC64Gfx(const byte *src, byte *dst, int size) const { +void GdiV1::decodeV1Gfx(const byte *src, byte *dst, int size) const {  	int x, z;  	byte color, run, common[4]; diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h index 4b44ddc376..0d81698c50 100644 --- a/engines/scumm/gfx.h +++ b/engines/scumm/gfx.h @@ -375,19 +375,19 @@ public:  class GdiV1 : public Gdi {  protected: -	/** Render settings which are specific to the C64 graphic decoders. */ +	/** Render settings which are specific to the v0/v1 graphic decoders. */  	struct {  		byte colors[4];  		byte charMap[2048], objectMap[2048], picMap[4096], colorMap[4096];  		byte maskMap[4096], maskChar[4096]; -	} _C64; +	} _V1;  protected: -	void decodeC64Gfx(const byte *src, byte *dst, int size) const; +	void decodeV1Gfx(const byte *src, byte *dst, int size) const; -	void drawStripC64Object(byte *dst, int dstPitch, int stripnr, int width, int height); -	void drawStripC64Background(byte *dst, int dstPitch, int stripnr, int height); -	void drawStripC64Mask(byte *dst, int stripnr, int width, int height) const; +	void drawStripV1Object(byte *dst, int dstPitch, int stripnr, int width, int height); +	void drawStripV1Background(byte *dst, int dstPitch, int stripnr, int height); +	void drawStripV1Mask(byte *dst, int stripnr, int width, int height) const;  	virtual bool drawStrip(byte *dstPtr, VirtScreen *vs,  					int x, int y, const int width, const int height, diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp index da238dc517..399cd91324 100644 --- a/engines/scumm/object.cpp +++ b/engines/scumm/object.cpp @@ -178,10 +178,7 @@ void ScummEngine::clearOwnerOf(int obj) {  		// Alternatively, scan the inventory to see if the object is in there...  		for (i = 0; i < _numInventory; i++) {  			if (_inventory[i] == obj) { -				if (_game.version == 0) -					assert(WIO_INVENTORY == whereIsObjectInventory(obj)); -				else -					assert(WIO_INVENTORY == whereIsObject(obj)); +				assert(WIO_INVENTORY == whereIsObject(obj));  				// Found the object! Nuke it from the inventory.  				_res->nukeResource(rtInventory, i); @@ -206,6 +203,9 @@ void ScummEngine::clearOwnerOf(int obj) {  }  bool ScummEngine::getClass(int obj, int cls) const { +	if (_game.version == 0) +		return false; +  	assertRange(0, obj, _numGlobalObjects - 1, "object");  	cls &= 0x7F;  	assertRange(1, cls, 32, "class"); @@ -233,6 +233,9 @@ bool ScummEngine::getClass(int obj, int cls) const {  }  void ScummEngine::putClass(int obj, int cls, bool set) { +	if (_game.version == 0) +		return; +  	assertRange(0, obj, _numGlobalObjects - 1, "object");  	cls &= 0x7F;  	assertRange(1, cls, 32, "class"); @@ -315,47 +318,38 @@ int ScummEngine::getObjectIndex(int object) const {  		return -1;  	for (i = (_numLocalObjects-1); i > 0; i--) { -		if (_game.version == 0 ) -			if( _objs[i].flags != _v0ObjectFlag ) -				continue; -  		if (_objs[i].obj_nr == object)  			return i;  	}  	return -1;  } -int ScummEngine::whereIsObjectInventory(int object) { -	int res = 0; -	_v0ObjectInInventory = true; -	res = whereIsObject(object); -	_v0ObjectInInventory = false; - -	return res; -} -  int ScummEngine::whereIsObject(int object) const {  	int i; -	if (object >= _numGlobalObjects) +	// Note: in MM v0 bg objects are greater _numGlobalObjects +	if (_game.version != 0 && object >= _numGlobalObjects)  		return WIO_NOT_FOUND;  	if (object < 1)  		return WIO_NOT_FOUND; -	if ((_objectOwnerTable[object] != OF_OWNER_ROOM && _game.version != 0) || _v0ObjectInInventory) { +	if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) && +		 _objectOwnerTable[object] != OF_OWNER_ROOM)  +	{  		for (i = 0; i < _numInventory; i++)  			if (_inventory[i] == object)  				return WIO_INVENTORY;  		return WIO_NOT_FOUND;  	} -	for (i = (_numLocalObjects-1); i > 0; i--) -		if ((_objs[i].obj_nr == object && !_v0ObjectIndex) || (_v0ObjectIndex && i == object)) { +	for (i = (_numLocalObjects-1); i > 0; i--) { +		if (_objs[i].obj_nr == object) {  			if (_objs[i].fl_object_index)  				return WIO_FLOBJECT;  			return WIO_ROOM;  		} +	}  	return WIO_NOT_FOUND;  } @@ -363,8 +357,8 @@ int ScummEngine::whereIsObject(int object) const {  int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {  	Actor *act; -	if (object < _numActors) { -		act = derefActorSafe(object, "getObjectOrActorXY"); +	if (objIsActor(object)) { +		act = derefActorSafe(objToActor(object), "getObjectOrActorXY");  		if (act && act->isInCurrentRoom()) {  			x = act->getRealPos().x;  			y = act->getRealPos().y; @@ -377,7 +371,7 @@ int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {  	case WIO_NOT_FOUND:  		return -1;  	case WIO_INVENTORY: -		if (_objectOwnerTable[object] < _numActors) { +		if (objIsActor(_objectOwnerTable[object])) {  			act = derefActor(_objectOwnerTable[object], "getObjectOrActorXY(2)");  			if (act && act->isInCurrentRoom()) {  				x = act->getRealPos().x; @@ -396,7 +390,7 @@ int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {   * Returns X, Y and direction in angles   */  void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) { -	int idx = (_v0ObjectIndex) ? object : getObjectIndex(object); +	int idx = getObjectIndex(object);  	assert(idx >= 0);  	ObjectData &od = _objs[idx];  	int state; @@ -439,8 +433,15 @@ void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) {  			y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y);  		}  	} else if (_game.version <= 2) { -		x = od.walk_x >> V12_X_SHIFT; -		y = od.walk_y >> V12_Y_SHIFT; +		if (od.actordir) { +			x = od.walk_x; +			y = od.walk_y; +		} else { +			x = od.x_pos + od.width / 2; +			y = od.y_pos + od.height / 2; +		} +		x = x >> V12_X_SHIFT; +		y = y >> V12_Y_SHIFT;  	} else {  		x = od.walk_x;  		y = od.walk_y; @@ -462,11 +463,11 @@ int ScummEngine::getObjActToObjActDist(int a, int b) {  	Actor *acta = NULL;  	Actor *actb = NULL; -	if (a < _numActors) -		acta = derefActorSafe(a, "getObjActToObjActDist"); +	if (objIsActor(a)) +		acta = derefActorSafe(objToActor(a), "getObjActToObjActDist"); -	if (b < _numActors) -		actb = derefActorSafe(b, "getObjActToObjActDist(2)"); +	if (objIsActor(b)) +		actb = derefActorSafe(objToActor(b), "getObjActToObjActDist(2)");  	if (acta && actb && acta->getRoom() == actb->getRoom() && acta->getRoom() && !acta->isInCurrentRoom())  		return 0; @@ -492,14 +493,6 @@ int ScummEngine::getObjActToObjActDist(int a, int b) {  	return getDist(x, y, x2, y2);  } -int ScummEngine_v0::findObjectIndex(int x, int y) { -	int objIdx; -	_v0ObjectIndex = true; -	objIdx = findObject(x, y); -	_v0ObjectIndex = false; -	return objIdx; -} -  int ScummEngine::findObject(int x, int y) {  	int i, b;  	byte a; @@ -509,11 +502,9 @@ int ScummEngine::findObject(int x, int y) {  		if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))  			continue; -		if (_game.version == 0) { -			if (_objs[i].flags == 0 && _objs[i].state & kObjectStateUntouchable) -				continue; -		} else { -			if (_game.version <= 2 && _objs[i].state & kObjectStateUntouchable) +		if ((_game.version == 0 && OBJECT_V0_TYPE(_objs[i].obj_nr) == kObjectV0TypeFG) || +			(_game.version > 0 && _game.version <= 2)) { +			if (_objs[i].state & kObjectStateUntouchable)  				continue;  		} @@ -529,15 +520,8 @@ int ScummEngine::findObject(int x, int y) {  				}  #endif  				if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x && -				    _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y) { -					// MMC64: Set the object search flag -					if (_game.version == 0) -						_v0ObjectFlag = _objs[i].flags; -					if (_game.version == 0 && _v0ObjectIndex) -						return i; -					else -						return _objs[i].obj_nr; -				} +				    _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y) +					return _objs[i].obj_nr;  				break;  			}  		} while ((_objs[b].state & mask) == a); @@ -824,7 +808,7 @@ void ScummEngine_v3old::resetRoomObjects() {  	else  		ptr = room + 29; -	// Default pointer of objects without image, in C64 verison of Maniac Mansion +	// Default pointer of objects without image, in v0 version of Maniac Mansion  	int defaultPtr = READ_LE_UINT16(ptr + 2 * _numObjectsInRoom);  	for (i = 0; i < _numObjectsInRoom; i++) { @@ -843,9 +827,6 @@ void ScummEngine_v3old::resetRoomObjects() {  		if (_dumpScripts) {  			char buf[32];  			sprintf(buf, "roomobj-%d-", _roomResource); -			if (_game.version == 0) -				sprintf(buf + 11, "%d-", od->flags); -  			dumpResource(buf, od->obj_nr, room + od->OBCDoffset);  		}  	} @@ -911,8 +892,7 @@ void ScummEngine_v0::resetRoomObject(ObjectData *od, const byte *room, const byt  	const byte *ptr = room + od->OBCDoffset;  	ptr -= 2; -	od->obj_nr = *(ptr + 6); -	od->flags = *(ptr + 7); +	od->obj_nr = OBJECT_V0(*(ptr + 6), *(ptr + 7));  	od->x_pos = *(ptr + 8) * 8;  	od->y_pos = ((*(ptr + 9)) & 0x7F) * 8; @@ -1072,8 +1052,8 @@ void ScummEngine::updateObjectStates() {  	int i;  	ObjectData *od = &_objs[1];  	for (i = 1; i < _numLocalObjects; i++, od++) { -		// V0 MM, Room objects with Flag == 1 are objects with 'no-state' (room specific objects, non-pickup) -		if (_game.version == 0 && od->flags == 1) +		// V0 MM, objects with type == 1 are room objects (room specific objects, non-pickup) +		if (_game.version == 0 && OBJECT_V0_TYPE(od->obj_nr) == kObjectV0TypeBG)  			continue;  		if (od->obj_nr > 0) @@ -1163,8 +1143,8 @@ const byte *ScummEngine::getObjOrActorName(int obj) {  	byte *objptr;  	int i; -	if (obj < _numActors && _game.version >= 1) -		return derefActor(obj, "getObjOrActorName")->getActorName(); +	if (objIsActor(obj)) +		return derefActor(objToActor(obj), "getObjOrActorName")->getActorName();  	for (i = 0; i < _numNewNames; i++) {  		if (_newNames[i] == obj) { @@ -1173,7 +1153,7 @@ const byte *ScummEngine::getObjOrActorName(int obj) {  		}  	} -	objptr = getOBCDFromObject(obj); +	objptr = getOBCDFromObject(obj, true);  	if (objptr == NULL)  		return NULL; @@ -1200,7 +1180,7 @@ const byte *ScummEngine::getObjOrActorName(int obj) {  void ScummEngine::setObjectName(int obj) {  	int i; -	if (obj < _numActors && _game.version != 0) +	if (objIsActor(obj))  		error("Can't set actor %d name with new-name-of", obj);  	for (i = 0; i < _numNewNames; i++) { @@ -1226,13 +1206,10 @@ void ScummEngine::setObjectName(int obj) {  uint32 ScummEngine::getOBCDOffs(int object) const {  	int i; -	if ((_objectOwnerTable[object] != OF_OWNER_ROOM && (_game.version != 0))  || _v0ObjectInInventory) +	if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) && +		_objectOwnerTable[object] != OF_OWNER_ROOM)  		return 0; -	// V0 MM Return by Index -	if (_v0ObjectIndex) -		return _objs[object].OBCDoffset; -  	for (i = (_numLocalObjects-1); i > 0; i--) {  		if (_objs[i].obj_nr == object) {  			if (_objs[i].fl_object_index != 0) @@ -1243,21 +1220,22 @@ uint32 ScummEngine::getOBCDOffs(int object) const {  	return 0;  } -byte *ScummEngine::getOBCDFromObject(int obj) { -	bool useInventory = _v0ObjectInInventory; +byte *ScummEngine::getOBCDFromObject(int obj, bool v0CheckInventory) {  	int i;  	byte *ptr; -	_v0ObjectInInventory = false; - -	if ((_objectOwnerTable[obj] != OF_OWNER_ROOM && (_game.version != 0)) || useInventory) { +	if ((_game.version != 0 || OBJECT_V0_TYPE(obj) == 0) && +		_objectOwnerTable[obj] != OF_OWNER_ROOM)  +	{ +		if (_game.version == 0 && !v0CheckInventory) +			return 0;  		for (i = 0; i < _numInventory; i++) {  			if (_inventory[i] == obj)  				return getResourceAddress(rtInventory, i);  		}  	} else {  		for (i = (_numLocalObjects-1); i > 0; --i) { -			if ((_objs[i].obj_nr == obj && !_v0ObjectIndex) || (_v0ObjectIndex && i == obj)) { +			if (_objs[i].obj_nr == obj) {  				if (_objs[i].fl_object_index) {  					assert(_objs[i].OBCDoffset == 8);  					ptr = getResourceAddress(rtFlObject, _objs[i].fl_object_index); @@ -1510,11 +1488,37 @@ void ScummEngine::findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint id,  	}  } +bool ScummEngine_v0::objIsActor(int obj) { +	// object IDs < _numActors are used in v0 for objects too (e.g. hamster) +	return OBJECT_V0_TYPE(obj) == kObjectV0TypeActor; +} + +int ScummEngine_v0::objToActor(int obj) { +	return OBJECT_V0_ID(obj); +} + +int ScummEngine_v0::actorToObj(int actor) { +	return OBJECT_V0(actor, kObjectV0TypeActor); +} + +bool ScummEngine::objIsActor(int obj) { +	return obj < _numActors; +} + +int ScummEngine::objToActor(int obj) { +	return obj; +} + +int ScummEngine::actorToObj(int actor) { +	return actor; +} +  int ScummEngine::getObjX(int obj) { -	if (obj < _numActors) { -		if (obj < 1) -			return 0;									/* fix for indy4's map */ -		return derefActor(obj, "getObjX")->getRealPos().x; +	if (obj < 1) +		return 0;									/* fix for indy4's map */ + +	if (objIsActor(obj)) { +		return derefActor(objToActor(obj), "getObjX")->getRealPos().x;  	} else {  		if (whereIsObject(obj) == WIO_NOT_FOUND)  			return -1; @@ -1525,10 +1529,11 @@ int ScummEngine::getObjX(int obj) {  }  int ScummEngine::getObjY(int obj) { -	if (obj < _numActors) { -		if (obj < 1) -			return 0;									/* fix for indy4's map */ -		return derefActor(obj, "getObjY")->getRealPos().y; +	if (obj < 1) +		return 0;									/* fix for indy4's map */ + +	if (objIsActor(obj)) { +		return derefActor(objToActor(obj), "getObjY")->getRealPos().y;  	} else {  		if (whereIsObject(obj) == WIO_NOT_FOUND)  			return -1; @@ -1544,8 +1549,8 @@ int ScummEngine::getObjOldDir(int obj) {  int ScummEngine::getObjNewDir(int obj) {  	int dir; -	if (obj < _numActors) { -		dir = derefActor(obj, "getObjNewDir")->getFacing(); +	if (objIsActor(obj)) { +		dir = derefActor(objToActor(obj), "getObjNewDir")->getFacing();  	} else {  		int x, y;  		getObjectXYPos(obj, x, y, dir); diff --git a/engines/scumm/object.h b/engines/scumm/object.h index cdf8b09e6f..8212075e43 100644 --- a/engines/scumm/object.h +++ b/engines/scumm/object.h @@ -24,6 +24,26 @@  namespace Scumm { +static inline int OBJECT_V0(int id, byte type) { +	assert(id < 256); +	return (type << 8 | id); +} +#define OBJECT_V0_ID(obj)	(obj & 0xFF) +#define OBJECT_V0_TYPE(obj)	((obj >> 8) & 0xFF) + +enum ObjectV0Type { +	kObjectV0TypeFG = 0,    // foreground object +	                        //   - with owner/state, might (but has not to) be pickupable +	                        //     -> with entry in _objectOwner/StateTable +	                        //     -> all objects in _inventory have this type +	                        //   - image can be exchanged (background overlay) +	kObjectV0TypeBG = 1,    // background object +	                        //   - without owner/state, not pickupable  (room only) +	                        //     -> without entry in _objectOwner/StateTable +	                        //   - image cannot be exchanged (part of background image) +	kObjectV0TypeActor = 2  // object is an actor +}; +  enum ObjectClass {  	kObjectClassNeverClip = 20,  	kObjectClassAlwaysClip = 21, diff --git a/engines/scumm/player_apple2.h b/engines/scumm/player_apple2.h index 4cbd24b81d..c3e1b7fc60 100644 --- a/engines/scumm/player_apple2.h +++ b/engines/scumm/player_apple2.h @@ -247,7 +247,6 @@ public:  		_sampleRate = rate;  		_sampleConverter.setSampleRate(rate);   	} -	void startMusic(int songResIndex);  	virtual void startSound(int sound);  	virtual void stopSound(int sound);  	virtual void stopAllSounds(); diff --git a/engines/scumm/player_pce.cpp b/engines/scumm/player_pce.cpp index 786971c683..8d886ee008 100644 --- a/engines/scumm/player_pce.cpp +++ b/engines/scumm/player_pce.cpp @@ -269,13 +269,13 @@ void PSG_HuC6280::init() {  	reset();  	// Make waveform frequency table -	for(i = 0; i < 4096; i++) { +	for (i = 0; i < 4096; i++) {  		step = ((_clock / _rate) * 4096) / (i+1);  		_waveFreqTable[(1 + i) & 0xFFF] = (uint32)step;  	}  	// Make noise frequency table -	for(i = 0; i < 32; i++) { +	for (i = 0; i < 32; i++) {  		step = ((_clock / _rate) * 32) / (i+1);  		_noiseFreqTable[i] = (uint32)step;  	} @@ -283,7 +283,7 @@ void PSG_HuC6280::init() {  	// Make volume table  	// PSG_HuC6280 has 48dB volume range spread over 32 steps  	step = 48.0 / 32.0; -	for(i = 0; i < 31; i++) { +	for (i = 0; i < 31; i++) {  		_volumeTable[i] = (uint16)level;  		level /= pow(10.0, step / 20.0);  	} @@ -323,7 +323,7 @@ void PSG_HuC6280::write(int offset, byte data) {  	case 0x04: // Channel control (key-on, DDA mode, volume)  		// 1-to-0 transition of DDA bit resets waveform index -		if((chan->control & 0x40) && ((data & 0x40) == 0)) { +		if ((chan->control & 0x40) && ((data & 0x40) == 0)) {  			chan->index = 0;  		}  		chan->control = data; @@ -383,9 +383,9 @@ void PSG_HuC6280::update(int16* samples, int sampleCnt) {  	// Clear buffer  	memset(samples, 0, 2 * sampleCnt * sizeof(int16)); -	for(ch = 0; ch < 6; ch++) { +	for (ch = 0; ch < 6; ch++) {  		// Only look at enabled channels -		if(_channel[ch].control & 0x80) { +		if (_channel[ch].control & 0x80) {  			int lal = (_channel[ch].balance >> 4) & 0x0F;  			int ral = (_channel[ch].balance >> 0) & 0x0F;  			int al  = _channel[ch].control & 0x1F; @@ -395,25 +395,25 @@ void PSG_HuC6280::update(int16* samples, int sampleCnt) {  			// Calculate volume just as the patent says  			vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal); -			if(vll > 0x1F) vll = 0x1F; +			if (vll > 0x1F) vll = 0x1F;  			vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal); -			if(vlr > 0x1F) vlr = 0x1F; +			if (vlr > 0x1F) vlr = 0x1F;  			vll = _volumeTable[vll];  			vlr = _volumeTable[vlr];  			// Check channel mode -			if(_channel[ch].control & 0x40) { +			if (_channel[ch].control & 0x40) {  				/* DDA mode */ -				for(i = 0; i < sampleCnt; i++) { +				for (i = 0; i < sampleCnt; i++) {  					samples[2*i]     += (int16)(vll * (_channel[ch].dda - 16));  					samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16));  				}  			} else {  				/* Waveform mode */  				uint32 step = _waveFreqTable[_channel[ch].frequency]; -				for(i = 0; i < sampleCnt; i += 1) { +				for (i = 0; i < sampleCnt; i += 1) {  					int offset;  					int16 data;  					offset = (_channel[ch].counter >> 12) & 0x1F; diff --git a/engines/scumm/player_pce.h b/engines/scumm/player_pce.h index eb6afd892a..427fb1ace6 100644 --- a/engines/scumm/player_pce.h +++ b/engines/scumm/player_pce.h @@ -76,7 +76,6 @@ public:  	virtual ~Player_PCE();  	virtual void setMusicVolume(int vol) { _maxvol = vol; } -	void startMusic(int songResIndex);  	virtual void startSound(int sound);  	virtual void stopSound(int sound);  	virtual void stopAllSounds(); diff --git a/engines/scumm/player_sid.cpp b/engines/scumm/player_sid.cpp index f0f60a3924..ecfaef22c1 100644 --- a/engines/scumm/player_sid.cpp +++ b/engines/scumm/player_sid.cpp @@ -683,7 +683,7 @@ void Player_SID::stopSound_intern(int soundResID) { // $5093  	releaseResource(soundResID);  } -void Player_SID::stopAllSounds_intern() { // $4CAA +void Player_SID::stopMusic_intern() { // $4CAA  	statusBits1B = 0;  	isMusicPlaying = false; @@ -1352,7 +1352,7 @@ void Player_SID::stopSound(int nr) {  void Player_SID::stopAllSounds() {  	Common::StackLock lock(_mutex); -	stopAllSounds_intern(); +	resetPlayerState();  }  int Player_SID::getSoundStatus(int nr) const { diff --git a/engines/scumm/player_sid.h b/engines/scumm/player_sid.h index baeb7bbef0..12e3573575 100644 --- a/engines/scumm/player_sid.h +++ b/engines/scumm/player_sid.h @@ -57,7 +57,6 @@ public:  	virtual ~Player_SID();  	virtual void setMusicVolume(int vol) { _maxvol = vol; } -	void startMusic(int songResIndex);  	virtual void startSound(int sound);  	virtual void stopSound(int sound);  	virtual void stopAllSounds(); @@ -95,7 +94,7 @@ private:  	void initMusic(int songResIndex); // $7de6  	int initSound(int soundResID); // $4D0A  	void stopSound_intern(int soundResID); // $5093 -	void stopAllSounds_intern(); // $4CAA +	void stopMusic_intern(); // $4CAA  	void resetSID(); // $48D8  	void update(); // $481B diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp index 63cbef8944..9ee8fb93a9 100644 --- a/engines/scumm/room.cpp +++ b/engines/scumm/room.cpp @@ -747,7 +747,7 @@ void ScummEngine_v3old::resetRoomSubBlocks() {  			}  			ptr = roomptr + *(roomptr + 0x15); -			size = numOfBoxes * SIZEOF_BOX_C64 + 1; +			size = numOfBoxes * SIZEOF_BOX_V0 + 1;  			_res->createResource(rtMatrix, 2, size + 1);  			getResourceAddress(rtMatrix, 2)[0] = numOfBoxes; diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 38dbd8270a..51a291b7f2 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1205,12 +1205,19 @@ void ScummEngine::saveOrLoad(Serializer *s) {  	// Save/load local objects  	//  	s->saveLoadArrayOf(_objs, _numLocalObjects, sizeof(_objs[0]), objectEntries); -	if (s->isLoading() && s->getVersion() < VER(13)) { -		// Since roughly v13 of the save games, the objs storage has changed a bit -		for (i = _numObjectsInRoom; i < _numLocalObjects; i++) { -			_objs[i].obj_nr = 0; +	if (s->isLoading()) { +		if (s->getVersion() < VER(13)) { +			// Since roughly v13 of the save games, the objs storage has changed a bit +			for (i = _numObjectsInRoom; i < _numLocalObjects; i++) +				_objs[i].obj_nr = 0; +		} else if (_game.version == 0 && s->getVersion() < VER(89)) { +			for (i = 0; i < _numLocalObjects; i++) { +				// Merge object id and type (previously stored in flags) +				if (_objs[i].obj_nr != 0 && OBJECT_V0_TYPE(_objs[i].obj_nr) == 0 && _objs[i].flags != 0) +					_objs[i].obj_nr = OBJECT_V0(_objs[i].obj_nr, _objs[i].flags); +				_objs[i].flags = 0; +			}  		} -  	} @@ -1497,6 +1504,14 @@ void ScummEngine_v0::saveOrLoad(Serializer *s) {  	const SaveLoadEntry v0Entrys[] = {  		MKLINE(ScummEngine_v0, _currentMode, sleByte, VER(78)),  		MKLINE(ScummEngine_v0, _currentLights, sleByte, VER(78)), +		MKLINE(ScummEngine_v0, _activeVerb, sleByte, VER(89)), +		MKLINE(ScummEngine_v0, _activeObject, sleUint16, VER(89)), +		MKLINE(ScummEngine_v0, _activeObject2, sleUint16, VER(89)), +		MKLINE(ScummEngine_v0, _cmdVerb, sleByte, VER(89)), +		MKLINE(ScummEngine_v0, _cmdObject, sleUint16, VER(89)), +		MKLINE(ScummEngine_v0, _cmdObject2, sleUint16, VER(89)), +		MKLINE(ScummEngine_v0, _walkToObject, sleUint16, VER(89)), +		MKLINE(ScummEngine_v0, _walkToObjectState, sleByte, VER(89)),  		MKEND()  	};   	s->saveLoadEntries(this, v0Entrys); diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index 064bdf1406..898f80f867 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -47,7 +47,7 @@ namespace Scumm {   * only saves/loads those which are valid for the version of the savegame   * which is being loaded/saved currently.   */ -#define CURRENT_VER 88 +#define CURRENT_VER 89  /**   * An auxillary macro, used to specify savegame versions. We use this instead diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp index 37ea3a9a9f..39420ee974 100644 --- a/engines/scumm/script.cpp +++ b/engines/scumm/script.cpp @@ -28,6 +28,7 @@  #include "scumm/object.h"  #include "scumm/resource.h"  #include "scumm/util.h" +#include "scumm/scumm_v0.h"  #include "scumm/scumm_v2.h"  #include "scumm/sound.h"  #include "scumm/verbs.h" @@ -131,8 +132,6 @@ void ScummEngine::runObjectScript(int object, int entry, bool freezeResistant, b  	initializeLocals(slot, vars); -	// V0 Ensure we don't try and access objects via index inside the script -	_v0ObjectIndex = false;  	runScriptNested(slot);  } @@ -196,9 +195,10 @@ int ScummEngine::getVerbEntrypoint(int obj, int entry) {  		return verboffs + 8 + READ_LE_UINT32(ptr + 1);  	} else if (_game.version <= 2) {  		do { +			const int kFallbackEntry = (_game.version == 0 ? 0x0F : 0xFF);  			if (!*verbptr)  				return 0; -			if (*verbptr == entry || *verbptr == 0xFF) +			if (*verbptr == entry || *verbptr == kFallbackEntry)  				break;  			verbptr += 2;  		} while (1); @@ -1000,7 +1000,7 @@ void ScummEngine::killScriptsAndResources() {  		for (i = 0; i < _numNewNames; i++) {  			const int obj = _newNames[i];  			if (obj) { -				const int owner = getOwner(obj); +				const int owner = getOwner((_game.version != 0 ? obj : OBJECT_V0_ID(obj)));  				// We can delete custom name resources if either the object is  				// no longer in use (i.e. not owned by anyone anymore); or if  				// it is an object which is owned by a room. @@ -1130,6 +1130,183 @@ void ScummEngine::checkAndRunSentenceScript() {  		runScript(sentenceScript, 0, 0, localParamList);  } +void ScummEngine_v0::walkToActorOrObject(int object) { +	int x, y, dir; +	Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "walkToObject"); + +	_walkToObject = object; +	_walkToObjectState = kWalkToObjectStateWalk; + +	if (OBJECT_V0_TYPE(object) == kObjectV0TypeActor) { +		walkActorToActor(VAR(VAR_EGO), OBJECT_V0_ID(object), 4); +		x = a->getRealPos().x; +		y = a->getRealPos().y; +	} else { +		walkActorToObject(VAR(VAR_EGO), object); +		getObjectXYPos(object, x, y, dir); +	} + +	VAR(6) = x; +	VAR(7) = y; + +	// actor must not move if frozen +	if (a->_miscflags & kActorMiscFlagFreeze) +		a->stopActorMoving(); +} + +bool ScummEngine_v0::checkPendingWalkAction() { +	// before a sentence script is executed, it might be necessary to walk to +	// and pickup objects before. Check if such an action is pending and handle +	// it if available. +	if (_walkToObjectState == kWalkToObjectStateDone) +		return false; + +	int actor = VAR(VAR_EGO); +	Actor_v0 *a = (Actor_v0 *)derefActor(actor, "checkPendingWalkAction"); + +	// wait until walking or turning action is finished +	if (a->_moving) +		return true; + +	// after walking and turning finally execute the script +	if (_walkToObjectState == kWalkToObjectStateTurn) { +		runSentenceScript(); +	// change actor facing +	} else { +		int x, y, distX, distY; +		if (objIsActor(_walkToObject)) { +			Actor *b = derefActor(objToActor(_walkToObject), "checkPendingWalkAction(2)"); +			x = b->getRealPos().x; +			y = b->getRealPos().y; +			if (x < a->getRealPos().x) +				x += 4; +			else +				x -= 4; +		} else { +			getObjectXYPos(_walkToObject, x, y); +		} +		AdjustBoxResult abr = a->adjustXYToBeInBox(x, y); +		distX = ABS(a->getRealPos().x - abr.x); +		distY = ABS(a->getRealPos().y - abr.y); + +		if (distX <= 4 && distY <= 8) { +			if (objIsActor(_walkToObject)) { // walk to actor finished +				// make actors turn to each other +				a->faceToObject(_walkToObject); +				int otherActor = objToActor(_walkToObject); +				// ignore the plant +				if (otherActor != 19) { +					Actor *b = derefActor(otherActor, "checkPendingWalkAction(3)"); +					b->faceToObject(actorToObj(actor)); +				} +			} else { // walk to object finished +				int tmpX, tmpY, dir; +				getObjectXYPos(_walkToObject, tmpX, tmpY, dir); +				a->turnToDirection(dir); +			} +			_walkToObjectState = kWalkToObjectStateTurn; +			return true; +		} +	} + +	_walkToObjectState = kWalkToObjectStateDone; +	return false; +} + +void ScummEngine_v0::checkAndRunSentenceScript() { +	if (checkPendingWalkAction()) +		return; + +	if (!_sentenceNum || _sentence[_sentenceNum - 1].freezeCount) +		return; + +	SentenceTab &st = _sentence[_sentenceNum - 1]; + +	if (st.preposition && st.objectB == st.objectA) { +		_sentenceNum--; +		return; +	} + +	_currentScript = 0xFF; + +	assert(st.objectA); + +	// If two objects are involved, at least one must be in the actors inventory +	if (st.objectB && +		(OBJECT_V0_TYPE(st.objectA) != kObjectV0TypeFG || _objectOwnerTable[st.objectA] != VAR(VAR_EGO)) && +		(OBJECT_V0_TYPE(st.objectB) != kObjectV0TypeFG || _objectOwnerTable[st.objectB] != VAR(VAR_EGO))) +	{ +		if (getVerbEntrypoint(st.objectA, kVerbPickUp)) +			doSentence(kVerbPickUp, st.objectA, 0); +		else if (getVerbEntrypoint(st.objectB, kVerbPickUp)) +			doSentence(kVerbPickUp, st.objectB, 0); +		else +			_sentenceNum--; +		return; +	} + +	_cmdVerb = st.verb; +	_cmdObject = st.objectA; +	_cmdObject2 = st.objectB; +	_sentenceNum--; + +	// abort sentence execution if the number of nested scripts is too high. +	// This might happen for instance if the sentence command depends on an +	// object that the actor has to pick-up in a nested doSentence() call. +	// If the actor is not able to pick-up the object (e.g. because it is not +	// reachable or pickupable) a nested pick-up command is triggered again +	// and again, so the actual sentence command will never be executed. +	// In this case the sentence command has to be aborted. +	_sentenceNestedCount++; +	if (_sentenceNestedCount > 6) { +		_sentenceNestedCount = 0; +		_sentenceNum = 0; +		return; +	} + +	if (whereIsObject(st.objectA) != WIO_INVENTORY) { +		if (_currentMode != kModeKeypad) { +			walkToActorOrObject(st.objectA); +			return; +		} +	} else if (st.objectB && whereIsObject(st.objectB) != WIO_INVENTORY) { +		walkToActorOrObject(st.objectB); +		return; +	} + +	runSentenceScript(); +	if (_currentMode == kModeKeypad) { +		_walkToObjectState = kWalkToObjectStateDone; +	} +} + +void ScummEngine_v0::runSentenceScript() { +	_redrawSentenceLine = true; + +	if (getVerbEntrypoint(_cmdObject, _cmdVerb) != 0) { +		// do not read in the dark +		if (!(_cmdVerb == kVerbRead && _currentLights == 0)) { +			VAR(VAR_ACTIVE_OBJECT2) = OBJECT_V0_ID(_cmdObject2); +			runObjectScript(_cmdObject, _cmdVerb, false, false, NULL); +			return; +		} +	} else { +		if (_cmdVerb == kVerbGive) { +			// no "give to"-script: give to other kid or ignore +			int actor = OBJECT_V0_ID(_cmdObject2); +			if (actor < 8) +				setOwnerOf(_cmdObject, actor); +			return; +		} +	} + +	if (_cmdVerb != kVerbWalkTo) { +		// perform verb's fallback action +		VAR(VAR_ACTIVE_VERB) = _cmdVerb; +		runScript(3, 0, 0, 0); +	} +} +  void ScummEngine_v2::runInputScript(int clickArea, int val, int mode) {  	int args[24];  	int verbScript; diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp index e2d3f40e8e..45901186cd 100644 --- a/engines/scumm/script_v0.cpp +++ b/engines/scumm/script_v0.cpp @@ -27,6 +27,7 @@  #include "scumm/resource.h"  #include "scumm/scumm_v0.h"  #include "scumm/verbs.h" +#include "scumm/util.h"  namespace Scumm { @@ -50,7 +51,7 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0x0b, o_setActorBitVar);  	/* 0C */  	OPCODE(0x0c, o_loadSound); -	OPCODE(0x0d, o_printEgo_c64); +	OPCODE(0x0d, o_printEgo);  	OPCODE(0x0e, o_putActorAtObject);  	OPCODE(0x0f, o2_clearState02);  	/* 10 */ @@ -59,7 +60,7 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0x12, o2_panCameraTo);  	OPCODE(0x13, o_lockCostume);  	/* 14 */ -	OPCODE(0x14, o_print_c64); +	OPCODE(0x14, o_print);  	OPCODE(0x15, o5_walkActorToActor);  	OPCODE(0x16, o5_getRandomNr);  	OPCODE(0x17, o2_clearState08); @@ -79,9 +80,9 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0x22, o4_saveLoadGame);  	OPCODE(0x23, o_stopCurrentScript);  	/* 24 */ -	OPCODE(0x24, o_unknown2); +	OPCODE(0x24, o_ifNotEqualActiveObject2);  	OPCODE(0x25, o5_loadRoom); -	OPCODE(0x26, o_getClosestObjActor); +	OPCODE(0x26, o_getClosestActor);  	OPCODE(0x27, o2_getActorY);  	/* 28 */  	OPCODE(0x28, o5_equalZero); @@ -91,7 +92,7 @@ void ScummEngine_v0::setupOpcodes() {  	/* 2C */  	OPCODE(0x2c, o_stopCurrentScript);  	OPCODE(0x2d, o2_putActorInRoom); -	OPCODE(0x2e, o_print_c64); +	OPCODE(0x2e, o_print);  	OPCODE(0x2f, o2_ifState08);  	/* 30 */  	OPCODE(0x30, o_loadCostume); @@ -101,7 +102,7 @@ void ScummEngine_v0::setupOpcodes() {  	/* 34 */  	OPCODE(0x34, o5_getDist);  	OPCODE(0x35, o_stopCurrentScript); -	OPCODE(0x36, o2_walkActorToObject); +	OPCODE(0x36, o_walkActorToObject);  	OPCODE(0x37, o2_clearState04);  	/* 38 */  	OPCODE(0x38, o2_isLessEqual); @@ -144,7 +145,7 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0x56, o_getActorMoving);  	OPCODE(0x57, o2_clearState08);  	/* 58 */ -	OPCODE(0x58, o_beginOverride); +	OPCODE(0x58, o2_beginOverride);  	OPCODE(0x59, o_stopCurrentScript);  	OPCODE(0x5a, o2_add);  	OPCODE(0x5b, o_getActorBitVar); @@ -154,14 +155,14 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0x5e, o2_walkActorTo);  	OPCODE(0x5f, o2_ifState04);  	/* 60 */ -	OPCODE(0x60, o_cursorCommand); +	OPCODE(0x60, o_setMode);  	OPCODE(0x61, o2_putActor);  	OPCODE(0x62, o2_stopScript);  	OPCODE(0x63, o_stopCurrentScript);  	/* 64 */ -	OPCODE(0x64, o_ifActiveObject); +	OPCODE(0x64, o_ifEqualActiveObject2);  	OPCODE(0x65, o_stopCurrentScript); -	OPCODE(0x66, o_getClosestObjActor); +	OPCODE(0x66, o_getClosestActor);  	OPCODE(0x67, o5_getActorFacing);  	/* 68 */  	OPCODE(0x68, o5_isScriptRunning); @@ -177,11 +178,11 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0x70, o_lights);  	OPCODE(0x71, o_getBitVar);  	OPCODE(0x72, o_nop); -	OPCODE(0x73, o5_getObjectOwner); +	OPCODE(0x73, o_getObjectOwner);  	/* 74 */  	OPCODE(0x74, o5_getDist); -	OPCODE(0x75, o_printEgo_c64); -	OPCODE(0x76, o2_walkActorToObject); +	OPCODE(0x75, o_printEgo); +	OPCODE(0x76, o_walkActorToObject);  	OPCODE(0x77, o2_clearState04);  	/* 78 */  	OPCODE(0x78, o2_isGreater); @@ -239,7 +240,7 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0xa2, o4_saveLoadGame);  	OPCODE(0xa3, o_stopCurrentScript);  	/* A4 */ -	OPCODE(0xa4, o_unknown2); +	OPCODE(0xa4, o_ifNotEqualActiveObject2);  	OPCODE(0xa5, o5_loadRoom);  	OPCODE(0xa6, o_stopCurrentScript);  	OPCODE(0xa7, o2_getActorY); @@ -251,7 +252,7 @@ void ScummEngine_v0::setupOpcodes() {  	/* AC */  	OPCODE(0xac, o_stopCurrentScript);  	OPCODE(0xad, o2_putActorInRoom); -	OPCODE(0xae, o_print_c64); +	OPCODE(0xae, o_print);  	OPCODE(0xaf, o2_ifNotState08);  	/* B0 */  	OPCODE(0xb0, o_loadCostume); @@ -261,7 +262,7 @@ void ScummEngine_v0::setupOpcodes() {  	/* B4 */  	OPCODE(0xb4, o5_getDist);  	OPCODE(0xb5, o_stopCurrentScript); -	OPCODE(0xb6, o2_walkActorToObject); +	OPCODE(0xb6, o_walkActorToObject);  	OPCODE(0xb7, o2_setState04);  	/* B8 */  	OPCODE(0xb8, o2_isLessEqual); @@ -314,12 +315,12 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0xde, o2_walkActorTo);  	OPCODE(0xdf, o2_ifNotState04);  	/* E0 */ -	OPCODE(0xe0, o_cursorCommand); +	OPCODE(0xe0, o_setMode);  	OPCODE(0xe1, o2_putActor);  	OPCODE(0xe2, o2_stopScript);  	OPCODE(0xe3, o_stopCurrentScript);  	/* E4 */ -	OPCODE(0xe4, o_ifActiveObject); +	OPCODE(0xe4, o_ifEqualActiveObject2);  	OPCODE(0xe5, o_loadRoomWithEgo);  	OPCODE(0xe6, o_stopCurrentScript);  	OPCODE(0xe7, o5_getActorFacing); @@ -337,11 +338,11 @@ void ScummEngine_v0::setupOpcodes() {  	OPCODE(0xf0, o_lights);  	OPCODE(0xf1, o_getBitVar);  	OPCODE(0xf2, o_nop); -	OPCODE(0xf3, o5_getObjectOwner); +	OPCODE(0xf3, o_getObjectOwner);  	/* F4 */  	OPCODE(0xf4, o5_getDist);  	OPCODE(0xf5, o_stopCurrentScript); -	OPCODE(0xf6, o2_walkActorToObject); +	OPCODE(0xf6, o_walkActorToObject);  	OPCODE(0xf7, o2_setState04);  	/* F8 */  	OPCODE(0xf8, o2_isGreater); @@ -365,7 +366,7 @@ uint ScummEngine_v0::fetchScriptWord() {  int ScummEngine_v0::getActiveObject() {  	if (_opcode & PARAM_2) -		return _activeObject; +		return OBJECT_V0_ID(_cmdObject);  	return fetchScriptByte();  } @@ -406,172 +407,121 @@ void ScummEngine_v0::decodeParseString() {  	actorTalk(buffer);  } -void ScummEngine_v0::drawSentenceWord(int object, bool usePrep, bool objInInventory) { -	const byte *temp; -	int sentencePrep = 0; - -	// If object not in inventory, we except an index -	if (!objInInventory) -		_v0ObjectIndex = true; -	else -		_v0ObjectInInventory = true; - -	temp = getObjOrActorName(object); +void ScummEngine_v0::clearSentenceLine() { +	Common::Rect sentenceline; +	sentenceline.top = _virtscr[kVerbVirtScreen].topline; +	sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8; +	sentenceline.left = 0; +	sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; +	restoreBackground(sentenceline); +} -	_v0ObjectInInventory = false; -	_v0ObjectIndex = false; +void ScummEngine_v0::flushSentenceLine() { +	byte string[80]; +	const char *ptr = _sentenceBuf.c_str(); +	int i = 0, len = 0; -	// Append the 'object-name' -	if (temp) { -		_sentenceBuf += " "; -		_sentenceBuf += (const char *)temp; -	} +	// Maximum length of printable characters +	int maxChars = 40; +	while (*ptr) { +		if (*ptr != '@') +			len++; +		if (len > maxChars) { +			break; +		} -	// Append the modifier? (With / On / To / In) -	if (!usePrep) -		return; +		string[i++] = *ptr++; -	if (_verbs[_activeVerb].prep == 0xFF) { -		_v0ObjectInInventory = objInInventory; -		sentencePrep = verbPrep(object); -	} else { -		sentencePrep = _verbs[_activeVerb].prep;  	} +	string[i] = 0; -	if (sentencePrep > 0 && sentencePrep <= 4) { -		// The prepositions, like the fonts, were hard code in the engine. Thus -		// we have to do that, too, and provde localized versions for all the -		// languages MM/Zak are available in. -		static const char *const prepositions[][5] = { -			{ " ", " in", " with", " on", " to" },   // English -			{ " ", " mit", " mit", " mit", " zu" },  // German -			{ " ", " dans", " avec", " sur", " <" }, // French -			{ " ", " in", " con", " su", " a" },     // Italian -			{ " ", " en", " con", " en", " a" },     // Spanish -			}; -		int lang; -		switch (_language) { -		case Common::DE_DEU: -			lang = 1; -			break; -		case Common::FR_FRA: -			lang = 2; -			break; -		case Common::IT_ITA: -			lang = 3; -			break; -		case Common::ES_ESP: -			lang = 4; -			break; -		default: -			lang = 0;	// Default to english -		} +	_string[2].charset = 1; +	_string[2].ypos = _virtscr[kVerbVirtScreen].topline; +	_string[2].xpos = 0; +	_string[2].right = _virtscr[kVerbVirtScreen].w - 1; +	_string[2].color = 16; +	drawString(2, (byte *)string); +} -		_sentenceBuf += prepositions[lang][sentencePrep]; +void ScummEngine_v0::drawSentenceObject(int object) { +	const byte *temp; +	temp = getObjOrActorName(object); +	if (temp) { +		_sentenceBuf += " "; +		_sentenceBuf += (const char *)temp;  	}  } -void ScummEngine_v0::drawSentence() { -	Common::Rect sentenceline; -	bool		 inventoryFirst = false; -	if (!(_userState & 32)) -		return; +void ScummEngine_v0::drawSentenceLine() { +	_redrawSentenceLine = false; -	// Current Verb, Walk/Use -	if (getResourceAddress(rtVerb, _activeVerb)) { -		_sentenceBuf = (char *)getResourceAddress(rtVerb, _activeVerb); -	} else { +	if (!(_userState & USERSTATE_IFACE_SENTENCE))  		return; -	} - -	// If using inventory first, draw it first -	if (_activeInvExecute && _activeInventory) { -		drawSentenceWord(_activeInventory, true, true); -	} else { -		// Not using inventory, use selected object -		if (_activeObject) -			drawSentenceWord(_activeObjectIndex, true, false); -		else -			inventoryFirst = true; -	} - -	// Draw the inventory? -	if (_activeInventory > 0 && _activeObject2 == 0) { -		// Only if inventory isnt first (it will already be drawn by now) -		if (!_activeInvExecute) { -			drawSentenceWord(_activeInventory, inventoryFirst, true); -		} else { -			// Draw the active object, which could be inventory based, or room based -			if (_activeObject && !_activeObjectIndex) { -				drawSentenceWord(_activeObject, inventoryFirst, true); -			} else // Room based -				drawSentenceWord(_activeObjectIndex, inventoryFirst, false); -		} - -	// Draw the 2nd active object -	} else if (_activeObject2) { - -		// 2nd Object is in inventory -		if (_activeObject2Inv) { -			_v0ObjectInInventory = true; -			drawSentenceWord(_activeObject2, inventoryFirst, true); -		} else { -			drawSentenceWord(_activeObject2Index, inventoryFirst, false); +	clearSentenceLine(); + +	if (_activeVerb == kVerbNewKid) { +		_sentenceBuf = ""; +		for (int i = 0; i < 3; ++i) { +			const char *actorName; +			int actorId = VAR(97 + i); +			if (actorId == 0) { +				// after usage of the radiation suit, kid vars are set to 0 +				actorName = " "; +			} else { +				Actor *a = derefActor(actorId, "drawSentenceLine"); +				actorName = (char *)a->getActorName(); +			} +			_sentenceBuf += Common::String::format("%-13s", actorName);  		} +		flushSentenceLine(); +		return;  	} -	// Draw the active actor -	if (_activeActor) { -		Actor *a = derefActor(_activeActor, ""); +	// Current Verb +	if (_activeVerb == kVerbNone) +		_activeVerb = kVerbWalkTo; -		_sentenceBuf += " "; -		_sentenceBuf += (const char *)a->getActorName(); -	} +	char *verbName = (char *)getResourceAddress(rtVerb, _activeVerb); +	assert(verbName); +	_sentenceBuf = verbName; -	_string[2].charset = 1; -	_string[2].ypos = _virtscr[kVerbVirtScreen].topline; -	_string[2].xpos = 0; -	_string[2].right = _virtscr[kVerbVirtScreen].w - 1; -	_string[2].color = 16; +	if (_activeObject) { +		// Draw the 1st active object +		drawSentenceObject(_activeObject); -	byte string[80]; -	const char *ptr = _sentenceBuf.c_str(); -	int i = 0, len = 0; +		// Append verb preposition +		int sentencePrep = activeVerbPrep(); +		if (sentencePrep) { +			drawPreposition(sentencePrep); -	// Maximum length of printable characters -	int maxChars = 40; -	while (*ptr) { -		if (*ptr != '@') -			len++; -		if (len > maxChars) { -			break; +			// Draw the 2nd active object +			if (_activeObject2) +				drawSentenceObject(_activeObject2);  		} - -		string[i++] = *ptr++; -  	} -	string[i] = 0; -	sentenceline.top = _virtscr[kVerbVirtScreen].topline; -	sentenceline.bottom = _virtscr[kVerbVirtScreen].topline + 8; -	sentenceline.left = 0; -	sentenceline.right = _virtscr[kVerbVirtScreen].w - 1; -	restoreBackground(sentenceline); - -	drawString(2, (byte *)string); +	flushSentenceLine();  }  void ScummEngine_v0::o_stopCurrentScript() { -	int script; +	stopScriptCommon(0); +} -	script = vm.slot[_currentScript].number; +void ScummEngine_v0::o_walkActorToObject() { +	int actor = getVarOrDirectByte(PARAM_1); +	int objId = fetchScriptByte(); +	int obj; -	if (_currentScript != 0 && vm.slot[_currentScript].number == script) -		stopObjectCode(); +	if (_opcode & 0x40) +		obj = OBJECT_V0(objId, kObjectV0TypeBG);  	else -		stopScript(script); +		obj = OBJECT_V0(objId, kObjectV0TypeFG); + +	if (whereIsObject(obj) != WIO_NOT_FOUND) { +		walkActorToObject(actor, obj); +	}  }  void ScummEngine_v0::o_loadSound() { @@ -625,18 +575,16 @@ void ScummEngine_v0::o_loadRoom() {  }  void ScummEngine_v0::o_loadRoomWithEgo() { -	Actor *a; +	Actor_v0 *a;  	int obj, room, x, y, dir;  	obj = fetchScriptByte();  	room = fetchScriptByte(); -	a = derefActor(VAR(VAR_EGO), "o_loadRoomWithEgo"); +	a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "o_loadRoomWithEgo");  	//0x634F -	if (((ActorC64 *)a)->_miscflags & 0x40) { -		// TODO: Check if this is the correct function -		// to be calling here +	if (a->_miscflags & kActorMiscFlagFreeze) {  		stopObjectCode();  		return;  	} @@ -654,15 +602,14 @@ void ScummEngine_v0::o_loadRoomWithEgo() {  	x = r.x;  	y = r.y;  	a->putActor(x, y, _currentRoom); -	a->setDirection(dir + 180); - +	  	camera._dest.x = camera._cur.x = a->getPos().x;  	setCameraAt(a->getPos().x, a->getPos().y);  	setCameraFollows(a);  	_fullRedraw = true; -	resetSentence(false); +	resetSentence();  	if (x >= 0 && y >= 0) {  		a->startWalkActor(x, y, -1); @@ -679,27 +626,39 @@ void ScummEngine_v0::o_unlockRoom() {  	_res->unlock(rtRoom, resid);  } -void ScummEngine_v0::o_cursorCommand() { -	// TODO -	int state = 0; +void ScummEngine_v0::setMode(byte mode) { +	int state; + +	_currentMode = mode; -	_currentMode = fetchScriptByte();  	switch (_currentMode) { -	case 0: -		state = 15; +	case kModeCutscene: +		_redrawSentenceLine = false; +		// Note: do not change freeze state here +		state = USERSTATE_SET_IFACE |  +			USERSTATE_SET_CURSOR;  		break; -	case 1: -		state = 31; +	case kModeKeypad: +		_redrawSentenceLine = false; +		state = USERSTATE_SET_IFACE |  +			USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON | +			USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON;  		break; -	case 2: -		break; -	case 3: -		state = 247; +	case kModeNormal: +	case kModeNoNewKid: +		state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL |  +			USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON | +			USERSTATE_SET_FREEZE;  		break; +	default: +		error("Invalid mode: %d", mode);  	}  	setUserState(state); -	debug(0, "o_cursorCommand(%d)", _currentMode); +} + +void ScummEngine_v0::o_setMode() { +	setMode(fetchScriptByte());  }  void ScummEngine_v0::o_lights() { @@ -724,24 +683,31 @@ void ScummEngine_v0::o_lights() {  void ScummEngine_v0::o_animateActor() {  	int act = getVarOrDirectByte(PARAM_1);  	int anim = getVarOrDirectByte(PARAM_2); -	int unk = fetchScriptByte(); - -	debug(0,"o_animateActor: unk %d", unk); - -	ActorC64 *a = (ActorC64*) derefActor(act, "o_animateActor"); - -	// 0x6993 -	if (anim == 0xFE) { -		a->_speaking = 0x80;	// Enabled, but not switching -		return; -	} -	// 0x69A3 -	if (anim == 0xFD) { -		a->_speaking = 0x00; -		return; +	int8 repeat = (int8) fetchScriptByte(); + +	Actor_v0 *a = (Actor_v0*) derefActor(act, "o_animateActor"); + +	a->_animFrameRepeat = repeat; +	 +	switch (anim) { + +		case 0xFE: +			// 0x6993 +			a->_speaking = 0x80;	// Enabled, but not switching +			return; + +		case 0xFD: +			// 0x69A3 +			a->_speaking = 0x00; +			return; +	 +		case 0xFF: +			a->stopActorMoving(); +			return;  	}  	a->animateActor(anim); +	a->animateCostume();  }  void ScummEngine_v0::o_getActorMoving() { @@ -760,7 +726,12 @@ void ScummEngine_v0::o_putActorAtObject() {  	a = derefActor(getVarOrDirectByte(PARAM_1), "o_putActorAtObject"); -	obj = fetchScriptByte(); +	int objId = fetchScriptByte(); +	if (_opcode & 0x40) +		obj = OBJECT_V0(objId, kObjectV0TypeBG); +	else +		obj = OBJECT_V0(objId, kObjectV0TypeFG); +  	if (whereIsObject(obj) != WIO_NOT_FOUND) {  		getObjectXYPos(obj, x, y);  		AdjustBoxResult r = a->adjustXYToBeInBox(x, y); @@ -776,20 +747,13 @@ void ScummEngine_v0::o_putActorAtObject() {  void ScummEngine_v0::o_pickupObject() {  	int obj = fetchScriptByte(); -	if (obj == 0) { -		obj = _activeObject; -	} +	if (!obj) +		obj = _cmdObject; -	if (obj < 1) { -		error("pickupObject received invalid index %d (script %d)", obj, vm.slot[_currentScript].number); -	} - -	if (getObjectIndex(obj) == -1) +	/* Don't take an object twice */ +	if (whereIsObject(obj) == WIO_INVENTORY)  		return; -	if (whereIsObjectInventory(_activeObject2) == WIO_INVENTORY)	/* Don't take an */ -		return;					/* object twice */ -  	addObjectToInventory(obj, _roomResource);  	markObjectRectAsDirty(obj);  	putOwner(obj, VAR(VAR_EGO)); @@ -800,14 +764,22 @@ void ScummEngine_v0::o_pickupObject() {  }  void ScummEngine_v0::o_setObjectName() { -	int obj = fetchScriptByte(); +	int obj; +	int objId = fetchScriptByte(); +	if (!objId) { +		obj = _cmdObject; +	} else { +		if (_opcode & 0x80) +			obj = OBJECT_V0(objId, kObjectV0TypeBG); +		else +			obj = OBJECT_V0(objId, kObjectV0TypeFG); +	}  	setObjectName(obj);  }  void ScummEngine_v0::o_nop() {  } -// TODO: Maybe translate actor flags in future.  void ScummEngine_v0::o_setActorBitVar() {  	byte act = getVarOrDirectByte(PARAM_1);  	byte mask = getVarOrDirectByte(PARAM_2); @@ -817,7 +789,7 @@ void ScummEngine_v0::o_setActorBitVar() {  	if (act >= _numActors)  		return; -	ActorC64 *a = (ActorC64 *)derefActor(act, "o_setActorBitVar"); +	Actor_v0 *a = (Actor_v0 *)derefActor(act, "o_setActorBitVar");  	if (mod)  		a->_miscflags |= mask; @@ -825,20 +797,24 @@ void ScummEngine_v0::o_setActorBitVar() {  		a->_miscflags &= ~mask;  	// This flag causes the actor to stop moving (used by script #158, Green Tentacle 'Oomph!') -	if (a->_miscflags & 0x40) +	if (a->_miscflags & kActorMiscFlagFreeze)  		a->stopActorMoving(); -	if (a->_miscflags & 0x80) -		a->setActorCostume(0);  	debug(0, "o_setActorBitVar(%d, %d, %d)", act, mask, mod);  } +void ScummEngine_v0::o_getObjectOwner() { +	getResultPos(); +	int obj = getVarOrDirectWord(PARAM_1); +	setResult(getOwner(obj ? obj : _cmdObject)); +} +  void ScummEngine_v0::o_getActorBitVar() {  	getResultPos();  	byte act = getVarOrDirectByte(PARAM_1);  	byte mask = getVarOrDirectByte(PARAM_2); -	ActorC64 *a = (ActorC64 *)derefActor(act, "o_getActorBitVar"); +	Actor_v0 *a = (Actor_v0 *)derefActor(act, "o_getActorBitVar");  	setResult((a->_miscflags & mask) ? 1 : 0);  	debug(0, "o_getActorBitVar(%d, %d, %d)", act, mask, (a->_miscflags & mask)); @@ -867,74 +843,97 @@ void ScummEngine_v0::o_getBitVar() {  	debug(0, "o_getBitVar (%d, %d %d)", flag, mask, _bitVars[flag] & (1 << mask));  } -void ScummEngine_v0::o_print_c64() { +void ScummEngine_v0::o_print() {  	_actorToPrintStrFor = fetchScriptByte();  	decodeParseString();  } -void ScummEngine_v0::o_printEgo_c64() { +void ScummEngine_v0::o_printEgo() {  	_actorToPrintStrFor = (byte)VAR(VAR_EGO);  	decodeParseString();  }  void ScummEngine_v0::o_doSentence() { -	byte entry = fetchScriptByte(); -	byte obj = fetchScriptByte(); -	fetchScriptByte(); +	byte verb = fetchScriptByte(); +	int obj, obj2; +	byte b; + +	b = fetchScriptByte(); +	if (b == 0xFF) { +		obj = _cmdObject2; +	} else if (b == 0xFE) { +		obj = _cmdObject; +	} else { +		obj = OBJECT_V0(b, (_opcode & 0x80) ? kObjectV0TypeBG : kObjectV0TypeFG); +	} -	runObjectScript(obj, entry, false, false, NULL); -} +	b = fetchScriptByte(); +	if (b == 0xFF) { +		obj2 = _cmdObject2; +	} else if (b == 0xFE) { +		obj2 = _cmdObject; +	} else { +		obj2 = OBJECT_V0(b, (_opcode & 0x40) ? kObjectV0TypeBG : kObjectV0TypeFG); +	} -void ScummEngine_v0::o_unknown2() { -	byte var1 = fetchScriptByte(); -	error("STUB: o_unknown2(%d)", var1); +	doSentence(verb, obj, obj2);  } -void ScummEngine_v0::o_ifActiveObject() { +bool ScummEngine_v0::ifEqualActiveObject2Common(bool checkType) {  	byte obj = fetchScriptByte(); +	if (!checkType || (OBJECT_V0_TYPE(_cmdObject2) == kObjectV0TypeFG)) +		return (obj == OBJECT_V0_ID(_cmdObject2)); +	return false; +} -	jumpRelative(obj == _activeInventory); +void ScummEngine_v0::o_ifEqualActiveObject2() { +	bool equal = ifEqualActiveObject2Common((_opcode & 0x80) == 0); +	jumpRelative(equal);  } -void ScummEngine_v0::o_getClosestObjActor() { -	int obj; -	int act; +void ScummEngine_v0::o_ifNotEqualActiveObject2() { +	bool equal = ifEqualActiveObject2Common((_opcode & 0x80) == 0); +	jumpRelative(!equal); +} + +void ScummEngine_v0::o_getClosestActor() { +	int act, check_act;  	int dist;  	// This code can't detect any actors farther away than 255 units  	// (pixels in newer games, characters in older ones.) But this is  	// perfectly OK, as it is exactly how the original behaved. -	int closest_obj = 0xFF, closest_dist = 0xFF; +	int closest_act = 0xFF, closest_dist = 0xFF;  	getResultPos();  	act = getVarOrDirectByte(PARAM_1); -	obj = (_opcode & PARAM_2) ? 25 : 7; +	check_act = (_opcode & PARAM_2) ? 25 : 7;  	do { -		dist = getObjActToObjActDist(act, obj); +		dist = getObjActToObjActDist(actorToObj(act), actorToObj(check_act));  		if (dist < closest_dist) {  			closest_dist = dist; -			closest_obj = obj; +			closest_act = check_act;  		} -	} while (--obj); +	} while (--check_act); -	setResult(closest_obj); +	setResult(closest_act);  }  void ScummEngine_v0::o_cutscene() { -	vm.cutSceneData[0] = _userState | (_userPut ? 16 : 0); +	vm.cutSceneData[0] = _currentMode;  	vm.cutSceneData[2] = _currentRoom; -	vm.cutSceneData[3] = camera._mode; -	// Hide inventory, freeze scripts, hide cursor -	setUserState(15); +	freezeScripts(0); +	setMode(kModeCutscene);  	_sentenceNum = 0; -	resetSentence(false); +	resetSentence();  	vm.cutScenePtr[0] = 0; +	vm.cutSceneScript[0] = 0;  }  void ScummEngine_v0::o_endCutscene() { @@ -944,68 +943,43 @@ void ScummEngine_v0::o_endCutscene() {  	vm.cutSceneScript[0] = 0;  	vm.cutScenePtr[0] = 0; -	// Reset user state to values before cutscene -	setUserState(vm.cutSceneData[0] | 7); +	setMode(vm.cutSceneData[0]); -	camera._mode = (byte) vm.cutSceneData[3]; -	if (camera._mode == kFollowActorCameraMode) { -		actorFollowCamera(VAR(VAR_EGO)); -	} else if (vm.cutSceneData[2] != _currentRoom) { +	if (_currentMode == kModeKeypad) {  		startScene(vm.cutSceneData[2], 0, 0); +		// in contrast to the normal keypad behavior we unfreeze scripts here +		unfreezeScripts(); +	} else { +		unfreezeScripts(); +		actorFollowCamera(VAR(VAR_EGO)); +		// set mode again to have the freeze mode right +		setMode(vm.cutSceneData[0]); +		_redrawSentenceLine = true;  	}  } -void ScummEngine_v0::o_beginOverride() { -	const int idx = vm.cutSceneStackPointer; -	assert(0 <= idx && idx < 5); - -	vm.cutScenePtr[idx] = _scriptPointer - _scriptOrgPointer; -	vm.cutSceneScript[idx] = _currentScript; - -	// Skip the jump instruction following the override instruction -	// (the jump is responsible for "skipping" cutscenes, and the reason -	// why we record the current script position in vm.cutScenePtr). -	fetchScriptByte(); -	ScummEngine::fetchScriptWord(); - -	// This is based on disassembly -	VAR(VAR_OVERRIDE) = 0; -} -  void ScummEngine_v0::o_setOwnerOf() {  	int obj, owner;  	obj = getVarOrDirectWord(PARAM_1);  	owner = getVarOrDirectByte(PARAM_2); -	if (obj == 0) -		obj = _activeInventory; +	if (!obj) +		obj = _cmdObject;  	setOwnerOf(obj, owner);  } -void ScummEngine_v0::resetSentence(bool walking) { -	_activeVerb = 13; - -	// If the actor is walking, or the screen is a keypad (no sentence verbs/objects are drawn) -	// Then reset all active objects (stops the radio crash, bug #3077966) -	if (!walking || !(_userState & 32)) { -		_v0ObjectFlag = 0; -		_activeInventory = 0; -		_activeObject = 0; -		_activeObject2 = 0; -		_activeObjectIndex = 0; -		_activeObject2Index = 0; -	} +void ScummEngine_v0::resetSentence() { +	_activeVerb = kVerbWalkTo; +	_activeObject = 0; +	_activeObject2 = 0; -	_verbExecuting = false; -	_verbPickup = false; +	_walkToObjectState = kWalkToObjectStateDone; +	_redrawSentenceLine = true; -	_activeActor = 0; -	_activeInvExecute = false; -	_activeObject2Inv = false; -	_activeObjectObtained = false; -	_activeObject2Obtained = false; +	_sentenceNum = 0; +	_sentenceNestedCount = 0;  }  } // End of namespace Scumm diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp index edb046d571..9c8742cffd 100644 --- a/engines/scumm/script_v2.cpp +++ b/engines/scumm/script_v2.cpp @@ -401,7 +401,7 @@ void ScummEngine_v2::decodeParseString() {  	_string[textSlot].overhead = false;  	if (_game.id == GID_MANIAC && _actorToPrintStrFor == 0xFF) { -		if (_game.platform == Common::kPlatformC64) { +		if (_game.version == 0) {  			_string[textSlot].color = 14;  		} else if (_game.features & GF_DEMO) {  			_string[textSlot].color = (_game.version == 2) ? 15 : 1; @@ -412,7 +412,7 @@ void ScummEngine_v2::decodeParseString() {  }  int ScummEngine_v2::readVar(uint var) { -	if (var >= 14 && var <= 16) +	if (_game.version >= 1 && var >= 14 && var <= 16)  		var = _scummVars[var];  	assertRange(0, var, _numVariables - 1, "variable (reading)"); @@ -953,12 +953,48 @@ void ScummEngine_v2::o2_doSentence() {  	}  } +void ScummEngine_v2::drawPreposition(int index) { +		// The prepositions, like the fonts, were hard code in the engine. Thus +		// we have to do that, too, and provde localized versions for all the +		// languages MM/Zak are available in. +		const char *prepositions[][5] = { +			{ " ", " in", " with", " on", " to" },   // English +			{ " ", " mit", " mit", " mit", " zu" },  // German +			{ " ", " dans", " avec", " sur", " <" }, // French +			{ " ", " in", " con", " su", " a" },     // Italian +			{ " ", " en", " con", " en", " a" },     // Spanish +			}; +		int lang; +		switch (_language) { +		case Common::DE_DEU: +			lang = 1; +			break; +		case Common::FR_FRA: +			lang = 2; +			break; +		case Common::IT_ITA: +			lang = 3; +			break; +		case Common::ES_ESP: +			lang = 4; +			break; +		default: +			lang = 0;	// Default to english +		} + +		if (_game.platform == Common::kPlatformNES) { +			_sentenceBuf += (const char *)(getResourceAddress(rtCostume, 78) + VAR(VAR_SENTENCE_PREPOSITION) * 8 + 2); +		} else +			_sentenceBuf += prepositions[lang][index]; +} +  void ScummEngine_v2::o2_drawSentence() {  	Common::Rect sentenceline;  	const byte *temp;  	int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0); -	if (!((_userState & 32) || (_game.platform == Common::kPlatformNES && _userState & 0xe0))) +	if (!((_userState & USERSTATE_IFACE_SENTENCE) ||  +	      (_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL))))  		return;  	if (getResourceAddress(rtVerb, slot)) @@ -986,38 +1022,7 @@ void ScummEngine_v2::o2_drawSentence() {  	}  	if (0 < VAR(VAR_SENTENCE_PREPOSITION) && VAR(VAR_SENTENCE_PREPOSITION) <= 4) { -		// The prepositions, like the fonts, were hard code in the engine. Thus -		// we have to do that, too, and provde localized versions for all the -		// languages MM/Zak are available in. -		const char *prepositions[][5] = { -			{ " ", " in", " with", " on", " to" },   // English -			{ " ", " mit", " mit", " mit", " zu" },  // German -			{ " ", " dans", " avec", " sur", " <" }, // French -			{ " ", " in", " con", " su", " a" },     // Italian -			{ " ", " en", " con", " en", " a" },     // Spanish -			}; -		int lang; -		switch (_language) { -		case Common::DE_DEU: -			lang = 1; -			break; -		case Common::FR_FRA: -			lang = 2; -			break; -		case Common::IT_ITA: -			lang = 3; -			break; -		case Common::ES_ESP: -			lang = 4; -			break; -		default: -			lang = 0;	// Default to english -		} - -		if (_game.platform == Common::kPlatformNES) { -			_sentenceBuf += (const char *)(getResourceAddress(rtCostume, 78) + VAR(VAR_SENTENCE_PREPOSITION) * 8 + 2); -		} else -			_sentenceBuf += prepositions[lang][VAR(VAR_SENTENCE_PREPOSITION)]; +		drawPreposition(VAR(VAR_SENTENCE_PREPOSITION));  	}  	if (VAR(VAR_SENTENCE_OBJECT2) > 0) { @@ -1186,11 +1191,7 @@ void ScummEngine_v2::o2_startScript() {  	runScript(script, 0, 0, 0);  } -void ScummEngine_v2::o2_stopScript() { -	int script; - -	script = getVarOrDirectByte(PARAM_1); - +void ScummEngine_v2::stopScriptCommon(int script) {  	if (_game.id == GID_MANIAC && _roomResource == 26 && vm.slot[_currentScript].number == 10001) {  	// FIXME: Nasty hack for bug #915575  	// Don't let the exit script for room 26 stop the script (116), when @@ -1211,26 +1212,31 @@ void ScummEngine_v2::o2_stopScript() {  		stopScript(script);  } +void ScummEngine_v2::o2_stopScript() { +	stopScriptCommon(getVarOrDirectByte(PARAM_1)); +} +  void ScummEngine_v2::o2_panCameraTo() {  	panCameraTo(getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER, 0);  } -void ScummEngine_v2::o2_walkActorToObject() { -	int obj; -	Actor *a; +void ScummEngine_v2::walkActorToObject(int actor, int obj) { +	int x, y, dir; +	getObjectXYPos(obj, x, y, dir); -	_v0ObjectFlag = 0; +	Actor *a = derefActor(actor, "walkActorToObject"); +	AdjustBoxResult r = a->adjustXYToBeInBox(x, y); +	x = r.x; +	y = r.y; -	a = derefActor(getVarOrDirectByte(PARAM_1), "o2_walkActorToObject"); -	obj = getVarOrDirectWord(PARAM_2); -	if (whereIsObject(obj) != WIO_NOT_FOUND) { -		int x, y, dir; -		getObjectXYPos(obj, x, y, dir); -		AdjustBoxResult r = a->adjustXYToBeInBox(x, y); -		x = r.x; -		y = r.y; +	a->startWalkActor(x, y, dir); +} -		a->startWalkActor(x, y, dir); +void ScummEngine_v2::o2_walkActorToObject() { +	int actor = getVarOrDirectByte(PARAM_1); +	int obj = getVarOrDirectWord(PARAM_2); +	if (whereIsObject(obj) != WIO_NOT_FOUND) { +		walkActorToObject(actor, obj);  	}  } @@ -1298,7 +1304,7 @@ void ScummEngine_v2::o2_findObject() {  	int x = getVarOrDirectByte(PARAM_1) * V12_X_MULTIPLIER;  	int y = getVarOrDirectByte(PARAM_2) * V12_Y_MULTIPLIER;  	obj = findObject(x, y); -	if (obj == 0 && (_game.platform == Common::kPlatformNES) && (_userState & 0x40)) { +	if (obj == 0 && (_game.platform == Common::kPlatformNES) && (_userState & USERSTATE_IFACE_INVENTORY)) {  		if (_mouseOverBoxV2 >= 0 && _mouseOverBoxV2 < 4)  			obj = findInventory(VAR(VAR_EGO), _mouseOverBoxV2 + _inventoryOffset + 1);  	} @@ -1310,7 +1316,7 @@ void ScummEngine_v2::o2_getActorX() {  	getResultPos();  	a = getVarOrDirectByte(PARAM_1); -	setResult(getObjX(a)); +	setResult(getObjX(actorToObj(a)));  }  void ScummEngine_v2::o2_getActorY() { @@ -1318,7 +1324,7 @@ void ScummEngine_v2::o2_getActorY() {  	getResultPos();  	a = getVarOrDirectByte(PARAM_1); -	setResult(getObjY(a)); +	setResult(getObjY(actorToObj(a)));  }  void ScummEngine_v2::o2_isGreater() { @@ -1480,7 +1486,9 @@ void ScummEngine_v2::o2_cutscene() {  	VAR(VAR_CURSORSTATE) = 200;  	// Hide inventory, freeze scripts, hide cursor -	setUserState(15); +	setUserState(USERSTATE_SET_IFACE |  +		USERSTATE_SET_CURSOR |  +		USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON);  	_sentenceNum = 0;  	stopScript(SENTENCE_SCRIPT); @@ -1499,7 +1507,7 @@ void ScummEngine_v2::o2_endCutscene() {  	VAR(VAR_CURSORSTATE) = vm.cutSceneData[1];  	// Reset user state to values before cutscene -	setUserState(vm.cutSceneData[0] | 7); +	setUserState(vm.cutSceneData[0] | USERSTATE_SET_IFACE | USERSTATE_SET_CURSOR | USERSTATE_SET_FREEZE);  	if ((_game.id == GID_MANIAC) && !(_game.platform == Common::kPlatformNES)) {  		camera._mode = (byte) vm.cutSceneData[3]; @@ -1519,7 +1527,7 @@ void ScummEngine_v2::o2_beginOverride() {  	// Skip the jump instruction following the override instruction  	fetchScriptByte(); -	fetchScriptWord(); +	ScummEngine::fetchScriptWord();  }  void ScummEngine_v2::o2_chainScript() { @@ -1565,24 +1573,24 @@ void ScummEngine_v2::o2_cursorCommand() {	// TODO: Define the magic numbers  }  void ScummEngine_v2::setUserState(byte state) { -	if (state & 4) {						// Userface +	if (state & USERSTATE_SET_IFACE) {			// Userface  		if (_game.platform == Common::kPlatformNES) -			_userState = (_userState & ~0xE0) | (state & 0xE0); +			_userState = (_userState & ~USERSTATE_IFACE_ALL) | (state & USERSTATE_IFACE_ALL);  		else -			_userState = state & (32 | 64 | 128); +			_userState = state & USERSTATE_IFACE_ALL;  	} -	if (state & 1) {						// Freeze -		if (state & 8) +	if (state & USERSTATE_SET_FREEZE) {		// Freeze +		if (state & USERSTATE_FREEZE_ON)  			freezeScripts(0);  		else  			unfreezeScripts();  	} -	if (state & 2) {						// Cursor Show/Hide +	if (state & USERSTATE_SET_CURSOR) {			// Cursor Show/Hide  		if (_game.platform == Common::kPlatformNES) -			_userState = (_userState & ~0x10) | (state & 0x10); -		if (state & 16) { +			_userState = (_userState & ~USERSTATE_CURSOR_ON) | (state & USERSTATE_CURSOR_ON); +		if (state & USERSTATE_CURSOR_ON) {  			_userPut = 1;  			_cursor.state = 1;  		} else { diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index 6426b75e1e..a5591b701f 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -1095,10 +1095,16 @@ void ScummEngine_v5::o5_getClosestObjActor() {  void ScummEngine_v5::o5_getDist() {  	int o1, o2;  	int r; +  	getResultPos(); +	  	o1 = getVarOrDirectWord(PARAM_1);  	o2 = getVarOrDirectWord(PARAM_2); -	r = getObjActToObjActDist(o1, o2); + +	if (_game.version == 0) // in v0 both parameters are always actor IDs, never objects +		r = getObjActToObjActDist(actorToObj(o1), actorToObj(o2)); +	else +		r = getObjActToObjActDist(o1, o2);  	// FIXME: MI2 race workaround, see bug #597022. We never quite figured out  	// what the real cause of this, or if it maybe occurs in the original, too... @@ -2464,8 +2470,40 @@ void ScummEngine_v5::o5_walkActorTo() {  	a->startWalkActor(x, y, -1);  } +void ScummEngine_v5::walkActorToActor(int actor, int toActor, int dist) { +	Actor *a = derefActor(actor, "walkActorToActor"); +	Actor *to = derefActor(toActor, "walkActorToActor(2)"); + +	if (_game.version <= 2) { +		dist *= V12_X_MULTIPLIER; +	} else if (dist == 0xFF) { +		dist = a->_scalex * a->_width / 0xFF; +		dist += (to->_scalex * to->_width / 0xFF) / 2; +	} +	int x = to->getPos().x; +	int y = to->getPos().y; +	if (x < a->getPos().x) +		x += dist; +	else +		x -= dist; + +	if (_game.version <= 2) { +		x /= V12_X_MULTIPLIER; +		y /= V12_Y_MULTIPLIER; +	} +	if (_game.version <= 3) { +		AdjustBoxResult abr = a->adjustXYToBeInBox(x, y); +		x = abr.x; +		y = abr.y; +	} +	a->startWalkActor(x, y, -1); + +	// WORKAROUND: See bug #2971126 for details on why this is here. +	if (_game.version == 0) +		o5_breakHere(); +} +  void ScummEngine_v5::o5_walkActorToActor() { -	int x, y;  	Actor *a, *a2;  	int nr = getVarOrDirectByte(PARAM_1);  	int nr2 = getVarOrDirectByte(PARAM_2); @@ -2499,33 +2537,7 @@ void ScummEngine_v5::o5_walkActorToActor() {  	if (!a2->isInCurrentRoom())  		return; -	if (_game.version <= 2) { -		dist *= V12_X_MULTIPLIER; -	} else if (dist == 0xFF) { -		dist = a->_scalex * a->_width / 0xFF; -		dist += (a2->_scalex * a2->_width / 0xFF) / 2; -	} -	x = a2->getPos().x; -	y = a2->getPos().y; -	if (x < a->getPos().x) -		x += dist; -	else -		x -= dist; - -	if (_game.version <= 2) { -		x /= V12_X_MULTIPLIER; -		y /= V12_Y_MULTIPLIER; -	} -	if (_game.version <= 3) { -		AdjustBoxResult abr = a->adjustXYToBeInBox(x, y); -		x = abr.x; -		y = abr.y; -	} -	a->startWalkActor(x, y, -1); - -	// WORKAROUND: See bug #2971126 for details on why this is here. -	if (_game.version == 0) -		o5_breakHere(); +	walkActorToActor(nr, nr2, dist);  }  void ScummEngine_v5::o5_walkActorToObject() { diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index d3cc218cd3..fc46f88df4 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -151,9 +151,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)  	_fileHandle = 0;  	// Init all vars -	_v0ObjectIndex = false; -	_v0ObjectInInventory = false; -	_v0ObjectFlag = 0;  	_imuse = NULL;  	_imuseDigital = NULL;  	_musicEngine = NULL; @@ -266,7 +263,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)  	_bytesPerPixel = 1;  	_doEffect = false;  	_snapScroll = false; -	_currentLights = 0;  	_shakeEnabled = false;  	_shakeFrame = 0;  	_screenStartStrip = 0; @@ -701,10 +697,6 @@ ScummEngine_v2::ScummEngine_v2(OSystem *syst, const DetectorResult &dr)  	_inventoryOffset = 0; -	_activeInventory = 0; -	_activeObject = 0; -	_activeVerb = 0; -  	VAR_SENTENCE_VERB = 0xFF;  	VAR_SENTENCE_OBJECT1 = 0xFF;  	VAR_SENTENCE_OBJECT2 = 0xFF; @@ -719,19 +711,18 @@ ScummEngine_v2::ScummEngine_v2(OSystem *syst, const DetectorResult &dr)  ScummEngine_v0::ScummEngine_v0(OSystem *syst, const DetectorResult &dr)  	: ScummEngine_v2(syst, dr) { -	_verbExecuting = false; -	_verbPickup = false;  	_currentMode = 0; +	_currentLights = 0; +	_activeVerb = kVerbNone; +	_activeObject = 0;  	_activeObject2 = 0; -	_activeObjectIndex = 0; -	_activeObject2Index = 0; -	_activeInvExecute = false; -	_activeObject2Inv = false; -	_activeObjectObtained = false; -	_activeObject2Obtained = false; - -	VAR_ACTIVE_ACTOR = 0xFF; + +	_cmdVerb = kVerbNone; +	_cmdObject = 0; +	_cmdObject2 = 0; + +	VAR_ACTIVE_OBJECT2 = 0xFF;  	VAR_IS_SOUND_RUNNING = 0xFF;  	VAR_ACTIVE_VERB = 0xFF;  } @@ -1034,7 +1025,7 @@ Common::Error ScummEngine::init() {  	// The	kGenUnchanged method is only used for 'container files', i.e. files  	// that contain the real game files bundled together in an archive format. -	// This is the case of the NES, C64 and Mac versions of certain games. +	// This is the case of the NES, v0 and Mac versions of certain games.  	// Note: All of these can also occur in 'extracted' form, in which case they  	// are treated like any other SCUMM game.  	if (_filenamePattern.genMethod == kGenUnchanged) { @@ -1388,8 +1379,8 @@ void ScummEngine::setupCostumeRenderer() {  		_costumeRenderer = new AkosRenderer(this);  		_costumeLoader = new AkosCostumeLoader(this);  	} else if (_game.version == 0) { -		_costumeRenderer = new C64CostumeRenderer(this); -		_costumeLoader = new C64CostumeLoader(this); +		_costumeRenderer = new V0CostumeRenderer(this); +		_costumeLoader = new V0CostumeLoader(this);  	} else if (_game.platform == Common::kPlatformNES) {  		_costumeRenderer = new NESCostumeRenderer(this);  		_costumeLoader = new NESCostumeLoader(this); @@ -1468,7 +1459,7 @@ void ScummEngine::resetScumm() {  	_sortedActors = new Actor * [_numActors];  	for (i = 0; i < _numActors; ++i) {  		if (_game.version == 0) -			_actors[i] = new ActorC64(this, i); +			_actors[i] = new Actor_v0(this, i);  		else if (_game.version <= 2)  			_actors[i] = new Actor_v2(this, i);  		else if (_game.version == 3) @@ -1973,6 +1964,14 @@ Common::Error ScummEngine::go() {  		if (delta < 1)	// Ensure we don't get into an endless loop  			delta = 1;  // by not decreasing sleepers. +		// WORKAROUND: walking speed in the original v0/v1 interpreter  +		// is sometimes slower (e.g. during scrolling) than in ScummVM. +		// This is important for the door-closing action in the dungeon, +		// otherwise (delta < 6) a single kid is able to escape.  +		if ((_game.version == 0 && isScriptRunning(132)) ||  +			(_game.version == 1 && isScriptRunning(137))) +			delta = 6; +  		// Wait...  		waitForTimer(delta * 1000 / 60 - diff); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index d9237b2b30..2f1e536f0a 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -303,6 +303,19 @@ struct SaveStateMetaInfos {  	uint32 playtime;  }; +enum UserStates { +	USERSTATE_SET_FREEZE      = 0x01,   // freeze scripts if USERSTATE_FREEZE_ON is set, unfreeze otherwise +	USERSTATE_SET_CURSOR      = 0x02,   // shows cursor if USERSTATE_CURSOR_ON is set, hides it otherwise +	USERSTATE_SET_IFACE       = 0x04,   // change user-interface (sentence-line, inventory, verb-area) +	USERSTATE_FREEZE_ON       = 0x08,   // only interpreted if USERSTATE_SET_FREEZE is set +	USERSTATE_CURSOR_ON       = 0x10,   // only interpreted if USERSTATE_SET_CURSOR is set +	USERSTATE_IFACE_SENTENCE  = 0x20,   // only interpreted if USERSTATE_SET_IFACE is set +	USERSTATE_IFACE_INVENTORY = 0x40,   // only interpreted if USERSTATE_SET_IFACE is set +	USERSTATE_IFACE_VERBS     = 0x80    // only interpreted if USERSTATE_SET_IFACE is set +}; + +#define USERSTATE_IFACE_ALL (USERSTATE_IFACE_SENTENCE | USERSTATE_IFACE_INVENTORY | USERSTATE_IFACE_VERBS) +  /**   * A list of resource types.   * WARNING: Do not change the order of these, as the savegame format relies @@ -502,10 +515,6 @@ protected:  	int32 *_scummVars;  	byte *_bitVars; -	bool _v0ObjectIndex;			// V0 Use object index, instead of object number -	bool _v0ObjectInInventory;		// V0 Use object number from inventory -	byte _v0ObjectFlag; -  	/* Global resource tables */  	int _numVariables, _numBitVariables, _numLocalObjects;  	int _numGlobalObjects, _numArray, _numVerbs, _numFlObject; @@ -646,7 +655,7 @@ protected:  	void updateScriptPtr();  	virtual void runInventoryScript(int i);  	void inventoryScriptIndy3Mac(); -	void checkAndRunSentenceScript(); +	virtual void checkAndRunSentenceScript();  	void runExitScript();  	void runEntryScript();  	void runAllScripts(); @@ -791,6 +800,9 @@ protected:  	void setOwnerOf(int obj, int owner);  	void clearOwnerOf(int obj);  	int getObjectRoom(int obj) const; +	virtual bool objIsActor(int obj); +	virtual int objToActor(int obj); +	virtual int actorToObj(int actor);  	int getObjX(int obj);  	int getObjY(int obj);  	void getObjectXYPos(int object, int &x, int &y)	{ int dir; getObjectXYPos(object, x, y, dir); } @@ -799,7 +811,6 @@ protected:  	int getObjNewDir(int obj);  	int getObjectIndex(int object) const;  	int getObjectImageCount(int object); -	int whereIsObjectInventory(int object);  	int whereIsObject(int object) const;  	int findObject(int x, int y);  	void findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint object, uint room); @@ -820,7 +831,7 @@ protected:  	virtual void clearDrawQueues();  	uint32 getOBCDOffs(int object) const; -	byte *getOBCDFromObject(int obj); +	byte *getOBCDFromObject(int obj, bool v0CheckInventory = true);  	const byte *getOBIMFromObjectData(const ObjectData &od);  	const byte *getObjectImage(const byte *ptr, int state);  	virtual int getObjectIdFromOBIM(const byte *obim); @@ -931,8 +942,7 @@ protected:  public:  	bool isLightOn() const; -	byte _currentLights; -	int getCurrentLights() const; +	virtual int getCurrentLights() const;  protected:  	void initScreens(int b, int h); diff --git a/engines/scumm/scumm_v0.h b/engines/scumm/scumm_v0.h index af481df0e0..144dd701d4 100644 --- a/engines/scumm/scumm_v0.h +++ b/engines/scumm/scumm_v0.h @@ -32,20 +32,35 @@ namespace Scumm {   */  class ScummEngine_v0 : public ScummEngine_v2 {  protected: +	enum CurrentMode { +		kModeCutscene = 0,   // cutscene active +		kModeKeypad = 1,     // kid selection / dial pad / save-load dialog +		kModeNoNewKid = 2,   // verb "new kid" disabled (e.g. when entering lab) +		kModeNormal = 3      // normal playing mode +	}; + +	enum WalkToObjectState { +		kWalkToObjectStateDone = 0, +		kWalkToObjectStateWalk = 1, +		kWalkToObjectStateTurn = 2 +	}; + +protected:  	byte _currentMode; -	bool _verbExecuting;			// is a verb executing -	bool _verbPickup;				// are we picking up an object during a verb execute +	byte _currentLights; -	int _activeActor;				// Actor Number -	int _activeObject2;				// 2nd Object Number +	int _activeVerb;		// selected verb +	int _activeObject;		// 1st selected object (see OBJECT_V0()) +	int _activeObject2;		// 2nd selected object or actor (see OBJECT_V0()) -	bool _activeInvExecute;			// is activeInventory first to be executed -	bool _activeObject2Inv;			// is activeobject2 in the inventory -	bool _activeObjectObtained;		// collected _activeobject? -	bool _activeObject2Obtained;	// collected _activeObject2? +	int _cmdVerb;			// script verb +	int _cmdObject;			// 1st script object (see OBJECT_V0()) +	int _cmdObject2;		// 2nd script object or actor (see OBJECT_V0()) +	int _sentenceNestedCount; -	int _activeObjectIndex; -	int _activeObject2Index; +	int _walkToObject; +	int _walkToObjectState; +	bool _redrawSentenceLine;  public:  	ScummEngine_v0(OSystem *syst, const DetectorResult &dr); @@ -64,26 +79,33 @@ protected:  	virtual void processInput(); -	virtual void runObject(int obj, int entry);  	virtual void saveOrLoad(Serializer *s); -	// V0 MM Verb commands -	int  verbPrep(int object); -	bool verbMove(int object, int objectIndex, bool invObject); -	bool verbMoveToActor(int actor); -	bool verbObtain(int object, int objIndex); -	bool verbExecutes(int object, bool inventory = false); -	bool verbExec(); - -	int findObjectIndex(int x, int y); +	virtual bool objIsActor(int obj); +	virtual int objToActor(int obj); +	virtual int actorToObj(int actor); +	// V0 MM Verb commands +	int getVerbPrepId(); +	int activeVerbPrep(); +	void walkToActorOrObject(int object); +	void verbExec(); + +	virtual void runSentenceScript(); +	virtual void checkAndRunSentenceScript(); +	bool checkPendingWalkAction(); +	bool checkSentenceComplete();  	virtual void checkExecVerbs();  	virtual void handleMouseOver(bool updateInventory); +	int verbPrepIdType(int verbid);  	void resetVerbs(); -	void setNewKidVerbs(); -	void drawSentenceWord(int object, bool usePrep, bool objInInventory); -	void drawSentence(); +	void clearSentenceLine(); +	void flushSentenceLine(); +	void drawSentenceObject(int object); +	void drawSentenceLine(); + +	void setMode(byte mode);  	void switchActor(int slot); @@ -92,12 +114,17 @@ protected:  	virtual int getActiveObject(); -	virtual void resetSentence(bool walking); +	virtual void resetSentence();  	virtual bool areBoxesNeighbors(int box1nr, int box2nr); -	/* Version C64 script opcodes */ +	bool ifEqualActiveObject2Common(bool checkType); + +	virtual int getCurrentLights() const; + +	/* Version 0 script opcodes */  	void o_stopCurrentScript(); +	void o_walkActorToObject();  	void o_loadSound();  	void o_getActorMoving();  	void o_animateActor(); @@ -112,30 +139,30 @@ protected:  	void o_lockScript();  	void o_loadScript();  	void o_lockRoom(); -	void o_cursorCommand(); +	void o_setMode();  	void o_lights();  	void o_unlockCostume();  	void o_unlockScript();  	void o_decrement();  	void o_nop(); +	void o_getObjectOwner();  	void o_getActorBitVar();  	void o_setActorBitVar();  	void o_getBitVar();  	void o_setBitVar();  	void o_doSentence(); -	void o_unknown2(); -	void o_ifActiveObject(); -	void o_getClosestObjActor(); -	void o_printEgo_c64(); -	void o_print_c64(); +	void o_ifEqualActiveObject2(); +	void o_ifNotEqualActiveObject2(); +	void o_getClosestActor(); +	void o_printEgo(); +	void o_print();  	void o_unlockRoom();  	void o_unlockSound();  	void o_cutscene();  	void o_endCutscene(); -	void o_beginOverride();  	void o_setOwnerOf(); -	byte VAR_ACTIVE_ACTOR; +	byte VAR_ACTIVE_OBJECT2;  	byte VAR_IS_SOUND_RUNNING;  	byte VAR_ACTIVE_VERB;  }; diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h index 47c5fa2626..b407fd3f8a 100644 --- a/engines/scumm/scumm_v2.h +++ b/engines/scumm/scumm_v2.h @@ -44,17 +44,13 @@ protected:  	Common::String _sentenceBuf;  	uint16 _inventoryOffset; -	int _activeInventory; -	int _activeObject; -	int _activeVerb; -  public:  	ScummEngine_v2(OSystem *syst, const DetectorResult &dr);  	virtual void resetScumm();  	void checkV2MouseOver(Common::Point pos); -	void checkV2Inventory(int x, int y); +	int checkV2Inventory(int x, int y);  	void redrawV2Inventory();  protected: @@ -89,6 +85,7 @@ protected:  	void ifNotStateCommon(byte type);  	void setStateCommon(byte type);  	void clearStateCommon(byte type); +	void stopScriptCommon(int script);  	virtual void resetSentence(bool walking);  	void setUserState(byte state); @@ -100,6 +97,10 @@ protected:  	virtual void setBuiltinCursor(int index); +	void drawPreposition(int index); + +	void walkActorToObject(int actor, int obj); +  	/* Version 2 script opcodes */  	void o2_actorFromPos();  	void o2_actorOps(); diff --git a/engines/scumm/scumm_v5.h b/engines/scumm/scumm_v5.h index b8a61c1677..0eef04b8de 100644 --- a/engines/scumm/scumm_v5.h +++ b/engines/scumm/scumm_v5.h @@ -87,6 +87,8 @@ protected:  	void drawFlashlight(); +	void walkActorToActor(int actor, int toActor, int dist); +  	/**  	 * Fetch the next script word, then if cond is *false*, perform a relative jump.  	 * So this corresponds to a "jne" jump instruction. diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index 26a6a2f3b1..6d132c601f 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -115,7 +115,7 @@ void ScummEngine_v0::setupScummVars() {  	VAR_CAMERA_POS_X = 2;  	VAR_HAVE_MSG = 3;  	VAR_ROOM = 4; -	VAR_ACTIVE_ACTOR = 5; +	VAR_ACTIVE_OBJECT2 = 5;  	VAR_OVERRIDE = 6;  	VAR_IS_SOUND_RUNNING = 8;  	VAR_ACTIVE_VERB = 9; @@ -546,7 +546,7 @@ void ScummEngine_v8::setupScummVars() {  #endif  void ScummEngine_v0::resetScummVars() { -	resetSentence(false); +	resetSentence();  	VAR(VAR_EGO) = 3; diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp index 67ed17c024..567ca31485 100644 --- a/engines/scumm/verbs.cpp +++ b/engines/scumm/verbs.cpp @@ -41,47 +41,58 @@ struct VerbSettings {  	int id;  	int x_pos;  	int y_pos; -	int prep;  	const char *name;  };  static const VerbSettings v0VerbTable_English[] = { -	{ 1,  8, 0,   0, "Open"}, -	{ 2,  8, 1,   0, "Close"}, -	{ 3,  0, 2,   4, "Give"}, -	{ 4, 32, 0,   0, "Turn on"}, -	{ 5, 32, 1,   0, "Turn off"}, -	{ 6, 32, 2,   2, "Fix"}, -	{ 7, 24, 0,   0, "New Kid"}, -	{ 8, 24, 1,   2, "Unlock"}, -	{ 9,  0, 0,   0, "Push"}, -	{10,  0, 1,   0, "Pull"}, -	{11, 24, 2, 255, "Use"}, -	{12,  8, 2,   0, "Read"}, -	{13, 15, 0,   0, "Walk to"}, -	{14, 15, 1,   0, "Pick up"}, -	{15, 15, 2,   0, "What is"} +	{kVerbOpen,     8, 0, "Open"}, +	{kVerbClose,    8, 1, "Close"}, +	{kVerbGive,     0, 2, "Give"}, +	{kVerbTurnOn,  32, 0, "Turn on"}, +	{kVerbTurnOff, 32, 1, "Turn off"}, +	{kVerbFix,     32, 2, "Fix"}, +	{kVerbNewKid,  24, 0, "New Kid"}, +	{kVerbUnlock,  24, 1, "Unlock"}, +	{kVerbPush,     0, 0, "Push"}, +	{kVerbPull,     0, 1, "Pull"}, +	{kVerbUse,     24, 2, "Use"}, +	{kVerbRead,     8, 2, "Read"}, +	{kVerbWalkTo,  15, 0, "Walk to"}, +	{kVerbPickUp,  15, 1, "Pick up"}, +	{kVerbWhatIs,  15, 2, "What is"}  }; -// FIXME: Replace * with the correct character  static const VerbSettings v0VerbTable_German[] = { -	{ 1,  7, 0,   0, "$ffne"}, -	{ 2, 13, 1,   0, "Schlie*e"}, -	{ 3,  0, 2,   4, "Gebe"}, -	{ 4, 37, 1,   0, "Ein"}, -	{ 5, 37, 0,   0, "Aus"}, -	{ 6, 23, 1,   2, "Repariere"}, -	{ 7, 34, 2,   0, "Person"}, -	{ 8, 23, 0,   2, "Schlie*e auf"}, -	{ 9,  0, 0,   0, "Dr<cke"}, -	{10,  0, 1,   0, "Ziehe"}, -	{11, 23, 2, 255, "Benutz"}, -	{12,  7, 2,   0, "Lese"}, -	{13, 13, 0,   0, "Gehe zu"}, -	{14,  7, 1,   0, "Nimm"}, -	{15, 13, 2,   0, "Was ist"} +	{kVerbOpen,     7, 0, "$ffne"}, +	{kVerbClose,   13, 1, "Schlie*e"}, +	{kVerbGive,     0, 2, "Gebe"}, +	{kVerbTurnOn,  37, 1, "Ein"}, +	{kVerbTurnOff, 37, 0, "Aus"}, +	{kVerbFix,     23, 1, "Repariere"}, +	{kVerbNewKid,  34, 2, "Person"}, +	{kVerbUnlock,  23, 0, "Schlie*e auf"}, +	{kVerbPush,     0, 0, "Dr<cke"}, +	{kVerbPull,     0, 1, "Ziehe"}, +	{kVerbUse,     23, 2, "Benutz"}, +	{kVerbRead,     7, 2, "Lese"}, +	{kVerbWalkTo,  13, 0, "Gehe zu"}, +	{kVerbPickUp,   7, 1, "Nimm"}, +	{kVerbWhatIs,  13, 2, "Was ist"}  }; +int ScummEngine_v0::verbPrepIdType(int verbid) { +	switch (verbid) { +	case kVerbUse: // depends on object1 +		return kVerbPrepObject; +	case kVerbGive:  +		return kVerbPrepTo; +	case kVerbUnlock: case kVerbFix: +		return kVerbPrepWith; +	default: +		return kVerbPrepNone; +	} +} +  void ScummEngine_v0::resetVerbs() {  	VirtScreen *virt = &_virtscr[kVerbVirtScreen];  	VerbSlot *vs; @@ -112,62 +123,22 @@ void ScummEngine_v0::resetVerbs() {  		vs->key = 0;  		vs->center = 0;  		vs->imgindex = 0; -		vs->prep = vtable[i - 1].prep; +		vs->prep = verbPrepIdType(vtable[i - 1].id);  		vs->curRect.left = vtable[i - 1].x_pos * 8;  		vs->curRect.top = vtable[i - 1].y_pos * 8 + virt->topline + 8;  		loadPtrToResource(rtVerb, i, (const byte*)vtable[i - 1].name);  	}  } -void ScummEngine_v0::setNewKidVerbs() { -	VirtScreen *virt = &_virtscr[kVerbVirtScreen]; -	VerbSlot *vs; -	int i; - -	for (i = 1; i < 16; i++) -		killVerb(i); - -	for (i = 1; i < 4; i++) { -		vs = &_verbs[i]; -		vs->verbid = i; -		vs->color = 5; -		vs->hicolor = 7; -		vs->dimcolor = 11; -		vs->type = kTextVerbType; -		vs->charset_nr = _string[0]._default.charset; -		vs->curmode = 1; -		vs->saveid = 0; -		vs->key = 0; -		vs->center = 0; -		vs->imgindex = 0; -		vs->prep = 0; -		vs->curRect.left = (i * 8) * 8; -		vs->curRect.top = virt->topline + 8; - -		Actor *a = derefActor(VAR(96 + i), "setNewKidVerbs"); -		loadPtrToResource(rtVerb, i, (const byte*)a->getActorName()); -	} -	setUserState(191); -} -  void ScummEngine_v0::switchActor(int slot) { -	resetSentence(false); - -	if (_currentRoom == 45) -		return; - -	// radiation suit? don't let the player switch -	if (VAR(VAR_EGO) == 8) -		return; +	resetSentence(); -	// verbs disabled? or just new kid button? -	if (_currentMode == 0 || _currentMode == 1 || _currentMode == 2) +	// actor switching only allowed during normal gamplay (not cutscene, ...) +	if (_currentMode != kModeNormal)  		return;  	VAR(VAR_EGO) = VAR(97 + slot); -	resetVerbs();  	actorFollowCamera(VAR(VAR_EGO)); -	setUserState(247);  }  void ScummEngine_v2::initV2MouseOver() { @@ -301,7 +272,7 @@ void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {  	int i, x, y, new_box = -1;  	// Don't do anything unless the inventory is active -	if (!(_userState & 64)) { +	if (!(_userState & USERSTATE_IFACE_INVENTORY)) {  		_mouseOverBoxV2 = -1;  		return;  	} @@ -354,14 +325,14 @@ void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {  	}  } -void ScummEngine_v2::checkV2Inventory(int x, int y) { +int ScummEngine_v2::checkV2Inventory(int x, int y) {  	int inventoryArea = (_game.platform == Common::kPlatformNES) ? 48: 32;  	int object = 0;  	y -= _virtscr[kVerbVirtScreen].topline;  	if ((y < inventoryArea) || !(_mouseAndKeyboardStat & MBS_LEFT_CLICK)) -		return; +		return 0;  	if (_mouseOverBoxesV2[kInventoryUpArrow].rect.contains(x, y)) {  		if (_inventoryOffset >= 2) { @@ -382,18 +353,9 @@ void ScummEngine_v2::checkV2Inventory(int x, int y) {  	}  	if (object >= 4) -		return; - -	object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset); - -	if (object > 0) { -		if (_game.version == 0) { -			_activeInventory = object; +		return 0; -		} else { -			runInputScript(kInventoryClickArea, object, 0); -		} -	} +	return findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);  }  void ScummEngine_v2::redrawV2Inventory() { @@ -406,7 +368,7 @@ void ScummEngine_v2::redrawV2Inventory() {  	_mouseOverBoxV2 = -1; -	if (!(_userState & 64))	// Don't draw inventory unless active +	if (!(_userState & USERSTATE_IFACE_INVENTORY))	// Don't draw inventory unless active  		return;  	// Clear on all invocations @@ -431,9 +393,7 @@ void ScummEngine_v2::redrawV2Inventory() {  		_string[1].right = _mouseOverBoxesV2[i].rect.right - 1;  		_string[1].color = _mouseOverBoxesV2[i].color; -		_v0ObjectInInventory = true;  		const byte *tmp = getObjOrActorName(obj); -		_v0ObjectInInventory = false;  		assert(tmp);  		// Prevent inventory entries from overflowing by truncating the text @@ -472,7 +432,7 @@ void ScummEngine_v2::redrawV2Inventory() {  }  void ScummEngine::redrawVerbs() { -	if (_game.version <= 2 && !(_userState & 128)) // Don't draw verbs unless active +	if (_game.version <= 2 && !(_userState & USERSTATE_IFACE_VERBS)) // Don't draw verbs unless active  		return;  	int i, verb = 0; @@ -516,7 +476,6 @@ void ScummEngine_v2::handleMouseOver(bool updateInventory) {  }  void ScummEngine_v0::handleMouseOver(bool updateInventory) { -	drawSentence();  	ScummEngine_v2::handleMouseOver(updateInventory);  } @@ -674,15 +633,8 @@ void ScummEngine_v2::checkExecVerbs() {  		if (object != -1) {  			object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset); - -			if (object > 0) { -				if (_game.version == 0) { -					_activeInventory = object; - -				} else { -					runInputScript(kInventoryClickArea, object, 0); -				} -			} +			if (object > 0) +				runInputScript(kInventoryClickArea, object, 0);  			return;  		} @@ -703,7 +655,9 @@ void ScummEngine_v2::checkExecVerbs() {  			runInputScript(kSentenceClickArea, 0, 0);  		} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + inventoryArea) {  			// Click into V2 inventory -			checkV2Inventory(_mouse.x, _mouse.y); +			int object = checkV2Inventory(_mouse.x, _mouse.y); +			if (object > 0) +				runInputScript(kInventoryClickArea, object, 0);  		} else {  			over = findVerbAtPos(_mouse.x, _mouse.y);  			if (over != 0) { @@ -717,550 +671,210 @@ void ScummEngine_v2::checkExecVerbs() {  	}  } -void ScummEngine_v0::runObject(int obj, int entry) { -	bool prev = _v0ObjectInInventory; - -	if (getVerbEntrypoint(obj, entry) == 0) { -		// If nothing was found, attempt to find the 'WHAT-IS' verb script -		// (which is not really the what-is script, as this verb never actually executes -		//  it merely seems to be some type of fallback) -		if (getVerbEntrypoint(obj, 0x0F) != 0) { -			entry = 0x0F; -		} -	} - -	_v0ObjectInInventory = prev; - -	if (getVerbEntrypoint(obj, entry) != 0) { -		_v0ObjectInInventory = prev; -		runObjectScript(obj, entry, false, false, NULL); -	} else if (entry != 13 && entry != 15) { -		if (_activeVerb != 3) { -			VAR(VAR_ACTIVE_VERB) = entry; -			runScript(3, 0, 0, 0); - -		// For some reasons, certain objects don't have a "give" script -		} else if (VAR(VAR_ACTIVE_ACTOR) > 0 && VAR(VAR_ACTIVE_ACTOR) < 8) { -			if (_activeInventory) -				setOwnerOf(_activeInventory, VAR(VAR_ACTIVE_ACTOR)); -		} -	} -} - -bool ScummEngine_v0::verbMoveToActor(int actor) { -	Actor *a = derefActor(VAR(VAR_EGO), "verbMoveToActor"); -	Actor *a2 = derefActor(actor, "verbMoveToActor"); -	int dist = getDist(a->getRealPos().x, a->getRealPos().y, a2->getRealPos().x, a2->getRealPos().y); - -	if (!a->_moving && dist > 4) { -		a->startWalkActor(a2->getRealPos().x, a2->getRealPos().y, -1); +int ScummEngine_v0::getVerbPrepId() { +	if (_verbs[_activeVerb].prep != 0xFF) { +		return _verbs[_activeVerb].prep;  	} else { -		if (dist <= 4) { -			a->stopActorMoving(); -			return false; -		} +		byte *ptr = getOBCDFromObject(_activeObject, true); +		assert(ptr); +		return (*(ptr + 11) >> 5);  	} - -	return true;  } -bool ScummEngine_v0::verbMove(int object, int objectIndex, bool invObject) { -	int x, y, dir; -	Actor *a = derefActor(VAR(VAR_EGO), "verbMove"); - -	if (_currentMode != 3 && _currentMode != 2) -		return false; - -	_v0ObjectIndex = true; -	getObjectXYPos(objectIndex, x, y, dir); -	_v0ObjectIndex = false; - -	// Detect distance from target object -	int dist =  getDist(a->getRealPos().x, a->getRealPos().y, x, y); - -	if (a->_moving) -		return true; - -	if (dist > 5) { -		a->startWalkActor(x, y, dir); -		VAR(6) = x; -		VAR(7) = y; -		return true; -	} else { -		// Finished walk, are we picking up the item? -		if (_verbPickup) { -			int oldActive = _activeObject, oldIndex = _activeObjectIndex; -			_activeObject = object; -			_activeObjectIndex = objectIndex; - -			_v0ObjectIndex = true; -			// Execute pickup -			runObject(objectIndex, 14); -			_v0ObjectIndex = false; - -			_activeObject = oldActive; -			_activeObjectIndex = oldIndex; - -			// Finished picking up -			_verbPickup = false; -		} -	} - -	return false; +int ScummEngine_v0::activeVerbPrep() { +	if (!_activeVerb || !_activeObject) +		return 0; +	return getVerbPrepId();  } -bool ScummEngine_v0::verbObtain(int obj, int objIndex) { -	bool didPickup = false; +void ScummEngine_v0::verbExec() { +	_sentenceNum = 0; +	_sentenceNestedCount = 0; -	int prep, where = whereIsObjectInventory(obj); - -	if (objIndex == 0) -		return false; - -	// Object in inventory ? -	if (where != WIO_INVENTORY) { -		_v0ObjectIndex = true; -		prep = verbPrep(objIndex); - -		if (prep == 1 || prep == 4) { -			if (_activeVerb != 13 && _activeVerb != 14) { -				_verbPickup = true; -				didPickup = true; -			} -		} else { -			_verbPickup = false; -		} - -		// Ignore verbs? -		Actor *a = derefActor(VAR(VAR_EGO), "verbObtain"); -		if (((ActorC64 *)a)->_miscflags & 0x40) { -			resetSentence(false); -			return false; +	if (_activeVerb == kVerbWhatIs) +		return; +		 +	if (!(_activeVerb == kVerbWalkTo && _activeObject == 0)) { +		doSentence(_activeVerb, _activeObject, _activeObject2); +		if (_activeVerb != kVerbWalkTo) { +			_activeVerb = kVerbWalkTo; +			_activeObject = 0; +			_activeObject2 = 0;  		} - -		//attempt move to object -		if (verbMove(obj, objIndex, false)) -			return true; - -		if (didPickup && (prep == 1 || prep == 4)) -			if (_activeVerb != 13 && _activeVerb != 14) { -				_v0ObjectInInventory = true; - -				if (whereIsObject(obj) == WIO_INVENTORY) -					_activeInventory = obj; -				else -					resetSentence(false); - -				_v0ObjectInInventory = false; -			} +		_walkToObjectState = kWalkToObjectStateDone; +		return;  	} -	return false; -} - -int ScummEngine_v0::verbPrep(int object) { -	if (!_v0ObjectInInventory) -		_v0ObjectIndex = true; -	else -		_v0ObjectIndex = false; +	Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "verbExec"); +	int x = _virtualMouse.x / V12_X_MULTIPLIER; +	int y = _virtualMouse.y / V12_Y_MULTIPLIER; +	//actorSetPosInBox(); -	byte *ptr = getOBCDFromObject(object); -	_v0ObjectIndex = false; -	assert(ptr); -	return (*(ptr + 11) >> 5); -} +	// 0xB31 +	VAR(6) = x; +	VAR(7) = y; -bool ScummEngine_v0::verbExecutes(int object, bool inventory) { -	_v0ObjectInInventory = inventory; -	int prep = verbPrep(object); - -	if (prep == 2 || prep == 0) { -		return true; -	} +	if (a->_miscflags & kActorMiscFlagFreeze) +		return; -	return false; +	a->stopActorMoving(); +	a->startWalkActor(VAR(6), VAR(7), -1);  } -bool ScummEngine_v0::verbExec() { -	int prep = 0; -	int entry = (_currentMode != 0 && _currentMode != 1) ? _activeVerb : 15; - -	if ((!_activeInvExecute && _activeObject && getObjectIndex(_activeObject) == -1)) { -		resetSentence(false); -		return false; -	} - -	// Lets try walk to the object -	if (_activeObject && _activeObjectIndex && !_activeObjectObtained && _currentMode != 0) { -		prep = verbPrep(_activeObjectIndex); - -		if (verbObtain(_activeObject, _activeObjectIndex)) +bool ScummEngine_v0::checkSentenceComplete() { +	if (_activeVerb && _activeVerb != kVerbWalkTo && _activeVerb != kVerbWhatIs) { +		if (_activeObject && (!activeVerbPrep() || _activeObject2))  			return true; - -		_activeObjectObtained = true;  	} - -	// Attempt to obtain/reach object2 -	if (_activeObject2 && _activeObject2Index && !_activeObject2Obtained && _currentMode != 0) { -		prep = verbPrep(_activeObject2Index); - -		_v0ObjectInInventory = false; -		if (verbObtain(_activeObject2, _activeObject2Index)) -			return true; - -		if (prep != 1 && prep != 4) { -			_activeInventory = _activeObject; -			_activeObject = _activeObject2; -			_activeObjectIndex = _activeObject2Index; -			_activeObject2 = 0; -			_activeObject2Index = 0; -		} - -		_activeObject2Obtained = true; -	} - -	// Give-To -	if (_activeVerb == 3 && _activeInventory && _activeActor) { -		// FIXME: Actors need to turn and face each other -		if (verbMoveToActor(_activeActor)) { -			// Ignore verbs? -			Actor *a = derefActor(VAR(VAR_EGO), "verbExec"); -			if (((ActorC64 *)a)->_miscflags & 0x40) { -				resetSentence(false); -				return false; -			} - -			return true; -		} -		_v0ObjectInInventory = true; -		VAR(VAR_ACTIVE_ACTOR) = _activeActor; -		runObject(_activeInventory , 3); -		_v0ObjectInInventory = false; - -		resetSentence(false); -		return false; -	} - -	// Where we performing an action on an actor? -	if (_activeActor) { -		_v0ObjectIndex = true; -		runObject(_activeActor, entry); -		_v0ObjectIndex = false; -		_verbExecuting = false; - -		resetSentence(false); -		return false; -	} - -	// If we've finished walking (now near target), execute the action -	if (_activeObject && _activeObjectIndex && verbPrep(_activeObjectIndex) == 2) { -		_v0ObjectIndex = true; -		runObject(_activeObjectIndex, entry); -		_v0ObjectIndex = false; -		_verbExecuting = false; - -		if ((_currentMode == 3 || _currentMode == 2) && _activeVerb == 13) -			return false; - -		resetSentence(false); -		return false; -	} - -	// We acted on an inventory item -	if (_activeInventory && verbExecutes(_activeInventory, true) && _activeVerb != 3) { -		_v0ObjectInInventory = true; -		_activeObject = _activeInventory; -		runObject(_activeInventory, _activeVerb); - -		_verbExecuting = false; - -		if (_currentMode == 3 && _activeVerb == 13) { -			resetSentence(true); -			return false; -		} - -		resetSentence(false); -		return false; -	} - -	// Item not in inventory is executed -	if (_activeObject) { -		_v0ObjectIndex = true; -		runObject(_activeObjectIndex, entry); -		_v0ObjectIndex = false; -	} else if (_activeInventory) { -		// Not sure this is the correct way to do this, -		// however its working for most situations - segra -		if (verbExecutes(_activeInventory, true) == false) { -			if (_activeObject2 && _activeObject2Inv && verbExecutes(_activeObject2, true)) { -				_v0ObjectInInventory = true; - -				_activeObject = _activeInventory; -				_activeInventory = _activeObject2; - -				runObject(_activeObject, _activeVerb); -			} else { -				_v0ObjectInInventory = true; - -				if (_activeObject2) { -					_activeObject = _activeObject2; - -					runObject(_activeObject, _activeVerb); -				} else -					runObject(_activeInventory, _activeVerb); -			} -		} else { -			_v0ObjectInInventory = true; -			runObject(_activeInventory, _activeVerb); -		} -	} - -	_verbExecuting = false; - -	if (_activeVerb == 13) { -		resetSentence(true); -		return false; -	} - -	resetSentence(false); -  	return false;  }  void ScummEngine_v0::checkExecVerbs() { -	ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "checkExecVerbs"); +	Actor_v0 *a = (Actor_v0 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");  	VirtScreen *zone = findVirtScreen(_mouse.y); -	// Is a verb currently executing -	if (_verbExecuting) { -		// Check if mouse click -		if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) { -			int over = findVerbAtPos(_mouse.x, _mouse.y); -			int act  = getActorFromPos(_virtualMouse.x, _virtualMouse.y); -			int obj  = findObject(_virtualMouse.x, _virtualMouse.y); - -			if (over && over != _activeVerb) { +	bool execute = false; + +	if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) { +		int over = findVerbAtPos(_mouse.x, _mouse.y); +		// click region: verbs +		if (over) { +			if (_activeVerb != over) { // new verb +				// keep first object if no preposition is used yet +				if (activeVerbPrep()) +					_activeObject = 0; +				_activeObject2 = 0;  				_activeVerb = over; -				_verbExecuting = false; -				return; -			} - -			if (!obj && !act && !over) { -				resetSentence(false); +				_redrawSentenceLine = true;  			} else { -				a->stopActorMoving(); +				// execute sentence if complete +				if (checkSentenceComplete()) +					execute = true;  			} -		} else { - -			if (_verbExecuting && !verbExec()) -				return;  		}  	} -	// What-Is selected, any object we hover over is selected, on mouse press we set to WalkTo -	if (_activeVerb == 15) { -		int obj = findObject(_virtualMouse.x, _virtualMouse.y); -		int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y); -		_activeObject = obj; -		_activeObjectIndex = objIdx; - -		if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK)) -			_activeVerb = 13;	// Walk-To - -		return; +	if (a->_miscflags & kActorMiscFlagHide) { +		if (_activeVerb != kVerbNewKid) { +			_activeVerb = kVerbNone; +		}  	} -	if (_userPut <= 0 || _mouseAndKeyboardStat == 0) -		return; - -	if (_mouseAndKeyboardStat < MBS_MAX_KEY) { -		/* Check keypresses */ -		// TODO -	} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) { -		if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) { -			// TODO -		} else if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) { -			int prevInventory = _activeInventory; -			int invOff = _inventoryOffset; - -			// Click into V2 inventory -			checkV2Inventory(_mouse.x, _mouse.y); - -			// Did the Inventory position changed (arrows pressed, do nothing) -			if (invOff != _inventoryOffset) -				return; - -			// No inventory selected? -			if (!_activeInventory) -				return; +	if (_currentMode != kModeCutscene) { +		if (_currentMode == kModeKeypad) { +			_activeVerb = kVerbPush; +		} -			// Did we just change the selected inventory item? -			if (prevInventory && prevInventory != _activeInventory && _activeInventory != _activeObject2) { -				_v0ObjectInInventory = true; -				int prep = verbPrep(_activeInventory); -				_v0ObjectInInventory = true; -				int prep2 = verbPrep(prevInventory); - -				// Should the new inventory object remain as the secondary selected object -				// Or should the new inventory object become primary? -				if (prep != prep2 || prep != 1) { -					if (prep == 1 || prep == 3) { -						int tmp = _activeInventory; -						_activeInventory = prevInventory; -						prevInventory = tmp; +		if (_mouseAndKeyboardStat > 0 && _mouseAndKeyboardStat < MBS_MAX_KEY) { +			// keys already checked by input handler +		} else if ((_mouseAndKeyboardStat & MBS_MOUSE_MASK) || _activeVerb == kVerbWhatIs) { +			// click region: sentence line +			if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) { +				if (_activeVerb == kVerbNewKid) { +					if (_currentMode == kModeNormal) { +						int kid; +						int lineX = _mouse.x >> V12_X_SHIFT; +						if (lineX < 11) +							kid = 0; +						else if (lineX < 25) +							kid = 1; +						else +							kid = 2; +						_activeVerb = kVerbWalkTo; +						_redrawSentenceLine = true; +						drawSentenceLine(); +						switchActor(kid);  					} -				} - -				// Setup object2 -				_activeObject = 0; -				_activeInvExecute = true; -				_activeObject2Inv = true; -				_activeObject2 = _activeInventory; -				_activeInventory = prevInventory; -				return; -			} - -			// is the new selected inventory the same as the last selected?, reset to previous if it is -			if (_activeInventory == _activeObject2) -				_activeInventory = prevInventory; - -			// Inventory Selected changed -			if (prevInventory != _activeInventory) -				if (!_activeObject2 || prevInventory != _activeObject2) -					return; - -			if (_activeVerb == 11 && !(((_activeObject || _activeInventory)) || !_activeObject2)) -				return; -		} else { -			int over = findVerbAtPos(_mouse.x, _mouse.y); -			int act  = getActorFromPos(_virtualMouse.x, _virtualMouse.y); -			int obj  = findObject(_virtualMouse.x, _virtualMouse.y); -			int objIdx = findObjectIndex(_virtualMouse.x, _virtualMouse.y); - -			// If we already have an object selected, and we just clicked an actor -			// Clear any object we may of also clicked on -			if ((_activeObject || _activeInventory) && act) { -				obj = 0; -				objIdx = 0; -			} - -			if (a->_miscflags & 0x80) { -				if (_activeVerb != 7 && over != 7) { -					_activeVerb = 0; -					over = 0; -				} -			} - -			// Handle New Kid verb options -			if (_activeVerb == 7 || over == 7) { -				// Disable New-Kid (in the secret lab) -				if (_currentMode == 2 || _currentMode == 0) -					return; - -				if (_activeVerb == 7 && over) { -					_activeVerb = 13; -					switchActor(_verbs[over].verbid - 1); +					_activeVerb = kVerbWalkTo; +					_redrawSentenceLine = true;  					return; +				} else { +					// execute sentence if complete +					if (checkSentenceComplete()) +						execute = true;  				} - -				setNewKidVerbs(); -				_activeVerb = 7; - -				return; -			} - -			// Clicked on nothing, walk here? -			if (!over && !act && _activeVerb == 13 && !obj && _currentMode != 0) { -				// Clear all selected -				resetSentence(false); - -				// 0xB31 -				VAR(6) = _virtualMouse.x / V12_X_MULTIPLIER; -				VAR(7) = _virtualMouse.y / V12_Y_MULTIPLIER; - -				if (zone->number == kMainVirtScreen) { -					// Ignore verbs? -					if (a->_miscflags & 0x40) { -						resetSentence(false); +			// click region: inventory or main screen +			} else if ((zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) || +			           (zone->number == kMainVirtScreen)) +			{ +				int obj = 0; + +				// click region: inventory +				if (zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) { +					// click into inventory +					int invOff = _inventoryOffset; +					obj = checkV2Inventory(_mouse.x, _mouse.y); +					if (invOff != _inventoryOffset) { +						// inventory position changed (arrows pressed, do nothing)  						return;  					} -					a->stopActorMoving(); -					a->startWalkActor(VAR(6), VAR(7), -1); -					_verbExecuting = true; -				} -				return; -			} - -			// No new verb, use previous -			if (over == 0) -				over = _activeVerb; - -			// No verb selected, use walk-to -			if (!_activeVerb) -				_activeVerb = over = 13;		// Walk-To - -			// New verb selected -			if (_activeVerb != over) { -				_activeVerb = over; -				if (_activeVerb == 13) { -					resetSentence(false); -				} -				return; -			} - -			// Only allowing targetting actors if its the GIVE/USE verb -			if (_activeVerb == 3 || _activeVerb == 11) { -				// Different actor selected? -				if (act) { -					if (_activeActor != act) { -						_activeActor = act; -						return; +					// the second object of a give-to command has to be an actor +					if (_activeVerb == kVerbGive && _activeObject) +						obj = 0; +				// click region: main screen +				} else if (zone->number == kMainVirtScreen) { +					// click into main screen +					if (_activeVerb == kVerbGive && _activeObject) { +						int actor = getActorFromPos(_virtualMouse.x, _virtualMouse.y); +						if (actor != 0) +							obj = OBJECT_V0(actor, kObjectV0TypeActor); +					} else { +						obj = findObject(_virtualMouse.x, _virtualMouse.y);  					}  				} -			} - -			if (obj && obj != _activeObject) { -				if (!_activeObject) -					if (_activeInventory) -						_activeInvExecute = true; -				// USE -				if (_activeVerb == 11 || _activeVerb == 8) { -					if (obj != _activeObject || obj != _activeObject2) { -						if (!_activeObject || _activeInventory) { +				if (!obj) { +					if (_activeVerb == kVerbWalkTo) { +						_activeObject = 0; +						_activeObject2 = 0; +					} +				} else { +					if (activeVerbPrep() == kVerbPrepNone) { +						if (obj == _activeObject) +							execute = true; +						else  							_activeObject = obj; -							_activeObjectIndex = objIdx; -							return; -						} else { -							if (_activeObject2 != obj) { -								_activeObject2 = obj; -								_activeObject2Index = objIdx; -								return; -							} +						// immediately execute action in keypad/selection mode +						if (_currentMode == kModeKeypad) +							execute = true; +					} else { +						if (obj == _activeObject2) +							execute = true; +						if (obj != _activeObject) { +							_activeObject2 = obj; +							if (_currentMode == kModeKeypad) +								execute = true;  						}  					} -				} else { -					a->stopActorMoving(); - -					_activeObject = obj; -					_activeObjectIndex = objIdx; - -					if (_activeVerb != 13) -						return; +				} -					//return; +				_redrawSentenceLine = true; +				if (_activeVerb == kVerbWalkTo && zone->number == kMainVirtScreen) { +					_walkToObjectState = kWalkToObjectStateDone; +					execute = true;  				}  			}  		} +	} -		_verbExecuting = true; +	if (_redrawSentenceLine) +		drawSentenceLine(); -	}	// mouse k/b action +	if (!execute || !_activeVerb) +		return; + +	if (_activeVerb == kVerbWalkTo) +		verbExec(); +	else if (_activeObject) { +		// execute if we have a 1st object and either have or do not need a 2nd +		if (activeVerbPrep() == kVerbPrepNone || _activeObject2) +			verbExec(); +	}  }  void ScummEngine::verbMouseOver(int verb) {  	// Don't do anything unless verbs are active -	if (_game.version <= 2 && !(_userState & 128)) +	if (_game.version <= 2 && !(_userState & USERSTATE_IFACE_VERBS))  		return;  	if (_game.id == GID_FT) diff --git a/engines/scumm/verbs.h b/engines/scumm/verbs.h index fb4dc969e2..0aa008b4de 100644 --- a/engines/scumm/verbs.h +++ b/engines/scumm/verbs.h @@ -57,6 +57,34 @@ struct VerbSlot {  	uint16 imgindex;  }; +enum VerbsV0 { +	kVerbNone    = 0, +	kVerbOpen    = 1, +	kVerbClose   = 2, +	kVerbGive    = 3, +	kVerbTurnOn  = 4, +	kVerbTurnOff = 5, +	kVerbFix     = 6, +	kVerbNewKid  = 7, +	kVerbUnlock  = 8, +	kVerbPush    = 9, +	kVerbPull    = 10, +	kVerbUse     = 11, +	kVerbRead    = 12, +	kVerbWalkTo  = 13, +	kVerbPickUp  = 14, +	kVerbWhatIs  = 15 +}; + +enum VerbPrepsV0 { +	kVerbPrepNone   = 0, +	kVerbPrepIn     = 1, +	kVerbPrepWith   = 2, +	kVerbPrepOn     = 3, +	kVerbPrepTo     = 4, +	kVerbPrepObject = 0xFF // prep depends on object (USE) +}; +  } // End of namespace Scumm  #endif | 
