[Scummvm-git-logs] scummvm master -> a4811f67b9eb670dc0492f5c82b7805422baaa8b

sev- sev at scummvm.org
Wed Mar 17 16:34:49 UTC 2021


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
adaf5bb3b9 PEGASUS: Import the DVD version patch from GOG.com
a4811f67b9 NEWS: Mention recent improvements


Commit: adaf5bb3b93e99964abb6a13b20ab7bb901830ca
    https://github.com/scummvm/scummvm/commit/adaf5bb3b93e99964abb6a13b20ab7bb901830ca
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2021-03-17T17:32:53+01:00

Commit Message:
PEGASUS: Import the DVD version patch from GOG.com

Co-Authored-By: Keith Kaisershot <keithkaisershot at gmail.com>
Co-Authored-By: Matthew Hoops <clone2727 at gmail.com>
Co-Authored-By: Bastien Bouclet <bastien.bouclet at gmail.com>

Changed paths:
  A engines/pegasus/chase.cpp
  A engines/pegasus/chase.h
  A engines/pegasus/items/biochips/arthurchip.cpp
  A engines/pegasus/items/biochips/arthurchip.h
  A engines/pegasus/neighborhood/mars/canyonchase.cpp
  A engines/pegasus/neighborhood/mars/canyonchase.h
  A engines/pegasus/neighborhood/mars/tunnelpod.cpp
  A engines/pegasus/neighborhood/mars/tunnelpod.h
  A engines/pegasus/neighborhood/norad/alpha/subchase.cpp
  A engines/pegasus/neighborhood/norad/alpha/subchase.h
    AUTHORS
    devtools/credits.pl
    engines/pegasus/ai/ai_action.cpp
    engines/pegasus/ai/ai_area.cpp
    engines/pegasus/constants.h
    engines/pegasus/cursor.cpp
    engines/pegasus/detection.cpp
    engines/pegasus/input.cpp
    engines/pegasus/input.h
    engines/pegasus/interface.cpp
    engines/pegasus/items/biochips/opticalchip.cpp
    engines/pegasus/items/inventorypicture.cpp
    engines/pegasus/items/inventorypicture.h
    engines/pegasus/items/item.h
    engines/pegasus/menu.cpp
    engines/pegasus/menu.h
    engines/pegasus/metaengine.cpp
    engines/pegasus/module.mk
    engines/pegasus/neighborhood/caldoria/caldoria.cpp
    engines/pegasus/neighborhood/caldoria/caldoria.h
    engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp
    engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h
    engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp
    engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp
    engines/pegasus/neighborhood/mars/constants.h
    engines/pegasus/neighborhood/mars/mars.cpp
    engines/pegasus/neighborhood/mars/mars.h
    engines/pegasus/neighborhood/neighborhood.cpp
    engines/pegasus/neighborhood/neighborhood.h
    engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp
    engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp
    engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp
    engines/pegasus/neighborhood/norad/alpha/noradalpha.h
    engines/pegasus/neighborhood/norad/constants.h
    engines/pegasus/neighborhood/norad/delta/globegame.cpp
    engines/pegasus/neighborhood/norad/delta/globegame.h
    engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
    engines/pegasus/neighborhood/norad/delta/noraddelta.h
    engines/pegasus/neighborhood/norad/norad.cpp
    engines/pegasus/neighborhood/norad/subcontrolroom.cpp
    engines/pegasus/neighborhood/norad/subplatform.cpp
    engines/pegasus/neighborhood/prehistoric/prehistoric.cpp
    engines/pegasus/neighborhood/prehistoric/prehistoric.h
    engines/pegasus/neighborhood/tsa/fulltsa.cpp
    engines/pegasus/neighborhood/tsa/fulltsa.h
    engines/pegasus/neighborhood/tsa/tinytsa.cpp
    engines/pegasus/neighborhood/wsc/wsc.cpp
    engines/pegasus/neighborhood/wsc/wsc.h
    engines/pegasus/pegasus.cpp
    engines/pegasus/pegasus.h
    engines/pegasus/timers.cpp
    gui/credits.h


diff --git a/AUTHORS b/AUTHORS
index 436a88edee..98a66370be 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -847,7 +847,8 @@ Other contributions
                                       audio
      Kovacs Endre Janos             - Several fixes for Simon1
      Jeroen Janssen                 - Numerous readability and bugfix patches
-     Keith Kaisershot               - Several Pegasus Prime patches
+     Keith Kaisershot               - Several Pegasus Prime patches and DVD
+                                      additions
      Andreas Karlsson               - Initial port for SymbianOS
      Stefan Kristiansson            - Initial work on SDL2 support
      Claudio Matsuoka               - Daily Linux builds
@@ -1105,4 +1106,3 @@ Special thanks to
 
    Benjamin Haisch, for emimeshviewer, which our EMI code borrows heavily
    from.
-
diff --git a/devtools/credits.pl b/devtools/credits.pl
index a3638cd134..2c97fade40 100755
--- a/devtools/credits.pl
+++ b/devtools/credits.pl
@@ -1529,7 +1529,7 @@ begin_credits("Credits");
 				add_person("Janne Huttunen", "", "V3 actor mask support, Dig/FT SMUSH audio");
 				add_person("Kovács Endre János", "", "Several fixes for Simon1");
 				add_person("Jeroen Janssen", "japj", "Numerous readability and bugfix patches");
-				add_person("Keith Kaisershot", "blitter", "Several Pegasus Prime patches");
+				add_person("Keith Kaisershot", "blitter", "Several Pegasus Prime patches and DVD additions");
 				add_person("Andreas Karlsson", "Sprawl", "Initial port for SymbianOS");
 				add_person("Stefan Kristiansson", "skristiansson", "Initial work on SDL2 support");
 				add_person("Claudio Matsuoka", "", "Daily Linux builds");
diff --git a/engines/pegasus/ai/ai_action.cpp b/engines/pegasus/ai/ai_action.cpp
index 61e2dba3d8..737cc246b3 100644
--- a/engines/pegasus/ai/ai_action.cpp
+++ b/engines/pegasus/ai/ai_action.cpp
@@ -23,6 +23,7 @@
  *
  */
 
+#include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_action.h"
 #include "pegasus/ai/ai_area.h"
 
@@ -45,7 +46,7 @@ AIPlayMessageAction::AIPlayMessageAction(const Common::String &movieName, bool k
 }
 
 void AIPlayMessageAction::performAIAction(AIRule *) {
-	if (g_AIArea) {
+	if (g_AIArea && ((PegasusEngine *)g_engine)->isChattyAI()) {
 		g_AIArea->checkMiddleArea();
 		g_AIArea->playAIMovie(kRightAreaSignature, _movieName, _keepLastFrame, _interruptionFilter);
 	}
diff --git a/engines/pegasus/ai/ai_area.cpp b/engines/pegasus/ai/ai_area.cpp
index 34ca93e4e9..1becc00962 100644
--- a/engines/pegasus/ai/ai_area.cpp
+++ b/engines/pegasus/ai/ai_area.cpp
@@ -29,6 +29,7 @@
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
 #include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/biochipitem.h"
 #include "pegasus/items/biochips/opticalchip.h"
 #include "pegasus/items/biochips/pegasuschip.h"
@@ -50,6 +51,7 @@ AIArea::AIArea(InputHandler *nextHandler) : InputHandler(nextHandler), _leftArea
 	_middleBiochipTime = 0xffffffff;
 	_rightBiochipTime = 0xffffffff;
 	_lockCount = 0;
+	((PegasusEngine *)g_engine)->requestToggle(false);
 	startIdling();
 }
 
@@ -458,6 +460,10 @@ void AIArea::activateHotspots() {
 			case kOpticalBiochip:
 				((OpticalChip *)currentBiochip)->activateOpticalHotspots();
 				break;
+			case kArthurBiochip:
+				if (vm->isDVD())
+					((ArthurChip *)currentBiochip)->activateArthurHotspots();
+				break;
 			default:
 				break;
 			}
@@ -498,6 +504,12 @@ void AIArea::clickInHotspot(const Input &input, const Hotspot *hotspot) {
 					handled = true;
 				}
 				break;
+			case kArthurBiochip:
+				if (vm->isDVD() && (hotspot->getHotspotFlags() & kArthurBiochipSpotFlag) != 0) {
+					((ArthurChip *)currentBiochip)->clickInArthurHotspot(hotspot->getObjectID());
+					handled = true;
+				}
+				break;
 			default:
 				break;
 			}
diff --git a/engines/pegasus/chase.cpp b/engines/pegasus/chase.cpp
new file mode 100644
index 0000000000..43f88d3656
--- /dev/null
+++ b/engines/pegasus/chase.cpp
@@ -0,0 +1,123 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include "pegasus/chase.h"
+
+namespace Pegasus {
+
+// Notification constants.
+
+static const NotificationFlags kChaseNotificationFlags = kChaseEnteredBranchZone |
+															kChaseExitedBranchZone |
+															kChaseFinished;
+
+ChaseInteraction::ChaseInteraction(const InteractionID interactionID, Neighborhood *handler,
+	const NotificationID notificationID, NotificationManager *manager) : GameInteraction(interactionID, handler),
+	_chaseNotification(notificationID, manager), _steerPict(kNoDisplayElement), _directionNeeded(false),
+	_turnDirection(kNoTurn) {
+
+	// HACK HACK HACK HACK HACK
+	// TRIPLE SUPER ULTRA HACK
+	// This avoids a nasty optimization bug I have been unable to track down
+	// after days and days of searching. Let's just ship the game already.
+	setNextHandler(0);
+
+	_neighborhoodNotification = handler->getNeighborhoodNotification();
+}
+
+void ChaseInteraction::openInteraction() {
+	_steerPict.initFromPICTFile("Images/Interface/steer.pict", true);
+
+	_chaseNotification.notifyMe(this, kChaseNotificationFlags, kChaseNotificationFlags);
+
+	_neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag,
+										kDelayCompletedFlag | kSpotSoundCompletedFlag);
+}
+
+void ChaseInteraction::closeInteraction() {
+	_steerPict.hide();
+	_steerPict.deallocateSurface();
+
+	_chaseNotification.cancelNotification(this);
+
+	_neighborhoodNotification->cancelNotification(this);
+}
+
+void ChaseInteraction::receiveNotification(Notification *notification, const NotificationFlags flags) {
+	if (notification == &_chaseNotification)
+		switch (flags) {
+		case kChaseEnteredBranchZone:
+			_directionNeeded = true;
+			showControlsHint();
+			setUpBranch();
+			break;
+		case kChaseExitedBranchZone:
+			switch (_turnDirection) {
+			case kTurnLeft:
+				branchLeft();
+				break;
+			case kTurnRight:
+				branchRight();
+				break;
+			case kNoTurn:
+				dontBranch();
+				break;
+			default:
+				break;
+			}
+			hideControlsHint();
+			_turnDirection = kNoTurn;
+			_directionNeeded = false;
+			break;
+		case kChaseFinished:
+			_owner->requestDeleteCurrentInteraction();
+			break;
+		default:
+			break;
+		}
+}
+
+void ChaseInteraction::handleInput(const Input &input, const Hotspot *cursorSpot) {
+	if (input.anyDirectionInput()) {
+		if (_directionNeeded) {
+			if (input.leftButtonAnyDown())
+				_turnDirection = kTurnLeft;
+			else if (input.rightButtonAnyDown())
+				_turnDirection = kTurnRight;
+		}
+	} else {
+		this->InputHandler::handleInput(input, cursorSpot);
+	}
+}
+
+void ChaseInteraction::showControlsHint() {
+	_steerPict.show();
+}
+
+void ChaseInteraction::hideControlsHint() {
+	_steerPict.hide();
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/chase.h b/engines/pegasus/chase.h
new file mode 100644
index 0000000000..68b925baf9
--- /dev/null
+++ b/engines/pegasus/chase.h
@@ -0,0 +1,83 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef PEGASUS_CHASE_H
+#define PEGASUS_CHASE_H
+
+#include "pegasus/hotspot.h"
+#include "pegasus/interaction.h"
+#include "pegasus/notification.h"
+#include "pegasus/neighborhood/neighborhood.h"
+
+namespace Pegasus {
+
+// Notification constants.
+
+static const NotificationFlags kChaseEnteredBranchZone = 1;
+static const NotificationFlags kChaseExitedBranchZone = kChaseEnteredBranchZone << 1;
+static const NotificationFlags kChaseFinished = kChaseExitedBranchZone << 1;
+
+// Time to choose a direction.
+
+static const TimeValue kDecisionTime = 600 * 2;
+
+class ChaseInteraction : public GameInteraction, public NotificationReceiver {
+public:
+	ChaseInteraction(const InteractionID, Neighborhood *, NotificationID, NotificationManager *);
+	virtual ~ChaseInteraction() {}
+
+	virtual void handleInput(const Input &, const Hotspot *);
+
+protected:
+
+	virtual void openInteraction();
+	virtual void initInteraction() {}
+	virtual void closeInteraction();
+
+	virtual void receiveNotification(Notification *, const NotificationFlags);
+
+	virtual void setUpBranch() {}
+	virtual void branchLeft() {}
+	virtual void branchRight() {}
+	virtual void dontBranch() {}
+
+	virtual void showControlsHint();
+	virtual void hideControlsHint();
+
+	Notification _chaseNotification;
+
+	Notification *_neighborhoodNotification;
+
+	Picture _steerPict;
+
+private:
+
+	bool _directionNeeded;
+	TurnDirection _turnDirection;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h
index f288729849..260779c442 100644
--- a/engines/pegasus/constants.h
+++ b/engines/pegasus/constants.h
@@ -379,12 +379,15 @@ static const NotificationID kNoradPressureNotificationID = kNoradFillingStationN
 static const NotificationID kNoradUtilityNotificationID = kNoradPressureNotificationID + 1;
 static const NotificationID kNoradElevatorNotificationID = kNoradUtilityNotificationID + 1;
 static const NotificationID kNoradSubPlatformNotificationID = kNoradElevatorNotificationID + 1;
-static const NotificationID kSubControlNotificationID = kNoradSubPlatformNotificationID + 1;
+static const NotificationID kNoradSubChaseNotificationID = kNoradSubPlatformNotificationID + 1;
+static const NotificationID kSubControlNotificationID = kNoradSubChaseNotificationID + 1;
 static const NotificationID kNoradGreenBallNotificationID = kSubControlNotificationID + 1;
 static const NotificationID kNoradGlobeNotificationID = kNoradGreenBallNotificationID + 1;
 static const NotificationID kCaldoriaVidPhoneNotificationID = kNoradGlobeNotificationID + 1;
 static const NotificationID kCaldoriaMessagesNotificationID = kCaldoriaVidPhoneNotificationID + 1;
 static const NotificationID kCaldoriaBombTimerNotificationID = kCaldoriaMessagesNotificationID + 1;
+static const NotificationID kMarsTunnelPodNotificationID = kCaldoriaBombTimerNotificationID + 1;
+static const NotificationID kMarsCanyonChaseNotificationID = kMarsTunnelPodNotificationID + 1;
 
 // Sent to the shell by fShellNotification.
 static const NotificationFlags kGameStartingFlag = 1;
@@ -463,7 +466,12 @@ static const HotSpotID kAISolveSpotID = kAIHint3SpotID + 1;
 static const HotSpotID kAIBriefingSpotID = kAISolveSpotID + 1;
 static const HotSpotID kAIScanSpotID = kAIBriefingSpotID + 1;
 
-static const HotSpotID kPegasusRecallSpotID = kAIScanSpotID + 1;
+static const HotSpotID kArthurWisdomSpotID = kAIScanSpotID + 1;
+static const HotSpotID kChattyArthurSpotID = kArthurWisdomSpotID + 1;
+static const HotSpotID kChattyAISpotID = kChattyArthurSpotID + 1;
+static const HotSpotID kArthurHeadSpotID = kChattyAISpotID + 1;
+
+static const HotSpotID kPegasusRecallSpotID = kArthurHeadSpotID + 1;
 
 static const HotSpotID kAriesSpotID = kPegasusRecallSpotID + 1;
 static const HotSpotID kMercurySpotID = kAriesSpotID + 1;
@@ -501,7 +509,8 @@ static const HotSpotFlags kInfoReturnSpotFlag = kDropBiochipSpotFlag << 1;
 // Biochip and inventory hot spot flags...
 
 static const HotSpotFlags kAIBiochipSpotFlag = kInfoReturnSpotFlag << 1;
-static const HotSpotFlags kPegasusBiochipSpotFlag = kAIBiochipSpotFlag << 1;
+static const HotSpotFlags kArthurBiochipSpotFlag = kAIBiochipSpotFlag << 1;
+static const HotSpotFlags kPegasusBiochipSpotFlag = kArthurBiochipSpotFlag << 1;
 static const HotSpotFlags kOpticalBiochipSpotFlag = kPegasusBiochipSpotFlag << 1;
 static const HotSpotFlags kAirMaskSpotFlag = kOpticalBiochipSpotFlag << 1;
 
@@ -510,6 +519,7 @@ static const HotSpotFlags kJMPClickingSpotFlags = kClickSpotFlag |
 											kOpenDoorSpotFlag |
 											kInfoReturnSpotFlag |
 											kAIBiochipSpotFlag |
+											kArthurBiochipSpotFlag |
 											kPegasusBiochipSpotFlag |
 											kOpticalBiochipSpotFlag |
 											kAirMaskSpotFlag;
@@ -655,6 +665,7 @@ enum {
 	// Mars
 	kDeathWrongShuttleLock,
 	kDeathArrestedInMars,
+	kDeathCollidedWithPod,
 	kDeathRunOverByPod,
 	kDeathDidntGetOutOfWay,
 	kDeathReactorBurn,
diff --git a/engines/pegasus/cursor.cpp b/engines/pegasus/cursor.cpp
index 636b52051e..0dadac207b 100644
--- a/engines/pegasus/cursor.cpp
+++ b/engines/pegasus/cursor.cpp
@@ -155,6 +155,7 @@ void Cursor::loadCursorImage(CursorInfo &cursorInfo) {
 
 	cursorInfo.surface = new Graphics::Surface();
 
+	// The CD version uses (only) lower color cicn images for its cursors
 	Common::SeekableReadStream *cicnStream = vm->_resFork->getResource(MKTAG('c', 'i', 'c', 'n'), cursorInfo.tag);
 
 	if (!cicnStream)
diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp
index aeb7f425ad..5dcaf66706 100644
--- a/engines/pegasus/detection.cpp
+++ b/engines/pegasus/detection.cpp
@@ -49,6 +49,41 @@ static const PegasusGameDescription gameDescriptions[] = {
 		},
 	},
 
+	{
+		{
+			"pegasus",
+			"DVD",
+			AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2075337),
+			Common::EN_ANY,
+			Common::kPlatformMacintosh,
+			ADGF_MACRESFORK|GF_DVD,
+			GUIO0()
+		},
+	},
+
+	{
+		{	"pegasus",
+			"DVD",
+			AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2075337),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_MACRESFORK|GF_DVD,
+			GUIO0()
+		},
+	},
+
+	{
+		{
+			"pegasus",
+			"DVD",
+			AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2075337),
+			Common::EN_ANY,
+			Common::kPlatformLinux,
+			ADGF_MACRESFORK|GF_DVD,
+			GUIO0()
+		},
+	},
+
 	{
 		{
 			"pegasus",
@@ -73,7 +108,7 @@ static const PegasusGameDescription gameDescriptions[] = {
 		},
 	},
 
-		{
+	{
 		{
 			"pegasus",
 			"DVD Demo",
diff --git a/engines/pegasus/input.cpp b/engines/pegasus/input.cpp
index c9689adf07..471f1c5918 100644
--- a/engines/pegasus/input.cpp
+++ b/engines/pegasus/input.cpp
@@ -46,6 +46,7 @@ InputDeviceManager::InputDeviceManager() {
 
 	g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 2, false);
 	_lastRawBits = kAllUpBits;
+	_AKeyWasDown = false;
 }
 
 InputDeviceManager::~InputDeviceManager() {
@@ -53,17 +54,29 @@ InputDeviceManager::~InputDeviceManager() {
 }
 
 void InputDeviceManager::getInput(Input &input, const InputBits filter) {
-	// Poll for events, but ignore them!
-	// We'll pick them up in notifyEvent()
+	// Poll for events, but ignore most of them!
+	// We'll pick the rest up in notifyEvent()
 	// We do that so that any pollEvent() call can update the variables
 	// (ie. if one uses enter to access the restore menu, we never receive
 	// the key up event, which leads to bad things)
 	// This is to closely emulate what the GetKeys() function did on Mac OS
-	pumpEvents();
-
-	// Now create the bitfield
 	InputBits currentBits = 0;
 
+	Common::Event event;
+	while (g_system->getEventManager()->pollEvent(event)) {
+		switch (event.type) {
+			case Common::EVENT_WHEELUP:
+				currentBits |= (kRawButtonDown << kUpButtonShift);
+				break;
+			case Common::EVENT_WHEELDOWN:
+				currentBits |= (kRawButtonDown << kDownButtonShift);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Now fill in the rest of the bitfield
 	if (_keysDown[kPegasusActionUp])
 		currentBits |= (kRawButtonDown << kUpButtonShift);
 
@@ -94,6 +107,21 @@ void InputDeviceManager::getInput(Input &input, const InputBits filter) {
 	if (_keysDown[kPegasusActionShowBiochip])
 		currentBits |= (kRawButtonDown << kRightFireButtonShift);
 
+	if (((PegasusEngine *)g_engine)->isDVD()) {
+		if (_keysDown[kPegasusActionToggleChattyAI] && !_AKeyWasDown) {
+			((PegasusEngine *)g_engine)->requestToggle();
+			_AKeyWasDown = true;
+		} else if (!_keysDown[kPegasusActionToggleChattyAI])
+			_AKeyWasDown = false;
+	}
+
+	// Update mouse button state
+	// Note that we don't use EVENT_LBUTTONUP/EVENT_LBUTTONDOWN because
+	// they do not show if the button is being held down. We're treating
+	// both mouse buttons as the same for ease of use.
+	if (g_system->getEventManager()->getButtonState() != 0)
+		currentBits |= (kRawButtonDown << kTwoButtonShift);
+
 	// Update the mouse position too
 	input.setInputLocation(g_system->getEventManager()->getMousePos());
 
diff --git a/engines/pegasus/input.h b/engines/pegasus/input.h
index 68f9404259..151cdbe31d 100644
--- a/engines/pegasus/input.h
+++ b/engines/pegasus/input.h
@@ -53,6 +53,7 @@ enum PegasusAction {
 	kPegasusActionSaveGameState,
 	kPegasusActionLoadGameState,
 	kPegasusActionEnableEasterEgg,
+	kPegasusActionToggleChattyAI,
 
 	kPegasusActionCount
 };
@@ -76,6 +77,7 @@ protected:
 	// Keep track of which keys are down (= true) or not
 	bool _keysDown[kPegasusActionCount];
 	InputBits _lastRawBits;
+	bool _AKeyWasDown;
 };
 
 enum {
@@ -297,7 +299,7 @@ enum {
 };
 
 static const InputBits kHintInterruption = kFilterAllInputNoAuto;
-static const InputBits kWarningInterruption = kFilterNoInput;
+static const InputBits kWarningInterruption = kFilterAllInputNoAuto;
 static const InputBits kOpticalInterruption = kFilterAllInputNoAuto;
 
 /*
@@ -496,7 +498,7 @@ public:
 
 	static bool isRaiseInventoryInput(const Input &input) { return input.leftFireButtonDown(); }
 	static bool isRaiseBiochipsInput(const Input &input) { return input.rightFireButtonDown(); }
-	static InputBits getItemPanelsInputFilter() { return kFilterLeftFireButton | kFilterRightFireButton; }
+	static InputBits getItemPanelsInputFilter() { return kFilterFourButton | kFilterLeftFireButton | kFilterRightFireButton; }
 
 	static bool isToggleAIMiddleInput(const Input &input) { return input.threeButtonDown(); }
 
diff --git a/engines/pegasus/interface.cpp b/engines/pegasus/interface.cpp
index a0b3c1ca92..b4d23c0228 100644
--- a/engines/pegasus/interface.cpp
+++ b/engines/pegasus/interface.cpp
@@ -400,6 +400,7 @@ void Interface::raiseInventoryDrawer(const bool doCallBacks) {
 
 	if (((PegasusEngine *)g_engine)->isDVD()) {
 		_inventoryCloseSound.stopSound();
+		_inventoryOpenSound.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
 		_inventoryOpenSound.playSound();
 	}
 }
@@ -414,6 +415,15 @@ void Interface::playEndMessage() {
 
 void Interface::raiseInventoryDrawerForMessage() {
 	_inventoryPanel.disableLooping();
+
+	// The DVD version has a different image for the inventory
+	// for the end message.
+	if (((PegasusEngine *)g_engine)->isDVD()) {
+		_inventoryPanel.setCommPicture();
+		_inventoryPanel.throwAwayInventoryImage();
+		_inventoryPanel.initInventoryImage(&_inventoryPush);
+	}
+
 	raiseInventoryDrawerSync();
 }
 
@@ -466,6 +476,7 @@ void Interface::lowerInventoryDrawer(const bool doCallBacks) {
 
 		if (((PegasusEngine *)g_engine)->isDVD()) {
 			_inventoryOpenSound.stopSound();
+			_inventoryCloseSound.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
 			_inventoryCloseSound.playSound();
 		}
 	}
@@ -512,6 +523,7 @@ void Interface::raiseBiochipDrawer(const bool doCallBacks) {
 
 	if (((PegasusEngine *)g_engine)->isDVD()) {
 		_biochipCloseSound.stopSound();
+		_biochipOpenSound.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
 		_biochipOpenSound.playSound();
 	}
 }
@@ -551,6 +563,7 @@ void Interface::lowerBiochipDrawer(const bool doCallBacks) {
 
 		if (((PegasusEngine *)g_engine)->isDVD()) {
 			_biochipOpenSound.stopSound();
+			_biochipCloseSound.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
 			_biochipCloseSound.playSound();
 		}
 	}
diff --git a/engines/pegasus/items/biochips/arthurchip.cpp b/engines/pegasus/items/biochips/arthurchip.cpp
new file mode 100644
index 0000000000..1ce093673e
--- /dev/null
+++ b/engines/pegasus/items/biochips/arthurchip.cpp
@@ -0,0 +1,275 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
+
+namespace Common {
+DECLARE_SINGLETON(Pegasus::ArthurManager);
+}
+
+namespace Pegasus {
+
+static const char *kArthurWisdomMovies[] = {
+	"Images/AI/Globals/XGLOBA01",
+	"Images/AI/Globals/XGLOBA03",
+	"Images/AI/Globals/XGLOBA06",
+	"Images/AI/Globals/XGLOBA07",
+	"Images/AI/Globals/XGLOBA09",
+	"Images/AI/Globals/XGLOBA17",
+	"Images/AI/Globals/XGLOBA18",
+	"Images/AI/Globals/XGLOBA25",
+	"Images/AI/Globals/XGLOBA26",
+	"Images/AI/Globals/XGLOBA27",
+	"Images/AI/Globals/XGLOBA28",
+	"Images/AI/Globals/XGLOBA30",
+	"Images/AI/Globals/XGLOBA34",
+	"Images/AI/Globals/XGLOBA35",
+	"Images/AI/Globals/XGLOBA43",
+	"Images/AI/Globals/XGLOBA50",
+	"Images/AI/Globals/XGLOBA56",
+	"Images/AI/Globals/XGLOBA59",
+	"Images/AI/Globals/XGLOBA63",
+	"Images/AI/Globals/XGLOBB10",
+	"Images/AI/Globals/XGLOBB11",
+	"Images/AI/Globals/XGLOBB12",
+	"Images/AI/Globals/XGLOBB13",
+	"Images/AI/Globals/XGLOBB14",
+	"Images/AI/Globals/XGLOBB15",
+	"Images/AI/Globals/XGLOBB16",
+	"Images/AI/Globals/XGLOBB17",
+	"Images/AI/Globals/XGLOBB18",
+	"Images/AI/Globals/XGLOBB19",
+	"Images/AI/Globals/XGLOBB20",
+	"Images/AI/Globals/XGLOBB21",
+	"Images/AI/Globals/XGLOBB22",
+	"Images/AI/Globals/XGLOBB23",
+	"Images/AI/Globals/XGLOBB24",
+	"Images/AI/Globals/XGLOBB25",
+	"Images/AI/Globals/XGLOBB26",
+	"Images/AI/Globals/XGLOBB27",
+	"Images/AI/Globals/XGLOBB28",
+	"Images/AI/Globals/XGLOBB29",
+	"Images/AI/Globals/XGLOBB30",
+	"Images/AI/Globals/XGLOBB31",
+	"Images/AI/Globals/XGLOBB32",
+	"Images/AI/Globals/XGLOBB33",
+	"Images/AI/Globals/XGLOBB34",
+	"Images/AI/Globals/XGLOBB35",
+	"Images/AI/Globals/XGLOBB36",
+	"Images/AI/Globals/XGLOBB37",
+	"Images/AI/Globals/XGLOBB38",
+	"Images/AI/Globals/XGLOBB39",
+	"Images/AI/Globals/XGLOBB43",
+	"Images/AI/Globals/XGLOBB44",
+	"Images/AI/Globals/XGLOBA62"
+};
+
+ArthurChip *g_arthurChip = 0;
+
+ArthurChip::ArthurChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) :
+		BiochipItem(id, neighborhood, room, direction), _arthurWisdomHotspot(kArthurWisdomSpotID),
+		_chattyArthurHotspot(kChattyArthurSpotID), _chattyAIHotspot(kChattyAISpotID),
+		_arthurHeadHotspot(kArthurHeadSpotID) {
+	_lastArthurMovie = "";
+
+	_arthurWisdomHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 20, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 20 + 161, kAIMiddleAreaTop + 27 + 30));
+	_arthurWisdomHotspot.setHotspotFlags(kArthurBiochipSpotFlag);
+	g_allHotspots.push_back(&_arthurWisdomHotspot);
+
+	_chattyArthurHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 100, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 100 + 20, kAIMiddleAreaTop + 67 + 20));
+	_chattyArthurHotspot.setHotspotFlags(kArthurBiochipSpotFlag);
+	g_allHotspots.push_back(&_chattyArthurHotspot);
+
+	_chattyAIHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 130, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 130 + 20, kAIMiddleAreaTop + 67 + 20));
+	_chattyAIHotspot.setHotspotFlags(kArthurBiochipSpotFlag);
+	g_allHotspots.push_back(&_chattyAIHotspot);
+
+	_arthurHeadHotspot.setArea(Common::Rect(kAIRightAreaLeft, kAIRightAreaTop, kAIRightAreaLeft + kAIRightAreaWidth, kAIRightAreaTop + kAIRightAreaHeight));
+	_arthurHeadHotspot.setHotspotFlags(kArthurBiochipSpotFlag);
+	g_allHotspots.push_back(&_arthurHeadHotspot);
+
+	setItemState(kArthur000);
+
+	g_arthurChip = this;
+}
+
+ArthurChip::~ArthurChip() {
+	g_arthurChip = NULL;
+
+	g_allHotspots.removeOneHotspot(kArthurWisdomSpotID);
+	g_allHotspots.removeOneHotspot(kChattyArthurSpotID);
+	g_allHotspots.removeOneHotspot(kChattyAISpotID);
+	g_allHotspots.removeOneHotspot(kArthurHeadSpotID);
+}
+
+void ArthurChip::select() {
+	BiochipItem::select();
+	setUpArthurChip();
+}
+
+void ArthurChip::setUpArthurChip() {
+	PegasusEngine *vm = (PegasusEngine *)g_engine;
+	ItemState state = getItemState();
+
+	if (vm->isChattyArthur()) {
+		if (g_AIArea && vm->isChattyAI()) {
+			if (state != kArthur002)
+				setItemState(kArthur000);
+		} else if (state != kArthur102) {
+			setItemState(kArthur100);
+		}
+	} else {
+		if (g_AIArea && vm->isChattyAI()) {
+			if (state != kArthur012)
+				setItemState(kArthur010);
+		} else if (state != kArthur112) {
+			setItemState(kArthur110);
+		}
+	}
+}
+
+void ArthurChip::activateArthurHotspots() {
+	_arthurWisdomHotspot.setActive();
+	_chattyArthurHotspot.setActive();
+	_chattyAIHotspot.setActive();
+	_arthurHeadHotspot.setActive();
+}
+
+void ArthurChip::clickInArthurHotspot(HotSpotID id) {
+	PegasusEngine *vm = (PegasusEngine *)g_engine;
+	ItemState state, newState;
+
+	if (id == kArthurHeadSpotID) {
+		if (_lastArthurMovie != "")
+			playArthurMovie(_lastArthurMovie);
+		return;
+	}
+
+	newState = state = getItemState();
+	switch (state) {
+	case kArthur000:
+		switch (id) {
+		case kArthurWisdomSpotID:
+			newState = kArthur002;
+			break;
+		case kChattyArthurSpotID:
+			newState = kArthur010;
+			break;
+		case kChattyAISpotID:
+			newState = kArthur100;
+			break;
+		}
+		break;
+	case kArthur010:
+		switch (id) {
+		case kArthurWisdomSpotID:
+			newState = kArthur012;
+			break;
+		case kChattyArthurSpotID:
+			newState = kArthur000;
+			break;
+		case kChattyAISpotID:
+			newState = kArthur110;
+			break;
+		}
+		break;
+	case kArthur100:
+		switch (id) {
+		case kArthurWisdomSpotID:
+			newState = kArthur102;
+			break;
+		case kChattyArthurSpotID:
+			newState = kArthur110;
+			break;
+		case kChattyAISpotID:
+			newState = kArthur010;
+			break;
+		}
+		break;
+	case kArthur110:
+		switch (id) {
+		case kArthurWisdomSpotID:
+			newState = kArthur112;
+			break;
+		case kChattyArthurSpotID:
+			newState = kArthur100;
+			break;
+		case kChattyAISpotID:
+			newState = kArthur010;
+			break;
+		}
+		break;
+	}
+	setItemState(newState);
+	switch (id) {
+	case kArthurWisdomSpotID:
+		playArthurMovie(kArthurWisdomMovies[((PegasusEngine *)g_engine)->getRandomNumber((
+						sizeof(kArthurWisdomMovies) / sizeof(const char *)) - 1)]);
+		break;
+	case kChattyArthurSpotID:
+		vm->setChattyArthur(!vm->isChattyArthur());
+		break;
+	case kChattyAISpotID:
+		vm->setChattyAI(!vm->isChattyAI());
+		break;
+	}
+
+	setItemState(state);
+}
+
+void ArthurChip::playArthurMovie(const Common::String &movieName) {
+	if (g_AIArea) {
+		g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kHintInterruption);
+		if (movieName != "Images/AI/Globals/XGLOB00" &&
+			movieName != "Images/AI/Globals/XGLOB01" &&
+			movieName != "Images/AI/Globals/XGLOBAA0" &&
+			movieName != "Images/AI/Globals/XGLOBAA1" &&
+			movieName != "Images/AI/Globals/XGLOBAA2")
+			_lastArthurMovie = movieName;
+	}
+}
+
+bool ArthurChip::playArthurMovieForEvent(const Common::String &movieName, ArthurEvent event) {
+	PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+	if (vm->isDVD() && vm->playerHasItemID(kArthurBiochip) &&
+		vm->isChattyArthur() && !Arthur._arthurFlags.getFlag(event)) {
+		Arthur._arthurFlags.setFlag(event, true);
+		playArthurMovie(movieName);
+		return true;
+	} else {
+		return false;
+	}
+}
+
+void ArthurManager::resetArthurState() {
+	bool savedGameFlag = _arthurFlags.getFlag(kArthurLoadedSavedGame);
+	_arthurFlags.clearAllFlags();
+	_arthurFlags.setFlag(kArthurLoadedSavedGame, savedGameFlag);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/items/biochips/arthurchip.h b/engines/pegasus/items/biochips/arthurchip.h
new file mode 100644
index 0000000000..a68c29f13d
--- /dev/null
+++ b/engines/pegasus/items/biochips/arthurchip.h
@@ -0,0 +1,222 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef PEGASUS_ITEMS_BIOCHIPS_ARTHURCHIP_H
+#define PEGASUS_ITEMS_BIOCHIPS_ARTHURCHIP_H
+
+#include "pegasus/hotspot.h"
+#include "pegasus/util.h"
+#include "pegasus/items/biochips/biochipitem.h"
+
+namespace Pegasus {
+
+enum ArthurEvent {
+	kArthurLoadedSavedGame,
+	kArthurAttemptedLockedDoor,
+	kArthurAttemptedLockedDoorAgain,
+	kArthurDestroyedInventoryItem,
+	kArthurDisabledAI,
+
+	kArthurCaldoriaFinishedJoyride,
+	kArthurCaldoriaSelectedStickyBuns,
+	kArthurCaldoriaCreatedCornbread,
+	kArthurCaldoriaDrankOJ,
+	kArthurCaldoriaZoomedToLaundry,
+	kArthurCaldoriaReachedToilet,
+	kArthurCaldoriaReadPaper,
+	kArthurCaldoriaChoseAgencyHairStyle,
+	kArthurCaldoriaSawVacantApartment,
+	kArthurCaldoriaLookThroughTelescope,
+	kArthurCaldoriaRoofDoor,
+	kArthurCaldoriaUsedCardBomb,
+	kArthurCaldoriaBlownDoor,
+	kArthurCaldoriaSawVoiceAnalysis,
+	kArthurCaldoriaStunningSinclair,
+	kArthurCaldoriaSeeRoofBomb,
+	kArthurCaldoriaDisarmedNuke,
+
+	kArthurTSAEnteredCave,
+	kArthurTSASawAgent3,
+	kArthurTSASawBust,
+	kArthurTSAReachedJunction,
+	kArthurTSAClickedRobot1,
+	kArthurTSAClickedRobot2,
+	kArthurTSAOpenTBPMonitor,
+	kArthurTSASawJourneymanKey,
+	kArthurTSASawBiochips,
+	kArthurTSAUsedPegasus,
+	kArthurTSAConfinedByBaldwin,
+	kArthurTSARedirectedRobots,
+	kArthurTSAUsedTurbolift,
+	kArthurTSASawFirstOpMemMovie,
+	kArthurTSAInPegasusNoVideo,
+	kArthurTSASawBaldwinSayGo,
+	kArthurTSALeaving,
+
+	kArthurGoToPrehistoric,
+
+	kArthurPrehistoricReachedJunction,
+	kArthurPrehistoricSawBreaker,
+	kArthurPrehistoricBreakerThrown,
+	kArthurPrehistoricAtCliffEdge,
+	kArthurPrehistoricSawEggs,
+	kArthurPrehistoricZoomedToVault,
+	kArthurPrehistoricAttemptedBridge,
+	kArthurPrehistoricExtendedBridge,
+	kArthurPrehistoricCrossedBridge,
+	kArthurPrehistoricUnlockedVault,
+
+	kArthurMarsReadyForKiosk,
+	kArthurMarsLookedAtGuards,
+	kArthurMarsZoomedToKeyCard,
+	kArthurMarsTurnedOnTransport,
+	kArthurMarsCantFillMask,
+	kArthurMarsSawWelcomeVideos,
+	kArthurMarsRobotThrownPlayerWithMask,
+	kArthurMarsLeftPodNoCrowBar,
+	kArthurMarsLookAtEmptyTracks,
+	kArthurMarsEnteredReactor,
+	kArthurMarsSawLockedPanel,
+	kArthurMarsSawLockedPanelNoNitrogen,
+	kArthurMarsUsedLiquidNitrogen,
+	kArthurMarsFoundCardBomb,
+	kArthurMarsSolvedReactorGame,
+	kArthurMarsDeactivatedCardBomb,
+	kArthurMarsExitedReactorWithCardBomb,
+	kArthurMarsInAirlockNoOxygen,
+	kArthurMarsMazeReachedJunction,
+	kArthurMarsOxygen50Warning,
+	kArthurMarsOxygen25Warning,
+	kArthurMarsOxygen5Warning,
+	kArthurMarsFoundBuckets,
+	kArthurMarsApproachedBuckets,
+	kArthurMarsEnteredGearRoom,
+	kArthurMarsLookAtGears,
+	kArthurMarsExitedGearRoom,
+	kArthurMarsFoundNoShuttlePresent,
+	kArthurMarsEnteredShuttle,
+	kArthurMarsFoundDeadRobot,
+	kArthurMarsRobotHeadOpen,
+
+	kArthurWSCRemovedDart,
+	kArthurWSCPoisonedDuringGame,
+	kArthurWSCFailedMolecule,
+	kArthurWSCDesignedAntidote,
+	kArthurWSCSawAresHologram,
+	kArthurWSCLookAtMorphExperiment,
+	kArthurWSCStartMorphExperiment,
+	kArthurWSCSawMorphExperiment,
+	kArthurWSCLeftLabNoKeyOrCanisters,
+	kArthurWSCAtOppositeDoor,
+	kArthurWSCReadyForMap,
+	kArthurWSCAttemptedLockedDoor,
+	kArthurWSCSawSinclairDoor,
+	kArthurWSCSawSinclairDoorNoKey,
+	kArthurWSCAttemptedSinclairDoorNoKey,
+	kArthurWSCZoomedToSnake,
+	kArthurWSCActivatedComputer,
+	kArthurWSCZoomedToSinclairMessages,
+	kArthurWSCPlayedEasterEggMessage,
+	kArthurWSCGotMachineGun,
+	kArthurWSCSeenNerd,
+	kArthurWSCSawBrokenDoor,
+	kArthurWSCSawBrokenDoorNoCrowBar,
+	kArthurWSCUsedCrowBar,
+	kArthurWSCDidPlasmaDodge,
+	kArthurWSCEnteredAuditorium,
+	kArthurWSCSawSinclairLecture,
+	kArthurWSCEnteredPassage,
+	kArthurWSCInPassage,
+	kArthurWSCExitedPassage,
+	kArthurWSCSawCatwalkDoor,
+	kArthurWSCRobotHeadOpen,
+
+	kArthurNoradAtSecurityMonitor,
+	kArthurNoradSawFillingStation,
+	kArthurNoradSawIntakeWarning,
+	kArthurNoradDidntFillCanisters,
+	kArthurNoradSawUnconsciousOperator,
+	kArthurNoradAttemptedLockedDoor,
+	kArthurNoradAttemptedLockedDoorAgain,
+	kArthurNoradReachedPressureDoor,
+	kArthurNoradSawSubMessage,
+	kArthurNoradSawClawMonitor,
+	kArthurNoradPlayedWithClaw,
+	kArthurNoradEnteredSub,
+	kArthurNoradExitedSub,
+	kArthurNoradApproachedDamagedDoor,
+	kArthurNoradAtRetScanNoBiochip,
+	kArthurNoradStartGlobeGame,
+	kArthurNoradSelectedIncorrectSilo,
+	kArthurNoradFinishedGlobeGame,
+	kArthurNoradThreatenedByRobot,
+	kArthurNoradBeatRobotWithClaw,
+	kArthurNoradRobotHeadOpen,
+
+	kNumArthurFlags
+};
+
+class ArthurChip : public BiochipItem {
+public:
+	ArthurChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant);
+	virtual ~ArthurChip();
+
+	void select();
+
+	void setUpArthurChip();
+	void activateArthurHotspots();
+	void clickInArthurHotspot(HotSpotID);
+	void playArthurMovie(const Common::String &);
+	bool playArthurMovieForEvent(const Common::String &, ArthurEvent event);
+
+protected:
+	Hotspot _arthurWisdomHotspot;
+	Hotspot _chattyArthurHotspot;
+	Hotspot _chattyAIHotspot;
+	Hotspot _arthurHeadHotspot;
+	Common::String _lastArthurMovie;
+};
+
+class ArthurManager : public Common::Singleton<ArthurManager> {
+public:
+	ArthurManager() { resetArthurState(); }
+
+	void resetArthurState();
+
+protected:
+	friend class Common::Singleton<SingletonBaseType>;
+	friend class ArthurChip;
+
+private:
+	FlagsArray<byte, kNumArthurFlags> _arthurFlags;
+};
+
+extern ArthurChip *g_arthurChip;
+
+} // End of namespace Pegasus
+
+#define Arthur (::Pegasus::ArthurManager::instance())
+
+#endif
diff --git a/engines/pegasus/items/biochips/opticalchip.cpp b/engines/pegasus/items/biochips/opticalchip.cpp
index 888270dd8f..ad781f19b9 100644
--- a/engines/pegasus/items/biochips/opticalchip.cpp
+++ b/engines/pegasus/items/biochips/opticalchip.cpp
@@ -127,7 +127,16 @@ void OpticalChip::playOpMemMovie(HotSpotID id) {
 	Common::String movieName;
 	switch (id) {
 	case kAriesSpotID:
-		movieName = "Images/AI/Globals/OMAI";
+		// WORKAROUND: The original CD release played the ares video even
+		// when you destroyed the shuttle. For the DVD release, we have
+		// some new videos that can be played instead to workaround a plot
+		// loophole.
+		if (!((PegasusEngine *)g_engine)->isDVD() || _opticalFlags.getFlag(kOpticalAriesExposed))
+			movieName = "Images/AI/Globals/OMAI";
+		else if (_itemOwnerID == kPlayerID)
+			movieName = "Images/AI/Globals/OMN1";
+		else
+			movieName = "Images/AI/Globals/OMN0";
 		break;
 	case kMercurySpotID:
 		movieName = "Images/AI/Globals/OMMI";
diff --git a/engines/pegasus/items/inventorypicture.cpp b/engines/pegasus/items/inventorypicture.cpp
index fb7af0b59d..ddb96a4ad1 100644
--- a/engines/pegasus/items/inventorypicture.cpp
+++ b/engines/pegasus/items/inventorypicture.cpp
@@ -318,6 +318,10 @@ void InventoryItemsPicture::deactivateInventoryPicture() {
 	}
 }
 
+void InventoryItemsPicture::setCommPicture() {
+	_pictName = "Images/Items/Inventory/Comm Panel";
+}
+
 void InventoryItemsPicture::playEndMessage(DisplayElement *pushElement) {
 	PegasusEngine *vm = (PegasusEngine *)g_engine;
 
@@ -326,6 +330,7 @@ void InventoryItemsPicture::playEndMessage(DisplayElement *pushElement) {
 	_shouldDrawHighlight = false;
 	endMessage.shareSurface(this);
 	endMessage.initFromMovieFile("Images/Caldoria/A56 Congrats");
+	endMessage.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
 	endMessage.moveMovieBoxTo(kFinalMessageLeft - kInventoryPushLeft, kFinalMessageTop - kInventoryPushTop);
 	endMessage.setTriggeredElement(pushElement);
 	endMessage.start();
diff --git a/engines/pegasus/items/inventorypicture.h b/engines/pegasus/items/inventorypicture.h
index c4dc3b298b..815b067ce6 100644
--- a/engines/pegasus/items/inventorypicture.h
+++ b/engines/pegasus/items/inventorypicture.h
@@ -98,6 +98,7 @@ public:
 
 	void disableLooping() { _isLooping = false; }
 
+	void setCommPicture();
 	void playEndMessage(DisplayElement *);
 
 protected:
diff --git a/engines/pegasus/items/item.h b/engines/pegasus/items/item.h
index c8b8c51815..2fd600fec8 100644
--- a/engines/pegasus/items/item.h
+++ b/engines/pegasus/items/item.h
@@ -118,6 +118,7 @@ static const ItemID kArgonPickup = 24;
 // Biochips.
 
 static const ItemID kAIBiochip = 0;
+static const ItemID kArthurBiochip = 1;
 static const ItemID kInterfaceBiochip = 1;
 static const ItemID kMapBiochip = 2;
 static const ItemID kOpticalBiochip = 3;
@@ -248,6 +249,14 @@ static const ItemState kFlashlightOn = 115;
 static const ItemState kNitrogenEmpty = 116;
 static const ItemState kNitrogenFull = 117;
 static const ItemState kFullGlass = 118;
+static const ItemState kArthur000 = 119;
+static const ItemState kArthur002 = 120;
+static const ItemState kArthur010 = 121;
+static const ItemState kArthur012 = 122;
+static const ItemState kArthur100 = 123;
+static const ItemState kArthur102 = 124;
+static const ItemState kArthur110 = 125;
+static const ItemState kArthur112 = 126;
 
 // Extra IDs.
 
diff --git a/engines/pegasus/menu.cpp b/engines/pegasus/menu.cpp
index 5d48bcfb3c..ff308f80dc 100644
--- a/engines/pegasus/menu.cpp
+++ b/engines/pegasus/menu.cpp
@@ -155,7 +155,10 @@ MainMenu::MainMenu() : GameMenu(kMainMenuID), _menuBackground(0), _overviewButto
 		else
 			_menuBackground.initFromPICTFile("Images/Demo/DemoMenu.pict");
 	} else {
-		_menuBackground.initFromPICTFile("Images/Main Menu/MainMenu.mac");
+		if (((PegasusEngine *)g_engine)->isDVD())
+			_menuBackground.initFromPICTFile("Images/Main Menu/MainMenu_hq.mac");
+		else
+			_menuBackground.initFromPICTFile("Images/Main Menu/MainMenu.mac");
 	}
 	_menuBackground.setDisplayOrder(0);
 	_menuBackground.startDisplaying();
@@ -453,9 +456,12 @@ static const CoordType kCreditsMainMenuSelectTop = 408;
 
 static const TimeValue kCoreTeamTime = 0;
 static const TimeValue kSupportTeamTime = 1920;
-static const TimeValue kOriginalTeamTime = 3000;
-static const TimeValue kTalentTime = 4440;
-static const TimeValue kOtherTitlesTime = 4680;
+static const TimeValue kOriginalTeamCDTime = 3000;
+static const TimeValue kOriginalTeamDVDTime = 3240;
+static const TimeValue kTalentCDTime = 4440;
+static const TimeValue kTalentDVDTime = 4680;
+static const TimeValue kOtherTitlesCDTime = 4680;
+static const TimeValue kOtherTitlesDVDTime = 4920;
 
 static const TimeValue kFrameIncrement = 120; // Three frames...
 
@@ -463,12 +469,18 @@ static const TimeValue kFrameIncrement = 120; // Three frames...
 CreditsMenu::CreditsMenu() : GameMenu(kCreditsMenuID), _menuBackground(0), _creditsMovie(0),
 		_mainMenuButton(0), _largeSelect(0), _smallSelect(0) {
 
-	_menuBackground.initFromPICTFile("Images/Credits/CredScrn.pict");
+	if (((PegasusEngine *)g_engine)->isDVD())
+		_menuBackground.initFromPICTFile("Images/Credits/CredScrnScummVM.pict");
+	else
+		_menuBackground.initFromPICTFile("Images/Credits/CredScrn.pict");
 	_menuBackground.setDisplayOrder(0);
 	_menuBackground.startDisplaying();
 	_menuBackground.show();
 
-	_creditsMovie.initFromMovieFile("Images/Credits/Credits.movie");
+	if (((PegasusEngine *)g_engine)->isDVD())
+		_creditsMovie.initFromMovieFile("Images/Credits/Credits_scummVM.movie");
+	else
+		_creditsMovie.initFromMovieFile("Images/Credits/Credits.movie");
 	_creditsMovie.setDisplayOrder(1);
 	_creditsMovie.moveElementTo(kCreditsMovieLeft, kCreditsMovieTop);
 	_creditsMovie.startDisplaying();
@@ -493,6 +505,38 @@ CreditsMenu::CreditsMenu() : GameMenu(kCreditsMenuID), _menuBackground(0), _cred
 	_menuSelection = -1;
 
 	newMenuSelection(kCreditsMenuCoreTeam);
+
+	if (((PegasusEngine *)g_engine)->isDVD()) {
+		_menuLoop.attachFader(&_menuFader);
+		_menuLoop.initFromAIFFFile("Sounds/Credits.aiff");
+		_menuFader.setMasterVolume(((PegasusEngine *)g_engine)->getAmbienceLevel());
+	}
+}
+
+CreditsMenu::~CreditsMenu() {
+	if (_menuLoop.isPlaying())
+		stopCreditsMenuLoop();
+}
+
+void CreditsMenu::startCreditsMenuLoop() {
+	if (((PegasusEngine *)g_engine)->isDVD()) {
+		FaderMoveSpec spec;
+
+		_menuLoop.loopSound();
+		spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255);
+
+		_menuFader.startFader(spec);
+	}
+}
+
+void CreditsMenu::stopCreditsMenuLoop() {
+	if (((PegasusEngine *)g_engine)->isDVD()) {
+		FaderMoveSpec spec;
+
+		spec.makeTwoKnotFaderSpec(30, 0, 255, 30, 0);
+		_menuFader.startFaderSync(spec);
+		_menuLoop.stopSound();
+	}
 }
 
 // Assumes the new selection is never more than one away from the old...
@@ -502,29 +546,33 @@ void CreditsMenu::newMenuSelection(const int newSelection) {
 		case kCreditsMenuCoreTeam:
 			_smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop);
 			_creditsMovie.setTime(kCoreTeamTime);
-			_creditsMovie.redrawMovieWorld();
 			break;
 		case kCreditsMenuSupportTeam:
 			_smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop);
 			_creditsMovie.setTime(kSupportTeamTime);
-			_creditsMovie.redrawMovieWorld();
 			break;
 		case kCreditsMenuOriginalTeam:
 			_smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop);
-			_creditsMovie.setTime(kOriginalTeamTime);
-			_creditsMovie.redrawMovieWorld();
+			if (((PegasusEngine *)g_engine)->isDVD())
+				_creditsMovie.setTime(kOriginalTeamDVDTime);
+			else
+				_creditsMovie.setTime(kOriginalTeamCDTime);
 			break;
 		case kCreditsMenuTalent:
 			_smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop);
-			_creditsMovie.setTime(kTalentTime);
-			_creditsMovie.redrawMovieWorld();
+			if (((PegasusEngine *)g_engine)->isDVD())
+				_creditsMovie.setTime(kTalentDVDTime);
+			else
+				_creditsMovie.setTime(kTalentCDTime);
 			break;
 		case kCreditsMenuOtherTitles:
 			_smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop);
 			_smallSelect.show();
 			_largeSelect.hide();
-			_creditsMovie.setTime(kOtherTitlesTime);
-			_creditsMovie.redrawMovieWorld();
+			if (((PegasusEngine *)g_engine)->isDVD())
+				_creditsMovie.setTime(kOtherTitlesDVDTime);
+			else
+				_creditsMovie.setTime(kOtherTitlesCDTime);
 			break;
 		case kCreditsMenuMainMenu:
 			_smallSelect.hide();
@@ -533,22 +581,26 @@ void CreditsMenu::newMenuSelection(const int newSelection) {
 		default:
 			break;
 		}
+		_creditsMovie.redrawMovieWorld();
 
 		_menuSelection = newSelection;
 	}
 }
 
 void CreditsMenu::newMovieTime(const TimeValue newTime) {
+	// The DVD credits have an extra frame in the support team section
+	bool isDVD = ((PegasusEngine *)g_engine)->isDVD();
+
 	if (newTime < kSupportTeamTime) {
 		_smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop);
 		_menuSelection = kCreditsMenuCoreTeam;
-	} else if (newTime < kOriginalTeamTime) {
+	} else if ((isDVD && newTime < kOriginalTeamDVDTime) || (!isDVD && newTime < kOriginalTeamCDTime)) {
 		_smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop);
 		_menuSelection = kCreditsMenuSupportTeam;
-	} else if (newTime < kTalentTime) {
+	} else if ((isDVD && newTime < kTalentDVDTime) || (!isDVD && newTime < kTalentCDTime)) {
 		_smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop);
 		_menuSelection = kCreditsMenuOriginalTeam;
-	} else if (newTime < kOtherTitlesTime) {
+	} else if ((isDVD && newTime < kOtherTitlesDVDTime) || (!isDVD && newTime < kOtherTitlesCDTime)) {
 		_smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop);
 		_smallSelect.show();
 		_largeSelect.hide();
@@ -675,10 +727,11 @@ DeathMenu::DeathMenu(const DeathReason deathReason) : GameMenu(kDeathMenuID), _d
 			"dAunmade", "dAbombed", "dAshot", "dAassass", "dAnuked",
 			"dTunmade", "dTshot", "dPfall", "dPdino", "dPstuck",
 			"dNchoke", "dNcaught", "dNcaught", "dNsub", "dNrobot1",
-			"dNrobot2", "dMfall", "dMcaught", "dMtracks", "dMrobot",
-			"dMtoast", "dMexplo1", "dMexplo2", "dMchoke1", "dMchoke2",
-			"dMdroid", "dMfall", "dMgears", "dMshutt1", "dMshutt2",
-			"dWpoison", "dWcaught", "dWplasma", "dWshot", "dAfinale"
+			"dNrobot2", "dMfall", "dMcaught", "dMcollision", "dMtracks",
+			"dMrobot", "dMtoast", "dMexplo1", "dMexplo2", "dMchoke1",
+			"dMchoke2", "dMdroid", "dMshaft", "dMgears", "dMshutt1",
+			"dMshutt2", "dWpoison", "dWcaught", "dWplasma", "dWshot",
+			"dAfinale"
 		};
 
 		imageName += fileNames[deathReason - 1];
@@ -746,7 +799,10 @@ DeathMenu::DeathMenu(const DeathReason deathReason) : GameMenu(kDeathMenuID), _d
 		_largeSelect.setDisplayOrder(2);
 		_largeSelect.startDisplaying();
 	} else {
-		_triumphSound.initFromQuickTime("Sounds/Caldoria/Galactic Triumph");
+		if (vm->isDVD()) // Updated file for the DVD version
+			_triumphSound.initFromAIFFFile("Sounds/Caldoria/Galactic Triumph.44K.aiff");
+		else
+			_triumphSound.initFromQuickTime("Sounds/Caldoria/Galactic Triumph");
 		_triumphSound.setVolume(((PegasusEngine *)g_engine)->getAmbienceLevel());
 		_triumphSound.playSound();
 	}
@@ -884,6 +940,7 @@ void DeathMenu::drawAllScores() {
 	case kDeathRobotSubControlRoom:
 	case kDeathWrongShuttleLock:
 	case kDeathArrestedInMars:
+	case kDeathCollidedWithPod:
 	case kDeathRunOverByPod:
 	case kDeathDidntGetOutOfWay:
 	case kDeathReactorBurn:
diff --git a/engines/pegasus/menu.h b/engines/pegasus/menu.h
index 2a062232ac..232835f12a 100644
--- a/engines/pegasus/menu.h
+++ b/engines/pegasus/menu.h
@@ -97,9 +97,11 @@ protected:
 class CreditsMenu : public GameMenu {
 public:
 	CreditsMenu();
-	~CreditsMenu() override {}
+	~CreditsMenu() override;
 
 	void handleInput(const Input &input, const Hotspot *) override;
+	void startCreditsMenuLoop();
+	void stopCreditsMenuLoop();
 
 protected:
 	void newMenuSelection(const int);
@@ -111,6 +113,9 @@ protected:
 	Picture _mainMenuButton;
 	Picture _largeSelect;
 	Picture _smallSelect;
+
+	Sound _menuLoop;
+	SoundFader _menuFader;
 };
 
 class DeathMenu : public GameMenu {
diff --git a/engines/pegasus/metaengine.cpp b/engines/pegasus/metaengine.cpp
index aefc3b74c5..8f0e19243c 100644
--- a/engines/pegasus/metaengine.cpp
+++ b/engines/pegasus/metaengine.cpp
@@ -59,6 +59,10 @@ bool PegasusEngine::isWindows() const {
 	return _gameDescription->desc.platform == Common::kPlatformWindows;
 }
 
+bool PegasusEngine::isLinux() const {
+	return _gameDescription->desc.platform == Common::kPlatformLinux;
+}
+
 } // End of namespace Pegasus
 
 class PegasusMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk
index 1fe1963f32..3e1bf4ab11 100644
--- a/engines/pegasus/module.mk
+++ b/engines/pegasus/module.mk
@@ -1,6 +1,7 @@
 MODULE := engines/pegasus
 
 MODULE_OBJS = \
+	chase.o \
 	compass.o \
 	console.o \
 	cursor.o \
@@ -34,6 +35,7 @@ MODULE_OBJS = \
 	items/itemdragger.o \
 	items/itemlist.o \
 	items/biochips/aichip.o \
+	items/biochips/arthurchip.o \
 	items/biochips/biochipitem.o \
 	items/biochips/mapchip.o \
 	items/biochips/mapimage.o \
@@ -59,6 +61,7 @@ MODULE_OBJS = \
 	neighborhood/caldoria/caldoriabomb.o \
 	neighborhood/caldoria/caldoriamessages.o \
 	neighborhood/caldoria/caldoriamirror.o \
+	neighborhood/mars/canyonchase.o \
 	neighborhood/mars/energybeam.o \
 	neighborhood/mars/gravitoncannon.o \
 	neighborhood/mars/hermite.o \
@@ -72,6 +75,7 @@ MODULE_OBJS = \
 	neighborhood/mars/spacechase3d.o \
 	neighborhood/mars/spacejunk.o \
 	neighborhood/mars/tractorbeam.o \
+	neighborhood/mars/tunnelpod.o \
 	neighborhood/norad/norad.o \
 	neighborhood/norad/noradelevator.o \
 	neighborhood/norad/pressuredoor.o \
@@ -83,6 +87,7 @@ MODULE_OBJS = \
 	neighborhood/norad/alpha/noradalpha.o \
 	neighborhood/norad/alpha/panorama.o \
 	neighborhood/norad/alpha/panoramascroll.o \
+	neighborhood/norad/alpha/subchase.o \
 	neighborhood/norad/delta/globegame.o \
 	neighborhood/norad/delta/noraddelta.o \
 	neighborhood/prehistoric/prehistoric.o \
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp
index 42eca2a635..60a4a2fe08 100644
--- a/engines/pegasus/neighborhood/caldoria/caldoria.cpp
+++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp
@@ -25,6 +25,7 @@
 
 #include "common/system.h"
 #include "video/qt_decoder.h"
+#include "video/theora_decoder.h"
 
 #include "pegasus/cursor.h"
 #include "pegasus/energymonitor.h"
@@ -32,6 +33,7 @@
 #include "pegasus/interface.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/biochipitem.h"
 #include "pegasus/neighborhood/caldoria/caldoria.h"
 #include "pegasus/neighborhood/caldoria/caldoria4dsystem.h"
@@ -50,6 +52,19 @@ enum {
 	kCaldoria55Angle = -45
 };
 
+enum {
+	kCreateCornbread = 1000,
+	kWashingMachineZoomIn = 1001,
+	kWashingMachineLoop = 1002,
+	kWashingMachineZoomOut = 1003
+};
+
+enum {
+	kCaldoriaLaundryZoomInHotSpotID = 10000,
+	kCaldoriaLaundryZoomOutHotSpotID = 10001,
+	kCaldoriaCornbreadHotSpotID = 10002
+};
+
 enum {
 	kSinclairInterruptionTime1 = 2955,
 	kSinclairInterruptionTime2 = 6835,
@@ -63,6 +78,12 @@ enum {
 };
 
 enum {
+	kCaldoriaLaundryIntro1In = 0,
+	kCaldoriaLaundryIntro1Out = 2645,
+
+	kCaldoriaLaundryIntro2In = 2645,
+	kCaldoriaLaundryIntro2Out = 4933,
+
 	kCaldoriaReplicatorIntroIn = 4933,
 	kCaldoriaReplicatorIntroOut = 6557,
 
@@ -99,9 +120,6 @@ enum {
 	kCaldoriaNoOtherFloorIn = 21469,
 	kCaldoriaNoOtherFloorOut = 28013,
 
-	kCaldoria4DInstructionsIn = 28013,
-	kCaldoria4DInstructionsOut = 29730,
-
 	kCaldoriaDrinkOJIn = 33910,
 	kCaldoriaDrinkOJOut = 35846,
 
@@ -169,7 +187,9 @@ void SinclairCallBack::callBack() {
 }
 
 Caldoria::Caldoria(InputHandler* nextHandler, PegasusEngine *owner)
-		: Neighborhood(nextHandler, owner, "Caldoria", kCaldoriaID), _sinclairInterrupt(this) {
+		: Neighborhood(nextHandler, owner, "Caldoria", kCaldoriaID), _extraMovie(kNoDisplayElement),
+		_laundryZoomInSpot(kCaldoriaLaundryZoomInHotSpotID), _laundryZoomOutSpot(kCaldoriaLaundryZoomOutHotSpotID),
+		_cornbreadSpot(kCaldoriaCornbreadHotSpotID), _sinclairInterrupt(this), _lookingAtLaundry(false) {
 	setIsItemTaken(kKeyCard);
 	setIsItemTaken(kOrangeJuiceGlassEmpty);
 	GameState.setTakenItemID(kOrangeJuiceGlassFull, GameState.isTakenItemID(kOrangeJuiceGlassEmpty));
@@ -179,14 +199,35 @@ Caldoria::Caldoria(InputHandler* nextHandler, PegasusEngine *owner)
 
 Caldoria::~Caldoria() {
 	_sinclairInterrupt.releaseCallBack();
+	if (_vm->isDVD()) {
+		_vm->getAllHotspots().remove(&_laundryZoomInSpot);
+		_vm->getAllHotspots().remove(&_laundryZoomOutSpot);
+		_vm->getAllHotspots().remove(&_cornbreadSpot);
+	}
 }
 
 void Caldoria::init() {
 	Neighborhood::init();
 
+	if (_vm->isDVD()) {
+		_laundryZoomInSpot.setArea(Common::Rect(384, 120, 576, 320));
+		_laundryZoomInSpot.setHotspotFlags(kNeighborhoodSpotFlag | kZoomInSpotFlag);
+		_vm->getAllHotspots().push_back(&_laundryZoomInSpot);
+
+		_laundryZoomOutSpot.setArea(Common::Rect(64, 64, 576, 320));
+		_laundryZoomOutSpot.setHotspotFlags(kNeighborhoodSpotFlag | kZoomOutSpotFlag);
+		_vm->getAllHotspots().push_back(&_laundryZoomOutSpot);
+
+		_cornbreadSpot.setArea(Common::Rect(270, 233, 381, 298));
+		_cornbreadSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+		_vm->getAllHotspots().push_back(&_cornbreadSpot);
+	}
+
 	// We need this notification flag as well.
 	_neighborhoodNotification.notifyMe(this, kSinclairLoopDoneFlag, kSinclairLoopDoneFlag);
 
+	_extraMovieCallBack.setNotification(&_neighborhoodNotification);
+
 	_sinclairInterrupt.initCallBack(&_navMovie, kCallBackAtTime);
 
 	forceStridingStop(kCaldoria55, kSouth, kAltCaldoriaSinclairDown);
@@ -197,29 +238,66 @@ void Caldoria::start() {
 	g_energyMonitor->stopEnergyDraining();
 
 	if (!GameState.getCaldoriaSeenPullback()) {
+		Input input;
+		Common::String wakeModeMoviePath;
+		InputDevice.getInput(input, kPullbackInterruptFilter);
+		if (_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input)) {
+			wakeModeMoviePath = "Images/Caldoria/A00WM";
+		} else {
+			wakeModeMoviePath = "Images/Caldoria/A00WN";
+		}
+
 		_vm->_gfx->doFadeOutSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond);
 
-		g_system->delayMillis(2 * 1000);
+		Video::VideoDecoder *pullbackMovie = 0;
+		uint16 pullbackX, pullbackY;
 
-		Video::VideoDecoder *pullbackMovie = new Video::QuickTimeDecoder();
+#ifdef USE_THEORADEC
+		if (_vm->isDVD()) {
+			// Updated larger version
+			pullbackMovie = new Video::TheoraDecoder();
 
-		if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie"))
-			error("Could not load pullback movie");
+			if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.ogg")) {
+				delete pullbackMovie;
+			pullbackMovie = 0;
+			}
+		}
+#endif
+
+		if (!pullbackMovie) {
+			pullbackMovie = new Video::QuickTimeDecoder();
+
+			if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie"))
+				error("Could not load Pullback.movie");
+		}
 
 		pullbackMovie->setVolume(MIN<uint>(_vm->getSoundFXLevel(), 0xFF));
 
 		// Draw the first frame so we can fade to it
-		const Graphics::Surface *frame = pullbackMovie->decodeNextFrame();
-		assert(frame);
-		assert(frame->format == g_system->getScreenFormat());
-		g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, 64, 112, frame->w, frame->h);
-		_vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond);
+		const Graphics::Surface *frame = 0;
+
+		if (_vm->isDVD()) {
+			uint16 newHeight = (uint16)((640.0f / (float)pullbackMovie->getWidth()) * (float)pullbackMovie->getHeight());
+			pullbackX = 0;
+			pullbackY = (480 - newHeight) / 2;
+
+			_vm->_gfx->enableUpdates();
+		} else {
+			pullbackX = 80;
+			pullbackY = 112;
+
+			// Draw the first frame so we can fade to it
+			frame = pullbackMovie->decodeNextFrame();
+			assert(frame);
+			assert(frame->format == g_system->getScreenFormat());
+			g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, pullbackX, pullbackY, frame->w, frame->h);
+			_vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond);
+		}
 
 		bool saveAllowed = _vm->swapSaveAllowed(false);
 		bool openAllowed = _vm->swapLoadAllowed(false);
 
 		bool skipped = false;
-		Input input;
 
 		pullbackMovie->start();
 
@@ -228,13 +306,13 @@ void Caldoria::start() {
 				frame = pullbackMovie->decodeNextFrame();
 
 				if (frame) {
-					g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, 64, 112, frame->w, frame->h);
+					g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, pullbackX, pullbackY, frame->w, frame->h);
 					g_system->updateScreen();
 				}
 			}
 
 			InputDevice.getInput(input, kPullbackInterruptFilter);
-			if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested()) {
+			if ((input.anyInput() || _vm->saveRequested() || _vm->loadRequested()) && !GameState.getEasterEgg()) {
 				skipped = true;
 				break;
 			}
@@ -253,14 +331,58 @@ void Caldoria::start() {
 		ExtraTable::Entry entry;
 
 		if (!skipped) {
-			_vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false);
-			g_system->delayMillis(3 * 1000 / 2);
-			getExtraEntry(kCaldoria00WakeUp1, entry);
-			_navMovie.setTime(entry.movieStart);
-			_navMovie.redrawMovieWorld();
-			_navMovie.show();
-			_vm->refreshDisplay();
-			_vm->_gfx->doFadeInSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false);
+			if (_vm->isDVD()) {
+				Video::VideoDecoder* wakeModeMovie = 0;
+#ifdef USE_THEORADEC
+				wakeModeMovie = new Video::TheoraDecoder();
+				if (!wakeModeMovie->loadFile(wakeModeMoviePath + ".ogg")) {
+					delete wakeModeMovie;
+					wakeModeMovie = 0;
+				}
+#endif
+				if (!wakeModeMovie) {
+					wakeModeMovie = new Video::QuickTimeDecoder();
+					if (!wakeModeMovie->loadFile(wakeModeMoviePath + ".movie"))
+						error("Could not load Jonny Ego movie");
+				}
+
+				wakeModeMovie->setVolume(MIN<uint>(_vm->getSoundFXLevel(), 0xFF));
+
+				wakeModeMovie->start();
+
+				while (!_vm->shouldQuit() && !wakeModeMovie->endOfVideo()) {
+					if (wakeModeMovie->needsUpdate()) {
+						frame = wakeModeMovie->decodeNextFrame();
+
+						if (frame) {
+							g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
+							g_system->updateScreen();
+						}
+					}
+
+					InputDevice.getInput(input, kPullbackInterruptFilter);
+					if ((input.anyInput() || _vm->saveRequested() || _vm->loadRequested()) && !GameState.getEasterEgg()) {
+						skipped = true;
+						break;
+					}
+
+					g_system->delayMillis(10);
+				}
+
+				delete wakeModeMovie;
+
+				if (_vm->shouldQuit())
+					return;
+			} else {
+				_vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false);
+				g_system->delayMillis(3 * 1000 / 2);
+				getExtraEntry(kCaldoria00WakeUp1, entry);
+				_navMovie.setTime(entry.movieStart);
+				_navMovie.redrawMovieWorld();
+				_navMovie.show();
+				_vm->refreshDisplay();
+				_vm->_gfx->doFadeInSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false);
+			}
 		} else {
 			getExtraEntry(kCaldoria00WakeUp1, entry);
 			_navMovie.setTime(entry.movieStart);
@@ -274,6 +396,15 @@ void Caldoria::start() {
 	Neighborhood::start();
 }
 
+void Caldoria::throwAwayInterface() {
+	Neighborhood::throwAwayInterface();
+	if (_vm->isDVD()) {
+		_vm->getAllHotspots().remove(&_laundryZoomInSpot);
+		_vm->getAllHotspots().remove(&_laundryZoomOutSpot);
+		_vm->getAllHotspots().remove(&_cornbreadSpot);
+	}
+}
+
 void Caldoria::flushGameState() {
 	GameState.setCaldoriaFuseTimeLimit(_utilityFuse.getTimeRemaining());
 }
@@ -714,18 +845,34 @@ void Caldoria::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec
 void Caldoria::loadAmbientLoops() {
 	RoomID room = GameState.getCurrentRoom();
 
-	if (room == kCaldoria00 && GameState.getCaldoriaWokenUp())
-		loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4);
-	else if (room >= kCaldoria01 && room <= kCaldoria14)
-		loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4);
-	else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45)
-		loadLoopSound1("Sounds/Caldoria/Elevator Loop.AIFF", 0x100 / 5);
-	else if (room == kCaldoria44)
-		loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.AIFF");
-	else if (room >= kCaldoria15 && room <= kCaldoria48)
-		loadLoopSound1("Sounds/Caldoria/Industrial Nuage.aiff", 2 * 0x100 / 3);
-	else if (room >= kCaldoria49 && room <= kCaldoria56)
-		loadLoopSound1("Sounds/Caldoria/A50NLB00.22K.AIFF", 0x100 / 4);
+	if (_vm->isDVD()) {
+		// Updated sounds in the DVD version
+		if (room == kCaldoria00 && GameState.getCaldoriaWokenUp())
+			loadLoopSound1("Sounds/Caldoria/Apartment Music.32K.aiff", 0x100 / 4);
+		else if (room >= kCaldoria01 && room <= kCaldoria14)
+			loadLoopSound1("Sounds/Caldoria/Apartment Music.32K.aiff", 0x100 / 4);
+		else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45)
+			loadLoopSound1("Sounds/Caldoria/Elevator Loop.32K.aiff", 0x100 / 5);
+		else if (room == kCaldoria44)
+			loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.44K.aiff");
+		else if (room >= kCaldoria15 && room <= kCaldoria48)
+			loadLoopSound1("Sounds/Caldoria/Industrial Nuage.44K.aiff", 2 * 0x100 / 3);
+		else if (room >= kCaldoria49 && room <= kCaldoria56)
+			loadLoopSound1("Sounds/Caldoria/A50NLB00.32K.AIFF", 0x100 / 4);
+	} else {
+		if (room == kCaldoria00 && GameState.getCaldoriaWokenUp())
+			loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4);
+		else if (room >= kCaldoria01 && room <= kCaldoria14)
+			loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4);
+		else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45)
+			loadLoopSound1("Sounds/Caldoria/Elevator Loop.AIFF", 0x100 / 5);
+		else if (room == kCaldoria44)
+			loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.AIFF");
+		else if (room >= kCaldoria15 && room <= kCaldoria48)
+			loadLoopSound1("Sounds/Caldoria/Industrial Nuage.aiff", 2 * 0x100 / 3);
+		else if (room >= kCaldoria49 && room <= kCaldoria56)
+			loadLoopSound1("Sounds/Caldoria/A50NLB00.22K.AIFF", 0x100 / 4);
+	}
 }
 
 void Caldoria::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
@@ -748,8 +895,15 @@ void Caldoria::checkContinuePoint(const RoomID room, const DirectionConstant dir
 
 void Caldoria::spotCompleted() {
 	Neighborhood::spotCompleted();
-	if (GameState.getCurrentRoom() == kCaldoriaBinoculars)
+	switch (GameState.getCurrentRoom()) {
+	case kCaldoriaBinoculars:
 		startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput);
+		break;
+	case kCaldoriaToilet:
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA47", kArthurCaldoriaReadPaper);
+		break;
+	}
 }
 
 void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) {
@@ -773,6 +927,7 @@ void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) {
 
 	Neighborhood::arriveAt(room, direction);
 	Input dummy;
+	Sound flushSound;
 
 	switch (room) {
 	case kCaldoria00:
@@ -789,6 +944,27 @@ void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) {
 	case kCaldoria09:
 		_lastExtra = 0xffffffff;
 		break;
+	case kCaldoria10:
+		if (_vm->isDVD() && direction == kWest) {
+			_vm->_cursor->hide();
+			flushSound.initFromAIFFFile("Sounds/Caldoria/Apartment Toilet.32K.aiff");
+			flushSound.setVolume(_vm->getSoundFXLevel());
+			flushSound.playSound();
+			while (flushSound.isPlaying() && !_vm->shouldQuit()) {
+				InputDevice.getInput(dummy, kFilterNoInput);
+
+				_vm->checkCallBacks();
+				_vm->refreshDisplay();
+				_vm->_system->delayMillis(10);
+			}
+			if (_vm->shouldQuit())
+				return;
+			_vm->_cursor->hideUntilMoved();
+		} else if (direction == kEast) {
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA42", kArthurCaldoriaReachedToilet);
+		}
+		break;
 	case kCaldoriaToilet:
 		GameState.setScoringReadPaper(true);
 		break;
@@ -844,6 +1020,10 @@ void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) {
 	case kCaldoria49:
 		arriveAtCaldoria49();
 		break;
+	case kCaldoria48:
+		if (direction == kNorth && !GameState.getCaldoriaDoorBombed() && GameState.isTakenItemID(kCardBomb) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA12", kArthurCaldoriaRoofDoor);
+		break;
 	case kCaldoria53:
 		if (direction == kEast && !GameState.getCaldoriaSinclairShot())
 			zoomToSinclair();
@@ -873,30 +1053,77 @@ void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) {
 void Caldoria::doAIRecalibration() {
 	GameState.setCaldoriaDidRecalibration(true);
 
-	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB1", true, kRecalibrationInterruptFilter))
-		return;
+	Input input;
+	InputDevice.getInput(input, kPullbackInterruptFilter);
+	if (_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input)) {
+		Video::VideoDecoder *video = 0;
 
-	g_interface->calibrateEnergyBar();
-	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB4", true, kRecalibrationInterruptFilter))
-		return;
+		_vm->_cursor->hide();
+
+#ifdef USE_THEORADEC
+		video = new Video::TheoraDecoder();
+		if (!video->loadFile("Images/Caldoria/A00EA.ogg")) {
+			delete video;
+			video = 0;
+		}
+#endif
+
+		if (!video) {
+			video = new Video::QuickTimeDecoder();
+			if (!video->loadFile("Images/Caldoria/A00EA.movie"))
+				error("Could not load Month-O-Matic video");
+		}
+
+		video->setVolume(MIN<uint>(_vm->getSoundFXLevel(), 0xFF));
+		video->start();
+
+		while (!_vm->shouldQuit() && !video->endOfVideo()) {
+			if (video->needsUpdate()) {
+				const Graphics::Surface *frame = video->decodeNextFrame();
+
+				if (frame) {
+					g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, kNavAreaLeft, kNavAreaTop, frame->w, frame->h);
+					g_system->updateScreen();
+				}
+			}
+
+			InputDevice.pumpEvents();
+
+			g_system->delayMillis(10);
+		}
+
+		delete video;
+
+		if (_vm->shouldQuit())
+			return;
+
+		arriveAt(kCaldoria01, kEast);
+	} else if (_vm->isChattyAI()) {
+		if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB1", true, kRecalibrationInterruptFilter))
+			return;
+
+		g_interface->calibrateEnergyBar();
+		if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB4", true, kRecalibrationInterruptFilter))
+			return;
+
+		g_interface->raiseInventoryDrawerSync();
+		if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB6", true, kRecalibrationInterruptFilter)) {
+			g_interface->lowerInventoryDrawerSync();
+			return;
+		}
 
-	g_interface->raiseInventoryDrawerSync();
-	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB6", true, kRecalibrationInterruptFilter)) {
 		g_interface->lowerInventoryDrawerSync();
-		return;
-	}
+		g_interface->raiseBiochipDrawerSync();
 
-	g_interface->lowerInventoryDrawerSync();
-	g_interface->raiseBiochipDrawerSync();
+		if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB5", true, kRecalibrationInterruptFilter)) {
+			g_interface->lowerBiochipDrawerSync();
+			return;
+		}
 
-	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB5", true, kRecalibrationInterruptFilter)) {
 		g_interface->lowerBiochipDrawerSync();
-		return;
-	}
 
-	g_interface->lowerBiochipDrawerSync();
-
-	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB8", false, kRecalibrationInterruptFilter);
+		g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB8", false, kRecalibrationInterruptFilter);
+	}
 }
 
 void Caldoria::arriveAtCaldoria00() {
@@ -1021,12 +1248,26 @@ void Caldoria::setUpRoofTop() {
 	}
 }
 
+void Caldoria::leftButton(const Input &input) {
+	if (!(GameState.getCurrentRoomAndView() == MakeRoomView(kCaldoria11, kWest) && _lookingAtLaundry))
+		Neighborhood::leftButton(input);
+}
+
+void Caldoria::rightButton(const Input &input) {
+	if (!(GameState.getCurrentRoomAndView() == MakeRoomView(kCaldoria11, kWest) && _lookingAtLaundry))
+		Neighborhood::rightButton(input);
+}
+
 void Caldoria::downButton(const Input &input) {
 	switch (GameState.getCurrentRoomAndView()) {
 	case MakeRoomView(kCaldoria01, kEast):
 		GameState.setCaldoriaWokenUp(true);
 		startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput);
 		break;
+	case MakeRoomView(kCaldoria11, kWest):
+		if (_lookingAtLaundry)
+			startExtraSequence(kWashingMachineZoomOut, kExtraCompletedFlag, kFilterNoInput);
+		break;
 	default:
 		Neighborhood::downButton(input);
 		break;
@@ -1062,6 +1303,10 @@ void Caldoria::turnTo(const DirectionConstant direction) {
 	case kCaldoria09:
 		_lastExtra = 0xffffffff;
 		break;
+	case kCaldoria10:
+		if (direction == kEast && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA42", kArthurCaldoriaReachedToilet);
+		break;
 	case kCaldoria11:
 		if (direction == kEast && !GameState.getCaldoriaSeenMessages())
 			loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop);
@@ -1087,8 +1332,11 @@ void Caldoria::turnTo(const DirectionConstant direction) {
 			closeCroppedMovie();
 		break;
 	case kCaldoria48:
-		if (direction == kNorth && !GameState.getCaldoriaDoorBombed())
+		if (direction == kNorth && !GameState.getCaldoriaDoorBombed()) {
 			setCurrentActivation(kActivateRoofSlotEmpty);
+			if (GameState.isTakenItemID(kCardBomb) && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA12", kArthurCaldoriaRoofDoor);
+		}
 		break;
 	case kCaldoria53:
 		if (GameState.getCurrentDirection() == kEast && !GameState.getCaldoriaSinclairShot())
@@ -1123,7 +1371,10 @@ void Caldoria::zoomTo(const Hotspot *zoomOutSpot) {
 	// that doesn't involve the ClickInHotSpot function.
 	_zoomOutSpot = zoomOutSpot;
 
-	if (zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) {
+	if (_vm->isDVD() && zoomOutSpot->getObjectID() == kCaldoriaReplicatorOutSpotID) {
+		GameState.setEasterEgg(false);
+		Neighborhood::zoomTo(zoomOutSpot);
+	} else if (zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) {
 		if (_privateFlags.getFlag(kCaloriaPrivateLeftDrawerOpenFlag)) {
 			_privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false);
 			startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput);
@@ -1160,6 +1411,100 @@ void Caldoria::zoomToSinclair() {
 	startExtraSequence(kCa53EastZoomToSinclair, kExtraCompletedFlag, kFilterAllInput);
 }
 
+void Caldoria::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
+	short left = kNavAreaLeft, top = kNavAreaTop;
+	DisplayOrder displayOrder = kNavMovieOrder + 1;
+	TimeValue segmentStart = 0, segmentStop = 0;
+	bool loopSequence = false;
+	Common::Rect pushBounds;
+	NotificationFlags extraFlags;
+
+	switch (extraID) {
+	case kCreateCornbread:
+	case kWashingMachineZoomIn:
+	case kWashingMachineLoop:
+	case kWashingMachineZoomOut:
+		_turnPush.getBounds(pushBounds);
+
+		switch (extraID) {
+		case kCreateCornbread:
+			_extraMovie.initFromMovieFile("Images/Caldoria/A12RC.movie");
+			left = pushBounds.left;
+			top = pushBounds.top;
+			displayOrder = kNavMovieOrder + 1;
+			segmentStart = 0;
+			segmentStop = _extraMovie.getDuration();
+			loopSequence = false;
+			break;
+		case kWashingMachineZoomIn:
+			_extraMovie.initFromMovieFile("Images/Caldoria/A11WAS.movie");
+			left = pushBounds.left;
+			top = pushBounds.top;
+			displayOrder = kNavMovieOrder + 1;
+			segmentStart = 0;
+			segmentStop = 5480;
+			loopSequence = false;
+			break;
+		case kWashingMachineLoop:
+			// The washing machine movie will already be loaded after zooming in
+			left = pushBounds.left;
+			top = pushBounds.top;
+			displayOrder = kNavMovieOrder + 1;
+			segmentStart = 5480;
+			segmentStop = 9880;
+			loopSequence = true;
+			break;
+		case kWashingMachineZoomOut:
+			// The washing machine movie will still be loaded after looping
+			left = pushBounds.left;
+			top = pushBounds.top;
+			displayOrder = kNavMovieOrder + 1;
+			segmentStart = 9880;
+			segmentStop = 11200;
+			loopSequence = false;
+			break;
+		default:
+			break;
+		}
+
+		_lastExtra = extraID;
+		_turnPush.hide();
+
+		if (!loopSequence && g_AIArea)
+			g_AIArea->lockAIOut();
+
+		extraFlags = flags;
+		_interruptionFilter = interruptionFilter;
+		// Stop the nav movie before doing anything else
+		_navMovie.stop();
+		_navMovie.stopDisplaying();
+
+		_extraMovie.setVolume(_vm->getSoundFXLevel());
+		_extraMovie.moveElementTo(left, top);
+		_extraMovie.setDisplayOrder(displayOrder);
+		_extraMovie.startDisplaying();
+		_extraMovie.show();
+		_extraMovie.setFlags(0);
+		_extraMovie.setSegment(segmentStart, segmentStop);
+		_extraMovie.setTime(segmentStart);
+		if (loopSequence)
+			_extraMovie.setFlags(kLoopTimeBase);
+		else
+			extraFlags |= kNeighborhoodMovieCompletedFlag;
+		_extraMovieCallBack.cancelCallBack();
+		_extraMovieCallBack.initCallBack(&_extraMovie, kCallBackAtExtremes);
+		if (extraFlags != 0) {
+			_extraMovieCallBack.setCallBackFlag(extraFlags);
+			_extraMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+		}
+		_extraMovie.start();
+		break;
+	default:
+		Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
+		break;
+	}
+}
+
 void Caldoria::receiveNotification(Notification *notification, const NotificationFlags flags) {
 	Neighborhood::receiveNotification(notification, flags);
 
@@ -1196,8 +1541,31 @@ void Caldoria::receiveNotification(Notification *notification, const Notificatio
 			}
 			break;
 		case kCreateOrangeJuice:
+			GameState.setCaldoriaMadeOJ(true);
 			setCurrentActivation(kActivateOJOnThePad);
-			requestSpotSound(kCaldoriaReplicatorOJChoiceIn, kCaldoriaReplicatorOJChoiceOut, kFilterNoInput, 0);
+			GameState.setEasterEgg(false);
+			break;
+		case kCreateCornbread:
+			_extraMovie.moveElementTo(0, 0);
+			_vm->_gfx->setCurSurface(_navMovie.getSurface());
+			_extraMovie.copyToCurrentPort();
+			_vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea());
+			_extraMovie.stopDisplaying();
+			_extraMovie.releaseMovie();
+			_navMovie.startDisplaying();
+			GameState.setEasterEgg(true);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB39", kArthurCaldoriaCreatedCornbread);
+			break;
+		case kWashingMachineZoomIn:
+			_lookingAtLaundry = true;
+			startExtraSequence(kWashingMachineLoop, kExtraCompletedFlag, kFilterAllInput);
+			break;
+		case kWashingMachineZoomOut:
+			_extraMovie.stopDisplaying();
+			_extraMovie.releaseMovie();
+			_navMovie.startDisplaying();
+			_lookingAtLaundry = false;
 			break;
 		case kCaldoria00SitDown:
 			arriveAt(kCaldoria00, kEast);
@@ -1240,15 +1608,27 @@ void Caldoria::receiveNotification(Notification *notification, const Notificatio
 		case kCa48NorthExplosion:
 			// Current biochip must be the shield if we got here.
 			_vm->getCurrentBiochip()->setItemState(kShieldNormal);
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA41", kArthurCaldoriaBlownDoor);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB42", kArthurCaldoriaBlownDoor);
+			}
 			break;
 		case kBinocularsZoomInOnShip:
 			setCurrentActivation(kActivateFocusedOnShip);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA03", kArthurCaldoriaLookThroughTelescope);
 			break;
 		case kCa49NorthVoiceAnalysis:
 			_utilityFuse.primeFuse(kSinclairShootsTimeLimit);
 			_utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::sinclairTimerExpired));
 			_utilityFuse.lightFuse();
 			GameState.setCaldoriaSawVoiceAnalysis(true);
+			if (_vm->isDVD() && g_AIArea)
+				g_AIArea->checkRules();
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA20", kArthurCaldoriaSawVoiceAnalysis);
 			break;
 		case kCa53EastZoomToSinclair:
 			if (GameState.getCaldoriaSinclairShot()) {
@@ -1274,6 +1654,10 @@ void Caldoria::receiveNotification(Notification *notification, const Notificatio
 		}
 	} else if ((flags & kSpotSoundCompletedFlag) != 0) {
 		switch (GameState.getCurrentRoom()) {
+		case kCaldoria11:
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA46", kArthurCaldoriaZoomedToLaundry);
+			break;
 		case kCaldoria20:
 		case kCaldoria21:
 		case kCaldoria26:
@@ -1281,6 +1665,8 @@ void Caldoria::receiveNotification(Notification *notification, const Notificatio
 		case kCaldoria34:
 		case kCaldoria35:
 			updateViewFrame();
+			if ((GameState.getCurrentRoom() == kCaldoria34 || GameState.getCurrentRoom() == kCaldoria35) && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA44", kArthurCaldoriaSawVacantApartment);
 			break;
 		case kCaldoria27:
 		case kCaldoria28:
@@ -1288,7 +1674,14 @@ void Caldoria::receiveNotification(Notification *notification, const Notificatio
 			updateElevatorMovie();
 			break;
 		case kCaldoriaReplicator:
-			emptyOJGlass();
+			if (_spotSounds.getStart() == kCaldoriaReplicatorWrongChoiceIn) {
+				if (g_arthurChip)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA16", kArthurCaldoriaSelectedStickyBuns);
+			} else {
+				emptyOJGlass();
+				if (g_arthurChip)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA45", kArthurCaldoriaDrankOJ);
+			}
 			break;
 		default:
 			break;
@@ -1360,6 +1753,16 @@ void Caldoria::activateHotspots() {
 	case kCaldoriaReplicator:
 		if (GameState.getCaldoriaMadeOJ())
 			_vm->getAllHotspots().deactivateOneHotspot(kCaldoriaMakeOJSpotID);
+		if (GameState.getEasterEgg())
+			_vm->getAllHotspots().activateOneHotspot(kCaldoriaCornbreadHotSpotID);
+		break;
+	case kCaldoria11:
+		if (_vm->isDVD() && GameState.getCurrentDirection() == kWest) {
+			if (_lookingAtLaundry)
+				_vm->getAllHotspots().activateOneHotspot(kCaldoriaLaundryZoomOutHotSpotID);
+			else
+				_vm->getAllHotspots().activateOneHotspot(kCaldoriaLaundryZoomInHotSpotID);
+		}
 		break;
 	case kCaldoria27:
 		if (GameState.isCurrentDoorOpen()) {
@@ -1454,11 +1857,28 @@ void Caldoria::clickInHotspot(const Input &input, const Hotspot *spot) {
 		startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kCaldoriaMakeStickyBunsSpotID:
-		requestSpotSound(kCaldoriaReplicatorWrongChoiceIn, kCaldoriaReplicatorWrongChoiceOut, kFilterNoInput, 0);
+		requestSpotSound(kCaldoriaReplicatorWrongChoiceIn, kCaldoriaReplicatorWrongChoiceOut, kFilterNoInput, kSpotSoundCompletedFlag);
 		break;
 	case kCaldoriaMakeOJSpotID:
-		GameState.setCaldoriaMadeOJ(true);
-		startExtraSequence(kCreateOrangeJuice, kExtraCompletedFlag, kFilterNoInput);
+		if (_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input)) {
+			startExtraSequence(kCreateCornbread, kExtraCompletedFlag, kFilterNoInput);
+		} else {
+			requestSpotSound(kCaldoriaReplicatorOJChoiceIn, kCaldoriaReplicatorOJChoiceOut, kFilterNoInput, 0);
+			requestExtraSequence(kCreateOrangeJuice, kExtraCompletedFlag, kFilterNoInput);
+		}
+		break;
+	case kCaldoriaCornbreadHotSpotID:
+		doArthurJoyride();
+		break;
+	case kCaldoriaLaundryZoomInHotSpotID:
+		if (_vm->isDVD()) {
+			startExtraSequence(kWashingMachineZoomIn, kExtraCompletedFlag, kFilterNoInput);
+			requestDelay(30, 10, kFilterNoInput, kDelayCompletedFlag);
+			requestSpotSound(kCaldoriaLaundryIntro1In, kCaldoriaLaundryIntro2Out, kFilterNoInput, kSpotSoundCompletedFlag);
+		}
+		break;
+	case kCaldoriaLaundryZoomOutHotSpotID:
+		startExtraSequence(kWashingMachineZoomOut, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kCaBedroomVidPhoneActivationSpotID:
 		newInteraction(kCaldoriaMessagesInteractionID);
@@ -1540,19 +1960,29 @@ void Caldoria::clickInHotspot(const Input &input, const Hotspot *spot) {
 
 void Caldoria::clickOnDoorbell(const HotSpotID doorBellSpotID) {
 	uint32 extra;
+	Sound doorbellSound;
 	ExtraTable::Entry entry;
+	Input input;
 
 	switch (doorBellSpotID) {
 	case kCaldoria20DoorbellSpotID:
+		if (_vm->isDVD())
+			doorbellSound.initFromAIFFFile("Sounds/Caldoria/AH5.AIFF");
 		extra = kCaldoria20Doorbell;
 		break;
 	case kCaldoria21DoorbellSpotID:
+		if (_vm->isDVD())
+			doorbellSound.initFromAIFFFile("Sounds/Caldoria/AH4.AIFF");
 		extra = kCaldoria21Doorbell;
 		break;
 	case kCaldoria26DoorbellSpotID:
+		if (_vm->isDVD())
+			doorbellSound.initFromAIFFFile("Sounds/Caldoria/AH3.AIFF");
 		extra = kCaldoria26Doorbell;
 		break;
 	case kCaldoria29DoorbellSpotID:
+		if (_vm->isDVD())
+			doorbellSound.initFromAIFFFile("Sounds/Caldoria/AH1.AIFF");
 		extra = kCaldoria29Doorbell;
 		break;
 	case kCaldoria34DoorbellSpotID:
@@ -1567,7 +1997,51 @@ void Caldoria::clickOnDoorbell(const HotSpotID doorBellSpotID) {
 
 	getExtraEntry(extra, entry);
 	showViewFrame(entry.movieStart);
-	requestSpotSound(kCaldoriaNobodyHomeIn, kCaldoriaNobodyHomeOut, kFilterNoInput, kSpotSoundCompletedFlag);
+	if (_vm->isDVD() && doorBellSpotID != kCaldoria34DoorbellSpotID && doorBellSpotID != kCaldoria35DoorbellSpotID) {
+		_vm->_cursor->hide();
+		doorbellSound.setVolume(_vm->getSoundFXLevel());
+		doorbellSound.playSound();
+		while (doorbellSound.isPlaying() && !_vm->shouldQuit()) {
+			InputDevice.getInput(input, kFilterNoInput);
+
+			_vm->checkCallBacks();
+			_vm->refreshDisplay();
+			_vm->_system->delayMillis(10);
+		}
+		if (_vm->shouldQuit())
+			return;
+		_vm->_cursor->hideUntilMoved();
+		updateViewFrame();
+	} else {
+		requestSpotSound(kCaldoriaNobodyHomeIn, kCaldoriaNobodyHomeOut, kFilterNoInput, kSpotSoundCompletedFlag);
+	}
+}
+
+void Caldoria::cantMoveThatWay(CanOpenDoorReason reason) {
+	switch (reason) {
+	case kCantMoveDoorClosed:
+	case kCantMoveDoorLocked:
+		openDoor();
+		break;
+	case kCantMoveBlocked:
+		switch (GameState.getCurrentRoomAndView()) {
+		case MakeRoomView(kCaldoria20, kWest):
+		case MakeRoomView(kCaldoria21, kEast):
+		case MakeRoomView(kCaldoria26, kSouth):
+		case MakeRoomView(kCaldoria29, kSouth):
+		case MakeRoomView(kCaldoria34, kWest):
+		case MakeRoomView(kCaldoria35, kEast):
+			cantOpenDoor(kCantOpenLocked);
+			break;
+		default:
+			zoomUpOrBump();
+			break;
+		}
+		break;
+	default:
+		bumpIntoWall();
+		break;
+	}
 }
 
 CanOpenDoorReason Caldoria::canOpenDoor(DoorTable::Entry &entry) {
@@ -1663,6 +2137,9 @@ void Caldoria::pickedUpItem(Item *item) {
 }
 
 void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
+	Input input;
+	Sound cardBombVoice;
+
 	switch (item->getObjectID()) {
 	case kKeyCard:
 		Neighborhood::dropItemIntoRoom(item, dropSpot);
@@ -1686,7 +2163,27 @@ void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
 		_utilityFuse.lightFuse();
 		GameState.setCaldoriaFuseTimeLimit(kCardBombCountDownTime);
 		loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop);
+		if (_vm->isDVD()) {
+			InputDevice.getInput(input, kFilterAllInput);
+			if (JMPPPInput::isEasterEggModifierInput(input))
+				cardBombVoice.initFromAIFFFile("Sounds/Caldoria/Card Bomb.Geno.aiff");
+			else
+				cardBombVoice.initFromAIFFFile("Sounds/Caldoria/Card Bomb.44K.aiff");
+			cardBombVoice.setVolume(_vm->getSoundFXLevel());
+			cardBombVoice.playSound();
+			while (cardBombVoice.isPlaying() && !_vm->shouldQuit()) {
+				InputDevice.getInput(input, kFilterNoInput);
+
+				_vm->checkCallBacks();
+				_vm->refreshDisplay();
+				_vm->_system->delayMillis(10);
+			}
+			if (_vm->shouldQuit())
+				return;
+		}
 		GameState.setScoringUsedCardBomb(true);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA19", kArthurCaldoriaUsedCardBomb);
 		break;
 	case kStunGun:
 		GameState.setCaldoriaGunAimed(true);
@@ -1697,6 +2194,8 @@ void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
 		_gunSprite->moveElementTo(kCaldoriaGunSpriteLeft, kCaldoriaGunSpriteTop);
 		_gunSprite->startDisplaying();
 		_gunSprite->show();
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA17", kArthurCaldoriaStunningSinclair);
 		break;
 	default:
 		Neighborhood::dropItemIntoRoom(item, dropSpot);
@@ -1704,6 +2203,42 @@ void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
 	}
 }
 
+void Caldoria::setSoundFXLevel(const uint16 level) {
+	Neighborhood::setSoundFXLevel(level);
+
+	if (_extraMovie.isMovieValid())
+		_extraMovie.setVolume(level);
+}
+
+void Caldoria::playMissingFloorSound() {
+	Input input;
+	Sound elevatorVoice;
+
+	InputDevice.getInput(input, kFilterAllInput);
+	if (_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input)) {
+		_vm->_cursor->hide();
+		elevatorVoice.initFromAIFFFile("Sounds/Caldoria/Elevator Denied.32K.aiff");
+		elevatorVoice.setVolume(_vm->getSoundFXLevel());
+		elevatorVoice.playSound();
+		while (elevatorVoice.isPlaying() && !_vm->shouldQuit()) {
+			InputDevice.getInput(input, kFilterNoInput);
+
+			_vm->checkCallBacks();
+			_vm->refreshDisplay();
+			_vm->_system->delayMillis(10);
+		}
+		if (_vm->shouldQuit())
+			return;
+		_vm->_cursor->hideUntilMoved();
+		updateElevatorMovie();
+	} else {
+		requestSpotSound(kCaldoriaNoOtherFloorIn,
+							kCaldoriaNoOtherFloorOut,
+							kFilterNoInput,
+							kSpotSoundCompletedFlag);
+	}
+}
+
 void Caldoria::takeElevator(uint startFloor, uint endFloor) {
 	_croppedMovie.stop();
 	_croppedMovie.setSegment(0, _croppedMovie.getDuration());
@@ -1717,12 +2252,12 @@ void Caldoria::takeElevator(uint startFloor, uint endFloor) {
 		case 2:
 			_croppedMovie.setTime(k1To2Time);
 			_croppedMovie.redrawMovieWorld();
-			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+			playMissingFloorSound();
 			break;
 		case 3:
 			_croppedMovie.setTime(k1To3Time);
 			_croppedMovie.redrawMovieWorld();
-			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+			playMissingFloorSound();
 			break;
 		case 4:
 			_croppedMovie.setSegment(k1To4Start, k1To4Stop);
@@ -1751,12 +2286,12 @@ void Caldoria::takeElevator(uint startFloor, uint endFloor) {
 		case 2:
 			_croppedMovie.setTime(k4To2Time);
 			_croppedMovie.redrawMovieWorld();
-			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+			playMissingFloorSound();
 			break;
 		case 3:
 			_croppedMovie.setTime(k4To3Time);
 			_croppedMovie.redrawMovieWorld();
-			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+			playMissingFloorSound();
 			break;
 		case 4:
 			// Do nothing.
@@ -1782,12 +2317,12 @@ void Caldoria::takeElevator(uint startFloor, uint endFloor) {
 		case 2:
 			_croppedMovie.setTime(k5To2Time);
 			_croppedMovie.redrawMovieWorld();
-			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+			playMissingFloorSound();
 			break;
 		case 3:
 			_croppedMovie.setTime(k5To3Time);
 			_croppedMovie.redrawMovieWorld();
-			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag);
+			playMissingFloorSound();
 			break;
 		case 4:
 			_croppedMovie.setSegment(k5To4Start, k5To4Stop);
@@ -2065,4 +2600,64 @@ Common::String Caldoria::getSoundSpotsName() {
 	return "Sounds/Caldoria/Caldoria Spots";
 }
 
+void Caldoria::doArthurJoyride() {
+	Video::VideoDecoder *video = 0;
+	BiochipItem *item;
+
+	setNextHandler(_vm);
+	throwAwayInterface();
+	loadLoopSound1("");
+
+	_vm->_cursor->hide();
+
+#ifdef USE_THEORADEC
+	video = new Video::TheoraDecoder();
+	if (!video->loadFile("Images/Caldoria/A12RD.ogg")) {
+		delete video;
+		video = 0;
+	}
+#endif
+
+	if (!video) {
+		video = new Video::QuickTimeDecoder();
+		if (!video->loadFile("Images/Caldoria/A12RD.movie"))
+			error("Could not load joyride video");
+	}
+
+	video->setVolume(MIN<uint>(_vm->getSoundFXLevel(), 0xFF));
+	video->start();
+
+	while (!_vm->shouldQuit() && !video->endOfVideo()) {
+		if (video->needsUpdate()) {
+			const Graphics::Surface *frame = video->decodeNextFrame();
+
+			if (frame) {
+				g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
+				g_system->updateScreen();
+			}
+		}
+
+		InputDevice.pumpEvents();
+
+		g_system->delayMillis(10);
+	}
+
+	delete video;
+
+	if (_vm->shouldQuit())
+		return;
+
+	reinstateMonocleInterface();
+	loadAmbientLoops();
+	updateViewFrame();
+	if (!_vm->playerHasItemID(kArthurBiochip)) {
+		item = (BiochipItem *)_vm->getAllItems().findItemByID(kArthurBiochip);
+		_vm->addItemToBiochips(item);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA06", kArthurCaldoriaFinishedJoyride);
+	}
+	g_interface->setCurrentBiochipID(kArthurBiochip);
+	GameState.setEasterEgg(false);
+}
+
 } // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.h b/engines/pegasus/neighborhood/caldoria/caldoria.h
index 5fca53baf9..8f242891b9 100644
--- a/engines/pegasus/neighborhood/caldoria/caldoria.h
+++ b/engines/pegasus/neighborhood/caldoria/caldoria.h
@@ -195,6 +195,7 @@ static const HotSpotID kCaldoriaRoofElevatorSpotID = 5065;
 static const HotSpotID kCaldoriaRoofDoorSpotID = 5066;
 static const HotSpotID kCaldoriaRoofCardDropSpotID = 5067;
 static const HotSpotID kCaldoria53EastSinclairTargetSpotID = 5068;
+static const HotSpotID kCaldoriaCornbread = 5069;
 
 // Extra sequence IDs.
 
@@ -383,6 +384,9 @@ static const DisplayElementID kCaldoriaUtilityID = kCaldoriaMessagesID + 1;
 static const DisplayElementID kCaldoriaBombGridID = kCaldoriaUtilityID + 1;
 static const DisplayElementID kCaldoriaBombTimerID = kCaldoriaBombGridID + 1;
 
+static const TimeValue kCaldoria4DInstructionsIn = 28013;
+static const TimeValue kCaldoria4DInstructionsOut = 29730;
+
 static const TimeValue kCaldoria4DBlankChoiceIn = 29730;
 static const TimeValue kCaldoria4DBlankChoiceOut = 33910;
 
@@ -422,6 +426,8 @@ public:
 
 	void checkContinuePoint(const RoomID, const DirectionConstant) override;
 
+	void setSoundFXLevel(const uint16) override;
+
 protected:
 	enum {
 		kCaldoriaPrivate4DSystemOpenFlag,
@@ -445,6 +451,7 @@ protected:
 
 	void init() override;
 	void start() override;
+	void throwAwayInterface() override;
 
 	void setUpRoofTop();
 
@@ -472,7 +479,10 @@ protected:
 	void arriveAtCaldoriaDeath();
 	void turnTo(const DirectionConstant) override;
 	void zoomTo(const Hotspot *) override;
+	void leftButton(const Input &) override;
+	void rightButton(const Input &) override;
 	void downButton(const Input &) override;
+	void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits) override;
 	void receiveNotification(Notification *, const NotificationFlags) override;
 	InputBits getInputFilter() override;
 	void activateHotspots() override;
@@ -483,6 +493,7 @@ protected:
 
 	Hotspot *getItemScreenSpot(Item *, DisplayElement *) override;
 	void dropItemIntoRoom(Item *, Hotspot *) override;
+	void playMissingFloorSound();
 	void takeElevator(uint, uint);
 	void updateElevatorMovie();
 	void openElevatorMovie();
@@ -495,7 +506,9 @@ protected:
 	void zoomToSinclair();
 	void playEndMessage();
 	void checkInterruptSinclair();
+	void doArthurJoyride();
 
+	void cantMoveThatWay(CanMoveForwardReason) override;
 	CanOpenDoorReason canOpenDoor(DoorTable::Entry &) override;
 	void doorOpened() override;
 
@@ -505,6 +518,15 @@ protected:
 
 	const Hotspot *_zoomOutSpot;
 
+	Hotspot _laundryZoomInSpot;
+	Hotspot _laundryZoomOutSpot;
+	Hotspot _cornbreadSpot;
+
+	Movie _extraMovie;
+	NotificationCallBack _extraMovieCallBack;
+
+	bool _lookingAtLaundry;
+
 	FuseFunction _utilityFuse;
 
 	long _sinclairLoopCount;
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp
index 303bcc2946..8539e16d38 100644
--- a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp
+++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp
@@ -222,7 +222,8 @@ void Caldoria4DSystem::useIdleTime() {
 void Caldoria4DSystem::initInteraction() {
 	setSpritesMovie();
 
-	_owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff");
+	playSound("Rock");
+	_owner->playSpotSoundSync(kCaldoria4DInstructionsIn, kCaldoria4DInstructionsOut);
 	loopExtra(k4DIslandLoop);
 }
 
@@ -255,8 +256,7 @@ void Caldoria4DSystem::handleInput(const Input &input, const Hotspot *cursorSpot
 
 void Caldoria4DSystem::activateHotspots() {
 	GameInteraction::activateHotspots();
-	if (_whichMenu == k4DAudioMenu)
-		g_allHotspots.activateOneHotspot(kCa4DChoice4SpotID);
+	g_allHotspots.activateOneHotspot(kCa4DChoice4SpotID);
 }
 
 void Caldoria4DSystem::clickInHotspot(const Input &input, const Hotspot *spot) {
@@ -347,7 +347,7 @@ void Caldoria4DSystem::makeRockChoice() {
 	if (_audioChoice != k4DRockChoice) {
 		_audioChoice = k4DRockChoice;
 		setSpritesMovie();
-		_owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff");
+		playSound("Rock");
 	}
 }
 
@@ -355,7 +355,7 @@ void Caldoria4DSystem::makeOrchestralChoice() {
 	if (_audioChoice != k4DOrchestralChoice) {
 		_audioChoice = k4DOrchestralChoice;
 		setSpritesMovie();
-		_owner->loadLoopSound1("Sounds/Caldoria/Orchestral.aiff");
+		playSound("Orchestral");
 	}
 }
 
@@ -363,7 +363,7 @@ void Caldoria4DSystem::makeRhythmsChoice() {
 	if (_audioChoice != k4DRhythmsChoice) {
 		_audioChoice = k4DRhythmsChoice;
 		setSpritesMovie();
-		_owner->loadLoopSound1("Sounds/Caldoria/Rhythms.aiff");
+		playSound("Rhythms");
 	}
 }
 
@@ -371,7 +371,7 @@ void Caldoria4DSystem::makeAcousticChoice() {
 	if (_audioChoice != k4DAcousticChoice) {
 		_audioChoice = k4DAcousticChoice;
 		setSpritesMovie();
-		_owner->loadLoopSound1("Sounds/Caldoria/Acoustic.aiff");
+		playSound("Acoustic");
 	}
 }
 
@@ -379,4 +379,14 @@ void Caldoria4DSystem::shutDown4DSystem() {
 	_whichMenu = k4DShuttingDown;
 }
 
+void Caldoria4DSystem::playSound(const Common::String &baseFileName) {
+	Common::String fileName = "Sounds/Caldoria/" + baseFileName;
+
+	// Updated DVD files
+	if (((PegasusEngine *)g_engine)->isDVD())
+		fileName += ".44K";
+
+	_owner->loadLoopSound1(fileName + ".aiff");
+}
+
 } // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h
index 1a057ec483..03477c1ed8 100644
--- a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h
+++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h
@@ -63,6 +63,8 @@ protected:
 	void useIdleTime() override;
 	void loopExtra(const ExtraID);
 
+	void playSound(const Common::String &baseFileName);
+
 	Movie _4DSpritesMovie;
 	TimeScale _4DSpritesScale;
 	uint _whichMenu;
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp
index 5da18ab9ea..97dbd9cb71 100644
--- a/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp
+++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp
@@ -25,6 +25,7 @@
 
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/neighborhood/caldoria/caldoria.h"
 #include "pegasus/neighborhood/caldoria/caldoriabomb.h"
 
@@ -48,7 +49,9 @@ static const uint32 kOnTime3 = kOffTime2 + kFlashOnTime;
 static const uint32 kOffTime3 = kOnTime3 + kFlashOffTime;
 static const uint32 kOnTime4 = kOffTime3 + kFlashOnTime;
 
-static const HotSpotID kVertextHotSpotBaseID = 10000;
+// Bomb hotspots start at 20000 since the extra Caldoria hotspots start at 10000.
+// Assigning these vice versa causes a hotspot in level 4 to never activate for some reason.
+static const HotSpotID kVertextHotSpotBaseID = 20000;
 
 static const CoordType kVertextHotSpotWidth = 24;
 static const CoordType kVertextHotSpotHeight = 24;
@@ -1248,6 +1251,12 @@ void CaldoriaBomb::receiveNotification(Notification *notification, const Notific
 			_lastVertex = -1;
 			_owner->_navMovie.setVolume(((PegasusEngine *)g_engine)->getAmbienceLevel());
 			startBombAmbient("Sounds/Caldoria/BmbLoop1.22K.AIFF");
+			if (g_arthurChip) {
+				if (((PegasusEngine *)g_engine)->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA14", kArthurCaldoriaSeeRoofBomb);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB28", kArthurCaldoriaSeeRoofBomb);
+			}
 			break;
 		case kCaldoria56BombStage2:
 		case kCaldoria56BombStage3:
@@ -1429,6 +1438,8 @@ void CaldoriaBomb::handleInput(const Input &input, const Hotspot *hotspot) {
 				_timer.hide();
 				_owner->_navMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
 				_owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput);
+				if (g_arthurChip)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA02", kArthurCaldoriaDisarmedNuke);
 				break;
 			default:
 				break;
diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp
index d39e9a7677..e53122d400 100644
--- a/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp
+++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp
@@ -25,6 +25,7 @@
 
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/neighborhood/neighborhood.h"
 #include "pegasus/neighborhood/caldoria/caldoria.h"
 #include "pegasus/neighborhood/caldoria/caldoriamirror.h"
@@ -43,6 +44,11 @@ void CaldoriaMirror::openInteraction() {
 void CaldoriaMirror::initInteraction() {
 	_owner->setCurrentActivation(kActivateMirrorReady);
 	_owner->startExtraSequence(kCaBathroomGreeting, kExtraCompletedFlag, kFilterNoInput);
+	// The original made the player click to start each of the following sequences,
+	// which was unnecessary, so it is automated here.
+	_owner->startExtraSequenceSync(kCaBathroomGreeting, kFilterNoInput);
+	_owner->startExtraSequenceSync(kCaBathroomBodyFat, kFilterNoInput);
+	_owner->startExtraSequence(kCaBathroomStylistIntro, kExtraCompletedFlag, kFilterNoInput);
 }
 
 void CaldoriaMirror::closeInteraction() {
@@ -82,32 +88,18 @@ void CaldoriaMirror::activateHotspots() {
 
 void CaldoriaMirror::clickInHotspot(const Input &input, const Hotspot *spot) {
 	switch (spot->getObjectID()) {
-	case kCaBathroomMirrorSpotID:
-		switch (_owner->getLastExtra()) {
-		case kCaBathroomGreeting:
-			_owner->startExtraSequence(kCaBathroomBodyFat, kExtraCompletedFlag, kFilterNoInput);
-			break;
-		case kCaBathroomBodyFat:
-			_owner->startExtraSequence(kCaBathroomStylistIntro, kExtraCompletedFlag, kFilterNoInput);
-			break;
-		case kCaBathroomRetrothrash:
-			_owner->startExtraSequence(kCaBathroomRetrothrashReturn, kExtraCompletedFlag, kFilterNoInput);
-			break;
-		case kCaBathroomGeoWave:
-			_owner->startExtraSequence(kCaBathroomGeoWaveReturn, kExtraCompletedFlag, kFilterNoInput);
-			break;
-		default:
-			break;
-		}
-		break;
+	// The original made the player click through several interstitial screens before
+	// reaching the hairstyle menu, which was unnecessary, so it's skipped here.
 	case kCaHairStyle1SpotID:
-		_owner->startExtraSequence(kCaBathroomRetrothrash, kExtraCompletedFlag, kFilterNoInput);
+		_owner->startExtraSequenceSync(kCaBathroomRetrothrash, kFilterNoInput);
+		_owner->startExtraSequence(kCaBathroomRetrothrashReturn, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kCaHairStyle2SpotID:
 		_owner->startExtraSequence(kCaBathroomAgencyStandard, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kCaHairStyle3SpotID:
-		_owner->startExtraSequence(kCaBathroomGeoWave, kExtraCompletedFlag, kFilterNoInput);
+		_owner->startExtraSequenceSync(kCaBathroomGeoWave, kFilterNoInput);
+		_owner->startExtraSequence(kCaBathroomGeoWaveReturn, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	default:
 		GameInteraction::clickInHotspot(input, spot);
@@ -131,12 +123,12 @@ void CaldoriaMirror::receiveNotification(Notification *, const NotificationFlags
 		_owner->requestDeleteCurrentInteraction();
 		GameState.setScoringFixedHair(true);
 		GameState.setCaldoriaDoneHygiene(true);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA43", kArthurCaldoriaChoseAgencyHairStyle);
 		break;
 	default:
 		break;
 	}
-
-	allowInput(true);
 }
 
 } // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/canyonchase.cpp b/engines/pegasus/neighborhood/mars/canyonchase.cpp
new file mode 100644
index 0000000000..9149f12c92
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/canyonchase.cpp
@@ -0,0 +1,540 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/neighborhood/mars/canyonchase.h"
+#include "pegasus/neighborhood/mars/mars.h"
+
+namespace Pegasus {
+
+// Segment start and end points.
+
+//static const TimeValue kPrepStart = 0;
+static const TimeValue kPrepEnd = 3000;
+static const TimeValue kLaunchStart = kPrepEnd;
+static const TimeValue kLaunchEnd = 6640;
+static const TimeValue kBranch1Start = kLaunchEnd;
+static const TimeValue kBranch1End = 22240;
+static const TimeValue kBranch2Start = kBranch1End;
+static const TimeValue kBranch2End = 28440;
+static const TimeValue kBranch3Start = kBranch2End;
+static const TimeValue kBranch3End = 38640;
+static const TimeValue kBranch4Start = kBranch3End;
+static const TimeValue kBranch4End = 43880;
+static const TimeValue kBranch5Start = kBranch4End;
+static const TimeValue kBranch5End = 58680;
+static const TimeValue kExitStart = kBranch5End;
+static const TimeValue kExitEnd = 66480;
+static const TimeValue kExitLoopPoint = 66200;
+static const TimeValue kExitGenoPoint = 62560;
+
+// Death start and end points.
+
+static const TimeValue kDeath1Start = 0;
+static const TimeValue kDeath1End = 2400;
+static const TimeValue kDeath2Start = kDeath1End;
+static const TimeValue kDeath2End = 4720;
+static const TimeValue kDeath3Start = kDeath2End;
+static const TimeValue kDeath3End = 7120;
+static const TimeValue kDeath4Start = kDeath3End;
+static const TimeValue kDeath4End = 9280;
+static const TimeValue kDeath5Start = kDeath4End;
+static const TimeValue kDeath5End = 12000;
+
+// Chase state.
+
+enum {
+	kCanyonLaunch,
+	kCanyonBranch1Left,
+	kCanyonBranch1Right,
+	kCanyonBranch2Left,
+	kCanyonBranch2Right,
+	kCanyonBranch3Left,
+	kCanyonBranch4Left,
+	kCanyonBranch4Right,
+	kCanyonBranch5Left,
+	kCanyonBranch5Right,
+	kCanyonExit,
+	kCanyonLoop
+};
+
+void MusicTimerEvent::fire() {
+	canyonChase->musicTimerExpired(*this);
+}
+
+CanyonChase::CanyonChase(Neighborhood *handler) : ChaseInteraction(kMarsCanyonChaseInteractionID, handler,
+							kMarsCanyonChaseNotificationID, (PegasusEngine *)g_engine), _canyonMovie1(kNoDisplayElement),
+							_canyonMovie2(kNoDisplayElement), _deathMovie(kNoDisplayElement), _genoMovie(kNoDisplayElement) {
+	_currentMovie = NULL;
+	_currentCallBack = NULL;
+}
+
+void CanyonChase::setSoundFXLevel(const uint16 fxLevel) {
+	_canyonMovie1.setVolume(fxLevel);
+	_canyonMovie2.setVolume(fxLevel);
+	_deathMovie.setVolume(fxLevel);
+}
+
+void CanyonChase::setAmbienceLevel(const uint16 level) {
+	_genoMovie.setVolume(level);
+	_musicFader.setMasterVolume(level);
+}
+
+void CanyonChase::startCanyonMusicLoop(void) {
+	FaderMoveSpec spec;
+
+	_musicLoop.loopSound();
+	spec.makeTwoKnotFaderSpec(10, 0, 0, 1, 255);
+	_musicFader.startFader(spec);
+}
+
+void CanyonChase::stopCanyonMusicLoop(const long ticks) {
+	FaderMoveSpec spec;
+
+	spec.makeTwoKnotFaderSpec(10, 0, 255, ticks, 0);
+	_musicFader.startFader(spec);
+}
+
+void CanyonChase::openInteraction() {
+	_canyonMovie1.initFromMovieFile("Images/Mars/Canyon_hq1.mov");
+	_canyonMovie1.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+	_canyonMovie1.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
+	_canyonMovie1.setDisplayOrder(kShuttleMonitorOrder);
+
+	_canyon1CallBack.setNotification(&_chaseNotification);
+	_canyon1CallBack.initCallBack(&_canyonMovie1, kCallBackAtExtremes);
+	_canyon1CallBack.setCallBackFlag(kChaseEnteredBranchZone);
+	_canyon1CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	_canyonMovie2.initFromMovieFile("Images/Mars/Canyon_hq2.mov");
+	_canyonMovie2.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+	_canyonMovie2.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
+	_canyonMovie2.setDisplayOrder(kShuttleMonitorOrder);
+
+	_canyon2CallBack.setNotification(&_chaseNotification);
+	_canyon2CallBack.initCallBack(&_canyonMovie2, kCallBackAtExtremes);
+	_canyon2CallBack.setCallBackFlag(kChaseEnteredBranchZone);
+	_canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	_deathMovie.initFromMovieFile("Images/Mars/Canyon_hqD.mov");
+	_deathMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+	_deathMovie.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
+	_deathMovie.setDisplayOrder(kShuttleMonitorOrder);
+
+	_deathCallBack.setNotification(&_chaseNotification);
+	_deathCallBack.initCallBack(&_deathMovie, kCallBackAtExtremes);
+	_deathCallBack.setCallBackFlag(kChaseFinished);
+	_deathCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	_musicLoop.attachFader(&_musicFader);
+	_musicLoop.initFromAIFFFile("Sounds/Mars/Canyon Loop.44K.16.AIFF");
+	_musicFader.setMasterVolume(((PegasusEngine *)g_engine)->getAmbienceLevel());
+
+	ChaseInteraction::openInteraction();
+
+	_steerPict.setDisplayOrder(kShuttleMonitorOrder + 1);
+	_steerPict.moveElementTo(kShuttleSteerLeft, kShuttleSteerTop);
+}
+
+void CanyonChase::initInteraction() {
+	_steerPict.startDisplaying();
+
+	// Launch branch is identical in both movies
+	_canyonState = kCanyonLaunch;
+	_canyonMovie1.setSegment(kLaunchStart, kLaunchEnd - kDecisionTime);
+	_canyonMovie1.setTime(kLaunchStart);
+	switchTo(_canyonMovie1, _canyon1CallBack);
+	startCanyonMusicLoop();
+	ChaseInteraction::initInteraction();
+}
+
+void CanyonChase::closeInteraction() {
+	_canyonMovie1.stop();
+	_canyonMovie1.stopDisplaying();
+	_canyonMovie1.releaseMovie();
+	_canyon1CallBack.releaseCallBack();
+
+	_canyonMovie2.stop();
+	_canyonMovie2.stopDisplaying();
+	_canyonMovie2.releaseMovie();
+	_canyon2CallBack.releaseCallBack();
+
+	_deathMovie.stop();
+	_deathMovie.stopDisplaying();
+	_deathMovie.releaseMovie();
+	_deathCallBack.releaseCallBack();
+
+	_genoMovie.stop();
+	_genoMovie.stopDisplaying();
+	_genoMovie.releaseMovie();
+	_genoCallBack.releaseCallBack();
+
+	ChaseInteraction::closeInteraction();
+}
+
+void CanyonChase::receiveNotification(Notification *notification, const NotificationFlags flags) {
+	Input input;
+
+	if (notification == &_chaseNotification && flags == kChaseFinished) {
+		if (_canyonState == kCanyonLoop) {
+			// Swallow the notification if we loop back to the beginning
+			InputDevice.getInput(input, kFilterAllInput);
+			if (JMPPPInput::isEasterEggModifierInput(input)) {
+				stopCanyonMusicLoop(15);
+				doGenoChase();
+			} else {
+				_canyonMovie2.setSegment(kExitGenoPoint, kExitLoopPoint - kDecisionTime);
+				_canyonMovie2.setTime(kExitGenoPoint);
+				switchTo(_canyonMovie2, _canyon2CallBack);
+				_canyon2CallBack.setCallBackFlag(kChaseEnteredBranchZone);
+				_canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+				_canyonState = kCanyonLaunch;
+			}
+			return;
+		} else if (_canyonState != kCanyonExit) {
+			// We died
+			((Mars *)_owner)->die(kDeathRanIntoCanyonWall);
+		}
+	}
+	ChaseInteraction::receiveNotification(notification, flags);
+}
+
+void CanyonChase::setUpBranch() {
+	TimeValue branchStart, branchEnd;
+
+	branchStart = 0;
+	branchEnd = 0;
+	switch (_canyonState) {
+	case kCanyonLaunch:
+	case kCanyonExit:
+		branchStart = kLaunchEnd - kDecisionTime;
+		branchEnd = kLaunchEnd;
+		break;
+	case kCanyonBranch1Left:
+	case kCanyonBranch1Right:
+		branchStart = kBranch1End - kDecisionTime;
+		branchEnd = kBranch1End;
+		break;
+	case kCanyonBranch2Left:
+	case kCanyonBranch2Right:
+		branchStart = kBranch2End - kDecisionTime;
+		branchEnd = kBranch2End;
+		break;
+	case kCanyonBranch3Left:
+		branchStart = kBranch3End - kDecisionTime;
+		branchEnd = kBranch3End;
+		break;
+	case kCanyonBranch4Left:
+	case kCanyonBranch4Right:
+		branchStart = kBranch4End - kDecisionTime;
+		branchEnd = kBranch4End;
+		break;
+	case kCanyonBranch5Left:
+	case kCanyonBranch5Right:
+		branchStart = kBranch5End - kDecisionTime;
+		branchEnd = kBranch5End;
+		break;
+	default:
+		break;
+	}
+
+	_currentMovie->setSegment(branchStart, branchEnd);
+	// Need to call SetTime here in case we loop
+	_currentMovie->setTime(branchStart);
+
+	_currentCallBack->setCallBackFlag(kChaseExitedBranchZone);
+	_currentCallBack->scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void CanyonChase::branchLeft() {
+	TimeValue branchStart, branchEnd;
+	Movie *movie;
+	NotificationCallBack *callBack;
+
+	branchStart = 0;
+	branchEnd = 0;
+	switch (_canyonState) {
+	case kCanyonLaunch:
+		branchStart = kBranch1Start;
+		branchEnd = kBranch1End - kDecisionTime;
+		_canyonState = kCanyonBranch1Left;
+		break;
+	case kCanyonBranch1Left:
+	case kCanyonBranch1Right:
+		branchStart = kBranch2Start;
+		branchEnd = kBranch2End - kDecisionTime;
+		_canyonState = kCanyonBranch2Left;
+		break;
+	case kCanyonBranch2Left:
+	case kCanyonBranch2Right:
+		branchStart = kBranch3Start;
+		branchEnd = kBranch3End - kDecisionTime;
+		_canyonState = kCanyonBranch3Left;
+		break;
+	case kCanyonBranch3Left:
+		branchStart = kBranch4Start;
+		branchEnd = kBranch4End - kDecisionTime;
+		_canyonState = kCanyonBranch4Left;
+		break;
+	case kCanyonBranch4Left:
+	case kCanyonBranch4Right:
+		branchStart = kBranch5Start;
+		branchEnd = kBranch5End - kDecisionTime;
+		_canyonState = kCanyonBranch5Left;
+		break;
+	case kCanyonBranch5Left:
+	case kCanyonBranch5Right:
+		dontBranch();
+		return;
+	default:
+		break;
+	}
+
+	// Left branches are in hq2 (except exit)
+	// Segment 5 branches are switched
+	if (_canyonState == kCanyonBranch5Left || _canyonState == kCanyonBranch5Right) {
+		movie = &_canyonMovie1;
+		callBack = &_canyon1CallBack;
+	} else {
+		movie = &_canyonMovie2;
+		callBack = &_canyon2CallBack;
+	}
+
+	movie->setSegment(branchStart, branchEnd);
+	movie->setTime(branchStart);
+
+	switchTo(*movie, *callBack);
+
+	callBack->setCallBackFlag(kChaseEnteredBranchZone);
+	callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void CanyonChase::branchRight() {
+	TimeValue branchStart, branchEnd;
+	NotificationFlags flag;
+	Movie *movie;
+	NotificationCallBack *callBack;
+
+	branchStart = 0;
+	branchEnd = 0;
+	flag = 0;
+	switch (_canyonState) {
+	case kCanyonLaunch:
+		branchStart = kBranch1Start;
+		branchEnd = kBranch1End - kDecisionTime;
+		_canyonState = kCanyonBranch1Right;
+		flag = kChaseEnteredBranchZone;
+		break;
+	case kCanyonBranch1Left:
+	case kCanyonBranch1Right:
+		branchStart = kBranch2Start;
+		branchEnd = kBranch2End - kDecisionTime;
+		_canyonState = kCanyonBranch2Right;
+		flag = kChaseEnteredBranchZone;
+		break;
+	case kCanyonBranch2Left:
+	case kCanyonBranch2Right:
+		dontBranch();
+		return;
+	case kCanyonBranch3Left:
+		branchStart = kBranch4Start;
+		branchEnd = kBranch4End - kDecisionTime;
+		_canyonState = kCanyonBranch4Right;
+		flag = kChaseEnteredBranchZone;
+		break;
+	case kCanyonBranch4Left:
+	case kCanyonBranch4Right:
+		branchStart = kBranch5Start;
+		branchEnd = kBranch5End - kDecisionTime;
+		_canyonState = kCanyonBranch5Right;
+		flag = kChaseEnteredBranchZone;
+		break;
+	case kCanyonBranch5Left:
+	case kCanyonBranch5Right:
+		// Exit loop branch is in hq2
+		branchStart = kExitStart;
+		branchEnd = kExitEnd;
+		_canyonState = kCanyonExit;
+		flag = kChaseFinished;
+		startMusicTimer(kCanyonChaseStart + kCanyonChaseExitedTime - kExitStart, kMovieTicksPerSecond,
+						kCanyonExited);
+		break;
+	default:
+		break;
+	}
+
+	// Right branches are in hq1 (except exit)
+	// Segment 5 branches are switched
+	if (_canyonState == kCanyonBranch5Left || _canyonState == kCanyonBranch5Right) {
+		movie = &_canyonMovie2;
+		callBack = &_canyon2CallBack;
+	} else {
+		movie = &_canyonMovie1;
+		callBack = &_canyon1CallBack;
+	}
+
+	movie->setSegment(branchStart, branchEnd);
+	movie->setTime(branchStart);
+
+	switchTo(*movie, *callBack);
+
+	callBack->setCallBackFlag(flag);
+	callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void CanyonChase::dontBranch() {
+	TimeValue branchStart, branchEnd;
+
+	branchStart = 0;
+	branchEnd = 0;
+	switch (_canyonState) {
+	case kCanyonLaunch:
+		branchStart = kDeath1Start;
+		branchEnd = kDeath1End;
+		break;
+	case kCanyonBranch1Left:
+	case kCanyonBranch1Right:
+		branchStart = kDeath2Start;
+		branchEnd = kDeath2End;
+		break;
+	case kCanyonBranch2Left:
+	case kCanyonBranch2Right:
+		branchStart = kDeath3Start;
+		branchEnd = kDeath3End;
+		break;
+	case kCanyonBranch3Left:
+		branchStart = kDeath4Start;
+		branchEnd = kDeath4End;
+		break;
+	case kCanyonBranch4Left:
+	case kCanyonBranch4Right:
+		branchStart = kDeath5Start;
+		branchEnd = kDeath5End;
+		break;
+	case kCanyonBranch5Left:
+	case kCanyonBranch5Right:
+		_canyonMovie2.setSegment(kExitStart, kExitGenoPoint);
+		_canyonMovie2.setTime(kExitStart);
+		switchTo(_canyonMovie2, _canyon2CallBack);
+		_canyon2CallBack.setCallBackFlag(kChaseFinished);
+		_canyon2CallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+		_canyonState = kCanyonLoop;
+		return;
+	default:
+		break;
+	}
+
+	_deathMovie.setSegment(branchStart, branchEnd);
+	_deathMovie.setTime(branchStart);
+
+	switchTo(_deathMovie, _deathCallBack);
+
+	startMusicTimer(10, 10, kCanyonRanIntoWall);
+}
+
+void CanyonChase::showControlsHint() {
+	((Mars *)_owner)->_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftKeypadHintTime);
+	((Mars *)_owner)->_lowerLeftShuttleMovie.redrawMovieWorld();
+	ChaseInteraction::showControlsHint();
+}
+
+void CanyonChase::hideControlsHint() {
+	((Mars *)_owner)->_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftCollisionTime);
+	((Mars *)_owner)->_lowerLeftShuttleMovie.redrawMovieWorld();
+	ChaseInteraction::hideControlsHint();
+}
+
+void CanyonChase::switchTo(Movie &movie, NotificationCallBack &callBack) {
+	if (_currentMovie != &movie) {
+		if (_currentMovie != NULL) {
+			_currentMovie->stop();
+			_currentMovie->hide();
+			_currentMovie->stopDisplaying();
+		}
+
+		_currentMovie = &movie;
+
+		_currentMovie->startDisplaying();
+		_currentMovie->show();
+		_currentMovie->start();
+	}
+
+	if (_currentCallBack != &callBack) {
+		_currentCallBack = &callBack;
+	}
+}
+
+void CanyonChase::startMusicTimer(TimeValue time, TimeScale scale, MusicTimerCode code) {
+	_musicFuse.primeFuse(time, scale);
+	_musicEvent.canyonChase = this;
+	_musicEvent.theEvent = code;
+	_musicFuse.setFunctor(new Common::Functor0Mem<void, MusicTimerEvent>(&_musicEvent, &MusicTimerEvent::fire));
+	_musicFuse.lightFuse();
+}
+
+void CanyonChase::musicTimerExpired(MusicTimerEvent &event) {
+	FaderMoveSpec spec;
+
+	switch (event.theEvent) {
+	case kCanyonRanIntoWall:
+		stopCanyonMusicLoop(5);
+		break;
+	case kCanyonExited:
+		spec.makeTwoKnotFaderSpec(20, 0, 255, 5, 160);
+		_musicFader.startFader(spec);
+		startMusicTimer(kCanyonChaseFadedTime, kMovieTicksPerSecond, kCanyonFaded);
+		break;
+	case kCanyonFaded:
+		spec.makeTwoKnotFaderSpec(10, 0, 160, 30, 0);
+		_musicFader.startFader(spec);
+		((Mars *)_owner)->startMarsTimer(kLaunchTubeDVDReachedTime, kMovieTicksPerSecond,
+											kMarsLaunchTubeReached);
+		break;
+	default:
+		break;
+	}
+}
+
+void CanyonChase::doGenoChase() {
+	_genoMovie.initFromMovieFile("Images/Mars/Canyon_hqG.mov");
+	_genoMovie.setVolume(((PegasusEngine *)g_engine)->getAmbienceLevel());
+	_genoMovie.moveElementTo(kShuttleWindowLeft, kShuttleWindowTop);
+	_genoMovie.setDisplayOrder(kShuttleMonitorOrder);
+	_genoMovie.startDisplaying();
+	_genoMovie.show();
+	_genoMovie.start();
+
+	_genoCallBack.setNotification(&_chaseNotification);
+	_genoCallBack.initCallBack(&_genoMovie, kCallBackAtExtremes);
+	_genoCallBack.setCallBackFlag(kChaseFinished);
+	_genoCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	_canyonState = kCanyonExit;
+
+	((Mars *)_owner)->startMarsTimer(_genoMovie.getDuration() - 5 * kMovieTicksPerSecond,
+										kMovieTicksPerSecond, kMarsLaunchTubeReached);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/canyonchase.h b/engines/pegasus/neighborhood/mars/canyonchase.h
new file mode 100644
index 0000000000..489fca0697
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/canyonchase.h
@@ -0,0 +1,108 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_CANYONCHASE_H
+#define PEGASUS_NEIGHBORHOOD_MARS_CANYONCHASE_H
+
+#include "pegasus/chase.h"
+#include "pegasus/fader.h"
+#include "pegasus/movie.h"
+#include "pegasus/sound.h"
+
+namespace Pegasus {
+
+class CanyonChase;
+class Mars;
+
+enum MusicTimerCode {
+	kCanyonRanIntoWall,
+	kCanyonExited,
+	kCanyonFaded
+};
+
+struct MusicTimerEvent {
+	CanyonChase *canyonChase;
+	MusicTimerCode theEvent;
+
+	void fire();
+};
+
+class CanyonChase : public ChaseInteraction {
+friend class Mars;
+friend struct MusicTimerEvent;
+public:
+
+	CanyonChase(Neighborhood *);
+	virtual ~CanyonChase() {}
+
+	void setSoundFXLevel(const uint16);
+	void setAmbienceLevel(const uint16);
+
+protected:
+
+	void startCanyonMusicLoop();
+	void stopCanyonMusicLoop(const long);
+
+	void openInteraction();
+	void initInteraction();
+	void closeInteraction();
+
+	void receiveNotification(Notification *, const NotificationFlags);
+
+	void setUpBranch();
+	void branchLeft();
+	void branchRight();
+	void dontBranch();
+
+	void showControlsHint();
+	void hideControlsHint();
+
+	void switchTo(Movie &, NotificationCallBack &);
+	void startMusicTimer(TimeValue, TimeScale, MusicTimerCode);
+	void musicTimerExpired(MusicTimerEvent &);
+	void doGenoChase();
+
+	Movie _canyonMovie1;
+	Movie _canyonMovie2;
+	Movie _deathMovie;
+	Movie _genoMovie;
+	NotificationCallBack _canyon1CallBack;
+	NotificationCallBack _canyon2CallBack;
+	NotificationCallBack _deathCallBack;
+	NotificationCallBack _genoCallBack;
+	Sound _musicLoop;
+	SoundFader _musicFader;
+	FuseFunction _musicFuse;
+
+	MusicTimerEvent _musicEvent;
+
+	Movie *_currentMovie;
+	NotificationCallBack *_currentCallBack;
+	short _canyonState;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/mars/constants.h b/engines/pegasus/neighborhood/mars/constants.h
index 0a0ec521c8..528a3b8514 100644
--- a/engines/pegasus/neighborhood/mars/constants.h
+++ b/engines/pegasus/neighborhood/mars/constants.h
@@ -32,6 +32,12 @@ namespace Pegasus {
 
 // Element Coordinates
 
+static const CoordType kPodScreenLeft = kNavAreaLeft + 88;
+static const CoordType kPodScreenTop = kNavAreaTop + 204;
+
+static const CoordType kPodSteerLeft = kNavAreaLeft + 212;
+static const CoordType kPodSteerTop = kNavAreaTop + 232;
+
 static const CoordType kUndoHiliteLeft = kNavAreaLeft + 140;
 static const CoordType kUndoHiliteTop = kNavAreaTop + 36;
 
@@ -99,6 +105,9 @@ static const CoordType kShuttleEnergyTop = 60;
 static const CoordType kShuttleEnergyWidth = 252;
 static const CoordType kShuttleEnergyHeight = 22;
 
+static const CoordType kShuttleSteerLeft = kShuttleWindowLeft + 136;
+static const CoordType kShuttleSteerTop = kShuttleWindowTop + 196;
+
 static const CoordType kPlanetStartLeft = kShuttleWindowLeft;
 static const CoordType kPlanetStartTop = kShuttleWindowTop + kShuttleWindowHeight;
 
@@ -130,11 +139,21 @@ static const TimeValue kShuttleSwingStart = 0;
 static const TimeValue kShuttleSwingStop = 5 * 600;
 
 static const TimeValue kCanyonChaseStart = kShuttleSwingStop;
-static const TimeValue kCanyonChaseStop = 60 * 600 + 43 * 600 + 14 * 40;
-
-static const TimeValue kLaunchTubeReachedTime = 60 * 600 + 38 * 600 - kCanyonChaseStart;
-static const TimeValue kCanyonChaseFinishedTime = kCanyonChaseStop - kCanyonChaseStart -
-											kLaunchTubeReachedTime;
+static const TimeValue kCanyonChaseCDStop = 60 * 600 + 43 * 600 + 14 * 40;
+static const TimeValue kCanyonChaseDVDStop = 60 * 600 + 50 * 600 + 12 * 40;
+
+static const TimeValue kCanyonChaseExitedTime = 60 * 600 + 40 * 600 + 13 * 40 - kCanyonChaseStart;
+static const TimeValue kCanyonChaseFadedTime = 60 * 600 + 43 * 600 + 6 * 40 - kCanyonChaseStart -
+											kCanyonChaseExitedTime;
+
+static const TimeValue kLaunchTubeCDReachedTime = 60 * 600 + 38 * 600 - kCanyonChaseStart;
+static const TimeValue kLaunchTubeDVDReachedTime = 60 * 600 + 45 * 600 - kCanyonChaseStart -
+											kCanyonChaseExitedTime - kCanyonChaseFadedTime;
+static const TimeValue kCanyonChaseCDFinishedTime = kCanyonChaseCDStop - kCanyonChaseStart -
+											kLaunchTubeCDReachedTime;
+static const TimeValue kCanyonChaseDVDFinishedTime = kCanyonChaseDVDStop - kCanyonChaseStart -
+											kCanyonChaseExitedTime - kCanyonChaseFadedTime -
+											kLaunchTubeDVDReachedTime;
 
 // Left shuttle.
 
@@ -183,6 +202,8 @@ static const TimeValue kShuttleLowerLeftTubeTime = 40;
 
 static const TimeValue kShuttleLowerLeftAutopilotTime = 80;
 
+static const TimeValue kShuttleLowerLeftKeypadHintTime = 120;
+
 // Lower Right shuttle.
 
 static const TimeValue kShuttleLowerRightOffTime = 0;
@@ -929,6 +950,11 @@ static const ExtraID kMarsMaze184WestLoop = 109;
 static const ExtraID kMarsMaze184WestDeath = 110;
 static const ExtraID kMars200DeathInBucket = 111;
 
+// Mars interactions.
+
+static const InteractionID kMarsTunnelPodInteractionID = 0;
+static const InteractionID kMarsCanyonChaseInteractionID = 1;
+
 static const ResIDType kReactorUndoHilitePICTID = 900;
 
 static const int16 kMars52Compass = 90;
diff --git a/engines/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp
index 2350be1a17..8f5104ff4c 100644
--- a/engines/pegasus/neighborhood/mars/mars.cpp
+++ b/engines/pegasus/neighborhood/mars/mars.cpp
@@ -25,16 +25,20 @@
 
 #include "common/events.h"
 #include "video/qt_decoder.h"
+#include "video/theora_decoder.h"
 
 #include "pegasus/cursor.h"
 #include "pegasus/energymonitor.h"
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/opticalchip.h"
 #include "pegasus/items/biochips/shieldchip.h"
 #include "pegasus/items/inventory/airmask.h"
+#include "pegasus/neighborhood/mars/canyonchase.h"
 #include "pegasus/neighborhood/mars/mars.h"
+#include "pegasus/neighborhood/mars/tunnelpod.h"
 
 namespace Pegasus {
 
@@ -44,6 +48,10 @@ static const int16 kMarsShieldPanelOffsetAngle = 22;
 
 static const CanMoveForwardReason kCantMoveRobotBlocking = kCantMoveLastReason + 1;
 
+static const ExtraID kMarsTurnOnSteerPod = 1000;
+static const ExtraID kMarsRobotBobSlow = 1001;
+static const ExtraID kMarsRobotGenoSlow = 1002;
+
 static const NotificationFlags kTimeForCanyonChaseFlag = kLastNeighborhoodNotificationFlag << 1;
 static const NotificationFlags kExplosionFinishedFlag = kTimeForCanyonChaseFlag << 1;
 static const NotificationFlags kTimeToTransportFlag = kExplosionFinishedFlag << 1;
@@ -52,6 +60,13 @@ static const NotificationFlags kMarsNotificationFlags = kTimeForCanyonChaseFlag
 													kExplosionFinishedFlag |
 													kTimeToTransportFlag;
 
+static const TimeValue kBucketClimbInTime = 1393980;
+static const TimeValue kBucketSeeGearRoomTime = 2240;
+static const TimeValue kBucketClimbOutTime = 1340;
+
+static const TimeValue kPodCautionDisplayedTime = 1631;
+static const TimeValue kPodCautionDismissedTime = 3889;
+
 static const TimeValue kLittleExplosionStart = 0 * 40;
 static const TimeValue kLittleExplosionStop = 24 * 40;
 
@@ -90,7 +105,7 @@ void MarsTimerEvent::fire() {
 }
 
 Mars::Mars(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Mars", kMarsID),
-		_guessObject(kNoDisplayElement), _undoPict(kNoDisplayElement), _guessHistory(kNoDisplayElement),
+		_extraMovie(kNoDisplayElement), _guessObject(kNoDisplayElement), _undoPict(kNoDisplayElement), _guessHistory(kNoDisplayElement),
 		_choiceHighlight(kNoDisplayElement), _shuttleInterface1(kNoDisplayElement), _shuttleInterface2(kNoDisplayElement),
 		_shuttleInterface3(kNoDisplayElement), _shuttleInterface4(kNoDisplayElement), _canyonChaseMovie(kNoDisplayElement),
 		_leftShuttleMovie(kNoDisplayElement), _rightShuttleMovie(kNoDisplayElement), _lowerLeftShuttleMovie(kNoDisplayElement),
@@ -134,12 +149,25 @@ void Mars::init() {
 
 	_neighborhoodNotification.notifyMe(this, kMarsNotificationFlags, kMarsNotificationFlags);
 
+	_extraMovieCallBack.setNotification(&_neighborhoodNotification);
+
 	_explosionCallBack.setNotification(&_neighborhoodNotification);
 	_explosionCallBack.setCallBackFlag(kExplosionFinishedFlag);
 
 	_weaponSelection = kNoWeapon;
 }
 
+GameInteraction *Mars::makeInteraction(const InteractionID interactionID) {
+	switch (interactionID) {
+	case kMarsTunnelPodInteractionID:
+		return new TunnelPod(this);
+	case kMarsCanyonChaseInteractionID:
+		return new CanyonChase(this);
+	default:
+		return NULL;
+	}
+}
+
 void Mars::flushGameState() {
 	g_energyMonitor->saveCurrentEnergyValue();
 }
@@ -152,6 +180,71 @@ void Mars::start() {
 	Neighborhood::start();
 }
 
+class ArthurOxygen50Action : public AIPlayMessageAction {
+public:
+	ArthurOxygen50Action();
+
+	virtual void performAIAction(AIRule *);
+};
+
+ArthurOxygen50Action::ArthurOxygen50Action() : AIPlayMessageAction("Images/AI/Mars/XMMAZB1", false, kWarningInterruption) {
+}
+
+void ArthurOxygen50Action::performAIAction(AIRule *rule) {
+	PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+	if (GameState.isTakenItemID(kArthurBiochip) && g_arthurChip && vm->isChattyArthur())
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA84", kArthurMarsOxygen50Warning);
+	else
+		AIPlayMessageAction::performAIAction(rule);
+}
+
+class ArthurOxygen25Action : public AIPlayMessageAction {
+public:
+	ArthurOxygen25Action();
+
+	virtual void performAIAction(AIRule *);
+};
+
+ArthurOxygen25Action::ArthurOxygen25Action() : AIPlayMessageAction("Images/AI/Mars/XMMAZB2", false, kWarningInterruption) {
+}
+
+void ArthurOxygen25Action::performAIAction(AIRule *rule) {
+	PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+	if (GameState.isTakenItemID(kArthurBiochip) && g_arthurChip && vm->isChattyArthur()) {
+		if (vm->getRandomBit())
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA85", kArthurMarsOxygen25Warning);
+		else
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA87", kArthurMarsOxygen25Warning);
+	} else {
+		AIPlayMessageAction::performAIAction(rule);
+	}
+}
+
+class ArthurOxygen5Action : public AIPlayMessageAction {
+public:
+	ArthurOxygen5Action();
+
+	virtual void performAIAction(AIRule *);
+};
+
+ArthurOxygen5Action::ArthurOxygen5Action() : AIPlayMessageAction("Images/AI/Mars/XMMAZB3", false, kWarningInterruption) {
+}
+
+void ArthurOxygen5Action::performAIAction(AIRule *rule) {
+	PegasusEngine *vm = (PegasusEngine *)g_engine;
+
+	if (GameState.isTakenItemID(kArthurBiochip) && g_arthurChip && vm->isChattyArthur()) {
+		if (vm->getRandomBit())
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA86", kArthurMarsOxygen5Warning);
+		else
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA88", kArthurMarsOxygen5Warning);
+	} else {
+		AIPlayMessageAction::performAIAction(rule);
+	}
+}
+
 class AirMaskCondition : public AICondition {
 public:
 	AirMaskCondition(const uint32);
@@ -218,12 +311,18 @@ void Mars::setUpAIRules() {
 		g_AIArea->addAIRule(rule);
 
 		AirMaskCondition *airMask50Condition = new AirMaskCondition(50);
-		messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB1", false);
+		if (_vm->isDVD())
+			messageAction = new ArthurOxygen50Action();
+		else
+			messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB1", false);
 		AIRule *rule50 = new AIRule(airMask50Condition, messageAction);
 
 		AirMaskCondition *airMask25Condition = new AirMaskCondition(25);
 		AICompoundAction *compound = new AICompoundAction();
-		messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB2", false);
+		if (_vm->isDVD())
+			messageAction = new ArthurOxygen25Action();
+		else
+			messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB2", false);
 		compound->addAction(messageAction);
 		deactivate = new AIDeactivateRuleAction(rule50);
 		compound->addAction(deactivate);
@@ -231,7 +330,10 @@ void Mars::setUpAIRules() {
 
 		AirMaskCondition *airMask5Condition = new AirMaskCondition(5);
 		compound = new AICompoundAction;
-		messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB3", false);
+		if (_vm->isDVD())
+			messageAction = new ArthurOxygen5Action();
+		else
+			messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB3", false);
 		compound->addAction(messageAction);
 		deactivate = new AIDeactivateRuleAction(rule50);
 		compound->addAction(deactivate);
@@ -412,8 +514,155 @@ void Mars::cantMoveThatWay(CanMoveForwardReason reason) {
 }
 
 void Mars::moveForward() {
-	if (GameState.getCurrentRoom() == kMars02 || (GameState.getCurrentRoom() >= kMars05 && GameState.getCurrentRoom() <= kMars08))
+	if (GameState.getCurrentRoom() == kMars02 || (GameState.getCurrentRoom() >= kMars05 && GameState.getCurrentRoom() <= kMars08)) {
 		loadLoopSound2("");
+	} else if (_vm->isDVD()) {
+		Movie movie(kNoDisplayElement);
+		Input input;
+
+		if (!GameState.isTakenItemID(kCardBomb) &&
+			GameState.getCurrentRoom() == kMars60 &&
+			GameState.getCurrentDirection() == kWest) {
+			loadLoopSound1("");
+			loadLoopSound2("");
+			movie.initFromMovieFile("Images/Mars/MMbomb.mov");
+			movie.setVolume(_vm->getSoundFXLevel());
+			movie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+			movie.setDisplayOrder(kNavMovieOrder + 1);
+			movie.startDisplaying();
+			movie.show();
+			movie.start();
+
+			while (movie.isRunning() && !_vm->shouldQuit()) {
+				InputDevice.getInput(input, kFilterNoInput);
+
+				_vm->checkCallBacks();
+				_vm->refreshDisplay();
+				_vm->_system->delayMillis(10);
+			}
+
+			if (_vm->shouldQuit())
+				return;
+
+			movie.moveElementTo(0, 0);
+			_vm->_gfx->setCurSurface(_navMovie.getSurface());
+			movie.copyToCurrentPort();
+			_vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea());
+
+			movie.hide();
+			movie.stopDisplaying();
+			movie.releaseMovie();
+
+			didntFindBomb();
+			return;
+		} else if (!GameState.getWalkthroughMode() &&
+					GameState.getCurrentRoom() == kMarsMaze200 &&
+					GameState.getCurrentDirection() == kWest) {
+			ExitTable::Entry exitEntry;
+			Common::Rect pushBounds;
+			bool leavingBucket;
+
+			// Fall down the shaft immediately if we miss the climb-in
+			if (_navMovie.getTime() >= kBucketClimbInTime) {
+				_navMovie.stop();
+
+				movie.initFromMovieFile("Images/Mars/MMfall.mov");
+				movie.setVolume(_vm->getSoundFXLevel());
+				movie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+				movie.setDisplayOrder(kNavMovieOrder + 1);
+				movie.startDisplaying();
+				movie.show();
+				movie.start();
+
+				while (movie.isRunning() && !_vm->shouldQuit()) {
+					InputDevice.getInput(input, kFilterNoInput);
+
+					_vm->checkCallBacks();
+					_vm->refreshDisplay();
+					_vm->_system->delayMillis(10);
+				}
+
+				if (_vm->shouldQuit())
+					return;
+
+				movie.moveElementTo(0, 0);
+				_vm->_gfx->setCurSurface(_navMovie.getSurface());
+				movie.copyToCurrentPort();
+				_vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea());
+
+				movie.hide();
+				movie.stopDisplaying();
+				movie.releaseMovie();
+
+				die(kDeathMissedOreBucket);
+				return;
+			}
+
+			canMoveForward(exitEntry);
+
+			leavingBucket = false;
+			_navMovie.stop();
+			_turnPush.getBounds(pushBounds);
+			_navMovie.moveElementTo(pushBounds.left, pushBounds.top);
+			_navMovie.show();
+			_navMovie.setFlags(0);
+
+			// Set segment so we can skip ahead as needed
+			// Ride up the shaft but don't try to get out
+			_navMovie.setSegment(exitEntry.movieStart, exitEntry.movieEnd - kBucketClimbOutTime);
+			_navMovie.setTime(exitEntry.movieStart);
+			_navMovie.start();
+			while (_navMovie.isRunning() && _navMovie.getTime() < exitEntry.movieEnd - kBucketSeeGearRoomTime) {
+				InputDevice.getInput(input, kFilterAllDirections);
+
+				if (input.upButtonDown() ||
+					input.downButtonDown() ||
+					input.leftButtonDown() ||
+					input.rightButtonDown()) {
+					_navMovie.stop();
+					_vm->getDeathSound().initFromAIFFFile("Sounds/Mars/Mars Maze Fall.AIFF");
+					_vm->getDeathSound().setVolume(_vm->getSoundFXLevel());
+					_vm->getDeathSound().playSound();
+					die(kDeathMissedOreBucket);
+					return;
+				}
+
+				_vm->checkCallBacks();
+				_vm->refreshDisplay();
+				g_system->delayMillis(10);
+			}
+
+			// We can see the gear room at this point so it's safe to try to get out
+			_navMovie.setTime(exitEntry.movieEnd - kBucketSeeGearRoomTime);
+			while (_navMovie.isRunning()) {
+				InputDevice.getInput(input, kFilterAllDirections);
+
+				if (input.upButtonAnyDown()) {
+					leavingBucket = true;
+				} else if (input.anyDirectionInput()) {
+					_navMovie.stop();
+					_vm->getDeathSound().initFromAIFFFile("Sounds/Mars/Mars Maze Fall.AIFF");
+					_vm->getDeathSound().setVolume(_vm->getSoundFXLevel());
+					_vm->getDeathSound().playSound();
+					die(kDeathMissedOreBucket);
+					return;
+				}
+
+				_vm->checkCallBacks();
+				_vm->refreshDisplay();
+				g_system->delayMillis(10);
+			}
+			_navMovie.stop();
+
+			if (leavingBucket) {
+				exitEntry.movieStart = exitEntry.movieEnd - kBucketClimbOutTime;
+				startExitMovie(exitEntry);
+			} else {
+				playDeathExtra(kMars200DeathInBucket, kDeathDidntLeaveBucket);
+			}
+			return;
+		}
+	}
 
 	Neighborhood::moveForward();
 }
@@ -735,6 +984,88 @@ void Mars::checkAirlockDoors() {
 	}
 }
 
+void Mars::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) {
+	Movie movie(kNoDisplayElement);
+	Input input;
+
+	if (_vm->isDVD() && GameState.getCurrentRoomAndView() == MakeRoomView(kMars18, kNorth) &&
+		GameState.getMarsPodAtUpperPlatform()) {
+		movie.initFromMovieFile("Images/Mars/M45DF.movie");
+		movie.setVolume(_vm->getSoundFXLevel());
+		movie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+		movie.setDisplayOrder(kNavMovieOrder + 1);
+		movie.startDisplaying();
+		movie.show();
+		movie.start();
+
+		while (movie.isRunning() && !_vm->shouldQuit()) {
+			InputDevice.getInput(input, kFilterNoInput);
+
+			_vm->checkCallBacks();
+			_vm->refreshDisplay();
+			_vm->_system->delayMillis(10);
+		}
+
+		if (_vm->shouldQuit())
+			return;
+
+		movie.moveElementTo(0, 0);
+		_vm->_gfx->setCurSurface(_navMovie.getSurface());
+		movie.copyToCurrentPort();
+		_vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea());
+
+		movie.hide();
+		movie.stopDisplaying();
+		movie.releaseMovie();
+
+		Neighborhood::doorOpened();
+	} else {
+		Neighborhood::startDoorOpenMovie(startTime, stopTime);
+	}
+}
+
+void Mars::startExitMovie(const ExitTable::Entry &exitEntry) {
+	Movie movie(kNoDisplayElement);
+	Input input;
+
+	if (_vm->isDVD() && GameState.getCurrentRoomAndView() == MakeRoomView(kMars18, kNorth) &&
+		GameState.getMarsPodAtUpperPlatform()) {
+		movie.initFromMovieFile("Images/Mars/M45DG.movie");
+		movie.setVolume(_vm->getSoundFXLevel());
+		movie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+		movie.setDisplayOrder(kNavMovieOrder + 1);
+		movie.startDisplaying();
+		movie.show();
+		movie.start();
+
+		while (movie.isRunning() && !_vm->shouldQuit()) {
+			InputDevice.getInput(input, kFilterNoInput);
+
+			_vm->checkCallBacks();
+			_vm->refreshDisplay();
+			_vm->_system->delayMillis(10);
+		}
+
+		if (_vm->shouldQuit())
+			return;
+
+		movie.moveElementTo(0, 0);
+		_vm->_gfx->setCurSurface(_navMovie.getSurface());
+		movie.copyToCurrentPort();
+		_vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea());
+
+		movie.hide();
+		movie.stopDisplaying();
+		movie.releaseMovie();
+
+		arriveAt(kMars32, kNorth);
+	} else {
+		Neighborhood::startExitMovie(exitEntry);
+	}
+	if (GameState.getCurrentRoomAndView() == MakeRoomView(kMars43, kEast) && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA07", kArthurMarsEnteredShuttle);
+}
+
 int16 Mars::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
 	int16 angle = Neighborhood::getStaticCompassAngle(room, dir);
 
@@ -906,33 +1237,78 @@ void Mars::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &co
 }
 
 void Mars::loadAmbientLoops() {
+	// Don't load an ambient loop if we died in a chase sequence
+	if (_vm->getEnergyDeathReason() == kDeathRanIntoCanyonWall || _vm->getEnergyDeathReason() == kDeathCollidedWithPod)
+		return;
+
 	RoomID room = GameState.getCurrentRoom();
 
 	if ((room >= kMars0A && room <= kMars21) || (room >= kMars41 && room <= kMars43)) {
-		if (GameState.getMarsSeenTimeStream())
-			loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF");
+		if (GameState.getMarsSeenTimeStream()) {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/Mars/Gantry Ambient.32K.16.AIFF");
+			else
+				loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF");
+		}
 	} else if (room >= kMars22 && room <= kMars31South) {
-		loadLoopSound1("Sounds/Mars/Reception.02.22K.8.AIFF", 0x100 / 4);
+		if (_vm->isDVD()) // Updated for the DVD version
+			loadLoopSound1("Sounds/Mars/Reception.02.32K.16.AIFF", 0x100 / 4);
+		else
+			loadLoopSound1("Sounds/Mars/Reception.02.22K.8.AIFF", 0x100 / 4);
 	} else if (room >= kMars32 && room <= kMars34) {
-		loadLoopSound1("Sounds/Mars/Pod Room Ambient.22K.8.AIFF");
-	} else if (room == kMars35) {
-		if (getAirQuality(room) == kAirQualityVacuum)
-			loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF");
+		if (_vm->isDVD()) // Updated for the DVD version
+			loadLoopSound1("Sounds/Mars/Pod Room Ambient.32K.16.AIFF");
 		else
-			loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF", 0x100 / 2);
+			loadLoopSound1("Sounds/Mars/Pod Room Ambient.22K.8.AIFF");
+	} else if (room == kMars35) {
+		if (getAirQuality(room) == kAirQualityVacuum) {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/Mars/Gear Room Ambient.44K.16.AIFF");
+			else
+				loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF");
+		} else {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/Mars/Gantry Ambient.32K.16.AIFF", 0x100 / 2);
+			else
+				loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF", 0x100 / 2);
+		}
 	} else if (room >= kMars36 && room <= kMars39) {
-		loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF");
+		if (_vm->isDVD()) // Updated for the DVD version
+			loadLoopSound1("Sounds/Mars/Gear Room Ambient.44K.16.AIFF");
+		else
+			loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF");
 	} else if (room >= kMars45 && room <= kMars51) {
-		loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF");
+		if (_vm->isDVD()) // Updated for the DVD version
+			loadLoopSound1("Sounds/Mars/Lower Mars Ambient.32K.16.AIFF");
+		else
+			loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF");
 	} else if (room >= kMars52 && room <= kMars58) {
-		loadLoopSound1("Sounds/Mars/ReactorLoop.22K.8.AIFF");
-	} else if (room == kMars60) {
-		if (getAirQuality(room) == kAirQualityVacuum)
-			loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF");
+		if (_vm->isDVD()) // Updated for the DVD version
+			loadLoopSound1("Sounds/Mars/ReactorLoop.44K.16.AIFF");
 		else
-			loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF", 0x100 / 2);
+			loadLoopSound1("Sounds/Mars/ReactorLoop.22K.8.AIFF");
+	} else if (room == kMars60) {
+		if (getAirQuality(room) == kAirQualityVacuum) {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/Mars/Mars Maze Ambient.32K.16.AIFF");
+			else
+				loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF");
+		} else {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/Mars/Lower Mars Ambient.32K.16.AIFF", 0x100 / 2);
+			else
+				loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF", 0x100 / 2);
+		}
 	} else if (room >= kMarsMaze004 && room <= kMarsMaze200) {
-		loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF");
+		if (_vm->isDVD()) { // Updated for the DVD version
+			if (GameState.getEasterEgg() || room == kMarsMaze200 ||
+				(room == kMarsMaze199 && GameState.getCurrentDirection() == kWest))
+				loadLoopSound1("Sounds/Mars/Mars Maze Ambient.32K.16.AIFF");
+			else
+				loadLoopSound1("Sounds/Mars/Mars Maze GenoLoop.32K.16.AIFF");
+		} else {
+			loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF");
+		}
 	} else if (room == kMarsRobotShuttle) {
 		loadLoopSound1("Sounds/Mars/Robot Shuttle.22K.8.AIFF");
 	}
@@ -1163,6 +1539,9 @@ void Mars::timerExpired(const uint32 eventType) {
 }
 
 void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
+	Input input;
+	InventoryItem *item;
+
 	switch (MakeRoomView(room, direction)) {
 	case MakeRoomView(kMars18, kNorth):
 		if (GameState.getMarsPodAtUpperPlatform())
@@ -1175,6 +1554,14 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 		else
 			setCurrentAlternate(kAltMarsNormal);
 		break;
+	case MakeRoomView(kMars31South, kSouth):
+		if (!GameState.isTakenItemID(kMarsCard) && g_arthurChip) {
+			if (_vm->getRandomBit())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA05", kArthurMarsZoomedToKeyCard);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA96", kArthurMarsZoomedToKeyCard);
+		}
+		break;
 	case MakeRoomView(kMars35, kEast):
 	case MakeRoomView(kMars35, kWest):
 		if (GameState.getMarsAirlockOpen())
@@ -1188,6 +1575,8 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 			setCurrentAlternate(kAltMars60AirlockEast);
 		else
 			setCurrentAlternate(kAltMars60AirlockWest);
+		if (!(g_airMask && g_airMask->getAirLeft() > 0) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA39", kArthurMarsInAirlockNoOxygen);
 		break;
 	case MakeRoomView(kMars45, kNorth):
 	case MakeRoomView(kMars45, kSouth):
@@ -1208,6 +1597,12 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 			setCurrentAlternate(kAltMarsNormal);
 		else
 			setCurrentAlternate(kAltMarsPodAtMars45);
+		if (g_arthurChip && room == kMars46) {
+			if (direction == kEast && !GameState.isTakenItemID(kCrowbar))
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA34", kArthurMarsLeftPodNoCrowBar);
+			else if (direction == kWest && GameState.getMarsPodAtUpperPlatform())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA82", kArthurMarsLookAtEmptyTracks);
+		}
 		break;
 	case MakeRoomView(kMars48, kNorth):
 	case MakeRoomView(kMars48, kSouth):
@@ -1229,6 +1624,16 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 		else
 			setCurrentAlternate(kAltMarsNormal);
 		break;
+	case MakeRoomView(kMarsMaze004, kWest):
+		if (_vm->isDVD() && GameState.getCurrentRoom() == kMars60) {
+			InputDevice.getInput(input, kFilterAllInput);
+			// Check easter egg modifier for the Geno mix
+			if (JMPPPInput::isEasterEggModifierInput(input))
+				GameState.setEasterEgg(true);
+			else
+				GameState.setEasterEgg(false);
+		}
+		break;
 	default:
 		break;
 	}
@@ -1242,6 +1647,11 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 		if (!GameState.getMarsSeenTimeStream())
 			startExtraLongSequence(kMarsArrivalFromTSA, kMars0AWatchShuttleDepart, kExtraCompletedFlag, kFilterNoInput);
 		break;
+	case MakeRoomView(kMars07, kNorth):
+		item = (InventoryItem *)g_allItems.findItemByID(kNitrogenCanister);
+		if (((g_airMask && g_airMask->getAirLeft() > 0) || item->getItemState() == kNitrogenFull) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA24", kArthurMarsSawWelcomeVideos);
+		break;
 	case MakeRoomView(kMars07, kSouth):
 	case MakeRoomView(kMars13, kNorth):
 		if (!GameState.getMarsHeardCheckInMessage()) {
@@ -1249,6 +1659,11 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 			GameState.setMarsHeardCheckInMessage(true);
 		}
 		break;
+	case MakeRoomView(kMars41, kEast):
+	case MakeRoomView(kMars42, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA73", kArthurMarsFoundNoShuttlePresent);
+		break;
 	case MakeRoomView(kMars44, kWest):
 		if (GameState.getMarsReadyForShuttleTransport())
 			startUpFromFinishedSpaceChase();
@@ -1264,6 +1679,8 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 	case MakeRoomView(kMars11, kSouth):
 	case MakeRoomView(kMars12, kSouth):
 		setCurrentActivation(kActivationReadyForKiosk);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA33", kArthurMarsReadyForKiosk);
 		break;
 	case MakeRoomView(kMars15, kWest):
 		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) {
@@ -1288,8 +1705,11 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 	case MakeRoomView(kMars19, kNorth):
 	case MakeRoomView(kMars19, kSouth):
 	case MakeRoomView(kMars19, kWest):
-		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave())
+		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) {
 			forceStridingStop(kMars19, kWest, kAltMarsNormal);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA38", kArthurMarsExitedGearRoom);
+		}
 
 		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown())
 			forceStridingStop(kMars17, kWest, kAltMarsNormal);
@@ -1340,8 +1760,24 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 			setCurrentActivation(kActivateReadyToPressurizeAirlock);
 		break;
 	case MakeRoomView(kMars39, kWest):
-		if (GameState.getLastRoom() == kMarsMaze200)
+		if (GameState.getLastRoom() == kMarsMaze200) {
 			GameState.setMarsPodAtUpperPlatform(false);
+			if (_vm->isDVD())
+				GameState.setEasterEgg(false);
+		}
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB40", kArthurMarsEnteredGearRoom);
+		break;
+	case MakeRoomView(kMars39, kNorth):
+	case MakeRoomView(kMars39, kSouth):
+	case MakeRoomView(kMars38, kNorth):
+	case MakeRoomView(kMars38, kSouth):
+	case MakeRoomView(kMars37, kNorth):
+	case MakeRoomView(kMars37, kSouth):
+	case MakeRoomView(kMars36, kNorth):
+	case MakeRoomView(kMars36, kSouth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA83", kArthurMarsLookAtGears);
 		break;
 	case MakeRoomView(kMars45, kSouth):
 		// Set up maze doors here.
@@ -1357,7 +1793,19 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 		if (!GameState.getMarsSeenRobotAtReactor()) {
 			// Preload the looping sound...
 			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0);
-			startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+			if (!_vm->isDVD()) {
+				startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+			} else {
+				InputDevice.getInput(input, kFilterAllInput);
+				if (JMPPPInput::isEasterEggModifierInput(input)) {
+					if (_vm->getRandomBit())
+						startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+					else
+						startExtraSequence(kMarsRobotBobSlow, kExtraCompletedFlag, kFilterNoInput);
+				} else {
+					startExtraSequence(kMarsRobotGenoSlow, kExtraCompletedFlag, kFilterNoInput);
+				}
+			}
 		} else if (!GameState.getMarsAvoidedReactorRobot()) {
 			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0);
 			loopExtraSequence(kMars48RobotLoops);
@@ -1397,10 +1845,14 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 			g_shield->setItemState(kShieldNormal);
 		g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal);
 		_vm->resetEnergyDeathReason();
+		if (GameState.isTakenItemID(kCardBomb) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA78", kArthurMarsExitedReactorWithCardBomb);
 		break;
+	case MakeRoomView(kMars52, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA11", kArthurMarsEnteredReactor);
 	case MakeRoomView(kMars52, kNorth):
 	case MakeRoomView(kMars52, kSouth):
-	case MakeRoomView(kMars52, kEast):
 	case MakeRoomView(kMars52, kWest):
 	case MakeRoomView(kMars54, kNorth):
 	case MakeRoomView(kMars54, kSouth):
@@ -1442,6 +1894,10 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 	case MakeRoomView(kMarsMaze007, kNorth):
 		launchMaze007Robot();
 		break;
+	case MakeRoomView(kMarsMaze009, kWest):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA20", kArthurMarsMazeReachedJunction);
+		break;
 	case MakeRoomView(kMarsMaze015, kSouth):
 		launchMaze015Robot();
 		break;
@@ -1464,6 +1920,18 @@ void Mars::arriveAt(const RoomID room, const DirectionConstant direction) {
 		GameState.setScoringThreadedMaze();
 		GameState.setMarsThreadedMaze(true);
 		break;
+	case MakeRoomView(kMarsMaze199, kWest):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA77", kArthurMarsFoundBuckets);
+		break;
+	case MakeRoomView(kMarsMaze200, kWest):
+		if (g_arthurChip) {
+			if (_vm->getRandomBit())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA10", kArthurMarsApproachedBuckets);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA76", kArthurMarsApproachedBuckets);
+		}
+		break;
 	case MakeRoomView(kMarsDeathRoom, kNorth):
 	case MakeRoomView(kMarsDeathRoom, kSouth):
 	case MakeRoomView(kMarsDeathRoom, kEast):
@@ -1495,6 +1963,8 @@ void Mars::shieldOff() {
 }
 
 void Mars::turnTo(const DirectionConstant direction) {
+	Input input;
+
 	switch (MakeRoomView(GameState.getCurrentRoom(), direction)) {
 	case MakeRoomView(kMars27, kNorth):
 	case MakeRoomView(kMars27, kSouth):
@@ -1522,6 +1992,8 @@ void Mars::turnTo(const DirectionConstant direction) {
 	case MakeRoomView(kMars11, kSouth):
 	case MakeRoomView(kMars12, kSouth):
 		setCurrentActivation(kActivationReadyForKiosk);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA33", kArthurMarsReadyForKiosk);
 		break;
 	case MakeRoomView(kMars18, kNorth):
 		if (GameState.getMarsPodAtUpperPlatform())
@@ -1533,6 +2005,11 @@ void Mars::turnTo(const DirectionConstant direction) {
 			GameState.setMarsHeardCheckInMessage(true);
 		}
 		break;
+	case MakeRoomView(kMars41, kEast):
+	case MakeRoomView(kMars42, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA73", kArthurMarsFoundNoShuttlePresent);
+		break;
 	case MakeRoomView(kMars34, kSouth):
 	case MakeRoomView(kMars45, kNorth):
 		setCurrentActivation(kActivateMarsPodClosed);
@@ -1558,6 +2035,21 @@ void Mars::turnTo(const DirectionConstant direction) {
 		if (GameState.getMarsThreadedMaze())
 			GameState.setScoringThreadedGearRoom();
 		break;
+	case MakeRoomView(kMars39, kNorth):
+	case MakeRoomView(kMars39, kSouth):
+	case MakeRoomView(kMars38, kNorth):
+	case MakeRoomView(kMars38, kSouth):
+	case MakeRoomView(kMars37, kNorth):
+	case MakeRoomView(kMars37, kSouth):
+	case MakeRoomView(kMars36, kNorth):
+	case MakeRoomView(kMars36, kSouth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA83", kArthurMarsLookAtGears);
+		break;
+	case MakeRoomView(kMars46, kWest):
+		if (GameState.getMarsPodAtUpperPlatform() && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA82", kArthurMarsLookAtEmptyTracks);
+		break;
 	case MakeRoomView(kMars48, kNorth):
 		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot())
 			die(kDeathDidntGetOutOfWay);
@@ -1566,7 +2058,19 @@ void Mars::turnTo(const DirectionConstant direction) {
 		if (!GameState.getMarsSeenRobotAtReactor()) {
 			// Preload the looping sound...
 			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0);
-			startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+			if (!_vm->isDVD()) {
+				startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+			} else {
+				InputDevice.getInput(input, kFilterAllInput);
+				if (JMPPPInput::isEasterEggModifierInput(input)) {
+					if (_vm->getRandomBit())
+						startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput);
+					else
+						startExtraSequence(kMarsRobotBobSlow, kExtraCompletedFlag, kFilterNoInput);
+				} else {
+					startExtraSequence(kMarsRobotGenoSlow, kExtraCompletedFlag, kFilterNoInput);
+				}
+			}
 		} else if (!GameState.getMarsAvoidedReactorRobot()) {
 			loopExtraSequence(kMars48RobotLoops);
 		} else if (GameState.isTakenItemID(kAirMask)) {
@@ -1582,6 +2086,8 @@ void Mars::turnTo(const DirectionConstant direction) {
 			setCurrentAlternate(kAltMarsTookMask);
 		else
 			setCurrentAlternate(kAltMarsNormal);
+		if (GameState.isTakenItemID(kCardBomb) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA78", kArthurMarsExitedReactorWithCardBomb);
 		break;
 	case MakeRoomView(kMars49, kSouth):
 		if (GameState.isTakenItemID(kAirMask))
@@ -1589,6 +2095,11 @@ void Mars::turnTo(const DirectionConstant direction) {
 		else
 			setCurrentActivation(kActivateMaskOnHolder);
 		break;
+	case MakeRoomView(kMars51, kWest):
+	case MakeRoomView(kMars50, kWest):
+		if (GameState.isTakenItemID(kCardBomb) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA78", kArthurMarsExitedReactorWithCardBomb);
+		break;
 	case MakeRoomView(kMars52, kNorth):
 	case MakeRoomView(kMars52, kSouth):
 	case MakeRoomView(kMars52, kEast):
@@ -1628,6 +2139,23 @@ void Mars::turnTo(const DirectionConstant direction) {
 	case MakeRoomView(kMarsMaze184, kWest):
 		launchMaze184Robot();
 		break;
+	case MakeRoomView(kMarsMaze199, kNorth):
+	case MakeRoomView(kMarsMaze199, kSouth):
+	case MakeRoomView(kMarsMaze199, kEast):
+	case MakeRoomView(kMarsMaze199, kWest):
+		if (_vm->isDVD())
+			loadAmbientLoops();
+		if (GameState.getCurrentDirection() == kWest && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA77", kArthurMarsFoundBuckets);
+		break;
+	case MakeRoomView(kMarsMaze200, kWest):
+		if (g_arthurChip) {
+			if (_vm->getRandomBit())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA10", kArthurMarsApproachedBuckets);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA76", kArthurMarsApproachedBuckets);
+		}
+		break;
 	default:
 		break;
 	}
@@ -1940,7 +2468,8 @@ void Mars::pickedUpItem(Item *item) {
 	case kAirMask:
 		setCurrentActivation(kActivateHotSpotAlways);
 		if (!GameState.getScoringGotOxygenMask()) {
-			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM48SB", false, kWarningInterruption);
+			if (_vm->isChattyAI())
+				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM48SB", false, kWarningInterruption);
 			GameState.setScoringGotOxygenMask();
 		}
 		break;
@@ -2005,13 +2534,25 @@ void Mars::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {
 		switch (item->getObjectID()) {
 		case kMarsCard:
 			Neighborhood::dropItemIntoRoom(item, dropSpot);
-			if (dropSpot && dropSpot->getObjectID() == kMars34NorthCardDropSpotID)
-				startExtraSequence(kMarsTurnOnPod, kExtraCompletedFlag, kFilterNoInput);
+			if (dropSpot && dropSpot->getObjectID() == kMars34NorthCardDropSpotID) {
+				if (_vm->isDVD()) {
+					if (!GameState.getWalkthroughMode())
+						startExtraSequence(kMarsTurnOnSteerPod, kExtraCompletedFlag, kFilterNoInput);
+					else
+						startExtraSequence(kMarsTurnOnPod, kExtraCompletedFlag, kFilterNoInput);
+					startMarsTimer(kPodCautionDisplayedTime, kMovieTicksPerSecond, kMarsPodCautionDisplayed);
+				} else {
+					startExtraSequence(kMarsTurnOnPod, kExtraCompletedFlag, kFilterNoInput);
+				}
+			}
 			break;
 		case kNitrogenCanister:
 			Neighborhood::dropItemIntoRoom(item, dropSpot);
-			if (dropSpot && dropSpot->getObjectID() == kMars57DropNitrogenSpotID)
+			if (dropSpot && dropSpot->getObjectID() == kMars57DropNitrogenSpotID) {
 				startExtraSequence(kMars57FreezeLock, kExtraCompletedFlag, kFilterNoInput);
+				if (g_arthurChip)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA19", kArthurMarsUsedLiquidNitrogen);
+			}
 			break;
 		case kCrowbar:
 			_utilityFuse.stopFuse();
@@ -2139,6 +2680,73 @@ void Mars::turnRight() {
 	}
 }
 
+void Mars::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
+	TimeValue segmentStart = 0, segmentStop = 0;
+	bool loopSequence = false;
+	Common::Rect pushBounds;
+	NotificationFlags extraFlags;
+
+	switch (extraID) {
+	case kMarsTurnOnSteerPod:
+	case kMarsRobotBobSlow:
+	case kMarsRobotGenoSlow:
+		_turnPush.getBounds(pushBounds);
+
+		switch (extraID) {
+		case kMarsTurnOnSteerPod:
+			_extraMovie.initFromMovieFile("Images/Mars/M45OMK.movie");
+			break;
+		case kMarsRobotBobSlow:
+			_extraMovie.initFromMovieFile("Images/Mars/M48RSB.movie");
+			break;
+		case kMarsRobotGenoSlow:
+			_extraMovie.initFromMovieFile("Images/Mars/M48RSA.movie");
+			break;
+		default:
+			break;
+		}
+		segmentStart = 0;
+		segmentStop = _extraMovie.getDuration();
+		loopSequence = false;
+
+		_lastExtra = extraID;
+		_turnPush.hide();
+
+		if (!loopSequence && g_AIArea)
+			g_AIArea->lockAIOut();
+
+		extraFlags = flags;
+		_interruptionFilter = interruptionFilter;
+		// Stop the nav movie before doing anything else
+		_navMovie.stop();
+		_navMovie.stopDisplaying();
+
+		_extraMovie.setVolume(_vm->getSoundFXLevel());
+		_extraMovie.moveElementTo(pushBounds.left, pushBounds.top);
+		_extraMovie.setDisplayOrder(kNavMovieOrder + 1);
+		_extraMovie.startDisplaying();
+		_extraMovie.show();
+		_extraMovie.setFlags(0);
+		_extraMovie.setSegment(segmentStart, segmentStop);
+		_extraMovie.setTime(segmentStart);
+		if (loopSequence)
+			_extraMovie.setFlags(kLoopTimeBase);
+		else
+			extraFlags |= kNeighborhoodMovieCompletedFlag;
+		_extraMovieCallBack.cancelCallBack();
+		_extraMovieCallBack.initCallBack(&_extraMovie, kCallBackAtExtremes);
+		if (extraFlags != 0) {
+			_extraMovieCallBack.setCallBackFlag(extraFlags);
+			_extraMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+		}
+		_extraMovie.start();
+		break;
+	default:
+		Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
+		break;
+	}
+}
+
 void Mars::receiveNotification(Notification *notification, const NotificationFlags flag) {
 	InventoryItem *item;
 
@@ -2164,6 +2772,8 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 											kMarsPodDepartedUpperPlatformOut);
 				GameState.setMarsHeardUpperPodMessage(true);
 			}
+			if (g_airMask && g_airMask->getAirLeft() > 0 && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA74", kArthurMarsRobotThrownPlayerWithMask);
 			break;
 		case kMarsInfoKioskIntro:
 			GameState.setScoringSawMarsKiosk();
@@ -2195,12 +2805,15 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 			}
 			break;
 		case kMarsTurnOnPod:
-			item = (InventoryItem *)_vm->getAllItems().findItemByID(kMarsCard);
-			_vm->addItemToInventory(item);
-			GameState.setScoringTurnedOnTransport();
-			loadLoopSound1("");
-			loadLoopSound2("");
-			startExtraSequence(kMarsTakePodToMars45, kExtraCompletedFlag, kFilterNoInput);
+			if (!_vm->isDVD()) {
+				item = (InventoryItem *)_vm->getAllItems().findItemByID(kMarsCard);
+				_vm->addItemToInventory(item);
+				GameState.setScoringTurnedOnTransport();
+				loadLoopSound1("");
+				loadLoopSound2("");
+			}
+			if (!_vm->isDVD() || GameState.getWalkthroughMode())
+				startExtraSequence(kMarsTakePodToMars45, kExtraCompletedFlag, kFilterNoInput);
 			break;
 		case kMarsTakePodToMars45:
 			arriveAt(kMars45, kSouth);
@@ -2223,6 +2836,12 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 			checkAirMask();
 			loadAmbientLoops();
 			break;
+		case kMarsRobotBobSlow:
+		case kMarsRobotGenoSlow:
+			_extraMovie.stopDisplaying();
+			_extraMovie.releaseMovie();
+			_navMovie.startDisplaying();
+			// Fall through...
 		case kMars48RobotApproaches:
 			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0);
 			GameState.setMarsSeenRobotAtReactor(true);
@@ -2250,6 +2869,12 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 			setCurrentActivation(kActivateMaskOnFiller);
 			setCurrentAlternate(kAltMarsMaskOnFiller);
 			GameState.setMarsMaskOnFiller(true);
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA13", kArthurMarsCantFillMask);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA80", kArthurMarsCantFillMask);
+			}
 			break;
 		case kMars58SpinLeft:
 		case kMars54SpinRight:
@@ -2298,6 +2923,14 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 			setCurrentActivation(kActivateReactorAskLowerScreen);
 			break;
 		case kMars57LowerScreenClosed:
+			item = (InventoryItem *)_vm->getAllItems().findItemByID(kNitrogenCanister);
+			if (g_arthurChip) {
+				if (item->getItemState() == kNitrogenFull)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA12", kArthurMarsSawLockedPanel);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA39", kArthurMarsSawLockedPanelNoNitrogen);
+			}
+			// Fall through...
 		case kMars57ThawLock:
 			setCurrentActivation(kActivateReactorReadyForNitrogen);
 			GameState.setMarsLockFrozen(false);
@@ -2334,6 +2967,12 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 		case kMars57RunDiagnostics:
 			setCurrentActivation(kActivateReactorRanDiagnostics);
 			GameState.setScoringFoundCardBomb();
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA73", kArthurMarsFoundCardBomb);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB34", kArthurMarsFoundCardBomb);
+			}
 			break;
 		case kMars57BombExplodes:
 		case kMars57BombExplodesInGame:
@@ -2361,11 +3000,16 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 		case kMars57ExposeBomb:
 			setCurrentActivation(kActivateReactorBombExposed);
 			_privateFlags.setFlag(kMarsPrivateBombExposedFlag, true);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA75", kArthurMarsDeactivatedCardBomb);
 			break;
 		case kMars57BackToNormal:
 			setCurrentActivation(kActivateReactorPlatformIn);
 			_privateFlags.setFlag(kMarsPrivateBombExposedFlag, false);
-			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM51SW", false, kWarningInterruption);
+			if (_vm->isChattyAI())
+				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM51SW", false, kWarningInterruption);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA02", kArthurMarsSolvedReactorGame);
 			break;
 		case kMars60WestSpinAirlockToEast:
 			GameState.setMarsAirlockOpen(true);
@@ -2387,6 +3031,19 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 			break;
 		case kMarsRobotHeadOpen:
 			setCurrentActivation(kActivationRobotHeadOpen);
+			if (g_arthurChip) {
+				switch (_vm->getRandomNumber(2)) {
+				case 0:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA36", kArthurMarsRobotHeadOpen);
+					break;
+				case 1:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA37", kArthurMarsRobotHeadOpen);
+					break;
+				case 2:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA40", kArthurMarsRobotHeadOpen);
+					break;
+				}
+			}
 			break;
 		case kMarsRobotHeadClose:
 			recallToTSASuccess();
@@ -2417,12 +3074,12 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 			_rightDamageShuttleMovie.hide();
 			playMovieSegment(&_rightShuttleMovie, kShuttleRightDestroyedStart, kShuttleRightDestroyedStop);
 			playSpotSoundSync(kShuttleDestroyedIn, kShuttleDestroyedOut);
-			throwAwayMarsShuttle();
+			transportOutFromSpaceChase(true);
 			reinstateMonocleInterface();
 			recallToTSASuccess();
 		}
 	} else if ((flag & kTimeToTransportFlag) != 0) {
-		transportToRobotShip();
+		transportOutFromSpaceChase(false);
 	}
 
 	if (g_AIArea)
@@ -2432,11 +3089,42 @@ void Mars::receiveNotification(Notification *notification, const NotificationFla
 void Mars::spotCompleted() {
 	Neighborhood::spotCompleted();
 
-	if (GameState.getCurrentRoom() == kMarsRobotShuttle)
-		g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XN59WD", false, kWarningInterruption);
+	switch (GameState.getCurrentRoomAndView()) {
+	case MakeRoomView(kMars27, kNorth):
+	case MakeRoomView(kMars28, kNorth):
+		if (g_arthurChip) {
+			switch (_vm->getRandomNumber(2)) {
+			case 0:
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA09", kArthurMarsLookedAtGuards);
+				break;
+			case 1:
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA81", kArthurMarsLookedAtGuards);
+				break;
+			case 2:
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB32", kArthurMarsLookedAtGuards);
+				break;
+			}
+		}
+		break;
+	case MakeRoomView(kMarsRobotShuttle, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA79", kArthurMarsFoundDeadRobot);
+		if (_vm->isChattyAI())
+			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XN59WD", false, kWarningInterruption);
+		break;
+	}
+}
+
+void Mars::startUpFromFinishedTunnelPod() {
+	arriveAt(kMars45, kSouth);
+	if (g_AIArea != NULL)
+		g_AIArea->checkMiddleArea();
 }
 
 void Mars::doCanyonChase() {
+	Input input;
+	FaderMoveSpec spec;
+
 	GameState.setScoringEnteredShuttle();
 	setNextHandler(_vm);
 	throwAwayInterface();
@@ -2447,9 +3135,24 @@ void Mars::doCanyonChase() {
 	_spotSounds.initFromQuickTime(getSoundSpotsName());
 	_spotSounds.setVolume(_vm->getSoundFXLevel());
 
-	Video::VideoDecoder *video = new Video::QuickTimeDecoder();
-	if (!video->loadFile("Images/Mars/M44ESA.movie"))
-		error("Could not load interface->shuttle transition video");
+	Video::VideoDecoder *video = 0;
+
+#ifdef USE_THEORADEC
+	if (_vm->isDVD()) {
+		video = new Video::TheoraDecoder();
+
+		if (!video->loadFile("Images/Mars/M44ESA_hq.ogg")) {
+			delete video;
+			video = 0;
+		}
+	}
+#endif
+
+	if (!video) {
+		video = new Video::QuickTimeDecoder();
+		if (!video->loadFile("Images/Mars/M44ESA.movie"))
+			error("Could not load interface->shuttle transition video");
+	}
 
 	video->setVolume(MIN<uint>(_vm->getSoundFXLevel(), 0xFF));
 
@@ -2482,11 +3185,18 @@ void Mars::doCanyonChase() {
 	initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left,
 							kShuttle4Top, true);
 
-	initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon.movie",
+	if (_vm->isDVD())
+		initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon_hq1.mov",
+						kShuttleMonitorOrder, kShuttleWindowLeft, kShuttleWindowTop, true);
+	else
+		initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon.movie",
 						kShuttleMonitorOrder, kShuttleWindowLeft, kShuttleWindowTop, true);
 	_canyonChaseMovie.setVolume(_vm->getSoundFXLevel());
 
-	loadLoopSound1("Sounds/Mars/Inside Cockpit.22K.8.AIFF");
+	if (_vm->isDVD())
+		loadLoopSound1("Sounds/Mars/Inside Cockpit.44K.16.AIFF");
+	else
+		loadLoopSound1("Sounds/Mars/Inside Cockpit.22K.8.AIFF");
 
 	// Swing shuttle around...
 	playMovieSegment(&_canyonChaseMovie, kShuttleSwingStart, kShuttleSwingStop);
@@ -2518,6 +3228,12 @@ void Mars::doCanyonChase() {
 	initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie",
 			kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false);
 
+	if (_vm->isDVD()) {
+		_musicLoop.attachFader(&_musicFader);
+		_musicLoop.initFromAIFFFile("Sounds/Mars/Canyon Loop.44K.16.AIFF");
+		_musicFader.setMasterVolume(_vm->getAmbienceLevel());
+	}
+
 	_centerShuttleMovie.show();
 	_centerShuttleMovie.setTime(kShuttleCenterBoardingTime);
 	playSpotSoundSync(kShuttleCockpitIn, kShuttleCockpitOut);
@@ -2592,10 +3308,39 @@ void Mars::doCanyonChase() {
 
 	loadLoopSound1("");
 
-	_canyonChaseMovie.setSegment(kCanyonChaseStart, kCanyonChaseStop);
-	_canyonChaseMovie.start();
+	if (_vm->isDVD()) {
+		InputDevice.getInput(input, kFilterAllInput);
+		if (JMPPPInput::isEasterEggModifierInput(input)) {
+			GameState.setEasterEgg(true);
+			initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon_hqG.mov",
+							kShuttleMonitorOrder, kShuttleWindowLeft, kShuttleWindowTop, true);
+			_canyonChaseMovie.setVolume(_vm->getAmbienceLevel());
+			_canyonChaseMovie.start();
+
+			startMarsTimer(_canyonChaseMovie.getDuration() - 5 * kMovieTicksPerSecond,
+							kMovieTicksPerSecond, kMarsLaunchTubeReached);
+		} else if (GameState.getWalkthroughMode()) {
+			_canyonChaseMovie.setSegment(kCanyonChaseStart, kCanyonChaseDVDStop);
+			_canyonChaseMovie.start();
+
+			_musicLoop.loopSound();
+			spec.makeTwoKnotFaderSpec(10, 0, 0, 1, 255);
+			_musicFader.startFader(spec);
+
+			startMarsTimer(kCanyonChaseExitedTime, kMovieTicksPerSecond, kMarsCanyonChaseExited);
+		} else {
+			_canyonChaseMovie.stop();
+			_canyonChaseMovie.stopDisplaying();
+			_canyonChaseMovie.releaseMovie();
 
-	startMarsTimer(kLaunchTubeReachedTime, kMovieTicksPerSecond, kMarsLaunchTubeReached);
+			newInteraction(kMarsCanyonChaseInteractionID);
+		}
+	} else {
+		_canyonChaseMovie.setSegment(kCanyonChaseStart, kCanyonChaseCDStop);
+		_canyonChaseMovie.start();
+
+		startMarsTimer(kLaunchTubeCDReachedTime, kMovieTicksPerSecond, kMarsLaunchTubeReached);
+	}
 }
 
 void Mars::startUpFromFinishedSpaceChase() {
@@ -2655,7 +3400,7 @@ void Mars::startUpFromFinishedSpaceChase() {
 
 	_lowerLeftShuttleMovie.show();
 
-	loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF");
+	playSpaceAmbient();
 
 	initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft,
 			kShuttleJunkTop, false);
@@ -2766,7 +3511,7 @@ void Mars::startUpFromSpaceChase() {
 
 	_lowerLeftShuttleMovie.show();
 
-	loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF");
+	playSpaceAmbient();
 
 	initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder,
 			kPlanetStartLeft, kPlanetStartTop, true);
@@ -2844,13 +3589,26 @@ void Mars::setSoundFXLevel(const uint16 level) {
 			!GameState.getMarsAvoidedReactorRobot())
 		_loop2Fader.setMasterVolume(level);
 
-	if (_canyonChaseMovie.isMovieValid())
+	if (_extraMovie.isMovieValid())
+		_extraMovie.setVolume(level);
+	if (GameState.getCurrentRoomAndView() == MakeRoomView(kMars48, kEast) &&
+		!GameState.getMarsAvoidedReactorRobot())
+		_loop2Fader.setMasterVolume(level);
+	if (!GameState.getEasterEgg() && _canyonChaseMovie.isMovieValid())
 		_canyonChaseMovie.setVolume(level);
-
 	if (_explosions.isMovieValid())
 		_explosions.setVolume(level);
 }
 
+void Mars::setAmbienceLevel(const uint16 level) {
+	Neighborhood::setAmbienceLevel(level);
+
+	if (GameState.getEasterEgg() && _canyonChaseMovie.isMovieValid())
+		_canyonChaseMovie.setVolume(level);
+	if (_musicLoop.isSoundLoaded())
+		_musicFader.setMasterVolume(level);
+}
+
 void Mars::startMarsTimer(TimeValue time, TimeScale scale, MarsTimerCode code) {
 	_utilityFuse.primeFuse(time, scale);
 	_marsEvent.mars = this;
@@ -2860,37 +3618,72 @@ void Mars::startMarsTimer(TimeValue time, TimeScale scale, MarsTimerCode code) {
 }
 
 void Mars::marsTimerExpired(MarsTimerEvent &event) {
+	InventoryItem *item;
+	FaderMoveSpec spec;
 	Common::Rect r;
 	uint16 x, y;
 
 	switch (event.event) {
+	case kMarsPodCautionDisplayed:
+		item = (InventoryItem *)_vm->getAllItems().findItemByID(kMarsCard);
+		_vm->addItemToInventory(item);
+		startMarsTimer(kPodCautionDismissedTime, kMovieTicksPerSecond, kMarsPodCautionDismissed);
+		return;
+	case kMarsPodCautionDismissed:
+		_extraMovie.stopDisplaying();
+		_extraMovie.releaseMovie();
+		_navMovie.startDisplaying();
+		GameState.setScoringTurnedOnTransport();
+		loadLoopSound1("");
+		loadLoopSound2("");
+		if (!GameState.getWalkthroughMode())
+			newInteraction(kMarsTunnelPodInteractionID);
+		return;
+		// Bail out early for pod events so input is not filtered
+	case kMarsCanyonChaseExited:
+		spec.makeTwoKnotFaderSpec(20, 0, 255, 5, 160);
+		_musicFader.startFader(spec);
+		startMarsTimer(kCanyonChaseFadedTime, kMovieTicksPerSecond, kMarsCanyonChaseFaded);
+		break;
+	case kMarsCanyonChaseFaded:
+		spec.makeTwoKnotFaderSpec(10, 0, 160, 30, 0);
+		_musicFader.startFader(spec);
+		startMarsTimer(kLaunchTubeDVDReachedTime, kMovieTicksPerSecond, kMarsLaunchTubeReached);
+		break;
 	case kMarsLaunchTubeReached:
 		_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftTubeTime);
 		_lowerLeftShuttleMovie.redrawMovieWorld();
-		startMarsTimer(kCanyonChaseFinishedTime, kMovieTicksPerSecond, kMarsCanyonChaseFinished);
+		startMarsTimer(kCanyonChaseCDFinishedTime, kMovieTicksPerSecond, kMarsCanyonChaseFinished);
 		break;
 	case kMarsCanyonChaseFinished:
 		GameState.setScoringEnteredLaunchTube();
+		GameState.setEasterEgg(false);
+
+		if (_canyonChaseMovie.isMovieValid()) {
+			while (_canyonChaseMovie.isRunning()) {
+				InputDevice.pumpEvents();
+				_vm->checkCallBacks();
+				_vm->refreshDisplay();
+				_vm->_system->delayMillis(10);
+			}
 
-		while (_canyonChaseMovie.isRunning()) {
-			InputDevice.pumpEvents();
-			_vm->checkCallBacks();
-			_vm->refreshDisplay();
-			_vm->_system->delayMillis(10);
+			_canyonChaseMovie.stop();
+			_canyonChaseMovie.stopDisplaying();
+			_canyonChaseMovie.releaseMovie();
 		}
 
-		_canyonChaseMovie.stop();
-		_canyonChaseMovie.stopDisplaying();
-		_canyonChaseMovie.releaseMovie();
-
 		_vm->_gfx->enableErase();
 
-		loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF");
+		playSpaceAmbient();
 
-		playSpotSoundSync(kShuttleConfiguringIn, kShuttleConfiguringOut);
-		playSpotSoundSync(kShuttleGeneratingIn, kShuttleGeneratingOut);
-		playSpotSoundSync(kShuttleBreakawayIn, kShuttleBreakawayOut);
-		playSpotSoundSync(kMarsAtmosphericBreakawayIn, kMarsAtmosphericBreakawayOut);
+		if (!_vm->isDVD()) {
+			// Don't play a couple sounds in the DVD version; they were put into
+			// the canyon video.
+			playSpotSoundSync(kShuttleConfiguringIn, kShuttleConfiguringOut);
+			playSpotSoundSync(kShuttleGeneratingIn, kShuttleGeneratingOut);
+			playSpotSoundSync(kShuttleBreakawayIn, kShuttleBreakawayOut);
+			playSpotSoundSync(kMarsAtmosphericBreakawayIn, kMarsAtmosphericBreakawayOut);
+		}
 
 		initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, kPlanetStartLeft, kPlanetStartTop, true);
 		_planetMovie.setFlags(kLoopTimeBase);
@@ -3077,13 +3870,44 @@ void Mars::throwAwayMarsShuttle() {
 	loadLoopSound1("");
 }
 
-void Mars::transportToRobotShip() {
+void Mars::playSpaceAmbient() {
+	if (_vm->isDVD())
+		loadLoopSound1("Sounds/Mars/Space Ambient.44K.16.AIFF");
+	else
+		loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF");
+}
+
+void Mars::transportOutFromSpaceChase(bool destroyedShip) {
 	throwAwayMarsShuttle();
 
-	Video::VideoDecoder *video = new Video::QuickTimeDecoder();
-	if (!video->loadFile("Images/Mars/M98EAE.movie"))
-		error("Could not load shuttle->interface transition video");
+	Video::VideoDecoder *video = 0;
 
+#ifdef USE_THEORADEC
+	if (_vm->isDVD()) {
+		video = new Video::TheoraDecoder();
+
+		if (destroyedShip) {
+			if (!video->loadFile("Images/Mars/M98EAP_hq.ogg")) {
+				delete video;
+				video = 0;
+			}
+		} else if (!video->loadFile("Images/Mars/M98EAE_hq.ogg")) {
+			delete video;
+			video = 0;
+		}
+	}
+#endif
+
+	if (!video) {
+		video = new Video::QuickTimeDecoder();
+		if (destroyedShip && _vm->isDVD()) {
+			if (!video->loadFile("Images/Mars/M98EAP.movie"))
+				error("Could not load shuttle->TSA transition video");
+		} else if (!video->loadFile("Images/Mars/M98EAE.movie"))
+			error("Could not load shuttle->interface transition video");
+	}
+
+	video->setVolume(MIN<uint>(_vm->getSoundFXLevel(), 0xFF));
 	video->start();
 
 	while (!_vm->shouldQuit() && !video->endOfVideo()) {
@@ -3104,18 +3928,20 @@ void Mars::transportToRobotShip() {
 	if (_vm->shouldQuit())
 		return;
 
-	reinstateMonocleInterface();
+	if (!destroyedShip) {
+		reinstateMonocleInterface();
 
-	g_energyMonitor->stopEnergyDraining();
-	g_energyMonitor->restoreLastEnergyValue();
-	_vm->resetEnergyDeathReason();
-	g_energyMonitor->startEnergyDraining();
+		g_energyMonitor->stopEnergyDraining();
+		g_energyMonitor->restoreLastEnergyValue();
+		_vm->resetEnergyDeathReason();
+		g_energyMonitor->startEnergyDraining();
 
-	arriveAt(kMarsRobotShuttle, kEast);
+		arriveAt(kMarsRobotShuttle, kEast);
 
-	_navMovie.stop();
-	_navMovie.setTime(_navMovie.getStart());
-	_navMovie.start();
+		_navMovie.stop();
+		_navMovie.setTime(_navMovie.getStart());
+		_navMovie.start();
+	}
 }
 
 const int kRobotTooStrong = 1;
diff --git a/engines/pegasus/neighborhood/mars/mars.h b/engines/pegasus/neighborhood/mars/mars.h
index 0f07bb2d16..5459091a0d 100644
--- a/engines/pegasus/neighborhood/mars/mars.h
+++ b/engines/pegasus/neighborhood/mars/mars.h
@@ -40,10 +40,16 @@
 
 namespace Pegasus {
 
+class CanyonChase;
 class InventoryItem;
 class Mars;
+class TunnelPod;
 
 enum MarsTimerCode {
+	kMarsPodCautionDisplayed,
+	kMarsPodCautionDismissed,
+	kMarsCanyonChaseExited,
+	kMarsCanyonChaseFaded,
 	kMarsLaunchTubeReached,
 	kMarsCanyonChaseFinished,
 	kMarsSpaceChaseFinished // Player ran out of time...
@@ -64,11 +70,14 @@ enum ShuttleWeaponSelection {
 };
 
 class Mars : public Neighborhood {
+friend class CanyonChase;
+friend class TunnelPod;
 friend struct MarsTimerEvent;
 public:
 	Mars(InputHandler *, PegasusEngine *);
 	~Mars() override;
 
+	GameInteraction *makeInteraction(const InteractionID) override;
 	void flushGameState() override;
 
 	uint16 getDateResID() const override;
@@ -94,6 +103,7 @@ public:
 	void checkContinuePoint(const RoomID, const DirectionConstant) override;
 
 	void setSoundFXLevel(const uint16) override;
+	void setAmbienceLevel(const uint16) override;
 
 	bool canSolve() override;
 	void doSolve() override;
@@ -133,12 +143,16 @@ protected:
 	CanOpenDoorReason canOpenDoor(DoorTable::Entry &) override;
 	void openDoor() override;
 	void closeDoorOffScreen(const RoomID, const DirectionConstant) override;
+	void startDoorOpenMovie(const TimeValue, const TimeValue) override;
+	void startExitMovie(const ExitTable::Entry &) override;
 	int16 getStaticCompassAngle(const RoomID, const DirectionConstant) override;
 	void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &) override;
 	void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &) override;
 	void turnTo(const DirectionConstant) override;
+	void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits) override;
 	void receiveNotification(Notification *, const NotificationFlags) override;
 	void doorOpened() override;
+	void startUpFromFinishedTunnelPod();
 	void setUpReactorEnergyDrain();
 	Hotspot *getItemScreenSpot(Item *, DisplayElement *) override;
 	void lockThawed();
@@ -170,6 +184,7 @@ protected:
 	void launchMaze136Robot();
 	void launchMaze184Robot();
 	void timerExpired(const uint32) override;
+	void showRobotAtReactor();
 	void spotCompleted() override;
 
 	void doCanyonChase(void);
@@ -178,13 +193,17 @@ protected:
 	void throwAwayMarsShuttle();
 	void startUpFromFinishedSpaceChase();
 	void startUpFromSpaceChase();
-	void transportToRobotShip();
+	void transportOutFromSpaceChase(bool);
 	void spaceChaseClick(const Input &, const HotSpotID);
 	void updateCursor(const Common::Point, const Hotspot *) override;
+	void playSpaceAmbient();
 
 	Common::String getSoundSpotsName() override;
 	Common::String getNavMovieName() override;
 
+	Movie _extraMovie;
+	NotificationCallBack _extraMovieCallBack;
+
 	InventoryItem *_attackingItem;
 	FuseFunction _bombFuse;
 	FuseFunction _noAirFuse;
@@ -202,6 +221,8 @@ protected:
 	Picture _shuttleInterface3;
 	Picture _shuttleInterface4;
 	Movie _canyonChaseMovie;
+	Sound _musicLoop;
+	SoundFader _musicFader;
 
 	MarsTimerEvent _marsEvent;
 
diff --git a/engines/pegasus/neighborhood/mars/tunnelpod.cpp b/engines/pegasus/neighborhood/mars/tunnelpod.cpp
new file mode 100644
index 0000000000..61ba86cdd5
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/tunnelpod.cpp
@@ -0,0 +1,373 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/arthurchip.h"
+#include "pegasus/neighborhood/mars/tunnelpod.h"
+#include "pegasus/neighborhood/mars/mars.h"
+
+namespace Pegasus {
+
+// Segment start and end points.
+
+static const TimeValue kLaunchStart = 315754;
+static const TimeValue kLaunchEnd = 319392;
+
+static const TimeValue kBranch1MainStart = 0;
+static const TimeValue kBranch1MainEnd = 3600;
+static const TimeValue kBranch2MainStart = kBranch1MainEnd;
+static const TimeValue kBranch2MainEnd = 13200;
+static const TimeValue kBranch3MainStart = kBranch2MainEnd;
+static const TimeValue kBranch3MainEnd = 20400;
+static const TimeValue kFinishMainStart = kBranch3MainEnd;
+static const TimeValue kFinishMainEnd = 30640;
+
+static const TimeValue kBranch2AltStart = 0;
+static const TimeValue kBranch2AltEnd = 13200;
+static const TimeValue kBranch3AltStart = kBranch2AltEnd;
+static const TimeValue kBranch3AltEnd = 22800;
+static const TimeValue kFinishAltStart = kBranch3AltEnd;
+static const TimeValue kFinishAltEnd = 33640;
+
+// Tunnel state.
+
+enum {
+	kTunnelLaunch,
+	kTunnelBranch1Left,
+	kTunnelBranch2Left,
+	kTunnelBranch2Right,
+	kTunnelBranch3Left,
+	kTunnelBranch3Right,
+	kTunnelFinish
+};
+
+
+TunnelPod::TunnelPod(Neighborhood *handler) : ChaseInteraction(kMarsTunnelPodInteractionID, handler,
+						kMarsTunnelPodNotificationID, (PegasusEngine *)g_engine), _tunnelMainMovie(kNoDisplayElement),
+						_tunnelAltMovie(kNoDisplayElement), _deathMovie(kNoDisplayElement) {
+	_currentMovie = NULL;
+	_currentCallBack = NULL;
+}
+
+void TunnelPod::setSoundFXLevel(const uint16 fxLevel) {
+	_tunnelMainMovie.setVolume(fxLevel);
+	_tunnelAltMovie.setVolume(fxLevel);
+	_deathMovie.setVolume(fxLevel);
+}
+
+void TunnelPod::openInteraction() {
+	((Mars *)_owner)->_navMovie.stop();
+
+	_tunnelCallBack.setNotification(&_chaseNotification);
+	_tunnelCallBack.initCallBack(&((Mars *)_owner)->_navMovie, kCallBackAtExtremes);
+	_tunnelCallBack.setCallBackFlag(kChaseEnteredBranchZone);
+	_tunnelCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	_tunnelMainMovie.initFromMovieFile("Images/Mars/Pod 2345M.mov");
+	_tunnelMainMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+	_tunnelMainMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+	_tunnelMainMovie.setDisplayOrder(kNavMovieOrder);
+
+	_tunnelMainCallBack.setNotification(&_chaseNotification);
+	_tunnelMainCallBack.initCallBack(&_tunnelMainMovie, kCallBackAtExtremes);
+	_tunnelMainCallBack.setCallBackFlag(kChaseEnteredBranchZone);
+	_tunnelMainCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	_tunnelAltMovie.initFromMovieFile("Images/Mars/Pod 345A.mov");
+	_tunnelAltMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+	_tunnelAltMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+	_tunnelAltMovie.setDisplayOrder(kNavMovieOrder);
+
+	_tunnelAltCallBack.setNotification(&_chaseNotification);
+	_tunnelAltCallBack.initCallBack(&_tunnelAltMovie, kCallBackAtExtremes);
+	_tunnelAltCallBack.setCallBackFlag(kChaseEnteredBranchZone);
+	_tunnelAltCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	_deathMovie.initFromMovieFile("Images/Mars/Pod 2D.mov");
+	_deathMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+	_deathMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+	_deathMovie.setDisplayOrder(kNavMovieOrder);
+
+	_deathCallBack.setNotification(&_chaseNotification);
+	_deathCallBack.initCallBack(&_deathMovie, kCallBackAtExtremes);
+	_deathCallBack.setCallBackFlag(kChaseFinished);
+	_deathCallBack.scheduleCallBack(kTriggerAtStop, 0 ,0);
+
+	ChaseInteraction::openInteraction();
+
+	_steerPict.setDisplayOrder(kNavMovieOrder + 1);
+	_steerPict.moveElementTo(kPodSteerLeft, kPodSteerTop);
+}
+
+void TunnelPod::initInteraction() {
+	_steerPict.startDisplaying();
+
+	_tunnelState = kTunnelLaunch;
+	((Mars *)_owner)->_navMovie.setSegment(kLaunchStart, kLaunchEnd - kDecisionTime);
+	((Mars *)_owner)->_navMovie.setTime(kLaunchStart);
+	((Mars *)_owner)->_navMovie.start();
+	_currentMovie = &((Mars *)_owner)->_navMovie;
+	_currentCallBack = &_tunnelCallBack;
+	ChaseInteraction::initInteraction();
+	if (g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB27", kArthurMarsTurnedOnTransport);
+}
+
+void TunnelPod::closeInteraction() {
+	((Mars *)_owner)->_navMovie.stop();
+	if (_tunnelState == kTunnelFinish) {
+	// Only bring nack the nav movie if we successfully finished the chase
+		((Mars *)_owner)->_navMovie.startDisplaying();
+		((Mars *)_owner)->_navMovie.show();
+	}
+	_tunnelCallBack.releaseCallBack();
+
+	_tunnelAltMovie.stop();
+	_tunnelAltMovie.stopDisplaying();
+	_tunnelAltMovie.releaseMovie();
+	_tunnelAltCallBack.releaseCallBack();
+
+	_deathMovie.stop();
+	_deathMovie.stopDisplaying();
+	_deathMovie.releaseMovie();
+	_deathCallBack.releaseCallBack();
+
+	ChaseInteraction::closeInteraction();
+}
+
+void TunnelPod::receiveNotification(Notification *notification, const NotificationFlags flags) {
+	if (notification == &_chaseNotification && flags == kChaseFinished) {
+		if (_tunnelState != kTunnelFinish) {
+			// We died
+			((Mars *)_owner)->die(kDeathCollidedWithPod);
+		} else {
+			((Mars *)_owner)->startUpFromFinishedTunnelPod();
+		}
+	}
+	ChaseInteraction::receiveNotification(notification, flags);
+}
+
+void TunnelPod::setUpBranch() {
+	TimeValue branchStart, branchEnd;
+
+	branchStart = 0;
+	branchEnd = 0;
+	switch (_tunnelState) {
+	case kTunnelLaunch:
+		branchStart = kLaunchEnd - kDecisionTime;
+		branchEnd = kLaunchEnd;
+		break;
+	case kTunnelBranch1Left:
+		branchStart = kBranch1MainEnd - kDecisionTime;
+		branchEnd = kBranch1MainEnd;
+		break;
+	case kTunnelBranch2Left:
+		branchStart = kBranch2AltEnd - kDecisionTime;
+		branchEnd = kBranch2AltEnd;
+		break;
+	case kTunnelBranch2Right:
+		branchStart = kBranch2MainEnd - kDecisionTime;
+		branchEnd = kBranch2MainEnd;
+		break;
+	case kTunnelBranch3Left:
+		branchStart = kBranch3MainEnd - kDecisionTime;
+		branchEnd = kBranch3MainEnd;
+		break;
+	case kTunnelBranch3Right:
+		branchStart = kBranch3AltEnd - kDecisionTime;
+		branchEnd = kBranch3AltEnd;
+		break;
+	default:
+		break;
+	}
+
+	_currentMovie->setSegment(branchStart, branchEnd);
+
+	_currentCallBack->setCallBackFlag(kChaseExitedBranchZone);
+	_currentCallBack->scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void TunnelPod::branchLeft() {
+	TimeValue branchStart, branchEnd;
+	NotificationFlags flag;
+	Movie *movie;
+	NotificationCallBack *callBack;
+
+	branchStart = 0;
+	branchEnd = 0;
+	flag = 0;
+	movie = NULL;
+	callBack = NULL;
+	switch (_tunnelState) {
+	case kTunnelLaunch:
+		branchStart = kBranch1MainStart;
+		branchEnd = kBranch1MainEnd - kDecisionTime;
+		_tunnelState = kTunnelBranch1Left;
+		flag = kChaseEnteredBranchZone;
+		movie = &_tunnelMainMovie;
+		callBack = &_tunnelMainCallBack;
+		break;
+	case kTunnelBranch1Left:
+		branchStart = kBranch2AltStart;
+		branchEnd = kBranch2AltEnd - kDecisionTime;
+		_tunnelState = kTunnelBranch2Left;
+		flag = kChaseEnteredBranchZone;
+		movie = &_tunnelAltMovie;
+		callBack = &_tunnelAltCallBack;
+		break;
+	case kTunnelBranch2Left:
+	case kTunnelBranch2Right:
+		branchStart = kBranch3MainStart;
+		branchEnd = kBranch3MainEnd - kDecisionTime;
+		_tunnelState = kTunnelBranch3Left;
+		flag = kChaseEnteredBranchZone;
+		movie = &_tunnelMainMovie;
+		callBack = &_tunnelMainCallBack;
+		break;
+	case kTunnelBranch3Left:
+	case kTunnelBranch3Right:
+		branchStart = kFinishAltStart;
+		branchEnd = kFinishAltEnd;
+		_tunnelState = kTunnelFinish;
+		flag = kChaseFinished;
+		movie = &_tunnelAltMovie;
+		callBack = &_tunnelAltCallBack;
+		break;
+	default:
+		break;
+	}
+
+	movie->setSegment(branchStart, branchEnd);
+	movie->setTime(branchStart);
+
+	switchTo(*movie, *callBack);
+
+	callBack->setCallBackFlag(flag);
+	callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void TunnelPod::branchRight() {
+	TimeValue branchStart, branchEnd;
+	NotificationFlags flag;
+	Movie *movie;
+	NotificationCallBack *callBack;
+
+	branchStart = 0;
+	branchEnd = 0;
+	flag = 0;
+	movie = NULL;
+	callBack = NULL;
+	switch (_tunnelState) {
+	case kTunnelLaunch:
+		switchTo(_deathMovie, _deathCallBack);
+		return;
+	case kTunnelBranch1Left:
+		branchStart = kBranch2MainStart;
+		branchEnd = kBranch2MainEnd - kDecisionTime;
+		_tunnelState = kTunnelBranch2Right;
+		flag = kChaseEnteredBranchZone;
+		movie = &_tunnelMainMovie;
+		callBack = &_tunnelMainCallBack;
+		break;
+	case kTunnelBranch2Left:
+	case kTunnelBranch2Right:
+		branchStart = kBranch3AltStart;
+		branchEnd = kBranch3AltEnd - kDecisionTime;
+		_tunnelState = kTunnelBranch3Right;
+		flag = kChaseEnteredBranchZone;
+		movie = &_tunnelAltMovie;
+		callBack = &_tunnelAltCallBack;
+		break;
+	case kTunnelBranch3Left:
+	case kTunnelBranch3Right:
+		branchStart = kFinishMainStart;
+		branchEnd = kFinishMainEnd;
+		_tunnelState = kTunnelFinish;
+		flag = kChaseFinished;
+		movie = &_tunnelMainMovie;
+		callBack = &_tunnelMainCallBack;
+		break;
+	default:
+		break;
+	}
+
+	movie->setSegment(branchStart, branchEnd);
+	movie->setTime(branchStart);
+
+	switchTo(*movie, *callBack);
+
+	callBack->setCallBackFlag(flag);
+	callBack->scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void TunnelPod::dontBranch() {
+	switch (_tunnelState) {
+	case kTunnelLaunch:
+	case kTunnelBranch1Left:
+		if (_currentMovie == &_tunnelAltMovie)
+			branchLeft();
+		else
+			branchRight();
+		break;
+	case kTunnelBranch2Left:
+	case kTunnelBranch2Right:
+		if (_currentMovie == &_tunnelAltMovie)
+			branchRight();
+		else
+			branchLeft();
+		break;
+	case kTunnelBranch3Left:
+	case kTunnelBranch3Right:
+		if (_currentMovie == &_tunnelAltMovie)
+			branchLeft();
+		else
+			branchRight();
+		break;
+	default:
+		break;
+	}
+}
+
+void TunnelPod::switchTo(Movie &movie, NotificationCallBack &callBack) {
+	if (_currentMovie != &movie) {
+		if (_currentMovie != NULL) {
+			_currentMovie->stop();
+			_currentMovie->hide();
+			_currentMovie->stopDisplaying();
+		}
+
+		_currentMovie = &movie;
+
+		_currentMovie->startDisplaying();
+		_currentMovie->show();
+		_currentMovie->start();
+	}
+
+	if (_currentCallBack != &callBack) {
+		_currentCallBack = &callBack;
+	}
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/mars/tunnelpod.h b/engines/pegasus/neighborhood/mars/tunnelpod.h
new file mode 100644
index 0000000000..75d842e282
--- /dev/null
+++ b/engines/pegasus/neighborhood/mars/tunnelpod.h
@@ -0,0 +1,76 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_MARS_TUNNELPOD_H
+#define PEGASUS_NEIGHBORHOOD_MARS_TUNNELPOD_H
+
+#include "pegasus/chase.h"
+#include "pegasus/movie.h"
+
+namespace Pegasus {
+
+class Mars;
+
+class TunnelPod : public ChaseInteraction {
+friend class Mars;
+friend struct MusicTimerEvent;
+public:
+
+	TunnelPod(Neighborhood *);
+	virtual ~TunnelPod() {}
+
+	void setSoundFXLevel(const uint16);
+
+protected:
+
+	void openInteraction();
+	void initInteraction();
+	void closeInteraction();
+
+	void receiveNotification(Notification *, const NotificationFlags);
+
+	void setUpBranch();
+	void branchLeft();
+	void branchRight();
+	void dontBranch();
+
+	void switchTo(Movie &, NotificationCallBack &);
+
+	Movie _tunnelMainMovie;
+	Movie _tunnelAltMovie;
+	Movie _deathMovie;
+	NotificationCallBack _tunnelCallBack;
+	NotificationCallBack _tunnelMainCallBack;
+	NotificationCallBack _tunnelAltCallBack;
+	NotificationCallBack _deathCallBack;
+
+	Movie *_currentMovie;
+	NotificationCallBack *_currentCallBack;
+	short _tunnelState;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/neighborhood.cpp b/engines/pegasus/neighborhood/neighborhood.cpp
index 0980240d40..a91431762f 100644
--- a/engines/pegasus/neighborhood/neighborhood.cpp
+++ b/engines/pegasus/neighborhood/neighborhood.cpp
@@ -36,6 +36,7 @@
 #include "pegasus/interface.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/mapchip.h"
 #include "pegasus/neighborhood/neighborhood.h"
 #include "pegasus/neighborhood/tsa/fulltsa.h"
@@ -725,7 +726,15 @@ void Neighborhood::cantMoveThatWay(CanMoveForwardReason reason) {
 }
 
 void Neighborhood::cantOpenDoor(CanOpenDoorReason) {
+	bool firstLockedDoor;
+
 	bumpIntoWall();
+	if (g_arthurChip) {
+		firstLockedDoor = g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA31", kArthurAttemptedLockedDoor);
+
+		if (!firstLockedDoor)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA32", kArthurAttemptedLockedDoorAgain);
+	}
 }
 
 void Neighborhood::turnTo(const DirectionConstant direction) {
@@ -1010,9 +1019,9 @@ void Neighborhood::startExitMovie(const ExitTable::Entry &exitEntry) {
 	GameState.setNextDirection(exitEntry.exitDirection);
 
 	if (exitEntry.movieEnd == exitEntry.exitEnd) // Just a walk.
-		startMovieSequence(exitEntry.movieStart, exitEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false);
+		startMovieSequence(exitEntry.movieStart, exitEntry.movieEnd, kMoveForwardCompletedFlag, false, kFilterNoInput);
 	else // We're stridin'!
-		startMovieSequence(exitEntry.movieStart, exitEntry.exitEnd, kStrideCompletedFlag, kFilterNoInput, false, exitEntry.movieEnd);
+		startMovieSequence(exitEntry.movieStart, exitEntry.exitEnd, kStrideCompletedFlag, false, kFilterNoInput, exitEntry.movieEnd);
 
 	if (g_compass)
 		g_compass->startFader(compassMove);
@@ -1027,14 +1036,14 @@ void Neighborhood::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
 	GameState.setNextRoom(zoomEntry.room);
 	GameState.setNextDirection(zoomEntry.direction);
 
-	startMovieSequence(zoomEntry.movieStart, zoomEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false);
+	startMovieSequence(zoomEntry.movieStart, zoomEntry.movieEnd, kMoveForwardCompletedFlag, false, kFilterNoInput);
 
 	if (g_compass)
 		g_compass->startFader(compassMove);
 }
 
 void Neighborhood::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) {
-	startMovieSequence(startTime, stopTime, kDoorOpenCompletedFlag, kFilterNoInput, false);
+	startMovieSequence(startTime, stopTime, kDoorOpenCompletedFlag, false, kFilterNoInput);
 }
 
 void Neighborhood::startTurnPush(const TurnDirection turnDirection, const TimeValue newView, const DirectionConstant nextDir) {
@@ -1195,7 +1204,7 @@ void Neighborhood::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *h
 
 void Neighborhood::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) {
 	_turnPush.hide();
-	startMovieSequence(startTime, stopTime, kSpotCompletedFlag, kFilterNoInput, false);
+	startMovieSequence(startTime, stopTime, kSpotCompletedFlag, false, kFilterNoInput);
 }
 
 void Neighborhood::startMovieSequence(const TimeValue startTime, const TimeValue stopTime, NotificationFlags flags, bool loopSequence,
@@ -1567,7 +1576,7 @@ void Neighborhood::startExtraLongSequence(const uint32 firstExtra, const uint32
 		getExtraEntry(lastExtra, lastEntry);
 		_lastExtra = firstExtra;
 		_turnPush.hide();
-		startMovieSequence(firstEntry.movieStart, lastEntry.movieEnd, flags, kFilterNoInput, interruptionFilter);
+		startMovieSequence(firstEntry.movieStart, lastEntry.movieEnd, flags, false, interruptionFilter);
 	}
 }
 
@@ -1583,6 +1592,7 @@ void Neighborhood::openCroppedMovie(const Common::String &movieName, CoordType l
 
 void Neighborhood::loopCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) {
 	openCroppedMovie(movieName, left, top);
+	_croppedMovie.setVolume(_vm->getSoundFXLevel());
 	_croppedMovie.redrawMovieWorld();
 	_croppedMovie.setFlags(kLoopTimeBase);
 	_croppedMovie.start();
@@ -1666,7 +1676,10 @@ void Neighborhood::handleInput(const Input &input, const Hotspot *cursorSpot) {
 		else if (input.rightButtonAnyDown())
 			rightButton(input);
 	}
-
+	if (_vm->toggleRequested()) {
+		_vm->requestToggle(false);
+		_vm->setChattyAI(!_vm->isChattyAI());
+	}
 	InputHandler::handleInput(input, cursorSpot);
 }
 
diff --git a/engines/pegasus/neighborhood/neighborhood.h b/engines/pegasus/neighborhood/neighborhood.h
index 1d63a85821..adaa113fcc 100644
--- a/engines/pegasus/neighborhood/neighborhood.h
+++ b/engines/pegasus/neighborhood/neighborhood.h
@@ -292,7 +292,7 @@ protected:
 
 	virtual void createNeighborhoodSpots();
 
-	void resetLastExtra() { _lastExtra = -1; }
+	void resetLastExtra() { _lastExtra = 0xffffffff; }
 
 	virtual void throwAwayInterface();
 
@@ -354,7 +354,7 @@ protected:
 	AlternateID _currentAlternate;
 	HotSpotActivationID _currentActivation;
 
-	int32 _lastExtra;
+	ExtraID _lastExtra;
 	DeathReason _extraDeathReason;
 
 	// Graphics
diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp
index 33d61c766e..46481a948d 100644
--- a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp
+++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp
@@ -24,6 +24,7 @@
  */
 
 #include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/neighborhood/norad/constants.h"
 #include "pegasus/neighborhood/norad/norad.h"
 #include "pegasus/neighborhood/norad/alpha/ecrmonitor.h"
@@ -216,6 +217,9 @@ void NoradAlphaECRMonitor::closeInteraction() {
 	_ecrPan.stopDisplaying();
 	_ecrPan.releasePanorama();
 	_ecrPanCallBack.releaseCallBack();
+
+	if (g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB33", kArthurNoradAtSecurityMonitor);
 }
 
 } // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp
index bc78fd9af9..e5c9063c31 100644
--- a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp
+++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp
@@ -25,6 +25,7 @@
 
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/inventory/airmask.h"
 #include "pegasus/neighborhood/norad/constants.h"
 #include "pegasus/neighborhood/norad/alpha/fillingstation.h"
@@ -194,6 +195,8 @@ void NoradAlphaFillingStation::splashFinished() {
 
 void NoradAlphaFillingStation::intakeWarningFinished() {
 	setStaticState(kFSMainMenu, kMainMenu);
+	if (g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA29", kArthurNoradSawIntakeWarning);
 }
 
 void NoradAlphaFillingStation::showIntakeInProgress(uint16 numSeconds) {
diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp
index fd8d19e362..d5e2dc4dc1 100644
--- a/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp
+++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp
@@ -23,19 +23,27 @@
  *
  */
 
+#include "pegasus/cursor.h"
 #include "pegasus/energymonitor.h"
 #include "pegasus/gamestate.h"
+#include "pegasus/interface.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/inventory/airmask.h"
 #include "pegasus/neighborhood/norad/constants.h"
 #include "pegasus/neighborhood/norad/subcontrolroom.h"
 #include "pegasus/neighborhood/norad/alpha/ecrmonitor.h"
 #include "pegasus/neighborhood/norad/alpha/fillingstation.h"
 #include "pegasus/neighborhood/norad/alpha/noradalpha.h"
+#include "pegasus/neighborhood/norad/alpha/subchase.h"
 
 namespace Pegasus {
 
+static const ExtraID kShowThermalScan = 1000;
+
+static const HotSpotID kThermalScanHotSpotID = 10000;
+
 const uint32 NoradAlpha::_noradAlphaClawExtras[22] = {
 	kN22ClawFromAToB,
 	kN22ClawALoop,
@@ -61,7 +69,9 @@ const uint32 NoradAlpha::_noradAlphaClawExtras[22] = {
 	kN22ClawDClockwise
 };
 
-NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Alpha", kNoradAlphaID) {
+NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner)
+	: Norad(nextHandler, owner, "Norad Alpha", kNoradAlphaID),
+	_thermalScanSpot(kThermalScanHotSpotID), _extraMovie(kNoDisplayElement) {
 	_elevatorUpRoomID = kNorad11South;
 	_elevatorDownRoomID = kNorad12South;
 	_elevatorUpSpotID = kNorad12ElevatorUpSpotID;
@@ -82,10 +92,10 @@ NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner) : Norad(
 	_lowerPressureDoorDownSpotID = kAlphaLowerPressureDoorDownSpotID;
 	_lowerPressureDoorAbortSpotID = kNorad21WestOutSpotID;
 
-	_pressureSoundIn = kPressureDoorIntro1In;
-	_pressureSoundOut = kPressureDoorIntro1Out;
-	_equalizeSoundIn = kPressureDoorIntro2In;
-	_equalizeSoundOut = kPressureDoorIntro2Out;
+	_pressureSoundIn = kAlphaPressureDoorIntro1In;
+	_pressureSoundOut = kAlphaPressureDoorIntro1Out;
+	_equalizeSoundIn = kAlphaPressureDoorIntro2In;
+	_equalizeSoundOut = kAlphaPressureDoorIntro2Out;
 	_accessDeniedIn = kAlphaAccessDeniedIn;
 	_accessDeniedOut = kAlphaAccessDeniedOut;
 
@@ -98,9 +108,22 @@ NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner) : Norad(
 	setIsItemTaken(kGasCanister);
 }
 
+NoradAlpha::~NoradAlpha() {
+	if (_vm->isDVD())
+		_vm->getAllHotspots().remove(&_thermalScanSpot);
+}
+
 void NoradAlpha::init() {
 	Norad::init();
 
+	_extraMovieCallBack.setNotification(&_neighborhoodNotification);
+
+	if (_vm->isDVD()) {
+		_thermalScanSpot.setArea(Common::Rect(216, 112, 336, 312));
+		_thermalScanSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+		_vm->getAllHotspots().push_back(&_thermalScanSpot);
+	}
+
 	Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID);
 	hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag);
 	HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasCanisterSpotID);
@@ -209,7 +232,7 @@ void NoradAlpha::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSp
 }
 
 void NoradAlpha::playClawMonitorIntro() {
-	playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut);
+	playSpotSoundSync(kAlphaLoadClawIntroIn, kAlphaLoadClawIntroOut);
 }
 
 GameInteraction *NoradAlpha::makeInteraction(const InteractionID interactionID) {
@@ -218,6 +241,8 @@ GameInteraction *NoradAlpha::makeInteraction(const InteractionID interactionID)
 		return new NoradAlphaECRMonitor(this);
 	case kNoradFillingStationInteractionID:
 		return new NoradAlphaFillingStation(this);
+	case kNoradSubChaseInteractionID:
+		return new SubChase(this);
 	default:
 		break;
 	}
@@ -250,17 +275,27 @@ void NoradAlpha::loadAmbientLoops() {
 				play unmanned loop
 */
 
-	if (!GameState.getNoradSeenTimeStream())
+	if (!GameState.getNoradSeenTimeStream() || !g_interface || _vm->getEnergyDeathReason() == kDeathSubDestroyed)
 		return;
 
 	RoomID room = GameState.getCurrentRoom();
 	if (GameState.getNoradGassed()) {
-		if (room >= kNorad11 && room <= kNorad19West)
-			loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
-		else if (room >= kNorad21 && room <= kNorad22West)
-			loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
-		else
-			loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
+		if (room >= kNorad11 && room <= kNorad19West) {
+			if (_vm->isDVD())
+				loadLoopSound1("Sounds/Norad/NEW SUB AMB.44K.AIFF", kNoradWarningVolume * 3);
+			else
+				loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
+		} else if (room >= kNorad21 && room <= kNorad22West) {
+			if (_vm->isDVD())
+				loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.32K.AIFF", kNoradWarningVolume * 3);
+			else
+				loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
+		} else {
+			if (_vm->isDVD())
+				loadLoopSound1("Sounds/Norad/WARNING LOOP.32K.AIFF", kNoradWarningVolume);
+			else
+				loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
+		}
 	} else {
 		loadLoopSound1("");
 	}
@@ -322,8 +357,14 @@ void NoradAlpha::arriveAt(const RoomID room, const DirectionConstant direction)
 	case kNorad04:
 		arriveAtNorad04();
 		break;
+	case kNorad07:
+		if (_vm->isDVD() && GameState.getLastRoom() == kNorad06)
+			startExtraSequence(kShowThermalScan, kExtraCompletedFlag, kFilterNoInput);
+		break;
 	case kNorad07North:
 		GameState.setScoringSawUnconsciousOperator(true);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA70", kArthurNoradSawUnconsciousOperator);
 		break;
 	case kNorad11:
 		GameState.setScoringWentThroughPressureDoor(true);
@@ -337,11 +378,30 @@ void NoradAlpha::arriveAt(const RoomID room, const DirectionConstant direction)
 }
 
 void NoradAlpha::arriveAtNorad01() {
-	if (!GameState.getNoradSeenTimeStream() && GameState.getCurrentDirection() == kSouth) {
-		GameState.setNoradN22MessagePlayed(false);
-		requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput);
-		// You are no match for me, human.
-		requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput);
+	Item *argonCanister, *nitrogenCanister;
+
+	switch (GameState.getCurrentDirection()) {
+	case kSouth:
+		if (!GameState.getNoradSeenTimeStream()) {
+			GameState.setNoradN22MessagePlayed(false);
+			requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput);
+			// You are no match for me, human.
+			requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput);
+		}
+		break;
+	case kEast:
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA33", kArthurNoradAtSecurityMonitor);
+		break;
+	case kWest:
+		if (GameState.getLastRoom() == kNorad01West) {
+			argonCanister = g_allItems.findItemByID(kArgonCanister);
+			nitrogenCanister = g_allItems.findItemByID(kNitrogenCanister);
+			if (((GameState.isTakenItemID(kArgonCanister) && argonCanister->getItemState() != kArgonFull) ||
+				(GameState.isTakenItemID(kNitrogenCanister) && nitrogenCanister->getItemState() != kNitrogenFull)) && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA34", kArthurNoradDidntFillCanisters);
+		}
+		break;
 	}
 }
 
@@ -366,20 +426,103 @@ void NoradAlpha::arriveAtNorad22() {
 	}
 }
 
+void NoradAlpha::turnTo(const DirectionConstant direction) {
+	Norad::turnTo(direction);
+	if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad01, kEast) && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA33", kArthurNoradAtSecurityMonitor);
+}
+
 void NoradAlpha::bumpIntoWall() {
 	requestSpotSound(kAlphaBumpIntoWallIn, kAlphaBumpIntoWallOut, kFilterNoInput, 0);
 	Neighborhood::bumpIntoWall();
 }
 
+void NoradAlpha::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
+	TimeValue segmentStart = 0, segmentStop = 0;
+	bool loopSequence = false;
+	Common::Rect pushBounds;
+	NotificationFlags extraFlags;
+
+	switch (extraID) {
+	case kShowThermalScan:
+		_turnPush.getBounds(pushBounds);
+
+		switch (extraID) {
+		case kShowThermalScan:
+			_extraMovie.initFromMovieFile("Images/Norad Alpha/N07NS.movie");
+			segmentStart = 0;
+			segmentStop = _extraMovie.getDuration();
+			loopSequence = false;
+			break;
+		default:
+			break;
+		}
+
+		_lastExtra = extraID;
+		_turnPush.hide();
+
+		if (!loopSequence && g_AIArea)
+			g_AIArea->lockAIOut();
+
+		extraFlags = flags;
+		_interruptionFilter = interruptionFilter;
+		// Stop the nav movie before doing anything else
+		_navMovie.stop();
+		_navMovie.stopDisplaying();
+
+		_extraMovie.setVolume(_vm->getSoundFXLevel());
+		_extraMovie.moveElementTo(pushBounds.left, pushBounds.top);
+		_extraMovie.setDisplayOrder(kNavMovieOrder + 1);
+		_extraMovie.startDisplaying();
+		_extraMovie.show();
+		_extraMovie.setFlags(0);
+		_extraMovie.setSegment(segmentStart, segmentStop);
+		_extraMovie.setTime(segmentStart);
+		if (loopSequence)
+			_extraMovie.setFlags(kLoopTimeBase);
+		else
+			extraFlags |= kNeighborhoodMovieCompletedFlag;
+		_extraMovieCallBack.cancelCallBack();
+		_extraMovieCallBack.initCallBack(&_extraMovie, kCallBackAtExtremes);
+		if (extraFlags != 0) {
+			_extraMovieCallBack.setCallBackFlag(extraFlags);
+			_extraMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+		}
+		_extraMovie.start();
+		break;
+	default:
+		Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
+		break;
+	}
+}
+
 void NoradAlpha::receiveNotification(Notification *notification, const NotificationFlags flags) {
 	if ((flags & kExtraCompletedFlag) != 0) {
 		switch (_lastExtra) {
+		case kShowThermalScan:
+			_interruptionFilter = kFilterAllInput;
+			_extraMovie.stopDisplaying();
+			_extraMovie.releaseMovie();
+			_navMovie.startDisplaying();
+			break;
+		case kNorad19ExitToSub:
+			if (_vm->isDVD()) {
+				_interruptionFilter = kFilterAllInput;
+				_vm->_cursor->hide();
+				setNextHandler(_vm);
+				throwAwayInterface();
+				loadLoopSound1("");
+				newInteraction(kNoradSubChaseInteractionID);
+				GameState.setScoringEnteredSub(true);
+			}
+			break;
 		case kNoradArriveFromTSA:
 			GameState.setNoradSeenTimeStream(true);
 			loadAmbientLoops();
 			break;
 		case kNorad01RobotTaunt:
-			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption);
+			if (_vm->isChattyAI())
+				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption);
 			_interruptionFilter = kFilterAllInput;
 			makeContinuePoint();
 			break;
@@ -398,11 +541,38 @@ void NoradAlpha::receiveNotification(Notification *notification, const Notificat
 			startExtraSequence(kNorad22SouthFinish, kExtraCompletedFlag, kFilterNoInput);
 			break;
 		case kNorad22SouthFinish:
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB29", kArthurNoradSawSubMessage);
 			_interruptionFilter = kFilterAllInput;
 			// Force ArriveAt to do its thing...
 			GameState.setCurrentRoom(kNorad21);
 			arriveAt(kNorad22, kSouth);
 			break;
+		case kN22ClawFromAToB:
+		case kN22ClawAPinch:
+		case kN22ClawACounterclockwise:
+		case kN22ClawAClockwise:
+		case kN22ClawFromBToA:
+		case kN22ClawFromBToC:
+		case kN22ClawFromBToD:
+		case kN22ClawBPinch:
+		case kN22ClawBCounterclockwise:
+		case kN22ClawBClockwise:
+		case kN22ClawFromCToB:
+		case kN22ClawCPinch:
+		case kN22ClawCCounterclockwise:
+		case kN22ClawCClockwise:
+		case kN22ClawFromDToB:
+		case kN22ClawDPinch:
+		case kN22ClawDCounterclockwise:
+		case kN22ClawDClockwise:
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA64", kArthurNoradPlayedWithClaw);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA66", kArthurNoradPlayedWithClaw);
+			}
+			break;
 		default:
 			break;
 		}
@@ -501,10 +671,19 @@ TimeValue NoradAlpha::getViewTime(const RoomID room, const DirectionConstant dir
 	return Norad::getViewTime(room, direction);
 }
 
+void NoradAlpha::setSoundFXLevel(const uint16 level) {
+	Neighborhood::setSoundFXLevel(level);
+
+	if (_extraMovie.isMovieValid())
+		_extraMovie.setVolume(level);
+}
+
 void NoradAlpha::turnOnFillingStation() {
 	if (GameState.getCurrentRoom() == kNorad01West && !GameState.getNoradFillingStationOn()) {
 		GameState.setNoradFillingStationOn(true);
 		updateViewFrame();
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA72", kArthurNoradSawFillingStation);
 	}
 }
 
@@ -556,6 +735,10 @@ void NoradAlpha::activateHotspots() {
 			}
 		}
 		break;
+	case MakeRoomView(kNorad07, kNorth):
+		if (_vm->isDVD())
+			_vm->getAllHotspots().activateOneHotspot(kThermalScanHotSpotID);
+		break;
 	case MakeRoomView(kNorad10, kEast):
 		if (GameState.isCurrentDoorOpen())
 			_vm->getAllHotspots().deactivateOneHotspot(kNorad10DoorSpotID);
@@ -581,6 +764,9 @@ void NoradAlpha::clickInHotspot(const Input &input, const Hotspot *cursorSpot) {
 				hotspotEntry->hotspotItem = item->getObjectID();
 			}
 		}
+	} else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad07, kNorth) &&
+				cursorSpot->getObjectID() == kThermalScanHotSpotID) {
+		startExtraSequence(kShowThermalScan, kExtraCompletedFlag, kFilterNoInput);
 	}
 }
 
diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.h b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h
index 5c709b9723..97a154ad83 100644
--- a/engines/pegasus/neighborhood/norad/alpha/noradalpha.h
+++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h
@@ -31,11 +31,13 @@
 namespace Pegasus {
 
 class Item;
+class SubChase;
 
 class NoradAlpha : public Norad {
+friend class SubChase;
 public:
 	NoradAlpha(InputHandler *, PegasusEngine *);
-	~NoradAlpha() override {}
+	~NoradAlpha() override;
 
 	void init() override;
 	void start() override;
@@ -77,6 +79,8 @@ public:
 
 	void checkContinuePoint(const RoomID, const DirectionConstant) override;
 
+	void setSoundFXLevel(const uint16) override;
+
 	bool canSolve() override;
 	void doSolve() override;
 
@@ -90,6 +94,9 @@ protected:
 	virtual void arriveAtNorad22();
 
 	void arriveAt(const RoomID, const DirectionConstant) override;
+	void turnTo(const DirectionConstant) override;
+
+	void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits) override;
 
 	void getZoomEntry(const HotSpotID, ZoomTable::Entry &) override;
 	TimeValue getViewTime(const RoomID, const DirectionConstant) override;
@@ -102,6 +109,11 @@ protected:
 
 	void bumpIntoWall() override;
 
+	Hotspot _thermalScanSpot;
+
+	Movie _extraMovie;
+	NotificationCallBack _extraMovieCallBack;
+
 	Item *_fillingStationItem;
 
 	bool _subPrepFailed;
diff --git a/engines/pegasus/neighborhood/norad/alpha/subchase.cpp b/engines/pegasus/neighborhood/norad/alpha/subchase.cpp
new file mode 100644
index 0000000000..36eb3d6780
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/subchase.cpp
@@ -0,0 +1,459 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#include "pegasus/pegasus.h"
+#include "pegasus/gamestate.h"
+#include "pegasus/neighborhood/norad/alpha/subchase.h"
+#include "pegasus/neighborhood/norad/alpha/noradalpha.h"
+#include "pegasus/neighborhood/norad/constants.h"
+
+namespace Pegasus {
+
+static const TimeScale kSubChaseScale = 600;
+
+static const DisplayOrder kSubChaseOrder = 27000;
+
+// Segment start and end points.
+
+static const TimeValue kIntroStart = 0;
+static const TimeValue kIntroEnd = 2400;
+static const TimeValue kDialogStart = kIntroEnd;
+static const TimeValue kDialogEnd = 20920;
+static const TimeValue kBranch1Start = kDialogEnd;
+static const TimeValue kBranch1End = 32120;
+static const TimeValue kBranch2LeftStart = kBranch1End;
+static const TimeValue kBranch2LeftEnd = 48080;
+static const TimeValue kBranch3Start = kBranch2LeftEnd;
+static const TimeValue kBranch3End = 61080;
+static const TimeValue kBranch4Start = kBranch3End;
+static const TimeValue kBranch4End = 84080;
+static const TimeValue kBranch5Start = kBranch4End;
+static const TimeValue kBranch5End = 94840;
+static const TimeValue kBranch6Start = kBranch5End;
+static const TimeValue kBranch6End = 106040;
+static const TimeValue kBranch7LeftStart = kBranch6End;
+static const TimeValue kBranch7LeftEnd = 118840;
+static const TimeValue kExitStart = kBranch7LeftEnd;
+static const TimeValue kExitEnd = 133200;
+static const TimeValue kBranch2RightStart = 133200;
+static const TimeValue kBranch2RightEnd = 149160;
+static const TimeValue kBranch7RightStart = 168000;
+static const TimeValue kBranch7RightEnd = 180800;
+
+// Death start and end points.
+
+static const TimeValue kDeath4Start = 149160;
+static const TimeValue kDeath4End = 158040;
+static const TimeValue kDeath5Start = kDeath4End;
+static const TimeValue kDeath5End = 163760;
+static const TimeValue kDeath6Start = kDeath5End;
+static const TimeValue kDeath6End = 168000;
+static const TimeValue kDeath7Start = 180800;
+static const TimeValue kDeath7End = 187040;
+
+// Chase state.
+
+enum {
+	kSubDialog,
+	kSubBranch1,
+	kSubBranch2Left,
+	kSubBranch2Right,
+	kSubBranch3,
+	kSubBranch4,
+	kSubBranch5,
+	kSubBranch6,
+	kSubBranch7Left,
+	kSubBranch7Right,
+	kSubExit
+};
+
+void HintTimerEvent::fire() {
+	subChase->hintTimerExpired(*this);
+}
+
+void BlinkTimerEvent::fire() {
+	subChase->blinkTimerExpired(*this);
+}
+
+SubChase::SubChase(Neighborhood *handler) : ChaseInteraction(kNoradSubChaseInteractionID, handler,
+						kNoradSubChaseNotificationID, (PegasusEngine *)g_engine), _subMovie(kNoDisplayElement),
+						_hintPict(kNoDisplayElement), _blinkPict(kNoDisplayElement), _canSteerSub(true) {
+}
+
+void SubChase::setSoundFXLevel(const uint16 fxLevel) {
+	_subMovie.setVolume(fxLevel);
+}
+
+void SubChase::openInteraction() {
+	_subMovie.initFromMovieFile("Images/Norad Alpha/Sub Chase Movie");
+	_subMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+	_subMovie.moveElementTo(0, 0);
+	_subMovie.setDisplayOrder(kSubChaseOrder);
+	_subMovie.startDisplaying();
+	_subMovie.show();
+
+	_subCallBack.setNotification(&_chaseNotification);
+	_subCallBack.initCallBack(&_subMovie, kCallBackAtExtremes);
+
+	ChaseInteraction::openInteraction();
+
+	_steerPict.setDisplayOrder(kSubChaseOrder + 1);
+	_steerPict.moveElementTo(kNoradSubSteerLeft, kNoradSubSteerTop);
+
+	_hintPict.initFromPICTFile("Images/Norad Alpha/Sub Chase steerk1.pict", true);
+	_hintPict.setDisplayOrder(kSubChaseOrder + 1);
+	_hintPict.moveElementTo(kNoradSubHintLeft, kNoradSubHintTop);
+	_blinkPict.initFromPICTFile("Images/Norad Alpha/Sub Chase steerk0.pict", true);
+	_blinkPict.setDisplayOrder(kSubChaseOrder + 1);
+	_blinkPict.moveElementTo(kNoradSubHintLeft, kNoradSubHintTop);
+}
+
+void SubChase::initInteraction() {
+	_canSteerSub = !GameState.getWalkthroughMode();
+
+	_owner->playMovieSegment(&_subMovie, kIntroStart, kIntroEnd);
+
+	_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	if (_canSteerSub) {
+		_steerPict.startDisplaying();
+		_hintPict.startDisplaying();
+		_blinkPict.startDisplaying();
+		startHintTimer(20000 - kDialogStart, kSubChaseScale, kStartedHint);
+		_subCallBack.setCallBackFlag(kChaseEnteredBranchZone);
+		_subMovie.setSegment(kDialogStart, kBranch1End - kDecisionTime);
+	} else {
+		_subCallBack.setCallBackFlag(kChaseFinished);
+		_subMovie.setSegment(kDialogStart, kExitEnd);
+	}
+
+	_subState = kSubDialog;
+	_subMovie.setTime(kDialogStart);
+	_subMovie.start();
+
+	ChaseInteraction::initInteraction();
+}
+
+void SubChase::closeInteraction() {
+	_subMovie.stop();
+	_subMovie.stopDisplaying();
+	_subMovie.releaseMovie();
+	_subCallBack.releaseCallBack();
+
+	_hintPict.hide();
+	_hintPict.deallocateSurface();
+
+	_blinkPict.hide();
+	_blinkPict.deallocateSurface();
+
+	ChaseInteraction::closeInteraction();
+}
+
+void SubChase::receiveNotification(Notification *notification, const NotificationFlags flags) {
+	if (notification == &_chaseNotification && flags == kChaseFinished) {
+		if (_subState != kSubDialog && _subState != kSubExit) {
+			// We died
+			((NoradAlpha *)_owner)->die(kDeathSubDestroyed);
+		} else {
+			_subMovie.stopDisplaying();
+			((PegasusEngine *)g_engine)->_gfx->enableErase();
+			((PegasusEngine *)g_engine)->_gfx->updateDisplay();
+			((PegasusEngine *)g_engine)->_gfx->disableErase();
+			((PegasusEngine *)g_engine)->jumpToNewEnvironment(kNoradDeltaID, kNorad41, kEast);
+		}
+	}
+	ChaseInteraction::receiveNotification(notification, flags);
+}
+
+void SubChase::handleInput(const Input &input, const Hotspot *cursorSpot) {
+	if (_subMovie.getTime() < kBranch1Start && input.anyInput()) {
+		if (_canSteerSub) {
+			if (!_blinkFuse.isFuseLit()) {
+				// If the hint is not blinking then it must not be running, so display it
+				_hintPict.show();
+				startBlinkTimer(10, 10, kEnteredBlinkState);
+			}
+			_subState = kSubBranch1;
+			startHintTimer(3000, kSubChaseScale, kEndedHint);
+		} else {
+			_subState = kSubExit;
+		}
+		_subMovie.setTime(kBranch1Start);
+	} else {
+		ChaseInteraction::handleInput(input, cursorSpot);
+	}
+}
+
+void SubChase::setUpBranch() {
+	TimeValue branchStart, branchEnd;
+
+	branchStart = 0;
+	branchEnd = 0;
+	switch (_subState) {
+	case kSubDialog:
+	case kSubBranch1:
+		branchStart = kBranch1End - kDecisionTime;
+		branchEnd = kBranch1End;
+		break;
+	case kSubBranch2Left:
+		branchStart = kBranch2LeftEnd - kDecisionTime;
+		branchEnd = kBranch2LeftEnd;
+		break;
+	case kSubBranch2Right:
+		branchStart = kBranch2RightEnd - kDecisionTime;
+		branchEnd = kBranch2RightEnd;
+		break;
+	case kSubBranch3:
+		branchStart = kBranch3End - kDecisionTime;
+		branchEnd = kBranch3End;
+		break;
+	case kSubBranch4:
+		branchStart = kBranch4End - kDecisionTime;
+		branchEnd = kBranch4End;
+		break;
+	case kSubBranch5:
+		branchStart = kBranch5End - kDecisionTime;
+		branchEnd = kBranch5End;
+		break;
+	case kSubBranch6:
+		branchStart = kBranch6End - kDecisionTime;
+		branchEnd = kBranch6End;
+		break;
+	case kSubBranch7Left:
+		branchStart = kBranch7LeftEnd - kDecisionTime;
+		branchEnd = kBranch7LeftEnd;
+		break;
+	case kSubBranch7Right:
+		branchStart = kBranch7RightEnd - kDecisionTime;
+		branchEnd = kBranch7RightEnd;
+		break;
+	default:
+		break;
+	}
+
+	_subMovie.setSegment(branchStart, branchEnd);
+
+	_subCallBack.setCallBackFlag(kChaseExitedBranchZone);
+	_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void SubChase::branchLeft() {
+	TimeValue branchStart, branchEnd;
+	NotificationFlags flag;
+
+	branchStart = 0;
+	branchEnd = 0;
+	flag = 0;
+	switch (_subState) {
+	case kSubDialog:
+	case kSubBranch1:
+		branchStart = kBranch2LeftStart;
+		branchEnd = kBranch2LeftEnd;
+		// Don't show the controls hint when we approach the whale
+		// since the branch segments here are identical
+		flag = kChaseExitedBranchZone;
+		_subState = kSubBranch2Left;
+		break;
+	case kSubBranch2Left:
+	case kSubBranch2Right:
+		branchStart = kBranch3Start;
+		branchEnd = kBranch3End - kDecisionTime;
+		flag = kChaseEnteredBranchZone;
+		_subState = kSubBranch3;
+		break;
+	case kSubBranch3:
+		branchStart = kBranch4Start;
+		branchEnd = kBranch4End - kDecisionTime;
+		flag = kChaseEnteredBranchZone;
+		_subState = kSubBranch4;
+		break;
+	case kSubBranch4:
+		branchStart = kDeath5Start;
+		branchEnd = kDeath5End;
+		flag = kChaseFinished;
+		_subState = kSubBranch5;
+		break;
+	case kSubBranch5:
+		branchStart = kBranch6Start;
+		branchEnd = kBranch6End - kDecisionTime;
+		flag = kChaseEnteredBranchZone;
+		_subState = kSubBranch6;
+		break;
+	case kSubBranch6:
+		branchStart = kBranch7LeftStart;
+		branchEnd = kBranch7LeftEnd;
+		flag = kChaseExitedBranchZone;
+		_subState = kSubBranch7Left;
+		break;
+	case kSubBranch7Left:
+	case kSubBranch7Right:
+		branchStart = kExitStart;
+		branchEnd = kExitEnd;
+		flag = kChaseFinished;
+		_subState = kSubExit;
+		break;
+	default:
+		break;
+	}
+
+	_subMovie.setSegment(branchStart, branchEnd);
+	_subMovie.setTime(branchStart);
+
+	_subCallBack.setCallBackFlag(flag);
+	_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void SubChase::branchRight() {
+	TimeValue branchStart, branchEnd;
+	NotificationFlags flag;
+
+	branchStart = 0;
+	branchEnd = 0;
+	flag = 0;
+	switch (_subState) {
+	case kSubDialog:
+	case kSubBranch1:
+		branchStart = kBranch2RightStart;
+		branchEnd = kBranch2RightEnd;
+		// Don't show the controls hint when we approach the whale
+		// since the branch segments here are identical
+		flag = kChaseExitedBranchZone;
+		_subState = kSubBranch2Right;
+		break;
+	case kSubBranch2Left:
+	case kSubBranch2Right:
+		branchStart = kBranch3Start;
+		branchEnd = kBranch3End - kDecisionTime;
+		flag = kChaseEnteredBranchZone;
+		_subState = kSubBranch3;
+		break;
+	case kSubBranch3:
+		branchStart = kDeath4Start;
+		branchEnd = kDeath4End;
+		flag = kChaseFinished;
+		_subState = kSubBranch4;
+		break;
+	case kSubBranch4:
+		branchStart = kBranch5Start;
+		branchEnd = kBranch5End - kDecisionTime;
+		flag = kChaseEnteredBranchZone;
+		_subState = kSubBranch5;
+		break;
+	case kSubBranch5:
+		branchStart = kDeath6Start;
+		branchEnd = kDeath6End;
+		flag = kChaseFinished;
+		_subState = kSubBranch6;
+		break;
+	case kSubBranch6:
+		if (((PegasusEngine *)g_engine)->getRandomBit()) {
+			branchStart = kBranch7RightStart;
+			branchEnd = kBranch7RightEnd;
+			flag = kChaseExitedBranchZone;
+		} else {
+			branchStart = kDeath7Start;
+			branchEnd = kDeath7End;
+			flag = kChaseFinished;
+		}
+		_subState = kSubBranch7Right;
+		break;
+	case kSubBranch7Left:
+	case kSubBranch7Right:
+		branchStart = kExitStart;
+		branchEnd = kExitEnd;
+		flag = kChaseFinished;
+		_subState = kSubExit;
+		break;
+	default:
+		break;
+	}
+
+	_subMovie.setSegment(branchStart, branchEnd);
+	_subMovie.setTime(branchStart);
+
+	_subCallBack.setCallBackFlag(flag);
+	_subCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+}
+
+void SubChase::dontBranch() {
+	if (((PegasusEngine *)g_engine)->getRandomBit())
+		branchLeft();
+	else
+		branchRight();
+}
+
+void SubChase::startHintTimer(TimeValue time, TimeScale scale, HintTimerCode code) {
+	if (_canSteerSub) {
+		_hintFuse.primeFuse(time, scale);
+		_hintEvent.subChase = this;
+		_hintEvent.theEvent = code;
+		_hintFuse.setFunctor(new Common::Functor0Mem<void, HintTimerEvent>(&_hintEvent, &HintTimerEvent::fire));
+		_hintFuse.lightFuse();
+	}
+}
+
+void SubChase::hintTimerExpired(HintTimerEvent &event) {
+	switch (event.theEvent) {
+	case kStartedHint:
+		_hintPict.show();
+		startBlinkTimer(10, 10, kEnteredBlinkState);
+		startHintTimer(3920, kSubChaseScale, kEndedHint);
+		break;
+	case kEndedHint:
+		_hintPict.hide();
+		_blinkPict.hide();
+		_blinkFuse.stopFuse();
+		break;
+	default:
+		break;
+	}
+}
+
+void SubChase::startBlinkTimer(TimeValue time, TimeScale scale, BlinkTimerCode code) {
+	_blinkFuse.primeFuse(time, scale);
+	_blinkEvent.subChase = this;
+	_blinkEvent.theEvent = code;
+	_blinkFuse.setFunctor(new Common::Functor0Mem<void, BlinkTimerEvent>(&_blinkEvent, &BlinkTimerEvent::fire));
+	_blinkFuse.lightFuse();
+}
+
+void SubChase::blinkTimerExpired(BlinkTimerEvent &event) {
+	switch (event.theEvent) {
+	case kEnteredBlinkState:
+		_hintPict.hide();
+		_blinkPict.show();
+		startBlinkTimer(5, 10, kExitedBlinkState);
+		break;
+	case kExitedBlinkState:
+		_blinkPict.hide();
+		_hintPict.show();
+		startBlinkTimer(10, 10, kEnteredBlinkState);
+		break;
+	default:
+		break;
+	}
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/neighborhood/norad/alpha/subchase.h b/engines/pegasus/neighborhood/norad/alpha/subchase.h
new file mode 100644
index 0000000000..8907dc8ac0
--- /dev/null
+++ b/engines/pegasus/neighborhood/norad/alpha/subchase.h
@@ -0,0 +1,111 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-2013 Presto Studios, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_SUBCHASE_H
+#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_SUBCHASE_H
+
+#include "pegasus/chase.h"
+#include "pegasus/movie.h"
+
+namespace Pegasus {
+
+class NoradAlpha;
+class SubChase;
+
+enum HintTimerCode {
+	kStartedHint,
+	kEndedHint
+};
+
+struct HintTimerEvent {
+	SubChase *subChase;
+	HintTimerCode theEvent;
+
+	void fire();
+};
+
+enum BlinkTimerCode {
+	kEnteredBlinkState,
+	kExitedBlinkState
+};
+
+struct BlinkTimerEvent {
+	SubChase *subChase;
+	BlinkTimerCode theEvent;
+
+	void fire();
+};
+
+class SubChase : public ChaseInteraction {
+friend class NoradAlpha;
+friend struct HintTimerEvent;
+friend struct BlinkTimerEvent;
+public:
+
+	SubChase(Neighborhood *);
+	virtual ~SubChase() {}
+
+	void setSoundFXLevel(const uint16);
+
+	void handleInput(const Input &, const Hotspot *);
+
+protected:
+
+	void openInteraction();
+	void initInteraction();
+	void closeInteraction();
+
+	void receiveNotification(Notification *, const NotificationFlags);
+
+	void startBranching();
+	void setUpBranch();
+	void branchLeft();
+	void branchRight();
+	void dontBranch();
+
+	void startHintTimer(TimeValue, TimeScale, HintTimerCode);
+	void hintTimerExpired(HintTimerEvent &);
+
+	void startBlinkTimer(TimeValue, TimeScale, BlinkTimerCode);
+	void blinkTimerExpired(BlinkTimerEvent &);
+
+	Movie _subMovie;
+	NotificationCallBack _subCallBack;
+	Picture _hintPict;
+	Picture _blinkPict;
+	FuseFunction _hintFuse;
+	FuseFunction _blinkFuse;
+
+	HintTimerEvent _hintEvent;
+	BlinkTimerEvent _blinkEvent;
+
+	short _subState;
+
+	bool _canSteerSub;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/neighborhood/norad/constants.h b/engines/pegasus/neighborhood/norad/constants.h
index 52e889f2e0..0b03064c9d 100644
--- a/engines/pegasus/neighborhood/norad/constants.h
+++ b/engines/pegasus/neighborhood/norad/constants.h
@@ -216,6 +216,7 @@ static const InteractionID kNoradElevatorInteractionID = 3;
 static const InteractionID kNoradPressureDoorInteractionID = 4;
 static const InteractionID kNoradSubControlRoomInteractionID = 5;
 static const InteractionID kNoradSubPlatformInteractionID = 6;
+static const InteractionID kNoradSubChaseInteractionID = 7;
 
 /////////////////////////////////////////////
 //
@@ -265,6 +266,12 @@ static const CoordType kNoradLowerDownTop = kNavAreaTop + 212;
 static const CoordType kNoradPlatformLeft = kNavAreaLeft + 36;
 static const CoordType kNoradPlatformTop = kNavAreaTop + 87;
 
+static const CoordType kNoradSubSteerLeft = 288;
+static const CoordType kNoradSubSteerTop = 28;
+
+static const CoordType kNoradSubHintLeft = 480;
+static const CoordType kNoradSubHintTop = 240;
+
 static const CoordType kNoradSubControlLeft = kNavAreaLeft + 0;
 static const CoordType kNoradSubControlTop = kNavAreaTop + 84;
 
@@ -385,8 +392,9 @@ static const DisplayElementID kClawMonitorGreenBallID = kSubControlCWID + 1;
 
 // Norad Delta display IDs.
 
-static const DisplayElementID kGlobeMonitorID = kNeighborhoodDisplayID;
-static const DisplayElementID kGlobeMovieID = kGlobeMonitorID + 14;
+static const DisplayElementID kGlobeRobotID = kNeighborhoodDisplayID;
+static const DisplayElementID kGlobeMonitorID = kGlobeRobotID + 1;
+static const DisplayElementID kGlobeMovieID = kGlobeMonitorID + 1;
 static const DisplayElementID kGlobeCircleLeftID = kGlobeMovieID + 1;
 static const DisplayElementID kGlobeCircleRightID = kGlobeCircleLeftID + 1;
 static const DisplayElementID kGlobeCircleUpID = kGlobeCircleRightID + 1;
diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
index 3735f6d854..ad60c987fd 100644
--- a/engines/pegasus/neighborhood/norad/delta/globegame.cpp
+++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp
@@ -25,6 +25,7 @@
 
 #include "pegasus/cursor.h"
 #include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/neighborhood/norad/constants.h"
 #include "pegasus/neighborhood/norad/delta/globegame.h"
 #include "pegasus/neighborhood/norad/delta/noraddelta.h"
@@ -396,10 +397,22 @@ static const TimeValue kGlobeMovieStartTime = 2 * 2 * kNumLongSlices * 600 / 15;
 static const TimeValue kTimePerGlobeFrame = 40;
 
 static const NotificationFlags kGlobeSplash1Finished = 1;
-static const NotificationFlags kGlobeTimerExpired = kGlobeSplash1Finished << 1;
+static const NotificationFlags kGlobeRobot1Finished = kGlobeSplash1Finished << 1;
+static const NotificationFlags kGlobeRobot2Finished = kGlobeRobot1Finished << 1;
+static const NotificationFlags kGlobeRobot3Finished = kGlobeRobot2Finished << 1;
+static const NotificationFlags kGlobeRobot4Finished = kGlobeRobot3Finished << 1;
+static const NotificationFlags kGlobeRobot5Finished = kGlobeRobot4Finished << 1;
+static const NotificationFlags kGlobeRobot6Finished = kGlobeRobot5Finished << 1;
+static const NotificationFlags kGlobeTimerExpired = kGlobeRobot6Finished << 1;
 static const NotificationFlags kMaxDeactivatedFinished = kGlobeTimerExpired << 1;
 
 static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished |
+													kGlobeRobot1Finished |
+													kGlobeRobot2Finished |
+													kGlobeRobot3Finished |
+													kGlobeRobot4Finished |
+													kGlobeRobot5Finished |
+													kGlobeRobot6Finished |
 													kGlobeTimerExpired |
 													kMaxDeactivatedFinished;
 
@@ -451,10 +464,10 @@ float radiansToDegrees(float angle) {
 }
 
 GlobeGame::GlobeGame(Neighborhood *handler) : GameInteraction(kNoradGlobeGameInteractionID, handler),
-		_monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID), _upperNamesMovie(kGlobeUpperNamesID),
-		_lowerNamesMovie(kGlobeLowerNamesID), _globeNotification(kNoradGlobeNotificationID, (PegasusEngine *)g_engine),
-		_globeCircleLeft(kGlobeCircleLeftID), _globeCircleRight(kGlobeCircleRightID),
-		_globeCircleUp(kGlobeCircleUpID), _globeCircleDown(kGlobeCircleDownID),
+		_robotMovie(kGlobeRobotID), _monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID),
+		_upperNamesMovie(kGlobeUpperNamesID), _lowerNamesMovie(kGlobeLowerNamesID),
+		_globeNotification(kNoradGlobeNotificationID, (PegasusEngine *)g_engine), _globeCircleLeft(kGlobeCircleLeftID),
+		_globeCircleRight(kGlobeCircleRightID), _globeCircleUp(kGlobeCircleUpID), _globeCircleDown(kGlobeCircleDownID),
 		_motionHighlightLeft(kMotionHiliteLeftID), _motionHighlightRight(kMotionHiliteRightID),
 		_motionHighlightUp(kMotionHiliteUpID), _motionHighlightDown(kMotionHiliteDownID),
 		_targetHighlightUpperLeft(kTargetHiliteUpperLeftID), _targetHighlightUpperRight(kTargetHiliteUpperRightID),
@@ -469,6 +482,20 @@ void GlobeGame::setSoundFXLevel(const uint16 fxLevel) {
 }
 
 void GlobeGame::openInteraction() {
+	if (((PegasusEngine *)g_engine)->isDVD()) {
+		_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor1");
+		_robotMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+		_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+		_robotMovie.setDisplayOrder(kGlobeMonitorLayer);
+		_robotMovie.startDisplaying();
+		_robotMovie.show();
+
+		_robotCallBack.setNotification(&_globeNotification);
+		_robotCallBack.initCallBack(&_robotMovie, kCallBackAtExtremes);
+		_robotCallBack.setCallBackFlag(kGlobeRobot1Finished);
+		_robotCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+	}
+
 	_monitorMovie.initFromMovieFile("Images/Norad Delta/N79 Left Monitor");
 	_monitorMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
 	_monitorMovie.moveElementTo(kGlobeMonitorLeft, kGlobeMonitorTop);
@@ -579,11 +606,20 @@ void GlobeGame::openInteraction() {
 }
 
 void GlobeGame::initInteraction() {
+	if (((PegasusEngine *)g_engine)->isDVD())
+		_robotMovie.start();
 	_monitorMovie.start();
 	_monitorMovie.redrawMovieWorld();
 }
 
 void GlobeGame::closeInteraction() {
+	if (((PegasusEngine *)g_engine)->isDVD()) {
+		_robotMovie.stop();
+		_robotMovie.stopDisplaying();
+		_robotMovie.releaseMovie();
+		_robotCallBack.releaseCallBack();
+	}
+
 	_monitorMovie.stop();
 	_monitorMovie.stopDisplaying();
 	_monitorMovie.releaseMovie();
@@ -642,16 +678,18 @@ void GlobeGame::receiveNotification(Notification *notification, const Notificati
 	if (notification == _neighborhoodNotification) {
 		switch (_gameState) {
 		case kPlayingRobotIntro:
-			_monitorMovie.stop();
-			_monitorMovie.setSegment(0, _monitorMovie.getDuration());
-			_monitorMovie.setTime(kSplash2End * scale - 1);
-			_monitorMovie.redrawMovieWorld();
-			_monitorMovie.setFlags(0);
+			if (!((PegasusEngine *)g_engine)->isDVD()) {
+				_monitorMovie.stop();
+				_monitorMovie.setSegment(0, _monitorMovie.getDuration());
+				_monitorMovie.setTime(kSplash2End * scale - 1);
+				_monitorMovie.redrawMovieWorld();
+				_monitorMovie.setFlags(0);
 
-			_owner->requestDelay(1, 2, kFilterNoInput, 0);
-			_owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut,
-					kFilterNoInput, kSpotSoundCompletedFlag);
-			_gameState = kPlayingStrikeAuthorized;
+				_owner->requestDelay(1, 2, kFilterNoInput, 0);
+				_owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut,
+						kFilterNoInput, kSpotSoundCompletedFlag);
+				_gameState = kPlayingStrikeAuthorized;
+			}
 			break;
 		case kPlayingStrikeAuthorized:
 			_monitorMovie.setSegment(kSplash3Start * scale, kSplash3Stop * scale);
@@ -761,20 +799,80 @@ void GlobeGame::receiveNotification(Notification *notification, const Notificati
 
 			switch (_currentSiloIndex) {
 			case 3:
-				_owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut,
-						kFilterNoInput, kSpotSoundCompletedFlag);
+				if (!((PegasusEngine *)g_engine)->isDVD()) {
+					_owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut,
+							kFilterNoInput, kSpotSoundCompletedFlag);
+				} else {
+					_robotMovie.hide();
+					_robotMovie.stopDisplaying();
+					_robotMovie.releaseMovie();
+
+					_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor2");
+					_robotMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+					_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+					_robotMovie.startDisplaying();
+					_robotMovie.show();
+					_robotMovie.start();
+
+					_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
+				}
 				break;
 			case 5:
-				_owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput,
-						kSpotSoundCompletedFlag);
+				if (!((PegasusEngine *)g_engine)->isDVD()) {
+					_owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput,
+							kSpotSoundCompletedFlag);
+				} else {
+					_robotMovie.hide();
+					_robotMovie.stopDisplaying();
+					_robotMovie.releaseMovie();
+
+					_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor3");
+					_robotMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+					_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+					_robotMovie.startDisplaying();
+					_robotMovie.show();
+					_robotMovie.start();
+
+					_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
+				}
 				break;
 			case 7:
-				_owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput,
-						kSpotSoundCompletedFlag);
+				if (!((PegasusEngine *)g_engine)->isDVD()) {
+					_owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput,
+							kSpotSoundCompletedFlag);
+				} else {
+					_robotMovie.hide();
+					_robotMovie.stopDisplaying();
+					_robotMovie.releaseMovie();
+
+					_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor4");
+					_robotMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+					_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+					_robotMovie.startDisplaying();
+					_robotMovie.show();
+					_robotMovie.start();
+
+					_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
+				}
 				break;
 			case 9:
-				_owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut,
-						kFilterNoInput, kSpotSoundCompletedFlag);
+				if (!((PegasusEngine *)g_engine)->isDVD()) {
+					_owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut,
+							kFilterNoInput, kSpotSoundCompletedFlag);
+				} else {
+					_robotMovie.hide();
+					_robotMovie.stopDisplaying();
+					_robotMovie.releaseMovie();
+
+					_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor5");
+					_robotMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+					_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+					_robotMovie.startDisplaying();
+					_robotMovie.show();
+					_robotMovie.start();
+
+					_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
+				}
 				break;
 			default:
 				_owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut,
@@ -786,7 +884,7 @@ void GlobeGame::receiveNotification(Notification *notification, const Notificati
 			}
 			break;
 		case kRobotTaunting:
-			_owner->requestDelay(1, 1, kFilterNoInput, 0);
+			_owner->requestDelay(1, 2, kFilterNoInput, 0);
 			_owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, kSpotSoundCompletedFlag);
 			_monitorMovie.setTime(kNewLaunchSiloTime * scale);
 			_monitorMovie.redrawMovieWorld();
@@ -810,27 +908,69 @@ void GlobeGame::receiveNotification(Notification *notification, const Notificati
 
 		switch (flags) {
 		case kGlobeSplash1Finished:
-			_owner->getExtraEntry(kN79BrightView, entry);
 			_monitorMovie.stop();
 			_monitorMovie.setSegment(kSplash1End * scale, kSplash2End * scale);
 			_monitorMovie.setFlags(kLoopTimeBase);
 			_monitorMovie.start();
-			_owner->showViewFrame(entry.movieStart);
-			_owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0);
-			_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
-			_gameState = kPlayingRobotIntro;
+			if (!((PegasusEngine *)g_engine)->isDVD()) {
+				_owner->getExtraEntry(kN79BrightView, entry);
+				_owner->showViewFrame(entry.movieStart);
+				_owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0);
+				_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+				_gameState = kPlayingRobotIntro;
+			}
+			break;
+		case kGlobeRobot1Finished:
+			if (((PegasusEngine *)g_engine)->isDVD()) {
+				_owner->getExtraEntry(kN79BrightView, entry);
+				_monitorMovie.stop();
+				_monitorMovie.setSegment(0, _monitorMovie.getDuration());
+				_monitorMovie.setTime(kSplash2End * scale - 1);
+				_monitorMovie.redrawMovieWorld();
+				_monitorMovie.setFlags(0);
+
+				_owner->showViewFrame(entry.movieStart);
+				_owner->requestDelay(1, 2, kFilterNoInput, 0);
+				_owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut,
+						kFilterNoInput, kSpotSoundCompletedFlag);
+
+				_gameState = kPlayingStrikeAuthorized;
+			}
 			break;
 		case kGlobeTimerExpired:
 			// Missile launched, player loses.
+			_upperNamesMovie.hide();
+			_lowerNamesMovie.hide();
+			_countdown.hide();
+			_monitorMovie.setTime(kMissileLaunchedTime * scale);
+			_monitorMovie.redrawMovieWorld();
 			_owner->requestSpotSound(kMissileLaunchedIn, kMissileLaunchedOut, kFilterNoInput, kSpotSoundCompletedFlag);
 			_gameState = kPlayerLost1;
 			break;
 		case kMaxDeactivatedFinished:
 			_monitorMovie.stop();
 			_monitorMovie.setSegment(0, _monitorMovie.getDuration());
-			_owner->requestDelay(1, 2, kFilterNoInput, 0);
-			_owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0);
-			_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA02", kArthurNoradFinishedGlobeGame);
+			if (!((PegasusEngine *)g_engine)->isDVD()) {
+				_owner->requestDelay(1, 2, kFilterNoInput, 0);
+				_owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0);
+				_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+			} else {
+				_robotMovie.hide();
+				_robotMovie.stopDisplaying();
+				_robotMovie.releaseMovie();
+
+				_robotMovie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor6");
+				_robotMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+				_robotMovie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+				_robotMovie.setDisplayOrder(kGlobeCountdownLayer + 1);
+				_robotMovie.startDisplaying();
+				_robotMovie.show();
+				_robotMovie.start();
+
+				_owner->requestDelay(_robotMovie.getDuration(), 0, kFilterNoInput, kDelayCompletedFlag);
+			}
 			_gameState = kPlayerWon2;
 			break;
 		default:
@@ -894,56 +1034,89 @@ void GlobeGame::spinGlobe(const Input &input, const Hotspot *spot, GlobeTrackDir
 }
 
 void GlobeGame::clickGlobe(const Input &input) {
-	int16 newSilo = findClickedSilo(input);
-
-	if (newSilo != -1) {
-		_targetHighlightUpperLeft.hide();
-		_targetHighlightUpperRight.hide();
-		_targetHighlightLowerLeft.hide();
-		_targetHighlightLowerRight.hide();
-		_lowerNamesMovie.show();
-		_lowerNamesMovie.setTime(newSilo * _lowerNamesMovie.getScale());
-		_lowerNamesMovie.redrawMovieWorld();
-		_owner->requestSpotSound(kSiloBeepIn, kSiloBeepOut, kFilterNoInput, 0);
+	Movie movie(kNoDisplayElement);
+	Input movieInput;
+
+	if (((PegasusEngine *)g_engine)->isDVD() && JMPPPInput::isEasterEggModifierInput(input)) {
+		((PegasusEngine *)g_engine)->_cursor->hide();
+
+		movie.initFromMovieFile("Images/Norad Delta/N79 Back Monitor7");
+		movie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel());
+		movie.moveElementTo(kNavAreaLeft, kNavAreaTop);
+		movie.setDisplayOrder(kGlobeCountdownLayer + 1);
+		movie.startDisplaying();
+		movie.show();
+		movie.start();
+
+		while (movie.isRunning() && !((PegasusEngine *)g_engine)->shouldQuit()) {
+			InputDevice.getInput(movieInput, kFilterNoInput);
+
+			((PegasusEngine *)g_engine)->checkCallBacks();
+			((PegasusEngine *)g_engine)->refreshDisplay();
+			((PegasusEngine *)g_engine)->_system->delayMillis(10);
+		}
 
-		if (newSilo == _targetSilo[_currentSiloIndex]) {
-			_currentSiloIndex++;
-			_countdown.stopCountdown();
-			_owner->requestSpotSound(kSiloDeactivatedIn, kSiloDeactivatedOut, kFilterNoInput, 0);
+		if (((PegasusEngine *)g_engine)->shouldQuit())
+			return;
 
-			if (_currentSiloIndex == kNumTargetSilos) {
-				// Player won.
-				_owner->requestDelay(1, 2, kFilterNoInput, 0);
-				_upperNamesMovie.hide();
-				_lowerNamesMovie.hide();
-				_countdown.hide();
-				_monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(),
-						kMaxDeactivatedStop * _monitorMovie.getScale());
-				_monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale());
-				_monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished);
-				_monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
-				_monitorMovie.start();
-				_owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut,
-						kFilterNoInput, kSpotSoundCompletedFlag);
+		movie.hide();
+		movie.stopDisplaying();
+		movie.releaseMovie();
 
-				// This sound was left out of the original.
-				_owner->requestSpotSound(kAllSilosDeactivatedIn, kAllSilosDeactivatedOut,
-						kFilterNoInput, kSpotSoundCompletedFlag);
-
-				_gameState = kPlayerWon1;
+		((PegasusEngine *)g_engine)->_cursor->hideUntilMoved();
+	} else {
+		int16 newSilo = findClickedSilo(input);
+		if (newSilo != -1) {
+			_targetHighlightUpperLeft.hide();
+			_targetHighlightUpperRight.hide();
+			_targetHighlightLowerLeft.hide();
+			_targetHighlightLowerRight.hide();
+			_lowerNamesMovie.show();
+			_lowerNamesMovie.setTime(newSilo * _lowerNamesMovie.getScale());
+			_lowerNamesMovie.redrawMovieWorld();
+			_owner->requestSpotSound(kSiloBeepIn, kSiloBeepOut, kFilterNoInput, 0);
+
+			if (newSilo == _targetSilo[_currentSiloIndex]) {
+				_currentSiloIndex++;
+				_countdown.stopCountdown();
+				_owner->requestSpotSound(kSiloDeactivatedIn, kSiloDeactivatedOut, kFilterNoInput, 0);
+
+				if (_currentSiloIndex == kNumTargetSilos) {
+					// Player won.
+					_owner->requestDelay(1, 2, kFilterNoInput, 0);
+					_upperNamesMovie.hide();
+					_lowerNamesMovie.hide();
+					_countdown.hide();
+					_monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(),
+							kMaxDeactivatedStop * _monitorMovie.getScale());
+					_monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale());
+					_monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished);
+					_monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+					_monitorMovie.start();
+					_owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut,
+							kFilterNoInput, kSpotSoundCompletedFlag);
+
+					// This sound was left out of the original.
+					_owner->requestSpotSound(kAllSilosDeactivatedIn, kAllSilosDeactivatedOut,
+							kFilterNoInput, kSpotSoundCompletedFlag);
+
+					_gameState = kPlayerWon1;
+				} else {
+					_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag);
+					_upperNamesMovie.hide();
+					_lowerNamesMovie.hide();
+					_countdown.hide();
+					_monitorMovie.setTime(kSiloDeactivatedTime * _monitorMovie.getScale());
+					_monitorMovie.redrawMovieWorld();
+					_gameState = kSiloDeactivated;
+				}
 			} else {
-				_owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag);
-				_upperNamesMovie.hide();
-				_lowerNamesMovie.hide();
-				_countdown.hide();
-				_monitorMovie.setTime(kSiloDeactivatedTime * _monitorMovie.getScale());
-				_monitorMovie.redrawMovieWorld();
-				_gameState = kSiloDeactivated;
+				_owner->requestDelay(5, 1, kFilterNoInput, kDelayCompletedFlag);
+				_gameState = kDelayingPlayer;
+				// Play "incorrect" sound?
+				if (g_arthurChip)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB38", kArthurNoradSelectedIncorrectSilo);
 			}
-		} else {
-			_owner->requestDelay(5, 1, kFilterNoInput, kDelayCompletedFlag);
-			_gameState = kDelayingPlayer;
-			// Play "incorrect" sound?
 		}
 	}
 }
diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.h b/engines/pegasus/neighborhood/norad/delta/globegame.h
index 3f2ec7fec9..d75cc5cf86 100644
--- a/engines/pegasus/neighborhood/norad/delta/globegame.h
+++ b/engines/pegasus/neighborhood/norad/delta/globegame.h
@@ -139,11 +139,13 @@ protected:
 	void screenPointTo3DPoint(int16, int16, Point3D &);
 	bool lineHitsGlobe(const Line3D &, Point3D &);
 
+	Movie _robotMovie;
 	Movie _monitorMovie;
 	Movie _globeMovie;
 	Movie _upperNamesMovie;
 	Movie _lowerNamesMovie;
 	Notification _globeNotification;
+	NotificationCallBack _robotCallBack;
 	NotificationCallBack _monitorCallBack;
 	GlobeTracker _globeTracker;
 	Picture _globeCircleLeft;
diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
index 96000d10d4..4c9f307b88 100644
--- a/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
+++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp
@@ -28,6 +28,7 @@
 #include "pegasus/interface.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/opticalchip.h"
 #include "pegasus/items/biochips/retscanchip.h"
 #include "pegasus/items/inventory/airmask.h"
@@ -267,21 +268,39 @@ void NoradDelta::loadAmbientLoops() {
 	if (GameState.getNoradArrivedFromSub()) {
 		RoomID room = GameState.getCurrentRoom();
 
-		if (room == kNorad79West) {
-			if (_privateFlags.getFlag(kNoradPrivateFinishedGlobeGameFlag))
-				loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF");
-			else
+		if (room >= kNorad78 && room <= kNorad79West) {
+			if (GameState.getNoradPlayedGlobeGame()) {
+				// blitter moved clone2727's globe room loop fix up here
+				// since the original didn't play the red alert sound during the
+				// globe game.
+
+				// clone2727 added this fix so we can play the correct sound
+				// with the DVD version. This originally loaded it into slot 2,
+				// which in addition to having the corrupted sound on the disk,
+				// caused it to never play.
+				if (_vm->isDVD())
+					loadLoopSound1("Sounds/Norad/GlobAmb2.32K.AIFF");
+				else
+					loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF");
+			} else
 				loadLoopSound1("Sounds/Norad/RedAlert.22K.AIFF");
-		} else if (room >= kNorad78 && room <= kNorad79) {
-			// clone2727 says: This looks like it should be loadLoopSound1...
-			loadLoopSound2("Sounds/Norad/RedAlert.22K.AIFF");
 		} else if (GameState.getNoradGassed()) {
-			if (room >= kNorad41 && room <= kNorad49South)
-				loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
-			else if (room >= kNorad59 && room <= kNorad60West)
-				loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
-			else
-				loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
+			if (room >= kNorad41 && room <= kNorad49South) {
+				if (_vm->isDVD())
+					loadLoopSound1("Sounds/Norad/NEW SUB AMB.44K.AIFF", kNoradWarningVolume * 3);
+				else
+					loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3);
+			} else if (room >= kNorad59 && room <= kNorad60West) {
+				if (_vm->isDVD())
+					loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.32K.AIFF", kNoradWarningVolume * 3);
+				else
+					loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3);
+			} else {
+				if (_vm->isDVD())
+					loadLoopSound1("Sounds/Norad/WARNING LOOP.32K.AIFF", kNoradWarningVolume);
+				else
+					loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume);
+			}
 		} else {
 			loadLoopSound1("");
 		}
@@ -292,15 +311,22 @@ void NoradDelta::loadAmbientLoops() {
 			else
 				loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0);
 		} else {
-			if (room == kNorad54North)
-				loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2);
-			else
+			if (room == kNorad54North) {
+				if (_vm->isDVD())
+					loadLoopSound2("Sounds/Norad/N54NAS.32K.AIFF", 0x100 / 2);
+				else
+					loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2);
+			} else
 				loadLoopSound2("");
 		}
 	} else {
 		// Start them off at zero...
-		if (GameState.getNoradGassed())
-			loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", 0, 0, 0);
+		if (GameState.getNoradGassed()) {
+			if (_vm->isDVD())
+				loadLoopSound1("Sounds/Norad/NEW SUB AMB.44K.AIFF", 0, 0, 0);
+			else
+				loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", 0, 0, 0);
+		}
 		if (!g_airMask->isAirFilterOn())
 			loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", 0, 0, 0);
 	}
@@ -332,39 +358,50 @@ void NoradDelta::arriveAt(const RoomID room, const DirectionConstant direction)
 
 	switch (room) {
 	case kNorad41:
-		if (direction == kEast && !GameState.getNoradArrivedFromSub()) {
-			GameState.setNoradPlayedGlobeGame(false);
+		if (direction == kEast) {
+			if (!GameState.getNoradArrivedFromSub()) {
+				GameState.setNoradPlayedGlobeGame(false);
 
-			GameState.setNoradBeatRobotWithClaw(false);
-			GameState.setNoradBeatRobotWithDoor(false);
-			GameState.setNoradRetScanGood(false);
+				GameState.setNoradBeatRobotWithClaw(false);
+				GameState.setNoradBeatRobotWithDoor(false);
+				GameState.setNoradRetScanGood(false);
 
-			GameState.setScoringExitedSub(true);
+				GameState.setScoringExitedSub(true);
 
-			getExtraEntry(kArriveFromSubChase, entry);
+				getExtraEntry(kArriveFromSubChase, entry);
 
-			loop1Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
-					entry.movieStart, kNoradWarningVolume);
-			loop1Spec.insertFaderKnot(7320, 0);
-			loop1Spec.insertFaderKnot(7880, kNoradWarningVolume);
+				loop1Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
+						entry.movieStart, kNoradWarningVolume);
+				loop1Spec.insertFaderKnot(7320, 0);
+				loop1Spec.insertFaderKnot(7880, kNoradWarningVolume);
 
-			loop2Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
-					entry.movieStart, kNoradSuckWindVolume);
-			loop1Spec.insertFaderKnot(7320, 0);
-			loop1Spec.insertFaderKnot(7880, kNoradSuckWindVolume);
+				loop2Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd -
+						entry.movieStart, kNoradSuckWindVolume);
+				loop1Spec.insertFaderKnot(7320, 0);
+				loop1Spec.insertFaderKnot(7880, kNoradSuckWindVolume);
 
-			startExtraSequence(kArriveFromSubChase, kExtraCompletedFlag, kFilterNoInput);
+				startExtraSequence(kArriveFromSubChase, kExtraCompletedFlag, kFilterNoInput);
 
-			startLoop1Fader(loop1Spec);
-			startLoop2Fader(loop2Spec);
+				startLoop1Fader(loop1Spec);
+				startLoop2Fader(loop2Spec);
+			} else {
+				if (g_arthurChip)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA08", kArthurNoradExitedSub);
+			}
 		}
 		break;
+	case kNorad54:
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA71", kArthurNoradApproachedDamagedDoor);
+		break;
 	case kNorad54North:
 		GameState.setScoringSawRobotAt54North(true);
 		break;
 	case kNorad68:
 		if (GameState.getNoradRetScanGood())
 			openDoor();
+		else if (!_vm->playerHasItemID(kRetinalScanBiochip) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA39", kArthurNoradAtRetScanNoBiochip);
 		break;
 	case kNorad68West:
 		arriveAtNorad68West();
@@ -400,6 +437,20 @@ void NoradDelta::arriveAtNorad79West() {
 		newInteraction(kNoradGlobeGameInteractionID);
 }
 
+void NoradDelta::turnTo(const DirectionConstant direction) {
+	Norad::turnTo(direction);
+	if (g_arthurChip) {
+		switch (GameState.getCurrentRoomAndView()) {
+		case MakeRoomView(kNorad54, kNorth):
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA71", kArthurNoradApproachedDamagedDoor);
+			break;
+		case MakeRoomView(kNorad68, kWest):
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA39", kArthurNoradApproachedDamagedDoor);
+			break;
+		}
+	}
+}
+
 void NoradDelta::bumpIntoWall() {
 	requestSpotSound(kDeltaBumpIntoWallIn, kDeltaBumpIntoWallOut, kFilterNoInput, 0);
 	Neighborhood::bumpIntoWall();
@@ -427,7 +478,11 @@ void NoradDelta::finishedGlobeGame() {
 	_privateFlags.setFlag(kNoradPrivateFinishedGlobeGameFlag, true);
 	GameState.setScoringFinishedGlobeGame(true);
 	loadAmbientLoops();
-	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption);
+	if (g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA63", kArthurNoradThreatenedByRobot);
+	if (_vm->isChattyAI())
+		g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption);
+	updateViewFrame();
 }
 
 bool NoradDelta::playingAgainstRobot() {
@@ -455,7 +510,8 @@ void NoradDelta::playerBeatRobotWithDoor() {
 	GameState.setNoradBeatRobotWithDoor(true);
 	updateViewFrame();
 	GameState.setScoringStoppedNoradRobot(true);
-	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
+	if (_vm->isChattyAI())
+		g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
 }
 
 void NoradDelta::playerBeatRobotWithClaw() {
@@ -463,7 +519,8 @@ void NoradDelta::playerBeatRobotWithClaw() {
 	updateViewFrame();
 	GameState.setScoringStoppedNoradRobot(true);
 	GameState.setScoringNoradGandhi(true);
-	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
+	if (_vm->isChattyAI())
+		g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption);
 }
 
 TimeValue NoradDelta::getViewTime(const RoomID room, const DirectionConstant direction) {
@@ -619,12 +676,27 @@ void NoradDelta::receiveNotification(Notification *notification, const Notificat
 		case kN59RobotHeadOpens:
 		case kN60RobotHeadOpens:
 			_privateFlags.setFlag(kNoradPrivateRobotHeadOpenFlag, true);
+			if (g_arthurChip) {
+				switch (_vm->getRandomNumber(2)) {
+				case 0:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA36", kArthurNoradRobotHeadOpen);
+					break;
+				case 1:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA37", kArthurNoradRobotHeadOpen);
+					break;
+				case 2:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA40", kArthurNoradRobotHeadOpen);
+					break;
+				}
+			}
 			break;
 		case kNoradDeltaRetinalScanBad:
 			retScan = (RetScanChip *)_vm->getCurrentBiochip();
 			retScan->setItemState(kNormalItem);
 			playSpotSoundSync(kRetinalScanFailedIn, kRetinalScanFailedOut);
 			downButton(dummy);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA13", kArthurNoradAtRetScanNoBiochip);
 			break;
 		case kNoradDeltaRetinalScanGood:
 			retScan = (RetScanChip *)_vm->getCurrentBiochip();
@@ -640,6 +712,9 @@ void NoradDelta::receiveNotification(Notification *notification, const Notificat
 		}
 
 		_interruptionFilter = kFilterAllInput;
+	} else if ((flags & kSpotSoundCompletedFlag) != 0) {
+		if (_spotSounds.getStart() == kToDeactivateIn && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB41", kArthurNoradStartGlobeGame);
 	}
 
 	g_AIArea->checkMiddleArea();
diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.h b/engines/pegasus/neighborhood/norad/delta/noraddelta.h
index 9132893c6d..e3d3b81a2b 100644
--- a/engines/pegasus/neighborhood/norad/delta/noraddelta.h
+++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.h
@@ -90,6 +90,7 @@ protected:
 	void arriveAt(const RoomID, const DirectionConstant) override;
 	void arriveAtNorad68West();
 	void arriveAtNorad79West();
+	void turnTo(const DirectionConstant) override;
 	TimeValue getViewTime(const RoomID, const DirectionConstant) override;
 	void openDoor() override;
 	void cantMoveThatWay(CanMoveForwardReason) override;
diff --git a/engines/pegasus/neighborhood/norad/norad.cpp b/engines/pegasus/neighborhood/norad/norad.cpp
index 770d559bf6..5b0d0b483a 100644
--- a/engines/pegasus/neighborhood/norad/norad.cpp
+++ b/engines/pegasus/neighborhood/norad/norad.cpp
@@ -27,6 +27,7 @@
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/inventory/airmask.h"
 #include "pegasus/neighborhood/norad/constants.h"
 #include "pegasus/neighborhood/norad/norad.h"
@@ -162,10 +163,14 @@ void Norad::arriveAtNoradElevator() {
 
 void Norad::arriveAtUpperPressureDoorRoom() {
 	newInteraction(kNoradPressureDoorInteractionID);
+	if (g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA69", kArthurNoradReachedPressureDoor);
 }
 
 void Norad::arriveAtLowerPressureDoorRoom() {
 	newInteraction(kNoradPressureDoorInteractionID);
+	if (g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA69", kArthurNoradReachedPressureDoor);
 }
 
 void Norad::arriveAtSubPlatformRoom() {
@@ -199,10 +204,18 @@ CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) {
 }
 
 void Norad::cantOpenDoor(CanOpenDoorReason reason) {
+	bool firstLockedDoor;
+
 	if (reason == kCantOpenBadPressure)
 		playSpotSoundSync(_pressureSoundIn, _pressureSoundOut);
 	else
 		playSpotSoundSync(_accessDeniedIn, _accessDeniedOut);
+	if (g_arthurChip) {
+		firstLockedDoor = g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA65", kArthurNoradAttemptedLockedDoor);
+
+		if (!firstLockedDoor)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA68", kArthurNoradAttemptedLockedDoorAgain);
+	}
 }
 
 void Norad::startExitMovie(const ExitTable::Entry &exitEntry) {
diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp
index b7a39fdc8f..98e6f68026 100644
--- a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp
+++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp
@@ -25,6 +25,7 @@
 
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/neighborhood/norad/constants.h"
 #include "pegasus/neighborhood/norad/norad.h"
 #include "pegasus/neighborhood/norad/subcontrolroom.h"
@@ -565,6 +566,8 @@ void SubControlRoom::receiveNotification(Notification *notification, const Notif
 		switch (flags) {
 		case kAlphaSplashFinished:
 			setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA29", kArthurNoradSawClawMonitor);
 			break;
 		case kPrepHighlightFinished:
 			if (GameState.getNoradSubPrepState() == kSubDamaged)
@@ -660,6 +663,8 @@ void SubControlRoom::receiveNotification(Notification *notification, const Notif
 				hideEverything();
 				_robotState = kPlayerWon;
 				owner->startExtraSequence(kN60PlayerFollowsRobotToDoor, kExtraCompletedFlag, kFilterAllInput);
+				if (g_arthurChip)
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA67", kArthurNoradBeatRobotWithClaw);
 				break;
 			case kPlayerWon:
 				((NoradDelta *)owner)->playerBeatRobotWithClaw();
diff --git a/engines/pegasus/neighborhood/norad/subplatform.cpp b/engines/pegasus/neighborhood/norad/subplatform.cpp
index 16b8ad658e..41cdeaf864 100644
--- a/engines/pegasus/neighborhood/norad/subplatform.cpp
+++ b/engines/pegasus/neighborhood/norad/subplatform.cpp
@@ -26,6 +26,7 @@
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/neighborhood/norad/constants.h"
 #include "pegasus/neighborhood/norad/norad.h"
 #include "pegasus/neighborhood/norad/subplatform.h"
@@ -148,6 +149,8 @@ void SubPlatform::receiveNotification(Notification *notification, const Notifica
 			loop1Spec.insertFaderKnot(5080, 0);
 
 			owner->startExtraSequence(kNorad19ExitToSub, kExtraCompletedFlag, kFilterNoInput);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA07", kArthurNoradEnteredSub);
 
 			owner->startLoop1Fader(loop1Spec);
 			owner->startLoop2Fader(loop2Spec);
@@ -165,7 +168,7 @@ void SubPlatform::receiveNotification(Notification *notification, const Notifica
 		default:
 			break;
 		}
-	} else if (notification == _neighborhoodNotification) {
+	} else if (notification == _neighborhoodNotification && !((PegasusEngine *)g_engine)->isDVD()) {
 		allowInput(true);
 		((PegasusEngine *)g_engine)->jumpToNewEnvironment(kNoradSubChaseID, kNoRoomID, kNoDirection);
 		GameState.setScoringEnteredSub(true);
diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp
index 16482c23a8..d147d99698 100644
--- a/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp
+++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp
@@ -31,6 +31,7 @@
 #include "pegasus/ai/ai_area.h"
 #include "pegasus/ai/ai_condition.h"
 #include "pegasus/ai/ai_rule.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/neighborhood/prehistoric/prehistoric.h"
 
 namespace Pegasus {
@@ -99,6 +100,9 @@ void Prehistoric::start() {
 	}
 
 	Neighborhood::start();
+
+	if (GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric02, kSouth) && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA01", kArthurGoToPrehistoric);
 }
 
 class FinishPrehistoricAction : public AIPlayMessageAction {
@@ -276,8 +280,16 @@ void Prehistoric::turnTo(const DirectionConstant newDirection) {
 	Neighborhood::turnTo(newDirection);
 
 	Item *keyCard;
+	bool doArthurFeelLikeYodelingMovie = false;
 
 	switch (GameState.getCurrentRoomAndView()) {
+	case MakeRoomView(kPrehistoric08, kEast):
+	case MakeRoomView(kPrehistoric10, kEast):
+	case MakeRoomView(kPrehistoric12, kEast):
+	case MakeRoomView(kPrehistoric14, kEast):
+	case MakeRoomView(kPrehistoric25, kWest):
+		doArthurFeelLikeYodelingMovie = true;
+		break;
 	case MakeRoomView(kPrehistoric18, kEast):
 		zoomToVault();
 		break;
@@ -291,6 +303,13 @@ void Prehistoric::turnTo(const DirectionConstant newDirection) {
 		// fall through
 	case MakeRoomView(kPrehistoric25, kEast):
 		setCurrentActivation(kActivationVaultClosed);
+		if (GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric25, kEast) &&
+			!GameState.isTakenItemID(kHistoricalLog) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA60", kArthurPrehistoricCrossedBridge);
+		break;
+	case MakeRoomView(kPrehistoric23, kWest):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA59", kArthurPrehistoricSawEggs);
 		break;
 	case MakeRoomView(kPrehistoric16, kNorth):
 	case MakeRoomView(kPrehistoric21, kWest):
@@ -313,6 +332,8 @@ void Prehistoric::turnTo(const DirectionConstant newDirection) {
 	default:
 		break;
 	}
+	if (doArthurFeelLikeYodelingMovie && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA15", kArthurPrehistoricAtCliffEdge);
 }
 
 void Prehistoric::zoomToVault() {
@@ -336,6 +357,7 @@ void Prehistoric::checkContinuePoint(const RoomID room, const DirectionConstant
 
 void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction) {
 	Item *keyCard;
+	bool doArthurLetsSpreadOutMovie, doArthurFeelLikeYodelingMovie;
 
 	if (MakeRoomView(room, direction) == MakeRoomView(kPrehistoric25, kEast) &&
 			_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) {
@@ -346,6 +368,8 @@ void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction)
 
 	Neighborhood::arriveAt(room, direction);
 
+	doArthurLetsSpreadOutMovie = false;
+	doArthurFeelLikeYodelingMovie = false;
 	switch (MakeRoomView(room, direction)) {
 	case MakeRoomView(kPrehistoricDeath, kNorth):
 	case MakeRoomView(kPrehistoricDeath, kSouth):
@@ -366,6 +390,20 @@ void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction)
 			startExtraSequence(kPreArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput);
 		}
 		break;
+	case MakeRoomView(kPrehistoric06, kNorth):
+	case MakeRoomView(kPrehistoric13, kWest):
+		doArthurLetsSpreadOutMovie = true;
+		break;
+	case MakeRoomView(kPrehistoric22North, kNorth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB44", kArthurPrehistoricSawBreaker);
+		break;
+	case MakeRoomView(kPrehistoric10, kEast):
+	case MakeRoomView(kPrehistoric12, kEast):
+	case MakeRoomView(kPrehistoric14, kEast):
+	case MakeRoomView(kPrehistoric25, kWest):
+		doArthurFeelLikeYodelingMovie = true;
+		break;
 	case MakeRoomView(kPrehistoric18, kEast):
 		zoomToVault();
 		break;
@@ -385,10 +423,11 @@ void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction)
 		if (g_AIArea)
 			g_AIArea->checkRules();
 		break;
+	case MakeRoomView(kPrehistoric13, kNorth):
+		doArthurLetsSpreadOutMovie = true;
 	case MakeRoomView(kPrehistoric08, kSouth):
 	case MakeRoomView(kPrehistoric10, kSouth):
 	case MakeRoomView(kPrehistoric12, kSouth):
-	case MakeRoomView(kPrehistoric13, kNorth):
 	case MakeRoomView(kPrehistoric14, kSouth):
 	case MakeRoomView(kPrehistoric15, kNorth):
 	case MakeRoomView(kPrehistoric16, kSouth):
@@ -406,10 +445,22 @@ void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction)
 		break;
 	case MakeRoomView(kPrehistoric25, kEast):
 		setCurrentActivation(kActivationVaultClosed);
+		if (!GameState.isTakenItemID(kHistoricalLog) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA60", kArthurPrehistoricCrossedBridge);
+		break;
+	case MakeRoomView(kPrehistoric23, kWest):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA59", kArthurPrehistoricSawEggs);
 		break;
 	default:
 		break;
 	}
+	if (g_arthurChip) {
+		if (doArthurLetsSpreadOutMovie)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA20", kArthurPrehistoricReachedJunction);
+		else if (doArthurFeelLikeYodelingMovie)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA15", kArthurPrehistoricAtCliffEdge);
+	}
 }
 
 void Prehistoric::loadAmbientLoops() {
@@ -437,9 +488,9 @@ void Prehistoric::loadAmbientLoops() {
 	case kPrehistoric20:
 		// 1/4 volume.
 		if (_vm->isDVD()) // Updated sound for the DVD version
-			loadLoopSound1("Sounds/Prehistoric/P02SAL00.32k.AIFF", 64);
+			loadLoopSound1("Sounds/Prehistoric/P02SAL00.32K.AIFF", 64);
 		else
-			loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64);
+			loadLoopSound1("Sounds/Prehistoric/P02SAL00.22K.AIFF", 64);
 		break;
 	case kPrehistoric08:
 	case kPrehistoric10:
@@ -450,16 +501,16 @@ void Prehistoric::loadAmbientLoops() {
 	case kPrehistoric21:
 		// 3/16 volume.
 		if (_vm->isDVD()) // Updated sound for the DVD version
-			loadLoopSound1("Sounds/Prehistoric/P02SAL00.32k.AIFF", 48);
+			loadLoopSound1("Sounds/Prehistoric/P02SAL00.32K.AIFF", 48);
 		else
-			loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 48);
+			loadLoopSound1("Sounds/Prehistoric/P02SAL00.22K.AIFF", 48);
 		break;
 	case kPrehistoric25:
 		// 1/8 volume.
 		if (_vm->isDVD()) // Updated sound for the DVD version
-			loadLoopSound1("Sounds/Prehistoric/P02SAL00.32k.AIFF", 32);
+			loadLoopSound1("Sounds/Prehistoric/P02SAL00.32K.AIFF", 32);
 		else
-			loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 32);
+			loadLoopSound1("Sounds/Prehistoric/P02SAL00.22K.AIFF", 32);
 		break;
 	case kPrehistoric22:
 	case kPrehistoric22North:
@@ -506,9 +557,9 @@ void Prehistoric::loadAmbientLoops() {
 	case kPrehistoric18:
 		if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) {
 			if (_vm->isDVD()) // Updated sound for the DVD version
-				loadLoopSound2("Sounds/Prehistoric/P18EAL00.44K.aiff", 0x100, 0, 0);
+				loadLoopSound2("Sounds/Prehistoric/P18EAL00.44K.AIFF", 0x100, 0, 0);
 			else
-				loadLoopSound2("Sounds/Prehistoric/P18EAL00.22k.AIFF", 0x100, 0, 0);
+				loadLoopSound2("Sounds/Prehistoric/P18EAL00.22K.AIFF", 0x100, 0, 0);
 		} else {
 			loadLoopSound2("");
 		}
@@ -518,9 +569,9 @@ void Prehistoric::loadAmbientLoops() {
 	case kPrehistoric22:
 	case kPrehistoric22North:
 		if (_vm->isDVD()) // Updated sound for the DVD version
-			loadLoopSound2("Sounds/Prehistoric/P24NAL00.32k.AIFF", 64);
+			loadLoopSound2("Sounds/Prehistoric/P24NAL00.32K.AIFF", 64);
 		else
-			loadLoopSound2("Sounds/Prehistoric/P24NAL00.22k.AIFF", 64);
+			loadLoopSound2("Sounds/Prehistoric/P24NAL00.22K.aiff", 64);
 		break;
 	default:
 		break;
@@ -574,6 +625,8 @@ void Prehistoric::receiveNotification(Notification *notification, const Notifica
 			break;
 		case kPre18EastZoom:
 			startExtraSequence(kPre18EastZoomOut, kExtraCompletedFlag, kFilterNoInput);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA61", kArthurPrehistoricZoomedToVault);
 			break;
 		case kPre18EastZoomOut:
 			GameState.setPrehistoricSeenBridgeZoom(true);
@@ -584,28 +637,50 @@ void Prehistoric::receiveNotification(Notification *notification, const Notifica
 			GameState.setPrehistoricTriedToExtendBridge(false);
 			loadAmbientLoops();
 			GameState.setScoringExtendedBridge(true);
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA10", kArthurPrehistoricExtendedBridge);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA56", kArthurPrehistoricExtendedBridge);
+			}
 			break;
 		case kPre18EastBridgeOut:
 			GameState.setPrehistoricTriedToExtendBridge(true);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA58", kArthurPrehistoricAttemptedBridge);
 			if (g_AIArea)
 				g_AIArea->checkMiddleArea();
 			break;
 		case kPre22ThrowBreaker:
 			GameState.setPrehistoricBreakerThrown(true);
 			GameState.setScoringThrewBreaker(true);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA57", kArthurPrehistoricBreakerThrown);
 			break;
 		case kPre25EastUnlockingVaultNoLog:
 		case kPre25EastUnlockingVaultWithLog:
+			if (!GameState.isTakenItemID(kHistoricalLog) && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA05", kArthurPrehistoricUnlockedVault);
 			_vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kJourneymanKey));
 			break;
 		default:
 			break;
 		}
+	} else if ((flags & kSpotCompletedFlag) != 0 &&
+				GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric08, kEast)) {
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA15", kArthurPrehistoricAtCliffEdge);
 	}
 
 	g_AIArea->checkMiddleArea();
 }
 
+void Prehistoric::spotCompleted() {
+	Neighborhood::spotCompleted();
+	if (GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric08, kEast) && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA15", kArthurPrehistoricAtCliffEdge);
+}
+
 Common::String Prehistoric::getBriefingMovie() {
 	Common::String movieName = Neighborhood::getBriefingMovie();
 
diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.h b/engines/pegasus/neighborhood/prehistoric/prehistoric.h
index f703acf2f5..57776d6e2d 100644
--- a/engines/pegasus/neighborhood/prehistoric/prehistoric.h
+++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.h
@@ -140,6 +140,7 @@ protected:
 	int16 getStaticCompassAngle(const RoomID, const DirectionConstant) override;
 	void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &) override;
 	void receiveNotification(Notification *, const NotificationFlags) override;
+	void spotCompleted() override;
 	void turnTo(const DirectionConstant) override;
 	void zoomToVault();
 	TimeValue getViewTime(const RoomID, const DirectionConstant) override;
diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp
index 7dcb58a413..07949dac00 100644
--- a/engines/pegasus/neighborhood/tsa/fulltsa.cpp
+++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp
@@ -23,12 +23,16 @@
  *
  */
 
+#include "common/file.h"
+
+#include "pegasus/compass.h"
 #include "pegasus/cursor.h"
 #include "pegasus/energymonitor.h"
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
 #include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/opticalchip.h"
 #include "pegasus/neighborhood/caldoria/caldoria.h"
 #include "pegasus/neighborhood/norad/constants.h"
@@ -492,7 +496,7 @@ enum {
 	kRedirectionCCDoorLeft = kNavAreaLeft + 174,
 	kRedirectionCCDoorTop = kNavAreaTop + 36,
 
-	kRedirectionRRDoorLeft = kNavAreaLeft + 418,
+	kRedirectionRRDoorLeft = kNavAreaLeft + 428,
 	kRedirectionRRDoorTop = kNavAreaTop + 32,
 
 	kRedirectionFDDoorLeft = kNavAreaLeft + 298,
@@ -536,6 +540,10 @@ enum {
 	kPegasusCanExit = true
 };
 
+static const ExtraID kEasterEggJimenez = 1000;
+static const ExtraID kEasterEggCastillo = 1001;
+static const ExtraID kEasterEggSinclair = 1002;
+
 // Monitor modes
 enum {
 	kMonitorNeutral = 0,
@@ -661,7 +669,8 @@ void RipTimer::timeChanged(const TimeValue newTime) {
 }
 
 FullTSA::FullTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Full TSA", kFullTSAID),
-		_ripTimer(kNoDisplayElement), _sprite1(kNoDisplayElement), _sprite2(kNoDisplayElement), _sprite3(kNoDisplayElement) {
+		_extraMovie(kNoDisplayElement), _blankMovie(kNoDisplayElement), _playedSolvedMusicCue(false), _ripTimer(kNoDisplayElement),
+		_sprite1(kNoDisplayElement), _sprite2(kNoDisplayElement), _sprite3(kNoDisplayElement) {
 	setIsItemTaken(kJourneymanKey);
 	setIsItemTaken(kPegasusBiochip);
 	setIsItemTaken(kMapBiochip);
@@ -669,6 +678,12 @@ FullTSA::FullTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood
 
 void FullTSA::init() {
 	Neighborhood::init();
+	_extraMovieCallBack.setNotification(&_neighborhoodNotification);
+	if (Common::File::exists("Images/TSA/Blank TSA.movie"))
+		_blankMovie.initFromMovieFile("Images/TSA/Blank TSA.movie");
+	_blankMovie.setVolume(_vm->getSoundFXLevel());
+	_blankMovie.setDisplayOrder(kNavMovieOrder + 1);
+	_blankMovie.startDisplaying();
 	_ripTimer.setDisplayOrder(kRipTimerOrder);
 	_ripTimer.startDisplaying();
 
@@ -694,6 +709,11 @@ void FullTSA::dieUncreatedInTSA() {
 void FullTSA::start() {
 	g_energyMonitor->stopEnergyDraining();
 
+	if (_vm->isDVD()) {
+		_entranceMusic.attachFader(&_entranceFader);
+		_entranceMusic.initFromAIFFFile("Sounds/TSA/TSA Entrance.32K.AIFF");
+		_entranceFader.setMasterVolume(_vm->getAmbienceLevel() / 2);
+	}
 	if (!GameState.getScoringEnterTSA()) {
 		_utilityFuse.primeFuse(GameState.getTSAFuseTimeLimit());
 		_utilityFuse.setFunctor(new Common::Functor0Mem<void, FullTSA>(this, &FullTSA::dieUncreatedInTSA));
@@ -855,24 +875,38 @@ void FullTSA::loadAmbientLoops() {
 	switch (GameState.getTSAState()) {
 	case kTSAPlayerDetectedRip:
 	case kTSAPlayerNeedsHistoricalLog:
-		if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red))
-			loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 4, 0, 0);
-		else if (room == kTSA25Cyan || room == kTSA25Red)
-			loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 6, 0, 0);
-		else
-			loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 / 4, 0, 0);
+		if (_vm->isDVD()) {
+			if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red))
+				loadLoopSound1("Sounds/TSA/TSA CLAXON.44K.AIFF", 0x100 * 3 / 16, 0, 0);
+			else if (room == kTSA25Cyan || room == kTSA25Red)
+				loadLoopSound1("Sounds/TSA/TSA CLAXON.44K.AIFF", 0x100 / 8, 0, 0);
+			else
+				loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 * 3 / 16, 0, 0);
+		} else {
+			if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red))
+				loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 4, 0, 0);
+			else if (room == kTSA25Cyan || room == kTSA25Red)
+				loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 6, 0, 0);
+			else
+				loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 / 4, 0, 0);
+		}
 		break;
 	default:
-		if (room >= kTSA00 && room <= kTSA02)
-			loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
-		else if (room >= kTSA03 && room <= kTSA15)
-			loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
-		else if (room >= kTSA16 && room <= kTSA0B)
-			loadLoopSound1("Sounds/TSA/T14SAEO1.22K.AIFF");
-		else if (room >= kTSA21Cyan && room <= kTSA25Red)
-			loadLoopSound1("Sounds/TSA/T15SAE01.22K.AIFF");
-		else if (room >= kTSA26 && room <= kTSA37)
-			loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
+		if (_vm->isDVD()) {
+			if ((room >= kTSA00 && room <= kTSA02) || (room >= kTSA03 && room <= kTSA15) || (room >= kTSA26 && room <= kTSA37))
+				loadLoopSound1("Sounds/TSA/T01NAE.NEW.32K.AIFF", 0x100 * 3 / 4, 0, 0);
+			else if (room >= kTSA16 && room <= kTSA0B)
+				loadLoopSound1("Sounds/TSA/T14SAEO1.32K.AIFF", 0x100 * 3 / 4, 0, 0);
+			else if (room >= kTSA21Cyan && room <= kTSA25Red)
+				loadLoopSound1("Sounds/TSA/T15SAE01.32K.AIFF", 0x100 * 3 / 4, 0, 0);
+		} else {
+			if ((room >= kTSA00 && room <= kTSA02) || (room >= kTSA03 && room <= kTSA15) || (room >= kTSA26 && room <= kTSA37))
+				loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
+			else if (room >= kTSA16 && room <= kTSA0B)
+				loadLoopSound1("Sounds/TSA/T14SAEO1.22K.AIFF");
+			else if (room >= kTSA21Cyan && room <= kTSA25Red)
+				loadLoopSound1("Sounds/TSA/T15SAE01.22K.AIFF");
+		}
 		break;
 	}
 }
@@ -1132,6 +1166,50 @@ void FullTSA::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) {
 		extraEntry.movieStart += kFullTSAFrameDuration * 3;
 }
 
+void FullTSA::showViewFrame(TimeValue viewTime) {
+	if ((int32)viewTime >= 0) {
+		_turnPush.hide();
+		_navMovie.stop();
+		_navMovie.setFlags(0);
+		if (_blankMovie.isMovieValid() &&
+			(GameState.getTSAState() == kRobotsAtCommandCenter ||
+			GameState.getTSAState() == kRobotsAtFrontDoor ||
+			GameState.getTSAState() == kRobotsAtReadyRoom) &&
+			(GameState.getCurrentRoom() == kTSA0A ||
+			GameState.getCurrentRoom() == kTSA06) &&
+			(GameState.getCurrentDirection() == kEast ||
+			GameState.getCurrentDirection() == kWest)) {
+			viewTime = 0;
+			if (GameState.getCurrentRoom() == kTSA06)
+				viewTime = 2 * _blankMovie.getScale();
+			if (GameState.getCurrentDirection() == kWest)
+				viewTime += _blankMovie.getScale();
+
+			_navMovie.hide();
+			_blankMovie.setSegment(0, _blankMovie.getDuration());
+			_blankMovie.setTime(viewTime);
+
+			Common::Rect pushBounds;
+			_turnPush.getBounds(pushBounds);
+
+			_blankMovie.moveElementTo(pushBounds.left, pushBounds.top);
+			_blankMovie.show();
+			_blankMovie.redrawMovieWorld();
+		} else {
+			_blankMovie.hide();
+			_navMovie.setSegment(0, _navMovie.getDuration());
+			_navMovie.setTime(viewTime);
+
+			Common::Rect pushBounds;
+			_turnPush.getBounds(pushBounds);
+
+			_navMovie.moveElementTo(pushBounds.left, pushBounds.top);
+			_navMovie.show();
+			_navMovie.redrawMovieWorld();
+		}
+	}
+}
+
 void FullTSA::pickedUpItem(Item *item) {
 	BiochipItem *biochip;
 
@@ -1220,6 +1298,119 @@ void FullTSA::startDoorOpenMovie(const TimeValue startTime, const TimeValue stop
 	Neighborhood::startDoorOpenMovie(startTime, stopTime);
 }
 
+void FullTSA::startTurnPush(const TurnDirection turnDirection, const TimeValue newView, const DirectionConstant nextDir) {
+	if (g_AIArea)
+		g_AIArea->lockAIOut();
+
+	_vm->_cursor->hide();
+
+	GameState.setNextDirection(nextDir);
+
+	_interruptionFilter = kFilterNoInput;
+	_turnPush.stopFader();
+
+	// Set up callback.
+	_turnPushCallBack.setCallBackFlag(kTurnCompletedFlag);
+	_turnPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+
+	// Stop nav movie.
+	_navMovie.stop();
+	_navMovie.setFlags(0);
+
+	if (_blankMovie.isMovieValid() &&
+		(GameState.getTSAState() == kRobotsAtCommandCenter ||
+		GameState.getTSAState() == kRobotsAtFrontDoor ||
+		GameState.getTSAState() == kRobotsAtReadyRoom) &&
+		(GameState.getCurrentRoom() == kTSA0A || GameState.getCurrentRoom() == kTSA06) &&
+		(nextDir == kEast || nextDir == kWest)) {
+		TimeValue newRobotView = 0;
+		if (GameState.getCurrentRoom() == kTSA06)
+			newRobotView = 2 * _blankMovie.getScale();
+		if (nextDir == kWest)
+			newRobotView += _blankMovie.getScale();
+
+		_blankMovie.setSegment(0, _blankMovie.getDuration());
+
+		_pushIn.initFromMovieFrame(_blankMovie.getMovie(), newRobotView);
+		_turnPush.setInAndOutElements(&_pushIn, &_navMovie);
+	} else {
+		// Set segment of nav movie to whole movie, so that subsequent initFromMovieFrame
+		// will work.
+		_navMovie.setSegment(0, _navMovie.getDuration());
+
+		_pushIn.initFromMovieFrame(_navMovie.getMovie(), newView);
+		if (_blankMovie.isMovieValid() &&
+			(GameState.getTSAState() == kRobotsAtCommandCenter ||
+			GameState.getTSAState() == kRobotsAtFrontDoor ||
+			GameState.getTSAState() == kRobotsAtReadyRoom) &&
+			(GameState.getCurrentRoom() == kTSA0A ||
+			GameState.getCurrentRoom() == kTSA06))
+			_turnPush.setInAndOutElements(&_pushIn, &_blankMovie);
+		else
+			_turnPush.setInAndOutElements(&_pushIn, &_navMovie);
+	}
+
+	_blankMovie.hide();
+	_navMovie.hide();
+
+	switch (turnDirection) {
+	case kTurnLeft:
+		_turnPush.setSlideDirection(kSlideRightMask);
+		break;
+	case kTurnRight:
+		_turnPush.setSlideDirection(kSlideLeftMask);
+		break;
+	case kTurnUp:
+		_turnPush.setSlideDirection(kSlideDownMask);
+		break;
+	case kTurnDown:
+		_turnPush.setSlideDirection(kSlideUpMask);
+		break;
+	}
+
+	_turnPush.show();
+
+	FaderMoveSpec moveSpec;
+	moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000);
+	_turnPush.startFader(moveSpec);
+
+	if (g_compass) {
+		_turnPush.pauseFader();
+
+		int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection());
+		int32 stopAngle = getStaticCompassAngle(GameState.getCurrentRoom(), nextDir);
+
+		if (turnDirection == kTurnLeft) {
+			if (startAngle < stopAngle)
+				startAngle += 360;
+		} else {
+			if (stopAngle < startAngle)
+				stopAngle += 360;
+		}
+
+		FaderMoveSpec turnSpec;
+		_turnPush.getCurrentFaderMove(turnSpec);
+
+		FaderMoveSpec compassMove;
+		compassMove.makeTwoKnotFaderSpec(turnSpec.getFaderScale(), turnSpec.getNthKnotTime(0), startAngle, turnSpec.getNthKnotTime(1), stopAngle);
+		g_compass->startFader(compassMove);
+	}
+
+	_turnPushCallBack.cancelCallBack();
+	_turnPush.continueFader();
+
+	do {
+		InputDevice.pumpEvents();
+
+		_vm->checkCallBacks();
+		_vm->refreshDisplay();
+		_vm->_system->delayMillis(10);
+	} while (_turnPush.isFading());
+
+	_turnPush.stopFader();
+	_neighborhoodNotification.setNotificationFlags(kTurnCompletedFlag, kTurnCompletedFlag);
+}
+
 InputBits FullTSA::getInputFilter() {
 	InputBits result = Neighborhood::getInputFilter();
 
@@ -1283,8 +1474,20 @@ void FullTSA::turnRight() {
 }
 
 void FullTSA::openDoor() {
+	FaderMoveSpec spec;
+
 	switch (GameState.getCurrentRoomAndView()) {
+	case MakeRoomView(kTSA14, kSouth):
+		if (_vm->isDVD()) {
+			spec.makeTwoKnotFaderSpec(10, 0, 255, 5, 0);
+			_entranceFader.startFader(spec);
+		}
+		break;
 	case MakeRoomView(kTSA15, kSouth):
+		if (_vm->isDVD()) {
+			spec.makeTwoKnotFaderSpec(10, 0, 255, 5, 0);
+			_entranceFader.startFader(spec);
+		}
 		if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog || GameState.getTSAState() == kRobotsAtFrontDoor)
 			setCurrentAlternate(kAltTSARedAlert);
 		break;
@@ -1295,6 +1498,26 @@ void FullTSA::openDoor() {
 	Neighborhood::openDoor();
 }
 
+void FullTSA::doorOpened() {
+	if (_vm->isDVD()) {
+		switch (GameState.getCurrentRoomAndView()) {
+		case MakeRoomView(kTSA02, kNorth):
+			if (_lastExtra == kTSA02NorthDoorWithAgent3 && g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB36", kArthurTSASawAgent3);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB37", kArthurTSASawAgent3);
+			}
+			break;
+		case MakeRoomView(kTSA16, kSouth):
+		case MakeRoomView(kTSA21Cyan, kSouth):
+			_entranceMusic.stopSound();
+			break;
+		}
+	}
+	Neighborhood::doorOpened();
+}
+
 CanMoveForwardReason FullTSA::canMoveForward(ExitTable::Entry &entry) {
 	if (GameState.getCurrentRoomAndView() == MakeRoomView(kTSA25Red, kNorth))
 		return kCantMoveBlocked;
@@ -1302,6 +1525,27 @@ CanMoveForwardReason FullTSA::canMoveForward(ExitTable::Entry &entry) {
 	return Neighborhood::canMoveForward(entry);
 }
 
+void FullTSA::moveForward() {
+	ExitTable::Entry exitEntry;
+	CanMoveForwardReason moveReason = kCanMoveForward;
+	FaderMoveSpec spec;
+
+	if (_vm->isDVD()) {
+		moveReason = canMoveForward(exitEntry);
+		if (moveReason == kCanMoveForward &&
+			GameState.getCurrentRoomAndView() == MakeRoomView(kTSA02, kNorth) &&
+			!GameState.allTimeZonesFinished()) {
+			_entranceMusic.playSound();
+			spec.makeOneKnotFaderSpec(255);
+			_entranceFader.startFader(spec);
+		}
+	}
+	Neighborhood::moveForward();
+	if (moveReason == kCanMoveForward && GameState.getCurrentRoomAndView() == MakeRoomView(kTSA01, kSouth) &&
+		GameState.allTimeZonesFinished() && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA51", kArthurTSALeaving);
+}
+
 CanOpenDoorReason FullTSA::canOpenDoor(DoorTable::Entry &entry) {
 	switch (GameState.getCurrentRoomAndView()) {
 	case MakeRoomView(kTSA02, kNorth):
@@ -1378,6 +1622,18 @@ void FullTSA::activateHotspots() {
 		if (!GameState.getTSAFrontDoorUnlockedOutside())
 			_vm->getAllHotspots().activateOneHotspot(kTSA02DoorSpotID);
 		break;
+	case MakeRoomView(kTSA0A, kEast):
+		if (GameState.getTSAState() == kRobotsAtCommandCenter ||
+			GameState.getTSAState() == kRobotsAtFrontDoor ||
+			GameState.getTSAState() == kRobotsAtReadyRoom)
+			_vm->getAllHotspots().deactivateOneHotspot(kTSA0AEastSpotID);
+		break;
+	case MakeRoomView(kTSA0A, kWest):
+		if (GameState.getTSAState() == kRobotsAtCommandCenter ||
+			GameState.getTSAState() == kRobotsAtFrontDoor ||
+			GameState.getTSAState() == kRobotsAtReadyRoom)
+			_vm->getAllHotspots().deactivateOneHotspot(kTSA0AWastSpotID);
+		break;
 	case MakeRoomView(kTSA0B, kEast):
 		if (GameState.getTSA0BZoomedIn())
 			switch (GameState.getTSAState()) {
@@ -1428,7 +1684,10 @@ void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
 		Neighborhood::clickInHotspot(input, clickedSpot);
 		break;
 	case kTSA03EastJimenezSpotID:
-		startExtraLongSequence(kTSA03JimenezZoomIn, kTSA03JimenezZoomOut, kExtraCompletedFlag, kFilterNoInput);
+		if (_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input))
+			startExtraSequence(kEasterEggJimenez, kExtraCompletedFlag, kFilterNoInput);
+		else
+			startExtraLongSequence(kTSA03JimenezZoomIn, kTSA03JimenezZoomOut, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kTSA03WestCrenshawSpotID:
 		startExtraLongSequence(kTSA03CrenshawZoomIn, kTSA03CrenshawZoomOut, kExtraCompletedFlag, kFilterNoInput);
@@ -1437,10 +1696,16 @@ void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
 		startExtraLongSequence(kTSA04MatsumotoZoomIn, kTSA04MatsumotoZoomOut, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kTSA04WestCastilleSpotID:
-		startExtraLongSequence(kTSA04CastilleZoomIn, kTSA04CastilleZoomOut, kExtraCompletedFlag, kFilterNoInput);
+		if (_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input))
+			startExtraSequence(kEasterEggCastillo, kExtraCompletedFlag, kFilterNoInput);
+		else
+			startExtraLongSequence(kTSA04CastilleZoomIn, kTSA04CastilleZoomOut, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kTSA05EastSinclairSpotID:
-		startExtraLongSequence(kTSA05SinclairZoomIn, kTSA05SinclairZoomOut, kExtraCompletedFlag, kFilterNoInput);
+		if (_vm->isDVD() && JMPPPInput::isEasterEggModifierInput(input))
+			startExtraSequence(kEasterEggSinclair, kExtraCompletedFlag, kFilterNoInput);
+		else
+			startExtraLongSequence(kTSA05SinclairZoomIn, kTSA05SinclairZoomOut, kExtraCompletedFlag, kFilterNoInput);
 		break;
 	case kTSA05WestWhiteSpotID:
 		startExtraLongSequence(kTSA05WhiteZoomIn, kTSA05WhiteZoomOut, kExtraCompletedFlag, kFilterNoInput);
@@ -1603,6 +1868,12 @@ void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
 	// Pegasus
 	case kTSA37NorthJumpToPrehistoricSpotID:
 		startExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput);
+		if (g_arthurChip) {
+			if (_vm->getRandomBit())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA30", kArthurTSAUsedPegasus);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA35", kArthurTSAUsedPegasus);
+		}
 		break;
 	case kTSA37NorthExitSpotID:
 		_sprite2.setCurrentFrameIndex(1);
@@ -1829,7 +2100,9 @@ void FullTSA::initializeComparisonMonitor(const int newMode, const ExtraID compa
 		}
 	}
 
-	_interruptionFilter = kFilterAllInput;
+	// Only allow input if we're not in the middle of series of queue requests.
+	if (actionQueueEmpty())
+		_interruptionFilter = kFilterAllInput;
 }
 
 void FullTSA::playLeftComparison() {
@@ -1940,7 +2213,7 @@ void FullTSA::playRightComparison() {
 // TSA state is kTSABossSawHistoricalLog.
 void FullTSA::startRobotGame() {
 	requestExtraSequence(kTSA0BNorthCantChangeHistory, 0, kFilterNoInput);
-	requestExtraSequence(kTSA0BAIInterruption, 0, kFilterNoInput);
+	requestExtraSequence(kTSA0BAIInterruption, kExtraCompletedFlag, kFilterNoInput);
 	requestExtraSequence(kTSA0BShowGuardRobots, 0, kFilterNoInput);
 	requestExtraSequence(kTSA0BRobotsToCommandCenter, kExtraCompletedFlag, kFilterNoInput);
 }
@@ -1954,7 +2227,7 @@ void FullTSA::startUpRobotMonitor() {
 	_sprite1.addPICTResourceFrame(kRedirectionRRRolloverPICTID, true,
 			kRedirectionRRRolloverLeft - kRedirectionSprite1Left,
 			kRedirectionRRRolloverTop - kRedirectionSprite1Top);
-	_sprite1.addPICTResourceFrame(kRedirectionFDRolloverPICTID, false,
+	_sprite1.addPICTResourceFrame(kRedirectionFDRolloverPICTID, true,
 			kRedirectionFDRolloverLeft - kRedirectionSprite1Left,
 			kRedirectionFDRolloverTop - kRedirectionSprite1Top);
 	_sprite1.addPICTResourceFrame(kRedirectionCCDoorPICTID, true,
@@ -1963,7 +2236,7 @@ void FullTSA::startUpRobotMonitor() {
 	_sprite1.addPICTResourceFrame(kRedirectionRRDoorPICTID, true,
 			kRedirectionRRDoorLeft - kRedirectionSprite1Left,
 			kRedirectionRRDoorTop - kRedirectionSprite1Top);
-	_sprite1.addPICTResourceFrame(kRedirectionFDDoorPICTID, false,
+	_sprite1.addPICTResourceFrame(kRedirectionFDDoorPICTID, true,
 			kRedirectionFDDoorLeft - kRedirectionSprite1Left,
 			kRedirectionFDDoorTop - kRedirectionSprite1Top);
 	_sprite1.addPICTResourceFrame(kRedirectionClosePICTID, false,
@@ -2052,11 +2325,14 @@ void FullTSA::arriveAt(const RoomID room, const DirectionConstant direction) {
 			loopExtraSequence(kTSATransporterArrowLoop, 0);
 		}
 		break;
+	case MakeRoomView(kTSA01, kNorth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA22", kArthurTSAEnteredCave);
+		break;
 	case MakeRoomView(kTSA03, kNorth):
 	case MakeRoomView(kTSA05, kNorth):
 	case MakeRoomView(kTSA0A, kNorth):
 	case MakeRoomView(kTSA06, kNorth):
-	case MakeRoomView(kTSA07, kNorth):
 		if (_utilityFuse.isFuseLit())
 			_utilityFuse.stopFuse();
 		GameState.setScoringEnterTSA(true);
@@ -2067,6 +2343,13 @@ void FullTSA::arriveAt(const RoomID room, const DirectionConstant direction) {
 		if (!GameState.getTSASeenRobotGreeting())
 			startExtraSequence(kTSA04NorthRobotGreeting, kExtraCompletedFlag, kFilterNoInput);
 		break;
+	case MakeRoomView(kTSA07, kNorth):
+		if (_utilityFuse.isFuseLit())
+			_utilityFuse.stopFuse();
+		GameState.setScoringEnterTSA(true);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA23", kArthurTSAReachedJunction);
+		break;
 	case MakeRoomView(kTSA03, kSouth):
 		GameState.setTSAFrontDoorUnlockedInside(GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished());
 		break;
@@ -2157,8 +2440,15 @@ void FullTSA::arriveAt(const RoomID room, const DirectionConstant direction) {
 		arriveAtTSA25Red();
 		break;
 	case MakeRoomView(kTSA34, kSouth):
-		if (GameState.getLastRoom() == kTSA37)
+		if (GameState.getLastRoom() == kTSA37) {
 			closeDoorOffScreen(kTSA37, kNorth);
+			if (_vm->isDVD() && GameState.allTimeZonesFinished() && !_playedSolvedMusicCue) {
+				_solvedMusicCue.initFromAIFFFile("Sounds/TSA/TSA NORM.32K.AIFF");
+				_solvedMusicCue.setVolume(_vm->getAmbienceLevel());
+				_solvedMusicCue.playSound();
+				_playedSolvedMusicCue = true;
+			}
+		}
 		break;
 	case MakeRoomView(kTSA37, kNorth):
 		arriveAtTSA37();
@@ -2200,7 +2490,8 @@ void FullTSA::checkRobotLocations(const RoomID room, const DirectionConstant dir
 			switch (GameState.getTSAState()) {
 			case kRobotsAtCommandCenter:
 				if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) {
-					g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption);
+					if (_vm->isChattyAI())
+						g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption);
 					_privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true);
 				}
 				break;
@@ -2364,7 +2655,8 @@ void FullTSA::turnTo(const DirectionConstant newDirection) {
 		switch (GameState.getTSAState()) {
 		case kRobotsAtCommandCenter:
 			if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) {
-				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption);
+				if (_vm->isChattyAI())
+					g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption);
 				_privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true);
 			}
 			break;
@@ -2439,6 +2731,65 @@ void FullTSA::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
 	}
 }
 
+void FullTSA::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
+	static const TimeValue times[3][2] = {
+		{ 0, 11720 },
+		{ 11720, 19840 },
+		{ 19840, 29960 }
+	};
+	TimeValue segmentStart = 0, segmentStop = 0;
+	bool loopSequence = false;
+	Common::Rect pushBounds;
+	NotificationFlags extraFlags;
+
+	switch (extraID) {
+	case kEasterEggJimenez:
+	case kEasterEggCastillo:
+	case kEasterEggSinclair:
+		_turnPush.getBounds(pushBounds);
+		_extraMovie.initFromMovieFile("Images/TSA/Wacky TSA.movie");
+		segmentStart = times[extraID - 1000][0];
+		segmentStop = times[extraID - 1000][1];
+		loopSequence = false;
+
+		_lastExtra = extraID;
+		_turnPush.hide();
+
+		if (!loopSequence && g_AIArea)
+			g_AIArea->lockAIOut();
+
+		extraFlags = flags;
+		_interruptionFilter = interruptionFilter;
+		// Stop the nav movie before doing anything else
+		_navMovie.stop();
+		_navMovie.stopDisplaying();
+
+		_extraMovie.setVolume(_vm->getSoundFXLevel());
+		_extraMovie.moveElementTo(pushBounds.left, pushBounds.top);
+		_extraMovie.setDisplayOrder(kNavMovieOrder + 1);
+		_extraMovie.startDisplaying();
+		_extraMovie.show();
+		_extraMovie.setFlags(0);
+		_extraMovie.setSegment(segmentStart, segmentStop);
+		_extraMovie.setTime(segmentStart);
+		if (loopSequence)
+			_extraMovie.setFlags(kLoopTimeBase);
+		else
+			extraFlags |= kNeighborhoodMovieCompletedFlag;
+		_extraMovieCallBack.cancelCallBack();
+		_extraMovieCallBack.initCallBack(&_extraMovie, kCallBackAtExtremes);
+		if (extraFlags != 0) {
+			_extraMovieCallBack.setCallBackFlag(extraFlags);
+			_extraMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+		}
+		_extraMovie.start();
+		break;
+	default:
+		Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
+		break;
+	}
+}
+
 void FullTSA::receiveNotification(Notification *notification, const NotificationFlags flags) {
 	ExtraID lastExtra = _lastExtra;
 
@@ -2458,12 +2809,15 @@ void FullTSA::receiveNotification(Notification *notification, const Notification
 	Neighborhood::receiveNotification(notification, flags);
 
 	InventoryItem *item;
+	bool doArthurSawBustMovie, doArthurRedirectedRobotsMovie;
 
 	if ((flags & kExtraCompletedFlag) != 0) {
 		// Only allow input if we're not in the middle of series of queue requests.
 		if (actionQueueEmpty())
 			_interruptionFilter = kFilterAllInput;
 
+		doArthurSawBustMovie = false;
+		doArthurRedirectedRobotsMovie = false;
 		switch (lastExtra) {
 		case kTSAGTCardSwipe:
 			item = (InventoryItem *)_vm->getAllItems().findItemByID(kKeyCard);
@@ -2494,21 +2848,43 @@ void FullTSA::receiveNotification(Notification *notification, const Notification
 			break;
 		case kTSA03JimenezZoomIn:
 			GameState.setScoringSawBust1(true);
+			doArthurSawBustMovie = true;
 			break;
 		case kTSA03CrenshawZoomIn:
 			GameState.setScoringSawBust2(true);
+			doArthurSawBustMovie = true;
 			break;
 		case kTSA04MatsumotoZoomIn:
 			GameState.setScoringSawBust3(true);
+			doArthurSawBustMovie = true;
 			break;
 		case kTSA04CastilleZoomIn:
 			GameState.setScoringSawBust4(true);
+			doArthurSawBustMovie = true;
 			break;
 		case kTSA05SinclairZoomIn:
 			GameState.setScoringSawBust5(true);
+			doArthurSawBustMovie = true;
 			break;
 		case kTSA05WhiteZoomIn:
 			GameState.setScoringSawBust6(true);
+			doArthurSawBustMovie = true;
+			break;
+		case kEasterEggJimenez:
+		case kEasterEggCastillo:
+		case kEasterEggSinclair:
+			_extraMovie.stopDisplaying();
+			_extraMovie.releaseMovie();
+			_navMovie.startDisplaying();
+			doArthurSawBustMovie = true;
+			break;
+		case kTSA0AEastRobot:
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA48", kArthurTSAClickedRobot1);
+			break;
+		case kTSA0AWestRobot:
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA53", kArthurTSAClickedRobot2);
 			break;
 
 		// Command center
@@ -2616,15 +2992,20 @@ void FullTSA::receiveNotification(Notification *notification, const Notification
 		case kTSA0BShowGuardRobots:
 			startUpRobotMonitor();
 			// Fall through
+		case kTSA0BRobotsFromCommandCenterToFrontDoor:
+		case kTSA0BRobotsFromReadyRoomToFrontDoor:
+			doArthurRedirectedRobotsMovie = true;
 		case kTSA0BRobotsFromCommandCenterToReadyRoom:
 		case kTSA0BRobotsFromReadyRoomToCommandCenter:
-		case kTSA0BRobotsFromCommandCenterToFrontDoor:
 		case kTSA0BRobotsFromFrontDoorToCommandCenter:
 		case kTSA0BRobotsFromFrontDoorToReadyRoom:
-		case kTSA0BRobotsFromReadyRoomToFrontDoor:
 			_sprite2.setCurrentFrameIndex(kRedirectionSecuredSprite);
 			_sprite2.show();
 			break;
+		case kTSA0BAIInterruption:
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA52", kArthurTSAConfinedByBaldwin);
+			break;
 
 		// TBP monitor.
 		case kTSA0BWestZoomIn:
@@ -2638,6 +3019,8 @@ void FullTSA::receiveNotification(Notification *notification, const Notification
 			}
 
 			initializeTBPMonitor(kMonitorNeutral, 0);
+			if (GameState.getTSAState() == kTSAPlayerForcedReview && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA55", kArthurTSAOpenTBPMonitor);
 			break;
 		case kTSA0BWestZoomOut:
 			GameState.setTSA0BZoomedIn(false);
@@ -2664,18 +3047,26 @@ void FullTSA::receiveNotification(Notification *notification, const Notification
 		case kTSA22RedEastZoomInSequence:
 			_privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, true);
 			setCurrentActivation(kActivationKeyVaultOpen);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA50", kArthurTSASawJourneymanKey);
 			break;
 		case kTSA23RedWestVaultZoomInSequence:
 			_privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, true);
 			setCurrentActivation(kActivationChipVaultOpen);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA54", kArthurTSASawBiochips);
 			break;
 		case kTSA25NorthPutOnSuit:
 			GameState.setTSABiosuitOn(true);
 			GameState.setScoringGotBiosuit(true);
 			// Fall through...
 		case kTSA25NorthAlreadyHaveSuit:
-			requestExtraSequence(kTSA25NorthDescending1, 0, kFilterNoInput);
+			requestExtraSequence(kTSA25NorthDescending1, kExtraCompletedFlag, kFilterNoInput);
+			break;
+		case kTSA25NorthDescending1:
 			requestExtraSequence(kTSA25NorthDescending2, kExtraCompletedFlag, kFilterNoInput);
+			if (GameState.getTSAState() != kTSAPlayerNeedsHistoricalLog && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA04", kArthurTSAUsedTurbolift);
 			break;
 		case kTSA25NorthDescending2:
 			arriveAt(kTSA26, kNorth);
@@ -2840,15 +3231,41 @@ void FullTSA::receiveNotification(Notification *notification, const Notification
 		case kTSA37CongratulationsToExit:
 			GameState.setTSAState(kPlayerFinishedWithTSA);
 			initializePegasusButtons(true);
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA49", kArthurTSASawBaldwinSayGo);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB22", kArthurTSASawBaldwinSayGo);
+			}
 			break;
 		default:
 			break;
 		}
+		if (g_arthurChip) {
+			if (doArthurSawBustMovie)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA21", kArthurTSASawBust);
+			else if (doArthurRedirectedRobotsMovie && GameState.getTSAState() == kRobotsAtFrontDoor)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA02", kArthurTSARedirectedRobots);
+		}
 	}
 
 	g_AIArea->checkMiddleArea();
 }
 
+void FullTSA::setSoundFXLevel(const uint16 level) {
+	Neighborhood::setSoundFXLevel(level);
+	if (_extraMovie.isMovieValid())
+		_extraMovie.setVolume(level);
+}
+
+void FullTSA::setAmbienceLevel(const uint16 level) {
+	Neighborhood::setAmbienceLevel(level);
+	if (_entranceMusic.isSoundLoaded())
+		_entranceFader.setMasterVolume(level);
+	if (_solvedMusicCue.isSoundLoaded())
+		_solvedMusicCue.setVolume(level);
+}
+
 void FullTSA::arriveFromPrehistoric() {
 	if (_vm->playerHasItemID(kHistoricalLog)) {
 		GameState.setScoringFinishedPrehistoric();
diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.h b/engines/pegasus/neighborhood/tsa/fulltsa.h
index 7e9d5dae42..6fc71e7759 100644
--- a/engines/pegasus/neighborhood/tsa/fulltsa.h
+++ b/engines/pegasus/neighborhood/tsa/fulltsa.h
@@ -68,6 +68,9 @@ public:
 
 	void checkContinuePoint(const RoomID, const DirectionConstant) override;
 
+	void setSoundFXLevel(const uint16) override;
+	void setAmbienceLevel(const uint16) override;
+
 	bool canSolve() override;
 	void doSolve() override;
 
@@ -99,9 +102,11 @@ protected:
 	void downButton(const Input &) override;
 	void startDoorOpenMovie(const TimeValue, const TimeValue) override;
 	TimeValue getViewTime(const RoomID, const DirectionConstant) override;
+	void showViewFrame(TimeValue) override;
 	void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &) override;
 	void turnTo(const DirectionConstant) override;
 	CanMoveForwardReason canMoveForward(ExitTable::Entry &) override;
+	void moveForward() override;
 	CanOpenDoorReason canOpenDoor(DoorTable::Entry &) override;
 	void bumpIntoWall() override;
 	void initializeTBPMonitor(const int, const ExtraID);
@@ -109,10 +114,13 @@ protected:
 	void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &) override;
 	Hotspot *getItemScreenSpot(Item *, DisplayElement *) override;
 	void openDoor() override;
+	void doorOpened() override;
 	void turnRight() override;
 	void turnLeft() override;
 	void closeDoorOffScreen(const RoomID, const DirectionConstant) override;
+	void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits) override;
 	void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionInput) override;
+	void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant) override;
 	void handleInput(const Input &, const Hotspot *) override;
 	void arriveAtTSA25Red();
 	void startUpComparisonMonitor();
@@ -142,6 +150,15 @@ protected:
 	void checkRobotLocations(const RoomID, const DirectionConstant);
 	void getExtraEntry(const uint32, ExtraTable::Entry &) override;
 
+	Movie _extraMovie;
+	NotificationCallBack _extraMovieCallBack;
+	Movie _blankMovie;
+
+	Sound _entranceMusic;
+	SoundFader _entranceFader;
+	bool _playedSolvedMusicCue;
+	Sound _solvedMusicCue;
+
 	Sprite _sprite1, _sprite2, _sprite3;
 	FuseFunction _utilityFuse;
 	RipTimer _ripTimer;
diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp
index ce0e5cfc05..42702a8627 100644
--- a/engines/pegasus/neighborhood/tsa/tinytsa.cpp
+++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp
@@ -28,6 +28,7 @@
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
 #include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/opticalchip.h"
 #include "pegasus/neighborhood/mars/constants.h"
 #include "pegasus/neighborhood/norad/constants.h"
@@ -189,7 +190,10 @@ Common::String TinyTSA::getEnvScanMovie() {
 }
 
 void TinyTSA::loadAmbientLoops() {
-	loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
+	if (_vm->isDVD()) // Updated sound in the DVD version
+		loadLoopSound1("Sounds/TSA/T01NAE.NEW.32K.AIFF");
+	else
+		loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF");
 }
 
 int16 TinyTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
@@ -404,6 +408,11 @@ void TinyTSA::receiveNotification(Notification *notification, const Notification
 					break;
 				}
 			}
+			if (((GameState.getNoradFinished() && !(GameState.getMarsFinished() || GameState.getWSCFinished())) ||
+				(GameState.getMarsFinished() && !(GameState.getNoradFinished() || GameState.getWSCFinished())) ||
+				(GameState.getWSCFinished() && !(GameState.getNoradFinished() || GameState.getMarsFinished()))) &&
+				g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB43", kArthurTSASawFirstOpMemMovie);
 
 			requestExtraSequence(kTinyTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput);
 			break;
@@ -412,6 +421,8 @@ void TinyTSA::receiveNotification(Notification *notification, const Notification
 			GameState.setTSAState(kPlayerLockedInPegasus);
 			showMainJumpMenu();
 			makeContinuePoint();
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA22", kArthurTSAInPegasusNoVideo);
 			break;
 		case kTinyTSA37JumpToNoradMenu:
 			setCurrentActivation(kActivationTinyTSAJumpToNorad);
diff --git a/engines/pegasus/neighborhood/wsc/wsc.cpp b/engines/pegasus/neighborhood/wsc/wsc.cpp
index 329a060600..d2fc62adb1 100644
--- a/engines/pegasus/neighborhood/wsc/wsc.cpp
+++ b/engines/pegasus/neighborhood/wsc/wsc.cpp
@@ -23,10 +23,12 @@
  *
  */
 
+#include "pegasus/cursor.h"
 #include "pegasus/energymonitor.h"
 #include "pegasus/gamestate.h"
 #include "pegasus/pegasus.h"
 #include "pegasus/ai/ai_area.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/opticalchip.h"
 #include "pegasus/items/biochips/shieldchip.h"
 #include "pegasus/neighborhood/wsc/wsc.h"
@@ -39,6 +41,12 @@ static const CanTurnReason kCantTurnWatchingDiagnosis = kCantTurnLastReason + 1;
 static const CanTurnReason kCantTurnWatchingAnalysis = kCantTurnWatchingDiagnosis + 1;
 static const CanTurnReason kCantTurnInMoleculeGame = kCantTurnWatchingAnalysis + 1;
 
+static const ExtraID kImplantNoGun = 1000;
+static const ExtraID kImplantWithGun = 1001;
+static const ExtraID kEasterEggWalchek = 1002;
+
+static const HotSpotID kBiotechImplantHotSpotID = 10000;
+
 static const TimeScale kMoleculesMovieScale = 600;
 static const TimeValue kMoleculeLoopTime = 4 * kMoleculesMovieScale;
 static const TimeValue kMoleculeFailTime = 2 * kMoleculesMovieScale;
@@ -485,7 +493,7 @@ static const CoordType kMoleculesMovieLeft = kNavAreaLeft + 112;
 static const CoordType kMoleculesMovieTop = kNavAreaTop + 40;
 
 WSC::WSC(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "WSC", kWSCID),
-		_moleculesMovie(kNoDisplayElement) {
+		_biotechImplantSpot(kBiotechImplantHotSpotID), _extraMovie(kNoDisplayElement), _moleculesMovie(kNoDisplayElement) {
 
 	_argonSprite = nullptr;
 	_cachedZoomSpot = nullptr;
@@ -504,6 +512,11 @@ WSC::WSC(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHan
 			GameState.isTakenItemID(kSinclairKey));
 }
 
+WSC::~WSC() {
+	if (_vm->isDVD())
+		_vm->getAllHotspots().remove(&_biotechImplantSpot);
+}
+
 uint16 WSC::getDateResID() const {
 	return kDate2310ID;
 }
@@ -511,12 +524,23 @@ uint16 WSC::getDateResID() const {
 void WSC::init() {
 	Neighborhood::init();
 
+	_extraMovieCallBack.setNotification(&_neighborhoodNotification);
+
 	_cachedZoomSpot = 0;
 	_argonSprite = 0;
 
 	// HACK: Fix the drag item for picking up the Sinclair Key Card
 	HotspotInfoTable::Entry *entry = findHotspotEntry(kWSC02SouthTakeArgonSpotID);
 	entry->hotspotItem = kArgonPickup;
+
+	if (_vm->isDVD()) {
+		Hotspot *aSpot = _vm->getAllHotspots().findHotspotByID(kW61TimeBendingSpotID);
+		aSpot->setArea(Common::Rect(97, 156, 275, 174));
+
+		_biotechImplantSpot.setArea(Common::Rect(kNavAreaLeft + 97, kNavAreaTop + 174, kNavAreaLeft + 275, kNavAreaTop + 182));
+		_biotechImplantSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag);
+		_vm->getAllHotspots().push_back(&_biotechImplantSpot);
+	}
 }
 
 void WSC::flushGameState() {
@@ -1032,27 +1056,27 @@ void WSC::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) {
 		break;
 	case kW61SouthScreenOnWithGun:
 		if (GameState.isTakenItemID(kMachineGun))
-			Neighborhood::getExtraEntry(id, extraEntry);
-		else
 			Neighborhood::getExtraEntry(kW61SouthScreenOnNoGun, extraEntry);
+		else
+			Neighborhood::getExtraEntry(id, extraEntry);
 		break;
 	case kW61SouthSmartAlloysWithGun:
 		if (GameState.isTakenItemID(kMachineGun))
-			Neighborhood::getExtraEntry(id, extraEntry);
-		else
 			Neighborhood::getExtraEntry(kW61SouthSmartAlloysNoGun, extraEntry);
+		else
+			Neighborhood::getExtraEntry(id, extraEntry);
 		break;
 	case kW61SouthMorphingWithGun:
 		if (GameState.isTakenItemID(kMachineGun))
-			Neighborhood::getExtraEntry(id, extraEntry);
-		else
 			Neighborhood::getExtraEntry(kW61SouthMorphingNoGun, extraEntry);
+		else
+			Neighborhood::getExtraEntry(id, extraEntry);
 		break;
 	case kW61SouthTimeBendingWithGun:
 		if (GameState.isTakenItemID(kMachineGun))
-			Neighborhood::getExtraEntry(id, extraEntry);
-		else
 			Neighborhood::getExtraEntry(kW61SouthTimeBendingNoGun, extraEntry);
+		else
+			Neighborhood::getExtraEntry(id, extraEntry);
 		break;
 	case kW98RobotHeadOpensLight:
 		if (GameState.getWSCCatwalkDark())
@@ -1118,6 +1142,21 @@ void WSC::bumpIntoWall() {
 	Neighborhood::bumpIntoWall();
 }
 
+void WSC::spotCompleted() {
+	Neighborhood::spotCompleted();
+	if (_vm->isDVD() && GameState.getCurrentRoomAndView() == MakeRoomView(kWSC58, kSouth) && g_arthurChip) {
+		g_AIArea->checkRules();
+		if (GameState.isTakenItemID(kCrowbar)) {
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA94", kArthurWSCSawBrokenDoor);
+		} else {
+			if (_vm->getRandomBit())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA95", kArthurWSCSawBrokenDoorNoCrowBar);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA39", kArthurWSCSawBrokenDoorNoCrowBar);
+		}
+	}
+}
+
 void WSC::closeDoorOffScreen(const RoomID room, const DirectionConstant) {
 	Item *keyCard;
 
@@ -1164,27 +1203,43 @@ void WSC::cantOpenDoor(CanOpenDoorReason reason) {
 	switch (GameState.getCurrentRoomAndView()) {
 	case MakeRoomView(kWSC22, kWest):
 		playSpotSoundSync(kNakamuraNotHomeIn, kNakamuraNotHomeOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	case MakeRoomView(kWSC23, kEast):
 		playSpotSoundSync(kHernandezNotHomeIn, kHernandezNotHomeOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	case MakeRoomView(kWSC26, kWest):
 		playSpotSoundSync(kGrailisNotHomeIn, kGrailisNotHomeOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	case MakeRoomView(kWSC27, kEast):
 		playSpotSoundSync(kWashingtonNotHomeIn, kWashingtonNotHomeOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	case MakeRoomView(kWSC32, kWest):
 		playSpotSoundSync(kTheriaultNotHomeIn, kTheriaultNotHomeOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	case MakeRoomView(kWSC33, kEast):
 		playSpotSoundSync(kSullivanNotHomeIn, kSullivanNotHomeOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	case MakeRoomView(kWSC41, kWest):
 		playSpotSoundSync(kGlennerNotHomeIn, kGlennerNotHomeOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	case MakeRoomView(kWSC42, kEast):
 		playSpotSoundSync(kSinclairNotHomeIn, kSinclairNotHomeOut);
+		if (!GameState.isTakenItemID(kSinclairKey) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA91", kArthurWSCAttemptedSinclairDoorNoKey);
 		break;
 	case MakeRoomView(kWSC15, kWest):
 	case MakeRoomView(kWSC25, kWest):
@@ -1192,6 +1247,8 @@ void WSC::cantOpenDoor(CanOpenDoorReason reason) {
 	case MakeRoomView(kWSC41, kEast):
 	case MakeRoomView(kWSC46, kWest):
 		playSpotSoundSync(kWSCLabClosedIn, kWSCLabClosedOut);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA98", kArthurWSCAttemptedLockedDoor);
 		break;
 	default:
 		Neighborhood::cantOpenDoor(reason);
@@ -1338,10 +1395,87 @@ void WSC::zoomTo(const Hotspot *hotspot) {
 }
 
 void WSC::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) {
-	if (extraID == kW61Brochure)
-		loadLoopSound1("");
+	TimeValue segmentStart = 0, segmentStop = 0;
+	bool loopSequence = false;
+	Common::Rect pushBounds;
+	NotificationFlags extraFlags;
+
+	switch (extraID) {
+	case kImplantNoGun:
+	case kImplantWithGun:
+	case kEasterEggWalchek:
+		_turnPush.getBounds(pushBounds);
+
+		switch (extraID) {
+		case kImplantNoGun:
+			_extraMovie.initFromMovieFile("Images/World Science Center/W61SNF.movie");
+			break;
+		case kImplantWithGun:
+			_extraMovie.initFromMovieFile("Images/World Science Center/W61SZF.movie");
+			break;
+		case kEasterEggWalchek:
+			_extraMovie.initFromMovieFile("Images/World Science Center/W61WZF.movie");
+			break;
+		default:
+			break;
+		}
+		segmentStart = 0;
+		segmentStop = _extraMovie.getDuration();
+		loopSequence = false;
 
-	Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
+		_lastExtra = extraID;
+		_turnPush.hide();
+
+		if (!loopSequence && g_AIArea)
+			g_AIArea->lockAIOut();
+
+		extraFlags = flags;
+		_interruptionFilter = interruptionFilter;
+		// Stop the nav movie before doing anything else
+		_navMovie.stop();
+		_navMovie.stopDisplaying();
+
+		_extraMovie.setVolume(_vm->getSoundFXLevel());
+		_extraMovie.moveElementTo(pushBounds.left, pushBounds.top);
+		_extraMovie.setDisplayOrder(kNavMovieOrder + 1);
+		_extraMovie.startDisplaying();
+		_extraMovie.show();
+		_extraMovie.setFlags(0);
+		_extraMovie.setSegment(segmentStart, segmentStop);
+		_extraMovie.setTime(segmentStart);
+		if (loopSequence)
+			_extraMovie.setFlags(kLoopTimeBase);
+		else
+			extraFlags |= kNeighborhoodMovieCompletedFlag;
+		_extraMovieCallBack.cancelCallBack();
+		_extraMovieCallBack.initCallBack(&_extraMovie, kCallBackAtExtremes);
+		if (extraFlags != 0) {
+			_extraMovieCallBack.setCallBackFlag(extraFlags);
+			_extraMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
+		}
+		_extraMovie.start();
+		break;
+	default:
+		switch (extraID) {
+		case kW61Brochure:
+			loadLoopSound1("");
+			break;
+		}
+		Neighborhood::startExtraSequence(extraID, flags, interruptionFilter);
+		if (extraID == kWSCSpinRobot && g_arthurChip) {
+			if (_vm->getRandomBit())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA93", kArthurWSCSawAresHologram);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB08", kArthurWSCSawAresHologram);
+		}
+		break;
+	}
+}
+
+void WSC::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) {
+	Neighborhood::startDoorOpenMovie(startTime, stopTime);
+	if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC58, kSouth) && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA19", kArthurWSCUsedCrowBar);
 }
 
 int16 WSC::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
@@ -1417,17 +1551,40 @@ void WSC::loadAmbientLoops() {
 	RoomID room = GameState.getCurrentRoom();
 
 	if (room >= kWSC01 && room <= kWSC04) {
-		if (GameState.getWSCSeenTimeStream())
-			loadLoopSound1("Sounds/World Science Center/WLabLoop.22K.AIFF", 0x100 / 2);
-	} else if ((room >= kWSC06 && room <= kWSC58) || (room >= kWSC62 && room <= kWSC63))
-		loadLoopSound1("Sounds/World Science Center/Organic Walls.22K.AIFF", 0x100 / 2);
-	else if (room >= kWSC82 && room <= kWSC92)
-		loadLoopSound1("Sounds/World Science Center/Creature Feature.22K.AIFF");
-	else if ((room >= kWSC60 && room <= kWSC61West) || (room >= kWSC64 && room <= kWSC81) ||
-			(room >= kWSC93 && room <= kWSC97))
-		loadLoopSound1("Sounds/World Science Center/The Other Side.22K.AIFF", 0x100 / 12);
-	else if (room == kWSC98)
-		loadLoopSound1("Sounds/World Science Center/WCatLoop.22K.AIFF");
+		if (GameState.getWSCSeenTimeStream()) {
+			if (_vm->isDVD())
+				loadLoopSound1("Sounds/World Science Center/WLabLoop.32K.AIFF", 0x100 / 4);
+			else
+				loadLoopSound1("Sounds/World Science Center/WLabLoop.22K.AIFF", 0x100 / 2);
+			if (_vm->isDVD()) {
+				if (GameState.getWSCPoisoned())
+					loadLoopSound2("Sounds/World Science Center/Poisoned.32K.16.AIFF", 0x100 / 4);
+				else
+					loadLoopSound2("");
+			}
+		}
+	} else {
+		if ((room >= kWSC06 && room <= kWSC58) || (room >= kWSC62 && room <= kWSC63)) {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/World Science Center/Organic Walls.32K.16.AIFF", 205); // ~80%
+			else
+				loadLoopSound1("Sounds/World Science Center/Organic Walls.22K.AIFF", 0x100 / 2);
+		} else if (room >= kWSC82 && room <= kWSC92) {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/World Science Center/Creature Feature.32K.16.AIFF");
+			else
+				loadLoopSound1("Sounds/World Science Center/Creature Feature.22K.AIFF");
+		} else if ((room >= kWSC60 && room <= kWSC61West) || (room >= kWSC64 && room <= kWSC81) ||
+					(room >= kWSC93 && room <= kWSC97)) {
+			if (_vm->isDVD()) // Updated for the DVD version
+				loadLoopSound1("Sounds/World Science Center/The Other Side.32K.16.AIFF", 51); // ~20%
+			else
+				loadLoopSound1("Sounds/World Science Center/The Other Side.22K.AIFF", 0x100 / 12);
+		} else if (room == kWSC98) {
+			loadLoopSound1("Sounds/World Science Center/WCatLoop.22K.AIFF");
+		}
+		loadLoopSound2("");
+	}
 }
 
 void WSC::checkContinuePoint(const RoomID room, const DirectionConstant direction) {
@@ -1548,12 +1705,21 @@ void WSC::arriveAt(const RoomID room, const DirectionConstant dir) {
 		if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote())
 			setCurrentActivation(kActivationReadyForSynthesis);
 		break;
+	case MakeRoomView(kWSC19, kWest):
+		if (!(GameState.isTakenItemID(kSinclairKey) && GameState.isTakenItemID(kArgonCanister) &&
+			GameState.isTakenItemID(kNitrogenCanister)) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA34", kArthurWSCLeftLabNoKeyOrCanisters);
+		break;
 	case MakeRoomView(kWSC16, kNorth):
 		if (getCurrentAlternate() == kAltWSCPeopleAtW19North) {
 			setCurrentAlternate(kAltWSCNormal);
 			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt19NorthFlag, true);
 		}
 		break;
+	case MakeRoomView(kWSC06, kNorth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA81", kArthurWSCAtOppositeDoor);
+		break;
 	case MakeRoomView(kWSC07, kSouth):
 	case MakeRoomView(kWSC56, kNorth):
 		setCurrentActivation(kActivationReadyForMap);
@@ -1564,6 +1730,12 @@ void WSC::arriveAt(const RoomID room, const DirectionConstant dir) {
 	case MakeRoomView(kWSC42, kEast):
 		_privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false);
 		setCurrentActivation(kActivationSinclairOfficeLocked);
+		if (g_arthurChip) {
+			if (GameState.isTakenItemID(kSinclairKey))
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA99", kArthurWSCSawSinclairDoor);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA92", kArthurWSCSawSinclairDoorNoKey);
+		}
 		break;
 	case MakeRoomView(kWSC58, kSouth):
 		setCurrentActivation(kActivationW58SouthDoorLocked);
@@ -1572,8 +1744,14 @@ void WSC::arriveAt(const RoomID room, const DirectionConstant dir) {
 	case MakeRoomView(kWSC60, kEast):
 		GameState.setScoringEnteredSinclairOffice();
 		break;
+	case MakeRoomView(kWSC60East, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB03", kArthurWSCZoomedToSnake);
+		break;
 	case MakeRoomView(kWSC61West, kWest):
 		setCurrentActivation(kActivationW61MessagesOff);
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB00", kArthurWSCZoomedToSinclairMessages);
 		break;
 	case MakeRoomView(kWSC61South, kSouth):
 		setCurrentActivation(kActivationW61SouthOff);
@@ -1581,12 +1759,21 @@ void WSC::arriveAt(const RoomID room, const DirectionConstant dir) {
 	case MakeRoomView(kWSC62, kSouth):
 		if (!GameState.getWSCDidPlasmaDodge()) {
 			g_AIArea->lockAIOut();
-			loadLoopSound1("Sounds/World Science Center/Plasma Rock.22K.AIFF");
+
+			if (_vm->isDVD())
+				loadLoopSound1("Sounds/World Science Center/Plasma Rock.44K.16.AIFF");
+			else
+				loadLoopSound1("Sounds/World Science Center/Plasma Rock.22K.AIFF");
+
 			requestExtraSequence(kW62SouthPlasmaRobotAppears, 0, kFilterNoInput);
 			requestExtraSequence(kW62ZoomToRobot, 0, kFilterNoInput);
 			requestExtraSequence(kW62ZoomOutFromRobot, kExtraCompletedFlag, kFilterNoInput);
 		}
 		break;
+	case MakeRoomView(kWSC64, kSouth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB06", kArthurWSCEnteredAuditorium);
+		break;
 	case MakeRoomView(kWSC65Screen, kSouth):
 		if (!GameState.getWSCSeenSinclairLecture()) {
 			GameState.setWSCSeenSinclairLecture(true);
@@ -1614,11 +1801,33 @@ void WSC::arriveAt(const RoomID room, const DirectionConstant dir) {
 		if (getCurrentAlternate() == kAltWSCW0ZDoorOpen)
 			turnLeft();
 		break;
+	case MakeRoomView(kWSC82, kSouth):
+	case MakeRoomView(kWSC82, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB05", kArthurWSCEnteredPassage);
+		break;
+	case MakeRoomView(kWSC90, kEast):
+	case MakeRoomView(kWSC91, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA90", kArthurWSCInPassage);
+		break;
 	case MakeRoomView(kWSC93, kEast):
 		GameState.setWSCBeenAtWSC93(true);
+	case MakeRoomView(kWSC93, kNorth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA38", kArthurWSCExitedPassage);
+		break;
+	case MakeRoomView(kWSC95, kWest):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA92", kArthurWSCSawCatwalkDoor);
 		break;
 	case MakeRoomView(kWSC98, kWest):
 		if (!GameState.getWSCRobotDead()) {
+			if (_vm->isDVD()) {
+				_welcomeSound.initFromAIFFFile("Sounds/World Science Center/Welcome Enrique.22K.AIFF");
+				_welcomeSound.setVolume(_vm->getSoundFXLevel());
+				_welcomeSound.playSound();
+			}
 			scheduleEvent(kGawkAtRobotTime2, 1, kTimerEventPlayerGawkingAtRobot2);
 			setCurrentActivation(kActivationRobotTurning);
 			if (g_AIArea)
@@ -1663,6 +1872,10 @@ void WSC::turnTo(const DirectionConstant direction) {
 		if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote())
 			setCurrentActivation(kActivationReadyForSynthesis);
 		break;
+	case MakeRoomView(kWSC06, kNorth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA81", kArthurWSCAtOppositeDoor);
+		break;
 	case MakeRoomView(kWSC07, kSouth):
 	case MakeRoomView(kWSC56, kNorth):
 		setCurrentActivation(kActivationReadyForMap);
@@ -1687,11 +1900,21 @@ void WSC::turnTo(const DirectionConstant direction) {
 	case MakeRoomView(kWSC42, kEast):
 		_privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false);
 		setCurrentActivation(kActivationSinclairOfficeLocked);
+		if (GameState.getCurrentRoom() == kWSC42 && g_arthurChip) {
+			if (GameState.isTakenItemID(kSinclairKey))
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA99", kArthurWSCSawSinclairDoor);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA92", kArthurWSCSawSinclairDoorNoKey);
+		}
 		break;
 	case MakeRoomView(kWSC58, kSouth):
 		setCurrentActivation(kActivationW58SouthDoorLocked);
 		_privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false);
 		break;
+	case MakeRoomView(kWSC64, kSouth):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB06", kArthurWSCEnteredAuditorium);
+		break;
 	case MakeRoomView(kWSC73, kWest):
 		setCurrentAlternate(kAltWSCNormal);
 		break;
@@ -1699,6 +1922,15 @@ void WSC::turnTo(const DirectionConstant direction) {
 		if (getCurrentAlternate() == kAltWSCW0ZDoorOpen)
 			startExtraSequence(kW0ZSpottedByWomen, kExtraCompletedFlag, kFilterNoInput);
 		break;
+	case MakeRoomView(kWSC82, kSouth):
+	case MakeRoomView(kWSC82, kEast):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB05", kArthurWSCEnteredPassage);
+		break;
+	case MakeRoomView(kWSC95, kWest):
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA92", kArthurWSCSawCatwalkDoor);
+		break;
 	default:
 		break;
 	}
@@ -1722,6 +1954,7 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 			setCurrentActivation(kActivationShotByRobot);
 			GameState.setWSCPoisoned(true);
 			setUpPoison();
+			loadAmbientLoops();
 			makeContinuePoint();
 			break;
 		case kWSCDartScan2:
@@ -1729,14 +1962,18 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 			GameState.setScoringRemovedDart();
 			GameState.setWSCRemovedDart(true);
 			setUpPoison();
-			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XW1WB2", false, kHintInterruption);
-			// Fall through...
-		case kWSCDartScanNo:
+			if (_vm->isChattyAI())
+				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XW1WB2", false, kHintInterruption);
 			GameState.setWSCAnsweredAboutDart(true);
 			startExtraSequence(kWSCDartScan3, kExtraCompletedFlag, kFilterNoInput);
 			break;
 		case kWSCDartScan3:
 			setCurrentActivation(kActivateHotSpotAlways);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB02", kArthurWSCRemovedDart);
+			break;
+		case kWSCDartScanNo:
+			die(kDeathDidntStopPoison);
 			break;
 		case kWSCAnalyzerPowerUp:
 		case kWSCAnalyzerPowerUpWithDart:
@@ -1770,10 +2007,18 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 			break;
 		case kWSC02TurnOnMorphScreen:
 			setCurrentActivation(kActivationReadyForMorph);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA29", kArthurWSCLookAtMorphExperiment);
 			break;
 		case kWSC02DropToMorphExperiment:
 			loopExtraSequence(kWSC02MorphLoop, kExtraCompletedFlag);
 			setCurrentActivation(kActivationMorphLooping);
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA97", kArthurWSCStartMorphExperiment);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB04", kArthurWSCStartMorphExperiment);
+			}
 			break;
 		case kWSC02MorphLoop:
 			if (_privateFlags.getFlag(kWSCPrivateInterruptedMorphFlag))
@@ -1788,6 +2033,8 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 		case kWSC02TurnOffMorphScreen:
 			setCurrentActivation(kActivationMorphScreenOff);
 			GameState.setWSCSawMorph(true);
+			if (!(GameState.isTakenItemID(kSinclairKey) && GameState.isTakenItemID(kArgonCanister)) && g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA96", kArthurWSCSawMorphExperiment);
 			break;
 		case kW03NorthActivate:
 			if (GameState.getWSCAnalyzedDart() && !GameState.getWSCDesignedAntidote())
@@ -1813,6 +2060,8 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 			_privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, false);
 			GameState.setWSCDesignedAntidote(true);
 			GameState.setScoringBuiltAntidote();
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA02", kArthurWSCDesignedAntidote);
 			break;
 		case kW03SouthCreateAntidote:
 			setCurrentActivation(kActivationSynthesizerLooping);
@@ -1825,9 +2074,13 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 		case kWSC56SouthMap:
 			setCurrentActivation(kActivateHotSpotAlways);
 			GameState.setScoringSawWSCDirectory();
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA33", kArthurWSCReadyForMap);
 			break;
 		case kNerdAtTheDoor1:
 			GameState.setWSCSeenNerd(true);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA28", kArthurWSCSeenNerd);
 			break;
 		case kNerdAtTheDoor2:
 			die(kDeathArrestedInWSC);
@@ -1848,6 +2101,13 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 		case kW61SouthTimeBendingNoGun:
 			GameState.setScoringSawSinclairEntry3();
 			break;
+		case kImplantWithGun:
+		case kImplantNoGun:
+		case kEasterEggWalchek:
+			_extraMovie.stopDisplaying();
+			_extraMovie.releaseMovie();
+			_navMovie.startDisplaying();
+			break;
 		case kW61MessagesOn:
 			GameState.setWSCOfficeMessagesOpen(true);
 			setCurrentActivation(kActivationW61MessagesOn);
@@ -1864,6 +2124,8 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 		case kW61SouthScreenOnNoGun:
 			_privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, true);
 			setCurrentActivation(kActivationW61SouthOn);
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA89", kArthurWSCActivatedComputer);
 			break;
 		case kW61SouthScreenOffWithGun:
 		case kW61SouthScreenOffNoGun:
@@ -1877,7 +2139,6 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 		case kW62ZoomOutFromRobot:
 			// Handle action queue before starting new movie sequences.
 			Neighborhood::receiveNotification(notification, flags);
-			_energyDrainRate = g_energyMonitor->getEnergyDrainRate();
 			g_energyMonitor->setEnergyDrainRate(0);
 			currentEnergy = g_energyMonitor->getCurrentEnergy();
 			_vm->setEnergyDeathReason(kDeathHitByPlasma);
@@ -1905,12 +2166,22 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 				g_energyMonitor->drainEnergy(kPlasmaEnergyNoShield);
 			}
 
-			g_energyMonitor->setEnergyDrainRate(_energyDrainRate);
+			setUpPoison();
 			g_AIArea->unlockAI();
 			GameState.setScoringFinishedPlasmaDodge();
 			GameState.setWSCDidPlasmaDodge(true);
 			restoreStriding(kWSC58, kSouth, kAltWSCNormal);
 			loadAmbientLoops();
+			if (g_arthurChip) {
+				if (_vm->getRandomBit())
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA11", kArthurWSCDidPlasmaDodge);
+				else
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB07", kArthurWSCDidPlasmaDodge);
+			}
+			break;
+		case kW65SouthSinclairLecture:
+			if (g_arthurChip)
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB29", kArthurWSCSawSinclairLecture);
 			break;
 		case kW0ZSpottedByWomen:
 			die(kDeathArrestedInWSC);
@@ -1974,8 +2245,9 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 			GameState.setWSCRobotDead(true);
 			GameState.setScoringStoppedWSCRobot();
 
-			// Video is not present
-			//g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption);
+			// Video is erroneously not present in the CD version
+			if (_vm->isDVD() && _vm->isChattyAI())
+				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption);
 			break;
 		case kW98RobotGassed:
 			item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister);
@@ -1984,23 +2256,47 @@ void WSC::receiveNotification(Notification *notification, const NotificationFlag
 			GameState.setWSCRobotDead(true);
 			GameState.setScoringStoppedWSCRobot();
 
-			// Video is not present
-			//g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption);
+			// Video is erroneously not present in the CD version
+			if (_vm->isDVD() && _vm->isChattyAI())
+				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption);
 			break;
 		case kW98RobotHeadOpensLight:
 		case kW98RobotHeadOpensDark:
 			setCurrentActivation(kActivationWSCRobotHeadOpen);
 			_privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, true);
+			if (g_arthurChip) {
+				switch (_vm->getRandomNumber(2)) {
+				case 0:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA36", kArthurWSCRobotHeadOpen);
+					break;
+				case 1:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA37", kArthurWSCRobotHeadOpen);
+					break;
+				case 2:
+					g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA40", kArthurWSCRobotHeadOpen);
+					break;
+				}
+			}
 			break;
 		case kW98RobotHeadClosesDark:
 		case kW98RobotHeadClosesLight:
 			setCurrentActivation(kActivationRobotGone);
 			_privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, false);
 			GameState.setWSCRobotGone(true);
+			if (GameState.isTakenItemID(kStunGun)) {
+				GameState.setWSCFinished(true);
+
+				if (!GameState.getWSCCatwalkDark())
+					GameState.setScoringWSCGandhi();
+
+				recallToTSASuccess();
+			}
 			break;
 		default:
 			break;
 		}
+		if ((_lastExtra == kW61WalchekEasterEgg1 || _lastExtra == kEasterEggWalchek) && g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA09", kArthurWSCPlayedEasterEggMessage);
 	}
 
 	Neighborhood::receiveNotification(notification, flags);
@@ -2071,6 +2367,8 @@ void WSC::startMoleculeGameLevel() {
 	}
 
 	_moleculesMovie.start();
+	if (_moleculeGameLevel == 3 && g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBA90", kArthurWSCPoisonedDuringGame);
 }
 
 void WSC::moleculeGameClick(const HotSpotID id) {
@@ -2152,6 +2450,8 @@ void WSC::moleculeGameClick(const HotSpotID id) {
 
 		_moleculesMovie.stop();
 		startMoleculeGameLevel();
+		if (g_arthurChip)
+			g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB38", kArthurWSCFailedMolecule);
 	}
 }
 
@@ -2197,27 +2497,44 @@ void WSC::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) {
 }
 
 void WSC::activateHotspots() {
+	Input input;
+
 	Neighborhood::activateHotspots();
 
-	if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC98, kWest) && _privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) {
-		if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag))
-			_vm->getAllHotspots().deactivateOneHotspot(kW98RetinalChipSpotID);
-		else
-			_vm->getAllHotspots().activateOneHotspot(kW98RetinalChipSpotID);
+	switch (GameState.getCurrentRoomAndView()) {
+	case MakeRoomView(kWSC61South, kSouth):
+		if (_vm->isDVD()) {
+			InputDevice.getInput(input, kFilterAllInput);
+			if (_privateFlags.getFlag(kWSCPrivateOfficeLogOpenFlag) &&
+				JMPPPInput::isEasterEggModifierInput(input))
+				_vm->getAllHotspots().activateOneHotspot(kBiotechImplantHotSpotID);
+		}
+		break;
+	case MakeRoomView(kWSC98, kWest):
+		if (_privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) {
+			if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag))
+				_vm->getAllHotspots().deactivateOneHotspot(kW98RetinalChipSpotID);
+			else
+				_vm->getAllHotspots().activateOneHotspot(kW98RetinalChipSpotID);
 
-		if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag))
-			_vm->getAllHotspots().deactivateOneHotspot(kW98MapChipSpotID);
-		else
-			_vm->getAllHotspots().activateOneHotspot(kW98MapChipSpotID);
+			if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag))
+				_vm->getAllHotspots().deactivateOneHotspot(kW98MapChipSpotID);
+			else
+				_vm->getAllHotspots().activateOneHotspot(kW98MapChipSpotID);
 
-		if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag))
-			_vm->getAllHotspots().deactivateOneHotspot(kW98OpticalChipSpotID);
-		else
-			_vm->getAllHotspots().activateOneHotspot(kW98OpticalChipSpotID);
+			if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag))
+				_vm->getAllHotspots().deactivateOneHotspot(kW98OpticalChipSpotID);
+			else
+				_vm->getAllHotspots().activateOneHotspot(kW98OpticalChipSpotID);
+		}
+		break;
 	}
 }
 
 void WSC::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
+	Movie movie(kNoDisplayElement);
+	Input movieInput;
+
 	if (JMPPPInput::isEasterEggModifierInput(input))
 		GameState.setEasterEgg(true);
 
@@ -2263,6 +2580,18 @@ void WSC::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {
 
 			_privateFlags.setFlag(kWSCPrivateClickedCatwalkCableFlag, true);
 			break;
+		case kBiotechImplantHotSpotID:
+			if (GameState.isTakenItemID(kMachineGun))
+				startExtraSequence(kImplantNoGun, kExtraCompletedFlag, kFilterNoInput);
+			else
+				startExtraSequence(kImplantWithGun, kExtraCompletedFlag, kFilterNoInput);
+			break;
+		case kW61WalchekMessageSpotID:
+			if (_vm->isDVD() && GameState.getEasterEgg() && _vm->getRandomBit())
+				startExtraSequence(kEasterEggWalchek, kExtraCompletedFlag, kFilterNoInput);
+			else
+				Neighborhood::clickInHotspot(input, clickedSpot);
+			break;
 		default:
 			Neighborhood::clickInHotspot(input, clickedSpot);
 			break;
@@ -2414,12 +2743,21 @@ void WSC::pickedUpItem(Item *item) {
 		_privateFlags.setFlag(kWSCDraggingAntidoteFlag, false);
 		playSpotSoundSync(kDrinkAntidoteIn, kDrinkAntidoteOut);
 		setUpPoison();
+		loadAmbientLoops();
 
 		if (!GameState.getWSCPickedUpAntidote()) {
 			GameState.setWSCPickedUpAntidote(true);
 			startExtraSequence(kW03SouthDeactivate, kExtraCompletedFlag, kFilterNoInput);
 		}
 		break;
+	case kMachineGun:
+		if (g_arthurChip) {
+			if (_vm->getRandomBit())
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB01", kArthurWSCGotMachineGun);
+			else
+				g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB09", kArthurWSCGotMachineGun);
+		}
+		break;
 	case kArgonPickup:
 		_vm->removeItemFromInventory((InventoryItem *)item);
 		item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister);
@@ -2611,6 +2949,15 @@ void WSC::doSolve() {
 	}
 }
 
+void WSC::setSoundFXLevel(const uint16 level) {
+	Neighborhood::setSoundFXLevel(level);
+
+	if (_extraMovie.isMovieValid())
+		_extraMovie.setVolume(level);
+	if (_welcomeSound.isSoundLoaded())
+		_welcomeSound.setVolume(level);
+}
+
 Common::String WSC::getNavMovieName() {
 	return "Images/World Science Center/WSC.movie";
 }
diff --git a/engines/pegasus/neighborhood/wsc/wsc.h b/engines/pegasus/neighborhood/wsc/wsc.h
index 239da28633..5d36d6fcc8 100644
--- a/engines/pegasus/neighborhood/wsc/wsc.h
+++ b/engines/pegasus/neighborhood/wsc/wsc.h
@@ -42,7 +42,7 @@ static const RoomID kWSC62 = 62;
 class WSC : public Neighborhood {
 public:
 	WSC(InputHandler *, PegasusEngine *);
-	~WSC() override {}
+	~WSC() override;
 
 	void flushGameState() override;
 
@@ -57,6 +57,8 @@ public:
 	bool canSolve() override;
 	void doSolve() override;
 
+	void setSoundFXLevel(const uint16) override;
+
 	void prepareForAIHint(const Common::String &) override;
 	void cleanUpAfterAIHint(const Common::String &) override;
 
@@ -126,6 +128,7 @@ protected:
 	void pickedUpItem(Item *) override;
 	void doorOpened() override;
 	void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits) override;
+	void startDoorOpenMovie(const TimeValue, const TimeValue) override;
 	void getExtraEntry(const uint32, ExtraTable::Entry &) override;
 	void takeItemFromRoom(Item *item) override;
 	void checkPeopleCrossing();
@@ -137,6 +140,7 @@ protected:
 	void getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) override;
 	void getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) override;
 	void bumpIntoWall() override;
+	void spotCompleted() override;
 	void activateHotspots() override;
 	void setUpAIRules() override;
 	Common::String getBriefingMovie() override;
@@ -153,12 +157,15 @@ protected:
 
 	FlagsArray<byte, kNumWSCPrivateFlags> _privateFlags;
 	const Hotspot *_cachedZoomSpot;
+	Hotspot _biotechImplantSpot;
+	Movie _extraMovie;
+	NotificationCallBack _extraMovieCallBack;
 	MoleculeBin _moleculeBin;
 	int32 _moleculeGameLevel, _numCorrect;
 	Movie _moleculesMovie;
 	uint32 _levelArray[6];
-	Common::Rational _energyDrainRate;
 	Sprite *_argonSprite;
+	Sound _welcomeSound;
 };
 
 } // End of namespace Pegasus
diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp
index 4affc6f244..7884191bc7 100644
--- a/engines/pegasus/pegasus.cpp
+++ b/engines/pegasus/pegasus.cpp
@@ -55,6 +55,7 @@
 #include "pegasus/ai/ai_area.h"
 #include "pegasus/items/itemlist.h"
 #include "pegasus/items/biochips/aichip.h"
+#include "pegasus/items/biochips/arthurchip.h"
 #include "pegasus/items/biochips/biochipitem.h"
 #include "pegasus/items/biochips/mapchip.h"
 #include "pegasus/items/biochips/opticalchip.h"
@@ -96,12 +97,20 @@ PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamede
 	_idlerHead = 0;
 	_currentCD = 1;
 	_introTimer = 0;
+	_toggleRequested = false;
+	_chattyAI = true;
+	_chattyArthur = true;
 	_aiSaveStream = 0;
+	_heardOverviewVoice = false;
 }
 
 PegasusEngine::~PegasusEngine() {
 	throwAwayEverything();
 
+	if (isDVD()) {
+		Arthur.destroy();
+	}
+
 	delete _resFork;
 	delete _cursor;
 	delete _continuePoint;
@@ -264,9 +273,13 @@ void PegasusEngine::createItems() {
 void PegasusEngine::createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction) {
 	switch (itemID) {
 	case kInterfaceBiochip:
-		// Unused in game, but still in the data and we need to create
-		// it because it's saved/loaded from save files.
-		new BiochipItem(itemID, neighborhoodID, roomID, direction);
+		if (isDVD()) {
+			new ArthurChip(itemID, neighborhoodID, roomID, direction);
+		} else {
+			// Unused in game, but still in the data and we need to create
+			// it because it's saved/loaded from save files.
+			new BiochipItem(itemID, neighborhoodID, roomID, direction);
+		}
 		break;
 	case kAIBiochip:
 		new AIChip(itemID, neighborhoodID, roomID, direction);
@@ -336,10 +349,26 @@ void PegasusEngine::runIntro() {
 	if (shouldQuit() || skipped)
 		return;
 
+#ifdef USE_THEORADEC
+	if (isDVD() && Common::File::exists(_introDirectory + "/BigMovie_hq.ogg")) {
+		Video::TheoraDecoder hqVideo;
+		hqVideo.setSoundType(Audio::Mixer::kPlainSoundType);
+
+		if (hqVideo.loadFile(_introDirectory + "/BigMovie_hq.ogg")) {
+			hqVideo.start();
+			playMovieScaled(&hqVideo, 0, 0);
+			return;
+		}
+	}
+#endif
+
 	video = new Video::QuickTimeDecoder();
 
 	if (!video->loadFile(_introDirectory + "/Big Movie.movie"))
-		error("Could not load intro movie");
+		if (!video->loadFile(_introDirectory + "/BigMovie.movie"))
+			error("Could not load intro movie");
+
+	video->setVolume(MIN<uint>(getAmbienceLevel(), 0xFF));
 
 	video->setVolume(MIN<uint>(getAmbienceLevel(), 0xFF));
 
@@ -545,7 +574,14 @@ bool PegasusEngine::loadFromStream(Common::SeekableReadStream *stream) {
 		}
 	}
 
+	if (isDVD()) {
+		Arthur.resetArthurState();
+		_screenDimmer.hide();
+		_screenDimmer.stopDisplaying();
+	}
 	startNeighborhood();
+	if (g_arthurChip)
+		g_arthurChip->playArthurMovieForEvent("Images/AI/Globals/XGLOBB24", kArthurLoadedSavedGame);
 
 	// Make a new continue point if this isn't already one
 	if (saveType == kNormalSave)
@@ -780,9 +816,26 @@ void PegasusEngine::introTimerExpired() {
 
 		bool skipped = false;
 
-		Video::VideoDecoder *video = new Video::QuickTimeDecoder();
-		if (!video->loadFile(_introDirectory + "/LilMovie.movie"))
-			error("Failed to load little movie");
+		Video::VideoDecoder *video = 0;
+
+#ifdef USE_THEORADEC
+		if (isDVD()) {
+			video = new Video::TheoraDecoder();
+
+			if (!video->loadFile(_introDirectory + "/LilMovie_hq.ogg")) {
+				delete video;
+				video = 0;
+			}
+		}
+#endif
+
+		if (!video) {
+			video = new Video::QuickTimeDecoder();
+			if (!video->loadFile(_introDirectory + "/LilMovie.movie"))
+				error("Failed to load little movie");
+		}
+
+		video->setVolume(MIN<uint>(getAmbienceLevel(), 0xFF));
 
 		video->setVolume(MIN<uint>(getAmbienceLevel(), 0xFF));
 
@@ -880,6 +933,7 @@ void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) {
 			_gfx->doFadeOutSync();
 			useMenu(new CreditsMenu());
 			_gfx->updateDisplay();
+			((CreditsMenu *)_gameMenu)->startCreditsMenuLoop();
 			_gfx->doFadeInSync();
 		}
 		break;
@@ -924,34 +978,103 @@ void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) {
 				_gfx->updateDisplay();
 				_gfx->doFadeInSync();
 			} else {
+				Input input;
+				if (isDVD()) {
+					InputDevice.getInput(input, kFilterAllInput);
+					if (JMPPPInput::isEasterEggModifierInput(input))
+						GameState.setEasterEgg(true);
+				}
+
 				_gfx->doFadeOutSync();
 				useMenu(0);
 				_gfx->enableErase();
 				_gfx->updateDisplay();
 				_gfx->disableErase();
 
-				Video::VideoDecoder *video = new Video::QuickTimeDecoder();
-				if (!video->loadFile(_introDirectory + "/Closing.movie"))
-					error("Could not load closing movie");
+				Video::VideoDecoder *video = 0;
+				if (GameState.getEasterEgg()) {
+#ifdef USE_THEORADEC
+					video = new Video::TheoraDecoder();
+					if (!video->loadFile(_introDirectory + "/Closing_hq2.ogg")) {
+						delete video;
+						video = 0;
+					}
+#endif
+					if (!video) {
+						video = new Video::QuickTimeDecoder();
+						if (!video->loadFile(_introDirectory + "/Closing2.movie"))
+							error("Could not load alternate closing movie");
+					}
+				} else {
+#ifdef USE_THEORADEC
+					video = new Video::TheoraDecoder();
+					if (!video->loadFile(_introDirectory + "/Closing_hq1.ogg")) {
+						delete video;
+						video = 0;
+					}
+#endif
+					if (!video) {
+						video = new Video::QuickTimeDecoder();
+						if (!video->loadFile(_introDirectory + "/Closing.movie"))
+							error("Could not load closing movie");
+					}
+				}
 
 				video->setVolume(MIN<uint>(getSoundFXLevel(), 0xFF));
 
-				uint16 x = (640 - video->getWidth() * 2) / 2;
-				uint16 y = (480 - video->getHeight() * 2) / 2;
+				uint16 newHeight = (uint16)((640.0f / (float)video->getWidth()) * (float)video->getHeight());
+				uint16 x = 0;
+				uint16 y = (480 - newHeight) / 2;
 
 				video->start();
-				playMovieScaled(video, x, y);
+				bool interrupted = playMovieScaled(video, x, y);
 
 				delete video;
 
+				if (isDVD() && !interrupted) {
+					// Display new post credits movie
+#ifdef USE_THEORADEC
+					video = new Video::TheoraDecoder();
+					if (!video->loadFile(_introDirectory + "/Closing_hq3.ogg")) {
+						delete video;
+						video = 0;
+					}
+#endif
+					if (!video) {
+						video = new Video::QuickTimeDecoder();
+						if (!video->loadFile(_introDirectory + "/Closing3.movie"))
+							error("Could not load closing 3 movie");
+					}
+
+					video->setVolume(MIN<uint>(getSoundFXLevel(), 0xFF));
+
+					video->start();
+					interrupted = playMovieScaled(video, 0, 0);
+					delete video;
+				}
+
 				if (shouldQuit())
 					return;
 
-				useMenu(new MainMenu());
-				_gfx->updateDisplay();
-				((MainMenu *)_gameMenu)->startMainMenuLoop();
-				_gfx->doFadeInSync();
-				resetIntroTimer();
+				if (isDVD()) {
+					useMenu(new CreditsMenu());
+					_gfx->updateDisplay();
+					((CreditsMenu *)_gameMenu)->startCreditsMenuLoop();
+					if (!interrupted)
+						_gfx->doFadeInSync();
+					else
+						_gfx->enableUpdates();
+				} else {
+					useMenu(new MainMenu());
+					_gfx->updateDisplay();
+					((MainMenu *)_gameMenu)->startMainMenuLoop();
+					if (!interrupted)
+						_gfx->doFadeInSync();
+					else
+						_gfx->enableUpdates();
+					resetIntroTimer();
+				}
+				GameState.setEasterEgg(false);
 			}
 		} else {
 			loadFromContinuePoint();
@@ -1080,6 +1203,8 @@ void PegasusEngine::doInterfaceOverview() {
 		Common::Rect(542, 36, 542 + 58, 36 + 20)
 	};
 
+	static const Common::Rect hiddenSpot = Common::Rect(595, 417, 595 + 4, 417 + 5);
+
 	_gfx->doFadeOutSync();
 	useMenu(0);
 
@@ -1118,7 +1243,18 @@ void PegasusEngine::doInterfaceOverview() {
 	controllerHighlight.startDisplaying();
 
 	Movie overviewText(kNoDisplayElement);
-	overviewText.initFromMovieFile("Images/Interface/Overview Mac.movie");
+
+	if (isDVD()) {
+		if (isLinux() && Common::File::exists("Images/Interface/Overview Linux.movie"))
+			overviewText.initFromMovieFile("Images/Interface/Overview Linux.movie");
+		else if (isLinux() || isWindows())
+			overviewText.initFromMovieFile("Images/Interface/Overview PC.movie");
+		else
+			overviewText.initFromMovieFile("Images/Interface/Overview ScummVM.movie");
+	} else {
+		overviewText.initFromMovieFile("Images/Interface/Overview Mac.movie");
+	}
+
 	overviewText.setDisplayOrder(0);
 	overviewText.moveElementTo(kNavAreaLeft, kNavAreaTop);
 	overviewText.startDisplaying();
@@ -1154,6 +1290,10 @@ void PegasusEngine::doInterfaceOverview() {
 	if (time == 2) {
 		highlight.hide();
 		controllerHighlight.show();
+
+		// Hidden message in the DVD version
+		if (isDVD() && hiddenSpot.contains(cursorLoc))
+			time = 12;
 	} else if (i != kNumOverviewSpots) {
 		controllerHighlight.hide();
 		Common::Rect r = overviewSpots[i];
@@ -1174,6 +1314,28 @@ void PegasusEngine::doInterfaceOverview() {
 	_gfx->updateDisplay();
 	_gfx->doFadeInSync();
 
+	Sound overviewVoice, overviewMusic;
+	SoundFader overviewMusicFader;
+
+	// In the DVD version, play the voice and some background sound
+	if (isDVD()) {
+		if (!_heardOverviewVoice) {
+			_heardOverviewVoice = true;
+			overviewVoice.initFromAIFFFile("Sounds/Overview.aiff");
+			overviewVoice.setVolume(getSoundFXLevel());
+			overviewVoice.playSound();
+		}
+
+		overviewMusic.attachFader(&overviewMusicFader);
+		overviewMusic.initFromAIFFFile("Sounds/TSA/T01NAE.NEW.32K.AIFF");
+		overviewMusicFader.setMasterVolume(getAmbienceLevel());
+		overviewMusic.loopSound();
+
+		FaderMoveSpec spec;
+		spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255);
+		overviewMusicFader.startFaderSync(spec);
+	}
+
 	for (;;) {
 		InputDevice.getInput(input, kFilterAllInput);
 
@@ -1195,6 +1357,10 @@ void PegasusEngine::doInterfaceOverview() {
 		if (time == 2) {
 			highlight.hide();
 			controllerHighlight.show();
+
+			// Hidden message in the DVD version
+			if (isDVD() && hiddenSpot.contains(cursorLoc))
+				time = 12;
 		} else if (i != kNumOverviewSpots) {
 			controllerHighlight.hide();
 			Common::Rect r = overviewSpots[i];
@@ -1223,6 +1389,16 @@ void PegasusEngine::doInterfaceOverview() {
 	highlight.hide();
 	_cursor->hide();
 
+	// Make sure we cut off the music and sound
+	overviewVoice.stopSound();
+
+	if (overviewMusic.isPlaying()) {
+		FaderMoveSpec spec;
+		spec.makeTwoKnotFaderSpec(30, 0, 255, 30, 0);
+		overviewMusicFader.startFaderSync(spec);
+		overviewMusic.stopSound();
+	}
+
 	_gfx->doFadeOutSync();
 	useMenu(new MainMenu());
 	_gfx->updateDisplay();
@@ -1376,6 +1552,40 @@ void PegasusEngine::cleanUpAfterAIHint(const Common::String &movieName) {
 		g_neighborhood->cleanUpAfterAIHint(movieName);
 }
 


Commit: a4811f67b9eb670dc0492f5c82b7805422baaa8b
    https://github.com/scummvm/scummvm/commit/a4811f67b9eb670dc0492f5c82b7805422baaa8b
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2021-03-17T17:34:14+01:00

Commit Message:
NEWS: Mention recent improvements

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index d15ef30a47..151b068b92 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -23,6 +23,7 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Switched ScummVM GUI output to UTF-32.
    - Updated the Roland MT-32 emulation code to the Munt project's mt32emu 2.4.2.
    - Updated Dropbox Cloud Storage to use the new Dropbox OAuth workflow.
+   - Major extension to the number of supported graphics scalers.
 
  AGOS:
    - Added support for the Japanese PC-98 version of Elvira 1.
@@ -55,6 +56,9 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Fixed fire animation in first room when loading saves.
    - Fixed MT-32 support.
 
+ Pegasus:
+   - Added support for DVD/GOG.com release.
+
  SAGA:
    - Added support for ITE GOG Mac CD v1.1.
    - Added support for ITE PC-98 Japanese.




More information about the Scummvm-git-logs mailing list