//////////////////////////////////////////////////////////////////////////
// high level scripting interface
//////////////////////////////////////////////////////////////////////////
bool BaseKeyboardState::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
	//////////////////////////////////////////////////////////////////////////
	// IsKeyDown
	//////////////////////////////////////////////////////////////////////////
	if (strcmp(name, "IsKeyDown") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();
		int vKey;

		if (val->_type == VAL_STRING && strlen(val->getString()) > 0) {
			const char *str = val->getString();
			char temp = str[0];
			if (temp >= 'A' && temp <= 'Z') {
				temp += ('a' - 'A');
			}
			vKey = (int)temp;
		} else {
			vKey = val->getInt();
		}

		bool isDown = _keyStates[vKeyToKeyCode(vKey)];

		stack->pushBool(isDown);
		return STATUS_OK;
	} else {
		return BaseScriptable::scCallMethod(script, stack, thisStack, name);
	}
}
Beispiel #2
0
SXDate::SXDate(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
	stack->correctParams(6);

	memset(&_tm, 0, sizeof(_tm));

	ScValue *valYear = stack->pop();
	_tm.tm_year = valYear->getInt() - 1900;
	_tm.tm_mon = stack->pop()->getInt() - 1;
	_tm.tm_mday = stack->pop()->getInt();
	_tm.tm_hour = stack->pop()->getInt();
	_tm.tm_min = stack->pop()->getInt();
	_tm.tm_sec = stack->pop()->getInt();

	if (valYear->isNULL()) {
		g_system->getTimeAndDate(_tm);
	}
}
SXString::SXString(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
	_string = nullptr;
	_capacity = 0;

	stack->correctParams(1);
	ScValue *val = stack->pop();

	if (val->isInt()) {
		_capacity = MAX(0, val->getInt());
		if (_capacity > 0) {
			_string = new char[_capacity];
			memset(_string, 0, _capacity);
		}
	} else {
		setStringVal(val->getString());
	}

	if (_capacity == 0) {
		setStringVal("");
	}
}
Beispiel #4
0
//////////////////////////////////////////////////////////////////////////
// high level scripting interface
//////////////////////////////////////////////////////////////////////////
bool AdActor::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
	//////////////////////////////////////////////////////////////////////////
	// GoTo / GoToAsync
	//////////////////////////////////////////////////////////////////////////
	if (strcmp(name, "GoTo") == 0 || strcmp(name, "GoToAsync") == 0) {
		stack->correctParams(2);
		int x = stack->pop()->getInt();
		int y = stack->pop()->getInt();
		goTo(x, y);
		if (strcmp(name, "GoToAsync") != 0) {
			script->waitForExclusive(this);
		}
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GoToObject / GoToObjectAsync
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GoToObject") == 0 || strcmp(name, "GoToObjectAsync") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();
		if (!val->isNative()) {
			script->runtimeError("actor.%s method accepts an entity refrence only", name);
			stack->pushNULL();
			return STATUS_OK;
		}
		AdObject *obj = (AdObject *)val->getNative();
		if (!obj || obj->getType() != OBJECT_ENTITY) {
			script->runtimeError("actor.%s method accepts an entity refrence only", name);
			stack->pushNULL();
			return STATUS_OK;
		}
		AdEntity *ent = (AdEntity *)obj;
		if (ent->getWalkToX() == 0 && ent->getWalkToY() == 0) {
			goTo(ent->_posX, ent->_posY);
		} else {
			goTo(ent->getWalkToX(), ent->getWalkToY(), ent->getWalkToDir());
		}
		if (strcmp(name, "GoToObjectAsync") != 0) {
			script->waitForExclusive(this);
		}
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// TurnTo / TurnToAsync
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "TurnTo") == 0 || strcmp(name, "TurnToAsync") == 0) {
		stack->correctParams(1);
		int dir;
		ScValue *val = stack->pop();

		// turn to object?
		if (val->isNative() && _gameRef->validObject((BaseObject *)val->getNative())) {
			BaseObject *obj = (BaseObject *)val->getNative();
			int angle = (int)(atan2((double)(obj->_posY - _posY), (double)(obj->_posX - _posX)) * (180 / 3.14));
			dir = (int)angleToDirection(angle);
		}
		// otherwise turn to direction
		else {
			dir = val->getInt();
		}

		if (dir >= 0 && dir < NUM_DIRECTIONS) {
			turnTo((TDirection)dir);
			if (strcmp(name, "TurnToAsync") != 0) {
				script->waitForExclusive(this);
			}
		}
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// IsWalking
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "IsWalking") == 0) {
		stack->correctParams(0);
		stack->pushBool(_state == STATE_FOLLOWING_PATH);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// MergeAnims
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MergeAnims") == 0) {
		stack->correctParams(1);
		stack->pushBool(DID_SUCCEED(mergeAnims(stack->pop()->getString())));
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// UnloadAnim
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "UnloadAnim") == 0) {
		stack->correctParams(1);
		const char *animName = stack->pop()->getString();

		bool found = false;
		for (uint32 i = 0; i < _anims.size(); i++) {
			if (scumm_stricmp(_anims[i]->getName(), animName) == 0) {
				// invalidate sprites in use
				if (_anims[i]->containsSprite(_tempSprite2)) {
					_tempSprite2 = nullptr;
				}
				if (_anims[i]->containsSprite(_currentSprite)) {
					_currentSprite = nullptr;
				}
				if (_anims[i]->containsSprite(_animSprite2)) {
					_animSprite2 = nullptr;
				}

				delete _anims[i];
				_anims[i] = nullptr;
				_anims.remove_at(i);
				i--;
				found = true;
			}
		}
		stack->pushBool(found);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// HasAnim
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "HasAnim") == 0) {
		stack->correctParams(1);
		const char *animName = stack->pop()->getString();
		stack->pushBool(getAnimByName(animName) != nullptr);
		return STATUS_OK;
	} else {
		return AdTalkHolder::scCallMethod(script, stack, thisStack, name);
	}
}
bool SXString::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
	//////////////////////////////////////////////////////////////////////////
	// Substring
	//////////////////////////////////////////////////////////////////////////
	if (strcmp(name, "Substring") == 0) {
		stack->correctParams(2);
		int start = stack->pop()->getInt();
		int end   = stack->pop()->getInt();

		if (end < start) {
			BaseUtils::swap(&start, &end);
		}

		//try {
		WideString str;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			str = StringUtil::utf8ToWide(_string);
		} else {
			str = StringUtil::ansiToWide(_string);
		}

		//WideString subStr = str.substr(start, end - start + 1);
		WideString subStr(str.c_str() + start, end - start + 1);

		if (_gameRef->_textEncoding == TEXT_UTF8) {
			stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
		} else {
			stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
		}
		//  } catch (std::exception &) {
		//      stack->pushNULL();
		//  }

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Substr
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Substr") == 0) {
		stack->correctParams(2);
		int start = stack->pop()->getInt();

		ScValue *val = stack->pop();
		int len = val->getInt();

		if (!val->isNULL() && len <= 0) {
			stack->pushString("");
			return STATUS_OK;
		}

		if (val->isNULL()) {
			len = strlen(_string) - start;
		}

