Ejemplo n.º 1
0
void RMTony::stop(CORO_PARAM) {
	CORO_BEGIN_CONTEXT;
	uint32 pid;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	if (_actionItem != NULL) {
		// Call MPAL to choose the direction
		_ctx->pid = mpalQueryDoAction(21, _actionItem->mpalCode(), 0);

		if (_ctx->pid == CORO_INVALID_PID_VALUE)
			CORO_INVOKE_0(RMCharacter::stop);
		else {
			_bNeedToStop = false;    // If we make the OnWhichDirection, we don't need at least after the Stop().
			_bMoving = false;
			CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->pid, CORO_INFINITE); // @@@ Put an assert after 10 seconds
		}
	} else {
		CORO_INVOKE_0(RMCharacter::stop);
	}

	if (!_bActionPending)
		return;

	_bActionPending = false;

	executeAction(_action, _actionItem->mpalCode(), _actionParm);

	_actionItem = NULL;

	CORO_END_CODE;
}
Ejemplo n.º 2
0
void RMTony::endStatic(CORO_PARAM, CharacterTalkType nTalk) {
	CORO_BEGIN_CONTEXT;
	int bodyEndPat;
	int finalPat;
	int headEndPat;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	_ctx->bodyEndPat = 0;
	_ctx->finalPat = 0;
	_ctx->headEndPat = 0;

	endStaticCalculate(nTalk, _ctx->bodyEndPat, _ctx->finalPat, _ctx->headEndPat);

	if (_ctx->headEndPat != 0) {
		setPattern(_ctx->headEndPat);

		CORO_INVOKE_0(waitForEndPattern);
	} else {
		// Play please
		_body.setPattern(_ctx->bodyEndPat);

		CORO_INVOKE_0(_body.waitForEndPattern);
	}

	setPattern(_ctx->finalPat);
	_body.setPattern(0);

	_bIsStaticTalk = false;

	CORO_END_CODE;
}
Ejemplo n.º 3
0
void RMTony::startStatic(CORO_PARAM, CharacterTalkType nTalk) {
	CORO_BEGIN_CONTEXT;
	int headPat, headLoopPat;
	int bodyStartPat, bodyLoopPat;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	_ctx->headPat = _ctx->headLoopPat = 0;
	_ctx->bodyStartPat = _ctx->bodyLoopPat = 0;

	startStaticCalculate(nTalk, _ctx->headPat, _ctx->headLoopPat,
		_ctx->bodyStartPat, _ctx->bodyLoopPat);

	// e vai con i pattern
	_bIsStaticTalk = true;

	setPattern(_ctx->headPat);
	_body.setPattern(_ctx->bodyStartPat);

	CORO_INVOKE_0(_body.waitForEndPattern);
	CORO_INVOKE_0(waitForEndPattern);

	if (_ctx->headLoopPat != -1)
		setPattern(_ctx->headLoopPat);
	_body.setPattern(_ctx->bodyLoopPat);

	CORO_END_CODE;
}
Ejemplo n.º 4
0
void RMTony::startTalk(CORO_PARAM, CharacterTalkType nTalkType) {
	CORO_BEGIN_CONTEXT;
	int headStartPat, bodyStartPat;
	int headLoopPat, bodyLoopPat;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	_ctx->headStartPat = _ctx->bodyStartPat = 0;
	_ctx->headLoopPat = _ctx->bodyLoopPat = 0;

	if (!startTalkCalculate(nTalkType, _ctx->headStartPat, _ctx->bodyStartPat,
	                        _ctx->headLoopPat, _ctx->bodyLoopPat))
		return;

	// Perform the set pattern
	if (_ctx->headStartPat != 0 || _ctx->bodyStartPat != 0) {
		setPattern(_ctx->headStartPat);
		_body.setPattern(_ctx->bodyStartPat);

		if (_ctx->bodyStartPat != 0)
			CORO_INVOKE_0(_body.waitForEndPattern);
		if (_ctx->headStartPat != 0)
			CORO_INVOKE_0(waitForEndPattern);
	}

	setPattern(_ctx->headLoopPat);
	if (_ctx->bodyLoopPat)
		_body.setPattern(_ctx->bodyLoopPat);

	CORO_END_CODE;
}
Ejemplo n.º 5
0
void RMDialogChoice::prepare(CORO_PARAM) {
	CORO_BEGIN_CONTEXT;
	int i;
	RMPoint ptPos;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	addPrim(new RMGfxPrimitive(&_dlgText, RMPoint(0, 0)));
	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155)));
	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83)));
	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83)));
	addPrim(new RMGfxPrimitive(&_dlgTextLine, RMPoint(0, 155 + 83 + 83 + 83)));

	_ctx->ptPos.set(20, 90);

	for (_ctx->i = 0; _ctx->i < _numChoices; _ctx->i++) {
		addPrim(new RMGfxPrimitive(&_drawedStrings[_ctx->i], _ctx->ptPos));
		_ptDrawStrings[_ctx->i] = _ctx->ptPos;
		_ctx->ptPos.offset(0, _drawedStrings[_ctx->i].getDimy() + 15);
	}

	CORO_INVOKE_0(drawOT);
	clearOT();

	_ptDrawPos.set(0, 480 - _ctx->ptPos._y);

	CORO_END_CODE;
}
Ejemplo n.º 6
0
void RMDialogChoice::setSelected(CORO_PARAM, int pos) {
	CORO_BEGIN_CONTEXT;
	RMGfxBox box;
	RMRect rc;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	if (pos == _curSelection)
		return;

	_ctx->box.setPriority(5);

	if (_curSelection != -1) {
		_ctx->box.setColor(0xCC, 0xCC, 0xFF);
		_ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[_curSelection]._y);
		_ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[_curSelection].getDimy());
		addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));

		addPrim(new RMGfxPrimitive(&_drawedStrings[_curSelection], _ptDrawStrings[_curSelection]));
		CORO_INVOKE_0(drawOT);
		clearOT();
	}

	if (pos != -1) {
		_ctx->box.setColor(100, 100, 100);
		_ctx->rc.topLeft() = RMPoint(18, _ptDrawStrings[pos]._y);
		_ctx->rc.bottomRight() = _ctx->rc.topLeft() + RMPoint(597, _drawedStrings[pos].getDimy());
		addPrim(new RMGfxPrimitive(&_ctx->box, _ctx->rc));
		addPrim(new RMGfxPrimitive(&_drawedStrings[pos], _ptDrawStrings[pos]));
	}

	CORO_INVOKE_0(drawOT);
	clearOT();

	_curSelection = pos;

	CORO_END_CODE;
}
Ejemplo n.º 7
0
void RMTony::stopNoAction(CORO_PARAM) {
	CORO_BEGIN_CONTEXT;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	if (_bAction)
		CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hActionThread, CORO_INFINITE);

	_bActionPending = false;
	_actionItem = NULL;
	CORO_INVOKE_0(stop);

	CORO_END_CODE;
}
Ejemplo n.º 8
0
void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) {
	CORO_BEGIN_CONTEXT;
	bool bRes;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	_ctx->bRes = false;

	if (type == 0)
		CORO_INVOKE_2(_opt.init, _bigBuf, _ctx->bRes);
	else if (type == 1)
		CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, true, _ctx->bRes);
	else if (type == 2)
		CORO_INVOKE_2(_opt.initNoLoadSave, _bigBuf, _ctx->bRes);
	else if (type == 3)
		CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, false, _ctx->bRes);
	else if (type == 4)
		CORO_INVOKE_3(_opt.initSaveMenuOnly, _bigBuf, false, _ctx->bRes);

	if (_ctx->bRes) {
		g_vm->pauseSound(true);

		disableInput();
		_inv.endCombine();
		_curActionObj = 0;
		_curAction = TA_GOTO;
		_point.setAction(_curAction);
		_point.setSpecialPointer(RMPointer::PTR_NONE);
		_point.setCustomPointer(NULL);
		enableMouse();
		g_vm->grabThumbnail();

		// Exists the IDLE to avoid premature death in loading
		_bMustEnterMenu = true;
		if (type == 1 || type == 2) {
			GLOBALS._bIdleExited = true;
		} else {
			CORO_INVOKE_0(_tony.stopNoAction);

			GLOBALS._bIdleExited = false;

			CoroScheduler.createProcess(exitAllIdles, &_nCurLoc, sizeof(int));
		}
	}

	CORO_END_CODE;
}
Ejemplo n.º 9
0
void RMDialogChoice::show(CORO_PARAM, RMGfxTargetBuffer *bigBuf) {
	CORO_BEGIN_CONTEXT;
	RMPoint destpt;
	int deltay;
	int starttime;
	int elaps;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	CORO_INVOKE_0(prepare);
	_bShow = false;

	if (!_nInList && bigBuf != NULL)
		bigBuf->addPrim(new RMGfxPrimitive(this));

	if (0) {
		_bShow = true;
	} else {
		_ctx->starttime = g_vm->getTime();
		_ctx->deltay = 480 - _ptDrawPos._y;
		_ctx->destpt = _ptDrawPos;
		_ptDrawPos.set(0, 480);

		if (!_nInList && bigBuf != NULL)
			bigBuf->addPrim(new RMGfxPrimitive(this));
		_bShow = true;

		_ctx->elaps = 0;
		while (_ctx->elaps < 700) {
			CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
			_ctx->elaps = g_vm->getTime() - _ctx->starttime;
			_ptDrawPos._y = 480 - ((_ctx->deltay * 100) / 700 * _ctx->elaps) / 100;
		}

		_ptDrawPos._y = _ctx->destpt._y;
	}

	CORO_END_CODE;
}
Ejemplo n.º 10
0
void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) {
	// PROBLEM: You should change the location in a separate process to do the OnEnter
	CORO_BEGIN_CONTEXT;
	Common::InSaveFile *f;
	byte *state, *statecmp;
	uint32 size, sizecmp;
	char buf[4];
	RMPoint tp;
	int loc;
	int ver;
	int i;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	_ctx->f = g_system->getSavefileManager()->openForLoading(fn);
	if (_ctx->f == NULL)
		return;
	_ctx->f->read(_ctx->buf, 4);

	if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') {
		delete _ctx->f;
		return;
	}

	_ctx->ver = _ctx->buf[3];

	if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) {
		delete _ctx->f;
		return;
	}

	if (_ctx->ver >= 0x3) {
		// There is a thumbnail. If the version is between 5 and 7, it's compressed
		if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) {
			_ctx->i = 0;
			_ctx->i = _ctx->f->readUint32LE();
			_ctx->f->seek(_ctx->i);
		} else {
			if (_ctx->ver >= 8)
				// Skip thumbnail size
				_ctx->f->skip(4);

			_ctx->f->seek(160 * 120 * 2, SEEK_CUR);
		}
	}

	if (_ctx->ver >= 0x5) {
		// Skip the difficulty level
		_ctx->f->seek(1, SEEK_CUR);
	}

	if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose
		_ctx->i = _ctx->f->readByte();
		_ctx->f->seek(_ctx->i, SEEK_CUR);
	}

	_ctx->loc = _ctx->f->readUint32LE();
	_ctx->tp._x = _ctx->f->readUint32LE();
	_ctx->tp._y = _ctx->f->readUint32LE();
	_ctx->size = _ctx->f->readUint32LE();

	if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) {
		// MPAL was packed!
		_ctx->sizecmp = _ctx->f->readUint32LE();
		_ctx->state = new byte[_ctx->size];
		_ctx->statecmp = new byte[_ctx->sizecmp];
		_ctx->f->read(_ctx->statecmp, _ctx->sizecmp);
		lzo1x_decompress(_ctx->statecmp, _ctx->sizecmp, _ctx->state, &_ctx->size);
		delete[] _ctx->statecmp;
	} else {
		// Read uncompressed MPAL data
		_ctx->state = new byte[_ctx->size];
		_ctx->f->read(_ctx->state, _ctx->size);
	}

	mpalLoadState(_ctx->state);
	delete[] _ctx->state;

	// Inventory
	_ctx->size = _ctx->f->readUint32LE();
	_ctx->state = new byte[_ctx->size];
	_ctx->f->read(_ctx->state, _ctx->size);
	_inv.loadState(_ctx->state);
	delete[] _ctx->state;

	if (_ctx->ver >= 0x2) {   // Version 2: box please
		_ctx->size = _ctx->f->readUint32LE();
		_ctx->state = new byte[_ctx->size];
		_ctx->f->read(_ctx->state, _ctx->size);
		g_vm->_theBoxes.loadState(_ctx->state);
		delete[] _ctx->state;
	}

	if (_ctx->ver >= 5) {
		// Version 5
		bool bStat = false;

		bStat = _ctx->f->readByte();
		_tony.setShepherdess(bStat);
		bStat = _ctx->f->readByte();
		_inter.setPerorate(bStat);

		charsLoadAll(_ctx->f);
	}

	if (_ctx->ver >= 6) {
		// Load options
		GLOBALS._bCfgInvLocked = _ctx->f->readByte();
		GLOBALS._bCfgInvNoScroll = _ctx->f->readByte();
		GLOBALS._bCfgTimerizedText = _ctx->f->readByte();
		GLOBALS._bCfgInvUp = _ctx->f->readByte();
		GLOBALS._bCfgAnni30 = _ctx->f->readByte();
		GLOBALS._bCfgAntiAlias = _ctx->f->readByte();
		GLOBALS._bShowSubtitles = _ctx->f->readByte();
		GLOBALS._bCfgTransparence = _ctx->f->readByte();
		GLOBALS._bCfgInterTips = _ctx->f->readByte();
		GLOBALS._bCfgDubbing = _ctx->f->readByte();
		GLOBALS._bCfgMusic = _ctx->f->readByte();
		GLOBALS._bCfgSFX = _ctx->f->readByte();
		GLOBALS._nCfgTonySpeed = _ctx->f->readByte();
		GLOBALS._nCfgTextSpeed = _ctx->f->readByte();
		GLOBALS._nCfgDubbingVolume = _ctx->f->readByte();
		GLOBALS._nCfgMusicVolume = _ctx->f->readByte();
		GLOBALS._nCfgSFXVolume = _ctx->f->readByte();

		// Load hotspots
		loadChangedHotspot(_ctx->f);
	}

	if (_ctx->ver >= 7) {
		loadMusic(_ctx->f);
	}

	delete _ctx->f;

	CORO_INVOKE_2(unloadLocation, false, NULL);
	loadLocation(_ctx->loc, _ctx->tp, RMPoint(-1, -1));
	_tony.setPattern(RMTony::PAT_STANDRIGHT);

	// On older versions, need to an enter action
	if (_ctx->ver < 5)
		mpalQueryDoAction(0, _ctx->loc, 0);
	else {
		// In the new ones, we just reset the mcode
		mCharResetCodes();
	}

	if (_ctx->ver >= 6)
		reapplyChangedHotspot();

	CORO_INVOKE_0(restoreMusic);

	_bGUIInterface = true;
	_bGUIInventory = true;
	_bGUIOption = true;

	CORO_END_CODE;
}
Ejemplo n.º 11
0
void RMGfxEngine::doFrame(CORO_PARAM, bool bDrawLocation) {
	CORO_BEGIN_CONTEXT;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	// Poll of input devices
	_input.poll();

	if (_bMustEnterMenu && GLOBALS._bIdleExited) {
		_bOption = true;
		_bMustEnterMenu = false;
		GLOBALS._bIdleExited = false;
	}

	if (_bOption) {
		CORO_INVOKE_1(_opt.doFrame, &_input);
		_bOption = !_opt.isClosing();
		if (!_bOption) {
			disableMouse();
			enableInput();
			mpalStartIdlePoll(_nCurLoc);
			g_vm->pauseSound(false);
		}
	}

	if (bDrawLocation && _bLocationLoaded) {
		// Location and objects
		_loc.doFrame(&_bigBuf);

		// Check the mouse input
		if (_bInput && !_tony.inAction()) {
			// If we are on the inventory, it is it who controls all input
			if (_inv.haveFocus(_input.mousePos()) && !_inter.active()) {
				// Left Click
				// **********
				if (_input.mouseLeftClicked()/* && m_itemName.IsItemSelected()*/) {
					// Left click activates the combine, if we are on an object
					if (_inv.leftClick(_input.mousePos(), _curActionObj)) {
						_curAction = TA_COMBINE;
						_point.setAction(_curAction);
					}
				} else

					// Right Click
					// ***********
					if (_input.mouseRightClicked()) {
						if (_itemName.isItemSelected()) {
							_curActionObj = 0;
							_inv.rightClick(_input.mousePos());
						} else
							_inv.rightClick(_input.mousePos());
					} else

						// Right Release
						// *************
						if (_input.mouseRightReleased()) {
							if (_inv.rightRelease(_input.mousePos(), _curAction)) {
								CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);

								_curAction = TA_GOTO;
								_point.setAction(_curAction);
							}
						}
			} else {
				// Options Menu
				// ************
				if (_bGUIOption) {
					if (!_tony.inAction() && _bInput) {
						if ((_input.mouseLeftClicked() && _input.mousePos()._x < 3 && _input.mousePos()._y < 3)) {
							CORO_INVOKE_1(openOptionScreen, 0);
							goto SKIPCLICKSINISTRO;
						} else if (_input.getAsyncKeyState(Common::KEYCODE_ESCAPE))
							CORO_INVOKE_1(openOptionScreen, 0);
						else if (!g_vm->getIsDemo()) {
							if (_input.getAsyncKeyState(Common::KEYCODE_F3) || _input.getAsyncKeyState(Common::KEYCODE_F5))
								// Save game screen
								CORO_INVOKE_1(openOptionScreen, 4);
							else if (_input.getAsyncKeyState(Common::KEYCODE_F2) || _input.getAsyncKeyState(Common::KEYCODE_F7))
								// Load game screen
								CORO_INVOKE_1(openOptionScreen, 3);
						}
					}
				}

				// Left Click
				// **************
				if (_input.mouseLeftClicked() && !_inter.active()) {

					if (_curAction != TA_COMBINE)
						CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _point.curAction());
					else if (_itemName.getSelectedItem() != NULL)
						CORO_INVOKE_4(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), TA_COMBINE, _curActionObj);

					if (_curAction == TA_COMBINE) {
						_inv.endCombine();
						_point.setSpecialPointer(RMPointer::PTR_NONE);
					}

					_curAction = TA_GOTO;
					_point.setAction(_curAction);
				}

SKIPCLICKSINISTRO:
				// Right Click
				// ************
				if (_curAction == TA_COMBINE) {
					// During a combine, it cancels it
					if (_input.mouseRightClicked()) {
						_inv.endCombine();
						_curActionObj = 0;
						_curAction = TA_GOTO;
						_point.setAction(_curAction);
						_point.setSpecialPointer(RMPointer::PTR_NONE);
					}
				} else if (_input.mouseRightClicked() && _itemName.isItemSelected() && _point.getSpecialPointer() == RMPointer::PTR_NONE) {
					if (_bGUIInterface) {
						// Before opening the interface, replaces GOTO
						_curAction = TA_GOTO;
						_curActionObj = 0;
						_point.setAction(_curAction);
						_inter.clicked(_input.mousePos());
					}
				}


				// Right Release
				// *************
				if (_input.mouseRightReleased()) {
					if (_bGUIInterface) {
						if (_inter.released(_input.mousePos(), _curAction)) {
							_point.setAction(_curAction);
							CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);

							_curAction = TA_GOTO;
							_point.setAction(_curAction);
						}
					}
				}
			}

			// Update the name under the mouse pointer
			_itemName.setMouseCoord(_input.mousePos());
			if (!_inter.active() && !_inv.miniActive())
				CORO_INVOKE_4(_itemName.doFrame, _bigBuf, _loc, _point, _inv);
		}

		// Interface & Inventory
		_inter.doFrame(_bigBuf, _input.mousePos());
		_inv.doFrame(_bigBuf, _point, _input.mousePos(), (!_tony.inAction() && !_inter.active() && _bGUIInventory));
	}

	// Animate Tony
	CORO_INVOKE_2(_tony.doFrame, &_bigBuf, _nCurLoc);

	// Update screen scrolling to keep Tony in focus
	if (_tony.mustUpdateScrolling() && _bLocationLoaded) {
		RMPoint showThis = _tony.position();
		showThis._y -= 60;
		_loc.updateScrolling(showThis);
	}

	if (_bLocationLoaded)
		_tony.setScrollPosition(_loc.scrollPosition());

	if ((!_tony.inAction() && _bInput) || _bAlwaysDrawMouse) {
		_point.showCursor();
	} else {
		_point.hideCursor();
	}
	_point.doFrame();

	// **********************
	// Draw the list in the OT
	// **********************
	CORO_INVOKE_0(_bigBuf.drawOT);

