diff options
33 files changed, 13923 insertions, 1 deletions
| diff --git a/base/plugins.cpp b/base/plugins.cpp index 1db9c0d499..93bd8348fc 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -172,6 +172,9 @@ public:  		#if PLUGIN_ENABLED_STATIC(TOON)  		LINK_PLUGIN(TOON)  		#endif +		#if PLUGIN_ENABLED_STATIC(TSAGE) +		LINK_PLUGIN(TSAGE) +		#endif  		#if PLUGIN_ENABLED_STATIC(TOUCHE)  		LINK_PLUGIN(TOUCHE)  		#endif @@ -111,6 +111,7 @@ add_engine teenagent "Teen Agent" yes  add_engine testbed "TestBed: the Testing framework" no  add_engine tinsel "Tinsel" yes  add_engine toon "Toonstruck" yes +add_engine tsage "Ringworld: Revenge Of The Patriarch" no  add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes  add_engine tucker "Bud Tucker in Double Trouble" yes diff --git a/dists/scummvm.rc b/dists/scummvm.rc index e123bebd6f..753b93a5fe 100644 --- a/dists/scummvm.rc +++ b/dists/scummvm.rc @@ -1,7 +1,7 @@  #include "winresrc.h"  #if defined (__MINGW32__) || defined(__CYGWIN32__) || defined(HAS_INCLUDE_SET) -IDI_ICON               ICON    DISCARDABLE     "icons/scummvm.ico" +IDI_ICON               ICON    DISCARDABLE     "../../icons/scummvm.ico"  #else  IDI_ICON               ICON    DISCARDABLE     "../../icons/scummvm.ico"  #endif diff --git a/engines/tsage/converse.cpp b/engines/tsage/converse.cpp new file mode 100644 index 0000000000..68f6fc2aaa --- /dev/null +++ b/engines/tsage/converse.cpp @@ -0,0 +1,1143 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/converse.cpp $ + * $Id: converse.cpp 230 2011-02-12 06:57:31Z dreammaster $ + * + */ + +#include "common/str-array.h" +  +#include "tsage/tsage.h" +#include "tsage/globals.h" +#include "tsage/staticres.h" + +namespace tSage { + +#define STRIP_WORD_DELAY 30 + + +SequenceManager::SequenceManager(): Action() { +	Common::set_to(&_objectList[0], &_objectList[6], (SceneObject *)NULL); +	_sequenceData.clear(); +	_field24 = 0; +	_sequenceOffset = 0; +	_resNum = 0; +	_field26 = 0; +	_objectIndex = 0; +	_keepActive = false; +	setup(); +} + +void SequenceManager::setup() { +	_sequenceOffset = 0; +	_objectIndex = 0; +	_sceneObject = _objectList[0]; +} + +void SequenceManager::synchronise(Serialiser &s) { +	s.syncAsSint32LE(_resNum); +	s.syncAsSint32LE(_sequenceOffset); +	s.syncAsByte(_keepActive); +	s.syncAsSint32LE(_field24); +	s.syncAsSint32LE(_field26); + +	s.syncAsSint32LE(_objectIndex); +	SYNC_POINTER(_sceneObject); +	for (int i = 0; i < 6; ++i) +		SYNC_POINTER(_objectList[i]); + +	int seqSize = _sequenceData.size(); +	s.syncAsUint32LE(seqSize); +	if (s.isLoading()) +		_sequenceData.resize(seqSize); +	if (seqSize > 0) +		s.syncBytes(&_sequenceData[0], seqSize); +} + +void SequenceManager::remove() { +	if ((!_sequenceData.empty()) && !_keepActive) { +		_sequenceData.clear(); +	} + +	if (_globals->_sceneObjects->contains(&_sceneText)) +		_sceneText.remove(); + +	Common::set_to(&_objectList[0], &_objectList[6], (SceneObject *)NULL); +	Action::remove(); +} + +void SequenceManager::signal() { +	if (_globals->_sceneObjects->contains(&_sceneText)) +		_sceneText.flag100(); + +	bool continueFlag = true; +	while (continueFlag) { +		if (_sequenceOffset >=_sequenceData.size()) { +			// Reached the end of the sequence +			if (!_keepActive) +				remove(); +			break; +		} + +		uint16 idx = static_cast<uint16>(getNextValue() - 32000); +		if (idx > 34) +			continue; + +		uint v1, v2, v3; +		switch (idx) { +		case 0: +			// Stop sequence +			continueFlag = false; +			break; +		case 1: +			_sceneObject->animate(ANIM_MODE_NONE); +			break; +		case 2: +			_sceneObject->animate(ANIM_MODE_2, NULL); +			break; +		case 3: +			_sceneObject->animate(ANIM_MODE_3); +			break; +		case 4: +			v1 = getNextValue(); +			v2 = getNextValue(); +			_sceneObject->animate(ANIM_MODE_8, v1, v2 ? this : NULL); +			break; +		case 5: +			v1 = getNextValue(); +			v2 = getNextValue(); +			_sceneObject->animate(ANIM_MODE_7, v1, v2 ? this : NULL); +			break; +		case 6: +			v2 = getNextValue(); +			_sceneObject->animate(ANIM_MODE_5, v2 ? this : NULL); +			break; +		case 7: +			v2 = getNextValue(); +			_sceneObject->animate(ANIM_MODE_6, v2 ? this : NULL); +			break; +		case 8: +			v1 = getNextValue(); +			v3 = getNextValue(); +			v2 = getNextValue(); +			_sceneObject->animate(ANIM_MODE_4, v1, v3, v2 ? this : NULL); +			break; +		case 9: +			v1 = getNextValue(); +			v3 = getNextValue(); +			v2 = getNextValue(); +			_globals->_sceneManager._scene->_sceneBounds.moveTo(v3, v2); +			_globals->_sceneManager._scene->loadScene(v1); +			break; +		case 10: { +			int resNum= getNextValue(); +			int lineNum = getNextValue(); +			int colour = getNextValue(); +			int xp = getNextValue(); +			int yp = getNextValue(); +			int width = getNextValue(); +			setMessage(resNum, lineNum, colour, Common::Point(xp, yp), width); +			break; +		} +		case 11: +			v1 = getNextValue(); +			v2 = getNextValue(); +			setAction(globalManager(), v2 ? this : NULL, v1, _objectList[0], _objectList[1], _objectList[2], _objectList[3], NULL); +			break; +		case 12: +			v1 = getNextValue(); +			setDelay(v1); +			break; +		case 13: { +			v1 = getNextValue(); +			v3 = getNextValue(); +			v2 = getNextValue(); +			NpcMover *mover = new NpcMover(); +			Common::Point destPos(v1, v3); +			_sceneObject->addMover(mover, &destPos, v2 ? this : NULL);			 +			break; +		} +		case 14: +			v1 = getNextValue(); +			_sceneObject->_numFrames = v1; +			break; +		case 15: +			v1 = getNextValue(); +			_sceneObject->_field7A = v1; +			break; +		case 16: +			v1 = getNextValue(); +			v2 = getNextValue(); +			_sceneObject->_moveDiff = Common::Point(v1, v2); +			break; +		case 17: +			_sceneObject->flag100(); +			break; +		case 18: +			_sceneObject->unflag100(); +			break; +		case 19: +			v1 = getNextValue(); +			_sceneObject->setVisage(v1); +			break; +		case 20: +			v1 = getNextValue(); +			_sceneObject->setStrip(v1); +			break; +		case 21: +			v1 = getNextValue(); +			_sceneObject->setFrame(v1); +			break; +		case 22: +			v1 = getNextValue(); +			_sceneObject->setPriority2(v1); +			break; +		case 23: +			v1 = getNextValue(); +			_sceneObject->changeZoom(v1); +			break; +		case 24: +			v1 = getNextValue(); +			v2 = getNextValue(); +			v3 = getNextValue(); +			_sceneObject->setPosition(Common::Point(v1, v2), v3); +			break; +		case 25: { +			int yStart = getNextValue(); +			int minPercent = getNextValue(); +			int yEnd = getNextValue(); +			int maxPercent = getNextValue(); +			_globals->_sceneManager._scene->setZoomPercents(yStart, minPercent, yEnd, maxPercent); +			break; +		} +		case 26: +			v1 = getNextValue(); +			v2 = getNextValue(); +			_SoundHandler.startSound(v1, v2 ? this : NULL, 127); +			break; +		case 27: { +			v1 = getNextValue(); +			v3 = getNextValue(); +			v2 = getNextValue(); +			PlayerMover *mover = new PlayerMover(); +			Common::Point destPos(v1, v3); +			_sceneObject->addMover(mover, &destPos, v2 ? this : NULL);			 +			break; +		} +		case 28: +			_objectIndex = getNextValue(); +			_sceneObject = _objectList[_objectIndex]; +			assert(_sceneObject); +			break; +		case 29: +			_sceneObject->animate(ANIM_MODE_NONE); +			break; +		case 30: +			v1 = getNextValue(); +			_globals->_scrollFollower = (v1 == 0xffff) ? NULL : _objectList[v1]; +			break; +		case 31: +			_sceneObject->setObjectWrapper(new SceneObjectWrapper()); +			break; +		case 32: +			_sceneObject->setObjectWrapper(NULL); +			break; +		case 33: +			v1 = getNextValue(); +			if (_keepActive) +				setDelay(1); +			else { +				_sceneText.remove(); +				_globals->_sceneManager._scene->_stripManager.start(v1, this); +			} +			break; +		case 34: { +			v1 = getNextValue(); +			v2 = getNextValue(); +			int objIndex1 = getNextValue(); +			int objIndex2 = getNextValue(); +			int objIndex3 = getNextValue(); +			int objIndex4 = getNextValue(); +			int objIndex5 = getNextValue(); +			 +			setAction(globalManager(), v2 ? this : NULL, v1, _objectList[objIndex1], _objectList[objIndex2], +				_objectList[objIndex3], _objectList[objIndex4], _objectList[objIndex5]); +			break; +		} +		default: +			error("SequenceManager::signal - Unknown action %d at offset %xh", idx, _sequenceOffset - 2); +			break; +		} +	} + +} + +void SequenceManager::process(Event &event) { +	if (((event.eventType == EVENT_BUTTON_DOWN) || (event.eventType == EVENT_KEYPRESS)) && +		!event.handled && _globals->_sceneObjects->contains(&_sceneText)) { +		// Remove the text item +		_sceneText.remove(); +	} else { +		Action::remove(); +	} +} + + +void SequenceManager::attached(EventHandler *newOwner, EventHandler *fmt, va_list va) { +	// Get the sequence number to use +	_resNum = va_arg(va, int); + +	byte *seqData = _vm->_dataManager->getResource(RES_SEQUENCE, _resNum, 0); +	uint seqSize = _vm->_memoryManager.getSize(seqData); + +	_sequenceData.resize(seqSize); +	Common::copy(seqData, seqData + seqSize, &_sequenceData[0]); + +	DEALLOCATE(seqData); + +	Common::set_to(&_objectList[0], &_objectList[6], (SceneObject *)NULL); +	for (int idx = 0; idx < 6; ++idx) { +		_objectList[idx] = va_arg(va, SceneObject *); +		if (!_objectList[idx]) +			break; +	} + +	setup(); +	Action::attached(newOwner, fmt, NULL); +} + +/** + * Returns the next Id in the sequence + */ +uint16 SequenceManager::getNextValue() { +	uint16 result = READ_LE_UINT16(&_sequenceData[0] + _sequenceOffset); +	_sequenceOffset += 2; +	return result; +} + +void SequenceManager::setMessage(int resNum, int lineNum, int colour, const Common::Point &pt, int width) { +	_sceneText._colour1 = colour; +	_sceneText._colour2 = 0; +	_sceneText._colour3 = 0; +	_sceneText._fontNumber = 2; +	_sceneText._width = width; + +	// Get the display message +	Common::String msg = _vm->_dataManager->getMessage(resNum, lineNum); + +	// Get the needed rect, and move it to the desired position +	Rect textRect; +	_globals->gfxManager().getStringBounds(msg.c_str(), textRect, width);	 +	Rect sceneBounds = _globals->_sceneManager._scene->_sceneBounds; +	sceneBounds.collapse(4, 2); +	textRect.moveTo(pt); +	textRect.contain(sceneBounds); + +	// Set the text message +	_sceneText.setup(msg); +	_sceneText.setPosition(Common::Point(textRect.left, textRect.top)); +	_sceneText.setPriority2(255); +	_sceneText.unflag100(); + +	// Set the delay based on the number of words +	int numWords = 0; +	const char *msgP = msg.c_str(); +	while (*msgP) { +		if (*msgP++ == ' ') +			++numWords; +	} + +	setDelay(numWords * 18 + 120); +} + +SequenceManager *SequenceManager::globalManager() { +	return &_globals->_sequenceManager; +} + +/*--------------------------------------------------------------------------*/ + +ConversationChoiceDialog::ConversationChoiceDialog() { +	_stdColour = 23; +	_highlightColour = _globals->_scenePalette._colours.background; +	_fontNumber = 1; +} + +int ConversationChoiceDialog::execute(const StringArray &choiceList) { +	_gfxManager._font.setFontNumber(_fontNumber); + +	_bounds = Rect(20, 0, 20, 0); +	_choiceList.clear(); + +	// Set up the list of choices +	int yp = 0; +	for (uint idx = 0; idx < choiceList.size(); ++idx) { +		Rect tempRect; +		_gfxManager._font.getStringBounds(choiceList[idx].c_str(), tempRect, 265); +		tempRect.moveTo(25, yp + 10); + +		_choiceList.push_back(ChoiceEntry(choiceList[idx], tempRect)); +		yp += tempRect.height() + 5; +		_bounds.extend(tempRect); +	} +	_selectedIndex = _choiceList.size(); + +	// Set the position for the dialog +	_bounds.bottom -= 10; +	yp = 180 - _bounds.height(); +	_bounds.translate(0, yp); +	_bounds.right = _bounds.left + 280; + +	// Draw the dialog +	draw(); +	_globals->_events.showCursor(); + +	// Event handling loop +	Event event; +	while (!_vm->getEventManager()->shouldQuit()) { +		while (!_globals->_events.getEvent(event, EVENT_KEYPRESS | EVENT_BUTTON_DOWN | EVENT_MOUSE_MOVE) &&  +				!_vm->getEventManager()->shouldQuit()) +			; +		if (_vm->getEventManager()->shouldQuit()) +			break; + +		if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode >= Common::KEYCODE_1) && +			(event.kbd.keycode <= (Common::KEYCODE_0 + (int)_choiceList.size()))) { +			// Selected an option by number +			_selectedIndex = event.kbd.keycode - Common::KEYCODE_1; +			break; +		} else if ((_selectedIndex != _choiceList.size()) && ((event.eventType == EVENT_BUTTON_DOWN) || +					(event.eventType == EVENT_BUTTON_UP))) { +			// Item selected +			break; +		} else { +			// Check if any item is highlighted +			event.mousePos.x -= _gfxManager._bounds.left; +			event.mousePos.y -= _gfxManager._bounds.top; + +			uint idx = 0; +			while ((idx < _choiceList.size()) && !_choiceList[idx]._bounds.contains(event.mousePos.x, event.mousePos.y)) +				++idx; + +			if (idx != _selectedIndex) { +				if (_selectedIndex != _choiceList.size()) { +					// De-highlight previously selected item +					_gfxManager._font._colours.foreground = _stdColour; +					_gfxManager._font.writeLines(_choiceList[_selectedIndex]._msg.c_str(),  +						_choiceList[_selectedIndex]._bounds, ALIGN_LEFT); +				} + +				_selectedIndex = idx; + +				if (_selectedIndex != _choiceList.size()) { +					// Highlight the new item +					_gfxManager._font._colours.foreground = _highlightColour; +					_gfxManager._font.writeLines(_choiceList[idx]._msg.c_str(), _choiceList[idx]._bounds, ALIGN_LEFT); +				} + +			} +		} +	} + +	// Remove the dialog +	remove(); + +	return _selectedIndex; +} + +void ConversationChoiceDialog::draw() { +	// Make a backup copy of the area the dialog will occupy +	Rect tempRect = _bounds; +	tempRect.collapse(-10, -10); +	_savedArea = Surface_getArea(_globals->_gfxManagerInstance.getSurface(), tempRect); + +	// Fill in the contents of the entire dialog +	_gfxManager._bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); +	drawFrame(); + +	_gfxManager._bounds = tempRect; +	_gfxManager._font._colours.foreground = _stdColour; +	_gfxManager.activate(); + +	// Loop through writing the conversation choices +	for (uint idx = 0; idx < _choiceList.size(); ++idx) { +		Common::String strNum = String::format("%d", idx + 1); +		 +		// Write the choice number +		_gfxManager._font.setPosition(13, _choiceList[idx]._bounds.top); +		_gfxManager._font.writeString(strNum.c_str()); + +		_gfxManager._font.writeLines(_choiceList[idx]._msg.c_str(), _choiceList[idx]._bounds, ALIGN_LEFT); +	} + +	_gfxManager.deactivate(); +} + +/*--------------------------------------------------------------------------*/ + +void Obj44::load(const byte *dataP) { +	_id = READ_LE_UINT16(dataP); +	for (int idx = 0; idx < OBJ44_LIST_SIZE; ++idx) +		_field2[idx] = READ_LE_UINT16(dataP + 2 + idx * 2); + +	const byte *listP = dataP + 0x10; +	for (int idx = 0; idx < OBJ44_LIST_SIZE; ++idx, listP += 10) { +		_list[idx]._id = READ_LE_UINT16(listP); +		_list[idx]._scriptOffset = READ_LE_UINT16(listP + 2); +	} + +	_speakerOffset = READ_LE_UINT16(dataP + 0x42); +} + +void Obj44::synchronise(Serialiser &s) { +	s.syncAsSint32LE(_id); +	for (int i = 0; i < OBJ44_LIST_SIZE; ++i) +		s.syncAsSint32LE(_field2[i]); +	for (int i = 0; i < OBJ44_LIST_SIZE; ++i) +		_list[OBJ44_LIST_SIZE].synchronise(s); +	s.syncAsUint32LE(_speakerOffset); +} + +/*--------------------------------------------------------------------------*/ + +StripManager::StripManager() { +	_callbackObject = NULL; +	_activeSpeaker = NULL; +	reset(); +} + +StripManager::~StripManager() { +} + +void StripManager::start(int stripNum, EventHandler *owner, StripCallback *callback) { +	reset(); + +	_stripNum = stripNum; +	_callbackObject = callback; +	_sceneNumber = _globals->_sceneManager._sceneNumber; +	_sceneBounds = _globals->_sceneManager._scene->_sceneBounds; +	_script.clear(); + +	assert(owner); +	owner->setAction(this, owner); +} + +void StripManager::reset() { +	_actionIndex = 0; +	_delayFrames = 0; +	_owner = NULL; +	_fmt = NULL; +	_field2E6 = false; +	_stripNum = -1; +	_obj44Index = 0; +	_field2E8 = 0; +	_field20 = 0; +	_activeSpeaker = NULL; +	_textShown = false; +	_callbackObject = NULL; + +	_obj44List.clear(); +	if (!_script.empty()) { +		_script.clear(); +	} +} + +void StripManager::load() { +	// Get the script +	byte *script = _vm->_dataManager->getResource(RES_STRIP, _stripNum, 2); +	uint scriptSize = _vm->_memoryManager.getSize(script); + +	_script.resize(scriptSize); +	Common::copy(script, script + scriptSize, &_script[0]); + +	DEALLOCATE(script); +	 +	// Get the object list +	byte *obj44List = _vm->_dataManager->getResource(RES_STRIP, _stripNum, 1); +	int dataSize = _vm->_memoryManager.getSize(obj44List); +	assert((dataSize % 0x44) == 0); + +	byte *dataP = obj44List; +	for (int idx = 0; idx < (dataSize / 0x44); ++idx, dataP += 0x44) { +		Obj44 obj; +		obj.load(dataP); +		_obj44List.push_back(obj); +	} +	 +	DEALLOCATE(obj44List); +} + +void StripManager::synchronise(Serialiser &s) { +	s.syncAsSint32LE(_stripNum); +	s.syncAsSint32LE(_obj44Index); +	s.syncAsSint32LE(_field20); +	s.syncAsSint32LE(_sceneNumber); +	_sceneBounds.synchronise(s); +	SYNC_POINTER(_activeSpeaker); +	s.syncAsByte(_textShown); +	s.syncAsByte(_field2E6); +	s.syncAsSint32LE(_field2E8); + +	// Synchronise the item list +	int arrSize = _obj44List.size(); +	s.syncAsUint16LE(arrSize); +	if (s.isLoading()) +		_obj44List.resize(arrSize); +	for (int i = 0; i < arrSize; ++i) +		_obj44List[i].synchronise(s); + +	// Synhcronise script data +	int scriptSize = _script.size(); +	s.syncAsUint16LE(scriptSize); +	if (s.isLoading()) +		_script.resize(scriptSize); +	if (scriptSize > 0) +		s.syncBytes(&_script[0], scriptSize); + +	// Add speaker list +	arrSize = _speakerList.size(); +	s.syncAsUint16LE(arrSize); +	if (s.isLoading()) +		_speakerList.resize(arrSize); +	for (int i = 0; i < arrSize; ++i) +		SYNC_POINTER(_speakerList[i]); + +	// TODO: Properly handle the callback function +	warning("TODO: StripManager::synchronise::fnCallback"); +} + +void StripManager::remove() { +	if (_textShown) { +		if (_activeSpeaker) +			_activeSpeaker->removeText(); +		_textShown = false; +	} + +	if (_activeSpeaker) +		_activeSpeaker->remove(); + +	if (_sceneNumber != _globals->_sceneManager._scene->_sceneNumber) { +		_globals->_sceneManager._scene->_sceneBounds = _sceneBounds; +		_globals->_sceneManager._scene->loadScene(_sceneNumber); +	} + +	Action::remove(); +} + +void StripManager::signal() { +	if (_textShown) { +		_activeSpeaker->removeText(); +		_textShown = false; +	} + +	if (_obj44Index < 0) { +		EventHandler *owner = _fmt; +		int stripNum = ABS(_obj44Index); +		remove(); +		 +		start(stripNum, owner); +		return; +	} else if (_obj44Index == 10000) { +		// Reached end of strip +		remove(); +		return; +	}  + +	// Run strip + +	if (_obj44List.size() == 0) +		// Load the data for the strip +		load(); + +	Obj44 &obj44 = _obj44List[_obj44Index]; +	_field2E8 = obj44._id; +	StringArray choiceList; + +	// Build up a list of script entries +	int idx; +	for (idx = 0; idx < OBJ44_LIST_SIZE; ++idx) { +		if (!obj44._list[idx]._id) +			break; + +		// Get the next one +		choiceList.push_back((const char *)&_script[0] + obj44._list[idx]._scriptOffset); +	} + +	int strIndex = 0; +	if (choiceList.size() > 1) +		// Get the user to select a conversation option +		strIndex = _choiceDialog.execute(choiceList); + +	if ((choiceList.size() != 1) && !_field2E6) +		_delayFrames = 1; +	else { +		Speaker *speakerP = getSpeaker((const char *)&_script[0] + obj44._speakerOffset); +		if (!speakerP) +			error("Speaker not found.  Screenplay: %s %d", (const char *)&_script[0] + obj44._speakerOffset, _stripNum); + +		if (speakerP != _activeSpeaker) { +			if (_activeSpeaker) +				_activeSpeaker->remove(); +			_activeSpeaker = speakerP; + +			if ((_activeSpeaker->_newSceneNumber == -1) && (_globals->_sceneManager._sceneNumber != _sceneNumber)) { +				_globals->_sceneManager._scene->_sceneBounds = _sceneBounds; +				_globals->_sceneManager._scene->loadScene(_sceneNumber); +			} + +			_activeSpeaker->proc12(this); +		} + +		if (_callbackObject) { +			for (idx = 0; idx < OBJ44_LIST_SIZE; ++idx) { +				if (!obj44._field2[idx]) +					break; +				 +				_callbackObject->stripCallback(obj44._field2[idx]); +			} +		} + +		_textShown = true; +		_activeSpeaker->setText(choiceList[strIndex]); +	} + +	_obj44Index = getNewIndex(obj44._list[strIndex]._id); +	if (_obj44Index == 10001) { +		MessageDialog::show("Strip Failure: Node not found", OK_BTN_STRING); +		_obj44Index = 0; +	} +} + +void StripManager::process(Event &event) { +	Action::process(event); +	if (event.handled) +		return; + +	if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) { +		if (_obj44Index != 10000) { +			int currIndex = _obj44Index; +			while (!_obj44List[_obj44Index + 1]._id) { +				_obj44Index = getNewIndex(_obj44List[_obj44Index]._id); +				if ((_obj44Index < 0) || (_obj44Index == 10000)) +					break; +				currIndex = _obj44Index; +			} + +			_field2E8 = _obj44List[currIndex]._id; +		} + +		// Signal the end of the strip +		_delayFrames = 0; +		event.handled = true; +		signal(); +	} else if (event.eventType & (EVENT_BUTTON_DOWN | EVENT_KEYPRESS)) { +		// Move to next sequence in the strip +		_delayFrames = 0; +		event.handled = true; +		signal(); +	} +} + +void StripManager::addSpeaker(Speaker *speaker) { +	assert(_speakerList.size() < 100); +	_speakerList.push_back(speaker); +} + +Speaker *StripManager::getSpeaker(const char *speakerName) { +	for (uint idx = 0; idx < _speakerList.size(); ++idx) { +		if (!strcmp(_speakerList[idx]->_speakerName.c_str(), speakerName)) +			return _speakerList[idx]; +	} + +	return NULL; +} + +int StripManager::getNewIndex(int id) { +	if (id == 10000) +		return id; +	 +	for (uint idx = 0; idx < _obj44List.size(); ++idx) { +		if (_obj44List[idx]._id == id) { +			return (id == 0) ? 10001 : idx; +		} +	} + +	return 10001; +} + +/*--------------------------------------------------------------------------*/ + +Speaker::Speaker(): EventHandler() { +	_newSceneNumber = -1; +	_hideObjects = true; +	_field18 = 0; +	_textWidth = 140; +	_textPos = Common::Point(10, 20); +	_fontNumber = 2; +	_textMode = ALIGN_LEFT; +	_colour1 = _colour2 = _colour3 = _globals->_scenePalette._colours.foreground; +	_action = NULL; +	_speakerName = "SPEAKER"; +} + +void Speaker::synchronise(Serialiser &s) { +	_fieldA.synchronise(s); +	SYNC_POINTER(_field18); +	s.syncString(_speakerName); +	s.syncAsSint32LE(_newSceneNumber); +	s.syncAsSint32LE(_oldSceneNumber); +	_sceneBounds.synchronise(s); +	s.syncAsSint32LE(_textWidth); +	s.syncAsSint16LE(_textPos.x); s.syncAsSint16LE(_textPos.y); +	s.syncAsSint32LE(_fontNumber); +	SYNC_ENUM(_textMode, TextAlign); +	s.syncAsSint16LE(_colour1); +	s.syncAsSint16LE(_colour2); +	s.syncAsSint16LE(_colour3); +	s.syncAsByte(_hideObjects); +} + +void Speaker::remove() { +	if (_hideObjects) +		SceneObjectList::deactivate(); +} + +void Speaker::proc12(Action *action) { +	_action = action; +	if (_newSceneNumber != -1) { +		_oldSceneNumber = _globals->_sceneManager._sceneNumber; +		_sceneBounds = _globals->_sceneManager._scene->_sceneBounds; +		_globals->_sceneManager._scene->loadScene(_newSceneNumber); +	} + +	if (_hideObjects) +		// Activate the object list for display +		_objectList.activate(); + +	// TODO: Implement word_4639A properly +	_globals->_sceneObjects->draw(); +} + +void Speaker::setText(const Common::String &msg) { +//	_objectList.draw(); +	_sceneText._colour1 = _colour1; +	_sceneText._colour2 = _colour2; +	_sceneText._colour3 = _colour3; +	_sceneText._width = _textWidth; +	_sceneText._fontNumber = _fontNumber; +	_sceneText._textMode = _textMode; +	_sceneText.setup(msg); +	_sceneText.setPosition(_textPos); +	_sceneText.setPriority2(256); + +	// Count the number of words (by spaces) in the string +	const char *msgP = msg.c_str(); +	int spaceCount = 0; +	while (*msgP) { +		if (*msgP++ == ' ') +			++spaceCount; +	} + +	int numFrames = spaceCount * STRIP_WORD_DELAY + 120; +	if (_action) +		_action->setDelay(numFrames); +} + +void Speaker::removeText() { +	_sceneText.remove(); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerGameText::SpeakerGameText(): Speaker() { +	_speakerName = "GAMETEXT"; +	_textPos = Common::Point(40, 40); +	_textMode = ALIGN_CENTRE; +	_colour1 = 7; +	_textWidth = 230; +	_hideObjects = false; +} + +/*--------------------------------------------------------------------------*/ + +ScreenSpeaker::ScreenSpeaker(): Speaker() { +	_npc = NULL; +	_textMode = ALIGN_CENTRE; +} + +void ScreenSpeaker::setText(const Common::String &msg) { +	GfxManager gfxMan; +	gfxMan.activate(); +	gfxMan._font.setFontNumber(_fontNumber); +	Rect textRect; +	 +	_globals->gfxManager().getStringBounds(msg.c_str(), textRect, _textWidth); +	if (_npc) { +		textRect.centre(_npc->_position.x, _npc->_bounds.top - (textRect.height() / 2 + 10)); +	} else { +		textRect.centre(_globals->_sceneManager._scene->_sceneBounds.left +  +			(_globals->_sceneManager._scene->_sceneBounds.width() / 2), +			_globals->_sceneManager._scene->_sceneBounds.top); +	} + +	Rect rect2 = _globals->_sceneManager._scene->_sceneBounds; +	rect2.collapse(10, 6); +	textRect.contain(rect2); + +	_textPos.x = textRect.left; +	_textPos.y = textRect.top; +	Speaker::setText(msg); + +	gfxMan.deactivate(); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerGText::SpeakerGText() { +	_speakerName = "GTEXT"; +	_textWidth = 160; +	_textPos = Common::Point(130, 10); +	_colour1 = 42; +	_hideObjects = false; +} + +void SpeakerGText::setText(const Common::String &msg) { +	// Set the animation properties +	_sceneObject.postInit(); +	_sceneObject.setVisage(9405); +	_sceneObject.setStrip2(3); +	_sceneObject.setPriority2(255); +	_sceneObject.changeZoom(100); +	_sceneObject._frame = 1; +	_sceneObject.setPosition(Common::Point(183, 71)); +	_sceneObject.animate(ANIM_MODE_7, 0, NULL); + +	// Set the text +	Rect textRect; +	_globals->gfxManager()._font.getStringBounds(msg.c_str(), textRect, _textWidth); +	textRect.centre(_sceneObject._position.x, _sceneObject._position.y); +	_textPos.x = textRect.left; +	setText(msg); +} + +void SpeakerGText::removeText() { +	_sceneObject.remove(); +	removeText(); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerOText::SpeakerOText(): SpeakerGText() { +	_speakerName = "OTEXT"; +	_textWidth = 240; +	_textPos = Common::Point(130, 10); +	_colour1 = 42; +	_hideObjects = false; +} + +/*--------------------------------------------------------------------------*/ + +SpeakerQText::SpeakerQText(): ScreenSpeaker() { +	_speakerName = "QTEXT"; +	_textPos = Common::Point(160, 40); +	_colour1 = 35; +	_textWidth = 240; +	_textMode = ALIGN_CENTRE; +	_hideObjects = false; +} + +/*--------------------------------------------------------------------------*/ + +SpeakerSText::SpeakerSText(): ScreenSpeaker() { +	_speakerName = "STEXT"; +	_colour1 = 13; +	_textWidth = 240; +	_textMode = ALIGN_CENTRE; +	_hideObjects = false; +} + +/*--------------------------------------------------------------------------*/ + +void SpeakerAction::signal() { +	switch (_actionIndex++) { +	case 0: +		setDelay(_globals->_randomSource.getRandomNumber(60) + 60); +		break; +	case 1: +		static_cast<SceneObject *>(_owner)->setFrame(1); +		static_cast<SceneObject *>(_owner)->animate(ANIM_MODE_5, this, NULL); +		break; +	case 2: +		setDelay(_globals->_randomSource.getRandomNumber(10)); +		_actionIndex = 0; +		break; +	default: +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +void AnimatedSpeaker::removeText() { +	Speaker::removeText(); +	_object1.remove(); +	_object2.remove(); + +	_objectList.draw(); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerQL::SpeakerQL(): AnimatedSpeaker() { +	_speakerName = "QL"; +	_newSceneNumber = 2610; +	_textPos = Common::Point(160, 30); +	_colour1 = 35; +	_textMode = ALIGN_CENTRE; +} + +void SpeakerQL::setText(const Common::String &msg) { +	_object1.postInit(&_objectList); +	_object1.setVisage(2612); +	_object1.setStrip2(2); +	_object1.setPriority2(255); +	_object1.changeZoom(100); +	_object1._frame = 1; +	_object1.setPosition(Common::Point(128, 146)); +	_object1.animate(ANIM_MODE_7, 0, NULL); +	 +	_object2.postInit(&_objectList); +	_object2.setVisage(2612); +	_object2.setStrip2(1); +	_object2.setPriority2(255); +	_object2.changeZoom(100); +	_object2._frame = 1; +	_object2.setPosition(Common::Point(122, 84)); +	_object2.setAction(&_speakerAction, NULL); + +	Speaker::setText(msg); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerSR::SpeakerSR() { +	_speakerName = "SR"; +	_newSceneNumber = 2811; +	_textPos = Common::Point(10, 30); +	_colour1 = 13; +	_textMode = ALIGN_CENTRE; +} + +void SpeakerSR::setText(const Common::String &msg) { +	_object1.postInit(&_objectList); +	_object1.setVisage(2813); +	_object1.setStrip2(2); +	_object1.setPriority2(255); +	_object1.changeZoom(100); +	_object1._frame = 1; +	_object1.setPosition(Common::Point(224, 198)); +	_object1.animate(ANIM_MODE_7, 0, NULL); +	 +	_object2.postInit(&_objectList); +	_object2.setVisage(2813); +	_object2.setStrip2(1); +	_object2.setPriority2(255); +	_object2.changeZoom(100); +	_object2._frame = 1; +	_object2.setPosition(Common::Point(203, 96)); +	_object2.setAction(&_speakerAction, NULL); + +	_object3.postInit(&_objectList); +	_object3.setVisage(2813); +	_object3.setStrip(3); +	_object3.setPosition(Common::Point(204, 91)); +	_object3.setPriority2(199); +	_object3._numFrames = 3; +	_object3.animate(ANIM_MODE_7, 0, NULL); + +	Speaker::setText(msg); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerSL::SpeakerSL() { +	_speakerName = "SL"; +	_newSceneNumber = 2810; +	_textPos = Common::Point(140, 30); +	_textWidth = 160; +	_colour1 = 13; +	_textMode = ALIGN_CENTRE; +} + +void SpeakerSL::setText(const Common::String &msg) { +	_object1.postInit(&_objectList); +	_object1.setVisage(2812); +	_object1.setStrip2(2); +	_object1.setPriority2(255); +	_object1.changeZoom(100); +	_object1._frame = 1; +	_object1.setPosition(Common::Point(95, 198)); +	_object1.animate(ANIM_MODE_7, 0, NULL); +	 +	_object2.postInit(&_objectList); +	_object2.setVisage(2812); +	_object2.setStrip2(1); +	_object2.setPriority2(255); +	_object2.changeZoom(100); +	_object2._frame = 1; +	_object2.setPosition(Common::Point(116, 96)); +	_object2.setAction(&_speakerAction, NULL); + +	Speaker::setText(msg); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerQR::SpeakerQR() { +	_speakerName = "QR"; +	_newSceneNumber = 2611; +	_textPos = Common::Point(10, 30); +	_colour1 = 13; +	_textMode = ALIGN_CENTRE; +} + +void SpeakerQR::setText(const Common::String &msg) { +	_object1.postInit(&_objectList); +	_object1.setVisage(2613); +	_object1.setStrip2(2); +	_object1.setPriority2(255); +	_object1.changeZoom(100); +	_object1._frame = 1; +	_object1.setPosition(Common::Point(191, 146)); +	_object1.animate(ANIM_MODE_7, 0, NULL); +	 +	_object2.postInit(&_objectList); +	_object2.setVisage(2613); +	_object2.setStrip2(1); +	_object2.setPriority2(255); +	_object2.changeZoom(100); +	_object2._frame = 1; +	_object2.setPosition(Common::Point(197, 84)); +	_object2.setAction(&_speakerAction, NULL); + +	Speaker::setText(msg); +} + +} // end of namespace tSage diff --git a/engines/tsage/converse.h b/engines/tsage/converse.h new file mode 100644 index 0000000000..172a3fb617 --- /dev/null +++ b/engines/tsage/converse.h @@ -0,0 +1,293 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/converse.h $ + * $Id: converse.h 230 2011-02-12 06:57:31Z dreammaster $ + * + */ + +#ifndef TSAGE_CONVERSE_H +#define TSAGE_CONVERSE_H + +#include "tsage/core.h" +#include "tsage/dialogs.h" + +namespace tSage { + +class StripCallback: public EventHandler { +public: +	virtual void stripCallback(int v) = 0; +}; + +class SequenceManager: public Action { +private: +	void setup(); +	uint16 getNextValue(); +	void setMessage(int resNum, int lineNum, int colour, const Common::Point &pt, int width); +	SequenceManager *globalManager(); +public: +	SceneText _sceneText; +	int _resNum; +	uint _sequenceOffset; +	bool _keepActive; +	int _field24; +	int _field26; +	Common::Array<byte> _sequenceData; +	int _objectIndex; +	SceneObject *_sceneObject; +	SceneObject *_objectList[6]; +	SoundHandler _SoundHandler; +public: +	SequenceManager(); + +	virtual Common::String getClassName() { return "SequenceManager"; } +	virtual void synchronise(Serialiser &s); +	virtual void remove(); +	virtual void signal(); +	virtual void process(Event &event); +	virtual void attached(EventHandler *newOwner, EventHandler *fmt, va_list va); +}; + + +class Speaker: public EventHandler { +public: +	Rect _fieldA; +	Action *_field18; +	Common::String _speakerName; +	int _newSceneNumber; +	int _oldSceneNumber; +	SceneObjectList _objectList; +	Rect _sceneBounds; +	SceneText _sceneText; +	int _textWidth; +	Common::Point _textPos; +	int _fontNumber; +	TextAlign _textMode; +	int _colour1, _colour2, _colour3; +	bool _hideObjects; +public: +	Speaker(); + +	virtual Common::String getClassName() { return "Speaker"; } +	virtual void synchronise(Serialiser &s); +	virtual void remove(); +	virtual void proc12(Action *action); +	virtual void setText(const Common::String &msg); +	virtual void removeText(); + +	void setTextPos(const Common::Point &pt) { _textPos = pt; } +}; + +class SpeakerGameText: public Speaker { +public: +	SpeakerGameText(); + +	virtual Common::String getClassName() { return "SpeakerGameText"; } +}; + +class ScreenSpeaker: public Speaker { +public: +	SceneItem *_npc; +public: +	ScreenSpeaker(); + +	virtual Common::String getClassName() { return "ScreenSpeaker"; } +	virtual void setText(const Common::String &msg); +}; + +class SpeakerGText: public Speaker { +public: +	SceneObject _sceneObject; +public: +	SpeakerGText(); + +	virtual Common::String getClassName() { return "SpeakerGText"; } +	virtual void setText(const Common::String &msg); +	virtual void removeText(); +};	 + +class SpeakerOText: public SpeakerGText { +public: +	SpeakerOText(); + +	virtual Common::String getClassName() { return "SpeakerOText"; } +};	 + +class SpeakerSText: public ScreenSpeaker { +public: +	SpeakerSText(); + +	virtual Common::String getClassName() { return "SpeakerSText"; } +}; + +class SpeakerQText: public ScreenSpeaker { +public: +	SpeakerQText(); + +	virtual Common::String getClassName() { return "SpeakerQText"; } +}; + +class SpeakerAction: public Action { +public: +	virtual void signal(); + +	virtual Common::String getClassName() { return "SpeakerAction"; } +}; + +class AnimatedSpeaker: public Speaker { +public: +	SceneObject _object1; +	SceneObject _object2; +	SpeakerAction _speakerAction; +public: +	virtual Common::String getClassName() { return "AnimatedSpeaker"; } +	virtual void removeText(); +}; + +class SpeakerQL: public AnimatedSpeaker { +public: +	SpeakerQL(); + +	virtual Common::String getClassName() { return "SpeakerQL"; } +	virtual void setText(const Common::String &msg); +}; + +class SpeakerSR: public AnimatedSpeaker { +public: +	SceneObject _object3; +public: +	SpeakerSR(); + +	virtual Common::String getClassName() { return "SpeakerSR"; } +	void setText(const Common::String &msg); +}; + +class SpeakerSL: public AnimatedSpeaker { +public: +	SpeakerSL(); + +	virtual void setText(const Common::String &msg); +}; + +class SpeakerQR: public AnimatedSpeaker { +public: +	SpeakerQR(); + +	void setText(const Common::String &msg); +}; + +class ChoiceEntry { +public: +	Common::String _msg; +	Rect _bounds; + +	ChoiceEntry() {} +	ChoiceEntry(const Common::String &msg, const Rect &bounds) {  +		_msg = msg; +		_bounds = bounds; +	} +}; + +class ConversationChoiceDialog: public ModalDialog { +public: +	int _stdColour; +	int _highlightColour; +	int _fontNumber; +	int _savedFgColour; +	int _savedFontNumber; +	Common::Array<ChoiceEntry> _choiceList; +	uint _selectedIndex; +public: +	ConversationChoiceDialog(); + +	void setColours(int stdColour, int highlightColour) { +		_stdColour = stdColour; +		_highlightColour = highlightColour; +	} +	void setFontNumber(int fontNum) { _fontNumber = fontNum; } +	int execute(const StringArray &choiceList); + +	virtual void draw(); +}; + +class Obj0A: public Serialisable { +public: +	int _id; +	uint _scriptOffset; + +	virtual void synchronise(Serialiser &s) {  +		s.syncAsSint32LE(_id); +		s.syncAsUint32LE(_scriptOffset); +	} +}; + +#define OBJ44_LIST_SIZE 5 + +class Obj44: public Serialisable { +public: +	int _id; +	int _field2[OBJ44_LIST_SIZE]; +	Obj0A _list[OBJ44_LIST_SIZE]; +	uint _speakerOffset; +public: +	void load(const byte *dataP); +	virtual void synchronise(Serialiser &s); +}; + +class StripManager: public Action { +private: +	void reset(); +	void load(); +	Speaker *getSpeaker(const char *speakerName); +	int getNewIndex(int newId); +public: +	int _stripNum; +	int _obj44Index; +	int _field20; +	int _sceneNumber; +	Rect _sceneBounds; +	ConversationChoiceDialog _choiceDialog; +	Common::Array<Speaker *> _speakerList; +	StripCallback *_callbackObject; +	Speaker *_activeSpeaker; +	bool _textShown; +	bool _field2E6; +	int _field2E8; +	Common::Array<Obj44> _obj44List; +	Common::Array<byte> _script; +public: +	StripManager(); +	virtual ~StripManager(); + +	virtual void synchronise(Serialiser &s); +	virtual void remove(); +	virtual void signal(); +	virtual void process(Event &event); + +	void start(int stripNum, EventHandler *owner, StripCallback *callback = NULL); +	void setCallback(StripCallback *callback) { _callbackObject = callback; } +	void setColours(int stdColour, int highlightColour) { _choiceDialog.setColours(stdColour, highlightColour); } +	void setFontNumber(int fontNum) { _choiceDialog.setFontNumber(fontNum); } +	void addSpeaker(Speaker *speaker); +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/core.cpp b/engines/tsage/core.cpp new file mode 100644 index 0000000000..af07568f02 --- /dev/null +++ b/engines/tsage/core.cpp @@ -0,0 +1,3455 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/core.cpp $ + * $Id: core.cpp 229 2011-02-12 06:50:14Z dreammaster $ + * + */ + +#include "common/system.h" +#include "common/config-manager.h" +#include "common/translation.h" +#include "engines/engine.h" +#include "gui/saveload.h" +#include "tsage/tsage.h" +#include "tsage/core.h" +#include "tsage/dialogs.h" +#include "tsage/events.h" +#include "tsage/scenes.h" +#include "tsage/staticres.h" +#include "tsage/globals.h" + +namespace tSage { + +// The engine uses ScumMVM screen buffering, so all logic is hardcoded to use pane buffer 0 +#define CURRENT_PANENUM 0 + +/*--------------------------------------------------------------------------*/ + +InvObject::InvObject(int sceneNumber, int rlbNum, int cursorNum, CursorType cursorId, const Common::String description): +		_sceneNumber(sceneNumber), _rlbNum(rlbNum), _cursorNum(cursorNum), _cursorId(cursorId), +		_description(description) { +	_displayResNum = 3; +	_iconResNum = 5; + +	// Decode the image for the inventory item to get it's display bounds +	uint size; +	byte *imgData = _vm->_dataManager->getSubResource(_displayResNum, _rlbNum, _cursorNum, &size); +	GfxSurface s = surfaceFromRes(imgData); +	_bounds = s.getBounds(); + +	DEALLOCATE(imgData); +} + +void InvObject::setCursor() { +	if (_iconResNum != -1) { +		GfxSurface s = surfaceFromRes(_iconResNum, _rlbNum, _cursorNum); +		 +		Graphics::Surface src = s.lockSurface(); +		_globals->_events.setCursor(src, s._transColour, s._centroid, _cursorId); +	} +} + +/*--------------------------------------------------------------------------*/ + +InvObjectList::InvObjectList(): +		_stunner(2280, 1, 2, OBJECT_STUNNER, "This is your stunner."), +		_scanner(1, 1, 3, OBJECT_SCANNER, "A combination scanner comm unit."), +		_stasisBox(5200, 1, 4, OBJECT_STASIS_BOX, "A stasis box."), +		_infoDisk(40, 1, 1, OBJECT_INFODISK, "The infodisk you took from the assassin."), +		_stasisNegator(0, 2, 2, OBJECT_STASIS_NEGATOR, "The stasis field negator."), +		_keyDevice(4250, 1, 6, OBJECT_KEY_DEVICE, "A magnetic key device."), +		_medkit(2280, 1, 7, OBJECT_MEDKIT,  "Your medkit."), +		_ladder(4100, 1, 8, OBJECT_LADDER, "The chief's ladder."), +		_rope(4150, 1, 9, OBJECT_ROPE, "The chief's rope."), +		_key(7700, 1, 11, OBJECT_KEY, "A key."), +		_translator(7700, 1, 13, OBJECT_TRANSLATOR,  "The dolphin translator box."), +		_ale(2150, 1, 10, OBJECT_ALE, "A bottle of ale."), +		_paper(7700, 1, 12, OBJECT_PAPER, "A slip of paper with the numbers 2,4, and 3 written on it."), +		_waldos(0, 1, 14, OBJECT_WALDOS, "A pair of waldos from the ruined probe."), +		_stasisBox2(8100, 1, 4, OBJECT_STASIS_BOX2, "A stasis box."), +		_ring(8100, 2, 5, OBJECT_RING, "This is a signet ring sent to you by Louis Wu."), +		_cloak(9850, 2, 6, OBJECT_CLOAK, "A fine silk cloak."), +		_tunic(9450, 2, 7, OBJECT_TUNIC, "The patriarch's soiled tunic."), +		_candle(9500, 2, 8, OBJECT_CANDLE, "A tallow candle."), +		_straw(9400, 2, 9, OBJECT_STRAW, "Clean, dry straw."), +		_scimitar(9850, 1, 18, OBJECT_SCIMITAR, "A scimitar from the Patriarch's closet."), +		_sword(9850, 1, 17, OBJECT_SWORD, "A short sword from the Patriarch's closet."), +		_helmet(9500, 2, 4, OBJECT_HELMET, "Some type of helmet."), +		_items(4300, 2, 10, OBJECT_ITEMS, "Two interesting items from the Tnuctipun vessel."), +		_concentrator(4300, 2, 11, OBJECT_CONCENTRATOR, "The Tnuctipun anti-matter concentrator contained in a stasis field."), +		_nullifier(5200, 2, 12, OBJECT_NULLIFIER, "A purported neural wave nullifier."), +		_peg(4045, 2, 16, OBJECT_PEG, "A peg with a symbol."), +		_vial(5100, 2, 17, OBJECT_VIAL, "A vial of the bat creatures anti-pheromone drug."), +		_jacket(9850, 3, 1, OBJECT_JACKET, "A natty padded jacket."), +		_tunic2(9850, 3, 2, OBJECT_TUNIC2, "A very hairy tunic."), +		_bone(5300, 3, 5, OBJECT_BONE, "A very sharp bone."), +		_jar(7700, 3, 4, OBJECT_JAR, "An jar filled with a green substance."), +		_emptyJar(7700, 3, 3, OBJECT_EMPTY_JAR, "An empty jar.") { + +	// Add the items to the list +	_itemList.push_back(&_stunner); +	_itemList.push_back(&_scanner); +	_itemList.push_back(&_stasisBox); +	_itemList.push_back(&_infoDisk); +	_itemList.push_back(&_stasisNegator); +	_itemList.push_back(&_keyDevice); +	_itemList.push_back(&_medkit); +	_itemList.push_back(&_ladder); +	_itemList.push_back(&_rope); +	_itemList.push_back(&_key); +	_itemList.push_back(&_translator); +	_itemList.push_back(&_ale); +	_itemList.push_back(&_paper); +	_itemList.push_back(&_waldos); +	_itemList.push_back(&_stasisBox2); +	_itemList.push_back(&_ring); +	_itemList.push_back(&_cloak); +	_itemList.push_back(&_tunic); +	_itemList.push_back(&_candle); +	_itemList.push_back(&_straw); +	_itemList.push_back(&_scimitar); +	_itemList.push_back(&_sword); +	_itemList.push_back(&_helmet); +	_itemList.push_back(&_items); +	_itemList.push_back(&_concentrator); +	_itemList.push_back(&_nullifier); +	_itemList.push_back(&_peg); +	_itemList.push_back(&_vial); +	_itemList.push_back(&_jacket); +	_itemList.push_back(&_tunic2); +	_itemList.push_back(&_bone); +	_itemList.push_back(&_jar); +	_itemList.push_back(&_emptyJar); + +	_selectedItem = NULL; +} + +void InvObjectList::synchronise(Serialiser &s) { +	SYNC_POINTER(_selectedItem); + +List<InvObject *> _itemList; +} + +/*--------------------------------------------------------------------------*/ + +void EventHandler::dispatch() {   +	if (_action) _action->dispatch(); +} + +void EventHandler::setAction(Action *action, EventHandler *fmt, ...) { +	if (_action) { +		_action->_fmt = NULL; +		_action->remove(); +	} + +	_action = action; +	if (action) { +		va_list va; +		va_start(va, fmt); +		_action->attached(this, fmt, va); +		va_end(va); +	} +} + +/*--------------------------------------------------------------------------*/ + +Action::Action() { +	_actionIndex = 0; +	_owner = NULL; +	_fmt = NULL; +} + +void Action::synchronise(Serialiser &s) { +	EventHandler::synchronise(s); +	if (s.isLoading()) +		remove(); + +	SYNC_POINTER(_owner); +	s.syncAsSint32LE(_actionIndex); +	s.syncAsSint32LE(_delayFrames); +	s.syncAsUint32LE(_startFrame); +	s.syncAsSint16LE(_field16); +	SYNC_POINTER(_fmt); +} + +void Action::remove() { +	if (_action) +		_action->remove(); + +	if (_owner) { +		_owner->_action = NULL; +		_owner = NULL; +	} else { +		_globals->_sceneManager.removeAction(this); +	} + +	_field16 = 0; +	if (_fmt) +		_fmt->signal(); +} + +void Action::process(Event &event) { +	if (_action) +		_action->process(event); +} + +void Action::dispatch() { +	if (_action) +		_action->dispatch(); + +	if (_delayFrames) { +		uint32 frameNumber = _globals->_events.getFrameNumber(); + +		if (frameNumber >= _startFrame) { +			_delayFrames -= frameNumber - _startFrame; +			_startFrame = frameNumber; +			if (_delayFrames <= 0) { +				_delayFrames = 0; +				signal(); +			} +		} +	} +} + +void Action::attached(EventHandler *newOwner, EventHandler *fmt, va_list va) { +	_actionIndex = 0; +	_delayFrames = 0; +	_startFrame = _globals->_events.getFrameNumber(); +	_owner = newOwner; +	_fmt = fmt; +	_field16 = 1; +	signal(); +} + +void Action::setDelay(int numFrames) { +	_delayFrames = numFrames; +	_startFrame = _globals->_events.getFrameNumber(); +} + +/*--------------------------------------------------------------------------*/ + +ObjectMover::~ObjectMover() { +	if (_sceneObject->_mover == this) +		_sceneObject->_mover = NULL; +} + +void ObjectMover::synchronise(Serialiser &s) { +	EventHandler::synchronise(s); +	 +	s.syncAsSint16LE(_destPosition.x); s.syncAsSint16LE(_destPosition.y); +	s.syncAsSint16LE(_moveDelta.x); s.syncAsSint16LE(_moveDelta.y); +	s.syncAsSint16LE(_moveSign.x); s.syncAsSint16LE(_moveSign.y); +	s.syncAsSint32LE(_minorDiff); +	s.syncAsSint32LE(_majorDiff); +	s.syncAsSint32LE(_field1A); +	SYNC_POINTER(_action); +	SYNC_POINTER(_sceneObject); +} + +void ObjectMover::remove() { +	if (_sceneObject->_mover == this) +		_sceneObject->_mover = NULL; + +	delete this; +} + +void ObjectMover::dispatch() { +	Common::Point currPos = _sceneObject->_position; +	int yDiff = _sceneObject->_yDiff; + +	if (dontMove()) +		return; + +	_sceneObject->_field6E = NULL; +	if (_moveDelta.x >= _moveDelta.y) { +		int xAmount = _moveSign.x * _sceneObject->_moveDiff.x * _sceneObject->_percent / 100; +		if (!xAmount) +			xAmount = _moveSign.x; +		currPos.x += xAmount; +		 +		int yAmount = ABS(_destPosition.y - currPos.y); +		int yChange = _majorDiff / ABS(xAmount); +		int ySign; + +		if (!yChange) +			ySign = _moveSign.y; +		else { +			int v = yAmount / yChange; +			_field1A += yAmount % yChange; +			if (_field1A >= yChange) { +				++v; +				_field1A -= yChange; +			} +			 +			ySign = _moveSign.y * v; +		} + +		currPos.y += ySign; +		_majorDiff -= ABS(xAmount); + +	} else { +		int yAmount = _moveSign.y * _sceneObject->_moveDiff.y * _sceneObject->_percent / 100; +		if (!yAmount) +			yAmount = _moveSign.y; +		currPos.y += yAmount; +		 +		int xAmount = ABS(_destPosition.x - currPos.x); +		int xChange = _majorDiff / ABS(yAmount); +		int xSign; + +		if (!xChange) +			xSign = _moveSign.x; +		else { +			int v = xAmount / xChange; +			_field1A += xAmount % xChange; +			if (_field1A >= xChange) { +				++v; +				_field1A -= xChange; +			} +			 +			xSign = _moveSign.x * v; +		} + +		currPos.x += xSign; +		_majorDiff -= ABS(yAmount); +	} + +//TODO:	_sceneObject->_field6E = _sceneObject->proc1(currPos); +	if (!_sceneObject->_field6E) { +		_sceneObject->setPosition(currPos, yDiff); +		_sceneObject->getHorizBounds(); + +		if (dontMove()) { +			_sceneObject->_position = _destPosition; +			endMove(); +		} +	} else { +		endMove(); +	} +} + +void ObjectMover::setup(const Common::Point &destPos) { +	_sceneObject->calcAngle(destPos); + +	if ((_sceneObject->_objectWrapper) && !(_sceneObject->_flags & OBJFLAG_8)) +		_sceneObject->_objectWrapper->dispatch(); + +	// Get the difference +	int diffX = destPos.x - _sceneObject->_position.x; +	int diffY = destPos.y - _sceneObject->_position.y; +	int xSign = (diffX < 0) ? -1 : (diffX > 0 ? 1 : 0); +	int ySign = (diffY < 0) ? -1 : (diffY > 0 ? 1 : 0); +	diffX = ABS(diffX); +	diffY = ABS(diffY); +	 +	if (diffX < diffY) { +		_minorDiff = diffX / 2; +		_majorDiff = diffY; +	} else { +		_minorDiff = diffY / 2; +		_majorDiff = diffX; +	} + +	// Set the destination position +	_destPosition = destPos; +	_moveDelta = Common::Point(diffX, diffY); +	_moveSign = Common::Point(xSign, ySign); +	_field1A = 0; + +	if (!diffX && !diffY) +		// Object is already at the correct destination +		endMove(); +} + +bool ObjectMover::dontMove() const { +	return (_majorDiff <= 0); +} + +void ObjectMover::endMove() { +	EventHandler *actionP = _action; +	remove(); + +	if (actionP) +		actionP->signal(); +} + +/*--------------------------------------------------------------------------*/ + +ObjectMover2::ObjectMover2(): ObjectMover() { +	_destObject = NULL; +} + +void ObjectMover2::synchronise(Serialiser &s) { +	ObjectMover::synchronise(s); + +	SYNC_POINTER(_destObject); +	s.syncAsSint32LE(_minArea); +	s.syncAsSint32LE(_maxArea); +} + +void ObjectMover2::dispatch() { +	int area = _sceneObject->getSpliceArea(_destObject); +	if (area > _maxArea) { +		// Setup again for the new destination +		setup(_destObject->_position); +	} else if (area >= _minArea) { +		// Keep dispatching +		ObjectMover::dispatch(); +	} else { +		// Within minimum, so end move +		endMove(); +	} +} + +void ObjectMover2::startMove(SceneObject *sceneObj, va_list va) { +	// Set up fields +	_sceneObject = sceneObj; + +	_minArea = va_arg(va, int); +	_maxArea = va_arg(va, int); +	_destObject = va_arg(va, SceneObject *); + +	setup(_destObject->_position); +} + +void ObjectMover2::endMove() { +	_sceneObject->_field6E = 64; +} + +/*--------------------------------------------------------------------------*/ + +void ObjectMover3::dispatch() { +	int area = _sceneObject->getSpliceArea(_destObject); +	if (area <= _minArea) { +		endMove(); +	} else { +		setup(_destObject->_position); +		ObjectMover::dispatch(); +	} +} + +void ObjectMover3::startMove(SceneObject *sceneObj, va_list va) { +	_sceneObject = va_arg(va, SceneObject *); +	_destObject = va_arg(va, SceneObject *); +	_minArea = va_arg(va, int); +	_action = va_arg(va, Action *); + +	setup(_destObject->_position); +} + +void ObjectMover3::endMove() { +	ObjectMover::endMove(); +} + +/*--------------------------------------------------------------------------*/ + +void NpcMover::startMove(SceneObject *sceneObj, va_list va) { +	_sceneObject = sceneObj; + +	Common::Point *destPos = va_arg(va, Common::Point *); +	_action = va_arg(va, Action *); + +	setup(*destPos); +} + +/*--------------------------------------------------------------------------*/ + +void PlayerMover::synchronise(Serialiser &s) { +	NpcMover::synchronise(s); + +	s.syncAsSint16LE(_finalDest.x); s.syncAsSint16LE(_finalDest.y); +	s.syncAsSint32LE(_routeIndex); + +	for (int i = 0; i < MAX_ROUTE_SIZE; ++i) { +		s.syncAsSint16LE(_routeList[i].x); s.syncAsSint16LE(_routeList[i].y); +	} +} + +void PlayerMover::startMove(SceneObject *sceneObj, va_list va) { +	_sceneObject = sceneObj; +	Common::Point *pt = va_arg(va, Common::Point *); +	_finalDest = *pt; +	_action = va_arg(va, Action *); +	 +	setDest(_finalDest); +} + +void PlayerMover::endMove() { +	while (++_routeIndex != 0) { +		if ((_routeList[_routeIndex].x == ROUTE_END_VAL) || +			(_routeList[_routeIndex].y == ROUTE_END_VAL) || +			(_sceneObject->_field6E)) { +			// Movement route is completely finished +			ObjectMover::endMove(); +			return; +		} + +		if ((_routeList[_routeIndex].x != _sceneObject->_position.x) || +			(_routeList[_routeIndex].y != _sceneObject->_position.y)) +			break; +	} + +	// Set up the new interim destination along the route +	_globals->_walkRegions._routeEnds.moveSrc = _globals->_walkRegions._routeEnds.moveDest; +	_globals->_walkRegions._routeEnds.moveDest = _routeList[_routeIndex]; +	setup(_routeList[_routeIndex]); +	dispatch(); +} + +void PlayerMover::setDest(const Common::Point &destPos) { +	_routeList[0] = _sceneObject->_position; + +	if (_globals->_walkRegions._resNum == -1) { +		// Scene has no walk regions defined, so player can walk anywhere directly +		_routeList[0] = destPos; +		_routeList[1] = Common::Point(ROUTE_END_VAL, ROUTE_END_VAL); +	} else { +		// Figure out a path to the destination (or as close as possible to it) +		pathfind(_routeList, _sceneObject->_position, destPos, _globals->_walkRegions._routeEnds); +	} + +	_routeIndex = 0; +	_globals->_walkRegions._routeEnds.moveSrc = _sceneObject->_position; +	_globals->_walkRegions._routeEnds.moveDest = _routeList[0]; +	setup(_routeList[0]); +} + +#define BREAK_LIST_SIZE 20 + +void PlayerMover::pathfind(Common::Point *routeList, Common::Point srcPos, Common::Point destPos, RouteEnds routeEnds) { +	List<int> regionIndexes; +	RouteEnds tempRouteEnds; +	int breakList[BREAK_LIST_SIZE]; +	Common::Point objPos; + +	// Get the region the source is in +	int srcRegion = _globals->_walkRegions.indexOf(srcPos); +	if (srcRegion == -1) { +		srcRegion = findClosestRegion(srcPos, regionIndexes); +	} + +	// Main loop for building up the path +	breakList[0] = 0; +	while (!breakList[0]) { +		// Check the destination region +		int destRegion = _globals->_walkRegions.indexOf(destPos, ®ionIndexes); + +		if ((srcRegion == -1) && (destRegion == -1)) { +			// Both source and destination are outside walkable areas +		} else if (srcRegion == -1) { +			// Source is outside walkable areas +			tempRouteEnds = routeEnds; +			objPos = _sceneObject->_position; +			 +			Common::Point newPos; +			findLinePoint(&tempRouteEnds, &objPos, 1, &newPos); +			int srcId = _globals->_walkRegions.indexOf(newPos); + +			if (srcId == -1) { +				tempRouteEnds.moveDest = tempRouteEnds.moveSrc; +				tempRouteEnds.moveSrc = routeEnds.moveDest; + +				findLinePoint(&tempRouteEnds, &objPos, 1, &newPos); +				srcRegion = _globals->_walkRegions.indexOf(newPos); + +				if (srcRegion == -1) +					srcRegion = checkMover(srcPos, destPos); +			} + +		} else if (destRegion == -1) { +			// Destination is outside walkable areas +			destRegion = findClosestRegion(destPos, regionIndexes); +			if (destRegion == -1) { +				// No further route found, so end it +				*routeList++ = srcPos; +				break; +			} else { +				_finalDest = destPos; +			} +		} + +		if (srcRegion == destRegion) { +			*routeList++ = (srcRegion == -1) ? srcPos : destPos; +			break; +		} + +		int var6; +		proc1(breakList, srcRegion, destRegion, var6); + +		if (!breakList[0]) { +			regionIndexes.push_back(destRegion); +			continue; +		} + +		_globals->_walkRegions._field18[0]._pt1 = srcPos; +		_globals->_walkRegions._field18[0]._pt2 = srcPos; +		_globals->_walkRegions._field18[1]._pt1 = destPos; +		_globals->_walkRegions._field18[1]._pt2 = destPos; + +		int tempList[20]; +		tempList[0] = 0; +		int endIndex = 0; +		int idx = 1; +	 +		do { +			int breakEntry = breakList[idx]; +			int breakEntry2 = breakList[idx + 1]; + +			int listIndex = 0;  +			while (_globals->_walkRegions._idxList[_globals->_walkRegions[breakEntry]._idxListIndex + listIndex] == +					breakEntry2) +				++listIndex; + +			tempList[idx] = _globals->_walkRegions._idxList2[_globals->_walkRegions[breakEntry]._idxList2Index  +				+ listIndex]; +			 +			++endIndex; +		} while (breakList[++idx] != destRegion); + +		tempList[idx] = 1; +		idx = 0; +		for (int listIndex = 1; listIndex <= endIndex; ++listIndex) { +			int var10 = tempList[listIndex]; +			int var12 = tempList[listIndex + 1]; + +			if (!sub_F8E5(_globals->_walkRegions._field18[0]._pt1, _globals->_walkRegions._field18[var12]._pt1, +					_globals->_walkRegions._field18[var10]._pt1, _globals->_walkRegions._field18[var10]._pt2) && +				!sub_F8E5(_globals->_walkRegions._field18[0]._pt1, _globals->_walkRegions._field18[var12]._pt2, +					_globals->_walkRegions._field18[var10]._pt1, _globals->_walkRegions._field18[var10]._pt2)) +				continue; + +			Common::Point tempPt; +			if (sub_F8E5(_globals->_walkRegions._field18[0]._pt1, _globals->_walkRegions._field18[0]._pt1, +					_globals->_walkRegions._field18[var10]._pt1, _globals->_walkRegions._field18[var10]._pt2, &tempPt)) { +				// Add point to the route list +				_globals->_walkRegions._field18[0]._pt1 = tempPt; +				*routeList++ = tempPt; +			} else { +				int v16 =  +					(findDistance(_globals->_walkRegions._field18[0]._pt1, _globals->_walkRegions._field18[var10]._pt1) << 1) + +					(findDistance(_globals->_walkRegions._field18[var10]._pt1, _globals->_walkRegions._field18[1]._pt1) << 1) + +					findDistance(_globals->_walkRegions._field18[var10]._pt1, _globals->_walkRegions._field18[var12]._pt1) + +					findDistance(_globals->_walkRegions._field18[var10]._pt1, _globals->_walkRegions._field18[var12]._pt2); +				 +				int v1A =  +					(findDistance(_globals->_walkRegions._field18[0]._pt1, _globals->_walkRegions._field18[var10]._pt2) << 1) + +					(findDistance(_globals->_walkRegions._field18[var10]._pt2, _globals->_walkRegions._field18[1]._pt2) << 1) + +					findDistance(_globals->_walkRegions._field18[var10]._pt2, _globals->_walkRegions._field18[var12]._pt1) + +					findDistance(_globals->_walkRegions._field18[var10]._pt2, _globals->_walkRegions._field18[var12]._pt2); + +				if (v16 < v1A) { +					checkMovement2(_globals->_walkRegions._field18[var10]._pt1,  +						_globals->_walkRegions._field18[var10]._pt2, 1, objPos); +				} else { +					checkMovement2(_globals->_walkRegions._field18[var10]._pt2,  +						_globals->_walkRegions._field18[var10]._pt1, 1, objPos); +				} + +				*routeList++ = objPos; +			} +		} + +		// Add in the route entry +		*routeList++ = _globals->_walkRegions._field18[idx]._pt1; +	} + +	// Mark the end of the path +	*routeList = Common::Point(ROUTE_END_VAL, ROUTE_END_VAL); +} + +int PlayerMover::regionIndexOf(const Common::Point &pt) { +	for (uint idx = 0; idx < _globals->_walkRegions._regionList.size(); ++idx) { +		if (_globals->_walkRegions._regionList[idx].contains(pt)) +			return idx + 1; +	} + +	return 0; +} + +int PlayerMover::findClosestRegion(Common::Point &pt, List<int> &indexList) { +	int newY = pt.y; +	int result = 0; + +	for (int idx = 1; idx < SCREEN_WIDTH; ++idx, newY += idx) { +		int newX = pt.x + idx; +		result = regionIndexOf(newX, pt.y); + +		if ((result == 0) || indexList.contains(result)) { +			newY = pt.y + idx; +			result = regionIndexOf(newX, newY); + +			if ((result == 0) || indexList.contains(result)) { +				newX -= idx; +				result = regionIndexOf(newX, newY); + +				if ((result == 0) || indexList.contains(result)) { +					newX -= idx; +					result = regionIndexOf(newX, newY); + +					if ((result == 0) || indexList.contains(result)) { +						newY -= idx; +						result = regionIndexOf(newX, newY); + +						if ((result == 0) || indexList.contains(result)) { +							newY -= idx; +							result = regionIndexOf(newX, newY); + +							if ((result == 0) || indexList.contains(result)) { +								newX += idx; +								result = regionIndexOf(newX, newY); + +								if ((result == 0) || indexList.contains(result)) { +									newX += idx; +									result = regionIndexOf(newX, newY); + +									if ((result == 0) || indexList.contains(result)) { +										continue; +									} +								} +							} +						} +					} +				} +			} +		} + +		// Found an index +		pt.x = newX; +		pt.y = newY; +		return result; +	} + +	return (result == 0) ? -1 : result; +} + +Common::Point *PlayerMover::findLinePoint(RouteEnds *routeEnds, Common::Point *objPos, int length, Common::Point *outPos) { +	int xp = objPos->x + (((routeEnds->moveDest.y - routeEnds->moveSrc.y) * 9) / 8); +	int yp = objPos->y - (((routeEnds->moveDest.x - routeEnds->moveSrc.x) * 8) / 9); + +	int xDiff = xp - objPos->x; +	int yDiff = yp - objPos->y; +	int xDirection = (xDiff == 0) ? 0 : ((xDiff < 0) ? 1 : -1); +	int yDirection = (yDiff == 0) ? 0 : ((yDiff < 0) ? 1 : -1); +	xDiff = ABS(xDiff); +	yDiff = ABS(yDiff); +	int majorChange = MAX(xDiff, yDiff) / 2; + +	int outX = objPos->x; +	int outY = objPos->y; + +	while (length-- > 0) { +		if (xDiff < yDiff) { +			outY += yDirection; +			majorChange += xDiff; +			if (majorChange > yDiff) { +				majorChange -= yDiff; +				outX += xDirection; +			} +		} else { +			outX += xDirection; +			majorChange += yDiff; +			if (majorChange > xDiff) { +				majorChange -= xDiff; +				outY += yDirection; +			} +		} +	} + +	outPos->x = outX; +	outPos->y = outY; +	return outPos; +} + +int PlayerMover::checkMover(Common::Point &srcPos, const Common::Point &destPos) { +	int regionIndex = 0; +	Common::Point objPos = _sceneObject->_position; +	uint32 regionBitList = _sceneObject->_regionBitList; +	_sceneObject->_regionBitList = 0; + +	_sceneObject->_position.x = srcPos.x; +	_sceneObject->_position.y = srcPos.y; +	_sceneObject->_mover = NULL; + +	NpcMover *mover = new NpcMover(); +	_sceneObject->addMover(mover, &destPos, NULL); +	 +	// Handle automatic movement of the player until a walkable region is reached, +	// or the end point of the movement is +	do { +		_sceneObject->_mover->dispatch(); + +		// Scan walk regions for point +		for (uint idx = 0; idx < _globals->_walkRegions._regionList.size(); ++idx) { +			if (_globals->_walkRegions[idx].contains(_sceneObject->_position)) { +				regionIndex = idx + 1; +				srcPos = _sceneObject->_position; +				break; +			} +		} +	} while ((regionIndex == 0) && (_sceneObject->_mover) && !_vm->shouldQuit()); + +	_sceneObject->_position = objPos; +	_sceneObject->_regionBitList = regionBitList; + +	if (_sceneObject->_mover) +		_sceneObject->_mover->remove(); + +	_sceneObject->_mover = this; +	return regionIndex; +} + +void PlayerMover::checkMovement2(const Common::Point &srcPos, const Common::Point &destPos, int numSteps, Common::Point &ptOut) { +	Common::Point objPos = _sceneObject->_position; +	_sceneObject->_position = srcPos; +	uint32 regionBitList = _sceneObject->_regionBitList; +	_sceneObject->_position = srcPos; +	_sceneObject->_mover = NULL; + +	NpcMover *mover = new NpcMover(); +	_sceneObject->addMover(mover, &destPos, NULL); + +	while ((numSteps > 0) && ((_sceneObject->_position.x != destPos.x) || (_sceneObject->_position.y != destPos.y))) { +		_sceneObject->_mover->dispatch(); +		--numSteps; +	} + +	ptOut = _sceneObject->_position; +	_sceneObject->_position = objPos; +	_sceneObject->_regionBitList = regionBitList; + +	if (_sceneObject->_mover) +		_sceneObject->_mover->remove(); +	 +	_sceneObject->_mover = this; +} + +int PlayerMover::proc1(int *routeList, int srcRegion, int destRegion, int &v) { +	int tempList[BREAK_LIST_SIZE]; +	v = 0; +	for (int idx = 0; idx <= *routeList; ++idx) +		tempList[idx] = routeList[idx]; + +	if (*routeList == BREAK_LIST_SIZE) +		// Sequence too long +		return 32000; + +	int regionIndex; +	for (regionIndex = 1; regionIndex < *tempList; ++regionIndex) { +		if (routeList[regionIndex] == srcRegion) +			return 32000; +	} + +	WalkRegion &srcWalkRegion = _globals->_walkRegions[srcRegion]; +	int distance; +	if (!routeList[0]) { +		// No route  +		distance = 0; +	} else { +		WalkRegion ®ion = _globals->_walkRegions[regionIndex]; +		distance = findDistance(region._pt, srcWalkRegion._pt); +	} + +	tempList[++*tempList] = srcRegion; +	int newIndex = *tempList; + +	if (srcRegion == destRegion) { +		v = 1; +		for (int idx = newIndex; idx <= *tempList; ++idx) { +			routeList[idx] = tempList[idx]; +			++*routeList; +		} +		return distance; +	} else { +		int foundIndex = 0; +		int idx = 0;  +		while (_globals->_walkRegions._idxList[srcWalkRegion._idxListIndex + idx]) { +			if (_globals->_walkRegions._idxList[srcWalkRegion._idxListIndex + idx] == destRegion) { +				foundIndex = idx; +				break; +			} + +			++idx; +		} + +		int resultOffset = 31990; +		while ((_globals->_walkRegions._idxList[srcWalkRegion._idxListIndex + foundIndex] != 0) && (v == 0)) { +			int newDistance = proc1(tempList, _globals->_walkRegions._idxList[srcWalkRegion._idxListIndex + foundIndex],  +				destRegion, v); +			 +			if ((newDistance <= resultOffset) || v) { +				routeList[0] = newIndex - 1; +				 +				for (int i = newIndex; i <= tempList[0]; ++i) { +					routeList[idx] = tempList[i]; +					++routeList[0]; +				} + +				resultOffset = newDistance; +			} + +			tempList[0] = newIndex; +		} + +		v = 0; +		return resultOffset + distance; +	} +} + +int PlayerMover::findDistance(const Common::Point &pt1, const Common::Point &pt2) { +	int diff = ABS(pt1.x - pt2.x); +	double xx = diff * diff; +	diff = ABS(pt1.y - pt2.y); +	double yy = diff * 8.0 / 7.0; +	yy *= yy; + +	return (int)sqrtf(xx + yy); +} + +bool PlayerMover::sub_F8E5(const Common::Point &pt1, const Common::Point &pt2, const Common::Point &pt3, +						  const Common::Point &pt4, Common::Point *ptOut) { +	double diff1 = pt2.x - pt1.x; +	double diff2 = pt2.y - pt1.y; +	double diff3 = pt4.x - pt3.x; +	double diff4 = pt4.y - pt3.y; +	double var10 = 0.0, var8 = 0.0; +	double var18 = 0.0, var20 = 0.0; + +	if (diff1 != 0.0) { +		var8 = diff2 / diff1; +		var18 = pt1.y - (pt1.x * var8); +	} +	if (diff3 != 0.0) { +		var10 = diff4 / diff3; +		var20 = pt3.y - (pt3.x * var10); +	} + +	if (var8 == var10) +		return false; +	 +	double var48, var50; +	if (diff1 == 0) { +		if (diff3 == 0) +			return false; + +		var48 = pt1.x; +		var50 = var10 * var48 + var20; +	} else { +		var48 = (diff3 == 0) ? pt3.x : (var20 - var18) / (var8 - var10); +		var50 = var8 * var48 + var18; +	} + +	bool var52 = false, var56 = false, var54 = false, var58 = false; +	Common::Point tempPt((int)(var48 + 0.5), (int)(var50 + 0.5)); + +	if ((tempPt.x >= pt3.x) && (tempPt.x <= pt4.x)) +		var56 = true; +	else if ((tempPt.x >= pt4.x) && (tempPt.x <= pt3.x)) +		var56 = true; +	if (var56) { +		if ((tempPt.y >= pt3.y) && (tempPt.y <= pt4.y)) +			var58 = true; +		else if ((tempPt.y >= pt4.y) && (tempPt.y <= pt3.y)) +			var58 = true; +	} + +	if ((tempPt.x >= pt1.x) && (tempPt.x <= pt2.x)) +		var52 = true; +	else if ((tempPt.x >= pt2.x) && (tempPt.x <= pt1.x)) +		var52 = true; +	if (var52) { +		if ((tempPt.y >= pt1.y) && (tempPt.y <= pt2.y)) +			var54 = true; +		else if ((tempPt.y >= pt2.y) && (tempPt.y <= pt1.y)) +			var54 = true; +	} + +	if (var52 && var54 && var56 && var58) { +		if (ptOut) +			*ptOut = tempPt; +		return true; +	} + +	return false; +} + +/*--------------------------------------------------------------------------*/ + +PaletteModifier::PaletteModifier() { +	_scenePalette = NULL; +	_action = NULL; +} + +/*--------------------------------------------------------------------------*/ + +PaletteRotation::PaletteRotation(): PaletteModifier() { +	_disabled = false; +	_delayFrames = 0; +	_delayCtr = 0; +	_frameNumber = _globals->_events.getFrameNumber(); +} + +void PaletteRotation::synchronise(Serialiser &s) { +	PaletteModifier::synchronise(s); + +	s.syncAsByte(_disabled); +	s.syncAsSint32LE(_delayFrames); +	s.syncAsSint32LE(_delayCtr); +	s.syncAsUint32LE(_frameNumber); +	s.syncAsSint32LE(_currIndex); +	s.syncAsSint32LE(_start); +	s.syncAsSint32LE(_end); +	s.syncAsSint32LE(_rotationMode); +	s.syncAsSint32LE(_duration); +	for (int i = 0; i < 256; ++i) +		s.syncAsUint32LE(_palette[i]); +} + +void PaletteRotation::signal() { +	if (_delayCtr) { +		uint32 frameNumber = _globals->_events.getFrameNumber(); + +		if (frameNumber >= _frameNumber) { +			_delayCtr -= frameNumber - _frameNumber; +			_frameNumber = frameNumber; + +			if (_delayCtr < 0) +				_delayCtr = 0; +		} +	} + +	if (_delayCtr) +		return; +	_delayCtr = _delayFrames; +	if (_disabled) +		return; + +	bool flag = true; +	switch (_rotationMode) { +	case 0: +		if (--_currIndex < _start) { +			flag = decDuration(); +			if (flag) +				_currIndex = _end - 1; +		} +		break; +	case 2: +		if (++_currIndex >= _end) { +			flag = decDuration(); +			if (flag) +				_currIndex = _start; +		} +		break; +	case 3: +		if (++_currIndex >= _end) { +			flag = decDuration(); +			if (flag) { +				_currIndex = _end - 2; +				_rotationMode = 3; +			} +		} +		break; +	case 4: +		if (--_currIndex < _start) { +			flag = decDuration(); +			if (flag) { +				_currIndex = _start + 1; +				_rotationMode = 2; +			} +		} +		break; +	} + +	if (flag) { +		int count2 = _currIndex - _start; +		int count = _end - _currIndex; +		g_system->getPaletteManager()->setPalette((const byte *)&_palette[_currIndex], _start, count); + +		if (count2) { +			g_system->getPaletteManager()->setPalette((const byte *)&_palette[_start], _start, count2); +		} +	} +} + +void PaletteRotation::remove() { +	Action *action = _action; +	g_system->getPaletteManager()->setPalette((const byte *)&_palette[0], _start, _end - _start); + +	if (_scenePalette->_listeners.contains(this)) +		_scenePalette->_listeners.remove(this); + +	delete this; +	if (action) +		action->signal(); +} + +void PaletteRotation::set(ScenePalette *palette, int start, int end, int rotationMode, int duration, Action *action) { +	_duration = duration; +	_disabled = false; +	_action = action; +	_scenePalette = palette; + +	Common::copy(&palette->_palette[0], &palette->_palette[256], &_palette[0]); + +	_start = start; +	_end = end + 1; +	_rotationMode = rotationMode; + +	switch (_rotationMode + 1) { +	case 0: +	case 4: +		_currIndex = _end; +		break; +	default: +		_currIndex = _start; +		break; +	}		 +} + +void PaletteRotation::setPalette(ScenePalette *palette, bool disabled) { +	_scenePalette = palette; +	_disabled = disabled; +	_delayFrames = 100; +} + +bool PaletteRotation::decDuration() { +	if (_duration) { +		if (--_duration == 0) { +			remove(); +			return false; +		} +	} +	return true; +} + +void PaletteRotation::setDelay(int amount) { +	_delayFrames = _delayCtr = amount; +} + +/*--------------------------------------------------------------------------*/ + +ScenePalette::ScenePalette() {  +	// Set a default gradiant range +	for (int idx = 0; idx < 256; ++idx) +		_palette[idx] = idx | (idx << 8) | (idx << 16); + +	_field412 = 0; +} + +ScenePalette::ScenePalette(int paletteNum) {  +	loadPalette(paletteNum);  +} + +bool ScenePalette::loadPalette(int paletteNum) { +	byte *palData = _vm->_dataManager->getResource(RES_PALETTE, paletteNum, 0); +	if (!palData) +		return false; + +	int palStart = READ_LE_UINT16(palData); +	int palSize = READ_LE_UINT16(palData + 2); +	assert(palSize <= 256); + +	uint32 *destP = &_palette[palStart]; +	byte *srcP = palData + 6; + + +	for (int i = 0; i < palSize; ++i, srcP += 3, ++destP) +		*destP = *srcP | (*(srcP + 1) << 8) | (*(srcP + 2) << 16); + +	DEALLOCATE(palData); +	return true; +} + +void ScenePalette::refresh() { +	// Set indexes for standard colours to closest colour in the palette +	_colours.background = indexOf(255, 255, 255);	// White background +	_colours.foreground = indexOf(0, 0, 0);			// Black foreground +	_redColour = indexOf(180, 0, 0);				// Red-ish +	_greenColour = indexOf(0, 180, 0);				// Green-ish +	_blueColour = indexOf(0, 0, 180);				// Blue-ish +	_aquaColour = indexOf(0, 180, 180);				// Aqua +	_purpleColour = indexOf(180, 0, 180);			// Purple +	_limeColour = indexOf(180, 180, 0);				// Lime + +	// Refresh the palette +	g_system->getPaletteManager()->setPalette((const byte *)&_palette[0], 0, 256); +} + +/** + * Loads a section of the palette into the game palette + */ +void ScenePalette::setPalette(int index, int count) { +	g_system->getPaletteManager()->setPalette((const byte *)&_palette[index], index, count); +} + +/** + * Returns the palette index with the closest matching colour to that specified + * @param r			R component + * @param g			G component + * @param b			B component + * @param threshold	Closeness threshold. + * @remarks	A threshold may be provided to specify how close the matching colour must be + */ +uint8 ScenePalette::indexOf(uint r, uint g, uint b, int threshold) { +	int palIndex = -1; + +	for (int i = 0; i < 256; ++i) { +		int ir = _palette[i] & 0xff; +		int ig = (_palette[i] >> 8) & 0xff; +		int ib = (_palette[i] >> 16) & 0xff; +		int rDiff = abs(ir - (int)r); +		int gDiff = abs(ig - (int)g); +		int bDiff = abs(ib - (int)b); +		 +		int idxThreshold = rDiff * rDiff + gDiff * gDiff + bDiff * bDiff; +		if (idxThreshold <= threshold) { +			threshold = idxThreshold; +			palIndex = i; +		} +	} + +	return palIndex; +} + +/** + * Loads the specified range of the palette with the current system palette + * @param start		Start index + * @param count		Number of palette entries + */ +void ScenePalette::getPalette(int start, int count) { +	g_system->getPaletteManager()->grabPalette((byte *)&_palette[start], start, count); +} + +void ScenePalette::signalListeners() { +	for (List<PaletteModifier *>::iterator i = _listeners.begin(); i != _listeners.end(); ++i) { +		(*i)->signal(); +	} +} + +void ScenePalette::clearListeners() { +	List<PaletteModifier *>::iterator i = _listeners.begin(); +	while (i != _listeners.end()) { +		PaletteModifier *obj = *i; +		++i; +		obj->remove(); +	} +} + +void ScenePalette::fade(const byte *adjustData, bool fullAdjust, int percent) { +	uint32 tempPalette[256]; + +	// Ensure the percent adjustment is within 0 - 100% +	percent = CLIP(percent, 0, 100); + +	for (int palIndex = 0; palIndex < 256; ++palIndex) { +		const byte *srcP = (const byte *)&_palette[palIndex]; +		byte *destP = (byte *)&tempPalette[palIndex]; + +		for (int rgbIndex = 0; rgbIndex < 3; ++rgbIndex, ++srcP, ++destP) { +			*destP = *srcP - ((*srcP - adjustData[rgbIndex]) * (100 - percent)) / 100; +		} + +		if (fullAdjust) +			adjustData += 3; +	} + +	// Set the altered pale4tte +	g_system->getPaletteManager()->setPalette((const byte *)&tempPalette[0], 0, 256); +	g_system->updateScreen(); +} + +PaletteRotation *ScenePalette::addRotation(int start, int end, int rotationMode, int duration, Action *action) { +	PaletteRotation *obj = new PaletteRotation(); + +	if ((rotationMode == 2) || (rotationMode == 3)) +		duration <<= 1; + +	obj->set(this, start, end, rotationMode, duration, action); +	return obj; +} + +void ScenePalette::changeBackground(const Rect &bounds, FadeMode fadeMode) { +	ScenePalette tempPalette; +	if (_globals->_sceneManager._hasPalette) { +		if ((fadeMode == FADEMODE_GRADUAL) || (fadeMode == FADEMODE_IMMEDIATE)) { +			// Fade out any active palette +			tempPalette.getPalette(); +			uint32 adjustData = 0; + +			for (int percent = 100; percent >= 0; percent -= 5) { +				if (fadeMode == FADEMODE_IMMEDIATE) +					percent = 0; +				tempPalette.fade((byte *)&adjustData, false, percent); +				g_system->delayMillis(10); +			} +		} else { +			_globals->_scenePalette.refresh(); +			_globals->_sceneManager._hasPalette = false; +		} +	} + +	_globals->_screenSurface.copyFrom(_globals->_sceneManager._scene->_backSurface,  +		bounds, Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), NULL); +} + +void ScenePalette::synchronise(Serialiser &s) { +	for (int i = 0; i < 256; ++i) +		s.syncAsUint32LE(_palette[i]); +	s.syncAsSint32LE(_colours.foreground); +	s.syncAsSint32LE(_colours.background); + +	s.syncAsSint32LE(_field412); +	s.syncAsByte(_redColour); +	s.syncAsByte(_greenColour); +	s.syncAsByte(_blueColour); +	s.syncAsByte(_aquaColour); +	s.syncAsByte(_purpleColour); +	s.syncAsByte(_limeColour);	 +} + +/*--------------------------------------------------------------------------*/ + +void SceneItem::synchronise(Serialiser &s) { +	EventHandler::synchronise(s); + +	_bounds.synchronise(s); +	s.syncString(_msg); +	s.syncAsSint32LE(_fieldE); +	s.syncAsSint32LE(_field10); +	s.syncAsSint16LE(_position.x); s.syncAsSint32LE(_position.y); +	s.syncAsSint16LE(_yDiff); +	s.syncAsSint32LE(_sceneRegionId); +} + +void SceneItem::remove() { +	_globals->_sceneItems.remove(this); +} + +void SceneItem::doAction(int action) { +	const char *msg = NULL; + +	switch ((int)action) { +	case CURSOR_LOOK: +		msg = LOOK_SCENE_HOTSPOT; +		break; +	case CURSOR_USE: +		msg = USE_SCENE_HOTSPOT; +		break; +	case CURSOR_TALK: +		msg = TALK_SCENE_HOTSPOT; +		break; +	case 0x1000: +		msg = SPECIAL_SCENE_HOTSPOT; +		break; +	default: +		msg = DEFAULT_SCENE_HOTSPOT; +		break; +	} +	 +	GUIErrorMessage(msg); +} + +bool SceneItem::contains(const Common::Point &pt) { +	const Rect &sceneBounds = _globals->_sceneManager._scene->_sceneBounds; + +	if (_sceneRegionId == 0) +		return _bounds.contains(pt.x + sceneBounds.left, pt.y + sceneBounds.top); +	else  +		return _globals->_sceneRegions.indexOf(Common::Point(pt.x + sceneBounds.left,  +			pt.y + sceneBounds.top)) == _sceneRegionId; +} + +void SceneItem::display(int resNum, int lineNum, ...) { +	Common::String msg = !resNum ? Common::String() : _vm->_dataManager->getMessage(resNum, lineNum); + +	if (_globals->_sceneObjects->contains(&_globals->_sceneText)) { +		_globals->_sceneObjects->remove(&_globals->_sceneText); +		_globals->_sceneObjects->draw(); +	} + +	GfxFontBackup font; +	Common::Point pos(160, 100); +	Rect textRect; +	int maxWidth = 120; +	bool keepOnscreen = false; +	bool centreText = true; +	 +	if (resNum) { +		va_list va; +		va_start(va, lineNum); + +		int mode; +		do { +			// Get next instruction +			mode = va_arg(va, int); + +			switch (mode) { +			case SET_WIDTH: +				// Set width +				maxWidth = va_arg(va, int); +				_globals->_sceneText._width = maxWidth; +				break; +			case SET_X: +				// Set the X Position +				pos.x = va_arg(va, int); +				break; +			case SET_Y: +				// Set the Y Position +				pos.y = va_arg(va, int); +				break; +			case SET_FONT: +				// Set the font number +				_globals->gfxManager()._font.setFontNumber(va_arg(va, int)); +				break; +			case SET_BG_COLOUR: { +				// Set the background colour +				int bgColour = va_arg(va, int); +				_globals->gfxManager()._font._colours.background = bgColour; +				break; +			} +			case SET_FG_COLOUR: +				// Set the foreground colour +				_globals->gfxManager()._font._colours.foreground = va_arg(va, int); +				break; +			case SET_KEEP_ONSCREEN: +				// Suppresses immediate display +				keepOnscreen = va_arg(va, int) != 0; +				break; +			case SET_EXT_BGCOLOUR: { +				// Set secondary bg colour +				int v = va_arg(va, int); +				_globals->_sceneText._colour2 = v; +				_globals->gfxManager()._font._colours2.background = v; +				break; +			} +			case SET_EXT_FGCOLOUR: { +				// Set secondary fg colour +				int v = va_arg(va, int); +				_globals->_sceneText._colour3 = v; +				_globals->gfxManager()._font._colours.foreground = v; +				break; +			} +			case SET_POS_MODE: +				// Set whether a custom x/y is used +				centreText = va_arg(va, int) != 0; +				break; +			case SET_TEXT_MODE: +				// Set the text mode +				_globals->_sceneText._textMode = (TextAlign)va_arg(va, int); +				break; +			default: +				break; +			} +		} while (mode != LIST_END); + +		va_end(va); +	} +	 +	if (resNum) { +		// Get required bounding size +		_globals->gfxManager().getStringBounds(msg.c_str(), textRect, maxWidth); +		textRect.centre(pos.x, pos.y); + +		textRect.contain(_globals->gfxManager()._bounds); +		if (centreText) { +			_globals->_sceneText._colour1 = _globals->_sceneText._colour2; +			_globals->_sceneText._colour2 = 0; +			_globals->_sceneText._colour3 = 0; +		} + +		_globals->_sceneText.setup(msg); +		if (centreText) { +			_globals->_sceneText.setPosition(Common::Point( +				_globals->_sceneManager._scene->_sceneBounds.left + textRect.left, +				_globals->_sceneManager._scene->_sceneBounds.top + textRect.top), 0); +		} else { +			_globals->_sceneText.setPosition(pos, 0); +		} + +		_globals->_sceneText.setPriority2(255); +		_globals->_sceneObjects->draw(); +	} + +	// Unless the flag is set to keep the message on-screen, show it until a mouse or keypress, then remove it +	if (!keepOnscreen && !msg.empty()) { +		Event event; + +		// Keep event on-screen until a mouse or keypress +		while (!_vm->getEventManager()->shouldQuit() && !_globals->_events.getEvent(event, +				EVENT_BUTTON_DOWN | EVENT_KEYPRESS)) { +			g_system->updateScreen(); +			g_system->delayMillis(10); +		} + +		_globals->_sceneText.remove(); +	} +} + +/*--------------------------------------------------------------------------*/ + +void SceneHotspot::doAction(int action) { +	switch ((int)action) { +	case CURSOR_LOOK: +		display(1, 0, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +		break; +	case CURSOR_USE: +		display(1, 5, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +		break; +	case CURSOR_TALK: +		display(1, 15, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +		break; +	case CURSOR_WALK: +		break; +	default: +		display(2, action, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +void SceneObjectWrapper::setSceneObject(SceneObject *so) { +	_sceneObject = so; +	so->_strip = 1; +	so->_flags |= OBJFLAG_PANES; +} + +void SceneObjectWrapper::synchronise(Serialiser &s) { +	EventHandler::synchronise(s); +	SYNC_POINTER(_sceneObject); +} + +void SceneObjectWrapper::dispatch() { +	_visageImages.setVisage(_sceneObject->_visage); +	int frameCount = _visageImages.getFrameCount(); +	int angle = _sceneObject->_angle; +	int strip = _sceneObject->_strip; + +	if (frameCount == 4) { +		if ((angle > 314) || (angle < 45)) +			strip = 4; +		if ((angle > 44) && (angle < 135)) +			strip = 1; +		if ((angle >= 135) && (angle < 225)) +			strip = 3; +		if ((angle >= 225) && (angle < 315)) +			strip = 2; +	} else if (frameCount == 8) { +		if ((angle > 330) || (angle < 30)) +			strip = 4; +		if ((angle >= 30) && (angle < 70)) +			strip = 7; +		if ((angle >= 70) && (angle < 110)) +			strip = 1; +		if ((angle >= 110) && (angle < 150)) +			strip = 5; +		if ((angle >= 150) && (angle < 210)) +			strip = 3; +		if ((angle >= 210) && (angle < 250)) +			strip = 6; +		if ((angle >= 250) && (angle < 290)) +			strip = 2; +		if ((angle >= 290) && (angle < 331)) +			strip = 8; +	} + +	if (strip > frameCount) +		strip = frameCount; + +	_sceneObject->setStrip(strip); +} + +/*--------------------------------------------------------------------------*/ + +SceneObject::SceneObject(): SceneHotspot() { +	_endAction = NULL; +	_mover = NULL; +	_objectWrapper = NULL; +	_flags = 0; +	_walkStartFrame = 0; +	_animateMode = ANIM_MODE_NONE; +	_updateStartFrame = 0; +	_moveDiff.x = 5; +	_moveDiff.y = 3; +	_numFrames = 10; +	_numFrames = 10; +	_field7A = 10; +	_regionBitList = 0; +	_sceneRegionId = 0; +	_percent = 100; +	_flags |= OBJFLAG_PANES; + +	_frameChange = 0; +} + +SceneObject::~SceneObject() { +	delete _mover; +	delete _objectWrapper; +} + +int SceneObject::getNewFrame() { +	int frameNum = _frame + _frameChange; + +	if (_frameChange > 0) { +		if (frameNum > getFrameCount()) { +			frameNum = 1; +			if (_animateMode == ANIM_MODE_1) +				++frameNum; +		} +	} else if (frameNum < 1) { +		frameNum = getFrameCount(); +	} + +	return frameNum; +} + +int SceneObject::getFrameCount() { +	_visageImages.setVisage(_visage, _strip); +	return _visageImages.getFrameCount(); +} + +void SceneObject::animEnded() { +	_animateMode = ANIM_MODE_NONE; +	if (_endAction) +		_endAction->signal(); +} + +int SceneObject::changeFrame() { +	int frameNum = _frame; +	uint32 mouseCtr = _globals->_events.getFrameNumber(); + +	if ((_updateStartFrame <= mouseCtr) || (_animateMode == ANIM_MODE_1)) { +		if (_numFrames > 0) { +			int v = 60 / _numFrames; +			_updateStartFrame = mouseCtr + v; + +			frameNum = getNewFrame(); +		} +	} + +	return frameNum; +} + +void SceneObject::setPosition(const Common::Point &p, int yDiff) { +	_position = p; +	_yDiff = yDiff; +	_flags |= OBJFLAG_PANES; +} + +void SceneObject::setZoom(int percent) { +	if (percent != _percent) { +		_percent = percent; +		_flags |= OBJFLAG_PANES; +	} +} + +void SceneObject::changeZoom(int percent) { +	if (percent == -1) +		_flags &= ~OBJFLAG_ZOOMED; +	else { +		_flags |= OBJFLAG_ZOOMED; +		setZoom(percent); +	} +} + +void SceneObject::setStrip(int stripNum) { +	if (stripNum != _strip) { +		_strip = stripNum; +		_flags |= OBJFLAG_PANES; +	} +} + +void SceneObject::setStrip2(int stripNum) { +	if (stripNum == -1) +		_flags &= ~OBJFLAG_8; +	else { +		_flags |= OBJFLAG_8; +		setStrip(stripNum); +	} +} + +void SceneObject::setFrame(int frameNum) { +	if (frameNum != _frame) { +		_frame = frameNum; +		_flags |= OBJFLAG_PANES; +	} +} + +void SceneObject::setFrame2(int frameNum) { +	if (frameNum == -1) { +		_flags |= OBJFLAG_NO_UPDATES; +		setFrame(frameNum); +	} else { +		_flags &= ~OBJFLAG_NO_UPDATES; +	} +} + +void SceneObject::setPriority(int priority) { +	if (priority != _priority) { +		_priority = priority; +		_flags |= OBJFLAG_PANES; +	} +} + +void SceneObject::setPriority2(int priority) { +	if (priority == -1) { +		_flags &= ~1; +	} else { +		_flags |= 1; +		setPriority(priority); +	} +} + +void SceneObject::setVisage(int visage) { +	if (visage != _visage) { +		_visage = visage; +		_flags |= OBJFLAG_PANES; +	} +} + +void SceneObject::setObjectWrapper(SceneObjectWrapper *objWrapper) { +	if (_objectWrapper) +		delete _objectWrapper; +	_objectWrapper = objWrapper; +	if (objWrapper) +		objWrapper->setSceneObject(this); +} + +void SceneObject::addMover(ObjectMover *mover, ...) { +	if (_mover)  +		delete _mover; +	_mover = mover; + +	if (mover) { +		// Set up the assigned mover +		_walkStartFrame = _globals->_events.getFrameNumber(); +		if (_field7A != 0) +			_walkStartFrame = 60 / _field7A; + +		// Signal the mover that movement is beginning +		va_list va; +		va_start(va, mover); +		mover->startMove(this, va); +		va_end(va); +	} +} + +void SceneObject::getHorizBounds() { +	Rect tempRect; + +	GfxSurface frame = getFrame(); +	tempRect.resize(frame, _position.x, _position.y - _yDiff, _percent); + +	_xs = tempRect.left; +	_xe = tempRect.right; +} + +int SceneObject::checkRegion(const Common::Point &pt) { +	Rect tempRect; +	int regionIndex = 0; + +	// Temporarily change the position +	Common::Point savedPos = _position; +	_position = pt; +	 +	int regIndex = _globals->_sceneRegions.indexOf(pt); +	if (_regionBitList & (1 << regIndex)) +		regionIndex = regIndex; + +	// Restore position +	_position = savedPos; + +	// Get the object's frame bounds +	GfxSurface frame = getFrame(); +	tempRect.resize(frame, _position.x, _position.y - _yDiff, _percent); + +	int yPos, newY; +	if ((_position.y - _yDiff) <= (pt.y - _yDiff)) { +		yPos = _position.y - _yDiff; +		newY = pt.y; +	} else { +		yPos = pt.y - _yDiff; +		newY = _position.y; +	} +	newY -= _yDiff; + +	List<SceneObject *>::iterator i; +	for (i = _globals->_sceneObjects->begin(); (regionIndex == 0) && (i != _globals->_sceneObjects->end()); ++i) { +		if ((*i) && ((*i)->_flags & OBJFLAG_1000)) { +			int objYDiff = (*i)->_position.y - _yDiff; +			if ((objYDiff >= yPos) && (objYDiff <= newY) && +				((*i)->_xs < tempRect.right) && ((*i)->_xe > tempRect.left)) { +				// Found index +				regionIndex = -1; //****DEBUG*** = *i; +				break; +			} +		} +	} + +	return regionIndex; +} + +void SceneObject::animate(AnimateMode animMode, ...) { +	_animateMode = animMode; +	_updateStartFrame = _globals->_events.getFrameNumber(); +	if (_numFrames) +		_updateStartFrame += 60 / _numFrames; + +	va_list va; +	va_start(va, animMode); + +	switch (_animateMode) { +	case ANIM_MODE_NONE: +		_endAction = NULL; +		break; + +	case ANIM_MODE_1: +		_frameChange = 1; +		_field2E = _position; +		_endAction = 0; +		break; + +	case ANIM_MODE_2: +		_frameChange = 1; +		_endAction = NULL; +		break; + +	case ANIM_MODE_3: +		_frameChange = -1; +		_endAction = NULL; +		break; + +	case ANIM_MODE_4: +		_endFrame = va_arg(va, int); +		_frameChange = va_arg(va, int); +		_endAction = va_arg(va, Action *); +		if (_endFrame == _frame) +			setFrame(getNewFrame()); +		break; + +	case ANIM_MODE_5: +		_frameChange = 1; +		_endFrame = getFrameCount(); +		_endAction = va_arg(va, Action *); +		if (_endFrame == _frame) +			setFrame(getNewFrame()); +		break; + +	case ANIM_MODE_6: +		_frameChange = -1; +		_endAction = va_arg(va, Action *); +		_endFrame = 1; +		if (_frame == _endFrame) +			setFrame(getNewFrame()); +		break; + +	case ANIM_MODE_7: +		_endFrame = va_arg(va, int); +		_endAction = va_arg(va, Action *); +		_frameChange = 1; +		break; + +	case ANIM_MODE_8: +		_field68 = va_arg(va, int); +		_endAction = va_arg(va, Action *); +		_frameChange = 1; +		_endFrame = getFrameCount(); +		if (_frame == _endFrame) +			setFrame(getNewFrame()); +		break;		 +	} +} + +SceneObject *SceneObject::clone() const { +	SceneObject *obj = new SceneObject(*this); +	return obj; +} + +void SceneObject::checkAngle(const SceneObject *obj) { +	_angle = GfxManager::getAngle(_position, obj->_position); + +	if (_objectWrapper) +		_objectWrapper->dispatch(); +} + +void SceneObject::flag100() { +	_flags |= OBJFLAG_100; +	if (_flags & OBJFLAG_200) +		_flags |= OBJFLAG_PANES; +} + +void SceneObject::unflag100() { +	if (_flags & OBJFLAG_100) { +		_flags &= ~OBJFLAG_100; +		_flags |= OBJFLAG_PANES; +	} +} + +int SceneObject::getSpliceArea(const SceneObject *obj) { +	int xd = ABS(_position.x - obj->_position.x); +	int yd = ABS(_position.y - obj->_position.y); +	 +	return (xd * xd + yd) / 2; +} + +void SceneObject::synchronise(Serialiser &s) { +	SceneHotspot::synchronise(s); + +	s.syncAsUint32LE(_updateStartFrame); +	s.syncAsUint32LE(_walkStartFrame); +	s.syncAsSint16LE(_field2E.x); s.syncAsSint16LE(_field2E.y); +	s.syncAsSint16LE(_percent); +	s.syncAsSint16LE(_priority); +	s.syncAsSint16LE(_angle); +	s.syncAsUint32LE(_flags); +	s.syncAsSint16LE(_xs); +	s.syncAsSint16LE(_xe); +	_paneRects[0].synchronise(s); +	_paneRects[1].synchronise(s); +	s.syncAsSint32LE(_visage); +	SYNC_POINTER(_objectWrapper); +	s.syncAsSint32LE(_strip); +	SYNC_ENUM(_animateMode, AnimateMode); +	s.syncAsSint32LE(_frame); +	s.syncAsSint32LE(_endFrame); +	s.syncAsSint32LE(_field68); +	s.syncAsSint32LE(_frameChange); +	s.syncAsSint32LE(_numFrames); +	s.syncAsSint32LE(_field6E); +	SYNC_POINTER(_mover); +	s.syncAsSint16LE(_moveDiff.x); s.syncAsSint16LE(_moveDiff.y); +	s.syncAsSint32LE(_field7A); +	SYNC_POINTER(_endAction); +	s.syncAsUint32LE(_regionBitList);	 +} + +void SceneObject::postInit(SceneObjectList *OwnerList) { +	if (!OwnerList) +		OwnerList = _globals->_sceneObjects; + +	if (!OwnerList->contains(this)) { +		_percent = 100; +		_priority = 255; +		_flags = 4; +		_visage = 0; +		_strip = 1; +		_frame = 1; +		_objectWrapper = NULL; +		_animateMode = ANIM_MODE_NONE; +		_endAction = 0; +		_mover = NULL; +		_yDiff = 0; +		_moveDiff.x = 5; +		_moveDiff.y = 3; +		_field7A = 10; +		_field6E = 64; +		_numFrames = 10; +		_regionBitList = 0; + +		OwnerList->push_back(this); +		_flags |= OBJFLAG_PANES; +	} +} + +void SceneObject::remove() { +	SceneItem::remove(); +	if (_globals->_sceneObjects->contains(this)) +		// For objects in the object list, flag the object for removal in the next drawing, so that  +		// the drawing code has a chance to restore the area previously covered by the object +		_flags |= OBJFLAG_PANES | OBJFLAG_REMOVE | OBJFLAG_100; +	else +		// Not in the list, so immediately remove the object +		removeObject(); +} + +void SceneObject::dispatch() { +	uint32 currTime = _globals->_events.getFrameNumber(); +	if (_action) +		_action->dispatch(); + +	if (_mover && (_walkStartFrame <= currTime)) { +		if (_field7A) { +			int frameInc = 60 / _field7A; +			_walkStartFrame = currTime + frameInc; +		} +		_mover->dispatch(); +	} + +	if (!(_flags & OBJFLAG_NO_UPDATES)) { +		switch (_animateMode) { +		case ANIM_MODE_1: +			if (isNoMover()) +				setFrame(1); +			else if ((_field2E.x != _position.x) || (_field2E.y != _position.y)) { +				setFrame(changeFrame()); +				_field2E = _position; + +			} +			break; + +		case ANIM_MODE_2: +		case ANIM_MODE_3: +			setFrame(changeFrame()); + +			break; +		case ANIM_MODE_4: +		case ANIM_MODE_5: +		case ANIM_MODE_6: +			if (_frame == _endFrame) +				animEnded(); +			else +				setFrame(changeFrame()); +			break; + +		case ANIM_MODE_7: +			if (changeFrame() != _frame) { +				// Pick a new random frame +				int frameNum = 0; +				do { +					int count = getFrameCount(); +					frameNum = _globals->_randomSource.getRandomNumber(count - 1); +				} while (frameNum == _frame); + +				setFrame(frameNum); +				if (_endFrame) { +					if (--_endFrame == 0) +						animEnded(); +				} +			} +			break; + +		case ANIM_MODE_8: +			if (_frame == _endFrame) { +				if (_frameChange != -1) { +					_frameChange = -1; +					_endFrame = 1; + +					setFrame(changeFrame()); +				} else if (!_field68 || (--_field68 > 0)) { +					_frameChange = 1; +					_endFrame = getFrameCount(); + +					setFrame(changeFrame()); +				} else { +					animEnded(); +				} +			} +			 +			break; + +		default: +			break; +		} +	} + +	// Handle updating the zoom and/or priority +	if (!(_flags & OBJFLAG_ZOOMED)) { +		int yp = MIN((int)_position.y, 255); +		setZoom(_globals->_sceneManager._scene->_zoomPercents[yp]); +	} +	if (!(_flags & OBJFLAG_FIXED_PRIORITY)) { +		setPriority(_position.y); +	} +} + +void SceneObject::calcAngle(const Common::Point &pt) { +	int newAngle = GfxManager::getAngle(_position, pt); +	if (newAngle != -1) +		_angle = newAngle; +} + +void SceneObject::removeObject() { +	if (_globals->_sceneItems.contains(this)) +		_globals->_sceneItems.remove(this); + +	if (_globals->_sceneObjects->contains(this)) +		_globals->_sceneObjects->remove(this); + +	if (_visage) { +		_vm->_memoryManager.deallocate(_visage); +		_visage = 0; +	} + +	if (_objectWrapper) { +		_objectWrapper->remove(); +		_objectWrapper = NULL; +	} +	if (_mover) { +		_mover->remove(); +		_mover = NULL; +	} +	if (_flags & 0x800) +		destroy(); +} + +GfxSurface SceneObject::getFrame() { +	_visageImages.setVisage(_visage, _strip); +	return _visageImages.getFrame(_frame); +} + +void SceneObject::reposition() { +	GfxSurface frame = getFrame(); +	_bounds.resize(frame, _position.x, _position.y - _yDiff, _percent); +	_xs = _bounds.left; +	_xe = _bounds.right; +} + +/** + * Draws an object into the scene + */ +void SceneObject::draw() { +	Rect destRect = _bounds; +	destRect.translate(_globals->_sceneManager._scene->_sceneBounds.left,  +		_globals->_sceneManager._scene->_sceneBounds.top); +	Region *priorityRegion = _globals->_sceneManager._scene->_priorities.find(_priority); +	GfxSurface frame = getFrame(); +	_globals->gfxManager().copyFrom(frame, destRect, priorityRegion);  +} + +/** + * Refreshes the background around the area of a scene object prior to it's being redrawn,  + * in case it is moving + */ +void SceneObject::updateScreen() { +	Rect objRect = _paneRects[CURRENT_PANENUM]; +	const Rect &sceneBounds = _globals->_sceneManager._scene->_sceneBounds; +	objRect.left = (objRect.left / 4) * 4; +	objRect.right = ((objRect.right + 3) / 4) * 4; +	objRect.clip(_globals->_sceneManager._scene->_sceneBounds); + +	if (objRect.isValidRect()) { +		Rect tempRect  = objRect; +		tempRect.translate(-_globals->_sceneOffset.x, -_globals->_sceneOffset.y); +		objRect.translate(-sceneBounds.left, -sceneBounds.top); + +		_globals->_screenSurface.copyFrom(_globals->_sceneManager._scene->_backSurface, objRect, tempRect); +	} +} + +/*--------------------------------------------------------------------------*/ + +void SceneObjectList::draw() { +	Common::Array<SceneObject *> objList; +	int paneNum = 0; +	int xAmount = 0, yAmount = 0; + +	if (_objList.size() == 0) { +		// Alternate draw mode + +		if (_globals->_paneRefreshFlag[paneNum] == 1) { +			// Load the background +			_globals->_sceneManager._scene->refreshBackground(0, 0); + +			Rect tempRect = _globals->_sceneManager._scene->_sceneBounds; +			tempRect.translate(-_globals->_sceneOffset.x, -_globals->_sceneOffset.y); +			ScenePalette::changeBackground(tempRect, _globals->_sceneManager._FadeMode); +		} else { +			_globals->_paneRegions[CURRENT_PANENUM].draw(); +		} + +		_globals->_paneRegions[CURRENT_PANENUM].setRect(0, 0, 0, 0); +		_globals->_sceneManager.fadeInIfNecessary(); + +	} else { +		// If there is a scroll follower, check whether it has moved off-screen +		if (_globals->_scrollFollower) { +			const Common::Point &objPos = _globals->_scrollFollower->_position; +			const Rect &scrollerRect = _globals->_sceneManager._scrollerRect; +			int loadCount = 0; + +			if (objPos.x >= scrollerRect.right) { +				xAmount = 8; +				loadCount = 20; +			} +			if (objPos.x < scrollerRect.left) { +				xAmount = -8; +				loadCount = 20; +			} +			if (objPos.y >= scrollerRect.bottom) { +				yAmount = 2; +				loadCount = 25; +			} +			if (objPos.y < scrollerRect.top) { +				yAmount = -2; +				loadCount = 25; +			} + +			if (loadCount > 0) +				_globals->_sceneManager.setBgOffset(Common::Point(xAmount, yAmount), loadCount); +		} + +		if (_globals->_sceneManager._sceneLoadCount > 0) { +			--_globals->_sceneManager._sceneLoadCount; +			_globals->_sceneManager._scene->loadBackground(_globals->_sceneManager._sceneBgOffset.x, +				_globals->_sceneManager._sceneBgOffset.y); +		} + +		// Set up the flag mask +		uint32 flagMask = (paneNum == 0) ? OBJFLAG_PANE_0 : OBJFLAG_PANE_1; + +		// Initial loop to set up object list and update object position, priority, and flags +		for (List<SceneObject *>::iterator i = _globals->_sceneObjects->begin(); +				i != _globals->_sceneObjects->end(); ++i) { +			SceneObject *obj = *i; +			objList.push_back(obj); + +			if (!(obj->_flags & OBJFLAG_100)) +				obj->_flags &= ~OBJFLAG_200; + +			// Reposition the bounds of the object to match the desired position +			obj->reposition(); + +			// Handle updating object priority +			if (!(obj->_flags & OBJFLAG_FIXED_PRIORITY)) { +				obj->_priority = MIN((int)obj->_position.y - 1,  +					(int)_globals->_sceneManager._scene->_backgroundBounds.bottom); +			} + +			if ((_globals->_paneRefreshFlag[paneNum] != 0) || !_globals->_paneRegions[paneNum].empty()) { +				obj->_flags |= flagMask; +			} +		} + +		// Check for any intersections, and then sort the object list by priority +		checkIntersection(objList, objList.size(), CURRENT_PANENUM); +		sortList(objList); + +		if (_globals->_paneRefreshFlag[paneNum] == 1) { +			// Load the background +			_globals->_sceneManager._scene->refreshBackground(0, 0); +		} + +		_globals->_sceneManager._scene->_sceneBounds.left &= ~3; +		_globals->_sceneManager._scene->_sceneBounds.right &= ~3; +		_globals->_sceneOffset.x &= ~3; + +		if (_globals->_paneRefreshFlag[paneNum] != 0) { +			// Change the background +			Rect tempRect = _globals->_sceneManager._scene->_sceneBounds; +			tempRect.translate(-_globals->_sceneOffset.x, -_globals->_sceneOffset.y); +			ScenePalette::changeBackground(tempRect, _globals->_sceneManager._FadeMode); +		} else { +			for (uint objIndex = 0; objIndex < objList.size(); ++objIndex) { +				SceneObject *obj = objList[objIndex]; +				 +				if ((obj->_flags & flagMask) && obj->_paneRects[paneNum].isValidRect()) +					obj->updateScreen(); +			} + +			_globals->_paneRegions[paneNum].draw(); +		} + +		_globals->_paneRegions[paneNum].setRect(0, 0, 0, 0); +redraw: +		// Main draw loop +		for (uint objIndex = 0; objIndex < objList.size(); ++objIndex) { +			SceneObject *obj = objList[objIndex]; + +			if ((obj->_flags & flagMask) && !(obj->_flags & OBJFLAG_100)) { +				obj->_paneRects[paneNum] = obj->_bounds; +				obj->draw(); +			} +		} + +		// Update the palette +		_globals->_sceneManager.fadeInIfNecessary(); +		_globals->_paneRefreshFlag[paneNum] = 0; + +		// Loop through the object list, removing any objects and refreshing the screen as necessary +		for (uint objIndex = 0; objIndex < objList.size(); ++objIndex) { +			SceneObject *obj = objList[objIndex]; + +			if (obj->_flags & OBJFLAG_100) +				obj->_flags |= OBJFLAG_200; +			obj->_flags &= ~flagMask; +			if (obj->_flags & OBJFLAG_REMOVE) { +				obj->_flags |= OBJFLAG_PANES; +				 +				checkIntersection(objList, objIndex, CURRENT_PANENUM); + +				obj->updateScreen(); +				obj->removeObject(); + +				// FIXME: Currently, removing objects causes screen flickers when the removed object intersects +				// another drawn object, since the background is briefly redrawn over the object. For now, I'm +				// using a forced jump back to redraw objects. In the long term, I should figure out how the +				// original game does this properly +				objList.remove_at(objIndex); +				goto redraw; +			} +		} +	} +} + +void SceneObjectList::checkIntersection(Common::Array<SceneObject *> &ObjList, uint ObjIndex, int PaneNum) { +	uint32 flagMask = (PaneNum == 0) ? OBJFLAG_PANE_0 : OBJFLAG_PANE_1; +	SceneObject *obj = (ObjIndex == ObjList.size()) ? NULL : ObjList[ObjIndex]; +	Rect rect1; + +	for (uint idx = 0; idx < ObjList.size(); ++idx) { +		SceneObject *currObj = ObjList[idx]; + +		if (ObjIndex == ObjList.size()) { +			if (currObj->_flags & flagMask) +				checkIntersection(ObjList, idx, PaneNum); +		} else if (idx != ObjIndex) { +			Rect &paneRect = obj->_paneRects[PaneNum]; +			Rect objBounds = currObj->_bounds; +			if (paneRect.isValidRect()) +				objBounds.extend(paneRect); + +			Rect objBounds2 = currObj->_bounds; +			if (paneRect.isValidRect()) +				objBounds2.extend(paneRect); + +			objBounds.left &= ~3; +			objBounds.right += 3; +			objBounds.right &= ~3; +			objBounds2.left &= ~3; +			objBounds2.right += 3; +			objBounds2.right &= ~3; + +			if (objBounds.intersects(objBounds2) && !(currObj->_flags & flagMask)) { +				currObj->_flags |= flagMask; +				checkIntersection(ObjList, idx, PaneNum); +			} +		} +	} +} + +struct SceneObjectLess { +	bool operator()(const SceneObject *x, const SceneObject *y) const { +		if (y->_priority > x->_priority) +			return true; +		else if ((y->_priority == x->_priority) && (y->_position.y > x->_position.y)) +			return true; +		else if ((y->_priority == x->_priority) && (y->_position.y == x->_position.y) && +				 (y->_yDiff > x->_yDiff)) +			return true; + +		return false; +	} +}; + +void SceneObjectList::sortList(Common::Array<SceneObject *> &ObjList) { +	Common::sort(ObjList.begin(), ObjList.end(), SceneObjectLess()); +} + +void SceneObjectList::activate() { +	SceneObjectList *objectList = _globals->_sceneObjects; +	_globals->_sceneObjects = this; +	_globals->_sceneObjects_queue.push_front(this); + +	// Flag all the objects as modified +	List<SceneObject *>::iterator i; +	for (i = begin(); i != end(); ++i) { +		(*i)->_flags |= OBJFLAG_PANES; +	} + +	// Replicate all existing objects on the old object list +	for (i = objectList->begin(); i != objectList->end(); ++i) { +		SceneObject *sceneObj = (*i)->clone(); +		sceneObj->_flags |= OBJFLAG_100 | OBJFLAG_REMOVE | OBJFLAG_800; +		push_front(sceneObj); +	} +} + +void SceneObjectList::deactivate() { +	if (_globals->_sceneObjects_queue.size() <= 1) +		return; + +	SceneObjectList *objectList = *_globals->_sceneObjects_queue.begin(); +	_globals->_sceneObjects_queue.pop_front(); +	_globals->_sceneObjects = *_globals->_sceneObjects_queue.begin(); + +	List<SceneObject *>::iterator i; +	for (i = objectList->begin(); i != objectList->end(); ++i) { +		if (!((*i)->_flags & OBJFLAG_800)) { +			SceneObject *sceneObj = (*i)->clone(); +			sceneObj->_flags |= OBJFLAG_100 | OBJFLAG_REMOVE | OBJFLAG_800; +			_globals->_sceneObjects->push_front(sceneObj); +		} +	} +} + +void SceneObjectList::synchronise(Serialiser &s) { +	_objList.synchronise(s); +} + +/*--------------------------------------------------------------------------*/ + +SceneText::SceneText(): SceneObject() { +	_fontNumber = 2; +	_width = 160; +	_textMode = ALIGN_LEFT; +	_colour2 = 0; +	_colour3 = 0; +} + +SceneText::~SceneText() { +} + +void SceneText::setup(const Common::String &msg) { +	GfxManager gfxMan(_textSurface); +	gfxMan.activate(); +	Rect textRect; +	 +	gfxMan._font.setFontNumber(_fontNumber); +	gfxMan._font._colours.foreground = _colour1; +	gfxMan._font._colours2.background = _colour2; +	gfxMan._font._colours2.foreground = _colour3; + +	gfxMan.getStringBounds(msg.c_str(), textRect, _width); +	_bounds = textRect; + +	// Set up a new blank surface to hold the text +	_textSurface.create(textRect.width(), textRect.height()); +	_textSurface._transColour = 0xff; +	_textSurface.fillRect(textRect, _textSurface._transColour); + +	// Write the text to the surface +	gfxMan._font.writeLines(msg.c_str(), textRect, _textMode);	 + +	// Do post-init, which adds this SceneText object to the scene +	postInit(); +	gfxMan.deactivate(); +} + +void SceneText::synchronise(Serialiser &s) { +	SceneObject::synchronise(s); + +	s.syncAsSint16LE(_fontNumber); +	s.syncAsSint16LE(_width); +	s.syncAsSint16LE(_colour1); +	s.syncAsSint16LE(_colour2); +	s.syncAsSint16LE(_colour3); +	SYNC_ENUM(_textMode, TextAlign); +} + +/*--------------------------------------------------------------------------*/ + +Visage::Visage() { +	_resNum = 0; +	_rlbNum = 0; +	_data = NULL; +} + +void Visage::setVisage(int resNum, int rlbNum) { +	if ((_resNum != resNum) || (_rlbNum != rlbNum)) { +		_resNum = resNum; +		_rlbNum = rlbNum; +		DEALLOCATE(_data); +		_data = _vm->_dataManager->getResource(RES_VISAGE, resNum, rlbNum); +		assert(_data); +	} +} + +Visage::~Visage() { +	DEALLOCATE(_data); +} + +GfxSurface Visage::getFrame(int frameNum) { +	int numFrames = READ_LE_UINT16(_data); +	if (frameNum > numFrames) +		frameNum = numFrames; +	if (frameNum > 0) +		--frameNum; + +	int offset = READ_UINT32(_data + 2 + frameNum * 4); +	byte *frameData = _data + offset; + +	return surfaceFromRes(frameData); +} + +int Visage::getFrameCount() const { +	return READ_LE_UINT16(_data); +} + +/*--------------------------------------------------------------------------*/ + +void Player::postInit(SceneObjectList *OwnerList) { +	SceneObject::postInit(); + +	_canWalk = true; +	_uiEnabled = true; +	_percent = 100; +	_field8C = 10; +	_moveDiff.x = 4; +	_moveDiff.y = 2; +} + +void Player::disableControl() { +	_canWalk = false; +	_uiEnabled = false; +	_globals->_events.hideCursor(); +} + +void Player::enableControl() { +	_canWalk = true; +	_uiEnabled = true; +	_globals->_events.showCursor(); + +	switch (_globals->_events.getCursor()) { +	case CURSOR_CROSSHAIRS: +		_globals->_events.setCursor(CURSOR_WALK); +		break; +	default: +		break; +	} +} + +void Player::process(Event &event) { +	if (!event.handled && (event.eventType == EVENT_BUTTON_DOWN) &&  +			(_globals->_events.getCursor() == CURSOR_WALK) && _globals->_player._canWalk && +			(_position != event.mousePos) && _globals->_sceneObjects->contains(this)) { + +		PlayerMover *newMover = new PlayerMover(); +		Common::Point destPos(event.mousePos.x - _globals->_sceneManager._scene->_sceneBounds.left, +			event.mousePos.y - _globals->_sceneManager._scene->_sceneBounds.top); + +		addMover(newMover, &destPos, NULL); +		event.handled = true; +	} +} + +void Player::synchronise(Serialiser &s) { +	SceneObject::synchronise(s); + +	s.syncAsByte(_canWalk); +	s.syncAsByte(_uiEnabled); +	s.syncAsSint16LE(_field8C); +} + +/*--------------------------------------------------------------------------*/ + +Region::Region(int resNum, int rlbNum, ResourceType ctlType) { +	_regionId = rlbNum; + +	byte *regionData = _vm->_dataManager->getResource(ctlType, resNum, rlbNum); +	assert(regionData); + +	// Set the region bounds +	_bounds.top = READ_LE_UINT16(regionData + 6); +	_bounds.left = READ_LE_UINT16(regionData + 8); +	_bounds.bottom = READ_LE_UINT16(regionData + 10); +	_bounds.right = READ_LE_UINT16(regionData + 12); + +	// Special handling for small size regions +	_regionSize = READ_LE_UINT16(regionData); +	if (_regionSize == 14) +		// No line slices +		return; + +	// Set up the line slices +	for (int y = 0; y < (_regionSize == 22 ? 1 : _bounds.height()); ++y) { +		int slicesCount = READ_LE_UINT16(regionData + 16 + y * 4); +		int slicesOffset = READ_LE_UINT16(regionData + 14 + y * 4); +		assert(slicesCount < 100); +		LineSliceSet sliceSet; +		sliceSet.load(slicesCount, regionData + 14 + slicesOffset); + +		_ySlices.push_back(sliceSet); +	} + +	DEALLOCATE(regionData); +} + +/** + * Returns true if the given region contains the specified point + * @param pt	Specified position + */ +bool Region::contains(const Common::Point &pt) { +	// First check if the point falls inside the overall bounding rectangle +	if (!_bounds.contains(pt) || _ySlices.empty()) +		return false; + +	// Get the correct Y line to use +	const LineSliceSet &line = getLineSlices(pt.y); + +	// Loop through the horizontal slice list to see if the point falls in one +	for (uint idx = 0; idx < line.items.size(); ++idx) { +		if ((pt.x >= line.items[idx].xs) && (pt.x < line.items[idx].xe)) +			return true; +	} + +	return false; +} + +/** + * Returns true if the given region is empty + */ +bool Region::empty() const { +	return !_bounds.isValidRect() && (_regionSize == 14); +} + +void Region::clear() { +	_bounds.set(0, 0, 0, 0); +	_regionId = 0; +	_regionSize = 0; +} + +void Region::setRect(const Rect &r) { +	setRect(r.left, r.top, r.right, r.bottom); +} + +void Region::setRect(int xs, int ys, int xe, int ye) { +	bool validRect = (ys < ye) && (xs < xe); +	_ySlices.clear(); + +	if (!validRect) { +		_regionSize = 14; +		_bounds.set(0, 0, 0, 0); +	} else { +		_regionSize = 22; +		_bounds.set(xs, ys, xe, ye); +		 +		LineSliceSet sliceSet; +		sliceSet.load2(1, xs, xe); + +		_ySlices.push_back(sliceSet); +	} +} + +const LineSliceSet &Region::getLineSlices(int yp) { +	return _ySlices[(_regionSize == 22) ? 0 : yp - _bounds.top];	 +} + +LineSliceSet Region::sectPoints(int yp, const LineSliceSet &sliceSet) { +	if ((yp < _bounds.top) || (yp >= _bounds.bottom)) +		return LineSliceSet(); + +	const LineSliceSet &ySet = getLineSlices(yp); +	return mergeSlices(sliceSet, ySet); +} + +LineSliceSet Region::mergeSlices(const LineSliceSet &set1, const LineSliceSet &set2) { +	LineSliceSet result; + +	uint set1Index = 0, set2Index = 0; + +	while ((set1Index < set1.items.size()) && (set2Index < set2.items.size())) { +		if (set1.items[set1Index].xe <= set2.items[set2Index].xs) { +			++set1Index; +		} else if (set2.items[set2Index].xe <= set1.items[set1Index].xs) { +			++set2Index; +		} else { +			bool set1Flag = set1.items[set1Index].xs >= set2.items[set2Index].xs; +			const LineSlice &slice = set1Flag ? set1.items[set1Index] : set2.items[set2Index]; +						 +			result.add(slice.xs, MIN(set1.items[set1Index].xe, set2.items[set2Index].xe)); +			if (set1Flag) +				++set1Index; +			else +				++set2Index; +		} +	} + +	return result; +} + +/** + * Copies the background covered by the given region to the screen surface + */ +void Region::draw() { +	Rect &sceneBounds = _globals->_sceneManager._scene->_sceneBounds; + +	for (int yp = sceneBounds.top; yp < sceneBounds.bottom; ++yp) { +		// Generate a line slice set +		LineSliceSet tempSet; +		tempSet.add(sceneBounds.left, sceneBounds.right); +		LineSliceSet newSet = sectPoints(yp, tempSet); +		 +		// Loop through the calculated slices +		for (uint idx = 0; idx < newSet.items.size(); ++idx) { +			Rect rect1(newSet.items[idx].xs, yp, newSet.items[idx].xe, yp + 1); +			rect1.left &= ~3; +			rect1.right = (rect1.right + 3) & ~3; +			 +			Rect rect2 = rect1; +			rect1.translate(-_globals->_sceneOffset.x, -_globals->_sceneOffset.y); +			rect2.translate(-sceneBounds.left, -sceneBounds.top); + +			_globals->gfxManager().getSurface().copyFrom(_globals->_sceneManager._scene->_backSurface, +				rect1, rect2); +		} +	} +} + +void Region::uniteLine(int yp, LineSliceSet &sliceSet) { +	// TODO: More properly implement like the original +	 +	// First expand the bounds as necessary to fit in the row +	if (_ySlices.empty()) { +		_bounds = Rect(sliceSet.items[0].xs, yp, sliceSet.items[sliceSet.items.size() - 1].xe, yp + 1); +		_ySlices.push_back(LineSliceSet()); +	} +	while (yp < _bounds.top) { +		_ySlices.insert_at(0, LineSliceSet()); +		--_bounds.top; +	} +	while (yp >= _bounds.bottom) { +		_ySlices.push_back(LineSliceSet()); +		++_bounds.bottom; +	} + +	// Merge the existing line set into the line +	LineSliceSet &destSet = _ySlices[yp - _bounds.top]; +	for (uint srcIndex = 0; srcIndex < sliceSet.items.size(); ++srcIndex) { +		LineSlice &srcSlice = sliceSet.items[srcIndex]; + +		// Check if overlaps existing slices +		uint destIndex = 0; +		while (destIndex < destSet.items.size()) { +			LineSlice &destSlice = destSet.items[destIndex]; +			if (((srcSlice.xs >= destSlice.xs) && (srcSlice.xs <= destSlice.xe)) || +				((srcSlice.xe >= destSlice.xs) && (srcSlice.xe <= destSlice.xe)) || +				((srcSlice.xs < destSlice.xs) && (srcSlice.xe > destSlice.xe))) { +				// Intersecting, so merge them +				destSlice.xs = MIN(srcSlice.xs, destSlice.xs); +				destSlice.xe = MAX(srcSlice.xe, destSlice.xe); +				break; +			} +			++destIndex; +		} +		if (destIndex == destSet.items.size()) { +			// No intersecting region found, so add it to the list +			destSet.items.push_back(srcSlice); +		} +	} + +	// Check whether to expand the left/bounds bounds +	if (destSet.items[0].xs < _bounds.left) +		destSet.items[0].xs = _bounds.left; +	if (destSet.items[destSet.items.size() - 1].xe > _bounds.right) +		_bounds.right = destSet.items[destSet.items.size() - 1].xe; +} + +/*--------------------------------------------------------------------------*/ + +void SceneRegions::load(int sceneNum) { +	clear(); + +	byte *regionData = _vm->_dataManager->getResource(RES_CONTROL, sceneNum, 9999, true); +	 +	if (regionData) { +		int regionCount = READ_LE_UINT16(regionData); +		for (int regionCtr = 0; regionCtr < regionCount; ++regionCtr) { +			int rlbNum = READ_LE_UINT16(regionData + regionCtr * 6 + 2); + +			push_back(Region(sceneNum, rlbNum)); +		} + +		DEALLOCATE(regionData); +	} +} + +int SceneRegions::indexOf(const Common::Point &pt) { +	for (SceneRegions::iterator i = begin(); i != end(); ++i) { +		if ((*i).contains(pt)) +			return (*i)._regionId; +	} + +	return 0; +} + +/*--------------------------------------------------------------------------*/ + +void SceneItemList::addItems(SceneItem *first, ...) { +	va_list va; +	va_start(va, first); + +	SceneItem *p = first; +	while (p) { +		push_back(p); +		p = va_arg(va, SceneItem *); +	} +} + +/*--------------------------------------------------------------------------*/ + +RegionSupportRec WalkRegion::_processList[PROCESS_LIST_SIZE]; + +void RegionSupportRec::process() { +	if (_xDiff < _yDiff) { +		_halfDiff += _xDiff; +		if (_halfDiff > _yDiff) { +			_halfDiff -= _yDiff; +			_xp += _xDirection; +		} +	} else { +		do { +			_xp += _xDirection; +			_halfDiff += _yDiff; +		} while (_halfDiff <= _xDiff); +		_halfDiff -= _xDiff; +	} +	--_yDiff2; +} + +/*--------------------------------------------------------------------------*/ + +void WalkRegion::loadRegion(byte *dataP, int size) { +	// First clear the region +	clear(); + +	// Decode the data for the region +	int dataCount, regionHeight; +	loadProcessList(dataP, size, dataCount, regionHeight); + +	int processIndex = 0, idx2 = 0, count; +	for (int yp = _processList[0]._yp; yp < regionHeight; ++yp) { +		process3(yp, dataCount, processIndex, idx2); +		process4(yp, processIndex, idx2, count); + +		loadRecords(yp, count, processIndex);  +	} +} + +void WalkRegion::loadProcessList(byte *dataP, int dataSize, int &dataIndex, int ®ionHeight) { +	dataIndex = 0; +	int x1 = READ_LE_UINT16(dataP + (dataSize - 1) * 4); +	int y1 = READ_LE_UINT16(dataP + (dataSize - 1) * 4 + 2); +	regionHeight = y1; + +	for (int idx = 0; idx < dataSize; ++idx) { +		int xp = READ_LE_UINT16(dataP + idx * 4); +		int yp = READ_LE_UINT16(dataP + idx * 4 + 2); +		if (yp != y1) { +			/* +			 * Commented out: doesn't seem to be used +			int v; +			if (idx == (dataSize - 1)) +				v = READ_LE_UINT16(dataP + 2); +			else  +				v = process1(idx, dataP, dataSize); +			warning("TODO: v not used? - %d", v); +			*/ +			process2(dataIndex, x1, y1, xp, yp); +			++dataIndex; +		} + +		// Keep regionHeight as the maximum of any y +		if (yp > regionHeight) +			regionHeight = yp; + +		x1 = xp; +		y1 = yp; +	} +} + +int WalkRegion::process1(int idx, byte *dataP, int dataSize) { +	int idx2 = idx + 1; +	if (idx2 == dataSize) +		idx2 = 0; + +	while (READ_LE_UINT16(dataP + idx2 * 4 + 2) == READ_LE_UINT16(dataP + idx * 4 + 2)) { +		if (idx2 == (dataSize - 1)) +			idx2 = 0; +		else +			++idx2; +	} + +	return READ_LE_UINT16(dataP + idx2 * 4 + 2); +} + +void WalkRegion::process2(int dataIndex, int x1, int y1, int x2, int y2) { +	int xDiff = ABS(x2 - x1); +	int yDiff = ABS(y2 - y1); +	int halfDiff = MAX(xDiff, yDiff) / 2; +	int yMax = MIN(y1, y2); +	 +	while (dataIndex && (_processList[dataIndex - 1]._yp > yMax)) { +		_processList[dataIndex] = _processList[dataIndex - 1]; +		--dataIndex; +	} +	_processList[dataIndex]._yp = yMax; + +	_processList[dataIndex]._xp = (y1 >= y2) ? x2 : x1; +	_processList[dataIndex]._xDiff = xDiff; +	_processList[dataIndex]._yDiff = yDiff; +	_processList[dataIndex]._halfDiff = halfDiff; + +	int xTemp = (y1 >= y2) ? x1 - x2 : x2 - x1; +	_processList[dataIndex]._xDirection = (xTemp == 0) ? 0 : ((xTemp < 0) ? -1 : 1); +	_processList[dataIndex]._yDiff2 = yDiff; +} + +void WalkRegion::process3(int yp, int dataCount, int &idx1, int &idx2) { +	while ((idx2 < (dataCount - 1)) && (_processList[idx2 + 1]._yp <= yp)) +		++idx2; +	while (!_processList[idx1]._yDiff2) +		++idx1; +} + +void WalkRegion::process4(int yp, int idx1, int idx2, int &count) { +	count = 0; +	for (int idx = idx1; idx <= idx2; ++idx) { +		if (_processList[idx]._yDiff2 > 0) +			++count; +		process5(idx, idx1); +	} +} + +void WalkRegion::process5(int idx1, int idx2) { +	while ((idx1 > idx2) && (_processList[idx1 - 1]._xp > _processList[idx1]._xp)) { +		SWAP(_processList[idx1], _processList[idx1 - 1]); +		--idx1; +	} +} + +void WalkRegion::loadRecords(int yp, int size, int processIndex) { +	LineSliceSet sliceSet; +	int sliceCount =  size / 2; + +	for (int idx = 0; idx < sliceCount; ++idx, ++processIndex) { +		while (!_processList[processIndex]._yDiff2) +			++processIndex; + +		int sliceXs = _processList[processIndex]._xp; +		_processList[processIndex].process(); + +		do { +			++processIndex; +		} while (!_processList[processIndex]._yDiff2); + +		int sliceXe = _processList[processIndex]._xp; +		_processList[processIndex].process(); + +		sliceSet.items.push_back(LineSlice(sliceXs, sliceXe)); +	} + +	uniteLine(yp, sliceSet); +} + +/*--------------------------------------------------------------------------*/ + +void WRField18::load(byte *data) { +	_pt1.x = READ_LE_UINT16(data); +	_pt1.y = READ_LE_UINT16(data + 2); +	_pt2.x = READ_LE_UINT16(data + 4); +	_pt2.y = READ_LE_UINT16(data + 6); +	_v = READ_LE_UINT16(data + 8); +} + +/*--------------------------------------------------------------------------*/ + +void WalkRegions::load(int sceneNum) { +	clear(); + +	_resNum = sceneNum; +	byte *regionData = _vm->_dataManager->getResource(RES_WALKRGNS, sceneNum, 1, true); +	if (!regionData) +		// No data, so return +		return; + +	byte *dataP; +	int dataSize; + +	// Load the field 18 list +	dataP = _vm->_dataManager->getResource(RES_WALKRGNS, sceneNum, 2); +	dataSize = _vm->_memoryManager.getSize(dataP); +	assert(dataSize % 10 == 0); + +	byte *p = dataP; +	for (int idx = 0; idx < (dataSize / 10); ++idx, p += 10) { +		WRField18 rec; +		rec.load(p); +		_field18.push_back(rec); +	} + +	DEALLOCATE(dataP); +	 +	// Load the idx list +	dataP = _vm->_dataManager->getResource(RES_WALKRGNS, sceneNum, 3); +	dataSize = _vm->_memoryManager.getSize(dataP); +	assert(dataSize % 2 == 0); + +	p = dataP; +	for (int idx = 0; idx < (dataSize / 2); ++idx, p += 2) +		_idxList.push_back(READ_LE_UINT16(p)); + +	DEALLOCATE(dataP); + +	// Load the secondary idx list +	dataP = _vm->_dataManager->getResource(RES_WALKRGNS, sceneNum, 4); +	dataSize = _vm->_memoryManager.getSize(dataP); +	assert(dataSize % 2 == 0); + +	p = dataP; +	for (int idx = 0; idx < (dataSize / 2); ++idx, p += 2) +		_idxList2.push_back(READ_LE_UINT16(p)); + +	DEALLOCATE(dataP); + +	// Handle the loading of the actual regions themselves +	dataP = _vm->_dataManager->getResource(RES_WALKRGNS, sceneNum, 5); + +	byte *pWalkRegion = regionData + 16; +	byte *srcP = dataP; +	for (; (int16)READ_LE_UINT16(pWalkRegion) != -20000; pWalkRegion += 16) { +		WalkRegion wr; + +		// Set the Walk region specific fields +		wr._pt.x = (int16)READ_LE_UINT16(pWalkRegion); +		wr._pt.y = (int16)READ_LE_UINT16(pWalkRegion + 2); +		wr._idxListIndex = READ_LE_UINT32(pWalkRegion + 4); +		wr._idxList2Index = READ_LE_UINT32(pWalkRegion + 8); + +		// Region in the region data +		int size = READ_LE_UINT16(srcP); +		srcP += 2; +		wr.loadRegion(srcP, size); + +		srcP += size * 4; +		_regionList.push_back(wr); +	} + +	DEALLOCATE(dataP); +	DEALLOCATE(regionData); +} + +/** + * Returns the index of the walk region that contains the given point + * @param pt		Point to locate + * @param indexList	List of region indexes that should be ignored + */ +int WalkRegions::indexOf(const Common::Point &pt, List<int> *indexList) { +	for (uint idx = 0; idx < _regionList.size(); ++idx) { +		if ((!indexList || !indexList->contains(idx + 1)) && _regionList[idx].contains(pt)) +			return idx + 1; +	} + +	return -1; +} + +/*--------------------------------------------------------------------------*/ + +void ScenePriorities::load(int resNum) { +	_resNum = resNum; +	clear(); + +	byte *regionData = _vm->_dataManager->getResource(RES_PRIORITY, resNum, 9999, true); +	 +	if (regionData) { +		int regionCount = READ_LE_UINT16(regionData); +		for (int regionCtr = 0; regionCtr < regionCount; ++regionCtr) { +			int rlbNum = READ_LE_UINT16(regionData + regionCtr * 6 + 2); + +			push_back(Region(resNum, rlbNum, RES_PRIORITY)); +		} + +		DEALLOCATE(regionData); +	} +} + +Region *ScenePriorities::find(int priority) { +	// If no priority regions are loaded, then return the placeholder region +	if (empty()) +		return &_defaultPriorityRegion; + +	if (priority > 255) +		priority = 255; + +	// Loop through the regions to find the closest for the givne priority level +	int minRegionId = 9998; +	Region *region = NULL; +	for (ScenePriorities::iterator i = begin(); i != end(); ++i) { +		Region *r = &(*i); +		int regionId = r->_regionId; + +		if ((regionId > priority) && (regionId < minRegionId)) { +			minRegionId = regionId; +			region = r; +		} +	} + +	assert(region); +	return region; +} + +/*--------------------------------------------------------------------------*/ + +GameHandler::GameHandler(): EventHandler() {  +	_nextWaitCtr = 1; +	_waitCtr.setCtr(1); +	_field14 = 10; +} + +GameHandler::~GameHandler() { +	_globals->_game.removeHandler(this); +} + +void GameHandler::execute() { +	if (_waitCtr.decCtr() == 0) { +		_waitCtr.setCtr(_nextWaitCtr); +		dispatch(); +	} +} + +void GameHandler::synchronise(Serialiser &s) { +	_lockCtr.synchronise(s); +	_waitCtr.synchronise(s); +	s.syncAsSint16LE(_nextWaitCtr); +	s.syncAsSint16LE(_field14); +} + +/*--------------------------------------------------------------------------*/ + +SceneHandler::SceneHandler() { +	_saveGameSlot = -1; +	_loadGameSlot = -1; +} + +void SceneHandler::registerHandler() { +	postInit(); +	_globals->_game.addHandler(this); +} + +void SceneHandler::postInit(SceneObjectList *OwnerList) { +	_delayTicks = 2; + +	_globals->_scenePalette.loadPalette(0); +	_globals->_scenePalette.refresh(); + +	// TODO: Bunch of other scene related setup goes here +	_globals->_soundManager.postInit(); + +	// Set some default flags and cursor +	_globals->setFlag(12); +	_globals->setFlag(34); +	_globals->_events.setCursor(CURSOR_WALK); + +	// Set the screen to scroll in response to the player moving off-screen +	_globals->_scrollFollower = &_globals->_player; + +	// Set the object's that will be in the player's inventory by default +	_globals->_inventory._stunner._sceneNumber = 1; +	_globals->_inventory._scanner._sceneNumber = 1; +	_globals->_inventory._ring._sceneNumber = 1; + +	// Currently hardcoded for first game room. Should be scene 1000 for title screen +	_globals->_sceneManager.setNewScene(30);	 +} + +void SceneHandler::process(Event &event) { +	// Main keypress handler +	if ((event.eventType == EVENT_KEYPRESS) && !event.handled) { +		switch (event.kbd.keycode) { +		case Common::KEYCODE_F1: +			// F1 - Help +			_globals->_events.setCursor(CURSOR_ARROW); +			MessageDialog::show(HELP_MSG, OK_BTN_STRING); +			break; + +		case Common::KEYCODE_F2: { +			// F2 - Sound Options +			ConfigDialog *dlg = new ConfigDialog(); +			dlg->runModal(); +			delete dlg; +			_globals->_events.setCursorFromFlag(); +			break; +		} + +		case Common::KEYCODE_F3: +			// F3 - Quit +			_globals->_game.quitGame(); +			event.handled = false; +			break; +			 +		case Common::KEYCODE_F4: +			// F4 - Restart +			_globals->_game.restartGame(); +			_globals->_events.setCursorFromFlag(); +			break; + +		case Common::KEYCODE_F7: +			// F7 - Restore +			_globals->_game.restoreGame(); +			_globals->_events.setCursorFromFlag(); +			break; + +		case Common::KEYCODE_F10: +			// F10 - Pause +			GfxDialog::setPalette(); +			MessageDialog::show(GAME_PAUSED_MSG, OK_BTN_STRING); +			_globals->_events.setCursorFromFlag(); +			break; + +		default: +			break; +		} + +		_globals->_events.setCursorFromFlag(); +	} + +	// Check for displaying right-click dialog +	if ((event.eventType == EVENT_BUTTON_DOWN) && (event.btnState == BTNSHIFT_RIGHT) &&  +			_globals->_player._uiEnabled) { +		RightClickDialog *dlg = new RightClickDialog(); +		dlg->execute(); +		delete dlg; + +		event.handled = true; +		return; +	} + +	// If there is an active scene, pass the event to it +	if (_globals->_sceneManager._scene) +		_globals->_sceneManager._scene->process(event); + +	// Separate check for F5 - Save key +	if (!event.handled) { +		if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_F5)) { +			// F5 - Save +			_globals->_game.saveGame(); +			event.handled = true; +			_globals->_events.setCursorFromFlag(); +		} + +		// Check for debugger +		if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_d) && +			(event.kbd.flags & KBD_CTRL)) { +			// Attach to the debugger +			_vm->_debugger->attach(); +			_vm->_debugger->onFrame(); +		} + +		// Mouse press handling +		if (_globals->_player._uiEnabled && (event.eventType == EVENT_BUTTON_DOWN) &&  +				!_globals->_sceneItems.empty()) { +			// Scan the item list to find one the mouse is within +			List<SceneItem *>::iterator i = _globals->_sceneItems.begin(); +			while ((i != _globals->_sceneItems.end()) && !(*i)->contains(event.mousePos)) +				++i; + +			if (i != _globals->_sceneItems.end()) { +				// Pass the action to the item +				(*i)->doAction(_globals->_events.getCursor()); +				event.handled = _globals->_events.getCursor() != CURSOR_WALK; + +				if (!_globals->_player._uiEnabled && !_globals->_player._canWalk && +						(_globals->_events.getCursor() != CURSOR_LOOK)) { +					_globals->_events.setCursor(CURSOR_WALK); +				} else if (_globals->_player._canWalk && (_globals->_events.getCursor() != CURSOR_LOOK)) { +					_globals->_events.setCursor(CURSOR_WALK); +				} else if (_globals->_player._uiEnabled && (_globals->_events.getCursor() != CURSOR_LOOK)) { +					_globals->_events.setCursor(CURSOR_USE); +				} +			} + +			// Handle player processing +			_globals->_player.process(event); +		} +	} +} + +void SceneHandler::dispatch() { +	// Handle game saving and loading +	if (_saveGameSlot != -1) { +		int saveSlot = _saveGameSlot; +		_saveGameSlot = -1; +		if (_saver->save(saveSlot, _saveName) != Common::kNoError) +			GUIErrorMessage(SAVE_ERROR_MSG); +	} +	if (_loadGameSlot != -1) { +		int loadSlot = _loadGameSlot; +		_loadGameSlot = -1; +		_saver->restore(loadSlot); +		_globals->_events.setCursorFromFlag(); +	} + +	_globals->_soundManager.dispatch(); +	_globals->_scenePalette.signalListeners(); + +	// Dispatch to any objects registered in the scene +	_globals->_sceneObjects->recurse(SceneHandler::handleListener); + +	// If a scene is active, then dispatch to it +	if (_globals->_sceneManager._scene)  +		_globals->_sceneManager._scene->dispatch(); + +	//TODO: Figure out purpose of the given list +	//_globals->_regions.forEach(SceneHandler::handleListener);	 + +	Event event; +	while (_globals->_events.getEvent(event)) +		process(event); + +	_globals->_sceneManager.checkScene(); +	_globals->_sceneObjects->draw(); + +	_vm->_debugger->onFrame(); + +	// Delay between frames +	_globals->_events.delay(_delayTicks); +} + +void SceneHandler::handleListener(EventHandler *obj) { +	obj->dispatch(); +} + +void SceneHandler::saveListener(Serialiser &ser) { +	warning("TODO: SceneHandler::saveListener"); +} + +/*--------------------------------------------------------------------------*/ + +void Game::execute() { +	// Main game loop +	bool activeFlag = false; +	do { +		// Process all currently atcive game handlers +		activeFlag = false; +		for (List<GameHandler *>::iterator i = _handlers.begin(); i != _handlers.end(); ++i) { +			GameHandler *gh = *i; +			if (gh->_lockCtr.getCtr() == 0) { +				gh->execute(); +				activeFlag = true; +			} +		} +	} while (activeFlag && !_vm->getEventManager()->shouldQuit()); +} + +void Game::restartGame() { +	if (MessageDialog::show(RESTART_MSG, CANCEL_BTN_STRING, RESTART_BTN_STRING) == 1) +		_globals->_game.restart(); +} + +void Game::saveGame() { +	if (_globals->getFlag(50)) +		MessageDialog::show(SAVING_NOT_ALLOWED_MSG, OK_BTN_STRING); +	else { +		// Show the save dialog +		handleSaveLoad(true, _globals->_sceneHandler._saveGameSlot, _globals->_sceneHandler._saveName); +	} +} + +void Game::restoreGame() { +	if (_globals->getFlag(50)) +		MessageDialog::show(RESTORING_NOT_ALLOWED_MSG, OK_BTN_STRING); +	else { +		// Show the load dialog +		handleSaveLoad(false, _globals->_sceneHandler._loadGameSlot, _globals->_sceneHandler._saveName); +	} +} + +void Game::quitGame() { +	if (MessageDialog::show(QUIT_CONFIRM_MSG, CANCEL_BTN_STRING, QUIT_BTN_STRING) == 1) +		_vm->quitGame(); +} + +void Game::handleSaveLoad(bool saveFlag, int &saveSlot, Common::String &saveName) { +	const EnginePlugin *plugin = 0; +	EngineMan.findGame(_vm->getGameId(), &plugin); +	GUI::SaveLoadChooser *dialog; +	if (saveFlag) +		dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save")); +	else +		dialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load")); + +	dialog->setSaveMode(saveFlag); + +	saveSlot = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); +	saveName = dialog->getResultString(); + +	delete dialog; +} + +void Game::restart() { +	_globals->_scenePalette.clearListeners(); +	_globals->_soundHandler.proc3(); +	 +	// Reset the flags +	_globals->reset(); +	_globals->setFlag(34); + +	// Clear save/load slots +	_globals->_sceneHandler._saveGameSlot = -1; +	_globals->_sceneHandler._loadGameSlot = -1; + +	_globals->_stripNum = 0; +	_globals->_events.setCursor(CURSOR_WALK); + +	// Reset item properties +	_globals->_inventory._stunner._sceneNumber = 1; +	_globals->_inventory._scanner._sceneNumber = 1; +	_globals->_inventory._stasisBox._sceneNumber = 5200; +	_globals->_inventory._infoDisk._sceneNumber = 40; +	_globals->_inventory._stasisNegator._sceneNumber = 0; +	_globals->_inventory._keyDevice._sceneNumber = 0; +	_globals->_inventory._medkit._sceneNumber = 2280; +	_globals->_inventory._ladder._sceneNumber = 4100; +	_globals->_inventory._rope._sceneNumber = 4150; +	_globals->_inventory._key._sceneNumber = 7700; +	_globals->_inventory._translator._sceneNumber = 2150; +	_globals->_inventory._paper._sceneNumber = 7700; +	_globals->_inventory._waldos._sceneNumber = 0; +	_globals->_inventory._ring._sceneNumber = 1; +	_globals->_inventory._stasisBox2._sceneNumber = 8100; +	_globals->_inventory._cloak._sceneNumber = 9850; +	_globals->_inventory._tunic._sceneNumber = 9450; +	_globals->_inventory._candle._sceneNumber = 9500; +	_globals->_inventory._straw._sceneNumber = 9400; +	_globals->_inventory._scimitar._sceneNumber = 9850; +	_globals->_inventory._sword._sceneNumber = 9850; +	_globals->_inventory._helmet._sceneNumber = 9500; +	_globals->_inventory._items._sceneNumber = 4300; +	_globals->_inventory._concentrator._sceneNumber = 4300; +	_globals->_inventory._nullifier._sceneNumber = 4300; +	_globals->_inventory._peg._sceneNumber = 4045; +	_globals->_inventory._vial._sceneNumber = 5100; +	_globals->_inventory._jacket._sceneNumber = 9850; +	_globals->_inventory._tunic2._sceneNumber = 9850; +	_globals->_inventory._bone._sceneNumber = 5300; +	_globals->_inventory._jar._sceneNumber = 7700; +	_globals->_inventory._emptyJar._sceneNumber = 7700; + +	// Change to the first game scene +	_globals->_sceneManager.changeScene(30); +} + +void Game::endGame(int resNum, int lineNum) { +	_globals->_events.setCursor(CURSOR_WALK); +	Common::String msg = _vm->_dataManager->getMessage(resNum, lineNum); +	bool savesExist = _saver->savegamesExist(); + +	if (!savesExist) { +		// No savegames exist, so prompt the user to restart or quit +		if (MessageDialog::show(msg, QUIT_BTN_STRING, RESTART_BTN_STRING) == 0) +			_vm->quitGame(); +		else +			restart(); +	} else { +		// Savegames exist, so prompt for Restore/Restart +		bool breakFlag; +		do { +			if (MessageDialog::show(msg, RESTART_BTN_STRING, RESTORE_BTN_STRING) == 0) { +				breakFlag = true; +			} else { +				handleSaveLoad(false, _globals->_sceneHandler._loadGameSlot, _globals->_sceneHandler._saveName); +				breakFlag = _globals->_sceneHandler._loadGameSlot > 0; +			} +		} while (!breakFlag); +	} + +	_globals->_events.setCursorFromFlag(); +} + +} // End of namespace tSage diff --git a/engines/tsage/core.h b/engines/tsage/core.h new file mode 100644 index 0000000000..3ef7585b62 --- /dev/null +++ b/engines/tsage/core.h @@ -0,0 +1,841 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/core.h $ + * $Id: core.h 227 2011-02-11 22:13:54Z dreammaster $ + * + */ + +#ifndef TSAGE_CORE_H +#define TSAGE_CORE_H + +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/error.h" +#include "common/list.h" +#include "common/rect.h" +#include "graphics/surface.h" +#include "tsage/events.h" +#include "tsage/graphics.h" +#include "tsage/resources.h" +#include "tsage/saveload.h" +#include "tsage/sound.h" + +namespace tSage { + +#define MAX_FLAGS 256 + +class EventHandler; +class SceneObject; +class SceneObjectList; +class ObjectMover; +class Action; +class Serialiser; + +class InvObject: public SavedObject { +public: +	int _sceneNumber; +	int _displayResNum; +	int _rlbNum; +	int _cursorNum; +	Rect _bounds; +	CursorType _cursorId; +	Common::String _description; +	int _iconResNum; +public: +	InvObject(int sceneNumber, int rlbNum, int cursorNum, CursorType cursorId, const Common::String description); + +	bool inInventory() const { return _sceneNumber == 1; } +	void setCursor(); + +	virtual Common::String getClassName() { return "InvObject"; } +	virtual void synchronise(Serialiser &s) { +		s.syncAsUint16LE(_sceneNumber); +	} +}; + +class InvObjectList: public SavedObject { +public: +	InvObject _stunner; +	InvObject _scanner; +	InvObject _stasisBox; +	InvObject _infoDisk; +	InvObject _stasisNegator; +	InvObject _keyDevice; +	InvObject _medkit; +	InvObject _ladder; +	InvObject _rope; +	InvObject _key; +	InvObject _translator; +	InvObject _ale; +	InvObject _paper; +	InvObject _waldos; +	InvObject _stasisBox2; +	InvObject _ring; +	InvObject _cloak; +	InvObject _tunic; +	InvObject _candle; +	InvObject _straw; +	InvObject _scimitar; +	InvObject _sword; +	InvObject _helmet; +	InvObject _items; +	InvObject _concentrator; +	InvObject _nullifier; +	InvObject _peg; +	InvObject _vial; +	InvObject _jacket; +	InvObject _tunic2; +	InvObject _bone; +	InvObject _jar; +	InvObject _emptyJar; + +	List<InvObject *> _itemList; +	InvObject *_selectedItem; +public: +	InvObjectList(); + +	virtual Common::String getClassName() { return "InvObjectList"; } +	virtual void synchronise(Serialiser &s); +}; + +/*--------------------------------------------------------------------------*/ + +/** + * Basic reference counter class + */ +class RefCounter: public Serialisable { +private: +	int _ctr; +public: +	RefCounter() { clear(); } +	virtual ~RefCounter() {} + +	RefCounter(int v) { _ctr = v; } + +	void clear() { _ctr = 0; } +	void setCtr(int v) { _ctr = v; } +	int decCtr() { +		if (_ctr > 0) --_ctr; +		return _ctr; +	} +	int incCtr() { return ++_ctr; } +	int getCtr() const { return _ctr; } + +	virtual void synchronise(Serialiser &s) { s.syncAsSint16LE(_ctr); } +}; + +class EventHandler: public SavedObject { +public: +	Action *_action; + +	EventHandler(): SavedObject() { _action = NULL; } +	virtual ~EventHandler() { destroy(); } + +	virtual void synchronise(Serialiser &s) { SYNC_POINTER(_action); } +	virtual Common::String getClassName() { return "EventHandler"; } +	virtual void postInit(SceneObjectList *OwnerList = NULL) {} +	virtual void remove() {} +	virtual void signal() {} +	virtual void process(Event &event) {} +	virtual void dispatch(); +	virtual void setAction(Action *action) { setAction(action, NULL); } +	virtual void setAction(Action *action, EventHandler *fmt, ...); +	virtual void destroy() {}; +}; + +class Action: public EventHandler { +public: +	EventHandler *_owner; +	int _actionIndex; +	int _delayFrames; +	uint32 _startFrame; +	int _field16; +	EventHandler *_fmt; + +	Action(); + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "Action"; } +	virtual void remove(); +	virtual void process(Event &event); +	virtual void dispatch(); +	virtual void attached(EventHandler *newOwner, EventHandler *fmt, va_list va); +	 +	void attach(EventHandler *newOwner, EventHandler *fmt, ...) { +		va_list va; +		va_start(va, fmt); +		attached(newOwner, fmt, va); +		va_end(va); +	} +	int getActionIndex() const { return _actionIndex; } +	void setActionIndex(int index) { _actionIndex = index; } +	void setDelay(int numFrames); +}; + +class ObjectMover: public EventHandler { +public: +	Common::Point _destPosition; +	Common::Point _moveDelta; +	Common::Point _moveSign; +	int _minorDiff; +	int _majorDiff; +	int _field1A; +	Action *_action; +	SceneObject *_sceneObject; +public: +	ObjectMover() { _action = NULL; _sceneObject = NULL; } +	virtual ~ObjectMover(); + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "ObjectMover"; } +	virtual void remove(); +	virtual void dispatch(); +	virtual void startMove(SceneObject *sceneObj, va_list va) {} +	virtual void setup(const Common::Point &destPos); +	virtual bool dontMove() const; +	virtual void endMove(); +}; + +class ObjectMover2: public ObjectMover { +public: +	SceneObject *_destObject; +	int _minArea; +	int _maxArea; +public: +	ObjectMover2(); +	virtual ~ObjectMover2() {} + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "ObjectMover2"; } +	virtual void dispatch(); +	virtual void startMove(SceneObject *sceneObj, va_list va); +	virtual void endMove(); +}; + +class ObjectMover3: public ObjectMover2 { +public: +	virtual Common::String getClassName() { return "ObjectMover3"; } +	virtual void dispatch(); +	virtual void startMove(SceneObject *sceneObj, va_list va); +	virtual void endMove(); +}; + +class NpcMover: public ObjectMover { +public: +	virtual Common::String getClassName() { return "NpcMover"; } +	virtual void startMove(SceneObject *sceneObj, va_list va); +}; + +#define MAX_ROUTE_SIZE 20 +#define ROUTE_END_VAL -20000 + +class RouteEnds { +public: +	Common::Point moveSrc; +	Common::Point moveDest; +}; + +class PlayerMover: public NpcMover { +private: +	void setDest(const Common::Point &destPos); +	void pathfind(Common::Point *routeList, Common::Point srcPos, Common::Point destPos, RouteEnds routeEnds); +	int regionIndexOf(const Common::Point &pt); +	int regionIndexOf(int xp, int yp) { return regionIndexOf(Common::Point(xp, yp)); } +	int findClosestRegion(Common::Point &pt, List<int> &indexList); +	int checkMover(Common::Point &srcPos, const Common::Point &destPos); +	void checkMovement2(const Common::Point &pt1, const Common::Point &pt2, int numSteps, Common::Point &ptOut); +	int proc1(int *routeList, int srcRegion, int destRegion, int &v); + +	static Common::Point *findLinePoint(RouteEnds *routeEnds, Common::Point *objPos, int length, Common::Point *outPos); +	static int findDistance(const Common::Point &pt1, const Common::Point &pt2); +	static bool sub_F8E5(const Common::Point &pt1, const Common::Point &pt2, const Common::Point &pt3, +		const Common::Point &pt4, Common::Point *ptOut = NULL); +public: +	Common::Point _finalDest; +	Common::Point _routeList[MAX_ROUTE_SIZE]; +	int _routeIndex; + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "PlayerMover"; } +	virtual void startMove(SceneObject *sceneObj, va_list va); +	virtual void endMove(); +}; + +/*--------------------------------------------------------------------------*/ + +class ScenePalette; + +class PaletteModifier: public SavedObject { +public: +	ScenePalette *_scenePalette; +	Action *_action; +public: +	PaletteModifier(); + +	virtual void synchronise(Serialiser &s) { +		SYNC_POINTER(_scenePalette); +		SYNC_POINTER(_action); +	} +	virtual void signal() = 0; +	virtual void remove() = 0; +}; + +class PaletteRotation: public PaletteModifier { +public: +	bool _disabled; +	int _delayFrames; +	int _delayCtr; +	uint32 _frameNumber; +	int _currIndex; +	int _start; +	int _end; +	int _rotationMode; +	int _duration; +	uint32 _palette[256]; +public: +	PaletteRotation(); + +	virtual Common::String getClassName() { return "PaletteRotation"; } +	virtual void synchronise(Serialiser &s); +	virtual void signal(); +	virtual void remove(); + +	void setDisabled(bool v) { _disabled = v; } +	void set(ScenePalette *palette, int start, int end, int rotationMode, int duration, Action *action); +	void setPalette(ScenePalette *palette, bool disabled); +	bool decDuration(); +	void setDelay(int amount); +}; + +enum FadeMode {FADEMODE_NONE = 0, FADEMODE_GRADUAL = 1, FADEMODE_IMMEDIATE = 2}; + +class ScenePalette: public SavedObject { +public: +	uint32 _palette[256]; +	GfxColours _colours; +	List<PaletteModifier *> _listeners; +	int _field412; + +	uint8 _redColour; +	uint8 _greenColour; +	uint8 _blueColour; +	uint8 _aquaColour; +	uint8 _purpleColour; +	uint8 _limeColour; +public: +	ScenePalette(); +	ScenePalette(int paletteNum); + +	bool loadPalette(int paletteNum); +	void refresh(); +	void setPalette(int index, int count); +	uint8 indexOf(uint r, uint g, uint b, int threshold = 0xffff); +	void getPalette(int start = 0, int count = 256); +	void signalListeners(); +	void clearListeners(); +	void fade(const byte *adjustData, bool fullAdjust, int percent); +	PaletteRotation *addRotation(int start, int end, int rotationMode, int duration = 0, Action *action = NULL); + +	static void changeBackground(const Rect &bounds, FadeMode fadeMode); + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "ScenePalette"; } +}; + +// DisplayParamType constant set. This must not be an enum +const int SET_WIDTH = 0; +const int SET_X = 1; +const int SET_Y = 2; +const int SET_FONT = 3; +const int SET_BG_COLOUR = 4; +const int SET_FG_COLOUR = 5; +const int SET_KEEP_ONSCREEN = 6; +const int SET_EXT_BGCOLOUR = 7; +const int SET_EXT_FGCOLOUR = 8; +const int SET_POS_MODE = 9; +const int SET_TEXT_MODE = 10; +const int LIST_END = -999; + +class SceneItem: public EventHandler { +public: +	Rect _bounds; +	Common::String _msg; +	int _fieldE, _field10; +	Common::Point _position; +	int _yDiff; +	int _sceneRegionId; +public: +	SceneItem(): EventHandler() { _msg = "Feature"; _action = NULL; } + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "SceneItem"; } +	virtual void remove(); +	virtual void destroy() {} +	virtual void startMover(CursorType action) { doAction(action); } +	virtual void doAction(int action); + +	bool contains(const Common::Point &pt); +	void setBounds(const Rect &newBounds) { _bounds = newBounds; } +	static void display(int resNum, int lineNum, ...); +	static void display2(int resNum, int lineNum) { +		display(resNum, lineNum, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +	} +}; + +class SceneHotspot: public SceneItem { +public: +	SceneHotspot(): SceneItem() {} + +	virtual Common::String getClassName() { return "SceneHotspot"; } +	virtual void doAction(int action); +}; + +enum AnimateMode {ANIM_MODE_NONE = 0, ANIM_MODE_1 = 1, ANIM_MODE_2 = 2, ANIM_MODE_3 = 3, +		ANIM_MODE_4 = 4, ANIM_MODE_5 = 5, ANIM_MODE_6 = 6, ANIM_MODE_7 = 7, ANIM_MODE_8 = 8}; + +class SceneObject; + +class Visage { +private: +	byte *_data; +public: +	int _resNum; +	int _rlbNum; +public: +	Visage(); +	~Visage(); + +	void setVisage(int resNum, int rlbNum = 9999); +	GfxSurface getFrame(int frameNum); +	int getFrameCount() const; +}; + +class SceneObjectWrapper: public EventHandler { +private: +	Visage _visageImages; +public: +	SceneObject *_sceneObject; +public: +	SceneObjectWrapper() { _sceneObject = NULL; } +	virtual ~SceneObjectWrapper() {} + +	void setSceneObject(SceneObject *so); + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "SceneObjectWrapper"; } +	virtual void dispatch(); +}; + +enum ObjectFlags {OBJFLAG_FIXED_PRIORITY = 1, OBJFLAG_NO_UPDATES = 2, OBJFLAG_ZOOMED = 4,  +	OBJFLAG_8 = 8, OBJFLAG_100 = 0x100, OBJFLAG_200 = 0x200, OBJFLAG_REMOVE = 0x400, OBJFLAG_800 = 0x800, +	OBJFLAG_1000 = 0x1000, OBJFLAG_PANE_0 = 0x4000, OBJFLAG_PANE_1 = 0x8000,  +	OBJFLAG_PANES = OBJFLAG_PANE_0 | OBJFLAG_PANE_1 +}; + +class SceneObject: public SceneHotspot { +private: +	Visage _visageImages; + +	int getNewFrame(); +	void animEnded(); +	int changeFrame(); +	bool isNoMover() const { return !_mover || (_field6E > 0); } +public: +	uint32 _updateStartFrame; +	uint32 _walkStartFrame; +	Common::Point _field2E; +	int _percent; +	int _priority; +	int _angle; +	uint32 _flags; +	int _xs, _xe; +	Rect _paneRects[2]; +	int _visage; +	SceneObjectWrapper *_objectWrapper; +	int _strip; +	AnimateMode  _animateMode; +	int _frame; +	int _endFrame; +	int _field68; +	int _frameChange; +	int _numFrames; +	int _field6E; +	EventHandler *_mover; +	Common::Point _moveDiff; +	int _field7A; +	Action *_endAction; +	uint32 _regionBitList; +public: +	SceneObject(); +	virtual ~SceneObject(); + +	void setPosition(const Common::Point &p, int yDiff = 0); +	void setStrip(int frameNum); +	void setStrip2(int frameNum); +	void setZoom(int percent); +	void changeZoom(int percent); +	void setFrame(int frameNum); +	void setFrame2(int frameNum); +	void setPriority(int priority); +	void setPriority2(int priority); +	void setVisage(int visage); +	void setObjectWrapper(SceneObjectWrapper *objWrapper); +	void addMover(ObjectMover *mover, ...); +	void getHorizBounds(); +	int checkRegion(const Common::Point &pt); +	void animate(AnimateMode animMode, ...); +	SceneObject *clone() const; +	void checkAngle(const SceneObject *obj); +	void flag100(); +	void unflag100(); +	int getSpliceArea(const SceneObject *obj); +	int getFrameCount(); + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "SceneObject"; } +	virtual void postInit(SceneObjectList *OwnerList = NULL); +	virtual void remove(); +	virtual void process(Event &event) { event.handled = true; } +	virtual void dispatch(); +	virtual void calcAngle(const Common::Point &pt); +	virtual void removeObject(); +	virtual GfxSurface getFrame(); +	virtual void reposition(); +	virtual void draw(); +	virtual void proc19() {} +	virtual void updateScreen(); +}; + +class SceneText: public SceneObject { +public: +	int _fontNumber; +	int _width; +	TextAlign _textMode; +	int _colour1; +	int _colour2; +	int _colour3; +	GfxSurface _textSurface; +public: +	SceneText(); +	~SceneText(); + +	void setup(const Common::String &msg); + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "SceneText"; } +	virtual GfxSurface getFrame() { return _textSurface; } +}; + +class Player: public SceneObject { +public: +	bool _canWalk; +	bool _uiEnabled; +	int _field8C; +public: +	Player(): SceneObject() {} + +	virtual Common::String getClassName() { return "Player"; } +	virtual void synchronise(Serialiser &s); +	virtual void postInit(SceneObjectList *OwnerList = NULL); +	virtual void process(Event &event); + +	void disableControl(); +	void enableControl(); +}; + +/*--------------------------------------------------------------------------*/ + +class LineSliceSet { +public: +	Common::Array<LineSlice> items; + +	void load(int size, const byte *srcP) { +		for (int i = 0; i < size; ++i, srcP += 4) +			items.push_back(LineSlice(READ_LE_UINT16(srcP), READ_LE_UINT16(srcP + 2))); +	} +	void load2(int size, ...) { +		va_list va; +		va_start(va, size); + +		while (size-- > 0) { +			int xs = va_arg(va, int); +			int xe = va_arg(va, int); +			items.push_back(LineSlice(xs, xe)); +		}			 +	} + +	void add(LineSlice &slice) { items.push_back(slice); } +	void add(int xs, int xe) { items.push_back(LineSlice(xs, xe)); } +	static LineSliceSet mergeSlices(const LineSliceSet &set1, LineSliceSet &set2); +}; + +class Region { +public: +	int _regionSize; +	int _regionId; +	Rect _bounds; +	Common::Array<LineSliceSet> _ySlices; +public: +	Region() { _regionSize = 0; _regionId = 0; } +	Region(int resNum, int rlbNum, ResourceType ctlType = RES_CONTROL); + +	bool contains(const Common::Point &pt); +	bool empty() const; +	void clear(); +	void setRect(const Rect &r); +	void setRect(int xs, int ys, int xe, int ye); +	const LineSliceSet &getLineSlices(int yp); +	LineSliceSet sectPoints(int yp, const LineSliceSet &sliceSet); +	void draw(); +	void uniteLine(int yp, LineSliceSet &sliceSet); + + +	static LineSliceSet mergeSlices(const LineSliceSet &set1, const LineSliceSet &set2); +}; + +class SceneRegions: public List<Region> { +public: +	void load(int sceneNum); + +	int indexOf(const Common::Point &pt); +}; + +class SceneObjectList: public SavedObject { +private: +	void checkIntersection(Common::Array<SceneObject *> &ObjList, uint ObjIndex, int PaneNum); +	void sortList(Common::Array<SceneObject *> &ObjList); + +	List<SceneObject *> _objList; +public: +	SceneObjectList() {} + +	virtual Common::String getClassName() { return "SceneObjectList"; } +	virtual void synchronise(Serialiser &s); + +	void draw(); +	void activate(); +	static void deactivate(); + +	typedef void (*EventHandlerFn)(EventHandler *fn); +	void recurse(EventHandlerFn Fn) { +		// Loop through each object +		for (List<SceneObject *>::iterator i = _objList.begin(); i != _objList.end(); ) { +			SceneObject *o = *i; +			++i; +			Fn(o); +		} +	} +	List<SceneObject *>::iterator begin() { return _objList.begin(); } +	List<SceneObject *>::iterator end() { return _objList.end(); } +	bool contains(SceneObject *sceneObj) { return _objList.contains(sceneObj); } +	void push_back(SceneObject *sceneObj) { _objList.push_back(sceneObj); } +	void push_front(SceneObject *sceneObj) { _objList.push_front(sceneObj); } +	void remove(SceneObject *sceneObj) { _objList.remove(sceneObj); } +}; + +class ScenePriorities: public List<Region> { +public: +	int _resNum; +	int _field14; +	int _field16; +	Region _defaultPriorityRegion; +public: +	void load(int resNum); + +	Region *find(int priority); +}; + +/*--------------------------------------------------------------------------*/ + +class GameSoundHandler { +public: +	void proc1() { +		warning("TODO: GameSoundHandler::proc1"); +	} +	void proc5(int v) { +		warning("TODO: GameSoundHandler::proc5"); +	} +}; + +class SoundHandler: public EventHandler { +public: +	GameSoundHandler _sound; +public: +	SoundHandler() {} + +	void startSound(int soundNum, Action *action = NULL, int volume = 127) { +		warning("TODO: SoundHandler::startSound"); +	} +	void proc1(Action *action) { +		warning("TODO: SoundHandler::proc1"); +	} +	void proc2(int v) { +		warning("TODO: SoundHandler::proc2"); +	} +	void proc3() { +		warning("TODO: SoundHandler::proc5"); +	} +	void proc4() { +		_sound.proc1(); +	} +	void proc5(int v) { +		_sound.proc5(v); +	} + +	virtual Common::String getClassName() { return "SoundHandler"; } +}; + +/*--------------------------------------------------------------------------*/ + +class SceneItemList: public List<SceneItem *> { +public: +	void addItems(SceneItem *first, ...); +}; + +/*--------------------------------------------------------------------------*/ + +class RegionSupportRec { +public: +	int _yp; +	int _xp; +	int _xDiff; +	int _yDiff; +	int _xDirection; +	int _halfDiff; +	int _yDiff2; + +	void process(); +}; + +#define PROCESS_LIST_SIZE 100 + +class WalkRegion: public Region { +private: +	static RegionSupportRec _processList[PROCESS_LIST_SIZE]; +	void loadProcessList(byte *dataP, int dataSize, int &dataIndex, int ®ionHeight); +	int process1(int idx, byte *dataP, int dataSize); +	void process2(int dataIndex, int x1, int y1, int x2, int y2); +	void process3(int yp, int dataCount, int &idx1, int &idx2); +	void process4(int yp, int idx1, int idx2, int &count); +	void process5(int idx1, int idx2); +	void loadRecords(int yp, int size, int processIndex); +	void process6(RegionSupportRec &rec); +public: +	Common::Point _pt; +	int _idxListIndex; +	int _idxList2Index; +public: +	void loadRegion(byte *dataP, int size); +}; + +class WRField18 { +public: +	Common::Point _pt1, _pt2; +	int _v; +public: +	void load(byte *data); +}; + +class WalkRegions { +public: +	int _resNum; +	RouteEnds _routeEnds; +	Common::Array<WalkRegion> _regionList; +	Common::Array<WRField18> _field18; +	Common::Array<int> _idxList; +	Common::Array<int> _idxList2; +public: +	WalkRegions() { _resNum = -1; } + +	void clear() { +		_regionList.clear(); +		_field18.clear(); +	} +	void load(int sceneNum); +	int indexOf(const Common::Point &pt, List<int> *indexList = NULL); +	WalkRegion &operator[](int idx) { +		assert((idx >= 1) && (idx <= (int)_regionList.size())); +		return _regionList[idx - 1]; +	} +}; + +/*--------------------------------------------------------------------------*/ + +class GameHandler: public EventHandler { +public: +	RefCounter _lockCtr; +	RefCounter _waitCtr; +	int _nextWaitCtr; +	int _field14; +public: +	GameHandler();  +	virtual ~GameHandler(); +	void execute(); + +	virtual void synchronise(Serialiser &s); +	virtual Common::String getClassName() { return "GameHandler"; } +	virtual void postInit(SceneObjectList *OwnerList = NULL) {} +	virtual void dispatch() {} +}; + +class SceneHandler: public GameHandler { +public: +	int _saveGameSlot; +	int _loadGameSlot; +	int _delayTicks; +	Common::String _saveName; +public: +	SceneHandler(); +	void registerHandler(); + +	virtual Common::String getClassName() { return "SceneHandler"; } +	virtual void postInit(SceneObjectList *OwnerList = NULL); +	virtual void process(Event &event); +	virtual void dispatch(); + +	static void handleListener(EventHandler *obj); +	static void saveListener(Serialiser &ser); +}; + +/*--------------------------------------------------------------------------*/ + +class Game { +private: +	List<GameHandler *> _handlers; + +	static bool notLockedFn(GameHandler *g); +	void restart(); +	void handleSaveLoad(bool saveFlag, int &saveSlot, Common::String &saveName); +public: +	void addHandler(GameHandler *entry) { _handlers.push_back(entry); } +	void removeHandler(GameHandler *entry) { _handlers.remove(entry); } + +	void execute(); +	void restartGame(); +	void saveGame(); +	void restoreGame(); +	void quitGame(); +	void endGame(int resNum, int lineNum); +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/debugger.cpp b/engines/tsage/debugger.cpp new file mode 100644 index 0000000000..ff3f6e3031 --- /dev/null +++ b/engines/tsage/debugger.cpp @@ -0,0 +1,109 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/debugger.cpp $ + * $Id: debugger.cpp 223 2011-02-09 13:03:31Z dreammaster $ + * + */ + +#include "tsage/debugger.h" +#include "common/config-manager.h" +#include "common/endian.h" +#include "tsage/globals.h" +#include "tsage/graphics.h" + + +namespace tSage { + +Debugger::Debugger(): GUI::Debugger() { +	DCmd_Register("continue",		WRAP_METHOD(Debugger, Cmd_Exit)); +	DCmd_Register("scene",			WRAP_METHOD(Debugger, Cmd_Scene)); +	DCmd_Register("walk_regions",	WRAP_METHOD(Debugger, Cmd_WalkRegions)); +} + +static int strToInt(const char *s) { +	if (!*s) +		// No string at all +		return 0; +	else if (toupper(s[strlen(s) - 1]) != 'H') +		// Standard decimal string +		return atoi(s); + +	// Hexadecimal string +	uint tmp = 0; +	int read = sscanf(s, "%xh", &tmp); +	if (read < 1) +		error("strToInt failed on string \"%s\"", s); +	return (int)tmp; +} + +/** + * This command loads up the specified new scene number + */ +bool Debugger::Cmd_Scene(int argc, const char **argv) { +	if (argc < 2) { +		DebugPrintf("Usage: %s <scene number> [prior scene #]\n", argv[0]); +		return true; +	} else { +		if (argc == 3) +			_globals->_sceneManager._sceneNumber = strToInt(argv[2]); + +		_globals->_sceneManager.changeScene(strToInt(argv[1]));		 +		return false; +	} +} + +/** + * This command draws the walk regions onto the screen + */ +bool Debugger::Cmd_WalkRegions(int argc, const char **argv) { +	if (argc != 1) { +		DebugPrintf("USage: %s\n", argv[0]); +		return true; +	} + +	// Colour index to use for the first walk region +	int colour = 16;	 + +	// Lock the background surface for access +	Graphics::Surface destSurface = _globals->_sceneManager._scene->_backSurface.lockSurface(); + +	// Loop through drawing each walk region in a different colour to the background surface +	for (uint regionIndex = 0; regionIndex < _globals->_walkRegions._regionList.size(); ++regionIndex, ++colour) { +		WalkRegion &wr = _globals->_walkRegions._regionList[regionIndex]; + +		for (int yp = wr._bounds.top; yp < wr._bounds.bottom; ++yp) { +			LineSliceSet sliceSet = wr.getLineSlices(yp); + +			for (uint idx = 0; idx < sliceSet.items.size(); ++idx) +				destSurface.hLine(sliceSet.items[idx].xs, yp, sliceSet.items[idx].xe, colour); +		} +	} + +	// Release the surface +	_globals->_sceneManager._scene->_backSurface.unlockSurface(); + +	// Mark the scene as requiring a full redraw +	_globals->_paneRefreshFlag[0] = 2; + +	return false; +} + +} // End of namespace tSage diff --git a/engines/tsage/debugger.h b/engines/tsage/debugger.h new file mode 100644 index 0000000000..94f4babc62 --- /dev/null +++ b/engines/tsage/debugger.h @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/debugger.h $ + * $Id: debugger.h 176 2011-01-25 11:33:33Z dreammaster $ + * + */ + +#ifndef TSAGE_DEBUGGER_H +#define TSAGE_DEBUGGER_H + +#include "common/scummsys.h" +#include "gui/debugger.h" + +namespace tSage { + +class Debugger : public GUI::Debugger { +public: +	Debugger(); +	virtual ~Debugger() {}  // we need this for __SYMBIAN32__ archaic gcc/UIQ + +protected: +	bool Cmd_Scene(int argc, const char **argv); +	bool Cmd_WalkRegions(int argc, const char **argv); +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp new file mode 100644 index 0000000000..421c5ff9df --- /dev/null +++ b/engines/tsage/detection.cpp @@ -0,0 +1,221 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/detection.cpp $ + * $Id: detection.cpp 209 2011-02-06 00:46:36Z dreammaster $ + * + */ + +#include "common/config-manager.h" +#include "common/system.h" +#include "common/savefile.h" + +#include "engines/advancedDetector.h" + +#include "base/plugins.h" + +#include "tsage/tsage.h" + +static const PlainGameDescriptor TSAgeGameTitles[] = { +	{ "tsage", "Unknown Tsunami TSAGE-based Game" }, +	{ "ring", "Ringworld: Revenge of the Patriarch" }, +	{ "blue", "Blue Force" }, +	{ 0, 0 } +}; + +namespace tSage { + +static const ADGameDescription TSAgeGameDescriptions[] = { +	// Ringworld English CD version +	{  +		"ring", +		"CD", +		AD_ENTRY1s("ring.rlb", "466f0e6492d9d0f34d35c5cd088de90f", 37847618), +		Common::EN_ANY, +		Common::kPlatformPC, +		ADGF_NO_FLAGS, +		Common::GUIO_NONE +	}, +	// Ringworld English Floppy version +	{ +		"ring", +		"Floppy", +		AD_ENTRY1s("ring.rlb", "61f78f68a56832ae95fe06748c403234", 8438770), +		Common::EN_ANY, +		Common::kPlatformPC, +		ADGF_NO_FLAGS, +		Common::GUIO_NONE +	}, +	// Blue Force +	{ +		"blue", +		"", +		AD_ENTRY1s("blue.rlb", "467da43c848cc0e800b547c59d84ccb1", 10032614), +		Common::EN_ANY, +		Common::kPlatformPC, +		ADGF_NO_FLAGS, +		Common::GUIO_NONE +	}, + +	AD_TABLE_END_MARKER, +}; + +const char *TSageEngine::getGameId() const { +	return _gameDescription->gameid; +} + +} // End of namespace tSage + +static const ADGameDescription TSAgeGameGeneric[] = { +	{"tsage", 0, +		AD_ENTRY1("tsage.rlb", NULL), +		Common::UNK_LANG, +		Common::kPlatformUnknown, +		0, +		Common::GUIO_NONE +	}, +	AD_TABLE_END_MARKER +}; + +static const ADFileBasedFallback TSAgeGameFallback[] = { +	{(const void*)&TSAgeGameGeneric[0], {"ring.rlb", NULL} }, +	{(const void*)&TSAgeGameGeneric[0], {"blue.rlb", NULL} }, +	{0, {NULL}} +}; + +static const ADParams detectionParams = { +	(const byte *)tSage::TSAgeGameDescriptions, +	sizeof(ADGameDescription), +	0, +	TSAgeGameTitles, +	0, +	"tsage", +	TSAgeGameFallback, +	kADFlagPrintWarningOnFileBasedFallback, +	Common::GUIO_NONE, +	0, +	NULL +}; + +#define MAX_SAVES 100 + +class TSageMetaEngine : public AdvancedMetaEngine { +public: +	TSageMetaEngine() : AdvancedMetaEngine(detectionParams) { +	} + +	virtual const char *getName() const { +		return "TsAGE Engine"; +	} + +	virtual const char *getOriginalCopyright() const { +		return "(c) Tsunami Media"; +	} + +	virtual bool hasFeature(MetaEngineFeature f) const { +		switch (f) { +		case kSupportsListSaves: +		case kSupportsDeleteSave: +		case kSupportsLoadingDuringStartup: +		case kSavesSupportMetaInfo: +		case kSavesSupportThumbnail: +		case kSavesSupportCreationDate: +		case kSavesSupportPlayTime: +			return true; +		default: +			return false; +		} +	} + +	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { +		if (desc) { +			*engine = new tSage::TSageEngine(syst, desc); +		} +		return desc != 0; +	} + +	static Common::String generateGameStateFileName(const char *target, int slot) { +		return Common::String::format("%s.%03d", target, slot); +	} + +	virtual SaveStateList listSaves(const char *target) const { +		Common::String pattern = target; +		pattern += ".*"; + +		Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern); +		tSage::tSageSavegameHeader header; + +		SaveStateList saveList; +		for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { +			int slot; +			const char *ext = strrchr(file->c_str(), '.'); +			if (ext && (slot = atoi(ext + 1)) >= 0 && slot < MAX_SAVES) { +				Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); + +				if (in) { +					if (tSage::Saver::readSavegameHeader(in, header)) { +						saveList.push_back(SaveStateDescriptor(slot, header.saveName)); +						delete header.thumbnail; +					} + +					delete in; +				} +			} +		} + +		return saveList; +	} + +	virtual int getMaximumSaveSlot() const { +		return MAX_SAVES - 1; +	} + +	virtual void removeSaveState(const char *target, int slot) const { +		Common::String filename = Common::String::format("%s.%03d", target, slot); +		g_system->getSavefileManager()->removeSavefile(filename); +	} + +	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const { +		Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading( +			generateGameStateFileName(target, slot)); +		assert(f); + +		tSage::tSageSavegameHeader header; +		tSage::Saver::readSavegameHeader(f, header); +		delete f; + +		// Create the return descriptor +		SaveStateDescriptor desc(slot, header.saveName); +		desc.setDeletableFlag(true); +		desc.setWriteProtectedFlag(false); +		desc.setThumbnail(header.thumbnail); +		desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); +		desc.setSaveTime(header.saveHour, header.saveMinutes); +		desc.setPlayTime(header.totalFrames * GAME_FRAME_TIME); + +		return desc; +	} +}; + +#if PLUGIN_ENABLED_DYNAMIC(TSAGE) +REGISTER_PLUGIN_DYNAMIC(TSAGE, PLUGIN_TYPE_ENGINE, TSageMetaEngine); +#else +REGISTER_PLUGIN_STATIC(TSAGE, PLUGIN_TYPE_ENGINE, TSageMetaEngine); +#endif diff --git a/engines/tsage/dialogs.cpp b/engines/tsage/dialogs.cpp new file mode 100644 index 0000000000..4f7bfec8b5 --- /dev/null +++ b/engines/tsage/dialogs.cpp @@ -0,0 +1,597 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/dialogs.cpp $ + * $Id: dialogs.cpp 215 2011-02-07 12:06:13Z dreammaster $ + * + */ + +#include "common/translation.h" +#include "tsage/tsage.h" +#include "tsage/core.h" +#include "tsage/dialogs.h" +#include "tsage/graphics.h" +#include "tsage/core.h" +#include "tsage/staticres.h" +#include "tsage/globals.h" + +namespace tSage { + +/*--------------------------------------------------------------------------*/ + +/** + * This dialog class provides a simple message display with support for either one or two buttons. + */ +MessageDialog::MessageDialog(const Common::String &message, const Common::String &btn1Message,  +							 const Common::String &btn2Message): GfxDialog() { +	// Set up the message +	addElements(&_msg, &_btn1, NULL); + +	_msg.set(message, 200, ALIGN_LEFT); +	_btn1._bounds.moveTo(_msg._bounds.left, _msg._bounds.bottom + 2); +	_defaultButton = &_btn1; + +	// Set up the first button +	_btn1.setText(btn1Message); +	_btn1._bounds.moveTo(_msg._bounds.right - _btn1._bounds.width(), _msg._bounds.bottom); + +	if (!btn2Message.empty()) { +		// Set up the second button +		_defaultButton = &_btn2; +		add(&_btn2); +		_btn2.setText(btn2Message); +		_btn2._bounds.moveTo(_msg._bounds.right - _btn2._bounds.width(), _msg._bounds.bottom); +		_btn1._bounds.translate(-(_btn2._bounds.width() + 4), 0); +	} + +	// Do post setup for the dialog +	setDefaults(); + +	// Set the dialog's centre +	setCentre(_globals->_dialogCentre.x, _globals->_dialogCentre.y); +} + +int MessageDialog::show(const Common::String &message, const Common::String &btn1Message, const Common::String &btn2Message) { +	// Ensure that the cursor is the arrow +	CursorType currentCursor = _globals->_events.getCursor(); +	if (currentCursor != CURSOR_ARROW) +		_globals->_events.setCursor(CURSOR_ARROW); + +	int result = show2(message, btn1Message, btn2Message); + +	// If the cursor was changed, change it back +	if (currentCursor != CURSOR_ARROW) +		_globals->_events.setCursor(currentCursor); + +	return result; +} + +int MessageDialog::show2(const Common::String &message, const Common::String &btn1Message, const Common::String &btn2Message) { +	MessageDialog *dlg = new MessageDialog(message, btn1Message, btn2Message); +	dlg->draw(); + +	GfxButton *selectedButton = dlg->execute(); +	int result =  (selectedButton == &dlg->_btn1) ? 0 : 1; + +	delete dlg; +	return result; +} + + +/*--------------------------------------------------------------------------*/ + +ConfigDialog::ConfigDialog(): GUI::OptionsDialog("", "GlobalConfig") { +	// +	// Sound controllers +	// + +	addVolumeControls(this, "GlobalConfig."); +	setVolumeSettingsState(true); // could disable controls by GUI options + +	// +	// Add the buttons +	// + +	new GUI::ButtonWidget(this, "GlobalConfig.Ok", _("~O~K"), 0, GUI::kOKCmd); +	new GUI::ButtonWidget(this, "GlobalConfig.Cancel", _("~C~ancel"), 0, GUI::kCloseCmd); +} + +/*--------------------------------------------------------------------------*/ + +#define BUTTON_WIDTH 28 +#define BUTTON_HEIGHT 29 + +RightClickButton::RightClickButton(int buttonIndex, int xp, int yp): GfxButton() { +	_buttonIndex = buttonIndex; +	this->_bounds.left = xp; +	this->_bounds.top = yp; +	this->_bounds.setWidth(BUTTON_WIDTH); +	this->_bounds.setHeight(BUTTON_HEIGHT); +	_savedButton = NULL; +} + +void RightClickButton::highlight() { +	if (_savedButton) { +		// Button was previously highlighted, so de-highlight by restoring saved area +		_globals->gfxManager().copyFrom(*_savedButton, _bounds.left, _bounds.top); +		delete _savedButton; +		_savedButton = NULL; +	} else { +		// Highlight button by getting the needed highlighted image resource +		_savedButton = Surface_getArea(_globals->gfxManager().getSurface(), _bounds); + +		uint size; +		byte *imgData = _vm->_dataManager->getSubResource(7, 2, _buttonIndex, &size); + +		GfxSurface btnSelected = surfaceFromRes(imgData); +		_globals->gfxManager().copyFrom(btnSelected, _bounds.left, _bounds.top); +	 +		DEALLOCATE(imgData); +	} +} + +/*--------------------------------------------------------------------------*/ + +/** + * This dialog implements the right-click dialog + */ +RightClickDialog::RightClickDialog(): GfxDialog(), +		_walkButton(1, 48, 12), _lookButton(2, 31, 29), _useButton(3, 65, 29), +		_talkButton(4, 14, 47), _inventoryButton(5, 48, 47), _optionsButton(6, 83, 47) { +	Rect rectArea, dialogRect; + +	// Set the palette and change the cursor +	_gfxManager.setDialogPalette(); +	_globals->_events.setCursor(CURSOR_ARROW); + +	// Get the dialog image +	_surface = surfaceFromRes(7, 1, 1); + +	// Set the dialog position +	dialogRect.resize(_surface, 0, 0, 100); +	dialogRect.centre(_globals->_events._mousePos.x, _globals->_events._mousePos.y); + +	// Ensure the dialog will be entirely on-screen +	Rect screenRect = _globals->gfxManager()._bounds; +	screenRect.collapse(4, 4); +	dialogRect.contain(screenRect); +	 +	_bounds = dialogRect; +	_gfxManager._bounds = _bounds; + +	_highlightedButton = NULL; +	_selectedAction = -1; +} + +RightClickDialog::~RightClickDialog() { +} + +RightClickButton *RightClickDialog::findButton(const Common::Point &pt) { +	RightClickButton *btnList[] = {  &_walkButton, &_lookButton, &_useButton, &_talkButton, &_inventoryButton, &_optionsButton }; + +	for (int i = 0; i < 6; ++i) { +		btnList[i]->_owner = this; + +		if (btnList[i]->_bounds.contains(pt)) +			return btnList[i]; +	} + +	return NULL; +} + +void RightClickDialog::draw() { +	// Save the covered background area +	_savedArea = Surface_getArea(_globals->_gfxManagerInstance.getSurface(), _bounds); + +	// Draw the dialog image +	_globals->gfxManager().copyFrom(_surface, _bounds.left, _bounds.top); +} + +bool RightClickDialog::process(Event &event) { +	switch (event.eventType) { +	case EVENT_MOUSE_MOVE: { +		// Check whether a button is highlighted +		RightClickButton *btn = findButton(event.mousePos); + +		if (btn != _highlightedButton) { +			// De-highlight any previously selected button +			if (_highlightedButton) { +				_highlightedButton->highlight(); +				_highlightedButton = NULL; +			} +			if (btn) { +				// Highlight the new button +				btn->highlight(); +				_highlightedButton = btn; +			} +		} +		event.handled = true; +		return true; +	} + +	case EVENT_BUTTON_DOWN: +		// If a button is highlighted, then flag the selected button index +		if (_highlightedButton) +			_selectedAction = _highlightedButton->_buttonIndex; +		else +			_selectedAction = _lookButton._buttonIndex; +		event.handled = true; +		return true; + +	default: +		break; +	} + +	return false; +} + +void RightClickDialog::execute() { +	// Draw the dialog +	draw(); + +	// Dialog event handler loop +	_gfxManager.activate(); + +	while (!_vm->getEventManager()->shouldQuit() && (_selectedAction == -1)) { +		Event evt; +		while (_globals->_events.getEvent(evt, EVENT_MOUSE_MOVE | EVENT_BUTTON_DOWN)) { +			evt.mousePos.x -= _bounds.left; +			evt.mousePos.y -= _bounds.top; + +			process(evt); +		} + +		g_system->delayMillis(10); +		g_system->updateScreen(); +	} + +	// Execute the specified action +	switch (_selectedAction) { +	case 1: +		// Look action +		_globals->_events.setCursor(CURSOR_LOOK); +		break; +	case 2: +		// Walk action +		_globals->_events.setCursor(CURSOR_WALK); +		break; +	case 3: +		// Use cursor +		_globals->_events.setCursor(CURSOR_USE); +		break; +	case 4: +		// Talk cursor +		_globals->_events.setCursor(CURSOR_TALK); +		break; +	case 5: +		// Inventory dialog +		InventoryDialog::show(); +		break; +	case 6: +		// Dialog options +		OptionsDialog::show(); +		break; +	} + +	_gfxManager.deactivate(); +} + +/*--------------------------------------------------------------------------*/ + +void ModalDialog::draw() { +	// Set the palette for use in the dialog +	setPalette(); + +	// Make a backup copy of the area the dialog will occupy +	Rect tempRect = _bounds; +	tempRect.collapse(-10, -10); +	_savedArea = Surface_getArea(_globals->_gfxManagerInstance.getSurface(), tempRect); + +	_gfxManager.activate(); + +	// Fill in the contents of the entire dialog +	_gfxManager._bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); +	drawFrame(); + +	// Draw each element in the dialog in order +	GfxElementList::iterator i; +	for (i = _elements.begin(); i != _elements.end(); ++i) { +		(*i)->draw(); +	} + +	_gfxManager.deactivate(); +} + +void ModalDialog::drawFrame() { +	Rect origRect = _bounds; +	_bounds.collapse(-10, -10); + +	// Fill the dialog area +	_globals->gfxManager().fillRect(origRect, 54); + +	// Draw top line +	GfxSurface surface = surfaceFromRes(8, 1, 7); +	for (int xp = _bounds.left + 10; xp < (_bounds.right - 20); xp += 10) +		surface.draw(Common::Point(xp, _bounds.top)); +	surface.draw(Common::Point(_bounds.right - 20, _bounds.top)); +	 +	surface = surfaceFromRes(8, 1, 1); +	surface.draw(Common::Point(_bounds.left, _bounds.top)); + +	surface = surfaceFromRes(8, 1, 4); +	surface.draw(Common::Point(_bounds.right - 10, _bounds.top)); + +	// Draw vertical edges +	surface = surfaceFromRes(8, 1, 2); +	for (int yp = _bounds.top + 10; yp < (_bounds.bottom - 20); yp += 10) +		surface.draw(Common::Point(_bounds.left, yp)); +	surface.draw(Common::Point(_bounds.left, _bounds.bottom - 20)); + +	surface = surfaceFromRes(8, 1, 5); +	for (int yp = _bounds.top + 10; yp < (_bounds.bottom - 20); yp += 10) +		surface.draw(Common::Point(_bounds.right - 10, yp)); +	surface.draw(Common::Point(_bounds.right - 10, _bounds.bottom - 20)); + +	// Draw bottom line +	surface = surfaceFromRes(8, 1, 8); +	for (int xp = _bounds.left + 10; xp < (_bounds.right - 20); xp += 10) +		surface.draw(Common::Point(xp, _bounds.bottom - 10)); +	surface.draw(Common::Point(_bounds.right - 20, _bounds.bottom - 10)); + +	surface = surfaceFromRes(8, 1, 3); +	surface.draw(Common::Point(_bounds.left, _bounds.bottom - 10)); + +	surface = surfaceFromRes(8, 1, 6); +	surface.draw(Common::Point(_bounds.right - 10, _bounds.bottom - 10)); + +	// Set the dialog's manager bounds +	_gfxManager._bounds = origRect; +} + +/*--------------------------------------------------------------------------*/ + +bool GfxInvImage::process(Event &event) { +	if (!event.handled && (event.eventType == EVENT_BUTTON_DOWN)) { +		event.handled = _bounds.contains(event.mousePos); +		return event.handled; +	} + +	return false; +} + +/*--------------------------------------------------------------------------*/ + +void InventoryDialog::show(bool allFlag) { +	if (!allFlag) { +		// Determine how many items are in the player's inventory +		int itemCount = 0; +		List<InvObject *>::iterator i; +		for (i = _globals->_inventory._itemList.begin(); i != _globals->_inventory._itemList.end(); ++i) { +			if ((*i)->inInventory()) +				++itemCount; +		} + +		if (itemCount == 0) { +			MessageDialog::show(INV_EMPTY_MSG, OK_BTN_STRING); +			return; +		} +	} + +	InventoryDialog *dlg = new InventoryDialog(allFlag); +	dlg->draw(); +	dlg->execute(); +	delete dlg; +} + +InventoryDialog::InventoryDialog(bool allFlag) { +	// Determine the maximum size of the image of any item in the player's inventory +	int imgWidth = 0, imgHeight = 0; + +	List<InvObject *>::iterator i; +	for (i = _globals->_inventory._itemList.begin(); i != _globals->_inventory._itemList.end(); ++i) { +		InvObject *invObject = *i; +		if (allFlag || invObject->inInventory()) { +			// Get the image for the item +			GfxSurface itemSurface = surfaceFromRes(invObject->_displayResNum, invObject->_rlbNum, invObject->_cursorNum); + +			// Maintain the dimensions of the largest item image +			imgWidth = MAX(imgWidth, (int)itemSurface.getBounds().width()); +			imgHeight = MAX(imgHeight, (int)itemSurface.getBounds().height()); + +			// Add the item to the display list +			_images.push_back(GfxInvImage()); +			_images[_images.size() - 1].setDetails(invObject->_displayResNum, invObject->_rlbNum, invObject->_cursorNum); +			_images[_images.size() - 1]._invObject = invObject; +			add(&_images[_images.size() - 1]); +		} +	} +	assert(_images.size() > 0); + +	// Figure out the number of columns/rows to show all the items +	int cellsSize = 3; +	while ((cellsSize * cellsSize) < (int)_images.size()) +		++cellsSize; + +	// Set the position of each inventory item to be displayed +	int cellX = 0; +	Common::Point pt(0, 0); + +	for (uint idx = 0; idx < _images.size(); ++idx) { +		if (cellX == cellsSize) { +			// Move to the start of the next line +			pt.x = 0; +			pt.y += imgHeight + 2; +			cellX = 0; +		} + +		_images[idx]._bounds.moveTo(pt.x, pt.y); + +		pt.x += imgWidth + 2; +		++cellX; +	} + +	// Set up the buttons +	pt.y += imgHeight + 2; +	_btnOk.setText(OK_BTN_STRING); +	_btnOk._bounds.moveTo((imgWidth + 2) * cellsSize - _btnOk._bounds.width(), pt.y); +	_btnLook.setText(LOOK_BTN_STRING); +	_btnLook._bounds.moveTo(_btnOk._bounds.left - _btnLook._bounds.width() - 2, _btnOk._bounds.top); +	addElements(&_btnLook, &_btnOk, NULL); + +	frame(); +	setCentre(SCREEN_CENTRE_X, SCREEN_CENTRE_Y); +} + +void InventoryDialog::execute() { +	if ((_globals->_inventory._selectedItem) && _globals->_inventory._selectedItem->inInventory()) +		_globals->_inventory._selectedItem->setCursor(); + +	GfxElement *hiliteObj; +	bool lookFlag = false; + +	while (!_vm->getEventManager()->shouldQuit()) { +		// Get events +		Event event; +		while (!_globals->_events.getEvent(event) && !_vm->getEventManager()->shouldQuit()) +			; +		if (_vm->getEventManager()->shouldQuit()) +			return; +		 +		hiliteObj = NULL; +		if ((event.eventType == EVENT_BUTTON_DOWN) && !_bounds.contains(event.mousePos)) +			break; + +		// Pass event to elements +		event.mousePos.x -= _gfxManager._bounds.left; +		event.mousePos.y -= _gfxManager._bounds.top; + +		for (GfxElementList::iterator i = _elements.begin(); i != _elements.end(); ++i) { +			if ((*i)->process(event)) +				hiliteObj = *i; +		} + +		if (!event.handled && event.eventType == EVENT_KEYPRESS) { +			if ((event.kbd.keycode == Common::KEYCODE_RETURN) || (event.kbd.keycode == Common::KEYCODE_ESCAPE)) { +				// Exit the dialog +				hiliteObj = &_btnOk; +				break; +			} +		} +			 +		if (hiliteObj == &_btnOk) { +			// Ok button clicked +			if (lookFlag) +				_globals->_events.setCursor(CURSOR_WALK); +			break; +		} else if (hiliteObj == &_btnLook) { +			// Look button clicked +			if (_btnLook._message == LOOK_BTN_STRING) { +				_btnLook._message = PICK_BTN_STRING; +				lookFlag = 1; +				_globals->_events.setCursor(CURSOR_LOOK); +			} else { +				_btnLook._message = LOOK_BTN_STRING; +				lookFlag = 0; +				_globals->_events.setCursor(CURSOR_WALK); +			} + +			_gfxManager.activate(); +			hiliteObj->draw(); +			_gfxManager.deactivate(); +		} else if (hiliteObj) { +			// Inventory item selected +			InvObject *invObject = static_cast<GfxInvImage *>(hiliteObj)->_invObject; +			if (lookFlag) { +				_globals->_screenSurface.displayText(invObject->_description); +			} else { +				_globals->_inventory._selectedItem = invObject; +				invObject->setCursor(); +			} +		} +	} +} + +/*--------------------------------------------------------------------------*/ + +void OptionsDialog::show() { +	OptionsDialog *dlg = new OptionsDialog(); +	dlg->draw(); + +	GfxButton *btn = dlg->execute(); + +	if (btn == &dlg->_btnQuit) { +		// Quit game +		if (MessageDialog::show(QUIT_CONFIRM_MSG, CANCEL_BTN_STRING, QUIT_BTN_STRING) == 1) { +			_vm->quitGame(); +		} +	} else if (btn == &dlg->_btnRestart) { +		// Restart game +		_globals->_game.restartGame(); +	} else if (btn == &dlg->_btnSound) { +		// Sound dialog +	} else if (btn == &dlg->_btnSave) { +		// Save button +		_globals->_game.saveGame(); +	} else if (btn == &dlg->_btnRestore) { +		// Restore button +		_globals->_game.restoreGame(); +	} + +	dlg->remove(); +	delete dlg; +} + +OptionsDialog::OptionsDialog() { +	// Set the element text +	_gfxMessage.set(OPTIONS_MSG, 140, ALIGN_LEFT); +	_btnRestore.setText(RESTORE_BTN_STRING); +	_btnSave.setText(SAVE_BTN_STRING); +	_btnRestart.setText(RESTART_BTN_STRING); +	_btnQuit.setText(QUIT_BTN_STRING); +	_btnSound.setText(SOUND_BTN_STRING); +	_btnResume.setText(RESUME_BTN_STRING); + +	// Set position of the elements +	_gfxMessage._bounds.moveTo(0, 1); +	_btnRestore._bounds.moveTo(0, _gfxMessage._bounds.bottom + 1); +	_btnSave._bounds.moveTo(0, _btnRestore._bounds.bottom + 1); +	_btnRestart._bounds.moveTo(0, _btnSave._bounds.bottom + 1); +	_btnQuit._bounds.moveTo(0, _btnRestart._bounds.bottom + 1); +	_btnSound._bounds.moveTo(0, _btnQuit._bounds.bottom + 1); +	_btnResume._bounds.moveTo(0, _btnSound._bounds.bottom + 1); + +	// Set all the buttons to the widest button +	GfxButton *btnList[6] = {&_btnRestore, &_btnSave, &_btnRestart, &_btnQuit, &_btnSound, &_btnResume}; +	int16 btnWidth = 0; +	for (int idx = 0; idx < 6; ++idx)  +		btnWidth = MAX(btnWidth, btnList[idx]->_bounds.width()); +	for (int idx = 0; idx < 6; ++idx) +		btnList[idx]->_bounds.setWidth(btnWidth); + +	// Add the items to the dialog +	addElements(&_gfxMessage, &_btnRestore, &_btnSave, &_btnRestart, &_btnQuit, &_btnSound, &_btnResume, NULL); + +	// Set the dialog size and position +	frame(); +	setCentre(160, 100); +} + + +} // End of namespace tSage diff --git a/engines/tsage/dialogs.h b/engines/tsage/dialogs.h new file mode 100644 index 0000000000..0fece89781 --- /dev/null +++ b/engines/tsage/dialogs.h @@ -0,0 +1,136 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/dialogs.h $ + * $Id: dialogs.h 215 2011-02-07 12:06:13Z dreammaster $ + * + */ + +#ifndef TSAGE_DIALOGS_H +#define TSAGE_DIALOGS_H + +#include "gui/options.h" +#include "tsage/events.h" +#include "tsage/graphics.h" +#include "common/list.h" +#include "common/rect.h" +#include "common/system.h" + +namespace tSage { + +class MessageDialog: public GfxDialog { +public: +	GfxButton _btn1, _btn2; +	GfxDialog _dialog; +	GfxMessage _msg; +public: +	MessageDialog(const Common::String &message, const Common::String &btn1Message, const Common::String &btn2Message = Common::String()); + +	static int show(const Common::String &message, const Common::String &btn1Message, const Common::String &btn2Message = Common::String()); +	static int show2(const Common::String &message, const Common::String &btn1Message, const Common::String &btn2Message = Common::String()); +}; + +class ConfigDialog : public GUI::OptionsDialog { +public: +	ConfigDialog(); +}; + +class RightClickButton: public GfxButton { +private: +	GfxSurface *_savedButton; +public: +	int _buttonIndex; + +	RightClickButton(int buttonIndex, int xp, int yp); +	~RightClickButton() { delete _savedButton; } + +	virtual void highlight(); +}; + +class RightClickDialog: public GfxDialog { +private: +	GfxSurface _surface; +	RightClickButton *_highlightedButton; +	int _selectedAction; +	RightClickButton _walkButton, _lookButton, _useButton, _talkButton, _inventoryButton, _optionsButton; + +	RightClickButton *findButton(const Common::Point &pt); +public: +	RightClickDialog(); +	~RightClickDialog(); + +	virtual void draw(); +	virtual bool process(Event &event); +	void execute(); +}; + +/*--------------------------------------------------------------------------*/ + +class ModalDialog: public GfxDialog { +protected: +	void drawFrame(); +public: +	virtual void draw(); +}; + +/*--------------------------------------------------------------------------*/ + +class GfxInvImage: public GfxImage { +public: +	InvObject *_invObject; +public: +	GfxInvImage(): GfxImage(), _invObject(NULL) {} + +	virtual bool process(Event &event); +}; + +#define MAX_INVOBJECT_DISPLAY 20 + +class InventoryDialog: public ModalDialog { +private: +	Common::Array<GfxInvImage> _images; +	GfxButton _btnOk, _btnLook; +public: +	InventoryDialog(bool allFlag = false); +	virtual ~InventoryDialog() {} +	void execute(); + +	static void show(bool allFlag = false); +}; + +/*--------------------------------------------------------------------------*/ + +class OptionsDialog: public ModalDialog { +private: +	GfxButton _btnSave, _btnRestore, _btnRestart; +	GfxButton _btnQuit, _btnResume; +	GfxButton _btnSound; +	GfxMessage _gfxMessage; +public: +	OptionsDialog(); +	virtual ~OptionsDialog() {} +	GfxButton *execute() { return GfxDialog::execute(&_btnResume); } + +	static void show(); +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/events.cpp b/engines/tsage/events.cpp new file mode 100644 index 0000000000..5348935dd9 --- /dev/null +++ b/engines/tsage/events.cpp @@ -0,0 +1,235 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/events.cpp $ + * $Id: events.cpp 224 2011-02-10 10:58:52Z dreammaster $ + * + */ + +#include "common/events.h" +#include "common/singleton.h" +#include "graphics/cursorman.h" +#include "common/system.h" + +#include "tsage/events.h" +#include "tsage/core.h" +#include "tsage/staticres.h" +#include "tsage/tsage.h" +#include "tsage/globals.h" + +namespace tSage { + +EventsClass::EventsClass() {  +	_frameNumber = 0; +	_priorFrameTime = 0; +	_prevDelayFrame = 0; +	_saver->addListener(this); +} + +bool EventsClass::pollEvent() { +	uint32 milli = g_system->getMillis(); +	if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) { +		_priorFrameTime = milli; +		++_frameNumber; + +		g_system->updateScreen(); +	} + +	if (!g_system->getEventManager()->pollEvent(_event)) return false; + +	// Handle keypress +	switch (_event.type) { +	case Common::EVENT_QUIT: +	case Common::EVENT_RTL: +		break; + +	case Common::EVENT_MOUSEMOVE: +	case Common::EVENT_LBUTTONDOWN: +	case Common::EVENT_LBUTTONUP: +	case Common::EVENT_RBUTTONDOWN: +	case Common::EVENT_RBUTTONUP: +		// Keep a copy of the current mouse position +		_mousePos = _event.mouse; +		break; + +	default: + 		break; +	} + +	return true; +} + +void EventsClass::waitForPress(int eventMask) { +	Event evt; +	while (!_vm->getEventManager()->shouldQuit() && !getEvent(evt, eventMask)) +		g_system->delayMillis(10); +} + +/** + * Standard event retrieval, which only returns keyboard and mouse clicks + */ +bool EventsClass::getEvent(Event &evt, int eventMask) { +	while (pollEvent() && !_vm->getEventManager()->shouldQuit()) { +		evt.handled = false; +		evt.eventType = EVENT_NONE; +		evt.mousePos = _event.mouse; +		evt.kbd = _event.kbd; + +		switch (_event.type) { +		case Common::EVENT_MOUSEMOVE: +			evt.eventType = EVENT_MOUSE_MOVE; +			break; +		case Common::EVENT_LBUTTONDOWN: +			evt.eventType = EVENT_BUTTON_DOWN; +			evt.btnState = BTNSHIFT_LEFT; +			break; +		case Common::EVENT_RBUTTONDOWN: +			evt.eventType = EVENT_BUTTON_DOWN; +			evt.btnState = BTNSHIFT_RIGHT; +			break; +		case Common::EVENT_MBUTTONDOWN: +			evt.eventType = EVENT_BUTTON_DOWN; +			evt.btnState = BTNSHIFT_MIDDLE; +			break; +		case Common::EVENT_LBUTTONUP: +		case Common::EVENT_RBUTTONUP: +		case Common::EVENT_MBUTTONUP: +			evt.eventType = EVENT_BUTTON_UP; +			evt.btnState = 0; +			break; +		case Common::EVENT_KEYDOWN: +			evt.eventType = EVENT_KEYPRESS; +			evt.kbd = _event.kbd; +			break; +		default: +			break; +		} + +		if (evt.eventType & eventMask) +			return true; +	} + +	return false; +} + +/** + * Sets the specified cursor + * + * @cursorType Specified cursor number + */ +void EventsClass::setCursor(CursorType cursorType) { +	_globals->clearFlag(122); +	 +	if (cursorType != CURSOR_ARROW) +		_currentCursor = cursorType; +	if (!CursorMan.isVisible()) +		showCursor(); + +	const byte *cursor; +	bool delFlag = true; +	uint size; + +	switch (cursorType) { +	case CURSOR_CROSSHAIRS: +		// Crosshairs cursor +		cursor = _vm->_dataManager->getSubResource(4, 1, 6, &size); +		_globals->setFlag(122); +		break; +	 +	case CURSOR_LOOK: +		// Look cursor +		cursor = _vm->_dataManager->getSubResource(4, 1, 5, &size); +		break; + +	case CURSOR_USE: +		// Use cursor +		cursor = _vm->_dataManager->getSubResource(4, 1, 4, &size); +		break; + +	case CURSOR_TALK: +		// Talk cursor +		cursor = _vm->_dataManager->getSubResource(4, 1, 3, &size); +		break; + +	case CURSOR_ARROW: +		// Arrow cursor +		cursor = CURSOR_ARROW_DATA; +		delFlag = false; +		break; + +	default: +		// Walk cursor +		cursor = CURSOR_WALK_DATA; +		delFlag = false; +		break; +	} + +	// Decode the cursor +	GfxSurface s = surfaceFromRes(cursor); + +	Graphics::Surface surface = s.lockSurface(); +	const byte *cursorData = (const byte *)surface.getBasePtr(0, 0); +	CursorMan.replaceCursor(cursorData, surface.w, surface.h, s._centroid.x, s._centroid.y, s._transColour); +	s.unlockSurface(); + +	if (delFlag) +		DEALLOCATE(cursor); +} + +void EventsClass::setCursor(Graphics::Surface &cursor, int transColour, const Common::Point &hotspot, CursorType cursorId) { +	const byte *cursorData = (const byte *)cursor.getBasePtr(0, 0); +	CursorMan.replaceCursor(cursorData, cursor.w, cursor.h, hotspot.x, hotspot.y, transColour); + +	_currentCursor = cursorId; +} + +void EventsClass::setCursorFromFlag() { +	setCursor(_globals->getFlag(122) ? CURSOR_CROSSHAIRS : _currentCursor); +} + +void EventsClass::showCursor() { +	CursorMan.showMouse(true); +} + +void EventsClass::hideCursor() { +	CursorMan.showMouse(false); +} + +/** + * Delays the game for the specified number of frames, if necessary, from the + * previous time the delay method was called + */ +void EventsClass::delay(int numFrames) { +	while (_frameNumber < (_prevDelayFrame + numFrames)) { +		uint32 delayAmount = CLIP(_priorFrameTime + GAME_FRAME_TIME - g_system->getMillis(),  +			(uint32)0, (uint32)GAME_FRAME_TIME); +		if (delayAmount > 0) +			g_system->delayMillis(delayAmount); + +		++_frameNumber; +		_priorFrameTime = g_system->getMillis(); +	} + +	g_system->updateScreen(); +	_prevDelayFrame = _frameNumber; +	_priorFrameTime = g_system->getMillis(); +} + +} // end of namespace tSage diff --git a/engines/tsage/events.h b/engines/tsage/events.h new file mode 100644 index 0000000000..093d392a48 --- /dev/null +++ b/engines/tsage/events.h @@ -0,0 +1,109 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/events.h $ + * $Id: events.h 212 2011-02-06 10:19:01Z dreammaster $ + * + */ + +#ifndef TSAGE_EVENTS_H +#define TSAGE_EVENTS_H + +#include "common/events.h" +#include "common/array.h" +#include "common/str.h" +#include "graphics/surface.h" +#include "tsage/saveload.h" + +namespace tSage { + +enum EventType {EVENT_NONE = 0, EVENT_BUTTON_DOWN = 1, EVENT_BUTTON_UP = 2, EVENT_KEYPRESS = 4, +	EVENT_MOUSE_MOVE = 8}; + +enum ButtonShiftFlags {BTNSHIFT_LEFT = 0, BTNSHIFT_RIGHT = 3, BTNSHIFT_MIDDLE = 4}; + +// Intrinisc game delay between execution frames. This runs at 60Hz +#define GAME_FRAME_TIME (1000 / 60) + +class GfxManager; + +class Event { +public: +	EventType eventType; +	Common::Point mousePos; +	int btnState; +	Common::KeyState kbd; +	int ctr; +	GfxManager *gfxMan; +	bool handled; +}; + +enum CursorType { +	OBJECT_STUNNER = 0, OBJECT_SCANNER = 1, OBJECT_STASIS_BOX = 2, +	OBJECT_INFODISK = 3, OBJECT_STASIS_NEGATOR = 4, OBJECT_KEY_DEVICE = 5, OBJECT_MEDKIT = 6, +	OBJECT_LADDER = 7, OBJECT_ROPE = 8, OBJECT_KEY = 9, OBJECT_TRANSLATOR = 10, OBJECT_ALE = 11, +	OBJECT_PAPER = 12, OBJECT_WALDOS = 13, OBJECT_STASIS_BOX2 = 14, OBJECT_RING = 15,  +	OBJECT_CLOAK = 16, OBJECT_TUNIC = 17, OBJECT_CANDLE = 18, OBJECT_STRAW = 19, OBJECT_SCIMITAR = 20, +	OBJECT_SWORD = 21, OBJECT_HELMET = 22, OBJECT_ITEMS = 23, OBJECT_CONCENTRATOR = 24, +	OBJECT_NULLIFIER = 25, OBJECT_PEG = 26, OBJECT_VIAL = 27, OBJECT_JACKET = 28,  +	OBJECT_TUNIC2 = 29, OBJECT_BONE = 30, OBJECT_EMPTY_JAR = 31, OBJECT_JAR = 32,  + +	CURSOR_WALK = 0x100, CURSOR_LOOK = 0x200,  +	CURSOR_700 = 700, CURSOR_USE = 0x400, CURSOR_TALK = 0x800, CURSOR_CROSSHAIRS = 0xfffe, CURSOR_ARROW = 0xffff +}; + +class EventsClass: public SaveListener { +private: +	Common::Event _event; +	CursorType _currentCursor; +	uint32 _frameNumber; +	uint32 _prevDelayFrame; +	uint32 _priorFrameTime; +public: +	EventsClass(); + +	Common::Point _mousePos; + +	void setCursor(CursorType cursorType); +	void setCursor(Graphics::Surface &cursor, int transColour, const Common::Point &hotspot, CursorType cursorId); +	void setCursorFromFlag(); +	CursorType getCursor() const { return _currentCursor; } +	void showCursor(); +	void hideCursor(); + +	bool pollEvent(); +	void waitForPress(int eventMask = EVENT_BUTTON_DOWN | EVENT_KEYPRESS); + +	bool getEvent(Event &evt, int eventMask = ~EVENT_MOUSE_MOVE); +	Common::Event event() { return _event; } +	Common::EventType type() { return _event.type; } +	uint32 getFrameNumber() const { return _frameNumber; } +	void delay(int numFrames); + +	virtual void listenerSynchronise(Serialiser &s) { +		s.syncAsUint32LE(_frameNumber); +		s.syncAsUint32LE(_prevDelayFrame); +		// TODO: Synchronise unknown stuff +	} +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp new file mode 100644 index 0000000000..bdf9d15011 --- /dev/null +++ b/engines/tsage/globals.cpp @@ -0,0 +1,96 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/globals.cpp $ + * $Id: globals.cpp 229 2011-02-12 06:50:14Z dreammaster $ + * + */ + +#include "tsage/globals.h" + +namespace tSage { + +Globals *_globals = NULL; + +/*--------------------------------------------------------------------------*/ + +/** + * Instantiates a saved object that can be instantiated + */ +static SavedObject *classFactoryProc(const Common::String &className) { +	if (className == "ObjectMover") return new ObjectMover(); +	if (className == "NpcMover") return new NpcMover(); +	if (className == "ObjectMover2") return new ObjectMover2(); +	if (className == "ObjectMover3") return new ObjectMover3(); +	if (className == "PlayerMover") return new PlayerMover(); +	 +	return NULL; +} + +/*--------------------------------------------------------------------------*/ + +Globals::Globals():  +		_dialogCentre(160, 140), +		_gfxManagerInstance(_screenSurface) { +	reset(); +	_gfxFontNumber = 50; +	_gfxColours.background = 53; +	_gfxColours.foreground = 18; +	_fontColours.background = 51; +	_fontColours.foreground = 54; +	 +	_screenSurface.setScreenSurface(); +	_gfxManagers.push_back(&_gfxManagerInstance); + +	_sceneObjects = &_sceneObjectsInstance; +	_sceneObjects_queue.push_front(_sceneObjects); + +	_stru_4642E = Common::Point(-1, -1); +} + +void Globals::reset() { +	Common::set_to(&_flags[0], &_flags[MAX_FLAGS], false); +	_saver->addFactory(classFactoryProc); +} + +void Globals::synchronise(Serialiser &s) { +	assert(_gfxManagers.size() == 1); + +	_sceneItems.synchronise(s); +	SYNC_POINTER(_sceneObjects); +	_sceneObjects_queue.synchronise(s); +	s.syncAsSint32LE(_gfxFontNumber); +	s.syncAsSint32LE(_gfxColours.background); +	s.syncAsSint32LE(_gfxColours.foreground); +	s.syncAsSint32LE(_fontColours.background); +	s.syncAsSint32LE(_fontColours.foreground); + +	s.syncAsSint16LE(_dialogCentre.x); s.syncAsSint16LE(_dialogCentre.y); +	_sceneListeners.synchronise(s); +	for (int i = 0; i < 256; ++i) +		s.syncAsByte(_flags[i]); + +	s.syncAsSint16LE(_sceneOffset.x); s.syncAsSint16LE(_sceneOffset.y); +	s.syncAsSint16LE(_stru_4642E.x); s.syncAsSint16LE(_stru_4642E.y); +	SYNC_POINTER(_scrollFollower); +	s.syncAsSint32LE(_stripNum); +} + +} // end of namespace tSage diff --git a/engines/tsage/globals.h b/engines/tsage/globals.h new file mode 100644 index 0000000000..30c295a7ab --- /dev/null +++ b/engines/tsage/globals.h @@ -0,0 +1,99 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/globals.h $ + * $Id: globals.h 229 2011-02-12 06:50:14Z dreammaster $ + * + */ + +#ifndef TSAGE_GLOBALS_H +#define TSAGE_GLOBALS_H + +#include "common/random.h" +#include "tsage/core.h" +#include "tsage/dialogs.h" +#include "tsage/scenes.h" +#include "tsage/events.h" +#include "tsage/saveload.h" + +namespace tSage { + +class Globals: public SavedObject { +public: +	GfxSurface _screenSurface; +	GfxManager _gfxManagerInstance; +	List<GfxManager *> _gfxManagers; +	SceneHandler _sceneHandler; +	Game _game; +	EventsClass _events; +	SceneManager _sceneManager; +	ScenePalette _scenePalette; +	SceneRegions _sceneRegions; +	SceneItemList _sceneItems; +	SceneObjectList _sceneObjectsInstance; +	SceneObjectList *_sceneObjects; +	List<SceneObjectList *> _sceneObjects_queue; +	SceneText _sceneText; +	int _gfxFontNumber; +	GfxColours _gfxColours; +	GfxColours _fontColours; +	SoundManager _soundManager; +	Common::Point _dialogCentre; +	WalkRegions _walkRegions; +	List<EventHandler *> _sceneListeners; +	bool _flags[256]; +	Player _player; +	SoundHandler _soundHandler; +	InvObjectList _inventory; +	Region _paneRegions[2]; +	int _paneRefreshFlag[2]; +	Common::Point _sceneOffset; +	Common::Point _stru_4642E; +	SceneObject *_scrollFollower; +	SequenceManager _sequenceManager; +	Common::RandomSource _randomSource; +	int _stripNum; +public: +	Globals(); + +	void reset(); +	void setFlag(int flagNum) { +		assert((flagNum > 0) && (flagNum < MAX_FLAGS)); +		_flags[flagNum] = true; +	} +	void clearFlag(int flagNum) { +		assert((flagNum > 0) && (flagNum < MAX_FLAGS)); +		_flags[flagNum] = false; +	} +	bool getFlag(int flagNum) const { +		assert((flagNum > 0) && (flagNum < MAX_FLAGS)); +		return _flags[flagNum]; +	} + +	GfxManager &gfxManager() { return **_gfxManagers.begin(); } +	virtual Common::String getClassName() { return "Globals"; } +	virtual void synchronise(Serialiser &s); +}; + +extern Globals *_globals; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/graphics.cpp b/engines/tsage/graphics.cpp new file mode 100644 index 0000000000..bb72661bb1 --- /dev/null +++ b/engines/tsage/graphics.cpp @@ -0,0 +1,1439 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/graphics.cpp $ + * $Id: graphics.cpp 225 2011-02-10 11:00:11Z dreammaster $ + * + */ + +#include "tsage/events.h" +#include "tsage/graphics.h" +#include "tsage/resources.h" +#include "tsage/tsage.h" +#include "tsage/core.h" +#include "common/algorithm.h" +#include "graphics/surface.h" +#include "tsage/globals.h" + +namespace tSage { + +/** + * Creates a new graphics surface with the specified area of another surface + * + * @src Source surface + * @bounds Area to backup + */ +GfxSurface *Surface_getArea(GfxSurface &src, const Rect &bounds) { +	assert(bounds.isValidRect()); +	GfxSurface *dest = new GfxSurface(); +	dest->create(bounds.width(), bounds.height()); + +	Graphics::Surface srcSurface = src.lockSurface(); +	Graphics::Surface destSurface = dest->lockSurface(); + +	byte *srcP = (byte *)srcSurface.getBasePtr(bounds.left, bounds.top); +	byte *destP = (byte *)destSurface.getBasePtr(0, 0); + +	for (int y = bounds.top; y < bounds.bottom; ++y, srcP += srcSurface.pitch, destP += destSurface.pitch) +		Common::copy(srcP, srcP + destSurface.pitch, destP); + +	src.unlockSurface(); +	dest->unlockSurface(); +	return dest; +} + +/** + * Translates a raw image resource into a graphics surface. The caller is then responsible + * for managing and destroying the surface when done with it + * + * @imgData Raw image resource + * @size Size of the resource + */ +GfxSurface surfaceFromRes(const byte *imgData) { +	Rect r(0, 0, READ_LE_UINT16(imgData), READ_LE_UINT16(imgData + 2)); +	GfxSurface s; +	s.create(r.width(), r.height()); +	s._centroid.x = READ_LE_UINT16(imgData + 4); +	s._centroid.y = READ_LE_UINT16(imgData + 6); +	s._transColour = *(imgData + 8); + +	bool rleEncoded = (imgData[9] & 2) != 0; + +	const byte *srcP = imgData + 10; +	Graphics::Surface destSurface = s.lockSurface(); +	byte *destP = (byte *)destSurface.getBasePtr(0, 0); + +	if (!rleEncoded) { +		Common::copy(srcP, srcP + (r.width() * r.height()), destP); +	} else { +		Common::set_to(destP, destP + (r.width() * r.height()), s._transColour); + +		for (int yp = 0; yp < r.height(); ++yp) { +			int width = r.width(); +			destP = (byte *)destSurface.getBasePtr(0, yp); + +			while (width > 0) { +				uint8 controlVal = *srcP++; +				if ((controlVal & 0x80) == 0) { +					// Copy specified number of bytes + +					Common::copy(srcP, srcP + controlVal, destP); +					width -= controlVal; +					srcP += controlVal; +					destP += controlVal; +				} else if ((controlVal & 0x40) == 0) { +					// Skip a specified number of output pixels +					destP += controlVal & 0x3f; +					width -= controlVal & 0x3f; +				} else { +					// Copy a specified pixel a given number of times +					controlVal &= 0x3f; +					int pixel = *srcP++; + +					Common::set_to(destP, destP + controlVal, pixel); +					destP += controlVal; +					width -= controlVal; +				} +			} +			assert(width == 0); +		} +	} +	 +	s.unlockSurface(); +	return s; +} + +GfxSurface surfaceFromRes(int resNum, int rlbNum, int subNum) { +	uint size; +	byte *imgData = _vm->_dataManager->getSubResource(resNum, rlbNum, subNum, &size); +	GfxSurface surface = surfaceFromRes(imgData); +	DEALLOCATE(imgData); + +	return surface; +} +/*--------------------------------------------------------------------------*/ + +void Rect::set(int16 x1, int16 y1, int16 x2, int16 y2) { +	left = x1; top = y1; +	right = x2; bottom = y2; +} + +/** + * Collapses the rectangle in all four directions by the given x and y amounts + * + * @dx x amount to collapse x edges by + * @dy y amount to collapse y edges by + */ +void Rect::collapse(int dx, int dy) { +	left += dx; right -= dx; +	top += dy; bottom -= dy; +} + +/** + * Centres the rectangle at a given position + * + * @xp x position for new centre + * @yp y position for new centre + */ +void Rect::centre(int xp, int yp) { +	moveTo(xp - (width() / 2), yp - (height() / 2)); +} + +/** + * Centres the rectangle at the centre of a second passed rectangle + * + * @r Second rectangle whose centre to use + */ +void Rect::centre(const Rect &r) { +	centre(r.left + (r.width() / 2), r.top + (r.height() / 2)); +} + +/* + * Repositions the bounds if necessary so it falls entirely within the passed bounds + * + * @r The bounds the current rect should be within + */ +void Rect::contain(const Rect &r) { +	if (left < r.left) translate(r.left - left, 0); +	if (right > r.right) translate(r.right - right, 0); +	if (top < r.top) translate(0, r.top - top); +	if (bottom > r.bottom) translate(0, r.bottom - bottom); +} + +/** + * Resizes and positions a given rect based on raw image data and a passed scaling percentage + * + * @frame Raw image frame + * @xp New x position + * @yp New y position + * @percent Scaling percentage + */ +void Rect::resize(const GfxSurface &surface, int xp, int yp, int percent) { +	int xe = surface.getBounds().width() * percent / 100; +	int ye = surface.getBounds().height() * percent / 100; +	this->set(0, 0, xe, ye); + +	if (!right) ++right; +	if (!bottom) ++bottom; + +	this->moveTo(xp, yp); + +	int xd = surface._centroid.x * percent / 100; +	int yd = surface._centroid.y * percent / 100; +	this->translate(-xd, -yd); +} + +/** + * Serialises the given rect + */ +void Rect::synchronise(Serialiser &s) { +	s.syncAsSint16LE(left); +	s.syncAsSint16LE(top); +	s.syncAsSint16LE(right); +	s.syncAsSint16LE(bottom); +} + +/*--------------------------------------------------------------------------*/ + +GfxSurface::GfxSurface(): _bounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) { +	_disableUpdates = false; +	_screenSurface = false; +	_lockSurfaceCtr = 0; +	_customSurface = NULL; +	_screenSurfaceP = NULL; +	_freeSurface = false; +} + +GfxSurface::GfxSurface(const GfxSurface &s) { +	assert(!s._lockSurfaceCtr); +	_disableUpdates = false; +	_lockSurfaceCtr = 0; +	_screenSurface = s._screenSurface; +	_screenSurfaceP = s._screenSurfaceP; +	_customSurface = s._customSurface; +	_centroid = s._centroid; +	_transColour = s._transColour; +	_bounds = s._bounds; + +	if (!_screenSurface) { +		create(s._customSurface->w, s._customSurface->h); +		Common::copy((const byte *)s._customSurface->pixels,  +			(const byte *)s._customSurface->pixels + (_bounds.width() * _bounds.height()),  +			(byte *)_customSurface->pixels); +	} +} + +GfxSurface::~GfxSurface() { +	if (_freeSurface) { +		_customSurface->free(); +		delete _customSurface; +	} +} + +/** + * Specifies that the surface will encapsulate the ScummVM screen surface + */ +void GfxSurface::setScreenSurface() { +	_screenSurface = true; +	_customSurface = NULL; +	_lockSurfaceCtr = 0; +} + +/** + * Specifies the underlying ScummmVM surface that this class should encapsulate + */ +void GfxSurface::setSurface(Graphics::Surface *s) { +	_customSurface = s; +	_screenSurface = false; +	_lockSurfaceCtr = 0; +} + +/** + * Specifies that the surface should maintain it's own internal surface + */ +void GfxSurface::create(int width, int height) { +	_screenSurface = false; +	_customSurface = new Graphics::Surface(); +	_customSurface->create(width, height, 1); +	_freeSurface = true; +	_bounds = Rect(0, 0, width, height); +} + +/** + * Locks the surface for access, and returns a raw ScummVM surface to manipulate it + */ +Graphics::Surface GfxSurface::lockSurface() { +	++_lockSurfaceCtr; + +	Graphics::Surface *src; +	if (_screenSurface) { +		if (_lockSurfaceCtr == 1) +			_screenSurfaceP = g_system->lockScreen(); +		src = _screenSurfaceP; +	} else +		src = _customSurface; +	assert(src); + +	// Setup the returned surface either as one pointing to the same pixels as the source, or  +	// as a subset of the source one based on the currently set bounds +	Graphics::Surface result; +	result.w = _bounds.width(); +	result.h = _bounds.height(); +	result.pitch = src->pitch; +	result.bytesPerPixel = src->bytesPerPixel; +	result.pixels = src->getBasePtr(_bounds.left, _bounds.top); + +	return result; +} + +/** + * Unlocks the surface after having accessed it with the lockSurface method + */ +void GfxSurface::unlockSurface() { +	assert(_lockSurfaceCtr > 0); +	--_lockSurfaceCtr; + +	if ((_lockSurfaceCtr == 0) && _screenSurface) { +		g_system->unlockScreen(); +	} +} + +/** + * Fills a specified rectangle on the surface with the specified colour + * + * @bounds Area to fill + * @colour Colour to use + */ +void GfxSurface::fillRect(const Rect &bounds, int colour) { +	Graphics::Surface surface = lockSurface(); +	surface.fillRect(bounds, colour); +	unlockSurface(); +} + +GfxSurface &GfxSurface::operator=(const GfxSurface &s) { +	assert(_lockSurfaceCtr == 0); +	assert(s._lockSurfaceCtr == 0); + +	_customSurface = s._customSurface; +	_screenSurface = s._screenSurface; +	_freeSurface = s._freeSurface; +	_disableUpdates = s._disableUpdates; +	_bounds = s._bounds; +	_centroid = s._centroid; +	_transColour = s._transColour; + +	if (_freeSurface) { +		// Surface owns the internal data, so replicate it so new surface owns it's own +		_customSurface = new Graphics::Surface(); +		_customSurface->create(_bounds.width(), _bounds.height(), 1); +		const byte *srcP = (const byte *)s._customSurface->getBasePtr(0, 0); +		byte *destP = (byte *)_customSurface->getBasePtr(0, 0); + +		Common::copy(srcP, srcP + (_bounds.width() * _bounds.height()), destP); +	} + +	return *this; +} + +/** + * Displays a message on-screen until either a mouse or keypress + */ +bool GfxSurface::displayText(const Common::String &msg, const Common::Point &pt) { +	// Set up a new graphics manager +	GfxManager gfxManager; +	gfxManager.activate(); +	gfxManager._font._colours.background = 0; +	gfxManager._font._colours.foreground = 7; +	gfxManager._font.setFontNumber(2); + +	// Get the area for text display +	Rect textRect; +	gfxManager.getStringBounds(msg.c_str(), textRect, 200); +	textRect.centre(pt.x, pt.y); + +	// Make a backup copy of the area the text will occupy +	Rect saveRect = textRect; +	saveRect.collapse(-20, -8); +	GfxSurface *savedArea = Surface_getArea(gfxManager.getSurface(), saveRect); + +	// Display the text +	gfxManager._font.writeLines(msg.c_str(), textRect, ALIGN_LEFT); + +	// Write for a  mouse or keypress +	Event event; +	while (!_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS) && !_vm->getEventManager()->shouldQuit()) +		; + +	// Restore the display area +	gfxManager.copyFrom(*savedArea, saveRect.left, saveRect.top); +	delete savedArea; + +	gfxManager.deactivate(); +	return (event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_RETURN); +} + +/** + * Loads a quarter of a screen from a resource + */ +void GfxSurface::loadScreenSection(Graphics::Surface &dest, int xHalf, int yHalf, int xSection, int ySection) { +	int screenNum = _globals->_sceneManager._scene->_activeScreenNumber; +	Rect updateRect(0, 0, 160, 100); +	updateRect.translate(xHalf * 160, yHalf * 100); +	int xHalfCount = (dest.w + 159) / 160; +	int yHalfCount = (dest.h + 99) / 100; + +	if (xSection < xHalfCount && ySection < yHalfCount) { +		int rlbNum = xSection * yHalfCount + ySection; +		byte *data = _vm->_dataManager->getResource(RES_BITMAP, screenNum, rlbNum); + +		for (int y = 0; y < updateRect.height(); ++y) { +			byte *pSrc = data + y * 160; +			byte *pDest = (byte *)dest.getBasePtr(updateRect.left, updateRect.top + y); + +			for (int x = 0; x < updateRect.width(); ++x, ++pSrc, ++pDest) { +				*pDest = *pSrc; +			} +		} + +		DEALLOCATE(data); +	} +} + +/** + * Returns an array indicating which pixels of a source image horizontally or vertically get + * included in a scaled image + */ +static int *scaleLine(int size, int srcSize) { +	int scale = 100 * size / srcSize; +	assert(scale >= 0); +	int *v = new int[size]; +	Common::set_to(v, &v[size], 0); + +	int distCtr = 0; +	int *destP = v; +	for (int distIndex = 0; distIndex < srcSize; ++distIndex) { +		distCtr += scale; +		while (distCtr >= 100) { +			assert(destP < &v[size]); +			*destP++ = distIndex; +			distCtr -= 100; +		} +	} +	 +	return v; +} + +/** + * Scales a passed surface, creating a new surface with the result + * @param srcImage		Source image to scale + * @param NewWidth		New width for scaled image + * @param NewHeight		New height for scaled image + * @remarks Caller is responsible for freeing the returned surface + */ +static GfxSurface ResizeSurface(GfxSurface &src, int xSize, int ySize) { +	GfxSurface s; +	s.create(xSize, ySize); + +	Graphics::Surface srcImage = src.lockSurface(); +	Graphics::Surface destImage = s.lockSurface(); + +	int *horizUsage = scaleLine(xSize, srcImage.w); +	int *vertUsage = scaleLine(ySize, srcImage.h); + +	// Loop to create scaled version +	for (int yp = 0; yp < ySize; ++yp) { +		const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]); +		byte *destP = (byte *)destImage.getBasePtr(0, yp); + +		for (int xp = 0; xp < xSize; ++xp) { +			const byte *tempSrcP = srcP + horizUsage[xp]; +			*destP++ = *tempSrcP++; +		} +	} + +	// Unlock surfaces +	src.unlockSurface(); +	s.unlockSurface(); + +	// Delete arrays and return surface +	delete[] horizUsage; +	delete[] vertUsage; +	return s; +} + +/** + * Copys an area from one GfxSurface to another + */ +void GfxSurface::copyFrom(GfxSurface &src, Rect srcBounds, Rect destBounds, Region *priorityRegion) { +	GfxSurface srcImage; + +	if (srcBounds == src.getBounds()) +		srcImage = src; +	else { +		// Set the source image to be the subset specified by the source bounds +		Graphics::Surface srcSurface = src.lockSurface(); + +		srcImage.create(srcBounds.width(), srcBounds.height()); +		Graphics::Surface destSurface = srcImage.lockSurface(); +		 +		const byte *srcP = (const byte *)srcSurface.getBasePtr(srcBounds.left, srcBounds.top); +		byte *destP = (byte *)destSurface.pixels; +		for (int yp = srcBounds.top; yp < srcBounds.bottom; ++yp, srcP += srcSurface.pitch, destP += destSurface.pitch) { +			Common::copy(srcP, srcP + srcBounds.width(), destP); +		} +		 +		srcImage.unlockSurface(); +		src.unlockSurface(); +	} + +	if ((destBounds.width() != srcBounds.width()) || (destBounds.height() != srcBounds.height())) +		srcImage = ResizeSurface(srcImage, destBounds.width(), destBounds.height()); + +	Graphics::Surface srcSurface = srcImage.lockSurface(); +	Graphics::Surface destSurface = lockSurface(); + +	// Adjust bounds to ensure destination will be on-screen +	int srcX = 0, srcY = 0; +	if (destBounds.left < 0) { +		srcX = -destBounds.left; +		destBounds.left = 0; +	} +	if (destBounds.top < 0) { +		srcY = -destBounds.top; +		destBounds.top = 0; +	} +	if (destBounds.right > destSurface.w) +		destBounds.right = destSurface.w; +	if (destBounds.bottom > destSurface.h) +		destBounds.bottom = destSurface.h; + +	if (destBounds.isValidRect()) { +		const byte *pSrc = (const byte *)srcSurface.getBasePtr(srcX, srcY); +		byte *pDest = (byte *)destSurface.getBasePtr(destBounds.left, destBounds.top); + +		for (int y = 0; y < destBounds.height(); ++y, pSrc += srcSurface.pitch, pDest += destSurface.pitch) { + +			if (!priorityRegion && (src._transColour == -1)) +				Common::copy(pSrc, pSrc + destBounds.width(), pDest); +			else { +				const byte *tempSrc = pSrc; +				byte *tempDest = pDest; +				int xp = destBounds.left; + +				while (tempSrc < (pSrc + destBounds.width())) { +					if (!priorityRegion || !priorityRegion->contains(Common::Point(xp, destBounds.top + y))) { +						if (*tempSrc != src._transColour) +							*tempDest = *tempSrc; +					} +					++tempSrc; +					++tempDest; +					++xp; +				} +			} +		} +	} + +	unlockSurface(); +	srcImage.unlockSurface(); +} + +void GfxSurface::draw(const Common::Point &pt, Rect *rect) { +	Rect tempRect = getBounds(); +	tempRect.translate(-_centroid.x, -_centroid.y); +	tempRect.translate(pt.x, pt.y); + +	if (rect) { +		// Only copy needed rect out without drawing +		*rect = tempRect; +	} else { +		// Draw image +		_globals->gfxManager().copyFrom(*this, tempRect, NULL); +	} +} + +/*--------------------------------------------------------------------------*/ + +GfxElement::GfxElement() { +	_owner = NULL; +	_keycode = 0; +	_flags = 0; +} + +void GfxElement::setDefaults() { +	_flags = 0; +	_fontNumber = _globals->_gfxFontNumber; +	_colours = _globals->_gfxColours; +	_fontColours = _globals->_fontColours; +} + +/** + * Highlights the specified graphics element + */ +void GfxElement::highlight() { +	// Get a lock on the surface  +	GfxManager &gfxManager = _globals->gfxManager(); +	Graphics::Surface surface = gfxManager.lockSurface(); + +	// Scan through the contents of the element, switching any occurances of the foreground  +	// colour with the background colour and vice versa +	Rect tempRect(_bounds); +	tempRect.collapse(2, 2); + +	for (int yp = tempRect.top; yp < tempRect.bottom; ++yp) { +		byte *lineP = (byte *)surface.getBasePtr(tempRect.left, yp); +		for (int xp = tempRect.left; xp < tempRect.right; ++xp, ++lineP) { +			if (*lineP == _colours.background) *lineP = _colours.foreground; +			else if (*lineP == _colours.foreground) *lineP = _colours.background; +		} +	} + +	// Release the surface +	gfxManager.unlockSurface(); +} + +/** + * Fills the background of the specified element with a border frame + */ +void GfxElement::drawFrame() { +	// Get a lock on the surface and save the active font +	GfxManager &gfxManager = _globals->gfxManager(); +	gfxManager.lockSurface(); + +	uint8 bgColour, fgColour; +	if (_flags & GFXFLAG_THICK_FRAME) { +		bgColour = 0; +		fgColour = 0; +	} else { +		bgColour = _fontColours.background; +		fgColour = _fontColours.foreground; +	} + +	Rect tempRect = _bounds; +	tempRect.collapse(3, 3); +	tempRect.collapse(-1, -1); +	gfxManager.fillRect(tempRect, _colours.background); + +	--tempRect.bottom; --tempRect.right; +	gfxManager.fillArea(tempRect.left, tempRect.top, bgColour); +	gfxManager.fillArea(tempRect.left, tempRect.bottom, fgColour); +	gfxManager.fillArea(tempRect.right, tempRect.top, fgColour); +	gfxManager.fillArea(tempRect.right, tempRect.bottom, fgColour); + +	tempRect.collapse(-1, -1); +	gfxManager.fillRect2(tempRect.left + 1, tempRect.top, tempRect.width() - 1, 1, bgColour); +	gfxManager.fillRect2(tempRect.left, tempRect.top + 1, 1, tempRect.height() - 1, bgColour); +	gfxManager.fillRect2(tempRect.left + 1, tempRect.bottom, tempRect.width() - 1, 1, fgColour); +	gfxManager.fillRect2(tempRect.right, tempRect.top + 1, 1, tempRect.height() - 1, fgColour); + +	gfxManager.fillArea(tempRect.left, tempRect.top, 0); +	gfxManager.fillArea(tempRect.left, tempRect.bottom, 0); +	gfxManager.fillArea(tempRect.right, tempRect.top, 0); +	gfxManager.fillArea(tempRect.right, tempRect.bottom, 0); + +	tempRect.collapse(-1, -1); +	gfxManager.fillRect2(tempRect.left + 2, tempRect.top, tempRect.width() - 3, 1, 0); +	gfxManager.fillRect2(tempRect.left, tempRect.top + 2, 1, tempRect.height() - 3, 0); +	gfxManager.fillRect2(tempRect.left + 2, tempRect.bottom, tempRect.width() - 3, 1, 0); +	gfxManager.fillRect2(tempRect.right, tempRect.top + 2, 1, tempRect.height() - 3, 0); + +	gfxManager.unlockSurface(); +} + +/** + * Handles events when the control has focus + * + * @event Event to process + */ +bool GfxElement::focusedEvent(Event &event) { +	bool highlightFlag = false; + +	while (!_vm->getEventManager()->shouldQuit()) { +		g_system->delayMillis(10); + +		if (_bounds.contains(event.mousePos)) { +			if (!highlightFlag) { +				// First highlight call to show the highlight +				highlightFlag = true; +				highlight(); +			} +		} else if (highlightFlag) { +			// Mouse is outside the element, so remove the highlight +			highlightFlag = false; +			highlight(); +		} + +		if (_globals->_events.getEvent(event, EVENT_BUTTON_UP)) +			break; +	} + +	if (highlightFlag) { +		// Mouse is outside the element, so remove the highlight +		highlight(); +	} + +	return highlightFlag; +} + +/*--------------------------------------------------------------------------*/ + +GfxImage::GfxImage(): GfxElement() { +	_resNum = 0; +	_rlbNum = 0; +	_cursorNum = 0; +} + +void GfxImage::setDetails(int resNum, int rlbNum, int cursorNum) { +	_resNum = resNum; +	_rlbNum = rlbNum; +	_cursorNum = cursorNum; +	setDefaults(); +} + +void GfxImage::setDefaults() { +	GfxElement::setDefaults(); + +	// Decode the image +	uint size; +	byte *imgData = _vm->_dataManager->getSubResource(_resNum, _rlbNum, _cursorNum, &size); +	_surface = surfaceFromRes(imgData); +	DEALLOCATE(imgData); +	 +	// Set up the display bounds +	Rect imgBounds = _surface.getBounds(); +	imgBounds.moveTo(_bounds.left, _bounds.top); +	_bounds = imgBounds; +} + +void GfxImage::draw() { +	Rect tempRect = _bounds; +	tempRect.translate(_globals->gfxManager()._topLeft.x, _globals->gfxManager()._topLeft.y); + +	_globals->gfxManager().copyFrom(_surface, tempRect);  +} + +/*--------------------------------------------------------------------------*/ + +GfxMessage::GfxMessage(): GfxElement() { +	_textAlign = ALIGN_LEFT; +	_width = 0; +} + +void GfxMessage::set(const Common::String &s, int width, TextAlign textAlign) { +	_message = s; +	_width = width; +	_textAlign = textAlign; + +	setDefaults(); +} + +void GfxMessage::setDefaults() { +	GfxElement::setDefaults(); + +	GfxFontBackup font; +	GfxManager &gfxManager = _globals->gfxManager(); +	Rect tempRect; + +	gfxManager._font.setFontNumber(this->_fontNumber); +	gfxManager.getStringBounds(_message.c_str(), tempRect, _width); +	 +	tempRect.collapse(-1, -1); +	tempRect.moveTo(_bounds.left, _bounds.top); +	_bounds = tempRect; +} + +void GfxMessage::draw() { +	GfxFontBackup font; +	GfxManager &gfxManager = _globals->gfxManager(); + +	// Set the font and colour +	gfxManager.setFillFlag(false); +	gfxManager._font.setFontNumber(_fontNumber); +	gfxManager._font._colours.foreground = this->_colours.foreground; + +	// Display the text +	gfxManager._font.writeLines(_message.c_str(), _bounds, _textAlign); +} + +/*--------------------------------------------------------------------------*/ + +void GfxButton::setDefaults() { +	GfxElement::setDefaults(); + +	GfxFontBackup font; +	GfxManager &gfxManager = _globals->gfxManager(); +	Rect tempRect; + +	// Get the string bounds and round up the x end to a multiple of 16 +	gfxManager._font.setFontNumber(this->_fontNumber); +	gfxManager._font.getStringBounds(_message.c_str(), tempRect, 240); +	tempRect.right = ((tempRect.right + 15) / 16) * 16; + +	// Set the button bounds to a reduced area +	tempRect.collapse(-3, -3); +	tempRect.moveTo(_bounds.left, _bounds.top); +	_bounds = tempRect; +} + +void GfxButton::draw() { +	// Get a lock on the surface and save the active font +	GfxFontBackup font; +	GfxManager &gfxManager = _globals->gfxManager(); +	gfxManager.lockSurface(); + +	// Draw a basic frame for the button +	drawFrame(); + +	// Set the font and colour +	gfxManager._font.setFontNumber(_fontNumber); +	gfxManager._font._colours.foreground = this->_colours.foreground; + +	// Display the button's text +	Rect tempRect(_bounds); +	tempRect.collapse(3, 3); +	gfxManager._font.writeLines(_message.c_str(), tempRect, ALIGN_CENTRE); + +	gfxManager.unlockSurface(); +} + +bool GfxButton::process(Event &event) { +	switch (event.eventType) { +	case EVENT_BUTTON_DOWN: +		if (!event.handled) { +			if (_bounds.contains(event.mousePos)) { +				bool result = focusedEvent(event); +				event.handled = true; +				return result; +			} +		} +		break; + +	case EVENT_KEYPRESS: +		if (!event.handled && (event.kbd.keycode == _keycode)) { +			// TODO: Ensure momentary click operation displays +			highlight(); +			g_system->delayMillis(20); +			highlight(); + +			event.handled = true; +			return true; +		} + +	default: +		break; +	} + +	return false; +} + +/*--------------------------------------------------------------------------*/ + +GfxDialog::GfxDialog() { +	_savedArea = NULL; +	_defaultButton = NULL; +} + +GfxDialog::~GfxDialog() { +	remove(); +} + +void GfxDialog::setDefaults() { +	GfxElement::setDefaults(); + +	// Initialise the embedded graphics manager +	_gfxManager.setDefaults(); + +	// Figure out a rect needed for all the added elements +	GfxElementList::iterator i; +	Rect tempRect; +	for (i = _elements.begin(); i != _elements.end(); ++i) +		tempRect.extend((*i)->_bounds); + +	// Set the dialog boundaries +	_gfxManager._bounds = tempRect; +	tempRect.collapse(-6, -6); +	_bounds = tempRect; +} + +void GfxDialog::remove() { +	if (_savedArea) { +		// Restore the area the dialog covered +		_globals->_gfxManagerInstance.copyFrom(*_savedArea, _bounds.left, _bounds.top); + +		delete _savedArea; +		_savedArea = NULL; +	} +} + +void GfxDialog::draw() { +	Rect tempRect(_bounds); + +	// Make a backup copy of the area the dialog will occupy +	_savedArea = Surface_getArea(_globals->_gfxManagerInstance.getSurface(), _bounds); + +	// Set the palette for use in the dialog +	setPalette(); + +	_gfxManager.activate(); + +	// Fill in the contents of the entire dialog +	_gfxManager._bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); +	drawFrame(); + +	// Reset the dialog's graphics manager to only draw within the dialog boundaries +	tempRect.translate(6, 6); +	_gfxManager._bounds = tempRect; + +	// Draw each element in the dialog in order +	GfxElementList::iterator i; +	for (i = _elements.begin(); i != _elements.end(); ++i) { +		(*i)->draw(); +	} + +	// If there's a default button, then draw it +	if (_defaultButton) { +		_defaultButton->_flags |= GFXFLAG_THICK_FRAME; +		_defaultButton->draw(); +	} + +	_gfxManager.deactivate(); +} + +void GfxDialog::add(GfxElement *element) {  +	_elements.push_back(element);  +	element->_owner = this; +} + +void GfxDialog::addElements(GfxElement *ge, ...) { +	va_list va; +	va_start(va, ge);  +	GfxElement *gfxElement = ge; +	while (gfxElement) { +		add(gfxElement); + +		gfxElement = va_arg(va, GfxElement *); +	} + +	va_end(va); +} + +void GfxDialog::setTopLeft(int xp, int yp) { +	_bounds.moveTo(xp - 6, yp - 6); +} + +void GfxDialog::setCentre(int xp, int yp) { +	setTopLeft(xp - (_bounds.width() / 2), yp - (_bounds.height() / 2)); +} + +GfxButton *GfxDialog::execute(GfxButton *defaultButton) { +	_gfxManager.activate(); + +	if (defaultButton != _defaultButton) { +		if (_defaultButton) { +			_defaultButton->_flags &= ~GFXFLAG_THICK_FRAME; +			_defaultButton->draw(); +		} +		_defaultButton = defaultButton; +	} +	if (_defaultButton) { +		_defaultButton->_flags |= GFXFLAG_THICK_FRAME; +		_defaultButton->draw(); +	} + +	// Event loop +	GfxButton *selectedButton = NULL; + +	while (!_vm->getEventManager()->shouldQuit()) { +		Event event; +		while (_globals->_events.getEvent(event)) { +			// Adjust mouse positions to be relative within the dialog +			event.mousePos.x -= _gfxManager._bounds.left; +			event.mousePos.y -= _gfxManager._bounds.top; + +			for (GfxElementList::iterator i = _elements.begin(); i != _elements.end(); ++i) { +				if ((*i)->process(event)) +					selectedButton = static_cast<GfxButton *>(*i); +			} +		} + +		if (selectedButton) +			break; +		else if (!event.handled) { +			if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) { +				selectedButton = NULL; +				break; +			} else if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_RETURN)) { +				selectedButton = defaultButton; +				break; +			} +		} +	} + +	_gfxManager.deactivate(); +	if (_defaultButton) +		_defaultButton->_flags &= ~GFXFLAG_THICK_FRAME; + +	return selectedButton; +} + +void GfxDialog::setPalette() { +	_globals->_scenePalette.loadPalette(0); +	_globals->_scenePalette.setPalette(0, 1); +	_globals->_scenePalette.setPalette(_globals->_scenePalette._colours.foreground, 1); +	_globals->_scenePalette.setPalette(_globals->_fontColours.background, 1); +	_globals->_scenePalette.setPalette(_globals->_fontColours.foreground, 1); +	_globals->_scenePalette.setPalette(255, 1); +} + +/*--------------------------------------------------------------------------*/ + +GfxManager::GfxManager(): _surface(_globals->_screenSurface), _oldManager(NULL) { +	_font.setOwner(this); +	_font._fillFlag = false; +	_bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); +} + +GfxManager::GfxManager(GfxSurface &s): _surface(s), _oldManager(NULL) { +	_font.setOwner(this); +	_font._fillFlag = false; +} + +void GfxManager::setDefaults() { +	Rect screenBounds(0, 0, g_system->getWidth(), g_system->getHeight()); + +	_surface.setBounds(screenBounds); +	_bounds = screenBounds; +	_pane0Rect4 = screenBounds; + +	_font._edgeSize = Common::Point(1, 1); +	_font._colours = _globals->_fontColours; +	_font.setFontNumber(_globals->_gfxFontNumber); +} + +void GfxManager::activate() { +	assert(!_globals->_gfxManagers.contains(this)); +	_globals->_gfxManagers.push_front(this); +} + +void GfxManager::deactivate() { +	// Assert that there will still be another manager, and we're correctly removing our own +	assert((_globals->_gfxManagers.size() > 1) && (&_globals->gfxManager() == this)); +	_globals->_gfxManagers.pop_front(); +} + +int GfxManager::getStringWidth(const char *s, int numChars) { +	return _font.getStringWidth(s, numChars); +} + +int GfxManager::getStringWidth(const char *s) { +	return _font.getStringWidth(s); +} + +void GfxManager::getStringBounds(const char *s, Rect &bounds, int maxWidth) { +	_font.getStringBounds(s, bounds, maxWidth);	 +} + +void GfxManager::fillArea(int xp, int yp, int colour) { +	_surface.setBounds(_bounds); +	Rect tempRect(xp, yp, xp + _font._edgeSize.x, yp + _font._edgeSize.y); +	_surface.fillRect(tempRect, colour); +} + +void GfxManager::fillRect(const Rect &bounds, int colour) { +	_surface.setBounds(_bounds); +	_surface.fillRect(bounds, colour); +} + +void GfxManager::fillRect2(int xs, int ys, int width, int height, int colour) { +	_surface.setBounds(_bounds); +	_surface.fillRect(Rect(xs, ys, xs + width, ys + height), colour); +} + +/** + * Sets up the standard palette for dialog displays + */ +void GfxManager::setDialogPalette() { +	// Get the main palette information +	uint32 palData[256]; +	uint count, start; +	_vm->_dataManager->getPalette(0, (byte *)&palData[0], &start, &count); +	g_system->getPaletteManager()->setPalette((byte *)&palData[0], start, count); + +	// Miscellaneous +	uint32 white = 0xffffffff; +	g_system->getPaletteManager()->setPalette((const byte *)&white, 255, 1); +} + +/** + * Returns the angle of line connecting two points + */ +int GfxManager::getAngle(const Common::Point &p1, const Common::Point &p2) { +	int xDiff = p2.x - p1.x, yDiff = p1.y - p2.y; + +	if (!xDiff && !yDiff) +		return -1; +	else if (!xDiff) +		return (p2.y >= p1.y) ? 180 : 0; +	else if (!yDiff) +		return (p2.x >= p1.x) ? 90 : 270; +	else { +		int result = (((xDiff * 100) / ((abs(xDiff) + abs(yDiff))) * 90) / 100); + +		if (yDiff < 0) +			result = 180 - result; +		else if (xDiff < 0) +			result += 360; + +		return result; +	} +} +/*--------------------------------------------------------------------------*/ + + +GfxFont::GfxFont() { +	_fontNumber = 50; +	_numChars = 0; +	_bpp = 0; +	_fontData = NULL; +	_fillFlag = false; +} + +GfxFont::~GfxFont() { +	DEALLOCATE(_fontData); +} + +/** + * Sets the current active font number + * + * @fontNumber New font number + */ +void GfxFont::setFontNumber(uint32 fontNumber) { +	if ((_fontNumber == fontNumber) && (_fontData)) +		return; + +	DEALLOCATE(_fontData); + +	_fontNumber = fontNumber; +	 +	_fontData = _vm->_tSageManager->getResource(RES_FONT, _fontNumber, 0, true); +	if (!_fontData) +		_fontData = _vm->_dataManager->getResource(RES_FONT, _fontNumber, 0); + +	_numChars = READ_LE_UINT16(_fontData + 4); +	_fontSize.y = READ_LE_UINT16(_fontData + 6); +	_fontSize.x = READ_LE_UINT16(_fontData + 8); +	_bpp = READ_LE_UINT16(_fontData + 10); +} + +/** + * Returns the width of the given specified character + * + * @ch Character to return width of + */ +int GfxFont::getCharWidth(char ch) { +	assert(_numChars > 0); +	uint32 charOffset = READ_LE_UINT32(_fontData + 12 + (uint8)ch * 4); +	return _fontData[charOffset] & 0x1f; +} + +/** + * Returns the width of the given string in the current font + * + * @s String to return the width of + * @numChars Number of characters within the string to use + */ +int GfxFont::getStringWidth(const char *s, int numChars) { +	assert(_numChars > 0); +	int width = 0; + +	for (; numChars > 0; --numChars, ++s) { +		uint32 charOffset = READ_LE_UINT32(_fontData + 12 + (uint8)*s * 4); +		int charWidth = _fontData[charOffset] & 0x1f; + +		width += charWidth; +	} + +	return width; +} + +/** + * Returns the width of the given string in the current font + * + * @s String to return the width of + */ +int GfxFont::getStringWidth(const char *s) { +	return getStringWidth(s, strlen(s)); +} + +/** + * Returns the maximum number of characters for words that will fit into a given width + * + * @s Message to be analysed + * @maxWidth Maximum allowed width + */ +int GfxFont::getStringFit(const char *&s, int maxWidth) { +	const char *nextWord = NULL; +	const char *sStart = s; +	int numChars = 1; +	int strWidth = 1; +	char nextChar; + +	for (;;) { +		nextChar = *s++; + +		if ((nextChar == '\r') || (nextChar == '\0')) +			break; + +		// Check if it's a word end +		if (nextChar == ' ') { +			nextWord = s; +		} + +		strWidth = getStringWidth(sStart, numChars); +		if (strWidth > maxWidth) { +			if (nextWord) { +				s = nextWord; +				nextChar = ' '; +			} +			break; +		} +			 +		++numChars; +	}  + +	int totalChars = s - sStart;	 +	if (nextChar == '\0') +		--s; +	if ((nextChar == ' ') || (nextChar == '\r') || (nextChar == '\0')) +		--totalChars; + +	return totalChars; +} + +/** + * Fills out the passed rect with the dimensions of a given string word-wrapped to a  + * maximum specified width + * + * @s Message to be analysed + * @bounds Rectangle to put output size into + * @maxWidth Maximum allowed line width in pixels + */ +void GfxFont::getStringBounds(const char *s, Rect &bounds, int maxWidth) { +	if (maxWidth == 0) { +		// No maximum width, so set bounds for a single line +		bounds.set(0, 0, getStringWidth(s), getHeight()); +	} else { +		int numLines = 0; +		int lineWidth = 0; + +		// Loop to figure out the number of lines required, and the maximum line width +		while (*s) { +			const char *msg = s; +			int numChars = getStringFit(msg, maxWidth); +			lineWidth = MAX(lineWidth, getStringWidth(s, numChars)); + +			s = msg; +			++numLines; +		} + +		bounds.set(0, 0, lineWidth, numLines * getHeight()); +	} +} + +/** + * Writes out a character at the currently set position using the active font + * + * @ch Character to display + */ +int GfxFont::writeChar(const char ch) { +	assert((_fontData != NULL) && ((uint8)ch < _numChars)); +	uint32 charOffset = READ_LE_UINT32(_fontData + 12 + (uint8)ch * 4); +	int charWidth = _fontData[charOffset] & 0x1f; +	int charHeight = (READ_LE_UINT16(_fontData + charOffset) >> 5) & 0x3f; +	int yOffset = (_fontData[charOffset + 1] >> 3) & 0x1f; +	const uint8 *dataP = &_fontData[charOffset + 2]; + +	// Lock the surface for access +	Graphics::Surface surfacePtr = _gfxManager->lockSurface(); + +	Rect charRect; +	charRect.set(0, 0, charWidth, _fontSize.y); +	charRect.translate(_topLeft.x + _position.x, _topLeft.y + _position.y + yOffset); + +	if (_fillFlag) +		surfacePtr.fillRect(charRect, _colours.background); + +	charRect.bottom = charRect.top + charHeight; + +	// Display the character +	int bitCtr = 0; +	uint8 v = 0; +	for (int yp = charRect.top; yp < charRect.bottom; ++yp) { +		byte *destP = (byte *)surfacePtr.getBasePtr(charRect.left, yp); + +		for (int xs = 0; xs < charRect.width(); ++xs, ++destP) { +			// Get the next colour index to use +			if ((bitCtr % 8) == 0) v = *dataP++; +			int colIndex = 0; +			for (int subCtr = 0; subCtr < _bpp; ++subCtr, ++bitCtr) { +				colIndex = (colIndex << 1) | (v & 0x80 ? 1 : 0); +				v <<= 1; +			} + +			switch (colIndex) { +			//case 0: *destP = _colours.background; break; +			case 1: *destP = _colours.foreground; break; +			case 2: *destP = _colours2.background; break; +			case 3: *destP = _colours2.foreground; break; +			} +		} +	} + +	_position.x += charWidth; +	_gfxManager->unlockSurface(); +	return charWidth; +} + +/** + * Writes the specified number of characters from the specified string at the current text position + * + * @s String to display + * @numChars Number of characters to print + */ +void GfxFont::writeString(const char *s, int numChars) { +	// Lock the surface for access +	_gfxManager->lockSurface(); + +	while ((numChars-- > 0) && (*s != '\0')) { +		writeChar(*s); +		++s; +	} + +	// Release the surface lock +	_gfxManager->unlockSurface(); +} + +/** + * Writes the the specified string at the current text position + * + * @s String to display + */ +void GfxFont::writeString(const char *s) { +	writeString(s, strlen(s)); +} + +/** + * Writes a specified string within a given area with support for word wrapping and text alignment types + * + * @s String to display + * @bounds Bounds to display the text within + * @align Text alignment mode + */ +void GfxFont::writeLines(const char *s, const Rect &bounds, TextAlign align) { +	int lineNum = 0; + +	// Lock the surface for access +	_gfxManager->lockSurface(); + +	while (*s) { +		const char *msgP = s; +		int numChars = getStringFit(msgP, bounds.width()); + +		_position.y = bounds.top + lineNum * getHeight(); + +		switch (align) { +		case ALIGN_RIGHT: +			// Right aligned text +			_position.x = bounds.right - getStringWidth(s, numChars); +			writeString(s, numChars); +			break; + +		case ALIGN_CENTRE: +			// Center aligned text +			_position.x = bounds.left + (bounds.width() / 2) - (getStringWidth(s, numChars) / 2); +			writeString(s, numChars); +			break; + +		case ALIGN_JUSTIFIED: { +			// Justified text +			// Get the number of words in the string portion +			int charCtr = 0, numWords = 0; +			while (charCtr < numChars) { +				if (s[charCtr] == ' ') +					++numWords; +				++charCtr; +			} +			// If end of string, count final word +			if (*msgP == '\0') +				++numWords; + +			// Display the words of the string +			int spareWidth = bounds.width() - getStringWidth(s, numChars); +			charCtr = 0; +			_position.x = bounds.left; + +			while (charCtr < numChars) { +				writeChar(s[charCtr]); +				if ((numWords > 0) && (s[charCtr] == ' ')) { +					int separationWidth = spareWidth / numWords; +					spareWidth -= separationWidth; +					--numWords; +					_position.x += separationWidth; +				} + +				++charCtr; +			} +			break; +		} + +		case ALIGN_LEFT: +		default: +			// Standard text +			_position.x = bounds.left; +			writeString(s, numChars); +			break; +		} + +		// Next line +		s = msgP; +		++lineNum; +	} + +	// Release the surface lock +	_gfxManager->unlockSurface(); +} + +/*--------------------------------------------------------------------------*/ + +GfxFontBackup::GfxFontBackup() { +	_edgeSize = _globals->gfxManager()._font._edgeSize; +	_position = _globals->gfxManager()._font._position; +	_colours = _globals->gfxManager()._font._colours; +	_fontNumber = _globals->gfxManager()._font._fontNumber; +} + +GfxFontBackup::~GfxFontBackup() { +	_globals->gfxManager()._font.setFontNumber(_fontNumber); +	_globals->gfxManager()._font._edgeSize = _edgeSize; +	_globals->gfxManager()._font._position = _position; +	_globals->gfxManager()._font._colours = _colours; +} + + +} // End of namespace tSage diff --git a/engines/tsage/graphics.h b/engines/tsage/graphics.h new file mode 100644 index 0000000000..caf3e6e092 --- /dev/null +++ b/engines/tsage/graphics.h @@ -0,0 +1,351 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/graphics.h $ + * $Id: graphics.h 184 2011-02-03 11:31:38Z dreammaster $ + * + */ + +#ifndef RING_GRAPHICS_H +#define RING_GRAPHICS_H + +#include "tsage/events.h" +#include "tsage/saveload.h" +#include "common/list.h" +#include "common/rect.h" +#include "common/system.h" +#include "graphics/surface.h" + +using namespace Common; + +namespace tSage { + +class GfxSurface; +class Region; + +/** + * Extended Rect class with extra support methods + */ +class Rect: public Common::Rect, public Serialisable { +public: +	Rect(): Common::Rect() {}; +	Rect(int16 x1, int16 y1, int16 x2, int16 y2): Common::Rect(x1, y1, x2, y2) {}; + +	void set(int16 x1, int16 y1, int16 x2, int16 y2); +	void collapse(int dx, int dy); +	void centre(int dx, int dy); +	void centre(const Rect &r); +	void contain(const Rect &r); +	void resize(const GfxSurface &surface, int xp, int yp, int percent); + +	virtual void synchronise(Serialiser &s); +}; + +class GfxColours { +public: +	uint8 foreground; +	uint8 background; + +	GfxColours(): foreground(0), background(0) {}; +}; + +class LineSlice { +public: +	int xs, xe; +	 +	LineSlice() { xs = 0; xe = 0; } +	LineSlice(int xStart, int xEnd) { xs = xStart; xe = xEnd; } +}; + +class GfxSurface { +private: +	Graphics::Surface *_customSurface; +	Graphics::Surface *_screenSurfaceP; +	int _lockSurfaceCtr; +	bool _screenSurface; +	bool _freeSurface; + +	bool _disableUpdates; +	Rect _bounds; +public: +	Common::Point _centroid; +	int _transColour; +public: +	GfxSurface(); +	GfxSurface(const GfxSurface &s); +	~GfxSurface(); + +	void setScreenSurface(); +	void setSurface(Graphics::Surface *s); +	Graphics::Surface lockSurface(); +	void unlockSurface(); +	void create(int width, int height); +	void setBounds(const Rect &bounds) { _bounds = bounds; }; +	const Rect &getBounds() const { return _bounds; }; + +	void copyFrom(GfxSurface &src, Rect srcBounds, Rect destBounds, Region *priorityRegion = NULL); +	void copyFrom(GfxSurface &src, Rect destBounds, Region *priorityRegion = NULL) { +		copyFrom(src, src.getBounds(), destBounds, priorityRegion); +	} +	void copyFrom(GfxSurface &src, int destX = 0, int destY = 0, Region *priorityRegion = NULL) { +		Rect tempRect = src.getBounds(); +		tempRect.moveTo(destX, destY); +		copyFrom(src, tempRect, priorityRegion); +	} +	void draw(const Common::Point &pt, Rect *rect = NULL); +	void fillRect(const Rect &bounds, int colour); +	GfxSurface &operator=(const GfxSurface &s); + +	static void loadScreenSection(Graphics::Surface &dest, int xHalf, int yHalf, int xSection, int ySection); +	static bool displayText(const Common::String &msg, const Common::Point &pt = Common::Point(160, 100)); +}; + +enum TextAlign {ALIGN_LEFT = 0, ALIGN_CENTRE = 1, ALIGN_RIGHT = 2, ALIGN_JUSTIFIED = 3}; + +class GfxFont { +	friend class GfxFontBackup; +private: +	GfxManager *_gfxManager; +	// Raw font details +	const byte *_fontData; +	int _numChars; +	Common::Point _fontSize; +	int _bpp; +public: +	// Font fields +	Common::Point _edgeSize; +	Common::Point _position; +	bool _fillFlag; +	GfxColours _colours; +	GfxColours _colours2; +	uint32 _fontNumber; +	Common::Point _topLeft; +public: +	GfxFont(); +	virtual ~GfxFont(); + +	void setFontNumber(uint32 fontNumber); +	int32 getHeight() const { return _fontSize.y; } +	int getCharWidth(char ch); +	int getStringWidth(const char *s, int numChars); +	int getStringWidth(const char *s); +	int getStringFit(const char *&s, int maxWidth); +	void getStringBounds(const char *s, Rect &bounds, int maxWidth); + +	void setOwner(GfxManager *owner) { _gfxManager = owner; } +	void setPosition(int xp, int yp) { _position.x = xp; _position.y = yp; } +	int writeChar(const char ch); +	void writeString(const char *s); +	void writeString(const char *s, int numChars); +	void writeLines(const char *s, const Rect &bounds, TextAlign align); +}; + +class GfxFontBackup { +private: +	GfxSurface *_surface; +	Common::Point _edgeSize; +	Common::Point _position; +	GfxColours _colours; +	uint32 _fontNumber; +public: +	GfxFontBackup(); +	~GfxFontBackup(); +}; + +enum GFX_FLAGS {GFXFLAG_THICK_FRAME = 8}; + +class GfxManager; + +class GfxElement { +public: +	GfxElement *_owner; +	Rect _bounds; +	uint16 _flags; +	uint16 _fontNumber; +	GfxColours _colours; +	GfxColours _fontColours; +	uint16 _keycode; +public: +	GfxElement(); +	virtual ~GfxElement() {} + +	void drawFrame(); + +	// Virtual table method +	virtual void setDefaults(); +	virtual void remove() { _owner = NULL; } +	virtual void highlight(); +	virtual void draw() {}; +	virtual bool process(Event &event) { return false; }; +	virtual bool focusedEvent(Event &event); +}; + +class GfxImage: public GfxElement { +public: +	GfxSurface _surface; +	int _resNum; +	int _rlbNum; +	int _cursorNum; +public: +	GfxImage(); + +	void setDetails(int resNum, int rlbNum, int cursorNum); + +	virtual void setDefaults(); +	virtual void draw(); +	virtual bool process(Event &event) { return false; } +}; + +class GfxMessage: public GfxElement { +public: +	Common::String _message; +	TextAlign _textAlign; +	int _width; +public: +	GfxMessage(); +	virtual ~GfxMessage() {} + +	void set(const Common::String &s, int width, TextAlign textAlign); + +	virtual void setDefaults(); +	virtual void draw(); +}; + +class GfxButton: public GfxElement { +private: +	void setFocus(); +public: +	Common::String _message; +public: +	GfxButton(): GfxElement() {}; +	virtual ~GfxButton() {} + +	void setText(const Common::String &s) { +		_message = s; +		setDefaults(); +	} + +	// Virtual table method +	virtual void setDefaults(); +	virtual void draw(); +	virtual bool process(Event &event); +}; + +class GfxManager { +private: +	GfxSurface &_surface; +public: +	GfxManager *_oldManager; +	Common::Point _topLeft; +	Rect _bounds; +	Rect _pane0Rect4; +	GfxFont _font; +public: +	GfxManager(); +	GfxManager(GfxSurface &s); +	virtual ~GfxManager() {} + +	void setDefaults(); +	void activate(); +	void deactivate(); + +	// Accessor methods +	int getStringWidth(const char *s, int numChars); +	int getStringWidth(const char *s); +	void getStringBounds(const char *s, Rect &bounds, int maxWidth); + +	void setDialogPalette(); +	Graphics::Surface lockSurface() {  +		_surface.setBounds(_bounds); +		return _surface.lockSurface(); +	} +	void unlockSurface() { _surface.unlockSurface(); }; +	void fillArea(int xp, int yp, int colour); +	void fillRect(const Rect &bounds, int colour); +	void fillRect2(int xs, int ys, int width, int height, int colour); +	void setFillFlag(bool v) { _font._fillFlag = v; } + +	static int getAngle(const Common::Point &p1, const Common::Point &p2); + +	// Virtual method table +	virtual void xorArea(const Common::Rect &r, int colour, int fillMode) { +		//_surface->xorArea(r, colour, fillMode); +	} +	virtual void draw(const Common::Rect &r, void *gfxData, int v1, GfxColours *colours) { +		//_surface->draw(r, gfxData, v1, colours); +	} +	virtual void copy(const byte *src, byte *dest, int size) { +		Common::copy(src, src + size, dest); +	} +	virtual void set(byte *dest, int size, byte val) { +		Common::set_to(dest, dest + size, val); +	} +	void copyFrom(GfxSurface &src, Rect destBounds, Region *priorityRegion = NULL) { +		_surface.setBounds(_bounds); +		_surface.copyFrom(src, destBounds, priorityRegion); +	} +	void copyFrom(GfxSurface &src, int destX, int destY) { +		_surface.setBounds(_bounds); +		_surface.copyFrom(src, destX, destY); +		g_system->updateScreen(); +	} +	GfxSurface &getSurface() { +		_surface.setBounds(_bounds); +		return _surface; +	} +}; + +typedef Common::List<GfxElement *> GfxElementList; + +class GfxDialog: public GfxElement { +public: +	GfxManager _gfxManager; +	GfxElementList _elements; +	GfxButton *_defaultButton; +	GfxSurface *_savedArea; +public: +	GfxDialog(); +	virtual ~GfxDialog(); + +	void add(GfxElement *element); +	void addElements(GfxElement *ge, ...); +	void setTopLeft(int xp, int yp); +	void setCentre(int xp, int yp); +	void frame() {  +		setDefaults(); +		_bounds.collapse(6, 6);  +	} +	GfxButton *execute(GfxButton *defaultButton = NULL); + +	virtual void setDefaults(); +	virtual void remove(); +	virtual void draw(); + +	static void setPalette(); +}; + +GfxSurface *Surface_getArea(GfxSurface &src, const Rect &bounds); + +GfxSurface surfaceFromRes(const byte *imgData); +GfxSurface surfaceFromRes(int resNum, int rlbNum, int subNum); + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/module.mk b/engines/tsage/module.mk new file mode 100644 index 0000000000..fc42ad932b --- /dev/null +++ b/engines/tsage/module.mk @@ -0,0 +1,27 @@ +MODULE := engines/tsage + +MODULE_OBJS := \ +	converse.o \ +	core.o \ +	debugger.o \ +	detection.o \ +	dialogs.o \ +	events.o \ +	globals.o \ +	graphics.o \ +	resources.o \ +	saveload.o \ +	scene_logic.o \ +	scenes.o \ +	sound.o \ +	staticres.o \ +	tsage.o + +# This module can be built as a plugin +ifdef BUILD_PLUGINS +PLUGIN := 1 +endif + +# Include common rules  +include $(srcdir)/rules.mk + diff --git a/engines/tsage/resources.cpp b/engines/tsage/resources.cpp new file mode 100644 index 0000000000..2df4b54b94 --- /dev/null +++ b/engines/tsage/resources.cpp @@ -0,0 +1,414 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/resources.cpp $ + * $Id: resources.cpp 145 2011-01-08 11:41:39Z dreammaster $ + * + */ + +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/file.h" +#include "common/stack.h" +#include "common/util.h" +#include "tsage/resources.h" + +namespace tSage { + + +MemoryManager::MemoryManager() { +	_memoryPool = new MemoryHeader*[MEMORY_POOL_SIZE]; +	Common::set_to(&_memoryPool[0], &_memoryPool[MEMORY_POOL_SIZE], (MemoryHeader *)NULL); +} + +MemoryManager::~MemoryManager() { +	for (int i = 0; i < MEMORY_POOL_SIZE; ++i) { +		if (_memoryPool[i] != NULL) +			free(_memoryPool[i]); +	} +	delete[] _memoryPool; +} + +uint16 MemoryManager::allocate(uint32 size) { +	int idx = 0; +	while ((idx < MEMORY_POOL_SIZE) && (_memoryPool[idx] != NULL))  +		++idx; +	if (idx == MEMORY_POOL_SIZE) +		error("Out of memory handles"); +	 +	// Create the new entry +	_memoryPool[idx] = (MemoryHeader *)malloc(sizeof(MemoryHeader) + size); +	_memoryPool[idx]->id = MEMORY_ENTRY_ID; +	_memoryPool[idx]->index = idx; +	_memoryPool[idx]->lockCtr = 0; +	_memoryPool[idx]->criticalCtr = 0; +	_memoryPool[idx]->tag = 0; +	_memoryPool[idx]->size = size; + +	// Return it's index +	return idx; +} + +byte *MemoryManager::allocate2(uint32 size) { +	uint32 idx = allocate(size); +	return lock(idx); +} + +byte *MemoryManager::lock(uint32 handle) { +	assert((int)handle < MEMORY_POOL_SIZE); +	return (byte *)_memoryPool[handle] + sizeof(MemoryHeader); +} + +int MemoryManager::indexOf(const byte *p) { +	for (int idx = 0; idx < MEMORY_POOL_SIZE; ++idx) { +		if (((byte *)_memoryPool[idx] + sizeof(MemoryHeader)) == p) +			return idx; +	} + +	return -1; +} + +void MemoryManager::deallocate(const byte *p) { +	if (!p) +		return; + +	int idx = indexOf(p); +	assert(idx != -1); +	if (_memoryPool[idx]->lockCtr-- == 0) { +		free(_memoryPool[idx]); +		_memoryPool[idx] = NULL; +	} +} + +uint32 MemoryManager::getSize(const byte *p) { +	int idx = indexOf(p); +	assert(idx >= 0); +	return _memoryPool[idx]->size; +} + +void MemoryManager::incLocks(const byte *p) { +	int idx = indexOf(p); +	assert(idx >= 0); +	_memoryPool[idx]->lockCtr++; +} + +/*-------------------------------------------------------------------------*/ + +static uint16 bitMasks[4] = {0x1ff, 0x3ff, 0x7ff, 0xfff}; + +uint16 BitReader::readToken() { +	assert((numBits >= 9) && (numBits <= 12)); +	uint16 result = _remainder; +	int bitsLeft = numBits - _bitsLeft; +	int bitOffset = _bitsLeft; +	_bitsLeft = 0; + +	while (bitsLeft >= 0) { +		_remainder = readByte(); +		result |= _remainder << bitOffset; +		bitsLeft -= 8; +		bitOffset += 8; +	} + +	_bitsLeft = -bitsLeft; +	_remainder >>= 8 - _bitsLeft; +	return result & bitMasks[numBits - 9]; +} + +/*-------------------------------------------------------------------------*/ + +RlbManager::RlbManager(MemoryManager &memManager, const Common::String filename):  +		_memoryManager(memManager) { + +	// If the resource strings list isn't yet loaded, load them +	if (_resStrings.size() == 0) { +		Common::File f; +		if (f.open("tsage.cfg")) { +			while (!f.eos()) { +				_resStrings.push_back(f.readLine()); +			} +			f.close(); +		} +	} + +	if (!_file.open(filename)) +		error("Missing file %s", filename.c_str()); + +	loadIndex(); +} + +RlbManager::~RlbManager() { +	_resStrings.clear(); +} + +void RlbManager::loadSection(uint32 fileOffset) { +	_resources.clear(); +	_file.seek(fileOffset); +	_sections.fileOffset = fileOffset; + +	if (_file.readUint32BE() != 0x544D492D) +		error("Data block is not valid Rlb data"); + +	/*uint8 unknown1 = */_file.readByte(); +	uint16 numEntries = _file.readByte(); + +	for (uint i = 0; i < numEntries; ++i) { +		uint16 id = _file.readUint16LE(); +		uint16 size = _file.readUint16LE(); +		uint16 uncSize = _file.readUint16LE(); +		uint8 sizeHi = _file.readByte(); +		uint8 type = _file.readByte() >> 5; +		assert(type <= 1); +		uint32 offset = _file.readUint32LE(); + +		ResourceEntry *re = new ResourceEntry(); +		re->id = id; +		re->fileOffset = offset; +		re->isCompressed = type != 0; +		re->size = ((sizeHi & 0xF) << 16) | size; +		re->uncompressedSize = ((sizeHi & 0xF) << 16) | uncSize; + +		_resources.push_back(*re); +	} +} + +struct DecodeReference { +	uint16 vWord; +	uint8 vByte; +}; + +/** + * Gets a resource from the currently loaded section + */ +byte *RlbManager::getResource(uint16 id, bool suppressErrors) { +	// Scan for an entry for the given Id +	ResourceEntry *re= NULL; +	ResourceList::iterator i; +	for (i = _resources.begin(); i != _resources.end(); ++i) { +		if ((*i).id == id) { +			re = &(*i); +			break; +		} +	} +	if (!re) { +		if (suppressErrors) +			return NULL; +		error("Could not find resource Id #%d", id); +	} + +	if (!re->isCompressed) { +		// Read in the resource data and return it +		byte *dataP = _memoryManager.allocate2(re->size); +		_file.seek(_sections.fileOffset + re->fileOffset); +		_file.read(dataP, re->size); + +		return dataP; +	} + +	/* +	 * Decompress the data block +	 */ + +	_file.seek(_sections.fileOffset + re->fileOffset); +	Common::ReadStream *compStream = _file.readStream(re->size); +	BitReader bitReader(*compStream); + +	byte *dataOut = _memoryManager.allocate2(re->uncompressedSize); +	byte *destP = dataOut; +	uint bytesWritten = 0; +	 +	uint16 ctrCurrent = 0x102, ctrMax = 0x200; +	uint16 word_48050 = 0, currentToken = 0, word_48054 =0; +	byte byte_49068 = 0, byte_49069 = 0; +	DecodeReference table[0x1000]; +	Common::Stack<uint16> tokenList; + +	for (;;) { +		// Get the next decode token +		uint16 token = bitReader.readToken();		 + +		// Handle the token +		if (token == 0x101) { +			// End of compressed stream +			break; +		} else if (token == 0x100) { +			// Reset bit-rate +			bitReader.numBits = 9; +			ctrMax = 0x200; +			ctrCurrent = 0x102; + +			// Set variables with next token +			currentToken = word_48050 = bitReader.readToken(); +			byte_49069 = byte_49068 = (byte)currentToken; + +			++bytesWritten; +			assert(bytesWritten <= re->uncompressedSize); +			*destP++ = byte_49069; +		} else { +			word_48054 = word_48050 = token; + +			if (token >= ctrCurrent) { +				word_48050 = currentToken; +				tokenList.push(byte_49068); +			} + +			while (word_48050 >= 0x100) { +				assert(word_48050 < 0x1000); +				tokenList.push(table[word_48050].vByte); +				word_48050 = table[word_48050].vWord; +			} + +			byte_49069 = byte_49068 = (byte)word_48050; +			tokenList.push(word_48050); + +			// Write out any cached tokens +			while (!tokenList.empty()) { +				++bytesWritten; +				assert(bytesWritten <= re->uncompressedSize); +				*destP++ = tokenList.pop(); +			} + +			assert(ctrCurrent < 0x1000); +			table[ctrCurrent].vByte = byte_49069; +			table[ctrCurrent].vWord = currentToken; +			++ctrCurrent; + +			currentToken = word_48054; +			if ((ctrCurrent >= ctrMax) && (bitReader.numBits != 12)) { +				// Move to the next higher bit-rate +				++bitReader.numBits; +				ctrMax <<= 1; +			} +		} +	}		 + +	assert(bytesWritten == re->uncompressedSize); +	delete compStream; +	return dataOut; +} + +/** + * Finds the correct section and loads the specified resource within it + */ +byte *RlbManager::getResource(ResourceType resType, uint16 resNum, uint16 rlbNum, bool suppressErrors) { +	SectionList::iterator i = _sections.begin(); +	while ((i != _sections.end()) && ((*i).resType != resType || (*i).resNum != resNum)) +		++i; +	if (i == _sections.end()) { +		if (suppressErrors) +			return NULL; +		error("Unknown resource type %d num %d", resType, resNum); +	} + +	loadSection((*i).fileOffset); + +	return getResource(rlbNum, suppressErrors); +} + +void RlbManager::loadIndex() { +	uint16 resNum, configId, fileOffset; + +	// Load the root resources section +	loadSection(0); + +	// Get the single resource from it +	const byte *pData = getResource(0); +	const byte *p = pData; +	 +	_sections.clear(); + +	// Loop through reading the entries +	while ((resNum = READ_LE_UINT16(p)) != 0xffff) { +		configId = READ_LE_UINT16(p + 2); +		fileOffset = READ_LE_UINT16(p + 4); +		p += 6; + +		SectionEntry *se = new SectionEntry(); +		se->resNum = resNum; +		se->resType = (ResourceType)(configId & 0x1f); +		se->fileOffset = (((configId >> 5) & 0x7ff) << 16) | fileOffset; + +		_sections.push_back(*se); +	} + +	_memoryManager.deallocate(pData); +} + +/** + * Retrieves the specified palette resource and returns it's data + * + * @paletteNum Specefies the palette number + */ +void RlbManager::getPalette(int paletteNum, uint8 *palData, uint *startNum, uint *numEntries) { +	// Get the specified palette +	byte *dataIn = getResource(RES_PALETTE, 0, paletteNum); +	assert(dataIn); + +	*startNum = READ_LE_UINT16(dataIn); +	*numEntries = READ_LE_UINT16(dataIn + 2); +	assert((*startNum < 256) && ((*startNum + *numEntries) <= 256)); + +	// Copy over the data +	for (uint i = 0; i < *numEntries; ++i) { +		*palData++ = dataIn[6 + i * 3]; +		*palData++ = dataIn[7 + i * 3]; +		*palData++ = dataIn[8 + i * 3]; +		*palData++ = 0; +	} + +	_memoryManager.deallocate(dataIn); +} + +byte *RlbManager::getSubResource(int resNum, int rlbNum, int index, uint *size) { +	// Get the specified image set +	byte *dataIn = getResource(RES_VISAGE, resNum, rlbNum); +	assert(dataIn); + +	int numEntries = READ_LE_UINT16(dataIn); +	uint32 entryOffset = READ_LE_UINT32(dataIn + 2 + (index - 1) * 4); +	uint32 nextOffset = (index == numEntries) ?  +			_memoryManager.getSize(dataIn) : READ_LE_UINT32(dataIn + 2 + index * 4); +	*size = nextOffset - entryOffset; +	assert(*size < (1024 * 1024)); + +	byte *entry = _memoryManager.allocate2(*size); +	Common::copy(&dataIn[entryOffset], &dataIn[nextOffset], entry); + +	_memoryManager.deallocate(dataIn); +	return entry; +} + +/** + * Retrieves a given message resource, and returns the specified message number + */ +Common::String RlbManager::getMessage(int resNum, int lineNum) { +	byte *msgData = getResource(RES_MESSAGE, resNum, 0); +	assert(msgData); + +	const char *srcP = (const char *)msgData; +	while (lineNum-- > 0)  +		srcP += strlen(srcP) + 1; + +	Common::String result(srcP); +	_memoryManager.deallocate(msgData); +	return result; +} + +} // end of namespace tSage diff --git a/engines/tsage/resources.h b/engines/tsage/resources.h new file mode 100644 index 0000000000..418563261f --- /dev/null +++ b/engines/tsage/resources.h @@ -0,0 +1,136 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/resources.h $ + * $Id: resources.h 145 2011-01-08 11:41:39Z dreammaster $ + * + */ + +#ifndef RING_RESOURCES_H +#define RING_RESOURCES_H + +#include "common/scummsys.h" +#include "common/file.h" +#include "common/list.h" +#include "common/str.h" +#include "common/str-array.h" +#include "common/util.h" +#include "graphics/surface.h" + +namespace tSage { + +// Magic number used by original game to identify valid memory blocks +const uint32 MEMORY_ENTRY_ID = 0xE11DA722; + +const int MEMORY_POOL_SIZE = 1000; + +enum ResourceType { RES_LIBRARY, RES_STRIP, RES_IMAGE, RES_PALETTE, RES_VISAGE, RES_SOUND, RES_MESSAGE, +		RES_FONT, RES_POINTER, RES_BANK, RES_SND_DRIVER, RES_PRIORITY, RES_CONTROL, RES_WALKRGNS, +		RES_BITMAP, RES_SAVE, RES_SEQUENCE }; + +struct MemoryHeader { +	uint32 id; +	int16 index; +	int lockCtr; +	int criticalCtr; +	uint8 tag; +	uint32 size; +}; + +struct SectionEntry { +	ResourceType resType; +	uint16 resNum; +	uint32 fileOffset; +}; + +struct ResourceEntry { +	uint16 id; +	bool isCompressed; +	uint32 fileOffset; +	uint32 size; +	uint32 uncompressedSize; +}; + +typedef Common::List<ResourceEntry> ResourceList; + +class SectionList: public Common::List<SectionEntry> { +public: +	uint32 fileOffset; +}; + +class MemoryManager { +private: +	MemoryHeader **_memoryPool; +public: +	MemoryManager(); +	~MemoryManager(); + +	uint16 allocate(uint32 size); +	byte *allocate2(uint32 size); +	byte *lock(uint32 handle); +	int indexOf(const byte *p); +	void deallocate(const byte *p); +	void deallocate(uint16 handle) { assert("TODO"); } +	uint32 getSize(const byte *p); +	void incLocks(const byte *p); +}; + +class BitReader { +private: +	Common::ReadStream &_stream; +	uint8 _remainder, _bitsLeft; +	byte readByte() { return _stream.eos() ? 0 : _stream.readByte(); } +public: +	BitReader(Common::ReadStream &s): _stream(s) {  +		numBits = 9;  +		_remainder = 0; +		_bitsLeft = 0; +	} +	uint16 readToken(); + +	int numBits; +}; + +class RlbManager { +private: +	Common::StringArray _resStrings; +	MemoryManager &_memoryManager; +private: +	Common::File _file; +	ResourceList _resources; +	SectionList _sections; + +	void loadSection(uint32 fileOffset); +	void loadIndex(); +public: +	RlbManager(MemoryManager &memManager, const Common::String filename); +	~RlbManager(); + +	byte *getResource(uint16 id, bool suppressErrors = false); +	byte *getResource(ResourceType resType, uint16 resNum, uint16 rlbNum, bool suppressErrors = false); +	void getPalette(int paletteNum, uint8 *palData, uint *startNum, uint *numEntries); +	byte *getSubResource(int resNum, int rlbNum, int index, uint *size); +	Common::String getMessage(int resNum, int lineNum); +}; + + +} // end of namespace tSage + +#endif diff --git a/engines/tsage/saveload.cpp b/engines/tsage/saveload.cpp new file mode 100644 index 0000000000..a33f0a5381 --- /dev/null +++ b/engines/tsage/saveload.cpp @@ -0,0 +1,387 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/saveload.cpp $ + * $Id: saveload.cpp 209 2011-02-06 00:46:36Z dreammaster $ + * + */ + +#include "common/savefile.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" +#include "tsage/globals.h" +#include "tsage/saveload.h" +#include "tsage/tsage.h" + +namespace tSage { + +Saver *_saver; + +SavedObject::SavedObject() { +	_saver->addObject(this); +} + +SavedObject::~SavedObject() { +	_saver->removeObject(this); +} + +/*--------------------------------------------------------------------------*/ + +Saver::Saver() { +	_macroSaveFlag = false; +	_macroRestoreFlag = false; +} + +Saver::~Saver() { +	// Internal validation that no saved object is still present +	int totalLost = 0; +	for (List<SavedObject *>::iterator i = _saver->_objList.begin(); i != _saver->_objList.end(); ++i) { +		SavedObject *so = *i; +		if (so) +			++totalLost; +	} + +	if (totalLost) +		warning("Saved object not destroyed"); +} + +/*--------------------------------------------------------------------------*/ + +void Serialiser::syncPointer(SavedObject **ptr, Common::Serializer::Version minVersion,  +		Common::Serializer::Version maxVersion) { +	int idx; +	assert(ptr); + +	if (isSaving()) { +		// Get the object index for the given pointer and write it out +		if (!*ptr) { +			idx = 0; +		} else { +			idx = _saver->blockIndexOf(*ptr); +			assert(idx > 0); +		} +		syncAsUint32LE(idx); +	} else { +		// Load in the object index and add it into the unresolved pointer list +		syncAsUint32LE(idx); +		*ptr = NULL; +		if (idx > 0) +			// For non-zero (null) pointers, create a record for later resolving it to an address +			_saver->addSavedObjectPtr(ptr, idx); +	} +} + +void Serialiser::validate(const Common::String &s, Common::Serializer::Version minVersion,  +		Common::Serializer::Version maxVersion) { +	Common::String tempStr = s; +	syncString(tempStr, minVersion, maxVersion); + +	if (isLoading() && (tempStr != s)) +		error("Savegame is corrupt"); +} + +void Serialiser::validate(int v, Common::Serializer::Version minVersion,  +		Common::Serializer::Version maxVersion) { +	int tempVal = v; +	syncAsUint32LE(tempVal, minVersion, maxVersion); +	if (isLoading() && (tempVal != v)) +		error("Savegame is corrupt"); +} + +/*--------------------------------------------------------------------------*/ + +Common::Error Saver::save(int slot, const Common::String &saveName) { +	assert(!getMacroRestoreFlag()); + +	// Signal any objects registered for notification +	_saveNotifiers.notify(false); + +	// Set fields +	_macroSaveFlag = true; +	_saveSlot = slot; + +	// Set up the serialiser +	Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(_vm->generateSaveName(slot)); +	Serialiser serialiser(NULL, saveFile); + +	// Write out the savegame header +	tSageSavegameHeader header; +	header.saveName = saveName; +	header.version = TSAGE_SAVEGAME_VERSION; +	writeSavegameHeader(saveFile, header); + +	// Save out objects that need to come at the start of the savegame +	for (List<SaveListener *>::iterator i = _listeners.begin(); i != _listeners.end(); ++i) { +		(*i)->listenerSynchronise(serialiser); +	} + +	// Save each registered SaveObject descendant object into the savegame file +	for (List<SavedObject *>::iterator i = _objList.begin(); i != _objList.end(); ++i) { +		serialiser.validate((*i)->getClassName()); +		(*i)->synchronise(serialiser); +	} + +	// Save file complete +	saveFile->writeString("END"); +	saveFile->finalize(); +	delete saveFile; + +	// Final post-save notification +	_macroSaveFlag = false; +	_saveNotifiers.notify(true); + +	return Common::kNoError; +} + +Common::Error Saver::restore(int slot) { +	assert(!getMacroSaveFlag()); + +	// Signal any objects registered for notification +	_loadNotifiers.notify(false); + +	// Set fields +	_macroSaveFlag = true; +	_saveSlot = slot; +	_unresolvedPtrs.clear(); + +	// Set up the serialiser +	Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(_vm->generateSaveName(slot)); +	Serialiser serialiser(saveFile, NULL); + +	// Read in the savegame header +	tSageSavegameHeader header; +	readSavegameHeader(saveFile, header); +	delete header.thumbnail; + +	// Load in data for objects that need to come at the start of the savegame +	for (List<SaveListener *>::iterator i = _listeners.begin(); i != _listeners.end(); ++i) { +		(*i)->listenerSynchronise(serialiser); +	} + +	// Loop through each registered object to load in the data +	for (List<SavedObject *>::iterator i = _objList.begin(); i != _objList.end(); ++i) { +		serialiser.validate((*i)->getClassName()); +		(*i)->synchronise(serialiser); +	} + +	// Loop through the remaining data of the file, instantiating new objects. +	// Note: I don't store pointers to instantiated objects here, because it's not necessary - the mere act +	// of instantiating a saved object registers it with the saver, and will then be resolved to whatever +	// object originally had a pointer to it as part of the post-processing step +	Common::String className; +	serialiser.syncString(className); +	while (className != "END") { +		SavedObject *savedObject; +		if (!_factoryPtr || ((savedObject = _factoryPtr(className)) == NULL)) +			error("Unknown class name '%s' encountered trying to restore savegame", className.c_str()); + +		// Populate the contents of the object +		savedObject->synchronise(serialiser); + +		// Move to next object +		serialiser.syncString(className); +	} + +	// Post-process any unresolved pointers to get the correct pointer +	resolveLoadPointers(); + +	delete saveFile; + +	// Final post-restore notifications +	_macroRestoreFlag = false; +	_loadNotifiers.notify(false); + +	return Common::kNoError; +} + +const char *SAVEGAME_STR = "SCUMMVM_TSAGE"; +#define SAVEGAME_STR_SIZE 13 + +bool Saver::readSavegameHeader(Common::InSaveFile *in, tSageSavegameHeader &header) { +	char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; +	header.thumbnail = NULL; + +	// Validate the header Id +	in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); +	if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE)) +		return false; + +	header.version = in->readByte(); +	if (header.version != TSAGE_SAVEGAME_VERSION) +		return false; + +	// Read in the string +	header.saveName.clear(); +	char ch; +	while ((ch = (char)in->readByte()) != '\0') header.saveName += ch; + +	// Get the thumbnail +	header.thumbnail = new Graphics::Surface(); +	if (!Graphics::loadThumbnail(*in, *header.thumbnail)) { +		delete header.thumbnail; +		header.thumbnail = NULL; +		return false; +	} + +	// Read in save date/time +	header.saveYear = in->readSint16LE(); +	header.saveMonth = in->readSint16LE(); +	header.saveDay = in->readSint16LE(); +	header.saveHour = in->readSint16LE(); +	header.saveMinutes = in->readSint16LE(); +	header.totalFrames = in->readUint32LE(); + +	return true; +} + +void Saver::writeSavegameHeader(Common::OutSaveFile *out, tSageSavegameHeader &header) { +	// Write out a savegame header +	out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1); + +	out->writeByte(TSAGE_SAVEGAME_VERSION); + +	// Write savegame name +	out->write(header.saveName.c_str(), header.saveName.size() + 1); + +	// Get the active palette +	uint32 workPal[256]; +	uint8 thumbPalette[256 * 3]; +	const byte *srcP = (const byte *)&workPal[0]; +	byte *destP = &thumbPalette[0]; +	g_system->getPaletteManager()->grabPalette((byte *)workPal, 0, 256); +	for (int idx = 0; idx < 256; ++idx, ++srcP) { +		*destP++ = *srcP++; +		*destP++ = *srcP++; +		*destP++ = *srcP++; +	} + +	// Create a thumbnail and save it +	Graphics::Surface *thumb = new Graphics::Surface(); +	Graphics::Surface s = _globals->_screenSurface.lockSurface(); +	::createThumbnail(thumb, (const byte *)s.pixels, SCREEN_WIDTH, SCREEN_HEIGHT, thumbPalette); +	Graphics::saveThumbnail(*out, *thumb); +	_globals->_screenSurface.unlockSurface(); +	delete thumb; + +	// Write out the save date/time +	TimeDate td; +	g_system->getTimeAndDate(td); +	out->writeSint16LE(td.tm_year + 1900); +	out->writeSint16LE(td.tm_mon + 1); +	out->writeSint16LE(td.tm_mday); +	out->writeSint16LE(td.tm_hour); +	out->writeSint16LE(td.tm_min); +	out->writeUint32LE(_globals->_events.getFrameNumber()); +} + +/** + * Adds a serialisable object that should be saved/restored before any other objects + */ +void Saver::addListener(SaveListener *obj) { +	_listeners.push_back(obj); +} + +/** + * Adds a listener to be notified before the saving starts + */ +void Saver::addSaveNotifier(SaveNotifierFn fn) { +	_saveNotifiers.push_back(fn); +} + +/** + * Adds a listener to be notified before the saving starts + */ +void Saver::addLoadNotifier(SaveNotifierFn fn) { +	_loadNotifiers.push_back(fn); +} + +/** + * Registers a SavedObject descendant object for being saved in savegame files + */ +void Saver::addObject(SavedObject *obj) { +	_objList.push_back(obj); +} + +/** + * Removes a SavedObject descendant object from the save object list + */ +void Saver::removeObject(SavedObject *obj) { +	_objList.remove(obj); +} + +/** + * Returns true if any savegames exist + */ +bool Saver::savegamesExist() const { +	Common::String slot1Name = _vm->generateSaveName(1); + +	Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slot1Name); +	bool result = saveFile != NULL; +	delete saveFile; +	return result; +} + +/** + * Returns the index of the saved block associated with the given saved object pointer + */ +int Saver::blockIndexOf(SavedObject *p) { +	int objIndex = 1; +	List<SavedObject *>::iterator iObj; + +	for (iObj = _objList.begin(); iObj != _objList.end(); ++iObj, ++objIndex) { +		SavedObject *iObjP = *iObj; +		if (iObjP == p) +			return objIndex; +	} + +	return 0; +} + +/** + * Returns the pointer associated with the specified object index + */ +void Saver::resolveLoadPointers() { +	if (_unresolvedPtrs.size() == 0) +		// Nothing to resolve +		return; + +	// Outer loop through the main object list +	int objIndex = 1; +	for (List<SavedObject *>::iterator iObj = _objList.begin(); iObj != _objList.end(); ++iObj, ++objIndex) { +		Common::List<SavedObjectRef>::iterator iPtr; + +		for (iPtr = _unresolvedPtrs.begin(); iPtr != _unresolvedPtrs.end(); ) { +			SavedObjectRef &r = *iPtr; +			if (r._objIndex == objIndex) { +				// Found an unresolved pointer to this object +				*r._savedObject = *iObj; +				iPtr = _unresolvedPtrs.erase(iPtr); +			} else { +				++iPtr; +			} +		} +	} +		 +	// At this point, all the unresolved pointers should have been resolved and removed +	if (_unresolvedPtrs.size() > 0) +		error("Could not resolve savegame block pointers"); +} + +} // End of namespace tSage diff --git a/engines/tsage/saveload.h b/engines/tsage/saveload.h new file mode 100644 index 0000000000..054d968105 --- /dev/null +++ b/engines/tsage/saveload.h @@ -0,0 +1,219 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/saveload.h $ + * $Id: saveload.h 209 2011-02-06 00:46:36Z dreammaster $ + * + */ + +#ifndef TSAGE_SAVELOAD_H +#define TSAGE_SAVELOAD_H + +#include "common/scummsys.h" +#include "common/list.h" +#include "common/memstream.h" +#include "common/savefile.h" +#include "common/serializer.h" + +namespace tSage { + +typedef void (*SaveNotifierFn)(bool postFlag); + +#define TSAGE_SAVEGAME_VERSION 1 + +class SavedObject; + +struct tSageSavegameHeader { +	uint8 version; +	Common::String saveName; +	Graphics::Surface *thumbnail; +	int saveYear, saveMonth, saveDay; +	int saveHour, saveMinutes; +	int totalFrames; +}; + +/*--------------------------------------------------------------------------*/ + +#define SYNC_POINTER(x) s.syncPointer((SavedObject **)&x) +#define SYNC_ENUM(FIELD, TYPE) int v_##FIELD## = (int)FIELD; s.syncAsUint16LE(v_##FIELD##); \ +	if (s.isLoading()) FIELD = (TYPE)v_##FIELD##; + +/** + * Derived serialiser class with extra synchronisation types + */ +class Serialiser: public Common::Serializer { +public: +	Serialiser(Common::SeekableReadStream *in, Common::WriteStream *out): Common::Serializer(in, out) {} + +	void syncPointer(SavedObject **ptr, Common::Serializer::Version minVersion = 0,  +		Common::Serializer::Version maxVersion = kLastVersion); +	void validate(const Common::String &s, Common::Serializer::Version minVersion = 0,  +		Common::Serializer::Version maxVersion = kLastVersion); +	void validate(int v, Common::Serializer::Version minVersion = 0,  +		Common::Serializer::Version maxVersion = kLastVersion); +}; + +/*--------------------------------------------------------------------------*/ + +class Serialisable { +public: +	virtual ~Serialisable() {} +	virtual void synchronise(Serialiser &s) = 0; +}; + +class SaveListener { +public: +	virtual ~SaveListener() {} +	virtual void listenerSynchronise(Serialiser &s) = 0; +}; + +/*--------------------------------------------------------------------------*/ + +class SavedObject: public Serialisable { +public: +	SavedObject(); +	virtual ~SavedObject(); + +	virtual Common::String getClassName() { return "SavedObject"; } +	virtual void synchronise(Serialiser &s) {} + +	static SavedObject *createInstance(const Common::String &className); +}; + +/*--------------------------------------------------------------------------*/ + +/** + * Derived list class with extra functionality + */ +template<typename T> +class List: public Common::List<T> { +public: +	bool contains(T v) { +		for (typename List<T>::iterator i = this->begin(); i != this->end(); ++i) +			if (*i == v) +				return true; +		return false; +	} + +	typedef void (*ForEachFn)(T fn); +	void forEach(ForEachFn Fn) { +		for (typename List<T>::iterator i = this->begin(); i != this->end(); ++i) +			Fn(*i); +	} + +	void synchronise(Serialiser &s) { +		int entryCount; + +		if (s.isLoading()) { +			List<T>::clear(); +			s.syncAsUint32LE(entryCount); + +			for (int idx = 0; idx < entryCount; ++idx) { +				List<T>::push_back(static_cast<T>((T)NULL)); +				T &obj = List<T>::back(); +				s.syncPointer((SavedObject **)&obj); +			} +		} else { +			// Get the list size +			entryCount = 0; +			typename List<T>::iterator i; +			for (i = List<T>::begin(); i != List<T>::end(); ++i, ++entryCount) +				; + +			// Write out list  +			s.syncAsUint32LE(entryCount); +			for (i = List<T>::begin(); i != List<T>::end(); ++i) { +				s.syncPointer((SavedObject **)&*i); +			} +		}		 +	} +}; + +/** + * Derived list class for holding function pointers + */ +template<typename T> +class FunctionList: public List<void (*)(T)> { +public: +	void notify(T v) { +		for (typename List<void (*)(T)>::iterator i = this->begin(); i != this->end(); ++i) { +			(*i)(v); +		} +	} +}; + +/*--------------------------------------------------------------------------*/ + +class SavedObjectRef { +public: +	SavedObject **_savedObject; +	int _objIndex; + +	SavedObjectRef(): _savedObject(NULL), _objIndex(-1) {} +	SavedObjectRef(SavedObject **so, int objIndex): _savedObject(so),  _objIndex(objIndex) {} +}; + +typedef SavedObject *(*SavedObjectFactory)(const Common::String &className); + +class Saver { +private: +	List<SavedObject *> _objList; +	FunctionList<bool> _saveNotifiers; +	FunctionList<bool> _loadNotifiers; +	List<SaveListener *> _listeners; + +	Common::List<SavedObjectRef> _unresolvedPtrs; +	SavedObjectFactory _factoryPtr; + +	bool _macroSaveFlag; +	bool _macroRestoreFlag; +	int _saveSlot; + +	void resolveLoadPointers(); +public: +	Saver(); +	~Saver(); + +	Common::Error save(int slot, const Common::String &saveName); +	Common::Error restore(int slot); +	static bool readSavegameHeader(Common::InSaveFile *in, tSageSavegameHeader &header); +	static void writeSavegameHeader(Common::OutSaveFile *out, tSageSavegameHeader &header); + +	void addListener(SaveListener *obj); +	void addSaveNotifier(SaveNotifierFn fn); +	void addLoadNotifier(SaveNotifierFn fn); +	void addObject(SavedObject *obj); +	void removeObject(SavedObject *obj); +	void addFactory(SavedObjectFactory fn) { _factoryPtr = fn; } +	void addSavedObjectPtr(SavedObject **ptr, int objIndex) {  +		_unresolvedPtrs.push_back(SavedObjectRef(ptr, objIndex)); +	} + +	bool savegamesExist() const; +	bool getMacroSaveFlag() const { return _macroSaveFlag; } +	bool getMacroRestoreFlag() const { return _macroRestoreFlag; } +	int blockIndexOf(SavedObject *p); +}; + +extern Saver *_saver; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/scene_logic.cpp b/engines/tsage/scene_logic.cpp new file mode 100644 index 0000000000..a890090de5 --- /dev/null +++ b/engines/tsage/scene_logic.cpp @@ -0,0 +1,2125 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/scene_logic.cpp $ + * $Id: scene_logic.cpp 232 2011-02-12 11:56:38Z dreammaster $ + * + */ + +#include "tsage/scene_logic.h" +#include "tsage/scenes.h" +#include "tsage/tsage.h" +#include "tsage/staticres.h" + +namespace tSage { + +Scene *SceneFactory::createScene(int sceneNumber) { +	switch (sceneNumber) { +	// Kziniti Palace (Introduction) +	case 10: return new Scene10(); +	// Outer Space (Introduction) +	case 15: return new Scene15(); +	// Cut-scenes for Ch'mee house in distance +	case 20: return new Scene20(); +	// Outside Ch'mee residence +	case 30: return new Scene30(); +	// Chmeee Home +	case 40: return new Scene40(); +	// By Speeders +	case 50: return new Scene50(); +	// Title screen +	case 1000: return new Scene1000(); + +	default: +		error("Unknown scene number - %d", sceneNumber); +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +//	Common::Array<int> _actions; + +DisplayHotspot::DisplayHotspot(int regionId, ...) { +	_sceneRegionId = regionId; + +	// Load up the actions +	va_list va; +	va_start(va, regionId); + +	int param = va_arg(va, int); +	while (param != LIST_END) { +		_actions.push_back(param); +		param = va_arg(va, int); +	} + +	va_end(va); +} + +bool DisplayHotspot::performAction(int action) { +	for (uint i = 0; i < _actions.size(); i += 3) { +		if (_actions[i] == action) { +			display(_actions[i + 1], _actions[i + 2], SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +			return true; +		} +	} + +	return false; +} + +/*-------------------------------------------------------------------------- + * Scene 10 - Kziniti Palace (Introduction) + * + *--------------------------------------------------------------------------*/ + +void Scene10::Scene10_Action1::signal() { +	Scene10 *parent = (Scene10 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(6); +		break; +	case 1: +		_globals->_scenePalette.addRotation(240, 254, -1); +		parent->_stripManager.start(10, this); +		break; +	case 2: +		parent->_speakerSText.setTextPos(Common::Point(20, 20)); +		parent->_speakerSText._colour1 = 10; +		parent->_speakerSText._textWidth = 160; +		parent->_stripManager.start(11, this, parent); +		break; +	case 3: +		parent->_object2.flag100(); +		parent->_object3.flag100(); +		parent->_object3.setAction(NULL); +		parent->_object4.animate(ANIM_MODE_5, this); +		break; +	case 4: +	case 9: +		parent->_object1.animate(ANIM_MODE_5, this); +		break; +	case 5: +		parent->_object2.setStrip(3); +		parent->_object2.setFrame(1); +		parent->_object2.setPosition(Common::Point(240, 51)); +		parent->_object2.unflag100(); +		 +		parent->_object3.setStrip(6); +		parent->_object3.setFrame(1); +		parent->_object3.setPosition(Common::Point(200, 76)); +		parent->_object3._numFrames = 20; +		parent->_object3.unflag100(); + +		parent->_stripManager.start(12, this, parent); +		break; +	case 6: +		parent->_object2.flag100(); +		parent->_object3.flag100(); +		parent->_object1.animate(ANIM_MODE_6, this); +		break; +	case 7: +		parent->_object3.unflag100(); +		parent->_object3.setStrip2(5); +		parent->_object3._numFrames = 10; +		parent->_object3.setPosition(Common::Point(180, 87)); +		parent->_object3.setAction(&parent->_action2); + +		parent->_object2.setStrip(4); +		parent->_object2.setFrame(1); +		parent->_object2.setPosition(Common::Point(204, 59)); +		parent->_object2.unflag100(); + +		parent->_stripManager.start(13, this, parent); +		break; +	case 8: +		parent->_object2.flag100(); +		parent->_object3.flag100(); +		parent->_object4.animate(ANIM_MODE_6, this); +		break; +	case 10: +		_globals->_soundHandler.proc1(this); +		break; +	case 11: +		_globals->_scenePalette.clearListeners(); +		_globals->_sceneManager.changeScene(15); +		break; +	} +} + +void Scene10::Scene10_Action2::signal() { +	Scene10 *parent = (Scene10 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(_globals->_randomSource.getRandomNumber(179)); +		break; +	case 1: +		parent->_object3.setFrame(1); +		parent->_object3.animate(ANIM_MODE_5, this); +		_actionIndex = 0; +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +void Scene10::postInit(SceneObjectList *OwnerList) { +	loadScene(10); +	setZoomPercents(0, 100, 200, 100); +	 +	_stripManager.addSpeaker(&_speakerSText); +	_stripManager.addSpeaker(&_speakerQText); +	_speakerSText._speakerName = "STEXT"; +	_speakerQText._speakerName = "QTEXT"; +	_speakerSText._hideObjects = false; +	_speakerQText._hideObjects = false; +	_speakerQText.setTextPos(Common::Point(140, 120)); +	_speakerQText._colour1 = 4; +	_speakerQText._textWidth = 160; +	_speakerSText.setTextPos(Common::Point(20, 20)); +	_speakerSText._colour1 = 7; +	_speakerSText._textWidth = 320; + +	_stripManager.setCallback(this); + +	_object1.postInit(); +	_object1.setVisage(10); +	_object1.setPosition(Common::Point(232, 90)); +	_object1.setPriority2(1); + +	_object2.postInit(); +	_object2.setVisage(10); +	_object2.setStrip(4); +	_object2.setFrame(1); +	_object2.setPosition(Common::Point(204, 59)); +	_object2.setPriority2(198); + +	_object3.postInit(); +	_object3.setVisage(10); +	_object3.setStrip2(5); +	_object3.setPosition(Common::Point(180, 87)); +	_object3.setPriority2(196); +	_object3.setAction(&_action2); + +	_object4.postInit(); +	_object4.setVisage(10); +	_object4.setStrip(2); +	_object4.setPosition(Common::Point(0, 209)); +	_object4.animate(ANIM_MODE_1, NULL); + +	_object5.postInit(); +	_object5.setVisage(11); +	_object5.setPosition(Common::Point(107, 146)); +	_object5.animate(ANIM_MODE_2, NULL); +	_object5._numFrames = 5; + +	_object6.postInit(); +	_object6.setVisage(11); +	_object6.setStrip(2); +	_object6.setPosition(Common::Point(287, 149)); +	_object6.animate(ANIM_MODE_2, NULL); +	_object6._numFrames = 5; + +	_globals->_sceneManager._scene->_sceneBounds.contain(_globals->_sceneManager._scene->_backgroundBounds); +	_globals->_sceneOffset.x = (_globals->_sceneManager._scene->_sceneBounds.left / 160) * 160; + +	setAction(&_action1); +	_globals->_soundHandler.startSound(5); +} + +void Scene10::stripCallback(int v) { +	switch (v) { +	case 1: +		_object2.animate(ANIM_MODE_7, -1, NULL); +		break; +	case 2: +		_object2.animate(ANIM_MODE_NONE); +		break; +	case 3: +		_object2.animate(ANIM_MODE_7, -1, NULL); +		_object3.animate(ANIM_MODE_5, NULL); +		break; +	default: +		break; +	} +} + +/*-------------------------------------------------------------------------- + * Scene 15 - Outer Space (Introduction) + * + *--------------------------------------------------------------------------*/ + +void Scene15::Scene15_Action1::signal() { +	Scene15 *parent = (Scene15 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(60); +		break; +	case 1: +		SceneItem::display(15, 0, SET_Y, 20, SET_FONT, 2, SET_BG_COLOUR, -1, SET_EXT_BGCOLOUR, 7, +				SET_WIDTH, 320, SET_KEEP_ONSCREEN, 1, LIST_END); +		setDelay(300); +		break; +	case 2: { +		SceneItem::display(15, 1, SET_Y, 20, SET_FONT, 2, SET_BG_COLOUR, -1, SET_EXT_BGCOLOUR, 7, +				SET_WIDTH, 320, SET_KEEP_ONSCREEN, 1, LIST_END); +		parent->_object1.postInit(); +		parent->_object1.setVisage(15); +		parent->_object1.setPosition(Common::Point(160, -10)); +		parent->_object1.animate(ANIM_MODE_2, NULL); +		Common::Point pt(160, 100); +		NpcMover *mover = new NpcMover(); +		parent->_object1.addMover(mover, &pt, this); +		parent->_soundHandler.startSound(7); +		break; +	} +	case 3: +		SceneItem::display(0, 0); +		_globals->_sceneManager.changeScene(20); +		break; +	}			 +} + +void Scene15::Scene15_Action1::dispatch() { +	Scene15 *parent = (Scene15 *)_globals->_sceneManager._scene; + +	if (parent->_object1._position.y < 100) +		parent->_object1.changeZoom(100 - parent->_object1._position.y); +	Action::dispatch(); +} + +/*--------------------------------------------------------------------------*/ + +void Scene15::postInit(SceneObjectList *OwnerList) { +	loadScene(15); +	Scene::postInit(); +	setZoomPercents(0, 100, 200, 100); +	_globals->_soundHandler.startSound(6); +	setAction(&_action1); +} + +/*-------------------------------------------------------------------------- + * Scene 20 - Cut-scenes where House Chmeee is in the distance + * + *--------------------------------------------------------------------------*/ + +void Scene20::Scene20_Action1::signal() { +	Scene20 *parent = (Scene20 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(120); +		break; +	case 1: +		parent->_stripManager.start(20, this); +		break; +	case 2: +		parent->_sound.proc1(this); +		break; +	case 3: +		_globals->_sceneManager._FadeMode = FADEMODE_GRADUAL; +		_globals->_sceneManager.changeScene(30);	// First game scene +		break; +	default: +		break; +	} +} + +void Scene20::Scene20_Action2::signal() { +	Scene20 *parent = (Scene20 *)_globals->_sceneManager._scene; +	NpcMover *npcMover; + +	switch (_actionIndex++) { +	case 0: +		setDelay(10); +		break; +	case 1: +		SceneItem::display(20, 1, SET_WIDTH, 200, SET_Y, 20, SET_X, 160, SET_KEEP_ONSCREEN, true,  +			SET_EXT_BGCOLOUR, 4, LIST_END); +		setDelay(120); +		break; +	case 2: { +		NpcMover *mover = new NpcMover(); +		Common::Point pt(455, 77); +		_globals->_player.addMover(mover, &pt, this); +		ObjectMover2 *mover2 = new ObjectMover2(); +		parent->_sceneObject2.addMover(mover2, 5, 10, &_globals->_player); +		ObjectMover2 *mover3 = new ObjectMover2(); +		parent->_sceneObject3.addMover(mover3, 10, 15, &_globals->_player); +		break; +	} +	case 3: { +		npcMover = new NpcMover(); +		Common::Point pt(557, 100); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 4: { +		npcMover = new NpcMover(); +		Common::Point pt(602, 90); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 5: { +		npcMover = new NpcMover(); +		Common::Point pt(618, 90); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 6: { +		npcMover = new NpcMover(); +		Common::Point pt(615, 81); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 7: { +		npcMover = new NpcMover(); +		Common::Point pt(588, 79); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 8: +		parent->_sound.proc4(); +		parent->_sound.proc1(this); +		break; +	case 9: +		SceneItem::display(0, 0, LIST_END); +		_globals->_sceneManager._FadeMode = FADEMODE_GRADUAL; +		_globals->_sceneManager.changeScene(40); +		break; +	default: +		break; +	} +} + +void Scene20::Scene20_Action3::signal() { +	Scene20 *parent = (Scene20 *)_globals->_sceneManager._scene; +	NpcMover *npcMover; + +	switch (_actionIndex++) { +	case 0: +		setDelay(120); +		break; +	case 1: { +		npcMover = new NpcMover(); +		Common::Point pt(615, 81); +		_globals->_player.addMover(npcMover, &pt, this); +		ObjectMover2 *mover1 = new ObjectMover2(); +		parent->_sceneObject2.addMover(mover1, 5, 10, &_globals->_player); +		ObjectMover2 *mover2 = new ObjectMover2(); +		parent->_sceneObject3.addMover(mover2, 20, 25, &_globals->_player); +		break; +	} +	case 2: { +		npcMover = new NpcMover(); +		Common::Point pt(618, 90); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 3: { +		_globals->_player._moveDiff = Common::Point(10, 10); +		parent->_sceneObject2._moveDiff = Common::Point(10, 10); +		parent->_sceneObject3._moveDiff = Common::Point(10, 10); +		npcMover = new NpcMover(); +		Common::Point pt(445, 132); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 4: { +		npcMover = new NpcMover(); +		Common::Point pt(151, 137); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 5: { +		npcMover = new NpcMover(); +		Common::Point pt(-15, 137); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 6: +		parent->_sound.startSound(60, this, 127); +		_globals->_soundHandler.proc4(); +		break; +	case 7: +		_globals->_sceneManager._FadeMode = FADEMODE_GRADUAL; +		_globals->_sceneManager.changeScene(90); +		break; +	default: +		break; +	} +} + +void Scene20::Scene20_Action4::signal() { +	Scene20 *parent = (Scene20 *)_globals->_sceneManager._scene; +	NpcMover *npcMover; + +	switch (_actionIndex++) { +	case 0: +		setDelay(60); +		break; +	case 1: { +		npcMover = new NpcMover(); +		Common::Point pt(486, 134); +		_globals->_player.addMover(npcMover, &pt, this); +		ObjectMover2 *mover1 = new ObjectMover2(); +		parent->_sceneObject2.addMover(mover1, 20, 35, &_globals->_player); +		break; +	} +	case 2: { +		_globals->_player._moveDiff = Common::Point(12, 12); +		parent->_sceneObject2._moveDiff = Common::Point(12, 12); +		NpcMover *mover1 = new NpcMover(); +		Common::Point pt(486, 134); +		parent->_sceneObject3.addMover(mover1, &pt, this); +		NpcMover *mover2 = new NpcMover(); +		pt = Common::Point(-15, 134); +		_globals->_player.addMover(mover2, &pt, NULL); +		NpcMover *mover3 = new NpcMover(); +		pt = Common::Point(-15, 134); +		parent->_sceneObject2.addMover(mover3, &pt, NULL); +		break; +	} +	case 3: { +		parent->_sceneObject3._moveDiff = Common::Point(20, 20); +		npcMover = new NpcMover(); +		Common::Point pt(320, 134); +		parent->_sceneObject3.addMover(npcMover, &pt, this); +		break; +	} +	case 4: { +		parent->_sound.startSound(28); +		parent->_sceneObject4.postInit(); +		parent->_sceneObject4.setVisage(21); +		parent->_sceneObject4.setStrip(3); +		parent->_sceneObject4.setPosition(Common::Point(parent->_sceneObject3._position.x - 36, +			parent->_sceneObject3._position.y - 1)); +		parent->_sceneObject4._moveDiff.x = 48; + +		ObjectMover3 *mover = new ObjectMover3(); +		parent->_sceneObject4.addMover(mover, &parent->_sceneObject2, 4, this); +		break; +	} +	case 5: { +		parent->_sound.startSound(42); +		parent->_sceneObject4.remove(); +		parent->_sceneObject2.setVisage(21); +		parent->_sceneObject2.setStrip(1); +		parent->_sceneObject2.setFrame(1); +		parent->_sceneObject2.animate(ANIM_MODE_5, NULL); +		 +		parent->_sceneObject2._moveDiff.x = 4; +		NpcMover *mover1 = new NpcMover(); +		Common::Point pt(parent->_sceneObject2._position.x - 12, parent->_sceneObject2._position.y + 5); +		parent->_sceneObject2.addMover(mover1, &pt, NULL); + +		parent->_sceneObject5.postInit(); +		parent->_sceneObject5.setVisage(21); +		parent->_sceneObject5.setStrip(3); +		parent->_sceneObject5.setPosition(Common::Point(parent->_sceneObject3._position.x - 36, +			parent->_sceneObject3._position.y - 1)); +		parent->_sceneObject5._moveDiff.x = 48; + +		ObjectMover3 *mover = new ObjectMover3(); +		parent->_sceneObject5.addMover(mover, &_globals->_player, 4, this); +		break; +	} +	case 6: { +		parent->_sound.startSound(42); +		parent->_sceneObject2.setStrip(2); +		parent->_sceneObject2.animate(ANIM_MODE_2, NULL); +		 +		parent->_sceneObject5.remove(); +		_globals->_player.setVisage(21); +		_globals->_player.setStrip(1); +		_globals->_player.setFrame(1); +		_globals->_player.animate(ANIM_MODE_5, this); +		_globals->_player._moveDiff.x = 4; + +		npcMover = new NpcMover(); +		Common::Point pt(_globals->_player._position.x - 25, _globals->_player._position.y + 5); +		_globals->_player.addMover(npcMover, &pt, this); +		break; +	} +	case 7: +		_globals->_player.setStrip(2); +		_globals->_player.animate(ANIM_MODE_2, NULL); +		parent->_sound.startSound(77, this, 127); +		break; +	case 8: +		_globals->_game.endGame(20, 0); +		break; +	default: +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +Scene20::Scene20() { +} + +void Scene20::postInit(SceneObjectList *OwnerList) { +	Scene::postInit(); +	setZoomPercents(0, 100, 200, 100); + +	_stripManager.addSpeaker(&_speakerQText); +	_stripManager.addSpeaker(&_speakerGameText); +	_speakerQText._npc = &_globals->_player; + +	if (_globals->_sceneManager._previousScene == 30) { +		_globals->_player.postInit(); +		_globals->_player.setVisage(20); +		_globals->_player.setPosition(Common::Point(405, 69)); +		_globals->_player._moveDiff = Common::Point(10, 10); +		_globals->_player.animate(ANIM_MODE_1, NULL); + +		_sceneObject2.postInit(); +		_sceneObject2.setPosition(Common::Point(400, 69)); +		_sceneObject2.animate(ANIM_MODE_1, NULL); +		 +		_sceneObject3.postInit(); +		_sceneObject3.setVisage(20); +		_sceneObject3.setPosition(Common::Point(395, 69)); +		_sceneObject3.animate(ANIM_MODE_1, NULL); + +		_sceneObject2._moveDiff = Common::Point(10, 10); +		_sceneObject3._moveDiff = Common::Point(10, 10); +		_globals->_soundHandler.startSound(20); +		_sound.startSound(21); +		_sound.proc5(1); +		setAction(&_action2); + +		_sceneBounds = Rect(320, 0, 640, 200); +	} else if (_globals->_sceneManager._previousScene == 60) { +		_globals->_player.postInit(); +		_globals->_player.setVisage(2640); +		_globals->_player.animate(ANIM_MODE_NONE, NULL); +		_globals->_player.setStrip2(1); +		_globals->_player.setFrame2(4); +		_globals->_player.setPriority2(200); +		_globals->_player.setPosition(Common::Point(425, 233)); + +		setAction(&_action1); +		_speakerQText.setTextPos(Common::Point(350, 20)); +		_speakerQText._textWidth = 260; +		_speakerGameText.setTextPos(Common::Point(350, 20)); +		_speakerGameText._textWidth = 260; +		 +		_globals->_soundHandler.startSound(8); +		_sceneBounds = Rect(320, 0, 640, 200); +	} else { +		_sound.startSound(30); +		_globals->_player.postInit(); +		_globals->_player.setVisage(20); +		_globals->_player.setPosition(Common::Point(588, 79)); +		_globals->_player._moveDiff = Common::Point(5, 5); +		_globals->_player.setPriority2(50); +		_globals->_player.animate(ANIM_MODE_1, NULL); + +		_sceneObject2.postInit(); +		_sceneObject2.setVisage(20); +		_sceneObject2.setPosition(Common::Point(583, 79)); +		_sceneObject2.animate(ANIM_MODE_1, NULL); + +		_sceneObject3.postInit(); +		_sceneObject3.setVisage(20); +		_sceneObject3.setStrip(2); +		_sceneObject2.setPosition(Common::Point(595, 79)); +		_sceneObject2.animate(ANIM_MODE_1, NULL); + +		if ((_globals->getFlag(120) && _globals->getFlag(116)) ||  +				(_globals->getFlag(117) && _globals->getFlag(119))) { +			setAction(&_action3); +		} else if (_globals->getFlag(104)) { +			_sceneMode = 21; +			setAction(&_sequenceManager, this, 21, &_globals->_player, &_sceneObject2, NULL); +		} else { +			_sceneObject3._moveDiff = Common::Point(8, 8); +			setAction(&_action4); +		} + +		_sceneBounds.centre(_globals->_player._position.x, _globals->_player._position.y); +	} + +	_globals->_player.disableControl(); +	loadScene(20); +} + +void Scene20::signal() { +	if (_sceneMode == 21) +		_globals->_sceneManager.changeScene(90); +} + +/*-------------------------------------------------------------------------- + * Scene 30 - First game scene (Outside Ch'mee house) + * + *--------------------------------------------------------------------------*/ + +void Scene30::Scene30_beamAction::signal() { +	Scene30 *parent = (Scene30 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: { +		// Disable control and move player to the doorway beam +		_globals->_player.disableControl(); +		NpcMover *mover = new NpcMover(); +		Common::Point pt(114, 198); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} + +	case 1: +		// Perform the animation of player raising hand +		_globals->_player.setVisage(31); +		_globals->_player.setStrip(1); +		_globals->_player.setFrame(1); +		_globals->_player.animate(ANIM_MODE_5, this); +		break; + +	case 2: +		// Hide the beam and lower the player's hand +		parent->_sound.startSound(10, 0, 127); +		_globals->_player.animate(ANIM_MODE_6, this); +		parent->_beam.remove(); +		break; + +	case 3: { +		// Bring the Kzin to the doorway +		_globals->_player.setVisage(0); +		_globals->_player.animate(ANIM_MODE_1, NULL); +		_globals->_player.setStrip(7); +		parent->_kzin.postInit(); +		parent->_kzin.setVisage(2801); +		parent->_kzin.animate(ANIM_MODE_1, NULL); +		parent->_kzin.setObjectWrapper(new SceneObjectWrapper()); +		parent->_kzin.setPosition(Common::Point(334, 1)); +		NpcMover *mover = new NpcMover(); +		Common::Point pt(158, 170); +		parent->_kzin.addMover(mover, &pt, this); +		_globals->_sceneItems.push_front(&parent->_kzin); +		break; +	} + +	case 4: +		// Open the door +		parent->_sound.startSound(11, 0, 127); +		parent->_door.animate(ANIM_MODE_5, this); +		break; + +	case 5: +		// Run the Kzin's talk sequence +		parent->_sound.startSound(13, 0, 127); +		_globals->_soundHandler.startSound(12, 0, 127); +		parent->_stripManager.start((parent->_sceneMode == 0) ? 30 : 37, this); +		break; + +	case 6: +		// Slight delay +		setDelay(3); +		break; + +	case 7: +		// Re-activate player control +		parent->_sceneMode = 31; +		parent->_kzin.setAction(&parent->_kzinAction); +		_globals->_player.enableControl(); + +		// End this action +		remove(); +		break; + +	default: +		break; +	} +} + +void Scene30::Scene30_kzinAction::signal() { +	Scene30 *parent = (Scene30 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(1200); +		break; +	case 1: +		_globals->_soundHandler.proc2(0); +		_globals->_player.disableControl(); +		setAction(&parent->_sequenceManager, _globals->_sceneManager._scene, 31, &parent->_kzin, &parent->_door, NULL); +		break; +	case 2: +		_globals->_player.enableControl(); +		remove(); +		break; +	default: +		break; +	} +} + +void Scene30::Scene30_ringAction::signal() { +	Scene30 *parent = (Scene30 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: {	 +		_globals->_player.disableControl(); +		parent->_kzin.setAction(NULL); +		NpcMover *mover = new NpcMover(); +		Common::Point pt(114, 198); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	 +	case 1: +		_globals->_player.checkAngle(&parent->_kzin); +		parent->_stripManager.start(32, this); +		break; + +	case 2: { +		_globals->_player.animate(ANIM_MODE_1, NULL); +		NpcMover *mover = new NpcMover(); +		Common::Point pt(143, 177); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} + +	case 3: +		parent->_sound.startSound(11, 0, 127); +		parent->_door.animate(ANIM_MODE_6, this); +		break; + +	case 4: { +		parent->_sound.startSound(13, 0, 127); +		NpcMover *kzinMover = new NpcMover(); +		Common::Point pt(354, 5); +		parent->_kzin.addMover(kzinMover, &pt, this); +		NpcMover *playerMover = new NpcMover(); +		pt = Common::Point(335, 36); +		_globals->_player.addMover(playerMover, &pt, this); +		break; +	} + +	case 5: +		break; + +	case 6: +		_globals->_sceneManager.changeScene(20); +		break; + +	default: +		break; +	} +} + +void Scene30::Scene30_talkAction::signal() { +	Scene30 *parent = (Scene30 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: { +		_globals->_player.disableControl(); +		parent->_kzin.setAction(NULL); +		NpcMover *mover = new NpcMover(); +		Common::Point pt(114, 198); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	case 1: +		_globals->_player.checkAngle(&parent->_kzin); +		parent->_stripManager.start(34, this); +		break; +	case 2: +		setDelay(5); +		break; +	case 3: +		parent->_kzin.setAction(&parent->_kzinAction); +		_globals->_player.enableControl(); +		remove(); +		break; +	default: +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +void Scene30::Scene30_kzin::doAction(int action) { +	Scene30 *parent = (Scene30 *)_globals->_sceneManager._scene; + +	switch (action) { +	case OBJECT_STUNNER: +		display2(30, 12); +		break; +	case OBJECT_SCANNER: +		display2(30, 11); +		break; +	case OBJECT_RING: +		_globals->_inventory._ring._sceneNumber = 30; +		parent->setAction(&parent->_ringAction); +		break; +	case CURSOR_LOOK: +		display2(30, 6); +		break; +	case CURSOR_USE: +		display2(30, 10); +		break; +	case CURSOR_TALK: +		_globals->_player.disableControl(); +		parent->setAction(&parent->_talkAction); +		break; +	default: +		SceneObject::doAction(action);	 +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +Scene30::Scene30(): +	_groundHotspot(9, OBJECT_SCANNER, 50, 17, CURSOR_LOOK, 30, 3, CURSOR_USE, 30, 8, LIST_END), +	_wallsHotspot(8, OBJECT_SCANNER, 50, 13, CURSOR_LOOK, 30, 0, CURSOR_USE, 30, 7, LIST_END), +	_courtyardHotspot(0, CURSOR_LOOK, 30, 4, LIST_END), +	_treeHotspot(10, OBJECT_SCANNER, 40, 39, CURSOR_LOOK, 30, 5, CURSOR_USE, 30, 9, LIST_END) { +} + +void Scene30::postInit(SceneObjectList *OwnerList) { +	Scene::postInit(); +	setZoomPercents(0, 100, 200, 100); + +	// Add the speaker classes to the strip manager +	_stripManager.addSpeaker(&_speakerQL); +	_stripManager.addSpeaker(&_speakerSR); +	_stripManager.addSpeaker(&_speakerSText); +	_stripManager.addSpeaker(&_speakerQText); +	_speakerSText._npc = &_kzin; +	_speakerQText._npc = &_globals->_player; +	 + +	// Setup player +	_globals->_player.postInit(); +	_globals->_player.setVisage(0); +	_globals->_player.animate(ANIM_MODE_1); +	_globals->_player.setObjectWrapper(new SceneObjectWrapper()); +	_globals->_player.setStrip(7); +	_globals->_player.setFrame(1); +	_globals->_player.setPosition(Common::Point(114, 198)); +	_globals->_player.changeZoom(75); +	_globals->_player.enableControl(); + +	// Set up beam object +	_beam.postInit(); +	_beam.setVisage(31); +	_beam.setStrip(2); +	_beam.setPosition(Common::Point(124, 178)); +	_beam.setPriority2(188); + +	// Set up door object +	_door.postInit(); +	_door.setVisage(30); +	_door.setPosition(Common::Point(150, 183)); + +	// Final processing and add of scene items +	_courtyardHotspot.setBounds(Rect(0, 0, 320, 200)); + +	// Add the objects and hotspots to the scene +	_globals->_sceneItems.addItems(&_beam, &_wallsHotspot, &_door, &_treeHotspot, &_groundHotspot,  +		&_courtyardHotspot, NULL); + +	// Load the scene data +	loadScene(30); +	_sceneMode = 0; +} + +void Scene30::signal() { +	if (_sceneMode == 31) { +		// Re-activate beam if the Kzin goes back inside +		_beam.postInit(); +		_beam.setVisage(31); +		_beam.setStrip(2); +		_beam.setPosition(Common::Point(124, 178)); +		_beam.setPriority2(188); +		_globals->_sceneItems.push_front(&_beam); +		_globals->_player.enableControl(); +	} else if (_sceneMode == 32) { +		_globals->_player.disableControl(); +		_sceneMode = 31; +		setAction(&_sequenceManager, _globals->_sceneManager._scene, 31, &_kzin, &_door, NULL); +	} +} + +/*-------------------------------------------------------------------------- + * Scene 40 - Chmeee Home + * + *--------------------------------------------------------------------------*/ + +void Scene40::Scene40_Action1::signal() { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(120); +		break; +	case 1: +		_globals->_events.setCursor(CURSOR_WALK); +		parent->_stripManager.start(40, this); +		break; +	case 2: +		parent->_doorway.postInit(); +		parent->_doorway.setVisage(46); +		parent->_doorway.setPosition(Common::Point(305, 61)); +		parent->_doorway.animate(ANIM_MODE_5, this); +		parent->_soundHandler.startSound(25); +		break; +	case 3: +		parent->_doorway.flag100(); +		parent->_dyingKzin.setPosition(Common::Point(296, 62)); +		_globals->_player.animate(ANIM_MODE_5, NULL); +		parent->_object1.setVisage(43); +		parent->_object1.setStrip(3); +		parent->_object1.animate(ANIM_MODE_5, NULL); +		parent->_object2.flag100(); +		parent->_object3.flag100(); +		parent->_stripManager.start(45, this); +		break; +	case 4: +		parent->_object2.remove(); +		parent->_object3.remove(); +		parent->_assassin.setVisage(42); +		parent->_assassin.setStrip(2); +		parent->_assassin.setFrame(1); +		parent->_assassin.setPosition(Common::Point(13, 171)); +		parent->_assassin.animate(ANIM_MODE_5, this); +		parent->_soundHandler.startSound(25); +		break; +	case 5: +		parent->_doorway.unflag100(); +		parent->_doorway.setVisage(42); +		parent->_doorway.setStrip(3); +		parent->_doorway.setFrame(1); +		parent->_doorway.setPosition(Common::Point(41, 144)); +		parent->_assassin.animate(ANIM_MODE_6, NULL); +		setDelay(6); +		break; +	case 6: +		parent->_doorway.setPosition(Common::Point(178, 101)); +		setDelay(6); +		break; +	case 7: +		parent->_doorway.setPosition(Common::Point(271, 69)); +		setDelay(6); +		break; +	case 8: +		parent->_doorway.remove(); +		parent->_dyingKzin.animate(ANIM_MODE_5, this); +		break; +	case 9: { +		parent->_dyingKzin.setStrip(1); +		parent->_dyingKzin.setFrame(1); +		parent->_dyingKzin._moveDiff.y = 15; +		parent->_dyingKzin.animate(ANIM_MODE_5, NULL); +		Common::Point pt(223, 186); +		NpcMover *mover = new NpcMover(); +		parent->_dyingKzin.addMover(mover, &pt, this); +		break; +	} +	case 10: { +		parent->_soundHandler.startSound(27); +		Common::Point pt(223, 184); +		NpcMover *mover = new NpcMover(); +		parent->_dyingKzin.addMover(mover, &pt, this); +		break; +	} +	case 11: { +		Common::Point pt(223, 186); +		NpcMover *mover = new NpcMover(); +		parent->_dyingKzin.addMover(mover, &pt, this); +		break; +	} +	case 12: { +		_globals->_soundHandler.startSound(26); +		_globals->_player._uiEnabled = true; +		parent->_assassin.setVisage(42); +		parent->_assassin.setPosition(Common::Point(4, 191)); +		parent->_assassin.setStrip(1); +		parent->_assassin.animate(ANIM_MODE_1, NULL); +		Common::Point pt(230, 187); +		NpcMover *mover = new NpcMover(); +		parent->_assassin.addMover(mover, &pt, this); +		break; +	} +	case 13: +		setDelay(180); +		break; +	case 14: +		parent->_assassin.setVisage(45); +		parent->_assassin.setStrip(1); +		parent->_assassin.setFrame(1); +		parent->_assassin.animate(ANIM_MODE_5, this); +		parent->_soundHandler.startSound(28); +		break; +	case 15: +		_globals->_player.disableControl(); +		parent->_object1.setVisage(40); +		parent->_object1.setStrip(4); +		parent->_object1.setFrame(1); +		parent->_object1.animate(ANIM_MODE_5, NULL); +		_globals->_player.setVisage(40); +		_globals->_player.setStrip(2); +		_globals->_player.setFrame(1); +		_globals->_player.animate(ANIM_MODE_5, this); +		break; +	case 16: +		_globals->_soundHandler.startSound(77, this); +		break; +	case 17: +		_globals->_game.endGame(40, 20); +		remove(); +		break; +	} +} + +void Scene40::Scene40_Action2::signal() { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		_globals->_player.disableControl(); +		if (parent->_assassin._position.x < 229) +			_actionIndex = 0; +		setDelay(1); +		break; +	case 1: +		parent->_assassin.animate(ANIM_MODE_NONE, NULL); +		_globals->_player.setStrip(2); +		_globals->_player.setFrame(1); +		_globals->_player.animate(ANIM_MODE_5, this); +		break; +	case 2: { +		parent->_soundHandler.startSound(28); +		parent->_doorway.postInit(); +		parent->_doorway.setVisage(16); +		parent->_doorway.setStrip2(6); +		parent->_doorway.setPriority2(200); +		parent->_doorway.setPosition(Common::Point(159, 191)); +		parent->_doorway._moveDiff = Common::Point(40, 40); +		parent->_doorway._field7A = 60; +		parent->_doorway.animate(ANIM_MODE_5, NULL); + +		Common::Point pt(271, 165); +		NpcMover *mover = new NpcMover(); +		parent->_doorway.addMover(mover, &pt, this); +		break; +	} +	case 3: +		parent->_doorway.remove(); +		parent->_assassin.setVisage(44); +		parent->_assassin._frame = 1; +		parent->_assassin.animate(ANIM_MODE_5, this); +		parent->_soundHandler.startSound(29); +		_globals->_inventory._infoDisk._sceneNumber = 40; +		break; +	case 4: +		_globals->_player.animate(ANIM_MODE_6, this); +		break; +	case 5: { +		_globals->_player.setVisage(0); +		_globals->_player.animate(ANIM_MODE_1, NULL); +		_globals->_player.setStrip(1); +		Common::Point pt(230, 195); +		PlayerMover *mover = new PlayerMover(); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	case 6: { +		_globals->_player.setStrip(7); +		parent->_object1.setVisage(2806); +		parent->_object1.animate(ANIM_MODE_1, NULL); +		SceneObjectWrapper *wrapper = new SceneObjectWrapper(); +		parent->_object1.setObjectWrapper(wrapper); +		Common::Point pt(200, 190); +		NpcMover *mover = new NpcMover(); +		parent->_object1.addMover(mover, &pt, this); +		break; +	} +	case 7: +		parent->_stripManager.start(44, this); +		break; +	case 8: { +		Common::Point pt(170, 260); +		NpcMover *mover = new NpcMover(); +		parent->_object1.addMover(mover, &pt, this); +		break; +	} +	case 9: +		parent->_dyingKzin.setAction(&parent->_action7); +		parent->_object1.remove(); +		_globals->_stripNum = 88; +		_globals->_events.setCursor(CURSOR_WALK); +		_globals->_player.enableControl(); +		parent->_assassin.setAction(&parent->_action8); +		break; +	} +} + +void Scene40::Scene40_Action3::signal() { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: { +		_globals->_player.setAction(NULL); +		_globals->_stripNum = 99; +		_globals->_player.disableControl(); +		Common::Point pt(240, 195); +		NpcMover *mover = new NpcMover(); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	case 1: +		_globals->_player.setVisage(5010); +		_globals->_player._strip = 2; +		_globals->_player._frame = 1; +		_globals->_player.animate(ANIM_MODE_4, 5, 1, this); +		break; +	case 2: +		parent->_assassin.setStrip(2); +		parent->_assassin.setFrame(1); +		_globals->_inventory._infoDisk._sceneNumber = 1; +		_globals->_player.animate(ANIM_MODE_6, this); +		break; +	case 3: +		_globals->_player.setVisage(0); +		_globals->_player.animate(ANIM_MODE_1, NULL); +		_globals->_player.setStrip(7); +		_globals->_stripNum = 88; +		_globals->_player.enableControl(); +		remove(); +		break; +	} +} + +void Scene40::Scene40_Action4::signal() { +	switch (_actionIndex++) { +	case 0: { +		Common::Point pt(178, 190); +		NpcMover *mover = new NpcMover(); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	case 1: +		_globals->_stripNum = 88; +		_globals->_player.enableControl(); +		break; +	} +} + +void Scene40::Scene40_Action5::signal() { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0:  +		setDelay(_globals->_randomSource.getRandomNumber(120)); +		break; +	case 1: +		parent->_object2.animate(ANIM_MODE_8, 1, this); +		_actionIndex = 0; +	} +} + +void Scene40::Scene40_Action6::signal() { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: { +		parent->_object1.postInit(); +		parent->_object1.setVisage(16); +		parent->_object1.setStrip2(6); +		parent->_object1.setPosition(Common::Point(313, 53)); +		parent->_object1._field7A = 60; + +		Common::Point pt(141, 194); +		NpcMover *mover = new NpcMover(); +		parent->_object1.addMover(mover, &pt, this); +		parent->_object1.animate(ANIM_MODE_5, NULL); + +		parent->_doorway.postInit(); +		parent->_doorway.setVisage(46); +		parent->_doorway.setPosition(Common::Point(305, 61)); +		parent->_doorway.animate(ANIM_MODE_5, this); +		parent->_soundHandler.startSound(25); +		break; +	} +	case 1: +		parent->_soundHandler.startSound(28); +		parent->_doorway.setPosition(Common::Point(148, 74)); +		parent->_doorway.setFrame(1); +		parent->_doorway.setStrip(2); +		parent->_doorway.animate(ANIM_MODE_5, this); +		break; +	case 2: +		remove(); +		break; +	} +} + +void Scene40::Scene40_Action7::signal() { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(_globals->_randomSource.getRandomNumber(500)); +		break; +	case 1: +		parent->_object7.postInit(); +		parent->_object7.setVisage(46); + +		if (_globals->_randomSource.getRandomNumber(32767) >= 16384) { +			parent->_object7.setStrip(3); +			parent->_object7.setPosition(Common::Point(15, 185)); +		} else { +			parent->_object7.setPosition(Common::Point(305, 61)); +			parent->_object7.setFrame(15); +		} +		break; +	case 2: +		parent->_object7.remove(); +		_actionIndex = 0; +		setDelay(60); +		break; +	} +} + +void Scene40::Scene40_Action8::signal() { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setDelay(300); +		break; +	case 1: +		_globals->_player.disableControl(); +		 +		if ((_globals->_player._position.y >= 197) || (_globals->_player._visage)) { +			_actionIndex = 1; +			setDelay(30); +		} else { +			parent->_doorway.postInit(); +			parent->_doorway.setVisage(16); +			parent->_doorway.setStrip2(6); +			parent->_doorway.setPriority2(200); +			parent->_doorway._field7A = 60; + +			if (_globals->_player._position.x >= 145) { +				parent->_doorway.setPriority2(-1); +				parent->_doorway.setPosition(Common::Point(6, 157)); +			} else { +				parent->_doorway.setPosition(Common::Point(313, 53)); +			} + +			parent->_doorway._moveDiff = Common::Point(40, 40); +			Common::Point pt(_globals->_player._position.x, _globals->_player._position.y - 18); +			NpcMover *mover = new NpcMover(); +			parent->_doorway.addMover(mover, &pt, this); +			parent->_doorway.animate(ANIM_MODE_5, NULL); +		} +		break; +	case 2: +		parent->_doorway.remove(); +		_globals->_player.setVisage(40); +		_globals->_player.setStrip(2); +		_globals->_player.setFrame(1); +		_globals->_player.animate(ANIM_MODE_5, this); +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +void Scene40::Scene40_DyingKzin::doAction(int action) { +	switch (action) { +	case OBJECT_STUNNER: +		SceneItem::display2(40, 43); +		break; +	case CURSOR_CROSSHAIRS: +		SceneItem::display2(40, 44);  +		break; +	case CURSOR_LOOK: +		SceneItem::display2(40, 12); +		break; +	case CURSOR_USE: +		SceneItem::display2(40, 18); +		break; +	default: +		SceneHotspot::doAction(action); +		break; +	} +} + +void Scene40::Scene40_Assassin::doAction(int action) { +	Scene40 *parent = (Scene40 *)_globals->_sceneManager._scene; + +	switch (action) { +	case CURSOR_CROSSHAIRS: +		if (parent->_assassin._visage == 44) +			SceneItem::display2(40, 21); +		else { +			_globals->_player.disableControl(); +			Common::Point pt(230, 187); +			NpcMover *mover = new NpcMover(); +			addMover(mover, &pt, NULL); +		} +		break; +	case OBJECT_SCANNER: +		SceneItem::display2(40, (parent->_assassin._visage == 44) ? 22 : 23); +		break; +	case CURSOR_LOOK: +		if (parent->_assassin._visage != 44) +			SceneItem::display2(40, 13); +		else +			SceneItem::display2(40, (_globals->_inventory._infoDisk._sceneNumber == 1) ? 19 : 14); +		break; +	case CURSOR_USE: +		if (parent->_assassin._visage != 44) +			SceneItem::display2(40, 15); +		else if (_globals->_inventory._infoDisk._sceneNumber == 1) +			SceneItem::display2(40, 19); +		else { +			_globals->_player.disableControl(); +			setAction(&parent->_action3); +		} +		break; +	case CURSOR_TALK: +		SceneItem::display2(40, 38); +		break; +	default: +		SceneHotspot::doAction(action); +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +void Scene40::Scene40_Item2::doAction(int action) { +	switch (action) { +	case CURSOR_CROSSHAIRS: +		SceneItem::display2(40, 35); +		_globals->_events.setCursor(CURSOR_WALK); +		break; +	case OBJECT_SCANNER: +		SceneItem::display2(40, 34); +		break; +	case CURSOR_LOOK: +		SceneItem::display2(40, 8); +		break; +	case CURSOR_USE: +		SceneItem::display2(40, 36); +		break; +	case CURSOR_TALK: +		SceneItem::display2(40, 37); +		break; +	default: +		SceneItem::doAction(action); +		break; +	} +} + +void Scene40::Scene40_Item6::doAction(int action) { +	switch (action) { +	case CURSOR_CROSSHAIRS: +		SceneItem::display2(40, 25); +		_globals->_events.setCursor(CURSOR_WALK); +		break; +	case OBJECT_SCANNER: +		SceneItem::display2(40, 42); +		break; +	case CURSOR_LOOK: +		SceneItem::display2(40, 6); +		break; +	case CURSOR_USE: +		SceneItem::display2(40, 36); +		break; +	default: +		SceneItem::doAction(action); +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +Scene40::Scene40(): +	_item1(2, OBJECT_SCANNER, 40, 24, CURSOR_CROSSHAIRS, 40, 25, CURSOR_LOOK, 40, 7, CURSOR_USE, 40, 16, LIST_END), +	_item3(5, OBJECT_SCANNER, 40, 26, CURSOR_CROSSHAIRS, 40, 27, CURSOR_LOOK, 40, 9, CURSOR_USE, 40, 17, LIST_END), +	_item4(6, OBJECT_SCANNER, 40, 31, CURSOR_CROSSHAIRS, 40, 32, CURSOR_LOOK, 40, 5, CURSOR_USE, 40, 33, LIST_END), +	_item5(0, CURSOR_LOOK, 40, 11, LIST_END), +	_item7(4, OBJECT_SCANNER, 40, 26, CURSOR_CROSSHAIRS, 40, 27, CURSOR_LOOK, 40, 9, CURSOR_USE, 40, 17, LIST_END), +	_item8(8, OBJECT_SCANNER, 40, 39, CURSOR_CROSSHAIRS, 40, 40, CURSOR_LOOK, 40, 3, CURSOR_USE, 40, 41, LIST_END) { +} + +void Scene40::postInit(SceneObjectList *OwnerList) { +	loadScene(40); +	Scene::postInit(); + +	setZoomPercents(0, 100, 200, 100); +	_globals->_stripNum = 99; + +	_stripManager.addSpeaker(&_speakerQR); +	_stripManager.addSpeaker(&_speakerSL); +	_stripManager.addSpeaker(&_speakerQText); +	_stripManager.addSpeaker(&_speakerSText); +	_stripManager.addSpeaker(&_speakerGameText); + +	_speakerGameText._colour1 = 9; +	_speakerGameText.setTextPos(Common::Point(160, 30)); +	_speakerQText._npc = &_globals->_player; +	_speakerSText._npc = &_object1; + +	_globals->_player.postInit(); +	_globals->_player.setVisage(0); +	_globals->_player.animate(ANIM_MODE_1, NULL); +	_globals->_player.setObjectWrapper(new SceneObjectWrapper()); +	_globals->_player.setPosition(Common::Point(130, 220)); +	_globals->_player.disableControl(); + +	if (_globals->_sceneManager._previousScene == 20) { +		_globals->_soundHandler.startSound(24); +		_globals->_player.setVisage(43); +		 +		_object1.postInit(); +		_object1.setVisage(41); +		_object1.setPosition(Common::Point(105, 220)); +		_object2.postInit(); +		_object2.setVisage(41); +		_object2.setStrip(6); +		_object2.setPriority2(200); +		_object2.setPosition(Common::Point(94, 189)); +		_object2.setAction(&_action5); + +		_object3.postInit(); +		_object3.setVisage(41); +		_object3.setStrip(5); +		_object3.setPriority2(205); +		_object3.setPosition(Common::Point(110, 186)); +		_object3._numFrames = 2; +		_object3.animate(ANIM_MODE_8, NULL, NULL); +		 +		_assassin.postInit(); +		_assassin.setPosition(Common::Point(-40, 191)); +		_globals->_sceneItems.push_back(&_assassin); + +		_dyingKzin.postInit(); +		_dyingKzin.setVisage(40); +		_dyingKzin.setStrip(6); +		_dyingKzin.setPosition(Common::Point(-90, 65)); +		_dyingKzin.setPriority2(170); + +		setAction(&_action1); +	} else { +		_doorway.postInit(); +		_doorway.setVisage(46); +		_doorway.setPosition(Common::Point(148, 74)); +		_doorway.setStrip(2); +		_doorway.setFrame(_doorway.getFrameCount()); +		 +		_dyingKzin.postInit(); +		_dyingKzin.setVisage(40); +		_dyingKzin.setPosition(Common::Point(205, 183)); +		_dyingKzin.setPriority2(170); +		_dyingKzin._frame = 9; +		_dyingKzin.setAction(&_action7); + +		_assassin.postInit(); +		_assassin.setVisage(44); +		_assassin.setPosition(Common::Point(230, 187)); +		_assassin.setAction(&_action8); + +		if (_globals->_inventory._infoDisk._sceneNumber == 40) { +			_assassin.setStrip(1); +			_assassin.setFrame(_assassin.getFrameCount()); +		} else { +			_assassin.setStrip(2); +		} + +		_globals->_sceneItems.push_back(&_assassin); +		_globals->_player.setPosition(Common::Point(170, 220)); + +		setAction(&_action4); +	} + +	_item5.setBounds(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); +	_item6._sceneRegionId = 3; +	_item2._sceneRegionId = 7; +	 +	_globals->_sceneItems.addItems(&_dyingKzin, &_item8, &_item1, &_item2, &_item3, &_item4,  +			&_item6, &_item7, &_item5, NULL); +} + +void Scene40::signal() { +	if (_sceneMode == 41) +		_globals->_sceneManager.changeScene(50); +} + +void Scene40::dispatch() { +	if ((_globals->_stripNum == 88) && (_globals->_player._position.y >= 197)) { +		_globals->_player.disableControl(); +		_globals->_stripNum = 0; +		_globals->_player.setAction(NULL); +		_sceneMode = 41; +		setAction(&_sequenceManager, this, 41, &_globals->_player, NULL); + +		if (_globals->_sceneManager._previousScene == 20) { +			_dyingKzin.setAction(&_action6); +		} +	} + +	Scene::dispatch(); +} + +/*-------------------------------------------------------------------------- + * Scene 50 - By Speeders + * + *--------------------------------------------------------------------------*/ + +void Scene50::Scene50_Action1::signal() { +	Scene50 *parent = (Scene50 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		setAction(&parent->_sequenceManager, this, 54, &_globals->_player, NULL); +		break; +	case 1: +		_globals->_events.setCursor(CURSOR_WALK); +		parent->_stripManager.start(63, this); +		break; +	case 2: +		if (parent->_stripManager._field2E8 != 107) { +			_globals->_player.enableControl(); +			remove(); +		} else { +			Common::Point pt(282, 139); +			NpcMover *mover = new NpcMover(); +			_globals->_player.addMover(mover, &pt, this); +		} +		break; +	case 3: +		_globals->_stripNum = -1; +		_globals->_sceneManager.changeScene(60); +		break; +	} +} + +void Scene50::Scene50_Action2::signal() { +	Scene50 *parent = (Scene50 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		_globals->_player.disableControl(); +		parent->_stripManager.start(66, this); +		break; +	case 1: { +		Common::Point pt(141, 142); +		NpcMover *mover = new NpcMover(); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	case 2: +		_globals->_sceneManager.changeScene(40); +		remove(); +		break; +	} +} + +void Scene50::Scene50_Action3::signal() { +	switch (_actionIndex++) { +	case 0: { +		_globals->_player.disableControl(); +		Common::Point pt(136, 185); +		NpcMover *mover = new NpcMover(); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	case 1: +		_globals->_sceneManager.changeScene(60); +		remove(); +		break; +	} +}		 + +/*--------------------------------------------------------------------------*/ + +void Scene50::Scene50_Object1::doAction(int action) { +	Scene50 *parent = (Scene50 *)_globals->_sceneManager._scene; + +	switch (action) { +	case OBJECT_STUNNER: +		SceneItem::display2(50, 20); +		break; +	case OBJECT_SCANNER: +		SceneItem::display2(50, 19);  +		break; +	case CURSOR_LOOK: +		SceneItem::display2(50, 4); +		break; +	case CURSOR_USE: +		SceneItem::display2(50, 21); +		break; +	case CURSOR_TALK: +		_globals->_player.disableControl(); +		parent->_sceneMode = 52; +		parent->setAction(&parent->_sequenceManager, parent, 52, NULL); +		break; +	default: +		SceneHotspot::doAction(action); +		break; +	} +} + +void Scene50::Scene50_Object2::doAction(int action) { +	Scene50 *parent = (Scene50 *)_globals->_sceneManager._scene; + +	switch (action) { +	case OBJECT_STUNNER: +		SceneItem::display2(50, 11); +		break; +	case OBJECT_SCANNER: +		SceneItem::display2(50, 10);  +		break; +	case CURSOR_LOOK: +		SceneItem::display2(50, 1); +		break; +	case OBJECT_INFODISK: +	case CURSOR_USE: +		_globals->_stripNum = 50; +		parent->setAction(&parent->_action3); +		break; +	default: +		SceneHotspot::doAction(action); +		break; +	} +} + +void Scene50::Scene50_Object3::doAction(int action) { +	Scene50 *parent = (Scene50 *)_globals->_sceneManager._scene; + +	switch (action) { +	case OBJECT_STUNNER: +		SceneItem::display2(50, 11); +		break; +	case OBJECT_SCANNER: +		SceneItem::display2(50, 10);  +		break; +	case CURSOR_LOOK: +		SceneItem::display2(50, 1); +		break; +	case OBJECT_INFODISK: +	case CURSOR_USE: +		SceneItem::display2(50, 8); +		break; +	case CURSOR_TALK: +		_globals->_player.disableControl(); +		parent->_sceneMode = 52; +		parent->setAction(&parent->_sequenceManager, parent, 52, NULL); +		break; +	default: +		SceneHotspot::doAction(action); +		break; +	} +} + +void Scene50::Scene50_Object4::doAction(int action) { +	Scene50 *parent = (Scene50 *)_globals->_sceneManager._scene; + +	switch (action) { +	case OBJECT_STUNNER: +		SceneItem::display2(50, 11); +		break; +	case OBJECT_SCANNER: +		SceneItem::display2(50, 10);  +		break; +	case CURSOR_LOOK: +		SceneItem::display2(50, 1); +		break; +	case OBJECT_INFODISK: +	case CURSOR_USE: +		_globals->_player.disableControl(); +		_globals->_stripNum = 0; +		parent->_sceneMode = 51; +		parent->setAction(&parent->_sequenceManager, parent, 51, &_globals->_player, NULL); +		break; +	default: +		SceneHotspot::doAction(action); +		break; +	} +} + +/*--------------------------------------------------------------------------*/ + +Scene50::Scene50():  +		_item0(0, CURSOR_LOOK, 50, 3, LIST_END), +		_item1(0, OBJECT_SCANNER, 50, 15, CURSOR_USE, 50, 16, CURSOR_LOOK, 50, 3, LIST_END), +		_item2(0, CURSOR_LOOK, 50, 7, LIST_END), +		_item3(8, OBJECT_STUNNER, 50, 14, OBJECT_SCANNER, 50, 13, CURSOR_LOOK, 50, 3, LIST_END), +		_item4(9, OBJECT_SCANNER, 40, 39, OBJECT_STUNNER, 40, 40, CURSOR_USE, 40, 41, CURSOR_LOOK, 50, 5, LIST_END), +		_item5(10, OBJECT_SCANNER, 50, 17, OBJECT_STUNNER, 50, 18, CURSOR_LOOK, 50, 6, CURSOR_USE, 30, 8, LIST_END) { +} + +void Scene50::postInit(SceneObjectList *OwnerList) { +	loadScene(50); +	Scene::postInit(); +	setZoomPercents(0, 100, 200, 100); + +	_stripManager.addSpeaker(&_speakerQText); +	_stripManager.addSpeaker(&_speakerSText); +	 +	_globals->_player.postInit(); +	_globals->_player.setVisage(0); +	_globals->_player.animate(ANIM_MODE_1, NULL); +	_globals->_player.setObjectWrapper(NULL); +	_globals->_player._canWalk = false; +	_globals->_player.changeZoom(75); +	_globals->_player._moveDiff.y = 3; + +	if (_globals->_sceneManager._previousScene == 40) { +		_globals->_player.setPosition(Common::Point(128, 123)); +	} else if (_globals->_stripNum == 50) { +		_globals->_player.setPosition(Common::Point(136, 185)); +	} else { +		_globals->_player.setPosition(Common::Point(270, 143)); +	} + +	_object2.postInit(); +	_object2.setVisage(2331); +	_object2.setStrip(6); +	_object2.setPosition(Common::Point(136, 192)); +	_object2.setPriority2(200); + +	_object3.postInit(); +	_object3.setVisage(2337); +	_object3.setStrip(6); +	_object3.setPosition(Common::Point(260, 180)); +	_object3.setPriority2(200); + +	_object4.postInit(); +	_object4.setVisage(2331); +	_object4.setStrip(6); +	_object4.setPosition(Common::Point(295, 144)); +	_object4.setPriority2(178); + +	_globals->_sceneItems.addItems(&_object3, &_object4, NULL); + +	if (!_globals->getFlag(101)) { +		_globals->_player.disableControl(); +		_globals->setFlag(101); +		setAction(&_action1); +	} else { +		_globals->_player.enableControl(); + +		if (_globals->_sceneManager._previousScene == 40) { +			_globals->_player.disableControl(); +			_sceneMode = 54; +			setAction(&_sequenceManager, this, 54, &_globals->_player, NULL); +		} +	} + +	_globals->_sceneItems.addItems(&_item4, &_item5, NULL); +	_item0.setBounds(Rect(200, 0, 320, 200)); +	_globals->_sceneItems.push_back(&_item0); +	_rect1 = Rect(80, 108, 160, 112); +} + +/*-------------------------------------------------------------------------- + * Scene 1000 - Title Screen + * + *--------------------------------------------------------------------------*/ + +void Scene1000::Scene1000_Action1::signal() { +	Scene1000 *parent = (Scene1000 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		_globals->_player.disableControl(); +		setDelay(10); +		break; +	case 1: +		parent->_object4.postInit(); +		parent->_object4.setVisage(1001); +		parent->_object4._frame = 1; +		parent->_object4.setStrip2(5); +		parent->_object4.changeZoom(100); +		parent->_object4.animate(ANIM_MODE_2, NULL); +		parent->_object4.setPosition(Common::Point(403, 163)); +		setDelay(90); +		break; +	case 2: { +		SceneItem::display(0, 0); +		parent->_object4.remove(); +		parent->_object1.changeZoom(-1); +		NpcMover *mover = new NpcMover(); +		Common::Point pt(180, 100); +		parent->_object1.addMover(mover, &pt, this); +		break; +	} +	case 3: +		_globals->_sceneManager.changeScene(1400); +		break; +	} + +} + +void Scene1000::Scene1000_Action2::signal() { +	switch (_actionIndex++) { +	case 0: +		_globals->_player.disableControl(); +		setDelay(10); +		break; +	case 1: +		SceneItem::display(1000, 0, SET_Y, 20, SET_FONT, 2, SET_BG_COLOUR, -1, +				SET_EXT_BGCOLOUR, 35, SET_WIDTH, 200, SET_KEEP_ONSCREEN, 1, LIST_END); +		setDelay(180); +		break; +	case 2: +		SceneItem::display(0, 0); +		_globals->_sceneManager.changeScene(2000); +		break; +	default: +		break; +	} +} + +void Scene1000::Scene1000_Action3::signal() { +	Scene1000 *parent = (Scene1000 *)_globals->_sceneManager._scene; + +	switch (_actionIndex++) { +	case 0: +		_globals->_sceneManager._scene->loadBackground(0, 0); +		setDelay(60); +		break; +	case 1: { +		NpcMover *mover = new NpcMover(); +		Common::Point pt(158, 31); +		parent->_object3.addMover(mover, &pt, this); +		break; +	} +	case 2: +	case 3: +		setDelay(60); +		break; +	case 4: +		_globals->_player.unflag100(); +		setDelay(240); +		break; +	case 5: { +		// Intro.txt file presence is used to allow user option to skip the introduction +		_globals->_player.enableControl(); +		Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading("Intro.txt"); +		if (!in) { +			// File not present, so create it +			Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving("Intro.txt"); +			out->finalize(); +			delete out; +			setDelay(1); +		} else { +			delete in; + +			// Prompt user for whether to start play or watch introduction +			if (MessageDialog::show2(WATCH_INTRO_MSG, START_PLAY_BTN_STRING, INTRODUCTION_BTN_STRING) == 0) { +				_actionIndex = 20; +				_globals->_soundHandler.proc1(this); +			} else { +				setDelay(1); +			} + +			_globals->_player.disableControl(); +		} +		break; +	} +	case 6: { +		parent->_object3.remove(); +		_globals->_player.setStrip2(2); +		NpcMover *mover = new NpcMover(); +		Common::Point pt(480, 100); +		_globals->_player.addMover(mover, &pt, this); +		break; +	} +	case 7: +		_globals->_scenePalette.loadPalette(1002); +		_globals->_scenePalette.refresh(); +		_globals->_scenePalette.addRotation(80, 95, -1); +		parent->_object3.postInit(); +		parent->_object3.setVisage(1002); +		parent->_object3.setStrip(1); +		parent->_object3.setPosition(Common::Point(284, 122)); +		parent->_object3.changeZoom(1); + +		zoom(true); +		setDelay(200); +		break; +	case 8: +		zoom(false); +		setDelay(10); +		break; +	case 9: +		parent->_object3.setStrip(2); +		parent->_object3.setPosition(Common::Point(285, 155)); + +		zoom(true); +		setDelay(400); +		break; +	case 10: +		zoom(false); +		setDelay(10); +		break; +	case 11: +		parent->_object3.setStrip(3); +		parent->_object3.setPosition(Common::Point(279, 172)); + +		zoom(true); +		setDelay(240); +		break; +	case 12: +		zoom(false); +		setDelay(10); +		break; +	case 13: +		parent->_object3.setStrip(4); +		parent->_object3.setPosition(Common::Point(270, 128)); + +		zoom(true); +		setDelay(300); +		break; +	case 14: +		zoom(false); +		setDelay(10); +		break; +	case 15: +		parent->_object3.setStrip(1); +		parent->_object3.setFrame(2); +		parent->_object3.setPosition(Common::Point(283, 137)); + +		zoom(true); +		setDelay(300); +		break; +	case 16: +		zoom(false); +		setDelay(10); +		break; +	case 17: +		parent->_object3.setStrip(5); +		parent->_object3.setFrame(1); +		parent->_object3.setPosition(Common::Point(292, 192)); +		 +		zoom(true); +		setDelay(300); +		break; +	case 18: +		zoom(false); +		_globals->_scenePalette.clearListeners(); +		_globals->_soundHandler.proc1(this); +		break; +	case 19: +		_globals->_sceneManager.changeScene(10); +		break; +	case 20: +		_globals->_sceneManager.changeScene(30); +		break; +	default: +		break; +	}		 +} + +void Scene1000::Scene1000_Action3::zoom(bool up) { +	Scene1000 *parent = (Scene1000 *)_globals->_sceneManager._scene; + +	if (up) { +		while ((parent->_object3._percent < 100) && !_vm->shouldQuit()) { +			parent->_object3.changeZoom(parent->_object3._percent + 5); +			_globals->_sceneObjects->draw(); +			_globals->_events.delay(1); +		} +	} else { +		while ((parent->_object3._percent > 0) && !_vm->shouldQuit()) { +			parent->_object3.changeZoom(parent->_object3._percent - 5); +			_globals->_sceneObjects->draw(); +			_globals->_events.delay(1); +		} +	} +} + +/*--------------------------------------------------------------------------*/ + +void Scene1000::postInit(SceneObjectList *OwnerList) { +	Scene::postInit(); +	setZoomPercents(0, 100, 200, 100); + +	if (_globals->_sceneManager._previousScene == 2000) { +		setZoomPercents(150, 10, 180, 100); +		_object1.postInit(); +		_object1.setVisage(1001); +		_object1._strip = 7; +		_object1.animate(ANIM_MODE_2, 0); +		_object1._moveDiff = Common::Point(1, 1); +		_object1.setPosition(Common::Point(120, 180)); + +		setAction(&_action2); + +		_globals->_sceneManager._scene->_sceneBounds.centre(_object1._position.x, _object1._position.y); +		_globals->_sceneManager._scene->_sceneBounds.contain(_globals->_sceneManager._scene->_backgroundBounds); +		 +		_globals->_sceneOffset.x = (_globals->_sceneManager._scene->_sceneBounds.left / 160) * 160; +		_globals->_soundHandler.startSound(114); +	} else if (_globals->_sceneManager._previousScene == 2222) { +		setZoomPercents(150, 10, 180, 100); +		_object1.postInit(); +		_object1.setVisage(1001); +		_object1._strip = 7; +		_object1.animate(ANIM_MODE_2, 0); +		_object1._moveDiff = Common::Point(2, 2); +		_object1.setPosition(Common::Point(120, 180)); +		 +		_globals->_sceneManager._scene->_sceneBounds.centre(_object1._position.x, _object1._position.y); +		_globals->_sceneManager._scene->_sceneBounds.contain(_globals->_sceneManager._scene->_backgroundBounds); +		_globals->_sceneOffset.x = (_globals->_sceneManager._scene->_sceneBounds.left / 160) * 160; + +		setAction(&_action1); +	} else { +		_globals->_soundHandler.startSound(4); +		setZoomPercents(0, 10, 30, 100); +		_object3.postInit(); +		_object3.setVisage(1050); +		_object3.changeZoom(-1); +		_object3.setPosition(Common::Point(158, 0)); +		 +		_globals->_player.postInit(); +		_globals->_player.setVisage(1050); +		_globals->_player.setStrip(3); +		_globals->_player.setPosition(Common::Point(160, 191)); +		_globals->_player._moveDiff.x = 12; +		_globals->_player.flag100(); +		_globals->_player.disableControl(); + +		_globals->_sceneManager._scene->_sceneBounds.centre(_object3._position.x, _object3._position.y); + +		setAction(&_action3); +	} + +	loadScene(1000); +} + +/*--------------------------------------------------------------------------*/ + + +} // End of namespace tSage diff --git a/engines/tsage/scene_logic.h b/engines/tsage/scene_logic.h new file mode 100644 index 0000000000..096d57a636 --- /dev/null +++ b/engines/tsage/scene_logic.h @@ -0,0 +1,382 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/scene_logic.h $ + * $Id: scene_logic.h 232 2011-02-12 11:56:38Z dreammaster $ + * + */ + +#ifndef TSAGE_SCENE_LOGIC_H +#define TSAGE_SCENE_LOGIC_H + +#include "common/scummsys.h" +#include "tsage/events.h" +#include "tsage/core.h" +#include "tsage/scenes.h" +#include "tsage/globals.h" + +namespace tSage { + +class SceneFactory { +public: +	static Scene *createScene(int sceneNumber); +}; + +class DisplayHotspot: public SceneHotspot { +private: +	Common::Array<int> _actions; +	bool performAction(int action); +public: +	DisplayHotspot(int regionId, ...); + +	virtual void doAction(int action) {  +		if (!performAction(action)) +			SceneHotspot::doAction(action); +	} +}; + +/*--------------------------------------------------------------------------*/ + +class Scene10: public Scene { +	/* Actions */ +	class Scene10_Action1: public Action { +	public: +		virtual void signal(); +	}; +	class Scene10_Action2: public Action { +	public: +		virtual void signal(); +	}; +public: +	Speaker _speakerSText; +	Speaker _speakerQText; +	Scene10_Action1 _action1; +	Scene10_Action2 _action2; +	SceneObject _object1, _object2, _object3; +	SceneObject _object4, _object5, _object6; + +	virtual void stripCallback(int v); +	virtual void postInit(SceneObjectList *OwnerList = NULL); +}; + +class Scene15: public Scene { +	/* Actions */ +	class Scene15_Action1: public Action { +	public: +		virtual void signal(); +		virtual void dispatch(); +	}; +public: +	Scene15_Action1 _action1; +	SceneObject _object1; +	SoundHandler _soundHandler; + +	virtual void postInit(SceneObjectList *OwnerList = NULL); +}; + +class Scene20: public Scene { +	/* Actions */ +	class Scene20_Action1: public Action { +	public: +		virtual void signal(); +	}; +	class Scene20_Action2: public Action { +	public: +		virtual void signal(); +	}; +	class Scene20_Action3: public Action { +	public: +		virtual void signal(); +	}; +	class Scene20_Action4: public Action { +	public: +		virtual void signal(); +	}; +public: +	SequenceManager _sequenceManager; +	SpeakerQText _speakerQText; +	SpeakerGameText _speakerGameText; +	Scene20_Action1 _action1; +	Scene20_Action2 _action2; +	Scene20_Action3 _action3; +	Scene20_Action4 _action4; +	SceneObject _sceneObject1, _sceneObject2, _sceneObject3, _sceneObject4, _sceneObject5; +	SoundHandler _sound; +public: +	Scene20(); +	virtual ~Scene20() {} + +	virtual void postInit(SceneObjectList *OwnerList = NULL); +	virtual void signal(); +}; + +class Scene30: public Scene { +	/* Scene objects */ +	// Doorway beam sensor +	class Scene30_beam: public SceneObject { +	public: +		virtual void doAction(int action) { +			if (action == OBJECT_SCANNER) +				display(30, 14, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +			else if (action == CURSOR_LOOK) +				display(30, 2, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +			else if (action == CURSOR_USE) { +				Scene30 *parent = (Scene30 *)_globals->_sceneManager._scene; +				parent->setAction(&parent->_beamAction); +			} else +				SceneObject::doAction(action); +		} +	}; + +	// Doorway object +	class Scene30_door: public SceneObject { +	public: +		virtual void doAction(int action) { +			if (action == OBJECT_SCANNER) +				display(30, 13, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +			else if (action == CURSOR_LOOK) +				display(30, 1, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +			else if (action == CURSOR_USE) +				display(30, 7, SET_WIDTH, 200, SET_EXT_BGCOLOUR, 7, LIST_END); +			else +				SceneObject::doAction(action); +		} +	}; + +	// Kzin object +	class Scene30_kzin: public SceneObject { +	public: +		virtual void doAction(int action); +	}; + +	/* Actions */ +	class Scene30_beamAction: public Action { +	public: +		virtual void signal(); +	}; +	class Scene30_kzinAction: public Action { +	public: +		virtual void signal(); +	}; +	class Scene30_ringAction: public Action { +	public: +		virtual void signal(); +	}; +	class Scene30_talkAction: public Action { +	public: +		virtual void signal(); +	}; + +public: +	SoundHandler _sound; +	DisplayHotspot _groundHotspot, _wallsHotspot, _courtyardHotspot, _treeHotspot; +	Scene30_beam _beam; +	Scene30_door _door; +	Scene30_kzin _kzin; + +	Scene30_beamAction _beamAction; +	Scene30_kzinAction _kzinAction; +	Scene30_ringAction _ringAction; +	Scene30_talkAction _talkAction; +	SequenceManager _sequenceManager; + +	SpeakerSR _speakerSR; +	SpeakerQL _speakerQL; +	SpeakerSText _speakerSText; +	SpeakerQText _speakerQText; +public: +	Scene30(); +	virtual ~Scene30() {} + +	virtual void postInit(SceneObjectList *OwnerList = NULL); +	virtual void signal(); +}; + +class Scene40: public Scene { +	/* Actions */ +	class Scene40_Action1: public Action { +	public: +		virtual void signal(); +	}; +	class Scene40_Action2: public Action { +	public: +		virtual void signal(); +	}; +	class Scene40_Action3: public Action { +	public: +		virtual void signal(); +	}; +	class Scene40_Action4: public Action { +	public: +		virtual void signal(); +	}; +	class Scene40_Action5: public Action { +	public: +		virtual void signal(); +	}; +	class Scene40_Action6: public Action { +	public: +		virtual void signal(); +	}; +	class Scene40_Action7: public Action { +	public: +		virtual void signal(); +	}; +	class Scene40_Action8: public Action { +	public: +		virtual void signal(); +	}; + +	/* Objects */ +	class Scene40_DyingKzin: public SceneObject { +	public: +		virtual void doAction(int action); +	}; +	class Scene40_Assassin: public SceneObject { +	public: +		virtual void doAction(int action); +	}; + +	/* Items */ +	class Scene40_Item2: public SceneItem { +	public: +		virtual void doAction(int action); +	}; +	class Scene40_Item6: public SceneItem { +	public: +		virtual void doAction(int action); +	}; +	class Scene40_Item8: public SceneItem { +	public: +		virtual void doAction(int action); +	}; +public: +	SequenceManager _sequenceManager; +	SpeakerSL _speakerSL; +	SpeakerQR _speakerQR; +	SpeakerQText _speakerQText; +	SpeakerSText _speakerSText; +	SpeakerGameText _speakerGameText; +	SoundHandler _soundHandler; +	Scene40_Action1 _action1; +	Scene40_Action2 _action2; +	Scene40_Action3 _action3; +	Scene40_Action4 _action4; +	Scene40_Action5 _action5; +	Scene40_Action6 _action6; +	Scene40_Action7 _action7; +	Scene40_Action8 _action8; +	SceneObject _object1, _object2, _object3; +	Scene40_DyingKzin _dyingKzin; +	Scene40_Assassin _assassin; +	SceneObject _doorway, _object7, _object8; +	DisplayHotspot _item1; +	Scene40_Item2 _item2; +	DisplayHotspot _item3, _item4, _item5; +	Scene40_Item6 _item6; +	DisplayHotspot _item7, _item8; + +	Scene40(); +	virtual void postInit(SceneObjectList *OwnerList = NULL); +	virtual void signal(); +	virtual void dispatch(); +}; + +class Scene50: public Scene { +	/* Actions */ +	class Scene50_Action1: public Action { +	public: +		virtual void signal(); +	}; +	class Scene50_Action2: public Action { +	public: +		virtual void signal(); +	}; +	class Scene50_Action3: public Action { +	public: +		virtual void signal(); +	}; +	 +	/* Objects */ +	class Scene50_Object1: public SceneObject { +	public: +		virtual void doAction(int action); +	}; +	class Scene50_Object2: public SceneObject { +	public: +		virtual void doAction(int action); +	}; +	class Scene50_Object3: public SceneObject { +	public: +		virtual void doAction(int action); +	}; +	class Scene50_Object4: public SceneObject { +	public: +		virtual void doAction(int action); +	}; + +public: +	SequenceManager _sequenceManager; +	Scene50_Action1 _action1; +	Scene50_Action2 _action2; +	Scene50_Action3 _action3; +	Scene50_Object1 _object1; +	Scene50_Object2 _object2; +	Scene50_Object3 _object3; +	Scene50_Object4 _object4; +	Rect _rect1; +	SpeakerSText _speakerSText; +	SpeakerQText _speakerQText; +	DisplayHotspot _item0, _item1, _item2; +	DisplayHotspot _item3, _item4, _item5; + +	Scene50(); +	virtual void postInit(SceneObjectList *OwnerList = NULL); +}; + +class Scene1000: public Scene { +	/* Actions */ +	class Scene1000_Action1: public Action { +	public: +		virtual void signal(); +	}; +	class Scene1000_Action2: public Action { +	public: +		virtual void signal(); +	}; +	class Scene1000_Action3: public Action { +	private: +		void zoom(bool up); +	public: +		virtual void signal(); +	}; + +public: +	SceneObject _object1, _object2, _object3, _object4; +	Scene1000_Action1 _action1; +	Scene1000_Action2 _action2; +	Scene1000_Action3 _action3; + +	virtual void postInit(SceneObjectList *OwnerList = NULL); +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/scenes.cpp b/engines/tsage/scenes.cpp new file mode 100644 index 0000000000..d8aa80bdf2 --- /dev/null +++ b/engines/tsage/scenes.cpp @@ -0,0 +1,431 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/scenes.cpp $ + * $Id: scenes.cpp 229 2011-02-12 06:50:14Z dreammaster $ + * + */ + +#include "tsage/scenes.h" +#include "tsage/globals.h" +#include "tsage/scene_logic.h" +#include "tsage/tsage.h" + +namespace tSage { + +SceneManager::SceneManager() {  +	_scene = NULL; +	_hasPalette = false; +	_sceneNumber = -1;  +	_nextSceneNumber = -1; +	_FadeMode = FADEMODE_GRADUAL; +	_scrollerRect = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); +	_saver->addListener(this); +} + +SceneManager::~SceneManager() { +	delete _scene; +} + +void SceneManager::setNewScene(int sceneNumber) { +	_nextSceneNumber = sceneNumber; +} + +void SceneManager::checkScene() { +	if (_nextSceneNumber != -1) { +		sceneChange(); +		_nextSceneNumber = -1; +	} + +	_globals->_sceneListeners.forEach(SceneHandler::handleListener); +} + +void SceneManager::sceneChange() { +	// Clear the scene objects +	List<SceneObject *>::iterator io = _globals->_sceneObjects->begin(); +	while (io != _globals->_sceneObjects->end()) { +		SceneObject *sceneObj = *io; +		++io; +		sceneObj->removeObject(); +	} + +	// Clear the scene change listeners +	_globals->_sceneManager._sceneChangeListeners.clear(); + +	// Clear the hotspot list +	List<SceneItem *>::iterator ii = _globals->_sceneItems.begin(); +	while (ii != _globals->_sceneItems.end()) { +		SceneItem *sceneItem = *ii; +		++ii; +		sceneItem->remove(); +	} +		 +	// TODO: Clear _list_45BAA list + +	// If there is an active scene, deactivate it +	if (_scene) { +		_previousScene = _sceneNumber; + +		delete _scene; +		_scene = NULL; +		_sceneNumber = -1; +	} + +	// Set the next scene to be active +	_sceneNumber = _nextSceneNumber; + +	// TODO: Unknown check of word_45CD3 / call to saver method + +	// Free any regions +	disposeRegions(); + +	// Instantiate and set the new scene +	_scene = getNewScene(); +	_scene->postInit(); +} + +Scene *SceneManager::getNewScene() { +	return SceneFactory::createScene(_nextSceneNumber); +} + +void SceneManager::fadeInIfNecessary() { +	if (_hasPalette) { +		uint32 adjustData = 0; +		for (int percent = 0; percent < 100; percent += 5) { +			if (_globals->_sceneManager._FadeMode == FADEMODE_IMMEDIATE) +				percent = 100; + +			_globals->_scenePalette.fade((const byte *)&adjustData, false, percent); +			g_system->delayMillis(10); +		} + +		_globals->_scenePalette.refresh(); +		_hasPalette = false; +	} +} + +void SceneManager::changeScene(int newSceneNumber) { +	// Fade out the scene +	ScenePalette scenePalette; +	uint32 adjustData = 0; +	scenePalette.clearListeners(); +	scenePalette.getPalette(); + +	for (int percent = 100; percent >= 0; percent -= 5) { +		scenePalette.fade((byte *)&adjustData, false, percent); +		g_system->delayMillis(10); +	} + +	// Stop any objects that were animating +	List<SceneObject *>::iterator i; +	for (i = _globals->_sceneObjects->begin(); i != _globals->_sceneObjects->end(); ++i) { +		SceneObject *sceneObj = *i; +		Common::Point pt(0, 0); +		sceneObj->addMover(NULL, &pt); +		sceneObj->setObjectWrapper(NULL); +		sceneObj->animate(ANIM_MODE_NONE, 0); + +		sceneObj->_flags &= !OBJFLAG_PANES; +	} + +	// Blank out the screen +	_globals->_screenSurface.fillRect(_globals->_screenSurface.getBounds(), 0); + +	// Set the new scene to be loaded +	setNewScene(newSceneNumber); +} + +void SceneManager::setup() { +	_saver->addLoadNotifier(SceneManager::loadNotifier); +	setBackSurface(); +} + +void SceneManager::setBackSurface() { +	int size = _globals->_sceneManager._scene->_backgroundBounds.width() * +		_globals->_sceneManager._scene->_backgroundBounds.height(); + +	if (size > 96000) { +		if (_globals->_sceneManager._scene->_backgroundBounds.width() <= SCREEN_WIDTH) { +			// Standard size creation +			_globals->_sceneManager._scene->_backSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT); +			_globals->_sceneManager._scrollerRect = Rect(0, 30, SCREEN_WIDTH, SCREEN_HEIGHT - 30); +		} else { +			// Double-size size creation +			_globals->_sceneManager._scene->_backSurface.create(SCREEN_WIDTH * 2, SCREEN_HEIGHT); +			_globals->_sceneManager._scrollerRect = Rect(80, 0, SCREEN_WIDTH - 80, SCREEN_HEIGHT); +		} +	} else { +		_globals->_sceneManager._scene->_backSurface.create( +			_globals->_sceneManager._scene->_backgroundBounds.width(), +			_globals->_sceneManager._scene->_backgroundBounds.height() +		); +		_globals->_sceneManager._scrollerRect = Rect(80, 20, SCREEN_WIDTH - 80, SCREEN_HEIGHT - 20); +	} +} + +void SceneManager::saveListener(int saveMode) { +	warning("TODO: SceneManager::saveLIstener"); +} + +void SceneManager::loadNotifier(bool postFlag) { +	if (postFlag) { +		if (_globals->_sceneManager._scene->_activeScreenNumber != -1) +			_globals->_sceneManager._scene->loadSceneData(_globals->_sceneManager._scene->_activeScreenNumber); +		_globals->_sceneManager._hasPalette = true; +	} +} + +void SceneManager::setBgOffset(const Common::Point &pt, int loadCount) { +	_sceneBgOffset = pt; +	_sceneLoadCount = loadCount; +} + +void SceneManager::listenerSynchronise(Serialiser &s) { +	s.validate("SceneManager"); +	_sceneChangeListeners.synchronise(s); +	 +	s.syncAsSint32LE(_sceneNumber); +	if (s.isLoading()) { +		changeScene(_sceneNumber); +		checkScene(); +	} + +	s.syncAsUint16LE(_globals->_sceneManager._scene->_activeScreenNumber); +	_globals->_sceneManager._scrollerRect.synchronise(s); +	SYNC_POINTER(_globals->_scrollFollower); +} + +/*--------------------------------------------------------------------------*/ + +Scene::Scene(): _sceneBounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),  +			_backgroundBounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) { +	_sceneMode = 0; +	_oldSceneBounds = Rect(4000, 4000, 4100, 4100); +} + +Scene::~Scene() { +	// TODO: Delete the obj11C object +} + +void Scene::synchronise(Serialiser &s) { +	s.syncAsSint32LE(_field12); +	s.syncAsSint32LE(_sceneNumber); +	s.syncAsSint32LE(_activeScreenNumber); +	s.syncAsSint32LE(_sceneMode); +	_backgroundBounds.synchronise(s); +	_sceneBounds.synchronise(s); +	_oldSceneBounds.synchronise(s); + +	for (int i = 0; i < 256; ++i) +		s.syncAsSint16LE(_enabledSections[i]); +	for (int i = 0; i < 256; ++i) +		s.syncAsSint16LE(_zoomPercents[i]); +} + +void Scene::postInit(SceneObjectList *OwnerList) { +	_action = NULL; +	_field12 = 0; +	_sceneMode = 0; +} + +void Scene::process(Event &event) { +	if (_action) +		_action->process(event); +} + +void Scene::dispatch() { +	if (_action) +		_action->dispatch(); +} + +void Scene::loadScene(int sceneNum) { +	_sceneNumber = sceneNum; +	if (_globals->_scenePalette.loadPalette(sceneNum)) +		_globals->_sceneManager._hasPalette = true; +	 +	loadSceneData(sceneNum); +} + +void Scene::loadSceneData(int sceneNum) { +	_globals->_sceneManager._scene->_activeScreenNumber = sceneNum; + +	// Get the basic scene size +	byte *data = _vm->_dataManager->getResource(RES_BITMAP, sceneNum, 9999); +	_backgroundBounds = Rect(0, 0, READ_LE_UINT16(data), READ_LE_UINT16(data + 2)); +	_globals->_sceneManager._scene->_sceneBounds.contain(_backgroundBounds); +	DEALLOCATE(data); + +	// Set up a surface for storing the scene background +	SceneManager::setBackSurface(); + +	// Load the data lists for the scene +	_globals->_walkRegions.load(sceneNum); + +	// Load the item regions of the scene +	_globals->_sceneRegions.load(sceneNum); + +	// Load the priority regions +	_priorities.load(sceneNum); + +	// Initialise the section enabled list +	Common::set_to(&_enabledSections[0], &_enabledSections[16 * 16], 0xffff); + +	_globals->_sceneOffset.x = (_sceneBounds.left / 160) * 160; +	_globals->_sceneOffset.y = (_sceneBounds.top / 100) * 100; +	_globals->_paneRefreshFlag[0] = 1; +	_globals->_paneRefreshFlag[1] = 1; +	_sceneMode = 1; +	_globals->_sceneManager._sceneLoadCount = 0; +	_globals->_sceneManager._sceneBgOffset = Common::Point(0, 0); + +	// Load the background for the scene +	loadBackground(0, 0); +} + +void Scene::loadBackground(int xAmount, int yAmount) { +	// Adjust the scene bounds by the passed scroll amounts +	_sceneBounds.translate(xAmount, yAmount); +	_sceneBounds.contain(_backgroundBounds); +	_sceneBounds.left &= ~3; +	_sceneBounds.right &= ~3; +	_globals->_sceneOffset.x &= ~3; + +	if ((_sceneBounds.top != _oldSceneBounds.top) || (_sceneBounds.left != _oldSceneBounds.left)) { +		if (_sceneMode == 0) { +			_globals->_paneRefreshFlag[0] = 2; +			_globals->_paneRefreshFlag[1] = 2; +			_sceneMode = 2; +		} +		_oldSceneBounds = _sceneBounds; +	} + +	_globals->_sceneOffset.x = (_sceneBounds.left / 160) * 160; +	_globals->_sceneOffset.y = (_sceneBounds.top / 100) * 100; + +	if ((_backgroundBounds.width() / 160) == 3) +		_globals->_sceneOffset.x = 0; +	if ((_backgroundBounds.height() / 100) == 3) +		_globals->_sceneOffset.y = 0; + +	if ((_globals->_sceneOffset.x != _globals->_stru_4642E.y) ||  +		(_globals->_sceneOffset.y != _globals->_stru_4642E.y)) { +		// Change has happend, so refresh background +		_globals->_stru_4642E = _globals->_sceneOffset; +		refreshBackground(xAmount, yAmount); +	} +} + +void Scene::refreshBackground(int xAmount, int yAmount) { +	if (_globals->_sceneManager._scene->_activeScreenNumber == -1) +		return; + +	// Set the quadrant ranges +	int xHalfCount = MIN(_backSurface.getBounds().width() / 160, _backgroundBounds.width() / 160); +	int yHalfCount = MIN(_backSurface.getBounds().height() / 100, _backgroundBounds.height() / 100); +	int xHalfOffset = (_backgroundBounds.width() / 160) == 3 ? 0 : _sceneBounds.left / 160; +	int yHalfOffset = (_backgroundBounds.height() / 100) == 3 ? 0 : _sceneBounds.top / 100; + +	// Set the limits and increment amounts +	int yInc = (xAmount < 0) ? -1 : 1; +	int xSection = (xAmount < 0) ? 15 : 0; +	int xSectionEnd = (xAmount < 0) ? -1 : 16; +	int xInc = (yAmount < 0) ? -1 : 1; +	int ySection = (yAmount < 0) ? 15 : 0; +	int ySectionEnd = (yAmount < 0) ? -1 : 16; +	bool changedFlag = false; + +	for (int yp = ySection; yp < ySectionEnd; yp += yInc) { +		for (int xp = xSection; xp < xSectionEnd; xp += xInc) { +			if ((yp < yHalfOffset) || (yp >= (yHalfOffset + yHalfCount)) || +				(xp < xHalfOffset) || (xp >= (xHalfOffset + xHalfCount))) { +				// Flag section as enabled +				_enabledSections[xp * 16 + yp] = 0xffff; +			} else { +				// Check if the section is enabled +				if (_enabledSections[xp * 16 + yp] || ((xAmount == 0) && (yAmount == 0))) { +					Graphics::Surface s = _backSurface.lockSurface(); +					GfxSurface::loadScreenSection(s, xp - xHalfOffset, yp - yHalfOffset, xp, yp); +					_backSurface.unlockSurface(); +					changedFlag = true; +				} else { +					int yv = _enabledSections[xp * 16 + yp] == ((xp - xHalfOffset) << 4) ? 0 : 1; +					if (yv != (yp - yHalfOffset)) { +						int xSectionTemp = _enabledSections[xp * 16 + yp] >> 4; +						int ySectionTemp = _enabledSections[xp * 16 + yp] & 0xffff; + +						reuseSection(xp - xHalfOffset, yp - yHalfOffset, xSectionTemp, ySectionTemp); +					} +				} + +				_enabledSections[xp * 16 + yp] =  +					((xp - xHalfOffset) << 4) && (yp - yHalfOffset); +			} +		} +	} + +	if (changedFlag) { +		signalListeners(); +	} +} + +void Scene::reuseSection(int xHalf, int yHalf, int xSection, int ySection) { +//	Rect rect1, rect2, rect3; + +	// TODO: Figure out purpose +} + +void Scene::signalListeners() { +	// TODO: Figure out method +} + +void Scene::setZoomPercents(int yStart, int minPercent, int yEnd, int maxPercent) { +	int var_6 = 0; +	int v = 0; +	while (v < yStart) +		_zoomPercents[v] = minPercent; + +	int diff1 = ABS(maxPercent - minPercent); +	int diff2 = ABS(yEnd - yStart); +	int var_8 = MAX(diff1, diff2); + +	while (var_8-- != 0) { +        _zoomPercents[v] = minPercent; +        if (diff2 <= diff1) { +                ++minPercent; +                var_6 += diff2; +                if (var_6 >= diff1) { +                        var_6 -= diff1; +                        ++v; +                } +        } else { +                ++v; +                var_6 += diff1; +                if (var_6 >= diff2) { +                        var_6 -= diff2; +                        ++minPercent; +                } +        } +	}        + +	while (yEnd < 256) +		_zoomPercents[yEnd++] = minPercent; +} + +} // End of namespace tSage diff --git a/engines/tsage/scenes.h b/engines/tsage/scenes.h new file mode 100644 index 0000000000..88caee398d --- /dev/null +++ b/engines/tsage/scenes.h @@ -0,0 +1,112 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/scenes.h $ + * $Id: scenes.h 216 2011-02-08 08:10:46Z dreammaster $ + * + */ + +#ifndef TSAGE_SCENES_H +#define TSAGE_SCENES_H + +#include "common/scummsys.h" +#include "tsage/converse.h" +#include "tsage/events.h" +#include "tsage/core.h" +#include "tsage/saveload.h" + +namespace tSage { + +class Scene: public StripCallback { +private: +	void reuseSection(int xHalf, int yHalf, int xSection, int ySection); +	void signalListeners(); +public: +	int _field12; +	int _sceneNumber; +	int _activeScreenNumber; +	int _sceneMode; +	StripManager _stripManager; + +	Rect _backgroundBounds; +	GfxSurface _backSurface; +	Rect _sceneBounds; +	Rect _oldSceneBounds; +	int _enabledSections[256]; +	int _zoomPercents[256]; +	ScenePriorities _priorities; +public: +	Scene(); +	virtual ~Scene(); + +	virtual Common::String getClassName() { return "Scene"; } +	virtual void synchronise(Serialiser &s); +	virtual void stripCallback(int v) {} +	virtual void postInit(SceneObjectList *OwnerList = NULL); +	virtual void process(Event &event); +	virtual void dispatch(); +	virtual void loadScene(int sceneNum); + +	void setZoomPercents(int yStart, int minPercent, int yEnd, int maxPercent); +	void loadBackground(int xAmount, int yAmount); +	void refreshBackground(int xAmount, int yAmount); +	void loadSceneData(int sceneNum); +}; + +class SceneManager: public GameHandler, public SaveListener { +private: +	void disposeRegions() { warning("TODO"); } +	Scene *getNewScene(); +public: +	Scene *_scene; +	bool _hasPalette; +	int _sceneNumber; +	int _previousScene; +	int _nextSceneNumber; +	FadeMode _FadeMode; +	Common::Point _sceneBgOffset; +	int _sceneLoadCount; +	Rect _scrollerRect; +	List<EventHandler *> _sceneChangeListeners; +public: +	SceneManager(); +	virtual ~SceneManager(); + +	virtual void listenerSynchronise(Serialiser &s); +	void setNewScene(int sceneNumber); +	void checkScene(); +	void sceneChange(); +	void fadeInIfNecessary(); +	void changeScene(int newSceneNumber); +	void setBgOffset(const Common::Point &pt, int loadCount); + +	void removeAction(Action *action) {  +		// Not currently implemented because addAction method doesn't seem to have any callers +	} + +	static void setup(); +	static void setBackSurface(); +	static void saveListener(int saveMode); +	static void loadNotifier(bool postFlag); +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp new file mode 100644 index 0000000000..e4182cdbb6 --- /dev/null +++ b/engines/tsage/sound.cpp @@ -0,0 +1,62 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/sound.cpp $ + * $Id: sound.cpp 204 2011-02-05 12:23:20Z dreammaster $ + * + */ + +#include "common/config-manager.h" +#include "common/endian.h" +#include "tsage/core.h" +#include "tsage/globals.h" +#include "tsage/debugger.h" +#include "tsage/graphics.h" + +namespace tSage { + +void SoundManager::postInit() { +	_saver->addSaveNotifier(&SoundManager::saveNotifier); +	_saver->addLoadNotifier(&SoundManager::loadNotifier); +	_saver->addListener(this); +} + +void SoundManager::saveNotifier(bool postFlag) { +	_globals->_soundManager.saveNotifierProc(postFlag); +} + +void SoundManager::saveNotifierProc(bool postFlag) { +	warning("TODO: SoundManager::saveNotifierProc"); +} + +void SoundManager::loadNotifier(bool postFlag) { +	_globals->_soundManager.loadNotifierProc(postFlag); +} + +void SoundManager::loadNotifierProc(bool postFlag) { +	warning("TODO: SoundManager::loadNotifierProc"); +} + +void SoundManager::listenerSynchronise(Serialiser &s) { +	s.validate("SoundManager"); +	warning("TODO: SoundManager listenerSynchronise"); +} + +} // End of namespace tSage diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h new file mode 100644 index 0000000000..c25cebd1c2 --- /dev/null +++ b/engines/tsage/sound.h @@ -0,0 +1,49 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/sound.h $ + * $Id: sound.h 184 2011-02-03 11:31:38Z dreammaster $ + * + */ + +#ifndef TSAGE_SOUND_H +#define TSAGE_SOUND_H + +#include "common/scummsys.h" +#include "tsage/saveload.h" + +namespace tSage { + +class SoundManager: public SaveListener { +public: +	void dispatch() {} +	virtual void listenerSynchronise(Serialiser &s); +	virtual void postInit(); + +	void proc2() {} +	static void saveNotifier(bool postFlag); +	void saveNotifierProc(bool postFlag); +	static void loadNotifier(bool postFlag); +	void loadNotifierProc(bool postFlag); +}; + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/staticres.cpp b/engines/tsage/staticres.cpp new file mode 100644 index 0000000000..44de35e2e1 --- /dev/null +++ b/engines/tsage/staticres.cpp @@ -0,0 +1,103 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/staticres.cpp $ + * $Id: staticres.cpp 219 2011-02-08 12:05:46Z dreammaster $ + * + */ + +#include "tsage/staticres.h" + +namespace tSage { + +const byte CURSOR_ARROW_DATA[] = { +	15, 0, 15, 0, 0, 0, 0, 0, 9, 0,  +	0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,  +	0x00, 0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,  +	0x00, 0xFF,	0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,  +	0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,  +	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,	0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, +	0x00, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0xFF, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,	0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,	0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09 +}; + +const byte CURSOR_WALK_DATA[] = { +	15, 0, 15, 0, 7, 0, 7, 0, 9, 0, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09,	0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x09,	0x09, 0x09, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, +	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0x09, 0x09, 0x09, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x09,	0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09,	0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09,	0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x00, 0xFF, 0x00, 0x09, 0x09, 0x09, 0x09, 0x09,	0x09, +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 +}; + +const char *LOOK_SCENE_HOTSPOT = "You see nothing special."; +const char *USE_SCENE_HOTSPOT = "That accomplishes nothing."; +const char *TALK_SCENE_HOTSPOT = "Yak, yak."; +const char *SPECIAL_SCENE_HOTSPOT = "That is a unique use for that."; +const char *DEFAULT_SCENE_HOTSPOT = "That accomplishes nothing."; +const char *SAVE_ERROR_MSG = "Error occurred saving game. Please do not try to restore this game!"; +const char *SAVING_NOT_ALLOWED_MSG = "Saving is not allowed at this time."; +const char *RESTORING_NOT_ALLOWED_MSG = "Restoring is not allowed at this time."; +const char *RESTART_CONFIRM_MSG = "Do you want to restart your game?"; +const char *WATCH_INTRO_MSG = "Do you wish to watch the introduction?"; +const char *INV_EMPTY_MSG = "You have nothing in your possesion."; + +const char *HELP_MSG = "Ringworld\rRevenge of the Patriarch\x14\rScummVM Version\r\r\ +\x01 Keyboard shortcuts...\rF2 - Sound options\rF3 - Quit\r\ +F4 - Restart\rF5 - Save game\rF7 - Restore Game\rF10 - Pause game"; +const char *QUIT_CONFIRM_MSG = "Do you want to quit playing this game?"; +const char *RESTART_MSG = "Do you want to restart this game?"; +const char *GAME_PAUSED_MSG = "Game is paused."; +const char *OPTIONS_MSG = "\x01Options..."; +const char *OK_BTN_STRING = " Ok "; +const char *CANCEL_BTN_STRING = "Cancel"; +const char *QUIT_BTN_STRING = " Quit "; +const char *RESTART_BTN_STRING = "Restart"; +const char *SAVE_BTN_STRING = "Save"; +const char *RESTORE_BTN_STRING = "Restore"; +const char *SOUND_BTN_STRING = "Sound"; +const char *RESUME_BTN_STRING = " Resume \rplay"; +const char *LOOK_BTN_STRING = "Look"; +const char *PICK_BTN_STRING = "Pick"; +const char *START_PLAY_BTN_STRING = " Start Play "; +const char *INTRODUCTION_BTN_STRING = "Introduction"; + + +} // End of namespace tSage diff --git a/engines/tsage/staticres.h b/engines/tsage/staticres.h new file mode 100644 index 0000000000..c0b219958b --- /dev/null +++ b/engines/tsage/staticres.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/staticres.h $ + * $Id: staticres.h 213 2011-02-07 10:02:58Z dreammaster $ + * + */ + +#ifndef TSAGE_STATICRES_H +#define TSAGE_STATICRES_H + +#include "common/scummsys.h" + +namespace tSage { + +extern const byte CURSOR_ARROW_DATA[]; + +extern const byte CURSOR_WALK_DATA[]; + +extern const char *LOOK_SCENE_HOTSPOT; +extern const char *USE_SCENE_HOTSPOT; +extern const char *TALK_SCENE_HOTSPOT; +extern const char *SPECIAL_SCENE_HOTSPOT; +extern const char *DEFAULT_SCENE_HOTSPOT; +extern const char *SAVE_ERROR_MSG; +extern const char *SAVING_NOT_ALLOWED_MSG; +extern const char *RESTORING_NOT_ALLOWED_MSG; +extern const char *RESTART_CONFIRM_MSG; +extern const char *WATCH_INTRO_MSG; + +// Dialogs +extern const char *HELP_MSG; +extern const char *QUIT_CONFIRM_MSG; +extern const char *RESTART_MSG; +extern const char *GAME_PAUSED_MSG; +extern const char *OPTIONS_MSG; +extern const char *OK_BTN_STRING; +extern const char *CANCEL_BTN_STRING; +extern const char *QUIT_BTN_STRING; +extern const char *RESTART_BTN_STRING; +extern const char *SAVE_BTN_STRING; +extern const char *RESTORE_BTN_STRING; +extern const char *SOUND_BTN_STRING; +extern const char *RESUME_BTN_STRING; +extern const char *LOOK_BTN_STRING; +extern const char *PICK_BTN_STRING; +extern const char *INV_EMPTY_MSG; +extern const char *START_PLAY_BTN_STRING; +extern const char *INTRODUCTION_BTN_STRING; + + +} // End of namespace tSage + +#endif diff --git a/engines/tsage/tsage.cpp b/engines/tsage/tsage.cpp new file mode 100644 index 0000000000..c45f0e6260 --- /dev/null +++ b/engines/tsage/tsage.cpp @@ -0,0 +1,133 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/tsage.cpp $ + * $Id: tsage.cpp 211 2011-02-06 06:59:31Z dreammaster $ + * + */ + +#include "common/config-manager.h" +#include "common/debug.h" +#include "common/debug-channels.h" +#include "common/system.h" +#include "common/savefile.h" +#include "engines/util.h" + +#include "tsage/tsage.h" +#include "tsage/core.h" +#include "tsage/dialogs.h" +#include "tsage/events.h" +#include "tsage/resources.h" +#include "tsage/globals.h" + +namespace tSage { + +TSageEngine *_vm = NULL; + +TSageEngine::TSageEngine(OSystem *system, const ADGameDescription *gameDesc): Engine(system), +		_gameDescription(gameDesc) { +	_vm = this; +	DebugMan.addDebugChannel(kRingDebugScripts, "scripts", "Scripts debugging"); +	_debugger = new Debugger(); +	_dataManager = NULL; +} + +Common::Error TSageEngine::init() { +	initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false); + +	return Common::kNoError; +} + +TSageEngine::~TSageEngine() { +	// Remove all of our debug levels here +	DebugMan.clearAllDebugChannels(); +	delete _debugger; +	delete _dataManager; +	delete _tSageManager; +} + +bool TSageEngine::hasFeature(EngineFeature f) const { +	return +		(f == kSupportsRTL) || +		(f == kSupportsLoadingDuringRuntime) || +		(f == kSupportsSavingDuringRuntime); +} + +void TSageEngine::initialise() { +	_tSageManager = new RlbManager(_memoryManager, "tsage.rlb"); +	_dataManager = new RlbManager(_memoryManager, "ring.rlb"); +} + +Common::Error TSageEngine::run() { +	// Basic initialisation +	initialise(); +	_saver = new Saver(); +	_globals = new Globals(); +	_globals->gfxManager().setDefaults(); + +	initialise(); + +	_globals->_events.showCursor(); + +	_globals->_sceneHandler.registerHandler(); +	_globals->_game.execute(); + +	delete _globals; +	delete _saver; +	return Common::kNoError; +} + +/** + * Returns true if it is currently okay to restore a game + */ +bool TSageEngine::canLoadGameStateCurrently() { +	return _globals->getFlag(50) == 0; +} + +/** + * Returns true if it is currently okay to save the game + */ +bool TSageEngine::canSaveGameStateCurrently() { +	return _globals->getFlag(50) == 0; +} + +/** + * Load the savegame at the specified slot index + */ +Common::Error TSageEngine::loadGameState(int slot) { +	return _saver->restore(slot); +} + +/** + * Save the game to the given slot index, and with the given name + */ +Common::Error TSageEngine::saveGameState(int slot, const char *desc) { +	return _saver->save(slot, desc); +} + +/** + * Support method that generates a savegame name + * @param slot		Slot number + */ +Common::String TSageEngine::generateSaveName(int slot) { +	return String::format("%s.%03d", _targetName.c_str(), slot); +} + +} // End of namespace tSage diff --git a/engines/tsage/tsage.h b/engines/tsage/tsage.h new file mode 100644 index 0000000000..0f5b75ae8a --- /dev/null +++ b/engines/tsage/tsage.h @@ -0,0 +1,97 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL: https://scummvm-misc.svn.sourceforge.net/svnroot/scummvm-misc/trunk/engines/tsage/tsage.h $ + * $Id: tsage.h 212 2011-02-06 10:19:01Z dreammaster $ + * + */ + +#ifndef TSAGE_H +#define TSAGE_H + +#include "engines/advancedDetector.h" +#include "engines/engine.h" +#include "common/rect.h" +#include "audio/mixer.h" +#include "common/file.h" + +#include "tsage/core.h" +#include "tsage/resources.h" +#include "tsage/debugger.h" +#include "tsage/events.h" +#include "tsage/graphics.h" +#include "tsage/resources.h" + + +namespace tSage { + +enum { +	GType_Ringworld = 0 +}; + +enum { +	GF_CD		= 1 <<  0, +	GF_LNGUNK	= 1 << 15 +}; + +enum { +	kRingDebugScripts = 1 << 0 +}; + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define SCREEN_CENTRE_X 160 +#define SCREEN_CENTRE_Y 100 + +class TSageEngine : public Engine { +private: +	const ADGameDescription *_gameDescription; +public: +	TSageEngine(OSystem *system, const ADGameDescription *gameDesc); +	~TSageEngine(); +	virtual bool hasFeature(EngineFeature f) const; + +	MemoryManager _memoryManager; +	Debugger *_debugger; +	RlbManager *_tSageManager; +	RlbManager *_dataManager; + +	const char *getGameId() const; + +	virtual Common::Error init(); +	virtual Common::Error run(); +	virtual bool canLoadGameStateCurrently(); +	virtual bool canSaveGameStateCurrently(); +	virtual Common::Error loadGameState(int slot); +	virtual Common::Error saveGameState(int slot, const char *desc); +	Common::String generateSaveName(int slot); + +	void initialise(); +}; + +extern TSageEngine *_vm; + +#define ALLOCATE_HANDLE(x) _vm->_memoryManager.allocate(x) +#define ALLOCATE(x) _vm->_memoryManager.allocate2(x) +#define DEALLOCATE(x) _vm->_memoryManager.deallocate(x) + +} // End of namespace tSage + +#endif | 