//		try {
		WideString str;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			str = StringUtil::utf8ToWide(_string);
		} else {
			str = StringUtil::ansiToWide(_string);
		}

//			WideString subStr = str.substr(start, len);
		WideString subStr(str.c_str() + start, len);

		if (_gameRef->_textEncoding == TEXT_UTF8) {
			stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
		} else {
			stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
		}
//		} catch (std::exception &) {
//			stack->pushNULL();
//		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ToUpperCase
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ToUpperCase") == 0) {
		stack->correctParams(0);

		WideString str;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			str = StringUtil::utf8ToWide(_string);
		} else {
			str = StringUtil::ansiToWide(_string);
		}

		str.toUppercase();

		if (_gameRef->_textEncoding == TEXT_UTF8) {
			stack->pushString(StringUtil::wideToUtf8(str).c_str());
		} else {
			stack->pushString(StringUtil::wideToAnsi(str).c_str());
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ToLowerCase
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ToLowerCase") == 0) {
		stack->correctParams(0);

		WideString str;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			str = StringUtil::utf8ToWide(_string);
		} else {
			str = StringUtil::ansiToWide(_string);
		}

		str.toLowercase();

		if (_gameRef->_textEncoding == TEXT_UTF8) {
			stack->pushString(StringUtil::wideToUtf8(str).c_str());
		} else {
			stack->pushString(StringUtil::wideToAnsi(str).c_str());
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// IndexOf
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "IndexOf") == 0) {
		stack->correctParams(2);

		const char *strToFind = stack->pop()->getString();
		int index = stack->pop()->getInt();

		WideString str;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			str = StringUtil::utf8ToWide(_string);
		} else {
			str = StringUtil::ansiToWide(_string);
		}

		WideString toFind;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			toFind = StringUtil::utf8ToWide(strToFind);
		} else {
			toFind = StringUtil::ansiToWide(strToFind);
		}

		int indexOf = StringUtil::indexOf(str, toFind, index);
		stack->pushInt(indexOf);

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Split
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Split") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();
		char separators[MAX_PATH_LENGTH] = ",";
		if (!val->isNULL()) {
			strcpy(separators, val->getString());
		}

		SXArray *array = new SXArray(_gameRef);
		if (!array) {
			stack->pushNULL();
			return STATUS_OK;
		}


		WideString str;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			str = StringUtil::utf8ToWide(_string);
		} else {
			str = StringUtil::ansiToWide(_string);
		}

		WideString delims;
		if (_gameRef->_textEncoding == TEXT_UTF8) {
			delims = StringUtil::utf8ToWide(separators);
		} else {
			delims = StringUtil::ansiToWide(separators);
		}

		Common::Array<WideString> parts;

		uint32 start = 0;
		for(uint32 i = 0; i < str.size() + 1; i++) {
			char ch = str.c_str()[i];
			if(ch=='\0' || delims.contains(ch))
			{
				char *part = new char[i - start + 1];
				if(i != start) {
					Common::strlcpy(part, str.c_str() + start, i - start + 1);
					part[i - start] = '\0';
				} else {
					part[0] = '\0';
				}
				val = new ScValue(_gameRef, part);
				array->push(val);
				delete[] part;
				delete val;
				val = nullptr;
				start = i + 1;
			}
		}

		for (Common::Array<WideString>::iterator it = parts.begin(); it != parts.end(); ++it) {
			WideString &part = (*it);

			if (_gameRef->_textEncoding == TEXT_UTF8) {
				val = new ScValue(_gameRef,  StringUtil::wideToUtf8(part).c_str());
			} else {
				val = new ScValue(_gameRef,  StringUtil::wideToAnsi(part).c_str());
			}

			array->push(val);
			delete val;
			val = nullptr;
		}

		stack->pushNative(array, false);
		return STATUS_OK;
	} else {
		return STATUS_FAILED;
	}
}
Beispiel #6
0
//////////////////////////////////////////////////////////////////////////
// high level scripting interface
//////////////////////////////////////////////////////////////////////////
bool AdObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {

	//////////////////////////////////////////////////////////////////////////
	// PlayAnim / PlayAnimAsync
	//////////////////////////////////////////////////////////////////////////
	if (strcmp(name, "PlayAnim") == 0 || strcmp(name, "PlayAnimAsync") == 0) {
		stack->correctParams(1);
		if (DID_FAIL(playAnim(stack->pop()->getString()))) {
			stack->pushBool(false);
		} else {
			if (strcmp(name, "PlayAnimAsync") != 0) {
				script->waitFor(this);
			}
			stack->pushBool(true);
		}
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Reset
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Reset") == 0) {
		stack->correctParams(0);
		reset();
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// IsTalking
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "IsTalking") == 0) {
		stack->correctParams(0);
		stack->pushBool(_state == STATE_TALKING);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// StopTalk / StopTalking
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "StopTalk") == 0 || strcmp(name, "StopTalking") == 0) {
		stack->correctParams(0);
		if (_sentence) {
			_sentence->finish();
		}
		if (_state == STATE_TALKING) {
			_state = _nextState;
			_nextState = STATE_READY;
			stack->pushBool(true);
		} else {
			stack->pushBool(false);
		}
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ForceTalkAnim
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ForceTalkAnim") == 0) {
		stack->correctParams(1);
		const char *animName = stack->pop()->getString();
		delete[] _forcedTalkAnimName;
		_forcedTalkAnimName = new char[strlen(animName) + 1];
		strcpy(_forcedTalkAnimName, animName);
		_forcedTalkAnimUsed = false;
		stack->pushBool(true);
		return STATUS_OK;
	}


	//////////////////////////////////////////////////////////////////////////
	// Talk / TalkAsync
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Talk") == 0 || strcmp(name, "TalkAsync") == 0) {
		stack->correctParams(5);

		const char *text    = stack->pop()->getString();
		ScValue *soundVal = stack->pop();
		int duration  = stack->pop()->getInt();
		ScValue *valStances = stack->pop();

		const char *stances = valStances->isNULL() ? nullptr : valStances->getString();

		int align = 0;
		ScValue *val = stack->pop();
		if (val->isNULL()) {
			align = TAL_CENTER;
		} else {
			align = val->getInt();
		}

		align = MIN(MAX(0, align), NUM_TEXT_ALIGN - 1);

		const char *sound = soundVal->isNULL() ? nullptr : soundVal->getString();

		talk(text, sound, duration, stances, (TTextAlign)align);
		if (strcmp(name, "TalkAsync") != 0) {
			script->waitForExclusive(this);
		}

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// StickToRegion
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "StickToRegion") == 0) {
		stack->correctParams(1);

		AdLayer *main = ((AdGame *)_gameRef)->_scene->_mainLayer;
		bool regFound = false;

		uint32 i;
		ScValue *val = stack->pop();
		if (val->isNULL() || !main) {
			_stickRegion = nullptr;
			regFound = true;
		} else if (val->isString()) {
			const char *regionName = val->getString();
			for (i = 0; i < main->_nodes.size(); i++) {
				if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region->getName() && scumm_stricmp(main->_nodes[i]->_region->getName(), regionName) == 0) {
					_stickRegion = main->_nodes[i]->_region;
					regFound = true;
					break;
				}
			}
		} else if (val->isNative()) {
			BaseScriptable *obj = val->getNative();

			for (i = 0; i < main->_nodes.size(); i++) {
				if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region == obj) {
					_stickRegion = main->_nodes[i]->_region;
					regFound = true;
					break;
				}
			}

		}

		if (!regFound) {
			_stickRegion = nullptr;
		}
		stack->pushBool(regFound);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetFont
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetFont") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();

		if (val->isNULL()) {
			setFont(nullptr);
		} else {
			setFont(val->getString());
		}

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetFont
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetFont") == 0) {
		stack->correctParams(0);
		if (_font && _font->getFilename()) {
			stack->pushString(_font->getFilename());
		} else {
			stack->pushNULL();
		}
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// TakeItem
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "TakeItem") == 0) {
		stack->correctParams(2);

		if (!_inventory) {
			_inventory = new AdInventory(_gameRef);
			((AdGame *)_gameRef)->registerInventory(_inventory);
		}

		ScValue *val = stack->pop();
		if (!val->isNULL()) {
			const char *itemName = val->getString();
			val = stack->pop();
			const char *insertAfter = val->isNULL() ? nullptr : val->getString();
			if (DID_FAIL(_inventory->insertItem(itemName, insertAfter))) {
				script->runtimeError("Cannot add item '%s' to inventory", itemName);
			} else {
				// hide associated entities
				((AdGame *)_gameRef)->_scene->handleItemAssociations(itemName, false);
			}

		} else {
			script->runtimeError("TakeItem: item name expected");
		}

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DropItem
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DropItem") == 0) {
		stack->correctParams(1);

		if (!_inventory) {
			_inventory = new AdInventory(_gameRef);
			((AdGame *)_gameRef)->registerInventory(_inventory);
		}

		ScValue *val = stack->pop();
		if (!val->isNULL()) {
			if (DID_FAIL(_inventory->removeItem(val->getString()))) {
				script->runtimeError("Cannot remove item '%s' from inventory", val->getString());
			} else {
				// show associated entities
				((AdGame *)_gameRef)->_scene->handleItemAssociations(val->getString(), true);
			}
		} else {
			script->runtimeError("DropItem: item name expected");
		}

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetItem
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetItem") == 0) {
		stack->correctParams(1);

		if (!_inventory) {
			_inventory = new AdInventory(_gameRef);
			((AdGame *)_gameRef)->registerInventory(_inventory);
		}

		ScValue *val = stack->pop();
		if (val->_type == VAL_STRING) {
			AdItem *item = ((AdGame *)_gameRef)->getItemByName(val->getString());
			if (item) {
				stack->pushNative(item, true);
			} else {
				stack->pushNULL();
			}
		} else if (val->isNULL() || val->getInt() < 0 || val->getInt() >= (int32)_inventory->_takenItems.size()) {
			stack->pushNULL();
		} else {
			stack->pushNative(_inventory->_takenItems[val->getInt()], true);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// HasItem
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "HasItem") == 0) {
		stack->correctParams(1);

		if (!_inventory) {
			_inventory = new AdInventory(_gameRef);
			((AdGame *)_gameRef)->registerInventory(_inventory);
		}

		ScValue *val = stack->pop();
		if (!val->isNULL()) {
			for (uint32 i = 0; i < _inventory->_takenItems.size(); i++) {
				if (val->getNative() == _inventory->_takenItems[i]) {
					stack->pushBool(true);
					return STATUS_OK;
				} else if (scumm_stricmp(val->getString(), _inventory->_takenItems[i]->getName()) == 0) {
					stack->pushBool(true);
					return STATUS_OK;
				}
			}
		} else {
			script->runtimeError("HasItem: item name expected");
		}

		stack->pushBool(false);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// CreateParticleEmitter
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "CreateParticleEmitter") == 0) {
		stack->correctParams(3);
		bool followParent = stack->pop()->getBool();
		int offsetX = stack->pop()->getInt();
		int offsetY = stack->pop()->getInt();

		PartEmitter *emitter = createParticleEmitter(followParent, offsetX, offsetY);
		if (emitter) {
			stack->pushNative(_partEmitter, true);
		} else {
			stack->pushNULL();
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DeleteParticleEmitter
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DeleteParticleEmitter") == 0) {
		stack->correctParams(0);
		if (_partEmitter) {
			_gameRef->unregisterObject(_partEmitter);
			_partEmitter = nullptr;
		}
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// AddAttachment
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "AddAttachment") == 0) {
		stack->correctParams(4);
		const char *filename = stack->pop()->getString();
		bool preDisplay = stack->pop()->getBool(true);
		int offsetX = stack->pop()->getInt();
		int offsetY = stack->pop()->getInt();

		bool res;
		AdEntity *ent = new AdEntity(_gameRef);
		if (DID_FAIL(res = ent->loadFile(filename))) {
			delete ent;
			ent = nullptr;
			script->runtimeError("AddAttachment() failed loading entity '%s'", filename);
			stack->pushBool(false);
		} else {
			_gameRef->registerObject(ent);

			ent->_posX = offsetX;
			ent->_posY = offsetY;
			ent->_active = true;

			if (preDisplay) {
				_attachmentsPre.add(ent);
			} else {
				_attachmentsPost.add(ent);
			}

			stack->pushBool(true);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RemoveAttachment
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RemoveAttachment") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();
		bool found = false;
		if (val->isNative()) {
			BaseScriptable *obj = val->getNative();
			for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
				if (_attachmentsPre[i] == obj) {
					found = true;
					_gameRef->unregisterObject(_attachmentsPre[i]);
					_attachmentsPre.remove_at(i);
					i--;
				}
			}
			for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
				if (_attachmentsPost[i] == obj) {
					found = true;
					_gameRef->unregisterObject(_attachmentsPost[i]);
					_attachmentsPost.remove_at(i);
					i--;
				}
			}
		} else {
			const char *attachmentName = val->getString();
			for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
				if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) {
					found = true;
					_gameRef->unregisterObject(_attachmentsPre[i]);
					_attachmentsPre.remove_at(i);
					i--;
				}
			}
			for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
				if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) {
					found = true;
					_gameRef->unregisterObject(_attachmentsPost[i]);
					_attachmentsPost.remove_at(i);
					i--;
				}
			}
		}
		stack->pushBool(found);

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetAttachment
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetAttachment") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();

		AdObject *ret = nullptr;
		if (val->isInt()) {
			int index = val->getInt();
			int currIndex = 0;
			for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
				if (currIndex == index) {
					ret = _attachmentsPre[i];
				}
				currIndex++;
			}
			for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
				if (currIndex == index) {
					ret = _attachmentsPost[i];
				}
				currIndex++;
			}
		} else {
			const char *attachmentName = val->getString();
			for (uint32 i = 0; i < _attachmentsPre.size(); i++) {
				if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) {
					ret = _attachmentsPre[i];
					break;
				}
			}
			if (!ret) {
				for (uint32 i = 0; i < _attachmentsPost.size(); i++) {
					if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) {
						ret = _attachmentsPre[i];
						break;
					}
				}
			}
		}

		if (ret != nullptr) {
			stack->pushNative(ret, true);
		} else {
			stack->pushNULL();
		}

		return STATUS_OK;
	} else {
		return BaseObject::scCallMethod(script, stack, thisStack, name);
	}
}