#define FSTEP (480/32)

	// Wipe
	if (_bWiping) {
		switch (_nWipeType) {
		case 1:
			if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top >= FSTEP * 2)) {
				CoroScheduler.setEvent(_hWipeEvent);
				_nWipeType = 3;
				break;
			}

			_rcWipeEllipse.top += FSTEP;
			_rcWipeEllipse.left += FSTEP;
			_rcWipeEllipse.right -= FSTEP;
			_rcWipeEllipse.bottom -= FSTEP;
			break;

		case 2:
			if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top < 480 - FSTEP)) {
				CoroScheduler.setEvent(_hWipeEvent);
				_nWipeType = 3;
				break;
			}

			_rcWipeEllipse.top -= FSTEP;
			_rcWipeEllipse.left -= FSTEP;
			_rcWipeEllipse.right += FSTEP;
			_rcWipeEllipse.bottom += FSTEP;
			break;
		}
	}

	CORO_END_CODE;
}
Ejemplo n.º 12
0
void RMTony::endTalk(CORO_PARAM) {
	CORO_BEGIN_CONTEXT;
	int headStandPat, headEndPat;
	int bodyEndPat, finalPat;
	bool bStatic;
	CORO_END_CONTEXT(_ctx);

	CORO_BEGIN_CODE(_ctx);

	_ctx->headStandPat = _ctx->headEndPat = 0;
	_ctx->bodyEndPat = _ctx->finalPat = 0;
	_ctx->bStatic = false;

	_ctx->bodyEndPat = 0;
	_ctx->headEndPat = 0;

	if (!endTalkCalculate(_ctx->headStandPat, _ctx->headEndPat, _ctx->bodyEndPat, _ctx->finalPat, _ctx->bStatic))
		return;

	// Handles the end of an animated and static, leaving everything unchanged
	if (_bIsStaticTalk) {
		if (_nTalkType == TALK_WITHBEARDSTATIC) {
			setPattern(0);
			if (_talkDirection == UP || _talkDirection == LEFT) {
				_body.setPattern(BPAT_WITHBEARDLEFT_STATIC);
				_nBodyOffset.set(-41, -14);
			} else if (_talkDirection == DOWN || _talkDirection == RIGHT) {
				_body.setPattern(BPAT_WITHBEARDRIGHT_STATIC);
				_nBodyOffset.set(-26, -14);
			}
		} else {
			setPattern(_ctx->headStandPat);

			CORO_INVOKE_0(_body.waitForEndPattern);
		}

		_bIsTalking = false;
		return;
	}

	// Set the pattern
	if (_ctx->headEndPat != 0 && _ctx->bodyEndPat != 0) {
		setPattern(_ctx->headEndPat);

		CORO_INVOKE_0(_body.waitForEndPattern);

		_body.setPattern(_ctx->bodyEndPat);

		CORO_INVOKE_0(waitForEndPattern);
		CORO_INVOKE_0(_body.waitForEndPattern);
	} else if (_ctx->bodyEndPat != 0) {
		setPattern(_ctx->headStandPat);

		CORO_INVOKE_0(_body.waitForEndPattern);

		_body.setPattern(_ctx->bodyEndPat);

		CORO_INVOKE_0(_body.waitForEndPattern);
	} else if (_ctx->headEndPat != 0) {
		CORO_INVOKE_0(_body.waitForEndPattern);

		setPattern(_ctx->headEndPat);

		CORO_INVOKE_0(waitForEndPattern);
	} else {
		CORO_INVOKE_0(_body.waitForEndPattern);
	}

	if (_ctx->finalPat != 0) {
		_body.setPattern(0);
		setPattern(_ctx->finalPat);
	}

	_bIsTalking = false;

	CORO_END_CODE;
}