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

dreammaster dreammaster at scummvm.org
Sat Feb 27 23:31:43 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:
334b2a9d48 AGS: Move play to Globals
ae9a824fba AGS: Fix text strings changed in game search & replace


Commit: 334b2a9d48949680b0b84a0e35b0a61463b8a9a9
    https://github.com/scummvm/scummvm/commit/334b2a9d48949680b0b84a0e35b0a61463b8a9a9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-02-27T15:31:34-08:00

Commit Message:
AGS: Move play to Globals

Changed paths:
    engines/ags/ags.cpp
    engines/ags/engine/ac/audiochannel.cpp
    engines/ags/engine/ac/character.cpp
    engines/ags/engine/ac/characterinfo_engine.cpp
    engines/ags/engine/ac/dialog.cpp
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/ac/draw.cpp
    engines/ags/engine/ac/draw.h
    engines/ags/engine/ac/drawingsurface.cpp
    engines/ags/engine/ac/dynamicsprite.cpp
    engines/ags/engine/ac/dynobj/scriptcamera.cpp
    engines/ags/engine/ac/dynobj/scriptdrawingsurface.cpp
    engines/ags/engine/ac/dynobj/scriptviewport.cpp
    engines/ags/engine/ac/event.cpp
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/gamestate.cpp
    engines/ags/engine/ac/gamestate.h
    engines/ags/engine/ac/global_audio.cpp
    engines/ags/engine/ac/global_character.cpp
    engines/ags/engine/ac/global_debug.cpp
    engines/ags/engine/ac/global_dialog.cpp
    engines/ags/engine/ac/global_display.cpp
    engines/ags/engine/ac/global_drawingsurface.cpp
    engines/ags/engine/ac/global_game.cpp
    engines/ags/engine/ac/global_gui.cpp
    engines/ags/engine/ac/global_hotspot.cpp
    engines/ags/engine/ac/global_inventoryitem.cpp
    engines/ags/engine/ac/global_invwindow.cpp
    engines/ags/engine/ac/global_mouse.cpp
    engines/ags/engine/ac/global_object.cpp
    engines/ags/engine/ac/global_overlay.cpp
    engines/ags/engine/ac/global_palette.cpp
    engines/ags/engine/ac/global_parser.cpp
    engines/ags/engine/ac/global_region.cpp
    engines/ags/engine/ac/global_room.cpp
    engines/ags/engine/ac/global_screen.cpp
    engines/ags/engine/ac/global_timer.cpp
    engines/ags/engine/ac/global_translation.cpp
    engines/ags/engine/ac/global_video.cpp
    engines/ags/engine/ac/global_viewport.cpp
    engines/ags/engine/ac/global_walkablearea.cpp
    engines/ags/engine/ac/gui.cpp
    engines/ags/engine/ac/guiinv.cpp
    engines/ags/engine/ac/hotspot.cpp
    engines/ags/engine/ac/inventoryitem.cpp
    engines/ags/engine/ac/invwindow.cpp
    engines/ags/engine/ac/listbox.cpp
    engines/ags/engine/ac/mouse.cpp
    engines/ags/engine/ac/object.cpp
    engines/ags/engine/ac/overlay.cpp
    engines/ags/engine/ac/parser.cpp
    engines/ags/engine/ac/region.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/ac/roomobject.cpp
    engines/ags/engine/ac/runtime_defines.h
    engines/ags/engine/ac/screen.cpp
    engines/ags/engine/ac/speech.cpp
    engines/ags/engine/ac/string.cpp
    engines/ags/engine/ac/sys_events.cpp
    engines/ags/engine/ac/system.cpp
    engines/ags/engine/ac/translation.cpp
    engines/ags/engine/ac/viewport_script.cpp
    engines/ags/engine/ac/walkablearea.cpp
    engines/ags/engine/ac/walkbehind.cpp
    engines/ags/engine/debugging/debug.cpp
    engines/ags/engine/device/mousew32.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/gui/cscidialog.cpp
    engines/ags/engine/main/config.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/main/engine_setup.cpp
    engines/ags/engine/main/game_run.cpp
    engines/ags/engine/main/game_start.cpp
    engines/ags/engine/main/quit.cpp
    engines/ags/engine/main/update.cpp
    engines/ags/engine/media/audio/audio.cpp
    engines/ags/engine/media/audio/queuedaudioitem.h
    engines/ags/engine/script/script.cpp
    engines/ags/globals.cpp
    engines/ags/globals.h
    engines/ags/plugins/agsplugin.cpp


diff --git a/engines/ags/ags.cpp b/engines/ags/ags.cpp
index 4784175dd4..ea5dd58196 100644
--- a/engines/ags/ags.cpp
+++ b/engines/ags/ags.cpp
@@ -70,7 +70,6 @@ using namespace Engine;
 extern HSaveError load_game(int slotNumber, bool &data_overwritten);
 
 extern GameSetup usetup;
-extern GameState play;
 extern int our_eip;
 extern AGSPlatformDriver *platform;
 extern int convert_16bit_bgr;
@@ -92,7 +91,7 @@ extern void quit_free();
 void main_pre_init() {
 	our_eip = -999;
 	Shared::AssetManager::SetSearchPriority(Shared::kAssetPriorityDir);
-	play.takeover_data = 0;
+	_GP(play).takeover_data = 0;
 }
 
 void main_create_platform_driver() {
@@ -171,9 +170,9 @@ static int main_process_cmdline(ConfigTree &cfg, int argc, const char *argv[]) {
 		} else if (scumm_stricmp(arg, "--takeover") == 0) {
 			if (argc < ee + 2)
 				break;
-			play.takeover_data = atoi(argv[ee + 1]);
-			strncpy(play.takeover_from, argv[ee + 2], 49);
-			play.takeover_from[49] = 0;
+			_GP(play).takeover_data = atoi(argv[ee + 1]);
+			strncpy(_GP(play).takeover_from, argv[ee + 2], 49);
+			_GP(play).takeover_from[49] = 0;
 			ee += 2;
 		} else if (scumm_strnicmp(arg, "--tell", 6) == 0) {
 			if (arg[6] == 0)
@@ -396,12 +395,12 @@ void AGSEngine::setGraphicsMode(size_t w, size_t h) {
 
 bool AGSEngine::canLoadGameStateCurrently() {
 	return !::AGS3::thisroom.Options.SaveLoadDisabled &&
-		!::AGS3::inside_script && !AGS3::play.fast_forward;
+		!::AGS3::inside_script && !_GP(play).fast_forward;
 }
 
 bool AGSEngine::canSaveGameStateCurrently() {
 	return !::AGS3::thisroom.Options.SaveLoadDisabled &&
-		!::AGS3::inside_script && !AGS3::play.fast_forward;
+		!::AGS3::inside_script && !_GP(play).fast_forward;
 }
 
 Common::Error AGSEngine::loadGameState(int slot) {
diff --git a/engines/ags/engine/ac/audiochannel.cpp b/engines/ags/engine/ac/audiochannel.cpp
index 5659ead8d6..8e6e06c941 100644
--- a/engines/ags/engine/ac/audiochannel.cpp
+++ b/engines/ags/engine/ac/audiochannel.cpp
@@ -28,16 +28,16 @@
 #include "ags/shared/game/roomstruct.h"
 #include "ags/engine/script/runtimescriptvalue.h"
 #include "ags/engine/media/audio/audio_system.h"
-
 #include "ags/shared/debugging/out.h"
 #include "ags/engine/script/script_api.h"
 #include "ags/engine/script/script_runtime.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
 using namespace AGS::Shared;
 
-extern GameState play;
+
 extern RoomStruct thisroom;
 extern CCAudioClip ccDynamicAudioClip;
 
@@ -46,7 +46,7 @@ int AudioChannel_GetID(ScriptAudioChannel *channel) {
 }
 
 int AudioChannel_GetIsPlaying(ScriptAudioChannel *channel) {
-	if (play.fast_forward) {
+	if (_GP(play).fast_forward) {
 		return 0;
 	}
 
@@ -91,7 +91,7 @@ int AudioChannel_GetPosition(ScriptAudioChannel *channel) {
 	auto *ch = lock.GetChannelIfPlaying(channel->id);
 
 	if (ch) {
-		if (play.fast_forward)
+		if (_GP(play).fast_forward)
 			return 999999999;
 
 		return ch->get_pos();
@@ -104,7 +104,7 @@ int AudioChannel_GetPositionMs(ScriptAudioChannel *channel) {
 	auto *ch = lock.GetChannelIfPlaying(channel->id);
 
 	if (ch) {
-		if (play.fast_forward)
+		if (_GP(play).fast_forward)
 			return 999999999;
 
 		return ch->get_pos_ms();
@@ -165,7 +165,7 @@ void AudioChannel_SetSpeed(ScriptAudioChannel *channel, int new_speed) {
 }
 
 void AudioChannel_Stop(ScriptAudioChannel *channel) {
-	if (channel->id == SCHAN_SPEECH && play.IsNonBlockingVoiceSpeech())
+	if (channel->id == SCHAN_SPEECH && _GP(play).IsNonBlockingVoiceSpeech())
 		stop_voice_nonblocking();
 	else
 		stop_or_fade_out_channel(channel->id, -1, nullptr);
diff --git a/engines/ags/engine/ac/character.cpp b/engines/ags/engine/ac/character.cpp
index 5056a07da8..2643fbefc5 100644
--- a/engines/ags/engine/ac/character.cpp
+++ b/engines/ags/engine/ac/character.cpp
@@ -566,10 +566,10 @@ int Character_IsCollidingWithObject(CharacterInfo *chin, ScriptObject *objid) {
 
 bool Character_IsInteractionAvailable(CharacterInfo *cchar, int mood) {
 
-	play.check_interaction_only = 1;
+	_GP(play).check_interaction_only = 1;
 	RunCharacterInteraction(cchar->index_id, mood);
-	int ciwas = play.check_interaction_only;
-	play.check_interaction_only = 0;
+	int ciwas = _GP(play).check_interaction_only;
+	_GP(play).check_interaction_only = 0;
 	return (ciwas == 2);
 }
 
@@ -792,7 +792,7 @@ void Character_SetAsPlayer(CharacterInfo *chaa) {
 	if (displayed_room != playerchar->room)
 		NewRoom(playerchar->room);
 	else   // make sure it doesn't run the region interactions
-		play.player_on_region = GetRegionIDAtRoom(playerchar->x, playerchar->y);
+		_GP(play).player_on_region = GetRegionIDAtRoom(playerchar->x, playerchar->y);
 
 	if ((playerchar->activeinv >= 0) && (playerchar->inv[playerchar->activeinv] < 1))
 		playerchar->activeinv = -1;
@@ -916,7 +916,7 @@ void Character_SetSpeed(CharacterInfo *chaa, int xspeed, int yspeed) {
 void Character_StopMoving(CharacterInfo *charp) {
 
 	int chaa = charp->index_id;
-	if (chaa == play.skip_until_char_stops)
+	if (chaa == _GP(play).skip_until_char_stops)
 		EndSkippingUntilCharStops();
 
 	if (charextra[chaa].xwas != INVALID_X) {
@@ -1045,22 +1045,22 @@ void Character_RunInteraction(CharacterInfo *chaa, int mood) {
 
 int Character_GetProperty(CharacterInfo *chaa, const char *property) {
 
-	return get_int_property(_GP(game).charProps[chaa->index_id], play.charProps[chaa->index_id], property);
+	return get_int_property(_GP(game).charProps[chaa->index_id], _GP(play).charProps[chaa->index_id], property);
 
 }
 void Character_GetPropertyText(CharacterInfo *chaa, const char *property, char *bufer) {
-	get_text_property(_GP(game).charProps[chaa->index_id], play.charProps[chaa->index_id], property, bufer);
+	get_text_property(_GP(game).charProps[chaa->index_id], _GP(play).charProps[chaa->index_id], property, bufer);
 }
 const char *Character_GetTextProperty(CharacterInfo *chaa, const char *property) {
-	return get_text_property_dynamic_string(_GP(game).charProps[chaa->index_id], play.charProps[chaa->index_id], property);
+	return get_text_property_dynamic_string(_GP(game).charProps[chaa->index_id], _GP(play).charProps[chaa->index_id], property);
 }
 
 bool Character_SetProperty(CharacterInfo *chaa, const char *property, int value) {
-	return set_int_property(play.charProps[chaa->index_id], property, value);
+	return set_int_property(_GP(play).charProps[chaa->index_id], property, value);
 }
 
 bool Character_SetTextProperty(CharacterInfo *chaa, const char *property, const char *value) {
-	return set_text_property(play.charProps[chaa->index_id], property, value);
+	return set_text_property(_GP(play).charProps[chaa->index_id], property, value);
 }
 
 ScriptInvItem *Character_GetActiveInventory(CharacterInfo *chaa) {
@@ -2255,7 +2255,7 @@ void _DisplaySpeechCore(int chid, const char *displbuf) {
 		// no text, just update the current character who's speaking
 		// this allows the portrait side to be switched with an empty
 		// speech line
-		play.swap_portrait_lastchar = chid;
+		_GP(play).swap_portrait_lastchar = chid;
 		return;
 	}
 
@@ -2280,8 +2280,8 @@ void _DisplayThoughtCore(int chid, const char *displbuf) {
 	if ((_GP(game).options[OPT_SPEECHTYPE] == 0) || (_GP(game).chars[chid].thinkview <= 0)) {
 		// lucasarts-style, so we want a speech bubble actually above
 		// their head (or if they have no think anim in Sierra-style)
-		width = data_to_game_coord(play.speech_bubble_width);
-		xpp = play.RoomToScreenX(data_to_game_coord(_GP(game).chars[chid].x)) - width / 2;
+		width = data_to_game_coord(_GP(play).speech_bubble_width);
+		xpp = _GP(play).RoomToScreenX(data_to_game_coord(_GP(game).chars[chid].x)) - width / 2;
 		if (xpp < 0)
 			xpp = 0;
 		// -1 will automatically put it above the char's head
@@ -2308,7 +2308,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 
 	said_speech_line = 1;
 
-	if (play.bgspeech_stay_on_display == 0) {
+	if (_GP(play).bgspeech_stay_on_display == 0) {
 		// remove any background speech
 		for (size_t i = 0; i < screenover.size();) {
 			if (screenover[i].timeout > 0)
@@ -2332,12 +2332,12 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 		}
 	}
 
-	play.messagetime = GetTextDisplayTime(texx);
-	play.speech_in_post_state = false;
+	_GP(play).messagetime = GetTextDisplayTime(texx);
+	_GP(play).speech_in_post_state = false;
 
 	if (isPause) {
-		postpone_scheduled_music_update_by(std::chrono::milliseconds(play.messagetime * 1000 / frames_per_second));
-		GameLoopUntilValueIsNegative(&play.messagetime);
+		postpone_scheduled_music_update_by(std::chrono::milliseconds(_GP(play).messagetime * 1000 / frames_per_second));
+		GameLoopUntilValueIsNegative(&_GP(play).messagetime);
 		return;
 	}
 
@@ -2347,7 +2347,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 	if (textcol == 0)
 		textcol = 16;
 
-	Rect ui_view = play.GetUIViewport();
+	Rect ui_view = _GP(play).GetUIViewport();
 	int allowShrink = 0;
 	int bwidth = widd;
 	if (bwidth < 0)
@@ -2459,27 +2459,27 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 		if ((useview >= 0) && (_GP(game).options[OPT_SPEECHTYPE] > 0)) {
 			// Sierra-style close-up portrait
 
-			if (play.swap_portrait_lastchar != aschar) {
+			if (_GP(play).swap_portrait_lastchar != aschar) {
 				// if the portraits are set to Alternate, OR they are
 				// set to Left but swap_portrait has been set to 1 (the old
 				// method for enabling it), then swap them round
 				if ((_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_ALTERNATE) ||
 				        ((_GP(game).options[OPT_PORTRAITSIDE] == 0) &&
-				         (play.swap_portrait_side > 0))) {
+				         (_GP(play).swap_portrait_side > 0))) {
 
-					if (play.swap_portrait_side == 2)
-						play.swap_portrait_side = 1;
+					if (_GP(play).swap_portrait_side == 2)
+						_GP(play).swap_portrait_side = 1;
 					else
-						play.swap_portrait_side = 2;
+						_GP(play).swap_portrait_side = 2;
 				}
 
 				if (_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_XPOSITION) {
 					// Portrait side based on character X-positions
-					if (play.swap_portrait_lastchar < 0) {
+					if (_GP(play).swap_portrait_lastchar < 0) {
 						// No previous character been spoken to
 						// therefore, assume it's the player
 						if (_GP(game).playercharacter != aschar && _GP(game).chars[_GP(game).playercharacter].room == speakingChar->room && _GP(game).chars[_GP(game).playercharacter].on == 1)
-							play.swap_portrait_lastchar = _GP(game).playercharacter;
+							_GP(play).swap_portrait_lastchar = _GP(game).playercharacter;
 						else
 							// The player's not here. Find another character in this room
 							// that it could be
@@ -2487,31 +2487,31 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 								if ((_GP(game).chars[ce].room == speakingChar->room) &&
 								        (_GP(game).chars[ce].on == 1) &&
 								        (ce != aschar)) {
-									play.swap_portrait_lastchar = ce;
+									_GP(play).swap_portrait_lastchar = ce;
 									break;
 								}
 							}
 					}
 
-					if (play.swap_portrait_lastchar >= 0) {
+					if (_GP(play).swap_portrait_lastchar >= 0) {
 						// if this character is right of the one before, put the
 						// portrait on the right
-						if (speakingChar->x > _GP(game).chars[play.swap_portrait_lastchar].x)
-							play.swap_portrait_side = -1;
+						if (speakingChar->x > _GP(game).chars[_GP(play).swap_portrait_lastchar].x)
+							_GP(play).swap_portrait_side = -1;
 						else
-							play.swap_portrait_side = 0;
+							_GP(play).swap_portrait_side = 0;
 					}
 				}
-				play.swap_portrait_lastlastchar = play.swap_portrait_lastchar;
-				play.swap_portrait_lastchar = aschar;
+				_GP(play).swap_portrait_lastlastchar = _GP(play).swap_portrait_lastchar;
+				_GP(play).swap_portrait_lastchar = aschar;
 			} else
 				// If the portrait side is based on the character's X position and the same character is
 				// speaking, compare against the previous *previous* character to see where the speech should be
-				if (_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_XPOSITION && play.swap_portrait_lastlastchar >= 0) {
-					if (speakingChar->x > _GP(game).chars[play.swap_portrait_lastlastchar].x)
-						play.swap_portrait_side = -1;
+				if (_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_XPOSITION && _GP(play).swap_portrait_lastlastchar >= 0) {
+					if (speakingChar->x > _GP(game).chars[_GP(play).swap_portrait_lastlastchar].x)
+						_GP(play).swap_portrait_side = -1;
 					else
-						play.swap_portrait_side = 0;
+						_GP(play).swap_portrait_side = 0;
 				}
 
 			// Determine whether to display the portrait on the left or right
@@ -2519,8 +2519,8 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 
 			if (_GP(game).options[OPT_SPEECHTYPE] == 3) {
 			}  // always on left with QFG-style speech
-			else if ((play.swap_portrait_side == 1) ||
-			         (play.swap_portrait_side == -1) ||
+			else if ((_GP(play).swap_portrait_side == 1) ||
+			         (_GP(play).swap_portrait_side == -1) ||
 			         (_GP(game).options[OPT_PORTRAITSIDE] == PORTRAIT_RIGHT))
 				portrait_on_right = 1;
 
@@ -2553,13 +2553,13 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 				// QFG4-style whole screen picture
 				closeupface = BitmapHelper::CreateBitmap(ui_view.GetWidth(), ui_view.GetHeight(), _GP(spriteset)[viptr->loops[0].frames[0].pic]->GetColorDepth());
 				closeupface->Clear(0);
-				if (xx < 0 && play.speech_portrait_placement) {
+				if (xx < 0 && _GP(play).speech_portrait_placement) {
 					facetalk_qfg4_override_placement_x = true;
-					view_frame_x = play.speech_portrait_x;
+					view_frame_x = _GP(play).speech_portrait_x;
 				}
-				if (yy < 0 && play.speech_portrait_placement) {
+				if (yy < 0 && _GP(play).speech_portrait_placement) {
 					facetalk_qfg4_override_placement_y = true;
-					view_frame_y = play.speech_portrait_y;
+					view_frame_y = _GP(play).speech_portrait_y;
 				} else {
 					view_frame_y = ui_view.GetHeight() / 2 - _GP(game).SpriteInfos[viptr->loops[0].frames[0].pic].Height / 2;
 				}
@@ -2569,8 +2569,8 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 				tdyp = -1;  // center vertically
 			} else {
 				// KQ6-style close-up face picture
-				if (yy < 0 && play.speech_portrait_placement) {
-					ovr_yp = play.speech_portrait_y;
+				if (yy < 0 && _GP(play).speech_portrait_placement) {
+					ovr_yp = _GP(play).speech_portrait_y;
 				} else if (yy < 0)
 					ovr_yp = adjust_y_for_guis(ovr_yp);
 				else
@@ -2580,7 +2580,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 				ovr_type = OVER_PICTURE;
 
 				if (yy < 0)
-					tdyp = ovr_yp + get_textwindow_top_border_height(play.speech_textwindow_gui);
+					tdyp = ovr_yp + get_textwindow_top_border_height(_GP(play).speech_textwindow_gui);
 			}
 			const ViewFrame *vf = &viptr->loops[0].frames[0];
 			const bool closeupface_has_alpha = (_GP(game).SpriteInfos[vf->pic].Flags & SPF_ALPHACHANNEL) != 0;
@@ -2588,16 +2588,16 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 
 			int overlay_x = get_fixed_pixel_size(10);
 			if (xx < 0) {
-				tdxp = bigx + get_textwindow_border_width(play.speech_textwindow_gui) / 2;
-				if (play.speech_portrait_placement) {
-					overlay_x = play.speech_portrait_x;
+				tdxp = bigx + get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
+				if (_GP(play).speech_portrait_placement) {
+					overlay_x = _GP(play).speech_portrait_x;
 					tdxp += overlay_x + get_fixed_pixel_size(6);
 				} else {
 					tdxp += get_fixed_pixel_size(16);
 				}
 
 				int maxWidth = (ui_view.GetWidth() - tdxp) - get_fixed_pixel_size(5) -
-				               get_textwindow_border_width(play.speech_textwindow_gui) / 2;
+				               get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
 
 				if (bwidth > maxWidth)
 					bwidth = maxWidth;
@@ -2613,10 +2613,10 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 			if (portrait_on_right) {
 				if ((xx < 0) || (widd < 0)) {
 					tdxp = get_fixed_pixel_size(9);
-					if (play.speech_portrait_placement) {
-						overlay_x = (ui_view.GetWidth() - bigx) - play.speech_portrait_x;
+					if (_GP(play).speech_portrait_placement) {
+						overlay_x = (ui_view.GetWidth() - bigx) - _GP(play).speech_portrait_x;
 						int maxWidth = overlay_x - tdxp - get_fixed_pixel_size(9) -
-						               get_textwindow_border_width(play.speech_textwindow_gui) / 2;
+						               get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
 						if (bwidth > maxWidth)
 							bwidth = maxWidth;
 					} else {
@@ -2626,7 +2626,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 					overlay_x = (xx + widd - bigx) - get_fixed_pixel_size(5);
 					tdxp = xx;
 				}
-				tdxp += get_textwindow_border_width(play.speech_textwindow_gui) / 2;
+				tdxp += get_textwindow_border_width(_GP(play).speech_textwindow_gui) / 2;
 				allowShrink = 2;
 			}
 			if (_GP(game).options[OPT_SPEECHTYPE] == 3)
@@ -2681,7 +2681,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 			if (widd < 0) {
 				bwidth = ui_view.GetWidth() / 2 + ui_view.GetWidth() / 6;
 				// If they are close to the screen edge, make the text narrower
-				int relx = play.RoomToScreenX(data_to_game_coord(speakingChar->x));
+				int relx = _GP(play).RoomToScreenX(data_to_game_coord(speakingChar->x));
 				if ((relx < ui_view.GetWidth() / 4) || (relx > ui_view.GetWidth() - (ui_view.GetWidth() / 4)))
 					bwidth -= ui_view.GetWidth() / 5;
 			}
@@ -2709,7 +2709,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 	our_eip = 155;
 	_display_at(tdxp, tdyp, bwidth, texx, DISPLAYTEXT_SPEECH, textcol, isThought, allowShrink, overlayPositionFixed);
 	our_eip = 156;
-	if ((play.in_conversation > 0) && (_GP(game).options[OPT_SPEECHTYPE] == 3))
+	if ((_GP(play).in_conversation > 0) && (_GP(game).options[OPT_SPEECHTYPE] == 3))
 		closeupface = nullptr;
 	if (closeupface != nullptr)
 		remove_screen_overlay(ovr_type);
@@ -2736,7 +2736,7 @@ void _displayspeech(const char *texx, int aschar, int xx, int yy, int widd, int
 	}
 	char_speaking = -1;
 	char_thinking = -1;
-	if (play.IsBlockingVoiceSpeech())
+	if (_GP(play).IsBlockingVoiceSpeech())
 		stop_voice_speech();
 }
 
@@ -2825,8 +2825,8 @@ PViewport FindNearestViewport(int charid) {
 	Rect bbox = GetCharacterRoomBBox(charid, true);
 	float min_dist = -1.f;
 	PViewport nearest_view;
-	for (int i = 0; i < play.GetRoomViewportCount(); ++i) {
-		auto view = play.GetRoomViewport(i);
+	for (int i = 0; i < _GP(play).GetRoomViewportCount(); ++i) {
+		auto view = _GP(play).GetRoomViewport(i);
 		if (!view->IsVisible())
 			continue;
 		auto cam = view->GetCamera();
@@ -2841,7 +2841,7 @@ PViewport FindNearestViewport(int charid) {
 			nearest_view = view;
 		}
 	}
-	return nearest_view ? nearest_view : play.GetRoomViewport(0);
+	return nearest_view ? nearest_view : _GP(play).GetRoomViewport(0);
 }
 
 //=============================================================================
diff --git a/engines/ags/engine/ac/characterinfo_engine.cpp b/engines/ags/engine/ac/characterinfo_engine.cpp
index 457de9e35c..a00321c88e 100644
--- a/engines/ags/engine/ac/characterinfo_engine.cpp
+++ b/engines/ags/engine/ac/characterinfo_engine.cpp
@@ -44,7 +44,7 @@ using namespace AGS::Shared;
 extern ViewStruct *views;
 
 extern int displayed_room;
-extern GameState play;
+
 extern int char_speaking;
 extern RoomStruct thisroom;
 extern unsigned int loopcounter;
@@ -274,8 +274,8 @@ int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
 			wait = update_lip_sync(view, loop, &fraa) - 1;
 			// closed mouth at end of sentence
 			// NOTE: standard lip-sync is synchronized with text timer, not voice file
-			if (play.speech_in_post_state ||
-				((play.messagetime >= 0) && (play.messagetime < play.close_mouth_speech_time)))
+			if (_GP(play).speech_in_post_state ||
+				((_GP(play).messagetime >= 0) && (_GP(play).messagetime < _GP(play).close_mouth_speech_time)))
 				frame = 0;
 
 			if (frame != fraa) {
@@ -313,10 +313,10 @@ int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
 				frame++;
 
 			if ((aa == char_speaking) &&
-				(play.speech_in_post_state ||
-				((!play.speech_has_voice) &&
-					(play.close_mouth_speech_time > 0) &&
-					(play.messagetime < play.close_mouth_speech_time)))) {
+				(_GP(play).speech_in_post_state ||
+				((!_GP(play).speech_has_voice) &&
+					(_GP(play).close_mouth_speech_time > 0) &&
+					(_GP(play).messagetime < _GP(play).close_mouth_speech_time)))) {
 				// finished talking - stop animation
 				animating = 0;
 				frame = 0;
@@ -346,7 +346,7 @@ int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
 				} else {
 					frame = 0;
 					// if it's a multi-loop animation, go back to start
-					if (play.no_multiloop_repeat == 0) {
+					if (_GP(play).no_multiloop_repeat == 0) {
 						while ((loop > 0) &&
 							(views[view].loops[loop - 1].RunNextLoop()))
 							loop--;
@@ -386,8 +386,8 @@ void CharacterInfo::update_character_follower(int &aa, int &numSheep, int *follo
 			if (room == 0) {
 				// appear in the new room
 				room = _GP(game).chars[following].room;
-				x = play.entered_at_x;
-				y = play.entered_at_y;
+				x = _GP(play).entered_at_x;
+				y = _GP(play).entered_at_y;
 			}
 		}
 		// wait a bit, so we're not constantly walking
@@ -403,25 +403,25 @@ void CharacterInfo::update_character_follower(int &aa, int &numSheep, int *follo
 			if (room == displayed_room) {
 				// only move to the room-entered position if coming into
 				// the current room
-				if (play.entered_at_x > (thisroom.Width - 8)) {
+				if (_GP(play).entered_at_x > (thisroom.Width - 8)) {
 					x = thisroom.Width + 8;
-					y = play.entered_at_y;
-				} else if (play.entered_at_x < 8) {
+					y = _GP(play).entered_at_y;
+				} else if (_GP(play).entered_at_x < 8) {
 					x = -8;
-					y = play.entered_at_y;
-				} else if (play.entered_at_y > (thisroom.Height - 8)) {
+					y = _GP(play).entered_at_y;
+				} else if (_GP(play).entered_at_y > (thisroom.Height - 8)) {
 					y = thisroom.Height + 8;
-					x = play.entered_at_x;
-				} else if (play.entered_at_y < thisroom.Edges.Top + 8) {
+					x = _GP(play).entered_at_x;
+				} else if (_GP(play).entered_at_y < thisroom.Edges.Top + 8) {
 					y = thisroom.Edges.Top + 1;
-					x = play.entered_at_x;
+					x = _GP(play).entered_at_x;
 				} else {
 					// not at one of the edges
 					// delay for a few seconds to let the player move
-					room = -play.follow_change_room_timer;
+					room = -_GP(play).follow_change_room_timer;
 				}
 				if (room >= 0) {
-					walk_character(aa, play.entered_at_x, play.entered_at_y, 1, true);
+					walk_character(aa, _GP(play).entered_at_x, _GP(play).entered_at_y, 1, true);
 					doing_nothing = 0;
 				}
 			}
diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index de5d419f4e..4e61043a88 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -71,7 +71,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 
 
-extern GameState play;
+
 extern ccInstance *dialogScriptsInst;
 extern int in_new_room;
 extern CharacterInfo *playerchar;
@@ -339,9 +339,9 @@ int write_dialog_options(Bitmap *ds, bool ds_has_alpha, int dlgxp, int curyp, in
 	for (ww = 0; ww < numdisp; ww++) {
 
 		if ((dtop->optionflags[(int)disporder[ww]] & DFLG_HASBEENCHOSEN) &&
-		        (play.read_dialog_option_colour >= 0)) {
+		        (_GP(play).read_dialog_option_colour >= 0)) {
 			// 'read' colour
-			text_color = ds->GetCompatibleColor(play.read_dialog_option_colour);
+			text_color = ds->GetCompatibleColor(_GP(play).read_dialog_option_colour);
 		} else {
 			// 'unread' colour
 			text_color = ds->GetCompatibleColor(playerchar->talkcolor);
@@ -487,7 +487,7 @@ void DialogOptions::Prepare(int _dlgnum, bool _runGameLoopsInBackground) {
 
 	can_run_delayed_command();
 
-	play.in_conversation ++;
+	_GP(play).in_conversation ++;
 
 	update_polled_stuff_if_runtime();
 
@@ -502,7 +502,7 @@ void DialogOptions::Prepare(int _dlgnum, bool _runGameLoopsInBackground) {
 
 	update_polled_stuff_if_runtime();
 
-	const Rect &ui_view = play.GetUIViewport();
+	const Rect &ui_view = _GP(play).GetUIViewport();
 	tempScrn = BitmapHelper::CreateBitmap(ui_view.GetWidth(), ui_view.GetHeight(), _GP(game).GetColorDepth());
 
 	set_mouse_cursor(CURS_ARROW);
@@ -513,7 +513,7 @@ void DialogOptions::Prepare(int _dlgnum, bool _runGameLoopsInBackground) {
 	numdisp = 0;
 
 	parserActivated = 0;
-	if ((dtop->topicFlags & DTFLG_SHOWPARSER) && (play.disable_dialog_parser == 0)) {
+	if ((dtop->topicFlags & DTFLG_SHOWPARSER) && (_GP(play).disable_dialog_parser == 0)) {
 		parserInput = new GUITextBox();
 		parserInput->Height = lineheight + get_fixed_pixel_size(4);
 		parserInput->SetShowBorder(true);
@@ -533,17 +533,17 @@ void DialogOptions::Show() {
 	if (numdisp < 1) quit("!DoDialog: all options have been turned off");
 	// Don't display the options if there is only one and the parser
 	// is not enabled.
-	if (!((numdisp > 1) || (parserInput != nullptr) || (play.show_single_dialog_option))) {
+	if (!((numdisp > 1) || (parserInput != nullptr) || (_GP(play).show_single_dialog_option))) {
 		chose = disporder[0];  // only one choice, so select it
 		return;
 	}
 
 	is_textwindow = 0;
-	forecol = play.dialog_options_highlight_color;
+	forecol = _GP(play).dialog_options_highlight_color;
 
 	mouseison = -1;
 	mousewason = -10;
-	const Rect &ui_view = play.GetUIViewport();
+	const Rect &ui_view = _GP(play).GetUIViewport();
 	dirtyx = 0;
 	dirtyy = 0;
 	dirtywidth = ui_view.GetWidth();
@@ -587,8 +587,8 @@ void DialogOptions::Show() {
 
 		}
 	} else {
-		//dlgyp=(play.viewport.GetHeight()-numdisp*txthit)-1;
-		const Rect &uiView = play.GetUIViewport();
+		//dlgyp=(_GP(play).viewport.GetHeight()-numdisp*txthit)-1;
+		const Rect &uiView = _GP(play).GetUIViewport();
 		areawid = uiView.GetWidth() - 5;
 		padding = TEXTWINDOW_PADDING_DEFAULT;
 		GET_OPTIONS_HEIGHT
@@ -601,7 +601,7 @@ void DialogOptions::Show() {
 		dialog_abs_x = 0;
 	}
 	if (!is_textwindow)
-		areawid -= data_to_game_coord(play.dialog_options_x) * 2;
+		areawid -= data_to_game_coord(_GP(play).dialog_options_x) * 2;
 
 	orixp = dlgxp;
 	oriyp = dlgyp;
@@ -609,7 +609,7 @@ void DialogOptions::Show() {
 	mouseison = -10;
 
 	update_polled_stuff_if_runtime();
-	if (!play.mouse_cursor_hidden)
+	if (!_GP(play).mouse_cursor_hidden)
 		ags_domouse(DOMOUSE_ENABLE);
 	update_polled_stuff_if_runtime();
 
@@ -617,7 +617,7 @@ void DialogOptions::Show() {
 	while (Run() && !SHOULD_QUIT) {
 	}
 
-	if (!play.mouse_cursor_hidden)
+	if (!_GP(play).mouse_cursor_hidden)
 		ags_domouse(DOMOUSE_DISABLE);
 }
 
@@ -635,7 +635,7 @@ void DialogOptions::Redraw() {
 
 	dlgxp = orixp;
 	dlgyp = oriyp;
-	const Rect &ui_view = play.GetUIViewport();
+	const Rect &ui_view = _GP(play).GetUIViewport();
 
 	bool options_surface_has_alpha = false;
 
@@ -662,7 +662,7 @@ void DialogOptions::Redraw() {
 		ccDialogOptionsRendering.needRepaint = false;
 	} else if (is_textwindow) {
 		// text window behind the options
-		areawid = data_to_game_coord(play.max_dialogoption_width);
+		areawid = data_to_game_coord(_GP(play).max_dialogoption_width);
 		int biggest = 0;
 		padding = guis[_GP(game).options[OPT_DIALOGIFACE]].Padding;
 		for (int i = 0; i < numdisp; ++i) {
@@ -673,9 +673,9 @@ void DialogOptions::Redraw() {
 		if (biggest < areawid - ((2 * padding + 6) + bullet_wid))
 			areawid = biggest + ((2 * padding + 6) + bullet_wid);
 
-		if (areawid < data_to_game_coord(play.min_dialogoption_width)) {
-			areawid = data_to_game_coord(play.min_dialogoption_width);
-			if (play.min_dialogoption_width > play.max_dialogoption_width)
+		if (areawid < data_to_game_coord(_GP(play).min_dialogoption_width)) {
+			areawid = data_to_game_coord(_GP(play).min_dialogoption_width);
+			if (_GP(play).min_dialogoption_width > _GP(play).max_dialogoption_width)
 				quit("!_GP(game).min_dialogoption_width is larger than _GP(game).max_dialogoption_width");
 		}
 
@@ -744,8 +744,8 @@ void DialogOptions::Redraw() {
 			options_surface_has_alpha = false;
 		}
 
-		dlgxp += data_to_game_coord(play.dialog_options_x);
-		dlgyp += data_to_game_coord(play.dialog_options_y);
+		dlgxp += data_to_game_coord(_GP(play).dialog_options_x);
+		dlgyp += data_to_game_coord(_GP(play).dialog_options_y);
 
 		// if they use a negative dialog_options_y, make sure the
 		// area gets marked as dirty
@@ -756,9 +756,9 @@ void DialogOptions::Redraw() {
 		curyp = dlgyp;
 		curyp = write_dialog_options(ds, options_surface_has_alpha, dlgxp, curyp, numdisp, mouseison, areawid, bullet_wid, usingfont, dtop, disporder, dispyp, linespacing, forecol, padding);
 
-		/*if (curyp > play.viewport.GetHeight()) {
-		  dlgyp = play.viewport.GetHeight() - (curyp - dlgyp);
-		  ds->FillRect(Rect(0,dlgyp-1,play.viewport.GetWidth()-1,play.viewport.GetHeight()-1);
+		/*if (curyp > _GP(play).viewport.GetHeight()) {
+		  dlgyp = _GP(play).viewport.GetHeight() - (curyp - dlgyp);
+		  ds->FillRect(Rect(0,dlgyp-1,_GP(play).viewport.GetWidth()-1,_GP(play).viewport.GetHeight()-1);
 		  goto redraw_options;
 		}*/
 		if (parserInput)
@@ -821,9 +821,9 @@ bool DialogOptions::Run() {
 	const bool new_custom_render = usingCustomRendering && _GP(game).options[OPT_DIALOGOPTIONSAPI] >= 0;
 
 	if (runGameLoopsInBackground) {
-		play.disabled_user_interface++;
+		_GP(play).disabled_user_interface++;
 		UpdateGameOnce(false, ddb, dirtyx, dirtyy);
-		play.disabled_user_interface--;
+		_GP(play).disabled_user_interface--;
 	} else {
 		update_audio_system_on_game_loop();
 		render_graphics(ddb, dirtyx, dirtyy);
@@ -835,14 +835,14 @@ bool DialogOptions::Run() {
 	}
 
 	int gkey;
-	if (run_service_key_controls(gkey) && !play.IsIgnoringInput()) {
+	if (run_service_key_controls(gkey) && !_GP(play).IsIgnoringInput()) {
 		if (parserInput) {
 			wantRefresh = true;
 			// type into the parser
 			if ((gkey == 361) || ((gkey == ' ') && (strlen(parserInput->Text) == 0))) {
 				// write previous contents into textbox (F3 or Space when box is empty)
-				for (unsigned int i = strlen(parserInput->Text); i < strlen(play.lastParserEntry); i++) {
-					parserInput->OnKeyPress(play.lastParserEntry[i]);
+				for (unsigned int i = strlen(parserInput->Text); i < strlen(_GP(play).lastParserEntry); i++) {
+					parserInput->OnKeyPress(_GP(play).lastParserEntry[i]);
 				}
 				//ags_domouse(DOMOUSE_DISABLE);
 				Redraw();
@@ -915,7 +915,7 @@ bool DialogOptions::Run() {
 	int mouseButtonPressed = NONE;
 	int mouseWheelTurn = 0;
 	if (run_service_mb_controls(mouseButtonPressed, mouseWheelTurn) && mouseButtonPressed >= 0 &&
-	        !play.IsIgnoringInput()) {
+	        !_GP(play).IsIgnoringInput()) {
 		if (mouseison < 0 && !new_custom_render) {
 			if (usingCustomRendering) {
 				runDialogOptionMouseClickHandlerFunc.params[0].SetDynamicObject(&ccDialogOptionsRendering, &ccDialogOptionsRendering);
@@ -988,7 +988,7 @@ bool DialogOptions::Run() {
 
 	update_polled_stuff_if_runtime();
 
-	if (play.fast_forward == 0) {
+	if (_GP(play).fast_forward == 0) {
 		WaitForNextFrame();
 	}
 
@@ -1000,7 +1000,7 @@ void DialogOptions::Close() {
 	invalidate_screen();
 
 	if (parserActivated) {
-		strcpy(play.lastParserEntry, parserInput->Text);
+		strcpy(_GP(play).lastParserEntry, parserInput->Text);
 		ParseText(parserInput->Text);
 		chose = CHOSE_TEXTPARSER;
 	}
@@ -1016,7 +1016,7 @@ void DialogOptions::Close() {
 
 	set_mouse_cursor(curswas);
 	// In case it's the QFG4 style dialog, remove the black screen
-	play.in_conversation--;
+	_GP(play).in_conversation--;
 	remove_screen_overlay(OVER_COMPLETE);
 
 	delete tempScrn;
@@ -1055,7 +1055,7 @@ void do_conversation(int dlgnum) {
 
 	// AGS 2.x always makes the mouse cursor visible when displaying a dialog.
 	if (loaded_game_file_version <= kGameVersion_272)
-		play.mouse_cursor_hidden = 0;
+		_GP(play).mouse_cursor_hidden = 0;
 
 	int dlgnum_was = dlgnum;
 	int previousTopics[MAX_TOPIC_HISTORY];
@@ -1068,7 +1068,7 @@ void do_conversation(int dlgnum) {
 	        (tocar == RUN_DIALOG_GOTO_PREVIOUS)) {
 		// 'stop' or 'goto-previous' from first startup script
 		remove_screen_overlay(OVER_COMPLETE);
-		play.in_conversation--;
+		_GP(play).in_conversation--;
 		return;
 	} else if (tocar >= 0)
 		dlgnum = tocar;
diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 1a4d7c5c17..4f086346de 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -60,7 +60,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Shared::BitmapHelper;
 
-extern GameState play;
+
 
 extern int longestline;
 extern AGSPlatformDriver *platform;
@@ -89,7 +89,7 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 	snprintf(todis, STD_BUFFER_SIZE - 1, "%s", text);
 	int usingGui = -1;
 	if (use_speech_textwindow)
-		usingGui = play.speech_textwindow_gui;
+		usingGui = _GP(play).speech_textwindow_gui;
 	else if (use_thought_gui)
 		usingGui = _GP(game).options[OPT_THOUGHTGUI];
 
@@ -105,13 +105,13 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 
 	// AGS 2.x: If the screen is faded out, fade in again when displaying a message box.
 	if (!asspch && (loaded_game_file_version <= kGameVersion_272))
-		play.screen_is_faded_out = 0;
+		_GP(play).screen_is_faded_out = 0;
 
 	// if it's a normal message box and the game was being skipped,
 	// ensure that the screen is up to date before the message box
 	// is drawn on top of it
 	// TODO: is this really necessary anymore?
-	if ((play.skip_until_char_stops >= 0) && (disp_type == DISPLAYTEXT_MESSAGEBOX))
+	if ((_GP(play).skip_until_char_stops >= 0) && (disp_type == DISPLAYTEXT_MESSAGEBOX))
 		render_graphics();
 
 	EndSkippingUntilCharStops();
@@ -120,7 +120,7 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 		// ensure that the window is wide enough to display
 		// any top bar text
 		int topBarWid = wgettextwidth_compensate(topBar.text, topBar.font);
-		topBarWid += data_to_game_coord(play.top_bar_borderwidth + 2) * 2;
+		topBarWid += data_to_game_coord(_GP(play).top_bar_borderwidth + 2) * 2;
 		if (longestline < topBarWid)
 			longestline = topBarWid;
 		// the top bar should behave like DisplaySpeech wrt blocking
@@ -130,12 +130,12 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 	if (asspch > 0) {
 		// update the all_buttons_disabled variable in advance
 		// of the adjust_x/y_for_guis calls
-		play.disabled_user_interface++;
+		_GP(play).disabled_user_interface++;
 		update_gui_disabled_status();
-		play.disabled_user_interface--;
+		_GP(play).disabled_user_interface--;
 	}
 
-	const Rect &ui_view = play.GetUIViewport();
+	const Rect &ui_view = _GP(play).GetUIViewport();
 	if (xx == OVR_AUTOPLACE);
 	// centre text in middle of screen
 	else if (yy < 0) yy = ui_view.GetHeight() / 2 - disp.fulltxtheight / 2 - padding;
@@ -222,10 +222,10 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 				else
 					text_color = text_window_ds->GetCompatibleColor(-asspch);
 
-				wouttext_aligned(text_window_ds, ttxleft, ttyp, oriwid, usingfont, text_color, Lines[ee], play.text_align);
+				wouttext_aligned(text_window_ds, ttxleft, ttyp, oriwid, usingfont, text_color, Lines[ee], _GP(play).text_align);
 			} else {
 				text_color = text_window_ds->GetCompatibleColor(asspch);
-				wouttext_aligned(text_window_ds, ttxleft, ttyp, wii, usingfont, text_color, Lines[ee], play.speech_text_align);
+				wouttext_aligned(text_window_ds, ttxleft, ttyp, wii, usingfont, text_color, Lines[ee], _GP(play).speech_text_align);
 			}
 		}
 	} else {
@@ -239,7 +239,7 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 		adjust_y_coordinate_for_text(&yoffs, usingfont);
 
 		for (size_t ee = 0; ee < Lines.Count(); ee++)
-			wouttext_aligned(text_window_ds, xoffs, yoffs + ee * disp.linespacing, oriwid, usingfont, text_color, Lines[ee], play.text_align);
+			wouttext_aligned(text_window_ds, xoffs, yoffs + ee * disp.linespacing, oriwid, usingfont, text_color, Lines[ee], _GP(play).text_align);
 	}
 
 	int ovrtype = OVER_TEXTMSG;
@@ -258,16 +258,16 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 	//
 	if (disp_type == DISPLAYTEXT_MESSAGEBOX) {
 		// If fast-forwarding, then skip immediately
-		if (play.fast_forward) {
+		if (_GP(play).fast_forward) {
 			remove_screen_overlay(OVER_TEXTMSG);
-			play.messagetime = -1;
+			_GP(play).messagetime = -1;
 			return 0;
 		}
 
-		if (!play.mouse_cursor_hidden)
+		if (!_GP(play).mouse_cursor_hidden)
 			ags_domouse(DOMOUSE_ENABLE);
 		int countdown = GetTextDisplayTime(todis);
-		int skip_setting = user_to_internal_skip_speech((SkipSpeechStyle)play.skip_display);
+		int skip_setting = user_to_internal_skip_speech((SkipSpeechStyle)_GP(play).skip_display);
 		// Loop until skipped
 		for (;;) {
 			if (SHOULD_QUIT)
@@ -278,32 +278,32 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 			int mbut, mwheelz;
 			if (run_service_mb_controls(mbut, mwheelz) && mbut >= 0) {
 				check_skip_cutscene_mclick(mbut);
-				if (play.fast_forward)
+				if (_GP(play).fast_forward)
 					break;
-				if (skip_setting & SKIP_MOUSECLICK && !play.IsIgnoringInput())
+				if (skip_setting & SKIP_MOUSECLICK && !_GP(play).IsIgnoringInput())
 					break;
 			}
 			int kp;
 			if (run_service_key_controls(kp)) {
 				check_skip_cutscene_keypress(kp);
-				if (play.fast_forward)
+				if (_GP(play).fast_forward)
 					break;
-				if ((skip_setting & SKIP_KEYPRESS) && !play.IsIgnoringInput())
+				if ((skip_setting & SKIP_KEYPRESS) && !_GP(play).IsIgnoringInput())
 					break;
 			}
 
 			update_polled_stuff_if_runtime();
 
-			if (play.fast_forward == 0) {
+			if (_GP(play).fast_forward == 0) {
 				WaitForNextFrame();
 			}
 
 			countdown--;
 
 			// Special behavior when coupled with a voice-over
-			if (play.speech_has_voice) {
+			if (_GP(play).speech_has_voice) {
 				// extend life of text if the voice hasn't finished yet
-				if (channel_is_playing(SCHAN_SPEECH) && (play.fast_forward == 0)) {
+				if (channel_is_playing(SCHAN_SPEECH) && (_GP(play).fast_forward == 0)) {
 					if (countdown <= 1)
 						countdown = 1;
 				} else // if the voice has finished, remove the speech
@@ -311,26 +311,26 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 			}
 			// Test for the timed auto-skip
 			if ((countdown < 1) && (skip_setting & SKIP_AUTOTIMER)) {
-				play.SetIgnoreInput(play.ignore_user_input_after_text_timeout_ms);
+				_GP(play).SetIgnoreInput(_GP(play).ignore_user_input_after_text_timeout_ms);
 				break;
 			}
 			// if skipping cutscene, don't get stuck on No Auto Remove text boxes
-			if ((countdown < 1) && (play.fast_forward))
+			if ((countdown < 1) && (_GP(play).fast_forward))
 				break;
 		}
-		if (!play.mouse_cursor_hidden)
+		if (!_GP(play).mouse_cursor_hidden)
 			ags_domouse(DOMOUSE_DISABLE);
 		remove_screen_overlay(OVER_TEXTMSG);
 		invalidate_screen();
 	} else {
 		// if the speech does not time out, but we are skipping a cutscene,
 		// allow it to time out
-		if ((play.messagetime < 0) && (play.fast_forward))
-			play.messagetime = 2;
+		if ((_GP(play).messagetime < 0) && (_GP(play).fast_forward))
+			_GP(play).messagetime = 2;
 
 		if (!overlayPositionFixed) {
 			screenover[nse].positionRelativeToScreen = false;
-			VpPoint vpt = play.GetRoomViewport(0)->ScreenToRoom(screenover[nse].x, screenover[nse].y, false);
+			VpPoint vpt = _GP(play).GetRoomViewport(0)->ScreenToRoom(screenover[nse].x, screenover[nse].y, false);
 			screenover[nse].x = vpt.first.X;
 			screenover[nse].y = vpt.first.Y;
 		}
@@ -338,7 +338,7 @@ int _display_main(int xx, int yy, int wii, const char *text, int disp_type, int
 		GameLoopUntilNoOverlay();
 	}
 
-	play.messagetime = -1;
+	_GP(play).messagetime = -1;
 	return 0;
 }
 
@@ -352,7 +352,7 @@ void _display_at(int xx, int yy, int wii, const char *text, int disp_type, int a
 
 	EndSkippingUntilCharStops();
 
-	if (try_auto_play_speech(text, text, play.narrator_speech, true)) {
+	if (try_auto_play_speech(text, text, _GP(play).narrator_speech, true)) {
 		// TODO: is there any need for this flag?
 		need_stop_speech = true;
 	}
@@ -376,7 +376,7 @@ bool try_auto_play_speech(const char *text, const char *&replace_text, int chari
 	replace_text = src; // skip voice tag
 	if (play_voice_speech(charid, sndid)) {
 		// if Voice Only, then blank out the text
-		if (play.want_speech == 2)
+		if (_GP(play).want_speech == 2)
 			replace_text = "  ";
 		return true;
 	}
@@ -389,7 +389,7 @@ int source_text_length = -1;
 
 int GetTextDisplayLength(const char *text) {
 	int len = (int)strlen(text);
-	if ((text[0] == '&') && (play.unfactor_speech_from_textlength != 0)) {
+	if ((text[0] == '&') && (_GP(play).unfactor_speech_from_textlength != 0)) {
 		// if there's an "&12 text" type line, remove "&12 " from the source length
 		size_t j = 0;
 		while ((text[j] != ' ') && (text[j] != 0))
@@ -405,7 +405,7 @@ int GetTextDisplayTime(const char *text, int canberel) {
 	auto fpstimer = ::lround(get_current_fps());
 
 	// if it's background speech, make it stay relative to game speed
-	if ((canberel == 1) && (play.bgspeech_game_speed == 1))
+	if ((canberel == 1) && (_GP(play).bgspeech_game_speed == 1))
 		fpstimer = 40;
 
 	if (source_text_length >= 0) {
@@ -420,18 +420,18 @@ int GetTextDisplayTime(const char *text, int canberel) {
 	if (uselen <= 0)
 		return 0;
 
-	if (play.text_speed + play.text_speed_modifier <= 0)
+	if (_GP(play).text_speed + _GP(play).text_speed_modifier <= 0)
 		quit("!Text speed is zero; unable to display text. Check your _GP(game).text_speed settings.");
 
 	// Store how many game loops per character of text
 	// This is calculated using a hard-coded 15 for the text speed,
 	// so that it's always the same no matter how fast the user
 	// can read.
-	loops_per_character = (((uselen / play.lipsync_speed) + 1) * fpstimer) / uselen;
+	loops_per_character = (((uselen / _GP(play).lipsync_speed) + 1) * fpstimer) / uselen;
 
-	int textDisplayTimeInMS = ((uselen / (play.text_speed + play.text_speed_modifier)) + 1) * 1000;
-	if (textDisplayTimeInMS < play.text_min_display_time_ms)
-		textDisplayTimeInMS = play.text_min_display_time_ms;
+	int textDisplayTimeInMS = ((uselen / (_GP(play).text_speed + _GP(play).text_speed_modifier)) + 1) * 1000;
+	if (textDisplayTimeInMS < _GP(play).text_min_display_time_ms)
+		textDisplayTimeInMS = _GP(play).text_min_display_time_ms;
 
 	return (textDisplayTimeInMS * fpstimer) / 1000;
 }
@@ -536,7 +536,7 @@ void wouttextxy_AutoOutline(Bitmap *ds, size_t font, int32_t color, const char *
 void wouttext_outline(Shared::Bitmap *ds, int xxp, int yyp, int font, color_t text_color, const char *texx) {
 	size_t const text_font = static_cast<size_t>(font);
 	// Draw outline (a backdrop) if requested
-	color_t const outline_color = ds->GetCompatibleColor(play.speech_text_shadow);
+	color_t const outline_color = ds->GetCompatibleColor(_GP(play).speech_text_shadow);
 	int const outline_font = get_font_outline(font);
 	if (outline_font >= 0)
 		wouttextxy(ds, xxp, yyp, static_cast<size_t>(outline_font), outline_color, texx);
@@ -776,19 +776,19 @@ void draw_text_window_and_bar(Bitmap **text_window_ds, bool should_free_ds,
 		ds = *text_window_ds;
 
 		// draw the top bar
-		color_t draw_color = ds->GetCompatibleColor(play.top_bar_backcolor);
+		color_t draw_color = ds->GetCompatibleColor(_GP(play).top_bar_backcolor);
 		ds->FillRect(Rect(0, 0, ds->GetWidth() - 1, topBar.height - 1), draw_color);
-		if (play.top_bar_backcolor != play.top_bar_bordercolor) {
+		if (_GP(play).top_bar_backcolor != _GP(play).top_bar_bordercolor) {
 			// draw the border
-			draw_color = ds->GetCompatibleColor(play.top_bar_bordercolor);
-			for (int j = 0; j < data_to_game_coord(play.top_bar_borderwidth); j++)
+			draw_color = ds->GetCompatibleColor(_GP(play).top_bar_bordercolor);
+			for (int j = 0; j < data_to_game_coord(_GP(play).top_bar_borderwidth); j++)
 				ds->DrawRect(Rect(j, j, ds->GetWidth() - (j + 1), topBar.height - (j + 1)), draw_color);
 		}
 
 		// draw the text
 		int textx = (ds->GetWidth() / 2) - wgettextwidth_compensate(topBar.text, topBar.font) / 2;
-		color_t text_color = ds->GetCompatibleColor(play.top_bar_textcolor);
-		wouttext_outline(ds, textx, play.top_bar_borderwidth + get_fixed_pixel_size(1), topBar.font, text_color, topBar.text);
+		color_t text_color = ds->GetCompatibleColor(_GP(play).top_bar_textcolor);
+		wouttext_outline(ds, textx, _GP(play).top_bar_borderwidth + get_fixed_pixel_size(1), topBar.font, text_color, topBar.text);
 
 		// don't draw it next time
 		topBar.wantIt = 0;
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index ffe0071305..40efba0b77 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -90,7 +90,7 @@ extern "C" void ios_render();
 
 extern GameSetup usetup;
 
-extern GameState play;
+
 extern int convert_16bit_bgr;
 extern ScriptSystem scsystem;
 extern AGSPlatformDriver *platform;
@@ -501,8 +501,8 @@ void dispose_room_drawdata() {
 
 void on_mainviewport_changed() {
 	if (!gfxDriver->RequiresFullRedrawEachFrame()) {
-		init_invalid_regions(-1, play.GetMainViewport().GetSize(), RectWH(play.GetMainViewport().GetSize()));
-		if (_GP(game).GetGameRes().ExceedsByAny(play.GetMainViewport().GetSize()))
+		init_invalid_regions(-1, _GP(play).GetMainViewport().GetSize(), RectWH(_GP(play).GetMainViewport().GetSize()));
+		if (_GP(game).GetGameRes().ExceedsByAny(_GP(play).GetMainViewport().GetSize()))
 			clear_letterbox_borders();
 	}
 }
@@ -543,7 +543,7 @@ void prepare_roomview_frame(Viewport *view) {
 void sync_roomview(Viewport *view) {
 	if (view->GetCamera() == nullptr)
 		return;
-	init_invalid_regions(view->GetID(), view->GetCamera()->GetRect().GetSize(), play.GetRoomViewportAbs(view->GetID()));
+	init_invalid_regions(view->GetID(), view->GetCamera()->GetRect().GetSize(), _GP(play).GetRoomViewportAbs(view->GetID()));
 	prepare_roomview_frame(view);
 }
 
@@ -551,10 +551,10 @@ void init_room_drawdata() {
 	if (gfxDriver->RequiresFullRedrawEachFrame())
 		return;
 	// Make sure all frame buffers are created for software drawing
-	int view_count = play.GetRoomViewportCount();
+	int view_count = _GP(play).GetRoomViewportCount();
 	CameraDrawData.resize(view_count);
-	for (int i = 0; i < play.GetRoomViewportCount(); ++i)
-		sync_roomview(play.GetRoomViewport(i).get());
+	for (int i = 0; i < _GP(play).GetRoomViewportCount(); ++i)
+		sync_roomview(_GP(play).GetRoomViewport(i).get());
 }
 
 void on_roomviewport_created(int index) {
@@ -594,7 +594,7 @@ void detect_roomviewport_overlaps(size_t z_index) {
 	if (gfxDriver->RequiresFullRedrawEachFrame())
 		return;
 	// Find out if we overlap or are overlapped by anything;
-	const auto &viewports = play.GetRoomViewportsZOrdered();
+	const auto &viewports = _GP(play).GetRoomViewportsZOrdered();
 	for (; z_index < viewports.size(); ++z_index) {
 		auto this_view = viewports[z_index];
 		const int this_id = this_view->GetID();
@@ -672,7 +672,7 @@ void render_black_borders() {
 		return;
 	{
 		gfxDriver->BeginSpriteBatch(RectWH(_GP(game).GetGameRes()), SpriteTransform());
-		const Rect &viewport = play.GetMainViewport();
+		const Rect &viewport = _GP(play).GetMainViewport();
 		if (viewport.Top > 0) {
 			// letterbox borders
 			blankImage->SetStretch(_GP(game).GetGameRes().Width, viewport.Top, false);
@@ -692,7 +692,7 @@ void render_black_borders() {
 void render_to_screen() {
 	// Stage: final plugin callback (still drawn on game screen
 	if (pl_any_want_hook(AGSE_FINALSCREENDRAW)) {
-		gfxDriver->BeginSpriteBatch(play.GetMainViewport(), SpriteTransform(), Point(0, play.shake_screen_yoff), (GlobalFlipType)play.screen_flipped);
+		gfxDriver->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(), Point(0, _GP(play).shake_screen_yoff), (GlobalFlipType)_GP(play).screen_flipped);
 		gfxDriver->DrawSprite(AGSE_FINALSCREENDRAW, 0, nullptr);
 	}
 	// Stage: engine overlay
@@ -705,10 +705,10 @@ void render_to_screen() {
 	while (!succeeded) {
 //		try {
 			// For software renderer, need to blacken upper part of the game frame when shaking screen moves image down
-			const Rect &viewport = play.GetMainViewport();
-			if (play.shake_screen_yoff > 0 && !gfxDriver->RequiresFullRedrawEachFrame())
-				gfxDriver->ClearRectangle(viewport.Left, viewport.Top, viewport.GetWidth() - 1, play.shake_screen_yoff, nullptr);
-			gfxDriver->Render(0, play.shake_screen_yoff, (GlobalFlipType)play.screen_flipped);
+			const Rect &viewport = _GP(play).GetMainViewport();
+			if (_GP(play).shake_screen_yoff > 0 && !gfxDriver->RequiresFullRedrawEachFrame())
+				gfxDriver->ClearRectangle(viewport.Left, viewport.Top, viewport.GetWidth() - 1, _GP(play).shake_screen_yoff, nullptr);
+			gfxDriver->Render(0, _GP(play).shake_screen_yoff, (GlobalFlipType)_GP(play).screen_flipped);
 
 #if AGS_PLATFORM_OS_ANDROID
 			if (_GP(game).color_depth == 1)
@@ -727,7 +727,7 @@ void render_to_screen() {
 
 // Blanks out borders around main viewport in case it became smaller (e.g. after loading another room)
 void clear_letterbox_borders() {
-	const Rect &viewport = play.GetMainViewport();
+	const Rect &viewport = _GP(play).GetMainViewport();
 	gfxDriver->ClearRectangle(0, 0, _GP(game).GetGameRes().Width - 1, viewport.Top - 1, nullptr);
 	gfxDriver->ClearRectangle(0, viewport.Bottom + 1, _GP(game).GetGameRes().Width - 1, _GP(game).GetGameRes().Height - 1, nullptr);
 }
@@ -913,10 +913,10 @@ void sort_out_char_sprite_walk_behind(int actspsIndex, int xx, int yy, int basel
 	        (actspswbcache[actspsIndex].xWas != xx) ||
 	        (actspswbcache[actspsIndex].yWas != yy) ||
 	        (actspswbcache[actspsIndex].baselineWas != basel)) {
-		actspswb[actspsIndex] = recycle_bitmap(actspswb[actspsIndex], thisroom.BgFrames[play.bg_frame].Graphic->GetColorDepth(), width, height, true);
+		actspswb[actspsIndex] = recycle_bitmap(actspswb[actspsIndex], thisroom.BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth(), width, height, true);
 		Bitmap *wbSprite = actspswb[actspsIndex];
 
-		actspswbcache[actspsIndex].isWalkBehindHere = sort_out_walk_behinds(wbSprite, xx, yy, basel, thisroom.BgFrames[play.bg_frame].Graphic.get(), actsps[actspsIndex], zoom);
+		actspswbcache[actspsIndex].isWalkBehindHere = sort_out_walk_behinds(wbSprite, xx, yy, basel, thisroom.BgFrames[_GP(play).bg_frame].Graphic.get(), actsps[actspsIndex], zoom);
 		actspswbcache[actspsIndex].xWas = xx;
 		actspswbcache[actspsIndex].yWas = yy;
 		actspswbcache[actspsIndex].baselineWas = basel;
@@ -1094,7 +1094,7 @@ void get_local_tint(int xpp, int ypp, int nolight,
 
 		int onRegion = 0;
 
-		if ((play.ground_level_areas_disabled & GLED_EFFECTS) == 0) {
+		if ((_GP(play).ground_level_areas_disabled & GLED_EFFECTS) == 0) {
 			// check if the player is on a region, to find its
 			// light/tint level
 			onRegion = GetRegionIDAtRoom(xpp, ypp);
@@ -1131,18 +1131,18 @@ void get_local_tint(int xpp, int ypp, int nolight,
 			tint_light = light_level;
 		}
 
-		if (play.rtint_enabled) {
-			if (play.rtint_level > 0) {
+		if (_GP(play).rtint_enabled) {
+			if (_GP(play).rtint_level > 0) {
 				// override with room tint
-				tint_red = play.rtint_red;
-				tint_green = play.rtint_green;
-				tint_blue = play.rtint_blue;
-				tint_amount = play.rtint_level;
-				tint_light = play.rtint_light;
+				tint_red = _GP(play).rtint_red;
+				tint_green = _GP(play).rtint_green;
+				tint_blue = _GP(play).rtint_blue;
+				tint_amount = _GP(play).rtint_level;
+				tint_light = _GP(play).rtint_light;
 			} else {
 				// override with room light level
 				tint_amount = 0;
-				light_level = play.rtint_light;
+				light_level = _GP(play).rtint_light;
 			}
 		}
 	}
@@ -1835,13 +1835,13 @@ void prepare_room_sprites() {
 	// Note that software DDB is just a tiny wrapper around bitmap, so overhead is negligible.
 	if (roomBackgroundBmp == nullptr) {
 		update_polled_stuff_if_runtime();
-		roomBackgroundBmp = gfxDriver->CreateDDBFromBitmap(thisroom.BgFrames[play.bg_frame].Graphic.get(), false, true);
+		roomBackgroundBmp = gfxDriver->CreateDDBFromBitmap(thisroom.BgFrames[_GP(play).bg_frame].Graphic.get(), false, true);
 	} else if (current_background_is_dirty) {
 		update_polled_stuff_if_runtime();
-		gfxDriver->UpdateDDBFromBitmap(roomBackgroundBmp, thisroom.BgFrames[play.bg_frame].Graphic.get(), false);
+		gfxDriver->UpdateDDBFromBitmap(roomBackgroundBmp, thisroom.BgFrames[_GP(play).bg_frame].Graphic.get(), false);
 	}
 	if (gfxDriver->RequiresFullRedrawEachFrame()) {
-		if (current_background_is_dirty || walkBehindsCachedForBgNum != play.bg_frame) {
+		if (current_background_is_dirty || walkBehindsCachedForBgNum != _GP(play).bg_frame) {
 			if (walkBehindMethod == DrawAsSeparateSprite) {
 				update_walk_behind_images();
 			}
@@ -1905,7 +1905,7 @@ PBitmap draw_room_background(Viewport *view, const SpriteTransform &room_trans)
 		// the following line takes up to 50% of the game CPU time at
 		// high resolutions and colour depths - if we can optimise it
 		// somehow, significant performance gains to be had
-		update_room_invreg_and_reset(view_index, roomcam_surface, thisroom.BgFrames[play.bg_frame].Graphic.get(), draw_to_camsurf);
+		update_room_invreg_and_reset(view_index, roomcam_surface, thisroom.BgFrames[_GP(play).bg_frame].Graphic.get(), draw_to_camsurf);
 	}
 
 	return CameraDrawData[view_index].Frame;
@@ -2020,7 +2020,7 @@ void draw_gui_and_overlays() {
 		our_eip = 38;
 		// Draw the GUIs
 		for (int gg = 0; gg < _GP(game).numgui; gg++) {
-			aa = play.gui_draw_order[gg];
+			aa = _GP(play).gui_draw_order[gg];
 			if (!guis[aa].IsDisplayed()) continue;
 
 			// Don't draw GUI if "GUIs Turn Off When Disabled"
@@ -2102,13 +2102,13 @@ static void construct_room_view() {
 	// reset the Baselines Changed flag now that we've drawn stuff
 	walk_behind_baselines_changed = 0;
 
-	for (const auto &viewport : play.GetRoomViewportsZOrdered()) {
+	for (const auto &viewport : _GP(play).GetRoomViewportsZOrdered()) {
 		if (!viewport->IsVisible())
 			continue;
 		auto camera = viewport->GetCamera();
 		if (!camera)
 			continue;
-		const Rect &view_rc = play.GetRoomViewportAbs(viewport->GetID());
+		const Rect &view_rc = _GP(play).GetRoomViewportAbs(viewport->GetID());
 		const Rect &cam_rc = camera->GetRect();
 		SpriteTransform room_trans(-cam_rc.Left, -cam_rc.Top,
 		                           (float)view_rc.GetWidth() / (float)cam_rc.GetWidth(),
@@ -2116,7 +2116,7 @@ static void construct_room_view() {
 		                           0.f);
 		if (gfxDriver->RequiresFullRedrawEachFrame()) {
 			// we draw everything as a sprite stack
-			gfxDriver->BeginSpriteBatch(view_rc, room_trans, Point(0, play.shake_screen_yoff), (GlobalFlipType)play.screen_flipped);
+			gfxDriver->BeginSpriteBatch(view_rc, room_trans, Point(0, _GP(play).shake_screen_yoff), (GlobalFlipType)_GP(play).screen_flipped);
 		} else {
 			if (CameraDrawData[viewport->GetID()].Frame == nullptr && CameraDrawData[viewport->GetID()].IsOverlap) {
 				// room background is prepended to the sprite stack
@@ -2144,7 +2144,7 @@ static void construct_room_view() {
 
 // Schedule ui rendering
 static void construct_ui_view() {
-	gfxDriver->BeginSpriteBatch(play.GetUIViewportAbs(), SpriteTransform(), Point(0, play.shake_screen_yoff), (GlobalFlipType)play.screen_flipped);
+	gfxDriver->BeginSpriteBatch(_GP(play).GetUIViewportAbs(), SpriteTransform(), Point(0, _GP(play).shake_screen_yoff), (GlobalFlipType)_GP(play).screen_flipped);
 	draw_gui_and_overlays();
 	put_sprite_list_on_screen(false);
 	clear_draw_list();
@@ -2153,13 +2153,13 @@ static void construct_ui_view() {
 void construct_game_scene(bool full_redraw) {
 	gfxDriver->ClearDrawLists();
 
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return;
 
 	our_eip = 3;
 
 	// React to changes to viewports and cameras (possibly from script) just before the render
-	play.UpdateViewports();
+	_GP(play).UpdateViewports();
 
 	gfxDriver->UseSmoothScaling(IS_ANTIALIAS_SPRITES);
 	gfxDriver->RenderSpritesAtScreenResolution(usetup.RenderAtScreenRes, usetup.Supersampling);
@@ -2167,16 +2167,16 @@ void construct_game_scene(bool full_redraw) {
 	pl_run_plugin_hooks(AGSE_PRERENDER, 0);
 
 	// Possible reasons to invalidate whole screen for the software renderer
-	if (full_redraw || play.screen_tint > 0 || play.shakesc_length > 0)
+	if (full_redraw || _GP(play).screen_tint > 0 || _GP(play).shakesc_length > 0)
 		invalidate_screen();
 
 	// TODO: move to game update! don't call update during rendering pass!
 	// IMPORTANT: keep the order same because sometimes script may depend on it
 	if (displayed_room >= 0)
-		play.UpdateRoomCameras();
+		_GP(play).UpdateRoomCameras();
 
 	// Stage: room viewports
-	if (play.screen_is_faded_out == 0 && is_complete_overlay == 0) {
+	if (_GP(play).screen_is_faded_out == 0 && is_complete_overlay == 0) {
 		if (displayed_room >= 0) {
 			construct_room_view();
 		} else if (!gfxDriver->RequiresFullRedrawEachFrame()) {
@@ -2189,13 +2189,13 @@ void construct_game_scene(bool full_redraw) {
 	our_eip = 4;
 
 	// Stage: UI overlay
-	if (play.screen_is_faded_out == 0) {
+	if (_GP(play).screen_is_faded_out == 0) {
 		construct_ui_view();
 	}
 }
 
 void construct_game_screen_overlay(bool draw_mouse) {
-	gfxDriver->BeginSpriteBatch(play.GetMainViewport(), SpriteTransform(), Point(0, play.shake_screen_yoff), (GlobalFlipType)play.screen_flipped);
+	gfxDriver->BeginSpriteBatch(_GP(play).GetMainViewport(), SpriteTransform(), Point(0, _GP(play).shake_screen_yoff), (GlobalFlipType)_GP(play).screen_flipped);
 	if (pl_any_want_hook(AGSE_POSTSCREENDRAW))
 		gfxDriver->DrawSprite(AGSE_POSTSCREENDRAW, 0, nullptr);
 
@@ -2234,23 +2234,23 @@ void construct_game_screen_overlay(bool draw_mouse) {
 	ags_domouse(DOMOUSE_NOCURSOR);
 
 	// Stage: mouse cursor
-	if (draw_mouse && !play.mouse_cursor_hidden && play.screen_is_faded_out == 0) {
+	if (draw_mouse && !_GP(play).mouse_cursor_hidden && _GP(play).screen_is_faded_out == 0) {
 		gfxDriver->DrawSprite(_G(mousex) - _G(hotx), _G(mousey) - _G(hoty), mouseCursor);
 		invalidate_sprite(_G(mousex) - _G(hotx), _G(mousey) - _G(hoty), mouseCursor, false);
 	}
 
-	if (play.screen_is_faded_out == 0) {
+	if (_GP(play).screen_is_faded_out == 0) {
 		// Stage: screen fx
-		if (play.screen_tint >= 1)
-			gfxDriver->SetScreenTint(play.screen_tint & 0xff, (play.screen_tint >> 8) & 0xff, (play.screen_tint >> 16) & 0xff);
+		if (_GP(play).screen_tint >= 1)
+			gfxDriver->SetScreenTint(_GP(play).screen_tint & 0xff, (_GP(play).screen_tint >> 8) & 0xff, (_GP(play).screen_tint >> 16) & 0xff);
 		// Stage: legacy letterbox mode borders
 		render_black_borders();
 	}
 
-	if (play.screen_is_faded_out != 0 && gfxDriver->RequiresFullRedrawEachFrame()) {
-		const Rect &main_viewport = play.GetMainViewport();
+	if (_GP(play).screen_is_faded_out != 0 && gfxDriver->RequiresFullRedrawEachFrame()) {
+		const Rect &main_viewport = _GP(play).GetMainViewport();
 		gfxDriver->BeginSpriteBatch(main_viewport, SpriteTransform());
-		gfxDriver->SetScreenFade(play.fade_to_red, play.fade_to_green, play.fade_to_blue);
+		gfxDriver->SetScreenFade(_GP(play).fade_to_red, _GP(play).fade_to_green, _GP(play).fade_to_blue);
 	}
 }
 
@@ -2259,7 +2259,7 @@ void construct_engine_overlay() {
 	gfxDriver->BeginSpriteBatch(viewport, SpriteTransform());
 
 	// draw the debug console, if appropriate
-	if ((play.debug_mode > 0) && (display_console != 0)) {
+	if ((_GP(play).debug_mode > 0) && (display_console != 0)) {
 		const int font = FONT_NORMAL;
 		int ypp = 1;
 		int txtspacing = getfontspacing_outlined(font);
@@ -2293,17 +2293,17 @@ void construct_engine_overlay() {
 
 static void update_shakescreen() {
 	// TODO: unify blocking and non-blocking shake update
-	play.shake_screen_yoff = 0;
-	if (play.shakesc_length > 0) {
-		if ((loopcounter % play.shakesc_delay) < (play.shakesc_delay / 2))
-			play.shake_screen_yoff = play.shakesc_amount;
+	_GP(play).shake_screen_yoff = 0;
+	if (_GP(play).shakesc_length > 0) {
+		if ((loopcounter % _GP(play).shakesc_delay) < (_GP(play).shakesc_delay / 2))
+			_GP(play).shake_screen_yoff = _GP(play).shakesc_amount;
 	}
 }
 
 // Draw everything
 void render_graphics(IDriverDependantBitmap *extraBitmap, int extraX, int extraY) {
 	// Don't render if skipping cutscene
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return;
 	// Don't render if we've just entered new room and are before fade-in
 	// TODO: find out why this is not skipped for 8-bit games
@@ -2323,7 +2323,7 @@ void render_graphics(IDriverDependantBitmap *extraBitmap, int extraX, int extraY
 	construct_game_screen_overlay(true);
 	render_to_screen();
 
-	if (!play.screen_is_faded_out) {
+	if (!_GP(play).screen_is_faded_out) {
 		// always update the palette, regardless of whether the plugin
 		// vetos the screen update
 		if (bg_just_changed) {
diff --git a/engines/ags/engine/ac/draw.h b/engines/ags/engine/ac/draw.h
index 481a502700..7c70aa9447 100644
--- a/engines/ags/engine/ac/draw.h
+++ b/engines/ags/engine/ac/draw.h
@@ -44,7 +44,7 @@ class IDriverDependantBitmap;
 
 using namespace AGS; // FIXME later
 
-#define IS_ANTIALIAS_SPRITES usetup.enable_antialiasing && (play.disable_antialiasing == 0)
+#define IS_ANTIALIAS_SPRITES usetup.enable_antialiasing && (_GP(play).disable_antialiasing == 0)
 
 // [IKM] WARNING: these definitions has to be made AFTER Allegro headers
 // were included, because they override few Allegro function names;
diff --git a/engines/ags/engine/ac/drawingsurface.cpp b/engines/ags/engine/ac/drawingsurface.cpp
index 3385d1a135..7357583743 100644
--- a/engines/ags/engine/ac/drawingsurface.cpp
+++ b/engines/ags/engine/ac/drawingsurface.cpp
@@ -53,7 +53,7 @@ using namespace AGS::Shared;
 using namespace AGS::Engine;
 
 
-extern GameState play;
+
 extern RoomStatus *croom;
 extern RoomObject *objs;
 extern CharacterCache *charcache;
@@ -66,11 +66,11 @@ extern Bitmap *dynamicallyCreatedSurfaces[MAX_DYNAMIC_SURFACES];
 void DrawingSurface_Release(ScriptDrawingSurface *sds) {
 	if (sds->roomBackgroundNumber >= 0) {
 		if (sds->modified) {
-			if (sds->roomBackgroundNumber == play.bg_frame) {
+			if (sds->roomBackgroundNumber == _GP(play).bg_frame) {
 				invalidate_screen();
 				mark_current_background_dirty();
 			}
-			play.raw_modified[sds->roomBackgroundNumber] = 1;
+			_GP(play).raw_modified[sds->roomBackgroundNumber] = 1;
 		}
 
 		sds->roomBackgroundNumber = -1;
@@ -342,7 +342,7 @@ void DrawingSurface_DrawString(ScriptDrawingSurface *sds, int xx, int yy, int fo
 	Bitmap *ds = sds->StartDrawing();
 	// don't use wtextcolor because it will do a 16->32 conversion
 	color_t text_color = sds->currentColour;
-	if ((ds->GetColorDepth() <= 8) && (play.raw_color > 255)) {
+	if ((ds->GetColorDepth() <= 8) && (_GP(play).raw_color > 255)) {
 		text_color = ds->GetCompatibleColor(1);
 		debug_script_warn("RawPrint: Attempted to use hi-color on 256-col background");
 	}
diff --git a/engines/ags/engine/ac/dynamicsprite.cpp b/engines/ags/engine/ac/dynamicsprite.cpp
index 859e3a5bfa..dc53b51714 100644
--- a/engines/ags/engine/ac/dynamicsprite.cpp
+++ b/engines/ags/engine/ac/dynamicsprite.cpp
@@ -310,7 +310,7 @@ ScriptDynamicSprite *DynamicSprite_CreateFromScreenShot(int width, int height) {
 	if (gotSlot <= 0)
 		return nullptr;
 
-	const Rect &viewport = play.GetMainViewport();
+	const Rect &viewport = _GP(play).GetMainViewport();
 	if (width <= 0)
 		width = viewport.GetWidth();
 	else
@@ -408,17 +408,17 @@ ScriptDynamicSprite *DynamicSprite_CreateFromExistingSprite_Old(int slot) {
 ScriptDynamicSprite *DynamicSprite_CreateFromBackground(int frame, int x1, int y1, int width, int height) {
 
 	if (frame == SCR_NO_VALUE) {
-		frame = play.bg_frame;
+		frame = _GP(play).bg_frame;
 	} else if ((frame < 0) || ((size_t)frame >= thisroom.BgFrameCount))
 		quit("!DynamicSprite.CreateFromBackground: invalid frame specified");
 
 	if (x1 == SCR_NO_VALUE) {
 		x1 = 0;
 		y1 = 0;
-		width = play.room_width;
-		height = play.room_height;
+		width = _GP(play).room_width;
+		height = _GP(play).room_height;
 	} else if ((x1 < 0) || (y1 < 0) || (width < 1) || (height < 1) ||
-		(x1 + width > play.room_width) || (y1 + height > play.room_height))
+		(x1 + width > _GP(play).room_width) || (y1 + height > _GP(play).room_height))
 		quit("!DynamicSprite.CreateFromBackground: invalid co-ordinates specified");
 
 	data_to_game_coords(&x1, &y1);
diff --git a/engines/ags/engine/ac/dynobj/scriptcamera.cpp b/engines/ags/engine/ac/dynobj/scriptcamera.cpp
index be9f7bacd6..ecfe1ae980 100644
--- a/engines/ags/engine/ac/dynobj/scriptcamera.cpp
+++ b/engines/ags/engine/ac/dynobj/scriptcamera.cpp
@@ -23,6 +23,7 @@
 #include "ags/engine/ac/dynobj/scriptcamera.h"
 #include "ags/engine/ac/gamestate.h"
 #include "ags/shared/util/bbop.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -61,7 +62,7 @@ ScriptCamera *Camera_Unserialize(int handle, const char *serializedData, int dat
 	// script references when Camera gets removed.
 	const int id = BBOp::Int32FromLE(*((const int *)serializedData));
 	if (id >= 0) {
-		auto scam = play.RegisterRoomCamera(id, handle);
+		auto scam = _GP(play).RegisterRoomCamera(id, handle);
 		if (scam)
 			return scam;
 	}
diff --git a/engines/ags/engine/ac/dynobj/scriptdrawingsurface.cpp b/engines/ags/engine/ac/dynobj/scriptdrawingsurface.cpp
index c3f68fdf29..6c52347753 100644
--- a/engines/ags/engine/ac/dynobj/scriptdrawingsurface.cpp
+++ b/engines/ags/engine/ac/dynobj/scriptdrawingsurface.cpp
@@ -38,7 +38,7 @@ using namespace AGS::Shared;
 extern RoomStruct thisroom;
 
 extern Bitmap *dynamicallyCreatedSurfaces[MAX_DYNAMIC_SURFACES];
-extern GameState play;
+
 
 Bitmap *ScriptDrawingSurface::GetBitmapSurface() {
 	// TODO: consider creating weak_ptr here, and store one in the DrawingSurface!
@@ -119,7 +119,7 @@ ScriptDrawingSurface::ScriptDrawingSurface() {
 	dynamicSurfaceNumber = -1;
 	isLinkedBitmapOnly = false;
 	linkedBitmapOnly = nullptr;
-	currentColour = play.raw_color;
+	currentColour = _GP(play).raw_color;
 	currentColourScript = 0;
 	modified = 0;
 	hasAlphaChannel = 0;
diff --git a/engines/ags/engine/ac/dynobj/scriptviewport.cpp b/engines/ags/engine/ac/dynobj/scriptviewport.cpp
index 99c3eae0e3..f1802a7042 100644
--- a/engines/ags/engine/ac/dynobj/scriptviewport.cpp
+++ b/engines/ags/engine/ac/dynobj/scriptviewport.cpp
@@ -23,6 +23,7 @@
 #include "ags/engine/ac/dynobj/scriptviewport.h"
 #include "ags/engine/ac/gamestate.h"
 #include "ags/shared/util/bbop.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -61,7 +62,7 @@ ScriptViewport *Viewport_Unserialize(int handle, const char *serializedData, int
 	// script references when Viewport gets removed.
 	const int id = BBOp::Int32FromLE(*((const int *)serializedData));
 	if (id >= 0) {
-		auto scview = play.RegisterRoomViewport(id, handle);
+		auto scview = _GP(play).RegisterRoomViewport(id, handle);
 		if (scview)
 			return scview;
 	}
diff --git a/engines/ags/engine/ac/event.cpp b/engines/ags/engine/ac/event.cpp
index aefcc02683..b6f43b71a7 100644
--- a/engines/ags/engine/ac/event.cpp
+++ b/engines/ags/engine/ac/event.cpp
@@ -52,7 +52,7 @@ using namespace AGS::Engine;
 extern RoomStruct thisroom;
 extern RoomStatus *croom;
 extern int displayed_room;
-extern GameState play;
+
 extern color palette[256];
 extern IGraphicsDriver *gfxDriver;
 extern AGSPlatformDriver *platform;
@@ -211,24 +211,24 @@ void process_event(EventHappened *evp) {
 	} else if (evp->type == EV_FADEIN) {
 		// if they change the transition type before the fadein, make
 		// sure the screen doesn't freeze up
-		play.screen_is_faded_out = 0;
+		_GP(play).screen_is_faded_out = 0;
 
 		// determine the transition style
-		int theTransition = play.fade_effect;
+		int theTransition = _GP(play).fade_effect;
 
-		if (play.next_screen_transition >= 0) {
+		if (_GP(play).next_screen_transition >= 0) {
 			// a one-off transition was selected, so use it
-			theTransition = play.next_screen_transition;
-			play.next_screen_transition = -1;
+			theTransition = _GP(play).next_screen_transition;
+			_GP(play).next_screen_transition = -1;
 		}
 
 		if (pl_run_plugin_hooks(AGSE_TRANSITIONIN, 0))
 			return;
 
-		if (play.fast_forward)
+		if (_GP(play).fast_forward)
 			return;
 
-		const bool ignore_transition = (play.screen_tint > 0);
+		const bool ignore_transition = (_GP(play).screen_tint > 0);
 		if (((theTransition == FADE_CROSSFADE) || (theTransition == FADE_DISSOLVE)) &&
 			(saved_viewport_bitmap == nullptr) && !ignore_transition) {
 			// transition type was not crossfade/dissolve when the screen faded out,
@@ -240,7 +240,7 @@ void process_event(EventHappened *evp) {
 
 		// TODO: use normal coordinates instead of "native_size" and multiply_up_*?
 		//const Size &data_res = _GP(game).GetDataRes();
-		const Rect &viewport = play.GetMainViewport();
+		const Rect &viewport = _GP(play).GetMainViewport();
 
 		if ((theTransition == FADE_INSTANT) || ignore_transition)
 			set_palette_range(palette, 0, 255, 0);
@@ -281,7 +281,7 @@ void process_event(EventHappened *evp) {
 				}
 				gfxDriver->SetMemoryBackBuffer(saved_backbuf);
 			}
-			play.screen_is_faded_out = 0;
+			_GP(play).screen_is_faded_out = 0;
 		} else if (theTransition == FADE_CROSSFADE) {
 			if (_GP(game).color_depth == 1)
 				quit("!Cannot use crossfade screen transition in 256-colour games");
@@ -375,7 +375,7 @@ void processallevents(int numev, EventHappened *evlist) {
 	EventHappened copyOfList[MAXEVENTS];
 	memcpy(&copyOfList[0], &evlist[0], sizeof(EventHappened) * numev);
 
-	int room_was = play.room_changes;
+	int room_was = _GP(play).room_changes;
 
 	inside_processevent++;
 
@@ -383,7 +383,7 @@ void processallevents(int numev, EventHappened *evlist) {
 
 		process_event(&copyOfList[dd]);
 
-		if (room_was != play.room_changes)
+		if (room_was != _GP(play).room_changes)
 			break;  // changed room, so discard other events
 	}
 
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 300d26923f..490ad6e18e 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -149,7 +149,7 @@ extern Bitmap *dynamicallyCreatedSurfaces[MAX_DYNAMIC_SURFACES];
 extern IGraphicsDriver *gfxDriver;
 
 //=============================================================================
-GameState play;
+
 GameSetup usetup;
 RoomStatus troom;    // used for non-saveable rooms, eg. intro
 RoomObject *objs;
@@ -244,7 +244,7 @@ int Game_IsAudioPlaying(int audioType) {
 	if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
 		quitprintf("!_GP(game).IsAudioPlaying: invalid audio type %d", audioType);
 
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return 0;
 
 	for (int aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
@@ -289,7 +289,7 @@ void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
 
 	if ((changeType == VOL_SETFUTUREDEFAULT) ||
 	        (changeType == VOL_BOTH)) {
-		play.default_audio_type_volumes[audioType] = volume;
+		_GP(play).default_audio_type_volumes[audioType] = volume;
 
 		// update queued clip volumes
 		update_queued_clips_volume(audioType, volume);
@@ -314,7 +314,7 @@ int Game_GetDialogCount() {
 }
 
 void set_debug_mode(bool on) {
-	play.debug_mode = on ? 1 : 0;
+	_GP(play).debug_mode = on ? 1 : 0;
 	debug_set_console(on);
 }
 
@@ -329,16 +329,16 @@ extern int acdialog_font;
 
 int oldmouse;
 void setup_for_dialog() {
-	cbuttfont = play.normal_font;
-	acdialog_font = play.normal_font;
-	if (!play.mouse_cursor_hidden)
+	cbuttfont = _GP(play).normal_font;
+	acdialog_font = _GP(play).normal_font;
+	if (!_GP(play).mouse_cursor_hidden)
 		ags_domouse(DOMOUSE_ENABLE);
 	oldmouse = cur_cursor;
 	set_mouse_cursor(CURS_ARROW);
 }
 void restore_after_dialog() {
 	set_mouse_cursor(oldmouse);
-	if (!play.mouse_cursor_hidden)
+	if (!_GP(play).mouse_cursor_hidden)
 		ags_domouse(DOMOUSE_DISABLE);
 	invalidate_screen();
 }
@@ -518,7 +518,7 @@ void save_game_dialog() {
 }
 
 void free_do_once_tokens() {
-	play.do_once_tokens.resize(0);
+	_GP(play).do_once_tokens.resize(0);
 }
 
 
@@ -527,7 +527,7 @@ void free_do_once_tokens() {
 void unload_game_file() {
 	close_translation();
 
-	play.FreeViewportsAndCameras();
+	_GP(play).FreeViewportsAndCameras();
 
 	characterScriptObjNames.clear();
 	free(charextra);
@@ -620,7 +620,7 @@ void unload_game_file() {
 	free_all_fonts();
 
 	free_do_once_tokens();
-	free(play.gui_draw_order);
+	free(_GP(play).gui_draw_order);
 
 	resetRoomStatuses();
 
@@ -637,7 +637,7 @@ const char *Game_GetGlobalStrings(int index) {
 	if ((index < 0) || (index >= MAXGLOBALSTRINGS))
 		quit("!_GP(game).GlobalStrings: invalid index");
 
-	return CreateNewScriptString(play.globalstrings[index]);
+	return CreateNewScriptString(_GP(play).globalstrings[index]);
 }
 
 
@@ -735,40 +735,40 @@ ScriptViewFrame *Game_GetViewFrame(int viewNumber, int loopNumber, int frame) {
 }
 
 int Game_DoOnceOnly(const char *token) {
-	for (int i = 0; i < (int)play.do_once_tokens.size(); i++) {
-		if (play.do_once_tokens[i] == token) {
+	for (int i = 0; i < (int)_GP(play).do_once_tokens.size(); i++) {
+		if (_GP(play).do_once_tokens[i] == token) {
 			return 0;
 		}
 	}
-	play.do_once_tokens.push_back(token);
+	_GP(play).do_once_tokens.push_back(token);
 	return 1;
 }
 
 int Game_GetTextReadingSpeed() {
-	return play.text_speed;
+	return _GP(play).text_speed;
 }
 
 void Game_SetTextReadingSpeed(int newTextSpeed) {
 	if (newTextSpeed < 1)
 		quitprintf("!_GP(game).TextReadingSpeed: %d is an invalid speed", newTextSpeed);
 
-	play.text_speed = newTextSpeed;
+	_GP(play).text_speed = newTextSpeed;
 }
 
 int Game_GetMinimumTextDisplayTimeMs() {
-	return play.text_min_display_time_ms;
+	return _GP(play).text_min_display_time_ms;
 }
 
 void Game_SetMinimumTextDisplayTimeMs(int newTextMinTime) {
-	play.text_min_display_time_ms = newTextMinTime;
+	_GP(play).text_min_display_time_ms = newTextMinTime;
 }
 
 int Game_GetIgnoreUserInputAfterTextTimeoutMs() {
-	return play.ignore_user_input_after_text_timeout_ms;
+	return _GP(play).ignore_user_input_after_text_timeout_ms;
 }
 
 void Game_SetIgnoreUserInputAfterTextTimeoutMs(int newValueMs) {
-	play.ignore_user_input_after_text_timeout_ms = newValueMs;
+	_GP(play).ignore_user_input_after_text_timeout_ms = newValueMs;
 }
 
 const char *Game_GetFileName() {
@@ -776,24 +776,24 @@ const char *Game_GetFileName() {
 }
 
 const char *Game_GetName() {
-	return CreateNewScriptString(play.game_name);
+	return CreateNewScriptString(_GP(play).game_name);
 }
 
 void Game_SetName(const char *newName) {
-	strncpy(play.game_name, newName, 99);
-	play.game_name[99] = 0;
-	::AGS::g_vm->set_window_title(play.game_name);
+	strncpy(_GP(play).game_name, newName, 99);
+	_GP(play).game_name[99] = 0;
+	::AGS::g_vm->set_window_title(_GP(play).game_name);
 }
 
 int Game_GetSkippingCutscene() {
-	if (play.fast_forward) {
+	if (_GP(play).fast_forward) {
 		return 1;
 	}
 	return 0;
 }
 
 int Game_GetInSkippableCutscene() {
-	if (play.in_cutscene) {
+	if (_GP(play).in_cutscene) {
 		return 1;
 	}
 	return 0;
@@ -837,10 +837,10 @@ const char *Game_GetGlobalMessages(int index) {
 }
 
 int Game_GetSpeechFont() {
-	return play.speech_font;
+	return _GP(play).speech_font;
 }
 int Game_GetNormalFont() {
-	return play.normal_font;
+	return _GP(play).normal_font;
 }
 
 const char *Game_GetTranslationFilename() {
@@ -876,15 +876,15 @@ ScriptAudioClip *Game_GetAudioClip(int index) {
 }
 
 ScriptCamera *Game_GetCamera() {
-	return play.GetScriptCamera(0);
+	return _GP(play).GetScriptCamera(0);
 }
 
 int Game_GetCameraCount() {
-	return play.GetRoomCameraCount();
+	return _GP(play).GetRoomCameraCount();
 }
 
 ScriptCamera *Game_GetAnyCamera(int index) {
-	return play.GetScriptCamera(index);
+	return _GP(play).GetScriptCamera(index);
 }
 
 void Game_SimulateKeyPress(int key_) {
@@ -1024,15 +1024,15 @@ void create_savegame_screenshot(Bitmap *&screenShot) {
 		debug_flags |= DBG_NOIFACE;
 		construct_game_scene(true);
 
-		int usewid = data_to_game_coord(play.screenshot_width);
-		int usehit = data_to_game_coord(play.screenshot_height);
-		const Rect &viewport = play.GetMainViewport();
+		int usewid = data_to_game_coord(_GP(play).screenshot_width);
+		int usehit = data_to_game_coord(_GP(play).screenshot_height);
+		const Rect &viewport = _GP(play).GetMainViewport();
 		if (usewid > viewport.GetWidth())
 			usewid = viewport.GetWidth();
 		if (usehit > viewport.GetHeight())
 			usehit = viewport.GetHeight();
 
-		if ((play.screenshot_width < 16) || (play.screenshot_height < 16))
+		if ((_GP(play).screenshot_width < 16) || (_GP(play).screenshot_height < 16))
 			quit("!Invalid _GP(game).screenshot_width/height, must be from 16x16 to screen res");
 
 		screenShot = CopyScreenIntoBitmap(usewid, usehit);
@@ -1172,31 +1172,31 @@ void restore_game_room_state(Stream *in) {
 
 void ReadGameState_Aligned(Stream *in, RestoredData &r_data) {
 	AlignedStream align_s(in, Shared::kAligned_Read);
-	play.ReadFromSavegame(&align_s, kGSSvgVersion_OldFormat, r_data);
+	_GP(play).ReadFromSavegame(&align_s, kGSSvgVersion_OldFormat, r_data);
 }
 
 void restore_game_play_ex_data(Stream *in) {
 	char rbuffer[200];
-	for (size_t i = 0; i < play.do_once_tokens.size(); ++i) {
+	for (size_t i = 0; i < _GP(play).do_once_tokens.size(); ++i) {
 		StrUtil::ReadCStr(rbuffer, in, sizeof(rbuffer));
-		play.do_once_tokens[i] = rbuffer;
+		_GP(play).do_once_tokens[i] = rbuffer;
 	}
 
-	in->ReadArrayOfInt32(&play.gui_draw_order[0], _GP(game).numgui);
+	in->ReadArrayOfInt32(&_GP(play).gui_draw_order[0], _GP(game).numgui);
 }
 
 void restore_game_play(Stream *in, RestoredData &r_data) {
-	int screenfadedout_was = play.screen_is_faded_out;
-	int roomchanges_was = play.room_changes;
+	int screenfadedout_was = _GP(play).screen_is_faded_out;
+	int roomchanges_was = _GP(play).room_changes;
 	// make sure the pointer is preserved
-	int32_t *gui_draw_order_was = play.gui_draw_order;
+	int32_t *gui_draw_order_was = _GP(play).gui_draw_order;
 
 	ReadGameState_Aligned(in, r_data);
 	r_data.Cameras[0].Flags = r_data.Camera0_Flags;
 
-	play.screen_is_faded_out = screenfadedout_was;
-	play.room_changes = roomchanges_was;
-	play.gui_draw_order = gui_draw_order_was;
+	_GP(play).screen_is_faded_out = screenfadedout_was;
+	_GP(play).room_changes = roomchanges_was;
+	_GP(play).gui_draw_order = gui_draw_order_was;
 
 	restore_game_play_ex_data(in);
 }
@@ -1335,7 +1335,7 @@ void restore_game_displayed_room_status(Stream *in, RestoredData &r_data) {
 
 		for (bb = 0; bb < MAX_ROOM_BGFRAMES; bb++) {
 			r_data.RoomBkgScene[bb] = nullptr;
-			if (play.raw_modified[bb]) {
+			if (_GP(play).raw_modified[bb]) {
 				r_data.RoomBkgScene[bb].reset(read_serialized_bitmap(in));
 			}
 		}
@@ -1470,7 +1470,7 @@ HSaveError restore_game_data(Stream *in, SavegameVersion svg_version, const Pres
 	_GP(game).ReadFromSaveGame_v321(in, gswas, compsc, chwas, olddict, mesbk);
 
 	// Modified custom properties are read separately to keep existing save format
-	play.ReadCustomProperties_v340(in);
+	_GP(play).ReadCustomProperties_v340(in);
 
 	ReadCharacterExtras_Aligned(in);
 	restore_game_palette(in);
@@ -1630,15 +1630,15 @@ bool try_restore_save(int slot) {
 }
 
 bool is_in_cutscene() {
-	return play.in_cutscene > 0;
+	return _GP(play).in_cutscene > 0;
 }
 
 CutsceneSkipStyle get_cutscene_skipstyle() {
-	return static_cast<CutsceneSkipStyle>(play.in_cutscene);
+	return static_cast<CutsceneSkipStyle>(_GP(play).in_cutscene);
 }
 
 void start_skipping_cutscene() {
-	play.fast_forward = 1;
+	_GP(play).fast_forward = 1;
 	// if a drop-down icon bar is up, remove it as it will pause the game
 	if (ifacepopped >= 0)
 		remove_popup_interface(ifacepopped);
@@ -1673,15 +1673,15 @@ bool check_skip_cutscene_mclick(int mbut) {
 // Helper functions used by StartCutscene/EndCutscene, but also
 // by SkipUntilCharacterStops
 void initialize_skippable_cutscene() {
-	play.end_cutscene_music = -1;
+	_GP(play).end_cutscene_music = -1;
 }
 
 void stop_fast_forwarding() {
 	// when the skipping of a cutscene comes to an end, update things
-	play.fast_forward = 0;
+	_GP(play).fast_forward = 0;
 	setpal();
-	if (play.end_cutscene_music >= 0)
-		newmusic(play.end_cutscene_music);
+	if (_GP(play).end_cutscene_music >= 0)
+		newmusic(_GP(play).end_cutscene_music);
 
 	{
 		AudioChannelsLock lock;
@@ -1710,7 +1710,7 @@ int __GetLocationType(int xxx, int yyy, int allowHotspot0) {
 
 	const int scrx = xxx;
 	const int scry = yyy;
-	VpPoint vpt = play.ScreenToRoomDivDown(xxx, yyy);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(xxx, yyy);
 	if (vpt.second < 0)
 		return 0;
 	xxx = vpt.first.X;
@@ -2315,12 +2315,12 @@ void RegisterGameAPI() {
 }
 
 void RegisterStaticObjects() {
-	ccAddExternalStaticObject("game", &play, &GameStaticManager);
-	ccAddExternalStaticObject("gs_globals", &play.globalvars[0], &GlobalStaticManager);
+	ccAddExternalStaticObject("game", &_GP(play), &GameStaticManager);
+	ccAddExternalStaticObject("gs_globals", &_GP(play).globalvars[0], &GlobalStaticManager);
 	ccAddExternalStaticObject("mouse", &scmouse, &GlobalStaticManager);
 	ccAddExternalStaticObject("palette", &palette[0], &GlobalStaticManager);
 	ccAddExternalStaticObject("system", &scsystem, &GlobalStaticManager);
-	ccAddExternalStaticObject("savegameindex", &play.filenumbers[0], &GlobalStaticManager);
+	ccAddExternalStaticObject("savegameindex", &_GP(play).filenumbers[0], &GlobalStaticManager);
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/gamestate.cpp b/engines/ags/engine/ac/gamestate.cpp
index 455b515fc9..4483c8447c 100644
--- a/engines/ags/engine/ac/gamestate.cpp
+++ b/engines/ags/engine/ac/gamestate.cpp
@@ -50,8 +50,22 @@ extern CharacterInfo *playerchar;
 extern ScriptSystem scsystem;
 
 GameState::GameState() {
-	_isAutoRoomViewport = true;
-	_mainViewportHasChanged = false;
+	Common::fill(&globalvars[0], &globalvars[MAXGLOBALVARS], 0);
+	Common::fill(&reserved[0], &reserved[GAME_STATE_RESERVED_INTS], 0);
+	Common::fill(&globalscriptvars[0], &globalscriptvars[MAXGSVALUES], 0);
+	Common::fill(&walkable_areas_on[0], &walkable_areas_on[MAX_WALK_AREAS + 1], 0);
+	Common::fill(&script_timers[0], &script_timers[MAX_TIMERS], 0);
+	Common::fill(&parsed_words[0], &parsed_words[MAX_PARSED_WORDS], 0);
+	Common::fill(&bad_parsed_word[0], &bad_parsed_word[100], 0);
+	Common::fill(&raw_modified[0], &raw_modified[MAX_ROOM_BGFRAMES], 0);
+	Common::fill(&filenumbers[0], &filenumbers[MAXSAVEGAMES], 0);
+	Common::fill(&music_queue[0], &music_queue[MAX_QUEUED_MUSIC], 0);
+	Common::fill(&takeover_from[0], &takeover_from[50], 0);
+	Common::fill(&playmp3file_name[0], &playmp3file_name[PLAYMP3FILE_MAX_FILENAME_LEN], 0);
+	Common::fill(&globalstrings[0][0], &globalstrings[MAXGLOBALSTRINGS - 1][MAX_MAXSTRLEN], 0);
+	Common::fill(&lastParserEntry[0], &lastParserEntry[MAX_MAXSTRLEN], 0);
+	Common::fill(&game_name[0], &game_name[100], 0);
+	Common::fill(&default_audio_type_volumes[0], &default_audio_type_volumes[MAX_AUDIO_TYPES], 0);
 }
 
 void GameState::Free() {
@@ -216,12 +230,12 @@ VpPoint GameState::ScreenToRoomDivDown(int scrx, int scry) {
 
 void GameState::CreatePrimaryViewportAndCamera() {
 	if (_roomViewports.size() == 0) {
-		play.CreateRoomViewport();
-		play.RegisterRoomViewport(0);
+		_GP(play).CreateRoomViewport();
+		_GP(play).RegisterRoomViewport(0);
 	}
 	if (_roomCameras.size() == 0) {
-		play.CreateRoomCamera();
-		play.RegisterRoomCamera(0);
+		_GP(play).CreateRoomCamera();
+		_GP(play).RegisterRoomCamera(0);
 	}
 	_roomViewports[0]->LinkCamera(_roomCameras[0]);
 	_roomCameras[0]->LinkToViewport(_roomViewports[0]);
@@ -368,8 +382,8 @@ bool GameState::IsNonBlockingVoiceSpeech() const {
 }
 
 bool GameState::ShouldPlayVoiceSpeech() const {
-	return !play.fast_forward &&
-		(play.want_speech >= 1) && (!ResPaths.SpeechPak.Name.IsEmpty());
+	return !_GP(play).fast_forward &&
+		(_GP(play).want_speech >= 1) && (!ResPaths.SpeechPak.Name.IsEmpty());
 }
 
 void GameState::ReadFromSavegame(Shared::Stream *in, GameStateSvgVersion svg_ver, RestoredData &r_data) {
diff --git a/engines/ags/engine/ac/gamestate.h b/engines/ags/engine/ac/gamestate.h
index e3bce8a22b..7e48b1314e 100644
--- a/engines/ags/engine/ac/gamestate.h
+++ b/engines/ags/engine/ac/gamestate.h
@@ -68,164 +68,164 @@ enum GameStateSvgVersion {
 
 // Adding to this might need to modify AGSDEFNS.SH and AGSPLUGIN.H
 struct GameState {
-	int  score;      // player's current score
-	int  usedmode;   // set by ProcessClick to last cursor mode used
-	int  disabled_user_interface;  // >0 while in cutscene/etc
-	int  gscript_timer;    // obsolete
-	int  debug_mode;       // whether we're in debug mode
+	int  score = 0;      // player's current score
+	int  usedmode = 0;   // set by ProcessClick to last cursor mode used
+	int  disabled_user_interface = 0;  // >0 while in cutscene/etc
+	int  gscript_timer = 0;    // obsolete
+	int  debug_mode = 0;       // whether we're in debug mode
 	int32_t globalvars[MAXGLOBALVARS];  // obsolete
-	int  messagetime;      // time left for auto-remove messages
-	int  usedinv;          // inventory item last used
-	int  inv_top, inv_numdisp, obsolete_inv_numorder, inv_numinline;
-	int  text_speed;       // how quickly text is removed
-	int  sierra_inv_color; // background used to paint defualt inv window
-	int  talkanim_speed;   // animation speed of talking anims
-	int  inv_item_wid, inv_item_hit; // set by SetInvDimensions
-	int  speech_text_shadow;         // colour of outline fonts (default black)
-	int  swap_portrait_side;         // sierra-style speech swap sides
-	int  speech_textwindow_gui;      // textwindow used for sierra-style speech
-	int  follow_change_room_timer;   // delay before moving following characters into new room
-	int  totalscore;           // maximum possible score
-	int  skip_display;         // how the user can skip normal Display windows
-	int  no_multiloop_repeat;  // for backwards compatibility
-	int  roomscript_finished;  // on_call finished in room
-	int  used_inv_on;          // inv item they clicked on
-	int  no_textbg_when_voice; // no textwindow bgrnd when voice speech is used
-	int  max_dialogoption_width; // max width of dialog options text window
-	int  no_hicolor_fadein;      // fade out but instant in for hi-color
-	int  bgspeech_game_speed;    // is background speech relative to game speed
-	int  bgspeech_stay_on_display; // whether to remove bg speech when DisplaySpeech is used
-	int  unfactor_speech_from_textlength; // remove "&10" when calculating time for text to stay
-	int  mp3_loop_before_end;    // (UNUSED!) loop this time before end of track (ms)
-	int  speech_music_drop;      // how much to drop music volume by when speech is played
-	int  in_cutscene;            // we are between a StartCutscene and EndCutscene
-	int  fast_forward;           // player has elected to skip cutscene
-	int  room_width;      // width of current room
-	int  room_height;     // height of current room
+	int  messagetime = 0;      // time left for auto-remove messages
+	int  usedinv = 0;          // inventory item last used
+	int  inv_top = 0, inv_numdisp = 0, obsolete_inv_numorder = 0, inv_numinline = 0;
+	int  text_speed = 0;       // how quickly text is removed
+	int  sierra_inv_color = 0; // background used to paint defualt inv window
+	int  talkanim_speed = 0;   // animation speed of talking anims
+	int  inv_item_wid = 0, inv_item_hit = 0; // set by SetInvDimensions
+	int  speech_text_shadow = 0;         // colour of outline fonts (default black)
+	int  swap_portrait_side = 0;         // sierra-style speech swap sides
+	int  speech_textwindow_gui = 0;      // textwindow used for sierra-style speech
+	int  follow_change_room_timer = 0;   // delay before moving following characters into new room
+	int  totalscore = 0;           // maximum possible score
+	int  skip_display = 0;         // how the user can skip normal Display windows
+	int  no_multiloop_repeat = 0;  // for backwards compatibility
+	int  roomscript_finished = 0;  // on_call finished in room
+	int  used_inv_on = 0;          // inv item they clicked on
+	int  no_textbg_when_voice = 0; // no textwindow bgrnd when voice speech is used
+	int  max_dialogoption_width = 0; // max width of dialog options text window
+	int  no_hicolor_fadein = 0;      // fade out but instant in for hi-color
+	int  bgspeech_game_speed = 0;    // is background speech relative to game speed
+	int  bgspeech_stay_on_display = 0; // whether to remove bg speech when DisplaySpeech is used
+	int  unfactor_speech_from_textlength = 0; // remove "&10" when calculating time for text to stay
+	int  mp3_loop_before_end = 0;    // (UNUSED!) loop this time before end of track (ms)
+	int  speech_music_drop = 0;      // how much to drop music volume by when speech is played
+	int  in_cutscene = 0;            // we are between a StartCutscene and EndCutscene
+	int  fast_forward = 0;           // player has elected to skip cutscene
+	int  room_width = 0;      // width of current room
+	int  room_height = 0;     // height of current room
 	// ** up to here is referenced in the plugin interface
-	int  game_speed_modifier;
-	int  score_sound;
-	int  takeover_data;  // value passed to RunAGSGame in previous game
-	int  replay_hotkey_unused;  // (UNUSED!) StartRecording: not supported
-	int  dialog_options_x;
-	int  dialog_options_y;
-	int  narrator_speech;
-	int  ambient_sounds_persist;
-	int  lipsync_speed;
-	int  close_mouth_speech_time; // stop speech animation at (messagetime - close_mouth_speech_time)
+	int  game_speed_modifier = 0;
+	int  score_sound = 0;
+	int  takeover_data = 0;  // value passed to RunAGSGame in previous game
+	int  replay_hotkey_unused = 0;  // (UNUSED!) StartRecording: not supported
+	int  dialog_options_x = 0;
+	int  dialog_options_y = 0;
+	int  narrator_speech = 0;
+	int  ambient_sounds_persist = 0;
+	int  lipsync_speed = 0;
+	int  close_mouth_speech_time = 0; // stop speech animation at (messagetime - close_mouth_speech_time)
 	// (this is designed to work in text-only mode)
-	int  disable_antialiasing;
-	int  text_speed_modifier;
-	HorAlignment text_align;
-	int  speech_bubble_width;
-	int  min_dialogoption_width;
-	int  disable_dialog_parser;
-	int  anim_background_speed;  // the setting for this room
-	int  top_bar_backcolor;
-	int  top_bar_textcolor;
-	int  top_bar_bordercolor;
-	int  top_bar_borderwidth;
-	int  top_bar_ypos;
-	int  screenshot_width;
-	int  screenshot_height;
-	int  top_bar_font;
-	HorAlignment speech_text_align;
-	int  auto_use_walkto_points;
-	int  inventory_greys_out;
-	int  skip_speech_specific_key;
-	int  abort_key;
-	int  fade_to_red;
-	int  fade_to_green;
-	int  fade_to_blue;
-	int  show_single_dialog_option;
-	int  keep_screen_during_instant_transition;
-	int  read_dialog_option_colour;
-	int  stop_dialog_at_end;
-	int  speech_portrait_placement; // speech portrait placement mode (automatic/custom)
-	int  speech_portrait_x; // a speech portrait x offset from corresponding screen side
-	int  speech_portrait_y; // a speech portrait y offset
-	int  speech_display_post_time_ms; // keep speech text/portrait on screen after text/voice has finished playing;
+	int  disable_antialiasing = 0;
+	int  text_speed_modifier = 0;
+	HorAlignment text_align = kHAlignNone;
+	int  speech_bubble_width = 0;
+	int  min_dialogoption_width = 0;
+	int  disable_dialog_parser = 0;
+	int  anim_background_speed = 0;  // the setting for this room
+	int  top_bar_backcolor = 0;
+	int  top_bar_textcolor = 0;
+	int  top_bar_bordercolor = 0;
+	int  top_bar_borderwidth = 0;
+	int  top_bar_ypos = 0;
+	int  screenshot_width = 0;
+	int  screenshot_height = 0;
+	int  top_bar_font = 0;
+	HorAlignment speech_text_align = kHAlignNone;
+	int  auto_use_walkto_points = 0;
+	int  inventory_greys_out = 0;
+	int  skip_speech_specific_key = 0;
+	int  abort_key = 0;
+	int  fade_to_red = 0;
+	int  fade_to_green = 0;
+	int  fade_to_blue = 0;
+	int  show_single_dialog_option = 0;
+	int  keep_screen_during_instant_transition = 0;
+	int  read_dialog_option_colour = 0;
+	int  stop_dialog_at_end = 0;
+	int  speech_portrait_placement = 0; // speech portrait placement mode (automatic/custom)
+	int  speech_portrait_x = 0; // a speech portrait x offset from corresponding screen side
+	int  speech_portrait_y = 0; // a speech portrait y offset
+	int  speech_display_post_time_ms = 0; // keep speech text/portrait on screen after text/voice has finished playing = 0;
 	// no speech animation is supposed to be played at this time
-	int  dialog_options_highlight_color; // The colour used for highlighted (hovered over) text in dialog options
+	int  dialog_options_highlight_color = 0; // The colour used for highlighted (hovered over) text in dialog options
 	int32_t reserved[GAME_STATE_RESERVED_INTS];  // make sure if a future version adds a var, it doesn't mess anything up
 	// ** up to here is referenced in the script "_GP(game)." object
-	long  randseed;    // random seed
-	int   player_on_region;    // player's current region
-	int   screen_is_faded_out; // the screen is currently black
-	int   check_interaction_only;
-	int   bg_frame, bg_anim_delay; // for animating backgrounds
-	int   music_vol_was;  // before the volume drop
-	short wait_counter;
-	char  wait_skipped_by; // tells how last wait was skipped [not serialized]
-	int   wait_skipped_by_data; // extended data telling how last wait was skipped [not serialized]
-	short mboundx1, mboundx2, mboundy1, mboundy2;
-	int   fade_effect;
-	int   bg_frame_locked;
+	long  randseed = 0;    // random seed
+	int   player_on_region = 0;    // player's current region
+	int   screen_is_faded_out = 0; // the screen is currently black
+	int   check_interaction_only = 0;
+	int   bg_frame = 0, bg_anim_delay = 0; // for animating backgrounds
+	int   music_vol_was = 0;  // before the volume drop
+	short wait_counter = 0;
+	char  wait_skipped_by = 0; // tells how last wait was skipped [not serialized]
+	int   wait_skipped_by_data = 0; // extended data telling how last wait was skipped [not serialized]
+	short mboundx1 = 0, mboundx2 = 0, mboundy1 = 0, mboundy2 = 0;
+	int   fade_effect = 0;
+	int   bg_frame_locked = 0;
 	int32_t globalscriptvars[MAXGSVALUES];
-	int   cur_music_number, music_repeat;
-	int   music_master_volume;
-	int   digital_master_volume;
+	int   cur_music_number = 0, music_repeat = 0;
+	int   music_master_volume = 0;
+	int   digital_master_volume = 0;
 	char  walkable_areas_on[MAX_WALK_AREAS + 1];
-	short screen_flipped;
-	int   entered_at_x, entered_at_y, entered_edge;
-	int   want_speech;
-	int   cant_skip_speech;
+	short screen_flipped = 0;
+	int   entered_at_x = 0, entered_at_y = 0, entered_edge = 0;
+	int   want_speech = 0;
+	int   cant_skip_speech = 0;
 	int32_t script_timers[MAX_TIMERS];
-	int   sound_volume, speech_volume;
-	int   normal_font, speech_font;
-	char  key_skip_wait;
-	int   swap_portrait_lastchar;
-	int   swap_portrait_lastlastchar;
-	int   separate_music_lib;
-	int   in_conversation;
-	int   screen_tint;
-	int   num_parsed_words;
+	int   sound_volume = 0, speech_volume = 0;
+	int   normal_font = 0, speech_font = 0;
+	char  key_skip_wait = 0;
+	int   swap_portrait_lastchar = 0;
+	int   swap_portrait_lastlastchar = 0;
+	int   separate_music_lib = 0;
+	int   in_conversation = 0;
+	int   screen_tint = 0;
+	int   num_parsed_words = 0;
 	short parsed_words[MAX_PARSED_WORDS];
 	char  bad_parsed_word[100];
-	int   raw_color;
+	int   raw_color = 0;
 	int32_t raw_modified[MAX_ROOM_BGFRAMES];
-	Shared::PBitmap raw_drawing_surface;
+	Shared::PBitmap raw_drawing_surface = nullptr;
 	short filenumbers[MAXSAVEGAMES];
-	int   room_changes;
-	int   mouse_cursor_hidden;
-	int   silent_midi;
-	int   silent_midi_channel;
-	int   current_music_repeating;  // remember what the loop flag was when this music started
-	unsigned long shakesc_delay;  // unsigned long to match loopcounter
-	int   shakesc_amount, shakesc_length;
-	int   rtint_red, rtint_green, rtint_blue, rtint_level, rtint_light;
-	bool  rtint_enabled;
-	int   end_cutscene_music;
-	int   skip_until_char_stops;
-	int   get_loc_name_last_time;
-	int   get_loc_name_save_cursor;
-	int   restore_cursor_mode_to;
-	int   restore_cursor_image_to;
-	short music_queue_size;
+	int   room_changes = 0;
+	int   mouse_cursor_hidden = 0;
+	int   silent_midi = 0;
+	int   silent_midi_channel = 0;
+	int   current_music_repeating = 0;  // remember what the loop flag was when this music started
+	unsigned long shakesc_delay = 0;  // unsigned long to match loopcounter
+	int   shakesc_amount = 0, shakesc_length = 0;
+	int   rtint_red = 0, rtint_green = 0, rtint_blue = 0, rtint_level = 0, rtint_light = 0;
+	bool  rtint_enabled = 0;
+	int   end_cutscene_music = 0;
+	int   skip_until_char_stops = 0;
+	int   get_loc_name_last_time = 0;
+	int   get_loc_name_save_cursor = 0;
+	int   restore_cursor_mode_to = 0;
+	int   restore_cursor_image_to = 0;
+	short music_queue_size = 0;
 	short music_queue[MAX_QUEUED_MUSIC];
-	short new_music_queue_size;
-	short crossfading_out_channel;
-	short crossfade_step;
-	short crossfade_out_volume_per_step;
-	short crossfade_initial_volume_out;
-	short crossfading_in_channel;
-	short crossfade_in_volume_per_step;
-	short crossfade_final_volume_in;
+	short new_music_queue_size = 0;
+	short crossfading_out_channel = 0;
+	short crossfade_step = 0;
+	short crossfade_out_volume_per_step = 0;
+	short crossfade_initial_volume_out = 0;
+	short crossfading_in_channel = 0;
+	short crossfade_in_volume_per_step = 0;
+	short crossfade_final_volume_in = 0;
 	QueuedAudioItem new_music_queue[MAX_QUEUED_MUSIC];
 	char  takeover_from[50];
 	char  playmp3file_name[PLAYMP3FILE_MAX_FILENAME_LEN];
 	char  globalstrings[MAXGLOBALSTRINGS][MAX_MAXSTRLEN];
 	char  lastParserEntry[MAX_MAXSTRLEN];
 	char  game_name[100];
-	int   ground_level_areas_disabled;
-	int   next_screen_transition;
-	int   gamma_adjustment;
-	short temporarily_turned_off_character;  // Hide Player Charactr ticked
-	short inv_backwards_compatibility;
-	int32_t *gui_draw_order;
+	int   ground_level_areas_disabled = 0;
+	int   next_screen_transition = 0;
+	int   gamma_adjustment = 0;
+	short temporarily_turned_off_character = 0;  // Hide Player Charactr ticked
+	short inv_backwards_compatibility = 0;
+	int32_t *gui_draw_order = nullptr;
 	std::vector<AGS::Shared::String> do_once_tokens;
-	int   text_min_display_time_ms;
-	int   ignore_user_input_after_text_timeout_ms;
+	int   text_min_display_time_ms = 0;
+	int   ignore_user_input_after_text_timeout_ms = 0;
 	int32_t   default_audio_type_volumes[MAX_AUDIO_TYPES];
 
 	// Dynamic custom property values for characters and items
@@ -235,16 +235,16 @@ struct GameState {
 	// Dynamic speech state
 	//
 	// Tells whether there is a voice-over played during current speech
-	bool  speech_has_voice;
-	// Tells whether the voice was played in blocking mode;
+	bool  speech_has_voice = 0;
+	// Tells whether the voice was played in blocking mode = 0;
 	// atm blocking speech handles itself, and we only need to finalize
-	// non-blocking voice speech during game update; speech refactor would be
+	// non-blocking voice speech during game update = 0; speech refactor would be
 	// required to get rid of this rule.
-	bool  speech_voice_blocking;
+	bool  speech_voice_blocking = 0;
 	// Tells whether character speech stays on screen not animated for additional time
-	bool  speech_in_post_state;
+	bool  speech_in_post_state = 0;
 
-	int shake_screen_yoff; // y offset of the shaking screen
+	int shake_screen_yoff = 0; // y offset of the shaking screen
 
 
 	GameState();
@@ -366,7 +366,7 @@ private:
 	void UpdateRoomCamera(int index);
 
 	// Defines if the room viewport should be adjusted to the room size automatically.
-	bool _isAutoRoomViewport;
+	bool _isAutoRoomViewport = true;
 	// Main viewport defines the rectangle of the drawn and interactable area
 	// in the most basic case it will be equal to the game size.
 	Viewport _mainViewport;
@@ -386,11 +386,11 @@ private:
 	std::vector<std::pair<ScriptCamera *, int32_t>> _scCameraRefs;
 
 	// Tells that the main viewport's position has changed since last game update
-	bool  _mainViewportHasChanged;
+	bool  _mainViewportHasChanged = false;
 	// Tells that room viewports need z-order resort
-	bool  _roomViewportZOrderChanged;
+	bool  _roomViewportZOrderChanged = false;
 
-	AGS_Clock::time_point _ignoreUserInputUntilTime;
+	AGS_Clock::time_point _ignoreUserInputUntilTime = 0;
 };
 
 // Converts legacy alignment type used in script API
@@ -400,7 +400,7 @@ HorAlignment ConvertLegacyScriptAlignment(LegacyScriptAlignment align);
 // Alignment constants in the Script API and still support old version.
 HorAlignment ReadScriptAlignment(int32_t align);
 
-extern GameState play;
+
 
 } // namespace AGS3
 
diff --git a/engines/ags/engine/ac/global_audio.cpp b/engines/ags/engine/ac/global_audio.cpp
index a1c97b28c0..d359ca43a5 100644
--- a/engines/ags/engine/ac/global_audio.cpp
+++ b/engines/ags/engine/ac/global_audio.cpp
@@ -42,7 +42,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 
 extern GameSetup usetup;
-extern GameState play;
+
 
 extern RoomStruct thisroom;
 extern SpeechLipSyncLine *splipsync;
@@ -101,7 +101,7 @@ void PlayAmbientSound(int channel, int sndnum, int vol, int x, int y) {
 }
 
 int IsChannelPlaying(int chan) {
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return 0;
 
 	if ((chan < 0) || (chan >= MAX_SOUND_CHANNELS))
@@ -114,7 +114,7 @@ int IsChannelPlaying(int chan) {
 }
 
 int IsSoundPlaying() {
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return 0;
 
 	// find if there's a sound playing
@@ -148,7 +148,7 @@ int PlaySoundEx(int val1, int channel) {
 		return -1;
 	}
 	// if skipping a cutscene, don't try and play the sound
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return -1;
 
 	// free the old sound
@@ -163,7 +163,7 @@ int PlaySoundEx(int val1, int channel) {
 	}
 
 	soundfx->_priority = 10;
-	soundfx->set_volume(play.sound_volume);
+	soundfx->set_volume(_GP(play).sound_volume);
 	set_clip_to_channel(channel, soundfx);
 	return channel;
 }
@@ -177,12 +177,12 @@ void StopAllSounds(int evenAmbient) {
 }
 
 void PlayMusicResetQueue(int newmus) {
-	play.music_queue_size = 0;
+	_GP(play).music_queue_size = 0;
 	newmusic(newmus);
 }
 
 void SeekMIDIPosition(int position) {
-	if (play.silent_midi == 0 && current_music_type != MUS_MIDI)
+	if (_GP(play).silent_midi == 0 && current_music_type != MUS_MIDI)
 		return;
 
 	AudioChannelsLock lock;
@@ -192,9 +192,9 @@ void SeekMIDIPosition(int position) {
 }
 
 int GetMIDIPosition() {
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return 99999;
-	if (play.silent_midi == 0 && current_music_type != MUS_MIDI)
+	if (_GP(play).silent_midi == 0 && current_music_type != MUS_MIDI)
 		return -1; // returns -1 on failure according to old manuals
 
 	AudioChannelsLock lock;
@@ -208,7 +208,7 @@ int GetMIDIPosition() {
 
 int IsMusicPlaying() {
 	// in case they have a "while (IsMusicPlaying())" loop
-	if ((play.fast_forward) && (play.skip_until_char_stops < 0))
+	if ((_GP(play).fast_forward) && (_GP(play).skip_until_char_stops < 0))
 		return 0;
 
 	// This only returns positive if there was a music started by old audio API
@@ -231,46 +231,46 @@ int PlayMusicQueued(int musnum) {
 
 	// Just get the queue size
 	if (musnum < 0)
-		return play.music_queue_size;
+		return _GP(play).music_queue_size;
 
-	if ((IsMusicPlaying() == 0) && (play.music_queue_size == 0)) {
+	if ((IsMusicPlaying() == 0) && (_GP(play).music_queue_size == 0)) {
 		newmusic(musnum);
 		return 0;
 	}
 
-	if (play.music_queue_size >= MAX_QUEUED_MUSIC) {
+	if (_GP(play).music_queue_size >= MAX_QUEUED_MUSIC) {
 		debug_script_log("Too many queued music, cannot add %d", musnum);
 		return 0;
 	}
 
-	if ((play.music_queue_size > 0) &&
-		(play.music_queue[play.music_queue_size - 1] >= QUEUED_MUSIC_REPEAT)) {
+	if ((_GP(play).music_queue_size > 0) &&
+		(_GP(play).music_queue[_GP(play).music_queue_size - 1] >= QUEUED_MUSIC_REPEAT)) {
 		debug_script_warn("PlayMusicQueued: cannot queue music after a repeating tune has been queued");
 		return 0;
 	}
 
-	if (play.music_repeat) {
+	if (_GP(play).music_repeat) {
 		debug_script_log("Queuing music %d to loop", musnum);
 		musnum += QUEUED_MUSIC_REPEAT;
 	} else {
 		debug_script_log("Queuing music %d", musnum);
 	}
 
-	play.music_queue[play.music_queue_size] = musnum;
-	play.music_queue_size++;
+	_GP(play).music_queue[_GP(play).music_queue_size] = musnum;
+	_GP(play).music_queue_size++;
 
-	if (play.music_queue_size == 1) {
+	if (_GP(play).music_queue_size == 1) {
 
 		clear_music_cache();
 
-		cachedQueuedMusic = load_music_from_disk(musnum, (play.music_repeat > 0));
+		cachedQueuedMusic = load_music_from_disk(musnum, (_GP(play).music_repeat > 0));
 	}
 
-	return play.music_queue_size;
+	return _GP(play).music_queue_size;
 }
 
 void scr_StopMusic() {
-	play.music_queue_size = 0;
+	_GP(play).music_queue_size = 0;
 	stopmusic();
 }
 
@@ -301,7 +301,7 @@ void SeekMP3PosMillis(int posn) {
 
 int GetMP3PosMillis() {
 	// in case they have "while (GetMP3PosMillis() < 5000) "
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return 999999;
 	if (current_music_type != MUS_MP3 && current_music_type != MUS_OGG)
 		return 0;  // returns 0 on failure according to old manuals
@@ -331,14 +331,14 @@ void SetMusicMasterVolume(int newvol) {
 		-LegacyMusicMasterVolumeAdjustment - (kRoomVolumeMax * LegacyRoomVolumeFactor);
 	if ((newvol < min_volume) | (newvol > 100))
 		quitprintf("!SetMusicMasterVolume: invalid volume - must be from %d to %d", min_volume, 100);
-	play.music_master_volume = newvol + LegacyMusicMasterVolumeAdjustment;
+	_GP(play).music_master_volume = newvol + LegacyMusicMasterVolumeAdjustment;
 	update_music_volume();
 }
 
 void SetSoundVolume(int newvol) {
 	if ((newvol < 0) | (newvol > 255))
 		quit("!SetSoundVolume: invalid volume - must be from 0-255");
-	play.sound_volume = newvol;
+	_GP(play).sound_volume = newvol;
 	Game_SetAudioTypeVolume(AUDIOTYPE_LEGACY_AMBIENT_SOUND, (newvol * 100) / 255, VOL_BOTH);
 	Game_SetAudioTypeVolume(AUDIOTYPE_LEGACY_SOUND, (newvol * 100) / 255, VOL_BOTH);
 	update_ambient_sound_vol();
@@ -365,7 +365,7 @@ void SetChannelVolume(int chan, int newvol) {
 void SetDigitalMasterVolume(int newvol) {
 	if ((newvol < 0) | (newvol > 100))
 		quit("!SetDigitalMasterVolume: invalid volume - must be from 0-100");
-	play.digital_master_volume = newvol;
+	_GP(play).digital_master_volume = newvol;
 #if !AGS_PLATFORM_SCUMMVM
 	auto newvol_f = static_cast<float>(newvol) / 100.0;
 	audio_core_set_master_volume(newvol_f);
@@ -373,11 +373,11 @@ void SetDigitalMasterVolume(int newvol) {
 }
 
 int GetCurrentMusic() {
-	return play.cur_music_number;
+	return _GP(play).cur_music_number;
 }
 
 void SetMusicRepeat(int loopflag) {
-	play.music_repeat = loopflag;
+	_GP(play).music_repeat = loopflag;
 }
 
 void PlayMP3File(const char *filename) {
@@ -389,7 +389,7 @@ void PlayMP3File(const char *filename) {
 	AssetPath asset_name("", filename);
 
 	int useChan = prepare_for_new_music();
-	bool doLoop = (play.music_repeat > 0);
+	bool doLoop = (_GP(play).music_repeat > 0);
 
 	SOUNDCLIP *clip = nullptr;
 
@@ -399,10 +399,10 @@ void PlayMP3File(const char *filename) {
 			if (clip->play()) {
 				set_clip_to_channel(useChan, clip);
 				current_music_type = MUS_OGG;
-				play.cur_music_number = 1000;
+				_GP(play).cur_music_number = 1000;
 				// save the filename (if it's not what we were supplied with)
-				if (filename != &play.playmp3file_name[0])
-					strcpy(play.playmp3file_name, filename);
+				if (filename != &_GP(play).playmp3file_name[0])
+					strcpy(_GP(play).playmp3file_name, filename);
 			} else {
 				clip->destroy();
 				delete clip;
@@ -417,10 +417,10 @@ void PlayMP3File(const char *filename) {
 			if (clip->play()) {
 				set_clip_to_channel(useChan, clip);
 				current_music_type = MUS_MP3;
-				play.cur_music_number = 1000;
+				_GP(play).cur_music_number = 1000;
 				// save the filename (if it's not what we were supplied with)
-				if (filename != &play.playmp3file_name[0])
-					strcpy(play.playmp3file_name, filename);
+				if (filename != &_GP(play).playmp3file_name[0])
+					strcpy(_GP(play).playmp3file_name, filename);
 			} else {
 				clip->destroy();
 				delete clip;
@@ -443,12 +443,12 @@ void PlaySilentMIDI(int mnum) {
 	if (current_music_type == MUS_MIDI)
 		quit("!PlaySilentMIDI: proper midi music is in progress");
 
-	play.silent_midi = mnum;
-	play.silent_midi_channel = SCHAN_SPEECH;
-	stop_and_destroy_channel(play.silent_midi_channel);
+	_GP(play).silent_midi = mnum;
+	_GP(play).silent_midi_channel = SCHAN_SPEECH;
+	stop_and_destroy_channel(_GP(play).silent_midi_channel);
 	// No idea why it uses speech voice channel, but since it does (and until this is changed)
 	// we have to correctly reset speech voice in case there was a nonblocking speech
-	if (play.IsNonBlockingVoiceSpeech())
+	if (_GP(play).IsNonBlockingVoiceSpeech())
 		stop_voice_nonblocking();
 
 	SOUNDCLIP *clip = load_sound_clip_from_old_style_number(true, mnum, false);
@@ -456,7 +456,7 @@ void PlaySilentMIDI(int mnum) {
 		quitprintf("!PlaySilentMIDI: failed to load aMusic%d", mnum);
 	}
 	AudioChannelsLock lock;
-	lock.SetChannel(play.silent_midi_channel, clip);
+	lock.SetChannel(_GP(play).silent_midi_channel, clip);
 	if (!clip->play()) {
 		clip->destroy();
 		delete clip;
@@ -474,7 +474,7 @@ void SetSpeechVolume(int newvol) {
 	auto *ch = lock.GetChannel(SCHAN_SPEECH);
 	if (ch)
 		ch->set_volume(newvol);
-	play.speech_volume = newvol;
+	_GP(play).speech_volume = newvol;
 }
 
 // 0 = text only
@@ -485,24 +485,24 @@ void SetVoiceMode(int newmod) {
 		quit("!SetVoiceMode: invalid mode number (must be 0,1,2)");
 	// If speech is turned off, store the mode anyway in case the
 	// user adds the VOX file later
-	if (play.want_speech < 0)
-		play.want_speech = (-newmod) - 1;
+	if (_GP(play).want_speech < 0)
+		_GP(play).want_speech = (-newmod) - 1;
 	else
-		play.want_speech = newmod;
+		_GP(play).want_speech = newmod;
 }
 
 int GetVoiceMode() {
-	return play.want_speech >= 0 ? play.want_speech : -(play.want_speech + 1);
+	return _GP(play).want_speech >= 0 ? _GP(play).want_speech : -(_GP(play).want_speech + 1);
 }
 
 int IsVoxAvailable() {
-	if (play.want_speech < 0)
+	if (_GP(play).want_speech < 0)
 		return 0;
 	return 1;
 }
 
 int IsMusicVoxAvailable() {
-	return play.separate_music_lib;
+	return _GP(play).separate_music_lib;
 }
 
 extern ScriptAudioChannel scrAudioChannel[MAX_SOUND_CHANNELS + 1];
@@ -535,16 +535,16 @@ static bool play_voice_clip_on_channel(const String &voice_name) {
 
 	String asset_name = voice_name;
 	asset_name.Append(".wav");
-	SOUNDCLIP *speechmp3 = my_load_wave(get_voice_over_assetpath(asset_name), play.speech_volume, 0);
+	SOUNDCLIP *speechmp3 = my_load_wave(get_voice_over_assetpath(asset_name), _GP(play).speech_volume, 0);
 
 	if (speechmp3 == nullptr) {
 		asset_name.ReplaceMid(asset_name.GetLength() - 3, 3, "ogg");
-		speechmp3 = my_load_ogg(get_voice_over_assetpath(asset_name), play.speech_volume);
+		speechmp3 = my_load_ogg(get_voice_over_assetpath(asset_name), _GP(play).speech_volume);
 	}
 
 	if (speechmp3 == nullptr) {
 		asset_name.ReplaceMid(asset_name.GetLength() - 3, 3, "mp3");
-		speechmp3 = my_load_mp3(get_voice_over_assetpath(asset_name), play.speech_volume);
+		speechmp3 = my_load_mp3(get_voice_over_assetpath(asset_name), _GP(play).speech_volume);
 	}
 
 	if (speechmp3 != nullptr) {
@@ -573,16 +573,16 @@ static bool play_voice_clip_impl(const String &voice_name, bool as_speech, bool
 	if (!as_speech)
 		return true;
 
-	play.speech_has_voice = true;
-	play.speech_voice_blocking = is_blocking;
+	_GP(play).speech_has_voice = true;
+	_GP(play).speech_voice_blocking = is_blocking;
 
 	cancel_scheduled_music_update();
-	play.music_vol_was = play.music_master_volume;
+	_GP(play).music_vol_was = _GP(play).music_master_volume;
 	// Negative value means set exactly; positive means drop that amount
-	if (play.speech_music_drop < 0)
-		play.music_master_volume = -play.speech_music_drop;
+	if (_GP(play).speech_music_drop < 0)
+		_GP(play).music_master_volume = -_GP(play).speech_music_drop;
 	else
-		play.music_master_volume -= play.speech_music_drop;
+		_GP(play).music_master_volume -= _GP(play).speech_music_drop;
 	apply_volume_drop_modifier(true);
 	update_music_volume();
 	update_ambient_sound_vol();
@@ -591,7 +591,7 @@ static bool play_voice_clip_impl(const String &voice_name, bool as_speech, bool
 
 // Stop voice-over clip and schedule audio volume reset
 static void stop_voice_clip_impl() {
-	play.music_master_volume = play.music_vol_was;
+	_GP(play).music_master_volume = _GP(play).music_vol_was;
 	// update the music in a bit (fixes two speeches follow each other
 	// and music going up-then-down)
 	schedule_music_update_at(AGS_Clock::now() + std::chrono::milliseconds(500));
@@ -600,7 +600,7 @@ static void stop_voice_clip_impl() {
 
 bool play_voice_speech(int charid, int sndid) {
 	// don't play speech if we're skipping a cutscene
-	if (!play.ShouldPlayVoiceSpeech())
+	if (!_GP(play).ShouldPlayVoiceSpeech())
 		return false;
 
 	String voice_file = get_cue_filename(charid, sndid);
@@ -623,19 +623,19 @@ bool play_voice_speech(int charid, int sndid) {
 
 	// change Sierra w/bgrnd  to Sierra without background when voice
 	// is available (for Tierra)
-	if ((_GP(game).options[OPT_SPEECHTYPE] == 2) && (play.no_textbg_when_voice > 0)) {
+	if ((_GP(game).options[OPT_SPEECHTYPE] == 2) && (_GP(play).no_textbg_when_voice > 0)) {
 		_GP(game).options[OPT_SPEECHTYPE] = 1;
-		play.no_textbg_when_voice = 2;
+		_GP(play).no_textbg_when_voice = 2;
 	}
 	return true;
 }
 
 bool play_voice_nonblocking(int charid, int sndid, bool as_speech) {
 	// don't play voice if we're skipping a cutscene
-	if (!play.ShouldPlayVoiceSpeech())
+	if (!_GP(play).ShouldPlayVoiceSpeech())
 		return false;
 	// don't play voice if there's a blocking speech with voice-over already
-	if (play.IsBlockingVoiceSpeech())
+	if (_GP(play).IsBlockingVoiceSpeech())
 		return false;
 
 	String voice_file = get_cue_filename(charid, sndid);
@@ -643,7 +643,7 @@ bool play_voice_nonblocking(int charid, int sndid, bool as_speech) {
 }
 
 void stop_voice_speech() {
-	if (!play.speech_has_voice)
+	if (!_GP(play).speech_has_voice)
 		return;
 
 	stop_voice_clip_impl();
@@ -651,25 +651,25 @@ void stop_voice_speech() {
 	// Reset lipsync
 	curLipLine = -1;
 	// Set back to Sierra w/bgrnd
-	if (play.no_textbg_when_voice == 2) {
-		play.no_textbg_when_voice = 1;
+	if (_GP(play).no_textbg_when_voice == 2) {
+		_GP(play).no_textbg_when_voice = 1;
 		_GP(game).options[OPT_SPEECHTYPE] = 2;
 	}
-	play.speech_has_voice = false;
-	play.speech_voice_blocking = false;
+	_GP(play).speech_has_voice = false;
+	_GP(play).speech_voice_blocking = false;
 }
 
 void stop_voice_nonblocking() {
-	if (!play.speech_has_voice)
+	if (!_GP(play).speech_has_voice)
 		return;
 	stop_voice_clip_impl();
 	// Only reset speech flags if we are truly playing a non-blocking voice;
 	// otherwise we might be inside blocking speech function and should let
 	// it keep these flags to be able to finalize properly.
 	// This is an imperfection of current speech implementation.
-	if (!play.speech_voice_blocking) {
-		play.speech_has_voice = false;
-		play.speech_voice_blocking = false;
+	if (!_GP(play).speech_voice_blocking) {
+		_GP(play).speech_has_voice = false;
+		_GP(play).speech_voice_blocking = false;
 	}
 }
 
diff --git a/engines/ags/engine/ac/global_character.cpp b/engines/ags/engine/ac/global_character.cpp
index 3c938a7c9e..cd35a484de 100644
--- a/engines/ags/engine/ac/global_character.cpp
+++ b/engines/ags/engine/ac/global_character.cpp
@@ -57,7 +57,7 @@ using namespace AGS::Shared;
 extern ViewStruct *views;
 extern RoomObject *objs;
 extern RoomStruct thisroom;
-extern GameState play;
+
 extern ScriptObject scrObj[MAX_ROOM_OBJECTS];
 extern ScriptInvItem scrInv[MAX_INV];
 
@@ -377,7 +377,7 @@ int GetCharacterSpeechAnimationDelay(CharacterInfo *cha) {
 		return 5;
 	}
 	if (_GP(game).options[OPT_GLOBALTALKANIMSPD] != 0)
-		return play.talkanim_speed;
+		return _GP(play).talkanim_speed;
 	else
 		return cha->speech_anim_speed;
 }
@@ -393,7 +393,7 @@ void RunCharacterInteraction(int cc, int mood) {
 	else if (mood == MODE_USE) {
 		passon = 3;
 		cdata = playerchar->activeinv;
-		play.usedinv = cdata;
+		_GP(play).usedinv = cdata;
 	} else if (mood == MODE_PICKUP) passon = 5;
 	else if (mood == MODE_CUSTOM1) passon = 6;
 	else if (mood == MODE_CUSTOM2) passon = 7;
@@ -432,7 +432,7 @@ int AreCharactersColliding(int cchar1, int cchar2) {
 int GetCharacterProperty(int cha, const char *property) {
 	if (!is_valid_character(cha))
 		quit("!GetCharacterProperty: invalid character");
-	return get_int_property(_GP(game).charProps[cha], play.charProps[cha], property);
+	return get_int_property(_GP(game).charProps[cha], _GP(play).charProps[cha], property);
 }
 
 void SetCharacterProperty(int who, int flag, int yesorno) {
@@ -443,11 +443,11 @@ void SetCharacterProperty(int who, int flag, int yesorno) {
 }
 
 void GetCharacterPropertyText(int item, const char *property, char *bufer) {
-	get_text_property(_GP(game).charProps[item], play.charProps[item], property, bufer);
+	get_text_property(_GP(game).charProps[item], _GP(play).charProps[item], property, bufer);
 }
 
 int GetCharIDAtScreen(int xx, int yy) {
-	VpPoint vpt = play.ScreenToRoomDivDown(xx, yy);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
 	if (vpt.second < 0)
 		return -1;
 	return is_pos_on_character(vpt.first.X, vpt.first.Y);
@@ -485,7 +485,7 @@ void update_invorder() {
 		}
 	}
 	// backwards compatibility
-	play.obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
+	_GP(play).obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
 
 	guis_need_update = 1;
 }
@@ -496,7 +496,7 @@ void add_inventory(int inum) {
 
 	Character_AddInventory(playerchar, &scrInv[inum], SCR_NO_VALUE);
 
-	play.obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
+	_GP(play).obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
 }
 
 void lose_inventory(int inum) {
@@ -505,7 +505,7 @@ void lose_inventory(int inum) {
 
 	Character_LoseInventory(playerchar, &scrInv[inum]);
 
-	play.obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
+	_GP(play).obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
 }
 
 void AddInventoryToCharacter(int charid, int inum) {
@@ -557,7 +557,7 @@ int DisplaySpeechBackground(int charid, const char *speel) {
 			i++;
 	}
 
-	int ovrl = CreateTextOverlay(OVR_AUTOPLACE, charid, play.GetUIViewport().GetWidth() / 2, FONT_SPEECH,
+	int ovrl = CreateTextOverlay(OVR_AUTOPLACE, charid, _GP(play).GetUIViewport().GetWidth() / 2, FONT_SPEECH,
 		-_GP(game).chars[charid].talkcolor, get_translation(speel), DISPLAYTEXT_NORMALOVERLAY);
 
 	int scid = find_overlay_of_type(ovrl);
diff --git a/engines/ags/engine/ac/global_debug.cpp b/engines/ags/engine/ac/global_debug.cpp
index 6b5acf38e7..05db804e52 100644
--- a/engines/ags/engine/ac/global_debug.cpp
+++ b/engines/ags/engine/ac/global_debug.cpp
@@ -55,7 +55,7 @@ using namespace AGS::Engine;
 
 
 extern GameSetup usetup;
-extern GameState play;
+
 extern RoomStruct thisroom;
 extern CharacterInfo *playerchar;
 
@@ -82,9 +82,9 @@ String GetRuntimeInfo() {
 		gfxDriver->GetDriverName(), filter->GetInfo().Name.GetCStr(),
 		render_frame.GetWidth(), render_frame.GetHeight(),
 		_GP(spriteset).GetCacheSize() / 1024, _GP(spriteset).GetMaxCacheSize() / 1024, _GP(spriteset).GetLockedSize() / 1024);
-	if (play.separate_music_lib)
+	if (_GP(play).separate_music_lib)
 		runtimeInfo.Append("[AUDIO.VOX enabled");
-	if (play.want_speech >= 1)
+	if (_GP(play).want_speech >= 1)
 		runtimeInfo.Append("[SPEECH.VOX enabled");
 	if (transtree != nullptr) {
 		runtimeInfo.Append("[Using translation ");
@@ -95,13 +95,13 @@ String GetRuntimeInfo() {
 }
 
 void script_debug(int cmdd, int dataa) {
-	if (play.debug_mode == 0) return;
+	if (_GP(play).debug_mode == 0) return;
 	int rr;
 	if (cmdd == 0) {
 		for (rr = 1; rr < _GP(game).numinvitems; rr++)
 			playerchar->inv[rr] = 1;
 		update_invorder();
-		//    Display("invorder decided there are %d items[display %d",play.inv_numorder,play.inv_numdisp);
+		//    Display("invorder decided there are %d items[display %d",_GP(play).inv_numorder,_GP(play).inv_numdisp);
 	} else if (cmdd == 1) {
 		String toDisplay = GetRuntimeInfo();
 		Display(toDisplay.GetCStr());
@@ -115,8 +115,8 @@ void script_debug(int cmdd, int dataa) {
 		const int camera_index = 0;
 		Bitmap *tempw = BitmapHelper::CreateBitmap(thisroom.WalkAreaMask->GetWidth(), thisroom.WalkAreaMask->GetHeight());
 		tempw->Blit(prepare_walkable_areas(-1), 0, 0, 0, 0, tempw->GetWidth(), tempw->GetHeight());
-		const Rect &viewport = play.GetRoomViewport(viewport_index)->GetRect();
-		const Rect &camera = play.GetRoomCamera(camera_index)->GetRect();
+		const Rect &viewport = _GP(play).GetRoomViewport(viewport_index)->GetRect();
+		const Rect &camera = _GP(play).GetRoomCamera(camera_index)->GetRect();
 		Bitmap *view_bmp = BitmapHelper::CreateBitmap(viewport.GetWidth(), viewport.GetHeight());
 		Rect mask_src = Rect(camera.Left / thisroom.MaskResolution, camera.Top / thisroom.MaskResolution, camera.Right / thisroom.MaskResolution, camera.Bottom / thisroom.MaskResolution);
 		view_bmp->StretchBlt(tempw, mask_src, RectWH(0, 0, viewport.GetWidth(), viewport.GetHeight()), Shared::kBitmap_Transparency);
@@ -169,8 +169,8 @@ void script_debug(int cmdd, int dataa) {
 		// TODO: support multiple viewports?!
 		const int viewport_index = 0;
 		const int camera_index = 0;
-		const Rect &viewport = play.GetRoomViewport(viewport_index)->GetRect();
-		const Rect &camera = play.GetRoomCamera(camera_index)->GetRect();
+		const Rect &viewport = _GP(play).GetRoomViewport(viewport_index)->GetRect();
+		const Rect &camera = _GP(play).GetRoomCamera(camera_index)->GetRect();
 		Bitmap *view_bmp = BitmapHelper::CreateBitmap(viewport.GetWidth(), viewport.GetHeight());
 		Rect mask_src = Rect(camera.Left / thisroom.MaskResolution, camera.Top / thisroom.MaskResolution, camera.Right / thisroom.MaskResolution, camera.Bottom / thisroom.MaskResolution);
 		view_bmp->StretchBlt(tempw, mask_src, RectWH(0, 0, viewport.GetWidth(), viewport.GetHeight()), Shared::kBitmap_Transparency);
diff --git a/engines/ags/engine/ac/global_dialog.cpp b/engines/ags/engine/ac/global_dialog.cpp
index 1eb6a7b8d9..23bb9ca938 100644
--- a/engines/ags/engine/ac/global_dialog.cpp
+++ b/engines/ags/engine/ac/global_dialog.cpp
@@ -36,7 +36,7 @@ namespace AGS3 {
 
 using namespace AGS::Shared;
 
-extern GameState play;
+
 extern DialogTopic *dialog;
 
 ScriptPosition last_in_dialog_request_script_pos;
@@ -46,9 +46,9 @@ void RunDialog(int tum) {
 
 	can_run_delayed_command();
 
-	if (play.stop_dialog_at_end != DIALOG_NONE) {
-		if (play.stop_dialog_at_end == DIALOG_RUNNING)
-			play.stop_dialog_at_end = DIALOG_NEWTOPIC + tum;
+	if (_GP(play).stop_dialog_at_end != DIALOG_NONE) {
+		if (_GP(play).stop_dialog_at_end == DIALOG_RUNNING)
+			_GP(play).stop_dialog_at_end = DIALOG_NEWTOPIC + tum;
 		else
 			quitprintf("!RunDialog: two NewRoom/RunDialog/StopDialog requests within dialog; last was called in \"%s\", line %d",
 				last_in_dialog_request_script_pos.Section.GetCStr(), last_in_dialog_request_script_pos.Line);
@@ -65,13 +65,13 @@ void RunDialog(int tum) {
 
 
 void StopDialog() {
-	if (play.stop_dialog_at_end == DIALOG_NONE) {
+	if (_GP(play).stop_dialog_at_end == DIALOG_NONE) {
 		debug_script_warn("StopDialog called, but was not in a dialog");
 		debug_script_log("StopDialog called but no dialog");
 		return;
 	}
 	get_script_position(last_in_dialog_request_script_pos);
-	play.stop_dialog_at_end = DIALOG_STOP;
+	_GP(play).stop_dialog_at_end = DIALOG_STOP;
 }
 
 void SetDialogOption(int dlg, int opt, int onoroff, bool dlg_script) {
diff --git a/engines/ags/engine/ac/global_display.cpp b/engines/ags/engine/ac/global_display.cpp
index b97937cf5f..c639304db4 100644
--- a/engines/ags/engine/ac/global_display.cpp
+++ b/engines/ags/engine/ac/global_display.cpp
@@ -45,7 +45,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 
 extern TopBarSettings topBar;
-extern GameState play;
+
 extern RoomStruct thisroom;
 extern int display_message_aschar;
 
@@ -70,26 +70,26 @@ void DisplayTopBar(int ypos, int ttexcol, int backcol, const char *title, const
 	source_text_length = real_text_sourcelen;
 
 	if (ypos > 0)
-		play.top_bar_ypos = ypos;
+		_GP(play).top_bar_ypos = ypos;
 	if (ttexcol > 0)
-		play.top_bar_textcolor = ttexcol;
+		_GP(play).top_bar_textcolor = ttexcol;
 	if (backcol > 0)
-		play.top_bar_backcolor = backcol;
+		_GP(play).top_bar_backcolor = backcol;
 
 	topBar.wantIt = 1;
 	topBar.font = FONT_NORMAL;
 	topBar.height = getfontheight_outlined(topBar.font);
-	topBar.height += data_to_game_coord(play.top_bar_borderwidth) * 2 + get_fixed_pixel_size(1);
+	topBar.height += data_to_game_coord(_GP(play).top_bar_borderwidth) * 2 + get_fixed_pixel_size(1);
 
 	// they want to customize the font
-	if (play.top_bar_font >= 0)
-		topBar.font = play.top_bar_font;
+	if (_GP(play).top_bar_font >= 0)
+		topBar.font = _GP(play).top_bar_font;
 
 	// DisplaySpeech normally sets this up, but since we're not going via it...
-	if (play.cant_skip_speech & SKIP_AUTOTIMER)
-		play.messagetime = GetTextDisplayTime(text);
+	if (_GP(play).cant_skip_speech & SKIP_AUTOTIMER)
+		_GP(play).messagetime = GetTextDisplayTime(text);
 
-	DisplayAtY(play.top_bar_ypos, text);
+	DisplayAtY(_GP(play).top_bar_ypos, text);
 }
 
 // Display a room/global message in the bar
@@ -126,13 +126,13 @@ void DisplayMessageAtY(int msnum, int ypos) {
 			DisplaySpeech(msgbufr, thisroom.MessageInfos[msnum].DisplayAs - 1);
 		} else {
 			// time out automatically if they have set that
-			int oldGameSkipDisp = play.skip_display;
+			int oldGameSkipDisp = _GP(play).skip_display;
 			if (thisroom.MessageInfos[msnum].Flags & MSG_TIMELIMIT)
-				play.skip_display = 0;
+				_GP(play).skip_display = 0;
 
 			DisplayAtY(ypos, msgbufr);
 
-			play.skip_display = oldGameSkipDisp;
+			_GP(play).skip_display = oldGameSkipDisp;
 		}
 		if (thisroom.MessageInfos[msnum].Flags & MSG_DISPLAYNEXT) {
 			msnum++;
@@ -151,13 +151,13 @@ void DisplayAt(int xxp, int yyp, int widd, const char *text) {
 	data_to_game_coords(&xxp, &yyp);
 	widd = data_to_game_coord(widd);
 
-	if (widd < 1) widd = play.GetUIViewport().GetWidth() / 2;
-	if (xxp < 0) xxp = play.GetUIViewport().GetWidth() / 2 - widd / 2;
+	if (widd < 1) widd = _GP(play).GetUIViewport().GetWidth() / 2;
+	if (xxp < 0) xxp = _GP(play).GetUIViewport().GetWidth() / 2 - widd / 2;
 	_display_at(xxp, yyp, widd, text, DISPLAYTEXT_MESSAGEBOX, 0, 0, 0, false);
 }
 
 void DisplayAtY(int ypos, const char *texx) {
-	const Rect &ui_view = play.GetUIViewport();
+	const Rect &ui_view = _GP(play).GetUIViewport();
 	if ((ypos < -1) || (ypos >= ui_view.GetHeight()))
 		quitprintf("!DisplayAtY: invalid Y co-ordinate supplied (used: %d; valid: 0..%d)", ypos, ui_view.GetHeight());
 
@@ -175,9 +175,9 @@ void DisplayAtY(int ypos, const char *texx) {
 
 		if (is_screen_dirty()) {
 			// erase any previous DisplaySpeech
-			play.disabled_user_interface++;
+			_GP(play).disabled_user_interface++;
 			UpdateGameOnce();
-			play.disabled_user_interface--;
+			_GP(play).disabled_user_interface--;
 		}
 
 		_display_at(-1, ypos, ui_view.GetWidth() / 2 + ui_view.GetWidth() / 4,
@@ -196,11 +196,11 @@ void SetSkipSpeech(SkipSpeechStyle newval) {
 		quit("!SetSkipSpeech: invalid skip mode specified");
 
 	debug_script_log("SkipSpeech style set to %d", newval);
-	play.cant_skip_speech = user_to_internal_skip_speech((SkipSpeechStyle)newval);
+	_GP(play).cant_skip_speech = user_to_internal_skip_speech((SkipSpeechStyle)newval);
 }
 
 SkipSpeechStyle GetSkipSpeech() {
-	return internal_skip_speech_to_user(play.cant_skip_speech);
+	return internal_skip_speech_to_user(_GP(play).cant_skip_speech);
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/global_drawingsurface.cpp b/engines/ags/engine/ac/global_drawingsurface.cpp
index e1b571066d..3e6e6ee9a1 100644
--- a/engines/ags/engine/ac/global_drawingsurface.cpp
+++ b/engines/ags/engine/ac/global_drawingsurface.cpp
@@ -45,20 +45,20 @@ using namespace AGS::Engine;
 
 extern Bitmap *raw_saved_screen;
 extern RoomStruct thisroom;
-extern GameState play;
+
 
 
 
 // Raw screen writing routines - similar to old CapturedStuff
-#define RAW_START() play.raw_drawing_surface = thisroom.BgFrames[play.bg_frame].Graphic; play.raw_modified[play.bg_frame] = 1
+#define RAW_START() _GP(play).raw_drawing_surface = thisroom.BgFrames[_GP(play).bg_frame].Graphic; _GP(play).raw_modified[_GP(play).bg_frame] = 1
 #define RAW_END()
-#define RAW_SURFACE() (play.raw_drawing_surface.get())
+#define RAW_SURFACE() (_GP(play).raw_drawing_surface.get())
 
 // RawSaveScreen: copy the current screen to a backup bitmap
 void RawSaveScreen() {
 	if (raw_saved_screen != nullptr)
 		delete raw_saved_screen;
-	PBitmap source = thisroom.BgFrames[play.bg_frame].Graphic;
+	PBitmap source = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
 	raw_saved_screen = BitmapHelper::CreateBitmapCopy(source.get());
 }
 // RawRestoreScreen: copy backup bitmap back to screen; we
@@ -69,7 +69,7 @@ void RawRestoreScreen() {
 		debug_script_warn("RawRestoreScreen: unable to restore, since the screen hasn't been saved previously.");
 		return;
 	}
-	PBitmap deston = thisroom.BgFrames[play.bg_frame].Graphic;
+	PBitmap deston = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
 	deston->Blit(raw_saved_screen, 0, 0, 0, 0, deston->GetWidth(), deston->GetHeight());
 	invalidate_screen();
 	mark_current_background_dirty();
@@ -87,7 +87,7 @@ void RawRestoreScreenTinted(int red, int green, int blue, int opacity) {
 
 	debug_script_log("RawRestoreTinted RGB(%d,%d,%d) %d%%", red, green, blue, opacity);
 
-	PBitmap deston = thisroom.BgFrames[play.bg_frame].Graphic;
+	PBitmap deston = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
 	tint_image(deston.get(), raw_saved_screen, red, green, blue, opacity);
 	invalidate_screen();
 	mark_current_background_dirty();
@@ -102,7 +102,7 @@ void RawDrawFrameTransparent(int frame, int translev) {
 	if (bg->GetColorDepth() <= 8)
 		quit("!RawDrawFrameTransparent: 256-colour backgrounds not supported");
 
-	if (frame == play.bg_frame)
+	if (frame == _GP(play).bg_frame)
 		quit("!RawDrawFrameTransparent: cannot draw current background onto itself");
 
 	RAW_START();
@@ -128,25 +128,25 @@ void RawClear(int clr) {
 }
 void RawSetColor(int clr) {
 	// set the colour at the appropriate depth for the background
-	play.raw_color = MakeColor(clr);
+	_GP(play).raw_color = MakeColor(clr);
 }
 void RawSetColorRGB(int red, int grn, int blu) {
 	if ((red < 0) || (red > 255) || (grn < 0) || (grn > 255) ||
 		(blu < 0) || (blu > 255))
 		quit("!RawSetColorRGB: colour values must be 0-255");
 
-	play.raw_color = makecol_depth(thisroom.BgFrames[play.bg_frame].Graphic->GetColorDepth(), red, grn, blu);
+	_GP(play).raw_color = makecol_depth(thisroom.BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth(), red, grn, blu);
 }
 void RawPrint(int xx, int yy, const char *text) {
 	RAW_START();
 	// don't use wtextcolor because it will do a 16->32 conversion
-	color_t text_color = play.raw_color;
-	if ((RAW_SURFACE()->GetColorDepth() <= 8) && (play.raw_color > 255)) {
+	color_t text_color = _GP(play).raw_color;
+	if ((RAW_SURFACE()->GetColorDepth() <= 8) && (_GP(play).raw_color > 255)) {
 		text_color = RAW_SURFACE()->GetCompatibleColor(1);
 		debug_script_warn("RawPrint: Attempted to use hi-color on 256-col background");
 	}
 	data_to_game_coords(&xx, &yy);
-	wouttext_outline(RAW_SURFACE(), xx, yy, play.normal_font, text_color, text);
+	wouttext_outline(RAW_SURFACE(), xx, yy, _GP(play).normal_font, text_color, text);
 	// we must invalidate the entire screen because these are room
 	// co-ordinates, not screen co-ords which it works with
 	invalidate_screen();
@@ -167,7 +167,7 @@ void RawPrintMessageWrapped(int xx, int yy, int wid, int font, int msgm) {
 		return;
 
 	RAW_START();
-	color_t text_color = play.raw_color;
+	color_t text_color = _GP(play).raw_color;
 	for (size_t i = 0; i < Lines.Count(); i++)
 		wouttext_outline(RAW_SURFACE(), xx, yy + linespacing * i, font, text_color, Lines[i]);
 	invalidate_screen();
@@ -264,11 +264,11 @@ void RawDrawLine(int fromx, int fromy, int tox, int toy) {
 	data_to_game_coords(&fromx, &fromy);
 	data_to_game_coords(&tox, &toy);
 
-	play.raw_modified[play.bg_frame] = 1;
+	_GP(play).raw_modified[_GP(play).bg_frame] = 1;
 	int ii, jj;
 	// draw a line thick enough to look the same at all resolutions
-	PBitmap bg = thisroom.BgFrames[play.bg_frame].Graphic;
-	color_t draw_color = play.raw_color;
+	PBitmap bg = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
+	color_t draw_color = _GP(play).raw_color;
 	for (ii = 0; ii < get_fixed_pixel_size(1); ii++) {
 		for (jj = 0; jj < get_fixed_pixel_size(1); jj++)
 			bg->DrawLine(Line(fromx + ii, fromy + jj, tox + ii, toy + jj), draw_color);
@@ -280,30 +280,30 @@ void RawDrawCircle(int xx, int yy, int rad) {
 	data_to_game_coords(&xx, &yy);
 	rad = data_to_game_coord(rad);
 
-	play.raw_modified[play.bg_frame] = 1;
-	PBitmap bg = thisroom.BgFrames[play.bg_frame].Graphic;
-	bg->FillCircle(Circle(xx, yy, rad), play.raw_color);
+	_GP(play).raw_modified[_GP(play).bg_frame] = 1;
+	PBitmap bg = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
+	bg->FillCircle(Circle(xx, yy, rad), _GP(play).raw_color);
 	invalidate_screen();
 	mark_current_background_dirty();
 }
 void RawDrawRectangle(int x1, int y1, int x2, int y2) {
-	play.raw_modified[play.bg_frame] = 1;
+	_GP(play).raw_modified[_GP(play).bg_frame] = 1;
 	data_to_game_coords(&x1, &y1);
 	data_to_game_round_up(&x2, &y2);
 
-	PBitmap bg = thisroom.BgFrames[play.bg_frame].Graphic;
-	bg->FillRect(Rect(x1, y1, x2, y2), play.raw_color);
+	PBitmap bg = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
+	bg->FillRect(Rect(x1, y1, x2, y2), _GP(play).raw_color);
 	invalidate_screen();
 	mark_current_background_dirty();
 }
 void RawDrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
-	play.raw_modified[play.bg_frame] = 1;
+	_GP(play).raw_modified[_GP(play).bg_frame] = 1;
 	data_to_game_coords(&x1, &y1);
 	data_to_game_coords(&x2, &y2);
 	data_to_game_coords(&x3, &y3);
 
-	PBitmap bg = thisroom.BgFrames[play.bg_frame].Graphic;
-	bg->DrawTriangle(Triangle(x1, y1, x2, y2, x3, y3), play.raw_color);
+	PBitmap bg = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
+	bg->DrawTriangle(Triangle(x1, y1, x2, y2, x3, y3), _GP(play).raw_color);
 	invalidate_screen();
 	mark_current_background_dirty();
 }
diff --git a/engines/ags/engine/ac/global_game.cpp b/engines/ags/engine/ac/global_game.cpp
index 99dda63ba7..26a0520278 100644
--- a/engines/ags/engine/ac/global_game.cpp
+++ b/engines/ags/engine/ac/global_game.cpp
@@ -77,7 +77,7 @@ using namespace AGS::Shared;
 
 #define ALLEGRO_KEYBOARD_HANDLER
 
-extern GameState play;
+
 extern ExecutingScript *curscript;
 extern int displayed_room;
 extern int game_paused;
@@ -101,10 +101,10 @@ extern int _G(psp_gfx_renderer);
 
 void GiveScore(int amnt) {
 	guis_need_update = 1;
-	play.score += amnt;
+	_GP(play).score += amnt;
 
-	if ((amnt > 0) && (play.score_sound >= 0))
-		play_audio_clip_by_index(play.score_sound);
+	if ((amnt > 0) && (_GP(play).score_sound >= 0))
+		play_audio_clip_by_index(_GP(play).score_sound);
 
 	run_on_event(GE_GOT_SCORE, RuntimeScriptValue().SetInt32(amnt));
 }
@@ -207,33 +207,33 @@ void SetGlobalInt(int index, int valu) {
 	if ((index < 0) | (index >= MAXGSVALUES))
 		quit("!SetGlobalInt: invalid index");
 
-	if (play.globalscriptvars[index] != valu) {
+	if (_GP(play).globalscriptvars[index] != valu) {
 		debug_script_log("GlobalInt %d set to %d", index, valu);
 	}
 
-	play.globalscriptvars[index] = valu;
+	_GP(play).globalscriptvars[index] = valu;
 }
 
 
 int GetGlobalInt(int index) {
 	if ((index < 0) | (index >= MAXGSVALUES))
 		quit("!GetGlobalInt: invalid index");
-	return play.globalscriptvars[index];
+	return _GP(play).globalscriptvars[index];
 }
 
 void SetGlobalString(int index, const char *newval) {
 	if ((index < 0) | (index >= MAXGLOBALSTRINGS))
 		quit("!SetGlobalString: invalid index");
 	debug_script_log("GlobalString %d set to '%s'", index, newval);
-	strncpy(play.globalstrings[index], newval, MAX_MAXSTRLEN);
+	strncpy(_GP(play).globalstrings[index], newval, MAX_MAXSTRLEN);
 	// truncate it to 200 chars, to be sure
-	play.globalstrings[index][MAX_MAXSTRLEN - 1] = 0;
+	_GP(play).globalstrings[index][MAX_MAXSTRLEN - 1] = 0;
 }
 
 void GetGlobalString(int index, char *strval) {
 	if ((index < 0) | (index >= MAXGLOBALSTRINGS))
 		quit("!GetGlobalString: invalid index");
-	strcpy(strval, play.globalstrings[index]);
+	strcpy(strval, _GP(play).globalstrings[index]);
 }
 
 // TODO: refactor this method, and use same shared procedure at both normal stop/startup and in RunAGSGame
@@ -255,7 +255,7 @@ int RunAGSGame(const char *newgame, unsigned int mode, int data) {
 		get_install_dir_path(gamefilenamebuf, newgame);
 		ResPaths.GamePak.Path = gamefilenamebuf;
 		ResPaths.GamePak.Name = Shared::Path::get_filename(gamefilenamebuf);
-		play.takeover_data = data;
+		_GP(play).takeover_data = data;
 		load_new_game_restore = -1;
 
 		if (inside_script) {
@@ -295,11 +295,11 @@ int RunAGSGame(const char *newgame, unsigned int mode, int data) {
 	if ((mode & RAGMODE_PRESERVEGLOBALINT) == 0) {
 		// reset GlobalInts
 		for (ee = 0; ee < MAXGSVALUES; ee++)
-			play.globalscriptvars[ee] = 0;
+			_GP(play).globalscriptvars[ee] = 0;
 	}
 
 	engine_init_game_settings();
-	play.screen_is_faded_out = 1;
+	_GP(play).screen_is_faded_out = 1;
 
 	if (load_new_game_restore >= 0) {
 		try_restore_save(load_new_game_restore);
@@ -386,7 +386,7 @@ void SetRestartPoint() {
 
 
 void SetGameSpeed(int newspd) {
-	newspd += play.game_speed_modifier;
+	newspd += _GP(play).game_speed_modifier;
 	if (newspd > 1000) newspd = 1000;
 	if (newspd < 10) newspd = 10;
 	set_game_speed(newspd);
@@ -394,7 +394,7 @@ void SetGameSpeed(int newspd) {
 }
 
 int GetGameSpeed() {
-	return ::lround(get_current_fps()) - play.game_speed_modifier;
+	return ::lround(get_current_fps()) - _GP(play).game_speed_modifier;
 }
 
 int SetGameOption(int opt, int setting) {
@@ -425,7 +425,7 @@ int SetGameOption(int opt, int setting) {
 		gui_disabled_style = convert_gui_disabled_style(_GP(game).options[OPT_DISABLEOFF]);
 	else if (opt == OPT_PORTRAITSIDE) {
 		if (setting == 0)  // set back to Left
-			play.swap_portrait_side = 0;
+			_GP(play).swap_portrait_side = 0;
 	}
 
 	return oldval;
@@ -452,17 +452,17 @@ void SkipUntilCharacterStops(int cc) {
 		quit("!SkipUntilCharacterStops: cannot be used within a cutscene");
 
 	initialize_skippable_cutscene();
-	play.fast_forward = 2;
-	play.skip_until_char_stops = cc;
+	_GP(play).fast_forward = 2;
+	_GP(play).skip_until_char_stops = cc;
 }
 
 void EndSkippingUntilCharStops() {
 	// not currently skipping, so ignore
-	if (play.skip_until_char_stops < 0)
+	if (_GP(play).skip_until_char_stops < 0)
 		return;
 
 	stop_fast_forwarding();
-	play.skip_until_char_stops = -1;
+	_GP(play).skip_until_char_stops = -1;
 }
 
 void StartCutscene(int skipwith) {
@@ -481,7 +481,7 @@ void StartCutscene(int skipwith) {
 	// make sure they can't be skipping and cutsceneing at the same time
 	EndSkippingUntilCharStops();
 
-	play.in_cutscene = skipwith;
+	_GP(play).in_cutscene = skipwith;
 	initialize_skippable_cutscene();
 }
 
@@ -494,8 +494,8 @@ int EndCutscene() {
 	if (!is_in_cutscene())
 		quit("!EndCutscene: not in a cutscene");
 
-	int retval = play.fast_forward;
-	play.in_cutscene = 0;
+	int retval = _GP(play).fast_forward;
+	_GP(play).in_cutscene = 0;
 	// Stop it fast-forwarding
 	stop_fast_forwarding();
 
@@ -524,11 +524,11 @@ void SaveCursorForLocationChange() {
 	char tempo[100];
 	GetLocationName(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey)), tempo);
 
-	if (play.get_loc_name_save_cursor != play.get_loc_name_last_time) {
-		play.get_loc_name_save_cursor = play.get_loc_name_last_time;
-		play.restore_cursor_mode_to = GetCursorMode();
-		play.restore_cursor_image_to = GetMouseCursor();
-		debug_script_log("Saving mouse: mode %d cursor %d", play.restore_cursor_mode_to, play.restore_cursor_image_to);
+	if (_GP(play).get_loc_name_save_cursor != _GP(play).get_loc_name_last_time) {
+		_GP(play).get_loc_name_save_cursor = _GP(play).get_loc_name_last_time;
+		_GP(play).restore_cursor_mode_to = GetCursorMode();
+		_GP(play).restore_cursor_image_to = GetMouseCursor();
+		debug_script_log("Saving mouse: mode %d cursor %d", _GP(play).restore_cursor_mode_to, _GP(play).restore_cursor_image_to);
 	}
 }
 
@@ -543,20 +543,20 @@ void GetLocationName(int xxx, int yyy, char *tempo) {
 	if (GetGUIAt(xxx, yyy) >= 0) {
 		int mover = GetInvAt(xxx, yyy);
 		if (mover > 0) {
-			if (play.get_loc_name_last_time != 1000 + mover)
+			if (_GP(play).get_loc_name_last_time != 1000 + mover)
 				guis_need_update = 1;
-			play.get_loc_name_last_time = 1000 + mover;
+			_GP(play).get_loc_name_last_time = 1000 + mover;
 			strcpy(tempo, get_translation(_GP(game).invinfo[mover].name));
-		} else if ((play.get_loc_name_last_time > 1000) && (play.get_loc_name_last_time < 1000 + MAX_INV)) {
+		} else if ((_GP(play).get_loc_name_last_time > 1000) && (_GP(play).get_loc_name_last_time < 1000 + MAX_INV)) {
 			// no longer selecting an item
 			guis_need_update = 1;
-			play.get_loc_name_last_time = -1;
+			_GP(play).get_loc_name_last_time = -1;
 		}
 		return;
 	}
 
 	int loctype = GetLocationType(xxx, yyy); // GetLocationType takes screen coords
-	VpPoint vpt = play.ScreenToRoomDivDown(xxx, yyy);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(xxx, yyy);
 	if (vpt.second < 0)
 		return;
 	xxx = vpt.first.X;
@@ -566,8 +566,8 @@ void GetLocationName(int xxx, int yyy, char *tempo) {
 
 	int onhs, aa;
 	if (loctype == 0) {
-		if (play.get_loc_name_last_time != 0) {
-			play.get_loc_name_last_time = 0;
+		if (_GP(play).get_loc_name_last_time != 0) {
+			_GP(play).get_loc_name_last_time = 0;
 			guis_need_update = 1;
 		}
 		return;
@@ -577,9 +577,9 @@ void GetLocationName(int xxx, int yyy, char *tempo) {
 	if (loctype == LOCTYPE_CHAR) {
 		onhs = getloctype_index;
 		strcpy(tempo, get_translation(_GP(game).chars[onhs].name));
-		if (play.get_loc_name_last_time != 2000 + onhs)
+		if (_GP(play).get_loc_name_last_time != 2000 + onhs)
 			guis_need_update = 1;
-		play.get_loc_name_last_time = 2000 + onhs;
+		_GP(play).get_loc_name_last_time = 2000 + onhs;
 		return;
 	}
 	// on object
@@ -592,17 +592,17 @@ void GetLocationName(int xxx, int yyy, char *tempo) {
 			tempo[0] = ' ';
 			tempo[1] = 0;
 		}
-		if (play.get_loc_name_last_time != 3000 + aa)
+		if (_GP(play).get_loc_name_last_time != 3000 + aa)
 			guis_need_update = 1;
-		play.get_loc_name_last_time = 3000 + aa;
+		_GP(play).get_loc_name_last_time = 3000 + aa;
 		return;
 	}
 	onhs = getloctype_index;
 	if (onhs > 0)
 		strcpy(tempo, get_translation(thisroom.Hotspots[onhs].Name));
-	if (play.get_loc_name_last_time != onhs)
+	if (_GP(play).get_loc_name_last_time != onhs)
 		guis_need_update = 1;
-	play.get_loc_name_last_time = onhs;
+	_GP(play).get_loc_name_last_time = onhs;
 }
 
 int IsKeyPressed(int keycode) {
@@ -851,7 +851,7 @@ int SaveScreenShot(const char *namm) {
 	else
 		fileName.Format("%s%s", svg_dir.GetCStr(), namm);
 
-	Bitmap *buffer = CopyScreenIntoBitmap(play.GetMainViewport().GetWidth(), play.GetMainViewport().GetHeight());
+	Bitmap *buffer = CopyScreenIntoBitmap(_GP(play).GetMainViewport().GetWidth(), _GP(play).GetMainViewport().GetHeight());
 	if (!buffer->SaveToFile(fileName, palette)) {
 		delete buffer;
 		return 0;
@@ -891,7 +891,7 @@ extern int getloctype_throughgui, getloctype_index;
 void RoomProcessClick(int xx, int yy, int mood) {
 	getloctype_throughgui = 1;
 	int loctype = GetLocationType(xx, yy);
-	VpPoint vpt = play.ScreenToRoomDivDown(xx, yy);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
 	if (vpt.second < 0)
 		return;
 	xx = vpt.first.X;
@@ -901,7 +901,7 @@ void RoomProcessClick(int xx, int yy, int mood) {
 		int hsnum = get_hotspot_at(xx, yy);
 		if (hsnum < 1);
 		else if (thisroom.Hotspots[hsnum].WalkTo.X < 1);
-		else if (play.auto_use_walkto_points == 0);
+		else if (_GP(play).auto_use_walkto_points == 0);
 		else {
 			xx = thisroom.Hotspots[hsnum].WalkTo.X;
 			yy = thisroom.Hotspots[hsnum].WalkTo.Y;
@@ -910,7 +910,7 @@ void RoomProcessClick(int xx, int yy, int mood) {
 		walk_character(_GP(game).playercharacter, xx, yy, 0, true);
 		return;
 	}
-	play.usedmode = mood;
+	_GP(play).usedmode = mood;
 
 	if (loctype == 0) {
 		// click on nothing -> hotspot 0
@@ -929,7 +929,7 @@ void RoomProcessClick(int xx, int yy, int mood) {
 int IsInteractionAvailable(int xx, int yy, int mood) {
 	getloctype_throughgui = 1;
 	int loctype = GetLocationType(xx, yy);
-	VpPoint vpt = play.ScreenToRoomDivDown(xx, yy);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(xx, yy);
 	if (vpt.second < 0)
 		return 0;
 	xx = vpt.first.X;
@@ -939,7 +939,7 @@ int IsInteractionAvailable(int xx, int yy, int mood) {
 	if ((mood == MODE_WALK) && (_GP(game).options[OPT_NOWALKMODE] == 0))
 		return 1;
 
-	play.check_interaction_only = 1;
+	_GP(play).check_interaction_only = 1;
 
 	if (loctype == 0) {
 		// click on nothing -> hotspot 0
@@ -954,8 +954,8 @@ int IsInteractionAvailable(int xx, int yy, int mood) {
 	} else if (loctype == LOCTYPE_HOTSPOT)
 		RunHotspotInteraction(getloctype_index, mood);
 
-	int ciwas = play.check_interaction_only;
-	play.check_interaction_only = 0;
+	int ciwas = _GP(play).check_interaction_only;
+	_GP(play).check_interaction_only = 0;
 
 	if (ciwas == 2)
 		return 1;
@@ -971,13 +971,13 @@ void GetMessageText(int msg, char *buffer) {
 void SetSpeechFont(int fontnum) {
 	if ((fontnum < 0) || (fontnum >= _GP(game).numfonts))
 		quit("!SetSpeechFont: invalid font number.");
-	play.speech_font = fontnum;
+	_GP(play).speech_font = fontnum;
 }
 
 void SetNormalFont(int fontnum) {
 	if ((fontnum < 0) || (fontnum >= _GP(game).numfonts))
 		quit("!SetNormalFont: invalid font number.");
-	play.normal_font = fontnum;
+	_GP(play).normal_font = fontnum;
 }
 
 void _sc_AbortGame(const char *text) {
@@ -1004,23 +1004,23 @@ void SetGraphicalVariable(const char *varName, int p_value) {
 }
 
 int WaitImpl(int skip_type, int nloops) {
-	play.wait_counter = nloops;
-	play.wait_skipped_by = SKIP_AUTOTIMER; // we set timer flag by default to simplify that case
-	play.wait_skipped_by_data = 0;
-	play.key_skip_wait = skip_type;
+	_GP(play).wait_counter = nloops;
+	_GP(play).wait_skipped_by = SKIP_AUTOTIMER; // we set timer flag by default to simplify that case
+	_GP(play).wait_skipped_by_data = 0;
+	_GP(play).key_skip_wait = skip_type;
 
-	GameLoopUntilValueIsZero(&play.wait_counter);
+	GameLoopUntilValueIsZero(&_GP(play).wait_counter);
 
 	if (_GP(game).options[OPT_BASESCRIPTAPI] < kScriptAPI_v351) {
 		// < 3.5.1 return 1 is skipped by user input, otherwise 0
-		return (play.wait_skipped_by & (SKIP_KEYPRESS | SKIP_MOUSECLICK)) != 0 ? 1 : 0;
+		return (_GP(play).wait_skipped_by & (SKIP_KEYPRESS | SKIP_MOUSECLICK)) != 0 ? 1 : 0;
 	}
 	// >= 3.5.1 return positive keycode, negative mouse button code, or 0 as time-out
-	switch (play.wait_skipped_by) {
+	switch (_GP(play).wait_skipped_by) {
 	case SKIP_KEYPRESS:
-		return play.wait_skipped_by_data;
+		return _GP(play).wait_skipped_by_data;
 	case SKIP_MOUSECLICK:
-		return -(play.wait_skipped_by_data + 1); // convert to 1-based code and negate
+		return -(_GP(play).wait_skipped_by_data + 1); // convert to 1-based code and negate
 	default:
 		return 0;
 	}
@@ -1043,7 +1043,7 @@ int WaitMouseKey(int nloops) {
 }
 
 void SkipWait() {
-	play.wait_counter = 0;
+	_GP(play).wait_counter = 0;
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/global_gui.cpp b/engines/ags/engine/ac/global_gui.cpp
index 56a53bc3be..76db50f33e 100644
--- a/engines/ags/engine/ac/global_gui.cpp
+++ b/engines/ags/engine/ac/global_gui.cpp
@@ -213,22 +213,22 @@ void SetGUIBackgroundPic(int guin, int slotn) {
 }
 
 void DisableInterface() {
-	play.disabled_user_interface++;
+	_GP(play).disabled_user_interface++;
 	guis_need_update = 1;
 	set_mouse_cursor(CURS_WAIT);
 }
 
 void EnableInterface() {
 	guis_need_update = 1;
-	play.disabled_user_interface--;
-	if (play.disabled_user_interface < 1) {
-		play.disabled_user_interface = 0;
+	_GP(play).disabled_user_interface--;
+	if (_GP(play).disabled_user_interface < 1) {
+		_GP(play).disabled_user_interface = 0;
 		set_default_cursor();
 	}
 }
 // Returns 1 if user interface is enabled, 0 if disabled
 int IsInterfaceEnabled() {
-	return (play.disabled_user_interface > 0) ? 0 : 1;
+	return (_GP(play).disabled_user_interface > 0) ? 0 : 1;
 }
 
 int GetGUIObjectAt(int xx, int yy) {
@@ -244,7 +244,7 @@ int GetGUIAt(int xx, int yy) {
 
 	int aa, ll;
 	for (ll = _GP(game).numgui - 1; ll >= 0; ll--) {
-		aa = play.gui_draw_order[ll];
+		aa = _GP(play).gui_draw_order[ll];
 		if (guis[aa].IsInteractableAt(xx, yy))
 			return aa;
 	}
@@ -259,8 +259,8 @@ void SetTextWindowGUI(int guinum) {
 	else if (!guis[guinum].IsTextWindow())
 		quit("!SetTextWindowGUI: specified GUI is not a text window");
 
-	if (play.speech_textwindow_gui == _GP(game).options[OPT_TWCUSTOM])
-		play.speech_textwindow_gui = guinum;
+	if (_GP(play).speech_textwindow_gui == _GP(game).options[OPT_TWCUSTOM])
+		_GP(play).speech_textwindow_gui = guinum;
 	_GP(game).options[OPT_TWCUSTOM] = guinum;
 }
 
diff --git a/engines/ags/engine/ac/global_hotspot.cpp b/engines/ags/engine/ac/global_hotspot.cpp
index 11cc70377e..891ecea208 100644
--- a/engines/ags/engine/ac/global_hotspot.cpp
+++ b/engines/ags/engine/ac/global_hotspot.cpp
@@ -83,7 +83,7 @@ int GetHotspotPointY(int hotspot) {
 }
 
 int GetHotspotIDAtScreen(int scrx, int scry) {
-	VpPoint vpt = play.ScreenToRoomDivDown(scrx, scry);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(scrx, scry);
 	if (vpt.second < 0) return 0;
 	return get_hotspot_at(vpt.first.X, vpt.first.Y);
 }
@@ -109,12 +109,12 @@ void RunHotspotInteraction(int hotspothere, int mood) {
 	else if (mood == MODE_USE) {
 		passon = 3;
 		cdata = playerchar->activeinv;
-		play.usedinv = cdata;
+		_GP(play).usedinv = cdata;
 	}
 
 	if ((_GP(game).options[OPT_WALKONLOOK] == 0) & (mood == MODE_LOOK));
-	else if (play.auto_use_walkto_points == 0);
-	else if ((mood != MODE_WALK) && (play.check_interaction_only == 0))
+	else if (_GP(play).auto_use_walkto_points == 0);
+	else if ((mood != MODE_WALK) && (_GP(play).check_interaction_only == 0))
 		MoveCharacterToHotspot(_GP(game).playercharacter, hotspothere);
 
 	// can't use the setevent functions because this ProcessClick is only
diff --git a/engines/ags/engine/ac/global_inventoryitem.cpp b/engines/ags/engine/ac/global_inventoryitem.cpp
index 3485c6691d..342e0673b9 100644
--- a/engines/ags/engine/ac/global_inventoryitem.cpp
+++ b/engines/ags/engine/ac/global_inventoryitem.cpp
@@ -40,7 +40,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 
 
-extern GameState play;
+
 extern int mouse_ifacebut_xoffs, mouse_ifacebut_yoffs;
 extern const char *evblockbasename;
 extern int evblocknum;
@@ -118,7 +118,7 @@ void RunInventoryInteraction(int iit, int modd) {
 	else if (modd == MODE_HAND)
 		run_event_block_inv(iit, 1);
 	else if (modd == MODE_USE) {
-		play.usedinv = playerchar->activeinv;
+		_GP(play).usedinv = playerchar->activeinv;
 		run_event_block_inv(iit, 3);
 	} else if (modd == MODE_TALK)
 		run_event_block_inv(iit, 2);
@@ -130,12 +130,12 @@ int IsInventoryInteractionAvailable(int item, int mood) {
 	if ((item < 0) || (item >= MAX_INV))
 		quit("!IsInventoryInteractionAvailable: invalid inventory number");
 
-	play.check_interaction_only = 1;
+	_GP(play).check_interaction_only = 1;
 
 	RunInventoryInteraction(item, mood);
 
-	int ciwas = play.check_interaction_only;
-	play.check_interaction_only = 0;
+	int ciwas = _GP(play).check_interaction_only;
+	_GP(play).check_interaction_only = 0;
 
 	if (ciwas == 2)
 		return 1;
@@ -144,11 +144,11 @@ int IsInventoryInteractionAvailable(int item, int mood) {
 }
 
 int GetInvProperty(int item, const char *property) {
-	return get_int_property(_GP(game).invProps[item], play.invProps[item], property);
+	return get_int_property(_GP(game).invProps[item], _GP(play).invProps[item], property);
 }
 
 void GetInvPropertyText(int item, const char *property, char *bufer) {
-	get_text_property(_GP(game).invProps[item], play.invProps[item], property, bufer);
+	get_text_property(_GP(game).invProps[item], _GP(play).invProps[item], property, bufer);
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/global_invwindow.cpp b/engines/ags/engine/ac/global_invwindow.cpp
index ac2a68a8ea..723c794534 100644
--- a/engines/ags/engine/ac/global_invwindow.cpp
+++ b/engines/ags/engine/ac/global_invwindow.cpp
@@ -26,20 +26,21 @@
 #include "ags/engine/ac/properties.h"
 #include "ags/shared/gui/guiinv.h"
 #include "ags/engine/script/executingscript.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
 extern ExecutingScript *curscript;
-extern GameState play;
+
 
 void sc_invscreen() {
 	curscript->queue_action(ePSAInvScreen, 0, "InventoryScreen");
 }
 
 void SetInvDimensions(int ww, int hh) {
-	play.inv_item_wid = ww;
-	play.inv_item_hit = hh;
-	play.inv_numdisp = 0;
+	_GP(play).inv_item_wid = ww;
+	_GP(play).inv_item_hit = hh;
+	_GP(play).inv_numdisp = 0;
 	// backwards compatibility
 	for (int i = 0; i < numguiinv; i++) {
 		guiinv[i].ItemWidth = ww;
diff --git a/engines/ags/engine/ac/global_mouse.cpp b/engines/ags/engine/ac/global_mouse.cpp
index 7c9471b501..d72f2345c2 100644
--- a/engines/ags/engine/ac/global_mouse.cpp
+++ b/engines/ags/engine/ac/global_mouse.cpp
@@ -22,17 +22,16 @@
 
 #include "ags/engine/ac/global_mouse.h"
 #include "ags/engine/ac/gamestate.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
-extern GameState play;
-
 void HideMouseCursor() {
-	play.mouse_cursor_hidden = 1;
+	_GP(play).mouse_cursor_hidden = 1;
 }
 
 void ShowMouseCursor() {
-	play.mouse_cursor_hidden = 0;
+	_GP(play).mouse_cursor_hidden = 0;
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/global_object.cpp b/engines/ags/engine/ac/global_object.cpp
index 590bf7789f..cbd3ec7736 100644
--- a/engines/ags/engine/ac/global_object.cpp
+++ b/engines/ags/engine/ac/global_object.cpp
@@ -71,7 +71,7 @@ int obj_lowest_yp;
 
 int GetObjectIDAtScreen(int scrx, int scry) {
 	// translate screen co-ordinates to room co-ordinates
-	VpPoint vpt = play.ScreenToRoomDivDown(scrx, scry);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(scrx, scry);
 	if (vpt.second < 0)
 		return -1;
 	return GetObjectIDAtRoom(vpt.first.X, vpt.first.Y);
@@ -267,8 +267,8 @@ void MergeObject(int obn) {
 	construct_object_gfx(obn, nullptr, &theHeight, true);
 
 	//Bitmap *oldabuf = graphics->bmp;
-	//abuf = thisroom.BgFrames.Graphic[play.bg_frame];
-	PBitmap bg_frame = thisroom.BgFrames[play.bg_frame].Graphic;
+	//abuf = thisroom.BgFrames.Graphic[_GP(play).bg_frame];
+	PBitmap bg_frame = thisroom.BgFrames[_GP(play).bg_frame].Graphic;
 	if (bg_frame->GetColorDepth() != actsps[obn]->GetColorDepth())
 		quit("!MergeObject: unable to merge object due to color depth differences");
 
@@ -415,7 +415,7 @@ void RunObjectInteraction(int aa, int mood) {
 	else if (mood == MODE_USE) {
 		passon = 3;
 		cdata = playerchar->activeinv;
-		play.usedinv = cdata;
+		_GP(play).usedinv = cdata;
 	}
 	evblockbasename = "object%d";
 	evblocknum = aa;
diff --git a/engines/ags/engine/ac/global_overlay.cpp b/engines/ags/engine/ac/global_overlay.cpp
index 4b9cc06b63..8914dea296 100644
--- a/engines/ags/engine/ac/global_overlay.cpp
+++ b/engines/ags/engine/ac/global_overlay.cpp
@@ -59,8 +59,8 @@ int CreateGraphicOverlay(int xx, int yy, int slott, int trans) {
 }
 
 int CreateTextOverlayCore(int xx, int yy, int wii, int fontid, int text_color, const char *text, int disp_type, int allowShrink) {
-	if (wii < 8) wii = play.GetUIViewport().GetWidth() / 2;
-	if (xx < 0) xx = play.GetUIViewport().GetWidth() / 2 - wii / 2;
+	if (wii < 8) wii = _GP(play).GetUIViewport().GetWidth() / 2;
+	if (xx < 0) xx = _GP(play).GetUIViewport().GetWidth() / 2 - wii / 2;
 	if (text_color == 0) text_color = 16;
 	return _display_main(xx, yy, wii, text, disp_type, fontid, -text_color, 0, allowShrink, false);
 }
diff --git a/engines/ags/engine/ac/global_palette.cpp b/engines/ags/engine/ac/global_palette.cpp
index 2d82202521..313e09f40c 100644
--- a/engines/ags/engine/ac/global_palette.cpp
+++ b/engines/ags/engine/ac/global_palette.cpp
@@ -29,7 +29,7 @@
 
 namespace AGS3 {
 
-extern GameState play;
+
 extern color palette[256];
 
 void CyclePalette(int strt, int eend) {
@@ -70,7 +70,7 @@ void UpdatePalette() {
 	if (_GP(game).color_depth > 1)
 		invalidate_screen();
 
-	if (!play.fast_forward)
+	if (!_GP(play).fast_forward)
 		setpal();
 }
 
diff --git a/engines/ags/engine/ac/global_parser.cpp b/engines/ags/engine/ac/global_parser.cpp
index 6776619898..e21d4794e2 100644
--- a/engines/ags/engine/ac/global_parser.cpp
+++ b/engines/ags/engine/ac/global_parser.cpp
@@ -24,15 +24,14 @@
 #include "ags/shared/ac/common.h"
 #include "ags/engine/ac/gamestate.h"
 #include "ags/engine/ac/string.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
-extern GameState play;
-
 int SaidUnknownWord(char *buffer) {
 	VALIDATE_STRING(buffer);
-	strcpy(buffer, play.bad_parsed_word);
-	if (play.bad_parsed_word[0] == 0)
+	strcpy(buffer, _GP(play).bad_parsed_word);
+	if (_GP(play).bad_parsed_word[0] == 0)
 		return 0;
 	return 1;
 }
diff --git a/engines/ags/engine/ac/global_region.cpp b/engines/ags/engine/ac/global_region.cpp
index 5ef84c06a9..4a33d1664b 100644
--- a/engines/ags/engine/ac/global_region.cpp
+++ b/engines/ags/engine/ac/global_region.cpp
@@ -30,6 +30,7 @@
 #include "ags/shared/game/roomstruct.h"
 #include "ags/shared/gfx/bitmap.h"
 #include "ags/engine/script/script.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -127,16 +128,16 @@ void DisableGroundLevelAreas(int alsoEffects) {
 	if ((alsoEffects < 0) || (alsoEffects > 1))
 		quit("!DisableGroundLevelAreas: invalid parameter: must be 0 or 1");
 
-	play.ground_level_areas_disabled = GLED_INTERACTION;
+	_GP(play).ground_level_areas_disabled = GLED_INTERACTION;
 
 	if (alsoEffects)
-		play.ground_level_areas_disabled |= GLED_EFFECTS;
+		_GP(play).ground_level_areas_disabled |= GLED_EFFECTS;
 
 	debug_script_log("Ground-level areas disabled");
 }
 
 void EnableGroundLevelAreas() {
-	play.ground_level_areas_disabled = 0;
+	_GP(play).ground_level_areas_disabled = 0;
 
 	debug_script_log("Ground-level areas re-enabled");
 }
diff --git a/engines/ags/engine/ac/global_room.cpp b/engines/ags/engine/ac/global_room.cpp
index 33e34c5766..7e489114f6 100644
--- a/engines/ags/engine/ac/global_room.cpp
+++ b/engines/ags/engine/ac/global_room.cpp
@@ -44,7 +44,7 @@ namespace AGS3 {
 
 using namespace Shared;
 
-extern GameState play;
+
 
 extern RoomStatus *croom;
 extern CharacterInfo *playerchar;
@@ -65,20 +65,20 @@ void SetAmbientTint(int red, int green, int blue, int opacity, int luminance) {
 
 	debug_script_log("Set ambient tint RGB(%d,%d,%d) %d%%", red, green, blue, opacity);
 
-	play.rtint_enabled = opacity > 0;
-	play.rtint_red = red;
-	play.rtint_green = green;
-	play.rtint_blue = blue;
-	play.rtint_level = opacity;
-	play.rtint_light = (luminance * 25) / 10;
+	_GP(play).rtint_enabled = opacity > 0;
+	_GP(play).rtint_red = red;
+	_GP(play).rtint_green = green;
+	_GP(play).rtint_blue = blue;
+	_GP(play).rtint_level = opacity;
+	_GP(play).rtint_light = (luminance * 25) / 10;
 }
 
 void SetAmbientLightLevel(int light_level) {
 	light_level = Math::Clamp(light_level, -100, 100);
 
-	play.rtint_enabled = light_level != 0;
-	play.rtint_level = 0;
-	play.rtint_light = light_level;
+	_GP(play).rtint_enabled = light_level != 0;
+	_GP(play).rtint_level = 0;
+	_GP(play).rtint_light = light_level;
 }
 
 extern ScriptPosition last_in_dialog_request_script_pos;
@@ -98,9 +98,9 @@ void NewRoom(int nrnum) {
 
 	can_run_delayed_command();
 
-	if (play.stop_dialog_at_end != DIALOG_NONE) {
-		if (play.stop_dialog_at_end == DIALOG_RUNNING)
-			play.stop_dialog_at_end = DIALOG_NEWROOM + nrnum;
+	if (_GP(play).stop_dialog_at_end != DIALOG_NONE) {
+		if (_GP(play).stop_dialog_at_end == DIALOG_RUNNING)
+			_GP(play).stop_dialog_at_end = DIALOG_NEWROOM + nrnum;
 		else {
 			quitprintf("!NewRoom: two NewRoom/RunDialog/StopDialog requests within dialog; last was called in \"%s\", line %d",
 				last_in_dialog_request_script_pos.Section.GetCStr(), last_in_dialog_request_script_pos.Line);
@@ -182,7 +182,7 @@ void CallRoomScript(int value) {
 	if (!inside_script)
 		quit("!CallRoomScript: not inside a script???");
 
-	play.roomscript_finished = 0;
+	_GP(play).roomscript_finished = 0;
 	RuntimeScriptValue rval_null;
 	curscript->run_another("on_call", kScInstRoom, 1, RuntimeScriptValue().SetInt32(value), rval_null);
 }
@@ -205,23 +205,23 @@ void SetBackgroundFrame(int frnum) {
 	if ((frnum < -1) || (frnum != -1 && (size_t)frnum >= thisroom.BgFrameCount))
 		quit("!SetBackgrondFrame: invalid frame number specified");
 	if (frnum < 0) {
-		play.bg_frame_locked = 0;
+		_GP(play).bg_frame_locked = 0;
 		return;
 	}
 
-	play.bg_frame_locked = 1;
+	_GP(play).bg_frame_locked = 1;
 
-	if (frnum == play.bg_frame) {
+	if (frnum == _GP(play).bg_frame) {
 		// already on this frame, do nothing
 		return;
 	}
 
-	play.bg_frame = frnum;
+	_GP(play).bg_frame = frnum;
 	on_background_frame_change();
 }
 
 int GetBackgroundFrame() {
-	return play.bg_frame;
+	return _GP(play).bg_frame;
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/global_screen.cpp b/engines/ags/engine/ac/global_screen.cpp
index 2dff91e985..20383bf4ae 100644
--- a/engines/ags/engine/ac/global_screen.cpp
+++ b/engines/ags/engine/ac/global_screen.cpp
@@ -42,7 +42,7 @@ using namespace AGS::Shared;
 using namespace AGS::Engine;
 
 extern GameSetup usetup;
-extern GameState play;
+
 
 extern RoomStruct thisroom;
 extern IGraphicsDriver *gfxDriver;
@@ -52,13 +52,13 @@ extern unsigned int loopcounter;
 
 void FlipScreen(int amount) {
 	if ((amount < 0) | (amount > 3)) quit("!FlipScreen: invalid argument (0-3)");
-	play.screen_flipped = amount;
+	_GP(play).screen_flipped = amount;
 }
 
 void ShakeScreen(int severe) {
 	EndSkippingUntilCharStops();
 
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return;
 
 	severe = data_to_game_coord(severe);
@@ -67,10 +67,10 @@ void ShakeScreen(int severe) {
 	// TODO: rely on game speed setting? and/or provide frequency and duration args
 	// TODO: unify blocking and non-blocking shake update
 
-	play.shakesc_length = 10;
-	play.shakesc_delay = 2;
-	play.shakesc_amount = severe;
-	play.mouse_cursor_hidden++;
+	_GP(play).shakesc_length = 10;
+	_GP(play).shakesc_delay = 2;
+	_GP(play).shakesc_amount = severe;
+	_GP(play).mouse_cursor_hidden++;
 
 	if (gfxDriver->RequiresFullRedrawEachFrame()) {
 		for (int hh = 0; hh < 40; hh++) {
@@ -88,7 +88,7 @@ void ShakeScreen(int severe) {
 		for (int hh = 0; hh < 40; hh++) {
 			platform->Delay(50);
 			const int yoff = hh % 2 == 0 ? 0 : severe;
-			play.shake_screen_yoff = yoff;
+			_GP(play).shake_screen_yoff = yoff;
 			render_to_screen();
 			update_polled_stuff_if_runtime();
 		}
@@ -96,10 +96,10 @@ void ShakeScreen(int severe) {
 		render_to_screen();
 	}
 
-	play.mouse_cursor_hidden--;
-	play.shakesc_length = 0;
-	play.shakesc_delay = 0;
-	play.shakesc_amount = 0;
+	_GP(play).mouse_cursor_hidden--;
+	_GP(play).shakesc_length = 0;
+	_GP(play).shakesc_delay = 0;
+	_GP(play).shakesc_amount = 0;
 }
 
 void ShakeScreenBackground(int delay, int amount, int length) {
@@ -108,14 +108,14 @@ void ShakeScreenBackground(int delay, int amount, int length) {
 
 	amount = data_to_game_coord(amount);
 
-	if (amount < play.shakesc_amount) {
+	if (amount < _GP(play).shakesc_amount) {
 		// from a bigger to smaller shake, clear up the borders
 		clear_letterbox_borders();
 	}
 
-	play.shakesc_amount = amount;
-	play.shakesc_delay = delay;
-	play.shakesc_length = length;
+	_GP(play).shakesc_amount = amount;
+	_GP(play).shakesc_delay = delay;
+	_GP(play).shakesc_length = length;
 }
 
 void TintScreen(int red, int grn, int blu) {
@@ -125,33 +125,33 @@ void TintScreen(int red, int grn, int blu) {
 	invalidate_screen();
 
 	if ((red == 0) && (grn == 0) && (blu == 0)) {
-		play.screen_tint = -1;
+		_GP(play).screen_tint = -1;
 		return;
 	}
 	red = (red * 25) / 10;
 	grn = (grn * 25) / 10;
 	blu = (blu * 25) / 10;
-	play.screen_tint = red + (grn << 8) + (blu << 16);
+	_GP(play).screen_tint = red + (grn << 8) + (blu << 16);
 }
 
 void my_fade_out(int spdd) {
 	EndSkippingUntilCharStops();
 
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return;
 
-	if (play.screen_is_faded_out == 0)
-		gfxDriver->FadeOut(spdd, play.fade_to_red, play.fade_to_green, play.fade_to_blue);
+	if (_GP(play).screen_is_faded_out == 0)
+		gfxDriver->FadeOut(spdd, _GP(play).fade_to_red, _GP(play).fade_to_green, _GP(play).fade_to_blue);
 
 	if (_GP(game).color_depth > 1)
-		play.screen_is_faded_out = 1;
+		_GP(play).screen_is_faded_out = 1;
 }
 
 void SetScreenTransition(int newtrans) {
 	if ((newtrans < 0) || (newtrans > FADE_LAST))
 		quit("!SetScreenTransition: invalid transition type");
 
-	play.fade_effect = newtrans;
+	_GP(play).fade_effect = newtrans;
 
 	debug_script_log("Screen transition changed");
 }
@@ -160,7 +160,7 @@ void SetNextScreenTransition(int newtrans) {
 	if ((newtrans < 0) || (newtrans > FADE_LAST))
 		quit("!SetNextScreenTransition: invalid transition type");
 
-	play.next_screen_transition = newtrans;
+	_GP(play).next_screen_transition = newtrans;
 
 	debug_script_log("SetNextScreenTransition engaged");
 }
@@ -170,15 +170,15 @@ void SetFadeColor(int red, int green, int blue) {
 		(blue < 0) || (blue > 255))
 		quit("!SetFadeColor: Red, Green and Blue must be 0-255");
 
-	play.fade_to_red = red;
-	play.fade_to_green = green;
-	play.fade_to_blue = blue;
+	_GP(play).fade_to_red = red;
+	_GP(play).fade_to_green = green;
+	_GP(play).fade_to_blue = blue;
 }
 
 void FadeIn(int sppd) {
 	EndSkippingUntilCharStops();
 
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return;
 
 	my_fade_in(palette, sppd);
diff --git a/engines/ags/engine/ac/global_timer.cpp b/engines/ags/engine/ac/global_timer.cpp
index e06a37eb11..84a0e463e5 100644
--- a/engines/ags/engine/ac/global_timer.cpp
+++ b/engines/ags/engine/ac/global_timer.cpp
@@ -24,23 +24,21 @@
 #include "ags/engine/ac/runtime_defines.h"
 #include "ags/shared/ac/common.h"
 #include "ags/engine/ac/gamestate.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
-extern GameState play;
-
-
 void script_SetTimer(int tnum, int timeout) {
 	if ((tnum < 1) || (tnum >= MAX_TIMERS))
 		quit("!StartTimer: invalid timer number");
-	play.script_timers[tnum] = timeout;
+	_GP(play).script_timers[tnum] = timeout;
 }
 
 int IsTimerExpired(int tnum) {
 	if ((tnum < 1) || (tnum >= MAX_TIMERS))
 		quit("!IsTimerExpired: invalid timer number");
-	if (play.script_timers[tnum] == 1) {
-		play.script_timers[tnum] = 0;
+	if (_GP(play).script_timers[tnum] == 1) {
+		_GP(play).script_timers[tnum] = 0;
 		return 1;
 	}
 	return 0;
diff --git a/engines/ags/engine/ac/global_translation.cpp b/engines/ags/engine/ac/global_translation.cpp
index 6fe8bb586a..33f7322094 100644
--- a/engines/ags/engine/ac/global_translation.cpp
+++ b/engines/ags/engine/ac/global_translation.cpp
@@ -37,7 +37,7 @@ namespace AGS3 {
 
 using namespace AGS::Shared::Memory;
 
-extern GameState play;
+
 extern AGSPlatformDriver *platform;
 extern TreeMap *transtree;
 extern char transFileName[MAX_PATH];
diff --git a/engines/ags/engine/ac/global_video.cpp b/engines/ags/engine/ac/global_video.cpp
index a711418210..2217f104cd 100644
--- a/engines/ags/engine/ac/global_video.cpp
+++ b/engines/ags/engine/ac/global_video.cpp
@@ -33,13 +33,14 @@
 #include "ags/engine/media/audio/audio_system.h"
 #include "ags/engine/platform/base/agsplatformdriver.h"
 #include "ags/shared/util/string_compat.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
 void scrPlayVideo(const char *name, int skip, int flags) {
 	EndSkippingUntilCharStops();
 
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return;
 	if (debug_flags & DBG_NOVIDEO)
 		return;
@@ -54,7 +55,7 @@ void scrPlayVideo(const char *name, int skip, int flags) {
 }
 
 void pause_sound_if_necessary_and_play_video(const char *name, int skip, int flags) {
-	int musplaying = play.cur_music_number, i;
+	int musplaying = _GP(play).cur_music_number, i;
 	int ambientWas[MAX_SOUND_CHANNELS];
 	for (i = 1; i < MAX_SOUND_CHANNELS; i++)
 		ambientWas[i] = ambient[i].channel;
diff --git a/engines/ags/engine/ac/global_viewport.cpp b/engines/ags/engine/ac/global_viewport.cpp
index 776272d2fd..d3ad8dfc4e 100644
--- a/engines/ags/engine/ac/global_viewport.cpp
+++ b/engines/ags/engine/ac/global_viewport.cpp
@@ -23,22 +23,23 @@
 #include "ags/engine/ac/global_viewport.h"
 #include "ags/engine/ac/draw.h"
 #include "ags/engine/debugging/debug_log.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
 void SetViewport(int offsx, int offsy) {
 	offsx = data_to_game_coord(offsx);
 	offsy = data_to_game_coord(offsy);
-	play.GetRoomCamera(0)->LockAt(offsx, offsy);
+	_GP(play).GetRoomCamera(0)->LockAt(offsx, offsy);
 }
 void ReleaseViewport() {
-	play.GetRoomCamera(0)->Release();
+	_GP(play).GetRoomCamera(0)->Release();
 }
 int GetViewportX() {
-	return game_to_data_coord(play.GetRoomCamera(0)->GetRect().Left);
+	return game_to_data_coord(_GP(play).GetRoomCamera(0)->GetRect().Left);
 }
 int GetViewportY() {
-	return game_to_data_coord(play.GetRoomCamera(0)->GetRect().Top);
+	return game_to_data_coord(_GP(play).GetRoomCamera(0)->GetRect().Top);
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/ac/global_walkablearea.cpp b/engines/ags/engine/ac/global_walkablearea.cpp
index 3a62a1627d..0a5f2239ea 100644
--- a/engines/ags/engine/ac/global_walkablearea.cpp
+++ b/engines/ags/engine/ac/global_walkablearea.cpp
@@ -27,6 +27,7 @@
 #include "ags/engine/ac/walkablearea.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/shared/game/roomstruct.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -69,7 +70,7 @@ void SetAreaScaling(int area, int min, int max) {
 void RemoveWalkableArea(int areanum) {
 	if ((areanum < 1) | (areanum > 15))
 		quit("!RemoveWalkableArea: invalid area number specified (1-15).");
-	play.walkable_areas_on[areanum] = 0;
+	_GP(play).walkable_areas_on[areanum] = 0;
 	redo_walkable_areas();
 	debug_script_log("Walkable area %d removed", areanum);
 }
@@ -77,13 +78,13 @@ void RemoveWalkableArea(int areanum) {
 void RestoreWalkableArea(int areanum) {
 	if ((areanum < 1) | (areanum > 15))
 		quit("!RestoreWalkableArea: invalid area number specified (1-15).");
-	play.walkable_areas_on[areanum] = 1;
+	_GP(play).walkable_areas_on[areanum] = 1;
 	redo_walkable_areas();
 	debug_script_log("Walkable area %d restored", areanum);
 }
 
 int GetWalkableAreaAtScreen(int x, int y) {
-	VpPoint vpt = play.ScreenToRoomDivDown(x, y);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(x, y);
 	if (vpt.second < 0)
 		return 0;
 	return GetWalkableAreaAtRoom(vpt.first.X, vpt.first.Y);
diff --git a/engines/ags/engine/ac/gui.cpp b/engines/ags/engine/ac/gui.cpp
index 3b775f7499..d59fafada6 100644
--- a/engines/ags/engine/ac/gui.cpp
+++ b/engines/ags/engine/ac/gui.cpp
@@ -223,8 +223,8 @@ int GUI_GetTransparency(ScriptGUI *tehgui) {
 
 void GUI_Centre(ScriptGUI *sgui) {
 	GUIMain *tehgui = &guis[sgui->id];
-	tehgui->X = play.GetUIViewport().GetWidth() / 2 - tehgui->Width / 2;
-	tehgui->Y = play.GetUIViewport().GetHeight() / 2 - tehgui->Height / 2;
+	tehgui->X = _GP(play).GetUIViewport().GetWidth() / 2 - tehgui->Width / 2;
+	tehgui->Y = _GP(play).GetUIViewport().GetHeight() / 2 - tehgui->Height / 2;
 }
 
 void GUI_SetBackgroundGraphic(ScriptGUI *tehgui, int slotn) {
@@ -410,13 +410,13 @@ void replace_macro_tokens(const char *text, String &fixed_text) {
 			macroname[idd] = 0;
 			tempo[0] = 0;
 			if (ags_stricmp(macroname, "score") == 0)
-				sprintf(tempo, "%d", play.score);
+				sprintf(tempo, "%d", _GP(play).score);
 			else if (ags_stricmp(macroname, "totalscore") == 0)
 				sprintf(tempo, "%d", MAXSCORE);
 			else if (ags_stricmp(macroname, "scoretext") == 0)
-				sprintf(tempo, "%d of %d", play.score, MAXSCORE);
+				sprintf(tempo, "%d of %d", _GP(play).score, MAXSCORE);
 			else if (ags_stricmp(macroname, "gamename") == 0)
-				strcpy(tempo, play.game_name);
+				strcpy(tempo, _GP(play).game_name);
 			else if (ags_stricmp(macroname, "overhotspot") == 0) {
 				// While game is in Wait mode, no overhotspot text
 				if (!IsInterfaceEnabled())
@@ -447,15 +447,15 @@ void update_gui_zorder() {
 		// find the right place in the draw order array
 		int insertAt = numdone;
 		for (b = 0; b < numdone; b++) {
-			if (guis[a].ZOrder < guis[play.gui_draw_order[b]].ZOrder) {
+			if (guis[a].ZOrder < guis[_GP(play).gui_draw_order[b]].ZOrder) {
 				insertAt = b;
 				break;
 			}
 		}
 		// insert the new item
 		for (b = numdone - 1; b >= insertAt; b--)
-			play.gui_draw_order[b + 1] = play.gui_draw_order[b];
-		play.gui_draw_order[insertAt] = a;
+			_GP(play).gui_draw_order[b + 1] = _GP(play).gui_draw_order[b];
+		_GP(play).gui_draw_order[insertAt] = a;
 		numdone++;
 	}
 
@@ -597,16 +597,16 @@ int gui_on_mouse_move() {
 		// Also work out the mouse-over GUI while we're at it
 		int ll;
 		for (ll = 0; ll < _GP(game).numgui; ll++) {
-			const int guin = play.gui_draw_order[ll];
+			const int guin = _GP(play).gui_draw_order[ll];
 			if (guis[guin].IsInteractableAt(_G(mousex), _G(mousey))) mouse_over_gui = guin;
 
 			if (guis[guin].PopupStyle != kGUIPopupMouseY) continue;
 			if (is_complete_overlay > 0) break; // interfaces disabled
-			//    if (play.disabled_user_interface>0) break;
+			//    if (_GP(play).disabled_user_interface>0) break;
 			if (ifacepopped == guin) continue;
 			if (!guis[guin].IsVisible()) continue;
 			// Don't allow it to be popped up while skipping cutscene
-			if (play.fast_forward) continue;
+			if (_GP(play).fast_forward) continue;
 
 			if (_G(mousey) < guis[guin].PopupAtMouseY) {
 				set_mouse_cursor(CURS_ARROW);
@@ -651,7 +651,7 @@ void gui_on_mouse_up(const int wasongui, const int wasbutdown) {
 			int iit = offset_over_inv((GUIInvWindow *)guio);
 			if (iit >= 0) {
 				evblocknum = iit;
-				play.used_inv_on = iit;
+				_GP(play).used_inv_on = iit;
 				if (_GP(game).options[OPT_HANDLEINVCLICKS]) {
 					// Let the script handle the click
 					// LEFTINV is 5, RIGHTINV is 6
diff --git a/engines/ags/engine/ac/guiinv.cpp b/engines/ags/engine/ac/guiinv.cpp
index fd8b78b864..6df6170d56 100644
--- a/engines/ags/engine/ac/guiinv.cpp
+++ b/engines/ags/engine/ac/guiinv.cpp
@@ -33,7 +33,7 @@
 namespace AGS3 {
 
 extern int gui_disabled_style;
-extern GameState play;
+
 extern CharacterExtras *charextra;
 
 
@@ -53,15 +53,15 @@ void GUIInvWindow::Draw(Bitmap *ds) {
 		return;
 
 	// backwards compatibility
-	play.inv_numinline = ColCount;
-	play.inv_numdisp = RowCount * ColCount;
-	play.obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
+	_GP(play).inv_numinline = ColCount;
+	_GP(play).inv_numdisp = RowCount * ColCount;
+	_GP(play).obsolete_inv_numorder = charextra[_GP(game).playercharacter].invorder_count;
 	// if the user changes top_inv_item, switch into backwards
 	// compatibiltiy mode
-	if (play.inv_top)
-		play.inv_backwards_compatibility = 1;
-	if (play.inv_backwards_compatibility)
-		TopItem = play.inv_top;
+	if (_GP(play).inv_top)
+		_GP(play).inv_backwards_compatibility = 1;
+	if (_GP(play).inv_backwards_compatibility)
+		TopItem = _GP(play).inv_top;
 
 	// draw the items
 	const int leftmost_x = X;
@@ -85,7 +85,7 @@ void GUIInvWindow::Draw(Bitmap *ds) {
 
 	if (!enabled &&
 		gui_disabled_style == GUIDIS_GREYOUT &&
-		play.inventory_greys_out == 1) {
+		_GP(play).inventory_greys_out == 1) {
 		// darken the inventory when disabled
 		GUI::DrawDisabledEffect(ds, RectWH(X, Y, Width, Height));
 	}
diff --git a/engines/ags/engine/ac/hotspot.cpp b/engines/ags/engine/ac/hotspot.cpp
index 02f9189158..92f1168d01 100644
--- a/engines/ags/engine/ac/hotspot.cpp
+++ b/engines/ags/engine/ac/hotspot.cpp
@@ -32,11 +32,11 @@
 #include "ags/shared/game/roomstruct.h"
 #include "ags/shared/gfx/bitmap.h"
 #include "ags/engine/script/runtimescriptvalue.h"
-
 #include "ags/shared/debugging/out.h"
 #include "ags/engine/script/script_api.h"
 #include "ags/engine/script/script_runtime.h"
 #include "ags/engine/ac/dynobj/scriptstring.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -88,10 +88,10 @@ const char *Hotspot_GetName_New(ScriptHotspot *hss) {
 
 bool Hotspot_IsInteractionAvailable(ScriptHotspot *hhot, int mood) {
 
-	play.check_interaction_only = 1;
+	_GP(play).check_interaction_only = 1;
 	RunHotspotInteraction(hhot->id, mood);
-	int ciwas = play.check_interaction_only;
-	play.check_interaction_only = 0;
+	int ciwas = _GP(play).check_interaction_only;
+	_GP(play).check_interaction_only = 0;
 	return (ciwas == 2);
 }
 
diff --git a/engines/ags/engine/ac/inventoryitem.cpp b/engines/ags/engine/ac/inventoryitem.cpp
index 428e1ecdb0..78a3317a14 100644
--- a/engines/ags/engine/ac/inventoryitem.cpp
+++ b/engines/ags/engine/ac/inventoryitem.cpp
@@ -94,23 +94,23 @@ int InventoryItem_CheckInteractionAvailable(ScriptInvItem *iitem, int mood) {
 }
 
 int InventoryItem_GetProperty(ScriptInvItem *scii, const char *property) {
-	return get_int_property(_GP(game).invProps[scii->id], play.invProps[scii->id], property);
+	return get_int_property(_GP(game).invProps[scii->id], _GP(play).invProps[scii->id], property);
 }
 
 void InventoryItem_GetPropertyText(ScriptInvItem *scii, const char *property, char *bufer) {
-	get_text_property(_GP(game).invProps[scii->id], play.invProps[scii->id], property, bufer);
+	get_text_property(_GP(game).invProps[scii->id], _GP(play).invProps[scii->id], property, bufer);
 }
 
 const char *InventoryItem_GetTextProperty(ScriptInvItem *scii, const char *property) {
-	return get_text_property_dynamic_string(_GP(game).invProps[scii->id], play.invProps[scii->id], property);
+	return get_text_property_dynamic_string(_GP(game).invProps[scii->id], _GP(play).invProps[scii->id], property);
 }
 
 bool InventoryItem_SetProperty(ScriptInvItem *scii, const char *property, int value) {
-	return set_int_property(play.invProps[scii->id], property, value);
+	return set_int_property(_GP(play).invProps[scii->id], property, value);
 }
 
 bool InventoryItem_SetTextProperty(ScriptInvItem *scii, const char *property, const char *value) {
-	return set_text_property(play.invProps[scii->id], property, value);
+	return set_text_property(_GP(play).invProps[scii->id], property, value);
 }
 
 //=============================================================================
diff --git a/engines/ags/engine/ac/invwindow.cpp b/engines/ags/engine/ac/invwindow.cpp
index 45c1dd4f90..0b67bbc520 100644
--- a/engines/ags/engine/ac/invwindow.cpp
+++ b/engines/ags/engine/ac/invwindow.cpp
@@ -54,7 +54,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 
 
-extern GameState play;
+
 extern CharacterExtras *charextra;
 extern ScriptInvItem scrInv[MAX_INV];
 extern int mouse_ifacebut_xoffs, mouse_ifacebut_yoffs;
@@ -227,7 +227,7 @@ void InventoryScreen::Prepare() {
 	toret = -1;
 	top_item = 0;
 	num_visible_items = 0;
-	MAX_ITEMAREA_HEIGHT = ((play.GetUIViewport().GetHeight() - BUTTONAREAHEIGHT) - get_fixed_pixel_size(20));
+	MAX_ITEMAREA_HEIGHT = ((_GP(play).GetUIViewport().GetHeight() - BUTTONAREAHEIGHT) - get_fixed_pixel_size(20));
 	in_inv_screen++;
 	inv_screen_newroom = -1;
 
@@ -290,8 +290,8 @@ int InventoryScreen::Redraw() {
 
 	windowwid = widest * ICONSPERLINE + get_fixed_pixel_size(4);
 	if (windowwid < get_fixed_pixel_size(105)) windowwid = get_fixed_pixel_size(105);
-	windowxp = play.GetUIViewport().GetWidth() / 2 - windowwid / 2;
-	windowyp = play.GetUIViewport().GetHeight() / 2 - windowhit / 2;
+	windowxp = _GP(play).GetUIViewport().GetWidth() / 2 - windowwid / 2;
+	windowyp = _GP(play).GetUIViewport().GetHeight() / 2 - windowhit / 2;
 	buttonyp = windowhit - BUTTONAREAHEIGHT;
 	bartop = get_fixed_pixel_size(2);
 	barxp = get_fixed_pixel_size(2);
@@ -305,7 +305,7 @@ int InventoryScreen::Redraw() {
 }
 
 void InventoryScreen::Draw(Bitmap *ds) {
-	color_t draw_color = ds->GetCompatibleColor(play.sierra_inv_color);
+	color_t draw_color = ds->GetCompatibleColor(_GP(play).sierra_inv_color);
 	ds->FillRect(Rect(0, 0, windowwid, windowhit), draw_color);
 	draw_color = ds->GetCompatibleColor(0);
 	ds->FillRect(Rect(barxp, bartop, windowwid - get_fixed_pixel_size(2), buttonyp - 1), draw_color);
@@ -325,7 +325,7 @@ void InventoryScreen::Draw(Bitmap *ds) {
 	// Draw Up and Down buttons if required
 	Bitmap *arrowblock = BitmapHelper::CreateTransparentBitmap(ARROWBUTTONWID, ARROWBUTTONWID);
 	draw_color = arrowblock->GetCompatibleColor(0);
-	if (play.sierra_inv_color == 0)
+	if (_GP(play).sierra_inv_color == 0)
 		draw_color = ds->GetCompatibleColor(14);
 
 	arrowblock->DrawLine(Line(ARROWBUTTONWID / 2, 2, ARROWBUTTONWID - 2, 9), draw_color);
@@ -357,7 +357,7 @@ void InventoryScreen::RedrawOverItem(Bitmap *ds, int isonitem) {
 
 bool InventoryScreen::Run() {
 	int kgn;
-	if (run_service_key_controls(kgn) && !play.IsIgnoringInput()) {
+	if (run_service_key_controls(kgn) && !_GP(play).IsIgnoringInput()) {
 		return false; // end inventory screen loop
 	}
 
@@ -375,7 +375,7 @@ bool InventoryScreen::Run() {
 		isonitem = -1;
 
 	int mclick, mwheelz;
-	if (!run_service_mb_controls(mclick, mwheelz) || play.IsIgnoringInput()) {
+	if (!run_service_mb_controls(mclick, mwheelz) || _GP(play).IsIgnoringInput()) {
 		mclick = NONE;
 	}
 
@@ -386,7 +386,7 @@ bool InventoryScreen::Run() {
 			int clickedon = isonitem;
 			if (clickedon < 0) return true; // continue inventory screen loop
 			evblocknum = dii[clickedon].num;
-			play.used_inv_on = dii[clickedon].num;
+			_GP(play).used_inv_on = dii[clickedon].num;
 
 			if (cmode == MODE_LOOK) {
 				//ags_domouse(DOMOUSE_DISABLE);
@@ -398,7 +398,7 @@ bool InventoryScreen::Run() {
 				return break_code == 0;
 			} else if (cmode == MODE_USE) {
 				// use objects on each other
-				play.usedinv = toret;
+				_GP(play).usedinv = toret;
 
 				// set the activeinv so the script can check it
 				int activeinvwas = playerchar->activeinv;
@@ -425,11 +425,11 @@ bool InventoryScreen::Run() {
 				return break_code == 0;
 			}
 			toret = dii[clickedon].num;
-			//        int plusng=play.using; play.using=toret;
+			//        int plusng=_GP(play).using; _GP(play).using=toret;
 			update_inv_cursor(toret);
 			set_mouse_cursor(MODE_USE);
 			cmode = MODE_USE;
-			//        play.using=plusng;
+			//        _GP(play).using=plusng;
 			//        break;
 			return true; // continue inventory screen loop
 		} else {
diff --git a/engines/ags/engine/ac/listbox.cpp b/engines/ags/engine/ac/listbox.cpp
index 2e8a32b1e3..b81f656ad3 100644
--- a/engines/ags/engine/ac/listbox.cpp
+++ b/engines/ags/engine/ac/listbox.cpp
@@ -45,7 +45,7 @@ namespace AGS3 {
 
 using namespace AGS::Shared;
 
-extern GameState play;
+
 
 
 // *** LIST BOX FUNCTIONS
@@ -146,7 +146,7 @@ int ListBox_FillSaveGameList(GUIListBox *listbox) {
 
 	// update the global savegameindex[] array for backward compatibilty
 	for (nn = 0; nn < numsaves; nn++) {
-		play.filenumbers[nn] = listbox->SavedGameIndex[nn];
+		_GP(play).filenumbers[nn] = listbox->SavedGameIndex[nn];
 	}
 
 	guis_need_update = 1;
diff --git a/engines/ags/engine/ac/mouse.cpp b/engines/ags/engine/ac/mouse.cpp
index f66f5cc3d3..f90b30a5ef 100644
--- a/engines/ags/engine/ac/mouse.cpp
+++ b/engines/ags/engine/ac/mouse.cpp
@@ -53,7 +53,7 @@ using namespace AGS::Shared;
 using namespace AGS::Engine;
 
 
-extern GameState play;
+
 extern ScriptSystem scsystem;
 
 extern CharacterInfo *playerchar;
@@ -81,14 +81,14 @@ void Mouse_SetVisible(int isOn) {
 }
 
 int Mouse_GetVisible() {
-	if (play.mouse_cursor_hidden)
+	if (_GP(play).mouse_cursor_hidden)
 		return 0;
 	return 1;
 }
 
 void SetMouseBounds(int x1, int y1, int x2, int y2) {
-	int xmax = game_to_data_coord(play.GetMainViewport().GetWidth()) - 1;
-	int ymax = game_to_data_coord(play.GetMainViewport().GetHeight()) - 1;
+	int xmax = game_to_data_coord(_GP(play).GetMainViewport().GetWidth()) - 1;
+	int ymax = game_to_data_coord(_GP(play).GetMainViewport().GetHeight()) - 1;
 	if ((x1 == 0) && (y1 == 0) && (x2 == 0) && (y2 == 0)) {
 		x2 = xmax;
 		y2 = ymax;
@@ -106,10 +106,10 @@ void SetMouseBounds(int x1, int y1, int x2, int y2) {
 	data_to_game_coords(&x1, &y1);
 	data_to_game_round_up(&x2, &y2);
 
-	play.mboundx1 = x1;
-	play.mboundx2 = x2;
-	play.mboundy1 = y1;
-	play.mboundy2 = y2;
+	_GP(play).mboundx1 = x1;
+	_GP(play).mboundx2 = x2;
+	_GP(play).mboundy1 = y1;
+	_GP(play).mboundy2 = y2;
 	Mouse::SetMoveLimit(Rect(x1, y1, x2, y2));
 }
 
@@ -291,7 +291,7 @@ void RefreshMouse() {
 }
 
 void SetMousePosition(int newx, int newy) {
-	const Rect &viewport = play.GetMainViewport();
+	const Rect &viewport = _GP(play).GetMainViewport();
 
 	if (newx < 0)
 		newx = 0;
diff --git a/engines/ags/engine/ac/object.cpp b/engines/ags/engine/ac/object.cpp
index a3a26afad2..82c11e417e 100644
--- a/engines/ags/engine/ac/object.cpp
+++ b/engines/ags/engine/ac/object.cpp
@@ -297,10 +297,10 @@ const char *Object_GetName_New(ScriptObject *objj) {
 
 bool Object_IsInteractionAvailable(ScriptObject *oobj, int mood) {
 
-	play.check_interaction_only = 1;
+	_GP(play).check_interaction_only = 1;
 	RunObjectInteraction(oobj->id, mood);
-	int ciwas = play.check_interaction_only;
-	play.check_interaction_only = 0;
+	int ciwas = _GP(play).check_interaction_only;
+	_GP(play).check_interaction_only = 0;
 	return (ciwas == 2);
 }
 
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index 9c990ace12..d85b4c42db 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -231,7 +231,7 @@ size_t add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, int pic
 
 void get_overlay_position(const ScreenOverlay &over, int *x, int *y) {
 	int tdxp, tdyp;
-	const Rect &ui_view = play.GetUIViewport();
+	const Rect &ui_view = _GP(play).GetUIViewport();
 
 	if (over.x == OVR_AUTOPLACE) {
 		// auto place on character
@@ -262,7 +262,7 @@ void get_overlay_position(const ScreenOverlay &over, int *x, int *y) {
 		tdyp = over.y + over._offsetY;
 
 		if (!over.positionRelativeToScreen) {
-			Point tdxy = play.RoomToScreen(tdxp, tdyp);
+			Point tdxy = _GP(play).RoomToScreen(tdxp, tdyp);
 			tdxp = tdxy.X;
 			tdyp = tdxy.Y;
 		}
diff --git a/engines/ags/engine/ac/parser.cpp b/engines/ags/engine/ac/parser.cpp
index 9aadb74034..1c61119f8d 100644
--- a/engines/ags/engine/ac/parser.cpp
+++ b/engines/ags/engine/ac/parser.cpp
@@ -40,20 +40,20 @@ namespace AGS3 {
 using namespace AGS::Shared;
 
 
-extern GameState play;
+
 
 int Parser_FindWordID(const char *wordToFind) {
 	return find_word_in_dictionary(wordToFind);
 }
 
 const char *Parser_SaidUnknownWord() {
-	if (play.bad_parsed_word[0] == 0)
+	if (_GP(play).bad_parsed_word[0] == 0)
 		return nullptr;
-	return CreateNewScriptString(play.bad_parsed_word);
+	return CreateNewScriptString(_GP(play).bad_parsed_word);
 }
 
 void ParseText(const char *text) {
-	parse_sentence(text, &play.num_parsed_words, play.parsed_words, nullptr, 0);
+	parse_sentence(text, &_GP(play).num_parsed_words, _GP(play).parsed_words, nullptr, 0);
 }
 
 // Said: call with argument for example "get apple"; we then check
@@ -62,7 +62,7 @@ void ParseText(const char *text) {
 int Said(const char *checkwords) {
 	int numword = 0;
 	short words[MAX_PARSED_WORDS];
-	return parse_sentence(checkwords, &numword, &words[0], play.parsed_words, play.num_parsed_words);
+	return parse_sentence(checkwords, &numword, &words[0], _GP(play).parsed_words, _GP(play).num_parsed_words);
 }
 
 //=============================================================================
@@ -151,7 +151,7 @@ int parse_sentence(const char *src_text, int *numwords, short *wordarray, short
 
 	numwords[0] = 0;
 	if (compareto == nullptr)
-		play.bad_parsed_word[0] = 0;
+		_GP(play).bad_parsed_word[0] = 0;
 
 	String uniform_text = src_text;
 	uniform_text.MakeLower();
@@ -275,8 +275,8 @@ int parse_sentence(const char *src_text, int *numwords, short *wordarray, short
 					return 0;
 				// if it's an unknown word, store it for use in messages like
 				// "you can't use the word 'xxx' in this game"
-				if ((word < 0) && (play.bad_parsed_word[0] == 0))
-					strcpy(play.bad_parsed_word, thisword);
+				if ((word < 0) && (_GP(play).bad_parsed_word[0] == 0))
+					strcpy(_GP(play).bad_parsed_word, thisword);
 			}
 
 			if (do_word_now) {
diff --git a/engines/ags/engine/ac/region.cpp b/engines/ags/engine/ac/region.cpp
index c97134c7fd..a094e3267f 100644
--- a/engines/ags/engine/ac/region.cpp
+++ b/engines/ags/engine/ac/region.cpp
@@ -54,7 +54,7 @@ ScriptRegion *GetRegionAtRoom(int xx, int yy) {
 }
 
 ScriptRegion *GetRegionAtScreen(int x, int y) {
-	VpPoint vpt = play.ScreenToRoomDivDown(x, y);
+	VpPoint vpt = _GP(play).ScreenToRoomDivDown(x, y);
 	if (vpt.second < 0)
 		return nullptr;
 	return GetRegionAtRoom(vpt.first.X, vpt.first.Y);
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 68236c76bc..93e5c04684 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -88,7 +88,7 @@ using namespace AGS::Engine;
 
 extern GameSetup usetup;
 
-extern GameState play;
+
 extern RoomStatus *croom;
 extern RoomStatus troom;    // used for non-saveable rooms, eg. intro
 extern int displayed_room;
@@ -137,7 +137,7 @@ ScriptDrawingSurface *Room_GetDrawingSurfaceForBackground(int backgroundNumber)
 		quit("!Room.GetDrawingSurfaceForBackground: no room is currently loaded");
 
 	if (backgroundNumber == SCR_NO_VALUE) {
-		backgroundNumber = play.bg_frame;
+		backgroundNumber = _GP(play).bg_frame;
 	}
 
 	if ((backgroundNumber < 0) || ((size_t)backgroundNumber >= thisroom.BgFrameCount))
@@ -271,7 +271,7 @@ void unload_old_room() {
 	for (ff = 0; ff < croom->numobj; ff++)
 		objs[ff].moving = 0;
 
-	if (!play.ambient_sounds_persist) {
+	if (!_GP(play).ambient_sounds_persist) {
 		for (ff = 1; ff < MAX_SOUND_CHANNELS; ff++)
 			StopAmbientSound(ff);
 	}
@@ -292,14 +292,14 @@ void unload_old_room() {
 		roominstFork = nullptr;
 		roominst = nullptr;
 	} else croom->tsdatasize = 0;
-	memset(&play.walkable_areas_on[0], 1, MAX_WALK_AREAS + 1);
-	play.bg_frame = 0;
-	play.bg_frame_locked = 0;
+	memset(&_GP(play).walkable_areas_on[0], 1, MAX_WALK_AREAS + 1);
+	_GP(play).bg_frame = 0;
+	_GP(play).bg_frame_locked = 0;
 	remove_screen_overlay(-1);
 	delete raw_saved_screen;
 	raw_saved_screen = nullptr;
 	for (ff = 0; ff < MAX_ROOM_BGFRAMES; ff++)
-		play.raw_modified[ff] = 0;
+		_GP(play).raw_modified[ff] = 0;
 	for (size_t i = 0; i < thisroom.LocalVariables.size() && i < MAX_GLOBAL_VARIABLES; ++i)
 		croom->interactionVariableValues[i] = thisroom.LocalVariables[i].Value;
 
@@ -314,8 +314,8 @@ void unload_old_room() {
 		charextra[ff].xwas = INVALID_X;
 	}
 
-	play.swap_portrait_lastchar = -1;
-	play.swap_portrait_lastlastchar = -1;
+	_GP(play).swap_portrait_lastchar = -1;
+	_GP(play).swap_portrait_lastlastchar = -1;
 
 	for (ff = 0; ff < croom->numobj; ff++) {
 		// un-export the object's script object
@@ -361,9 +361,9 @@ void unload_old_room() {
 	}
 
 	// if Hide Player Character was ticked, restore it to visible
-	if (play.temporarily_turned_off_character >= 0) {
-		_GP(game).chars[play.temporarily_turned_off_character].on = 1;
-		play.temporarily_turned_off_character = -1;
+	if (_GP(play).temporarily_turned_off_character >= 0) {
+		_GP(game).chars[_GP(play).temporarily_turned_off_character].on = 1;
+		_GP(play).temporarily_turned_off_character = -1;
 	}
 
 }
@@ -416,17 +416,17 @@ void update_letterbox_mode() {
 	    _GP(game).GetGameRes().Height;
 	new_main_view.SetHeight(viewport_height);
 
-	play.SetMainViewport(CenterInRect(game_frame, new_main_view));
-	play.SetUIViewport(new_main_view);
+	_GP(play).SetMainViewport(CenterInRect(game_frame, new_main_view));
+	_GP(play).SetUIViewport(new_main_view);
 }
 
 // Automatically reset primary room viewport and camera to match the new room size
 static void adjust_viewport_to_room() {
 	const Size real_room_sz = Size(data_to_game_coord(thisroom.Width), data_to_game_coord(thisroom.Height));
-	const Rect main_view = play.GetMainViewport();
+	const Rect main_view = _GP(play).GetMainViewport();
 	Rect new_room_view = RectWH(Size::Clamp(real_room_sz, Size(1, 1), main_view.GetSize()));
 
-	auto view = play.GetRoomViewport(0);
+	auto view = _GP(play).GetRoomViewport(0);
 	view->SetRect(new_room_view);
 	auto cam = view->GetCamera();
 	if (cam) {
@@ -438,8 +438,8 @@ static void adjust_viewport_to_room() {
 
 // Run through all viewports and cameras to make sure they can work in new room's bounds
 static void update_all_viewcams_with_newroom() {
-	for (int i = 0; i < play.GetRoomCameraCount(); ++i) {
-		auto cam = play.GetRoomCamera(i);
+	for (int i = 0; i < _GP(play).GetRoomCameraCount(); ++i) {
+		auto cam = _GP(play).GetRoomCamera(i);
 		const Rect old_pos = cam->GetRect();
 		cam->SetSize(old_pos.GetSize());
 		cam->SetAt(old_pos.Left, old_pos.Top);
@@ -454,7 +454,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 	String room_filename;
 	int cc;
 	done_es_error = 0;
-	play.room_changes ++;
+	_GP(play).room_changes ++;
 	// TODO: find out why do we need to temporarily lower color depth to 8-bit.
 	// Or do we? There's a serious usability problem in this: if any bitmap is
 	// created meanwhile it will have this color depth by default, which may
@@ -494,10 +494,10 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 	delay(100);
 	}*/
 
-	play.room_width = thisroom.Width;
-	play.room_height = thisroom.Height;
-	play.anim_background_speed = thisroom.BgAnimSpeed;
-	play.bg_anim_delay = play.anim_background_speed;
+	_GP(play).room_width = thisroom.Width;
+	_GP(play).room_height = thisroom.Height;
+	_GP(play).anim_background_speed = thisroom.BgAnimSpeed;
+	_GP(play).bg_anim_delay = _GP(play).anim_background_speed;
 
 	// do the palette
 	for (cc = 0; cc < 256; cc++) {
@@ -735,7 +735,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 		}
 	}
 	our_eip = 207;
-	play.entered_edge = -1;
+	_GP(play).entered_edge = -1;
 
 	if ((new_room_x != SCR_NO_VALUE) && (forchar != nullptr)) {
 		forchar->x = new_room_x;
@@ -749,7 +749,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 
 	if ((new_room_pos > 0) & (forchar != nullptr)) {
 		if (new_room_pos >= 4000) {
-			play.entered_edge = 3;
+			_GP(play).entered_edge = 3;
 			forchar->y = thisroom.Edges.Top + get_fixed_pixel_size(1);
 			forchar->x = new_room_pos % 1000;
 			if (forchar->x == 0) forchar->x = thisroom.Width / 2;
@@ -759,7 +759,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 				forchar->x = thisroom.Edges.Right - 3;
 			forchar->loop = 0;
 		} else if (new_room_pos >= 3000) {
-			play.entered_edge = 2;
+			_GP(play).entered_edge = 2;
 			forchar->y = thisroom.Edges.Bottom - get_fixed_pixel_size(1);
 			forchar->x = new_room_pos % 1000;
 			if (forchar->x == 0) forchar->x = thisroom.Width / 2;
@@ -769,7 +769,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 				forchar->x = thisroom.Edges.Right - 3;
 			forchar->loop = 3;
 		} else if (new_room_pos >= 2000) {
-			play.entered_edge = 1;
+			_GP(play).entered_edge = 1;
 			forchar->x = thisroom.Edges.Right - get_fixed_pixel_size(1);
 			forchar->y = new_room_pos % 1000;
 			if (forchar->y == 0) forchar->y = thisroom.Height / 2;
@@ -779,7 +779,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 				forchar->y = thisroom.Edges.Bottom - 3;
 			forchar->loop = 1;
 		} else if (new_room_pos >= 1000) {
-			play.entered_edge = 0;
+			_GP(play).entered_edge = 0;
 			forchar->x = thisroom.Edges.Left + get_fixed_pixel_size(1);
 			forchar->y = new_room_pos % 1000;
 			if (forchar->y == 0) forchar->y = thisroom.Height / 2;
@@ -840,16 +840,16 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 		new_room_pos = 0;
 	}
 	if (forchar != nullptr) {
-		play.entered_at_x = forchar->x;
-		play.entered_at_y = forchar->y;
+		_GP(play).entered_at_x = forchar->x;
+		_GP(play).entered_at_y = forchar->y;
 		if (forchar->x >= thisroom.Edges.Right)
-			play.entered_edge = 1;
+			_GP(play).entered_edge = 1;
 		else if (forchar->x <= thisroom.Edges.Left)
-			play.entered_edge = 0;
+			_GP(play).entered_edge = 0;
 		else if (forchar->y >= thisroom.Edges.Bottom)
-			play.entered_edge = 2;
+			_GP(play).entered_edge = 2;
 		else if (forchar->y <= thisroom.Edges.Top)
-			play.entered_edge = 3;
+			_GP(play).entered_edge = 3;
 	}
 	if (thisroom.Options.StartupMusic > 0)
 		PlayMusicResetQueue(thisroom.Options.StartupMusic);
@@ -865,7 +865,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 			// remember which character we turned off, in case they
 			// use SetPlyaerChracter within this room (so we re-enable
 			// the correct character when leaving the room)
-			play.temporarily_turned_off_character = _GP(game).playercharacter;
+			_GP(play).temporarily_turned_off_character = _GP(game).playercharacter;
 		}
 		if (forchar->flags & CHF_FIXVIEW) ;
 		else if (thisroom.Options.PlayerView == 0) forchar->view = forchar->defview;
@@ -882,10 +882,10 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 	// If we are not restoring a save, update cameras to accomodate for this
 	// new room; otherwise this is done later when cameras are recreated.
 	if (forchar != nullptr) {
-		if (play.IsAutoRoomViewport())
+		if (_GP(play).IsAutoRoomViewport())
 			adjust_viewport_to_room();
 		update_all_viewcams_with_newroom();
-		play.UpdateRoomCameras(); // update auto tracking
+		_GP(play).UpdateRoomCameras(); // update auto tracking
 	}
 	init_room_drawdata();
 
@@ -896,8 +896,8 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 			MergeObject(cc);
 	}
 	new_room_flags = 0;
-	play.gscript_timer = -1; // avoid screw-ups with changing screens
-	play.player_on_region = 0;
+	_GP(play).gscript_timer = -1; // avoid screw-ups with changing screens
+	_GP(play).player_on_region = 0;
 	// trash any input which they might have done while it was loading
 	ags_clear_input_buffer();
 	// no fade in, so set the palette immediately in case of 256-col sprites
@@ -996,9 +996,9 @@ void check_new_room() {
 		// make sure that any script calls don't re-call enters screen
 		int newroom_was = in_new_room;
 		in_new_room = 0;
-		play.disabled_user_interface ++;
+		_GP(play).disabled_user_interface ++;
 		process_event(&evh);
-		play.disabled_user_interface --;
+		_GP(play).disabled_user_interface --;
 		in_new_room = newroom_was;
 		//    setevent(EV_RUNEVBLOCK,EVB_ROOM,0,5);
 	}
@@ -1031,7 +1031,7 @@ void on_background_frame_change() {
 	invalidate_cached_walkbehinds();
 
 	// get the new frame's palette
-	memcpy(palette, thisroom.BgFrames[play.bg_frame].Palette, sizeof(color) * 256);
+	memcpy(palette, thisroom.BgFrames[_GP(play).bg_frame].Palette, sizeof(color) * 256);
 
 	// hi-colour, update the palette. It won't have an immediate effect
 	// but will be drawn properly when the screen fades in
@@ -1042,7 +1042,7 @@ void on_background_frame_change() {
 		return;
 
 	// Don't update the palette if it hasn't changed
-	if (thisroom.BgFrames[play.bg_frame].IsPaletteShared)
+	if (thisroom.BgFrames[_GP(play).bg_frame].IsPaletteShared)
 		return;
 
 	// 256-colours, tell it to update the palette (will actually be done as
diff --git a/engines/ags/engine/ac/roomobject.cpp b/engines/ags/engine/ac/roomobject.cpp
index d6e8caec64..f66e31dc7b 100644
--- a/engines/ags/engine/ac/roomobject.cpp
+++ b/engines/ags/engine/ac/roomobject.cpp
@@ -36,7 +36,7 @@ namespace AGS3 {
 using AGS::Shared::Stream;
 
 extern ViewStruct *views;
-extern GameState play;
+
 
 
 RoomObject::RoomObject() {
@@ -121,7 +121,7 @@ void RoomObject::update_cycle_view_forwards() {
 			cycling = 0;
 			frame--;
 		} else {
-			if (play.no_multiloop_repeat == 0) {
+			if (_GP(play).no_multiloop_repeat == 0) {
 				// multi-loop anims, go back to start of it
 				while ((loop > 0) &&
 					(views[view].loops[loop - 1].RunNextLoop()))
diff --git a/engines/ags/engine/ac/runtime_defines.h b/engines/ags/engine/ac/runtime_defines.h
index 5de5e34542..23d7425f81 100644
--- a/engines/ags/engine/ac/runtime_defines.h
+++ b/engines/ags/engine/ac/runtime_defines.h
@@ -85,7 +85,7 @@ const int LegacyRoomVolumeFactor = 30;
 
 #define DEBUG_CONSOLE_NUMLINES 6
 #define TXT_SCOREBAR        29
-#define MAXSCORE play.totalscore
+#define MAXSCORE _GP(play).totalscore
 #define CHANIM_REPEAT    2
 #define CHANIM_BACKWARDS 4
 #define ANIM_BACKWARDS 10
@@ -93,9 +93,9 @@ const int LegacyRoomVolumeFactor = 30;
 #define ANIM_REPEAT    2
 #define ANIM_ONCERESET 3
 #define FONT_STATUSBAR  0
-#define FONT_NORMAL     play.normal_font
+#define FONT_NORMAL     _GP(play).normal_font
 //#define FONT_SPEECHBACK 1
-#define FONT_SPEECH     play.speech_font
+#define FONT_SPEECH     _GP(play).speech_font
 #define MODE_WALK 0
 #define MODE_LOOK 1
 #define MODE_HAND 2
diff --git a/engines/ags/engine/ac/screen.cpp b/engines/ags/engine/ac/screen.cpp
index 92c862576c..63d4c18b37 100644
--- a/engines/ags/engine/ac/screen.cpp
+++ b/engines/ags/engine/ac/screen.cpp
@@ -42,7 +42,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
-extern GameState play;
+
 extern IGraphicsDriver *gfxDriver;
 extern AGSPlatformDriver *platform;
 
@@ -50,14 +50,14 @@ void my_fade_in(PALETTE p, int speed) {
 	if (_GP(game).color_depth > 1) {
 		set_palette(p);
 
-		play.screen_is_faded_out = 0;
+		_GP(play).screen_is_faded_out = 0;
 
-		if (play.no_hicolor_fadein) {
+		if (_GP(play).no_hicolor_fadein) {
 			return;
 		}
 	}
 
-	gfxDriver->FadeIn(speed, p, play.fade_to_red, play.fade_to_green, play.fade_to_blue);
+	gfxDriver->FadeIn(speed, p, _GP(play).fade_to_red, _GP(play).fade_to_green, _GP(play).fade_to_blue);
 }
 
 Bitmap *saved_viewport_bitmap = nullptr;
@@ -67,23 +67,23 @@ void current_fade_out_effect() {
 		return;
 
 	// get the screen transition type
-	int theTransition = play.fade_effect;
+	int theTransition = _GP(play).fade_effect;
 	// was a temporary transition selected? if so, use it
-	if (play.next_screen_transition >= 0)
-		theTransition = play.next_screen_transition;
-	const bool ignore_transition = play.screen_tint > 0;
+	if (_GP(play).next_screen_transition >= 0)
+		theTransition = _GP(play).next_screen_transition;
+	const bool ignore_transition = _GP(play).screen_tint > 0;
 
 	if ((theTransition == FADE_INSTANT) || ignore_transition) {
-		if (!play.keep_screen_during_instant_transition)
+		if (!_GP(play).keep_screen_during_instant_transition)
 			set_palette_range(black_palette, 0, 255, 0);
 	} else if (theTransition == FADE_NORMAL) {
 		my_fade_out(5);
 	} else if (theTransition == FADE_BOXOUT) {
 		gfxDriver->BoxOutEffect(true, get_fixed_pixel_size(16), 1000 / GetGameSpeed());
-		play.screen_is_faded_out = 1;
+		_GP(play).screen_is_faded_out = 1;
 	} else {
 		get_palette(old_palette);
-		const Rect &viewport = play.GetMainViewport();
+		const Rect &viewport = _GP(play).GetMainViewport();
 		saved_viewport_bitmap = CopyScreenIntoBitmap(viewport.GetWidth(), viewport.GetHeight());
 	}
 }
@@ -93,7 +93,7 @@ IDriverDependantBitmap *prepare_screen_for_transition_in() {
 		quit("Crossfade: buffer is null attempting transition");
 
 	saved_viewport_bitmap = ReplaceBitmapWithSupportedFormat(saved_viewport_bitmap);
-	const Rect &viewport = play.GetMainViewport();
+	const Rect &viewport = _GP(play).GetMainViewport();
 	if (saved_viewport_bitmap->GetHeight() < viewport.GetHeight()) {
 		Bitmap *enlargedBuffer = BitmapHelper::CreateBitmap(saved_viewport_bitmap->GetWidth(), viewport.GetHeight(), saved_viewport_bitmap->GetColorDepth());
 		enlargedBuffer->Blit(saved_viewport_bitmap, 0, 0, 0, (viewport.GetHeight() - saved_viewport_bitmap->GetHeight()) / 2, saved_viewport_bitmap->GetWidth(), saved_viewport_bitmap->GetHeight());
@@ -124,29 +124,29 @@ int Screen_GetScreenHeight() {
 }
 
 bool Screen_GetAutoSizeViewport() {
-	return play.IsAutoRoomViewport();
+	return _GP(play).IsAutoRoomViewport();
 }
 
 void Screen_SetAutoSizeViewport(bool on) {
-	play.SetAutoRoomViewport(on);
+	_GP(play).SetAutoRoomViewport(on);
 }
 
 ScriptViewport *Screen_GetViewport() {
-	return play.GetScriptViewport(0);
+	return _GP(play).GetScriptViewport(0);
 }
 
 int Screen_GetViewportCount() {
-	return play.GetRoomViewportCount();
+	return _GP(play).GetRoomViewportCount();
 }
 
 ScriptViewport *Screen_GetAnyViewport(int index) {
-	return play.GetScriptViewport(index);
+	return _GP(play).GetScriptViewport(index);
 }
 
 ScriptUserObject *Screen_ScreenToRoomPoint(int scrx, int scry) {
 	data_to_game_coords(&scrx, &scry);
 
-	VpPoint vpt = play.ScreenToRoom(scrx, scry);
+	VpPoint vpt = _GP(play).ScreenToRoom(scrx, scry);
 	if (vpt.second < 0)
 		return nullptr;
 
@@ -156,7 +156,7 @@ ScriptUserObject *Screen_ScreenToRoomPoint(int scrx, int scry) {
 
 ScriptUserObject *Screen_RoomToScreenPoint(int roomx, int roomy) {
 	data_to_game_coords(&roomx, &roomy);
-	Point pt = play.RoomToScreen(roomx, roomy);
+	Point pt = _GP(play).RoomToScreen(roomx, roomy);
 	game_to_data_coords(pt.X, pt.Y);
 	return ScriptStructHelpers::CreatePoint(pt.X, pt.Y);
 }
diff --git a/engines/ags/engine/ac/speech.cpp b/engines/ags/engine/ac/speech.cpp
index 10a1203f8e..ae2ab58018 100644
--- a/engines/ags/engine/ac/speech.cpp
+++ b/engines/ags/engine/ac/speech.cpp
@@ -87,34 +87,34 @@ SkipSpeechStyle internal_skip_speech_to_user(int internal_val) {
 //=============================================================================
 
 
-extern GameState play;
+
 
 RuntimeScriptValue Sc_Speech_GetAnimationStopTimeMargin(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.close_mouth_speech_time);
+	API_VARGET_INT(_GP(play).close_mouth_speech_time);
 }
 
 RuntimeScriptValue Sc_Speech_SetAnimationStopTimeMargin(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARSET_PINT(play.close_mouth_speech_time);
+	API_VARSET_PINT(_GP(play).close_mouth_speech_time);
 }
 
 RuntimeScriptValue Sc_Speech_GetCustomPortraitPlacement(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.speech_portrait_placement);
+	API_VARGET_INT(_GP(play).speech_portrait_placement);
 }
 
 RuntimeScriptValue Sc_Speech_SetCustomPortraitPlacement(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARSET_PINT(play.speech_portrait_placement);
+	API_VARSET_PINT(_GP(play).speech_portrait_placement);
 }
 
 RuntimeScriptValue Sc_Speech_GetDisplayPostTimeMs(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.speech_display_post_time_ms);
+	API_VARGET_INT(_GP(play).speech_display_post_time_ms);
 }
 
 RuntimeScriptValue Sc_Speech_SetDisplayPostTimeMs(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARSET_PINT(play.speech_display_post_time_ms);
+	API_VARSET_PINT(_GP(play).speech_display_post_time_ms);
 }
 
 RuntimeScriptValue Sc_Speech_GetGlobalSpeechAnimationDelay(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.talkanim_speed);
+	API_VARGET_INT(_GP(play).talkanim_speed);
 }
 
 RuntimeScriptValue Sc_Speech_SetGlobalSpeechAnimationDelay(const RuntimeScriptValue *params, int32_t param_count) {
@@ -122,23 +122,23 @@ RuntimeScriptValue Sc_Speech_SetGlobalSpeechAnimationDelay(const RuntimeScriptVa
 		debug_script_warn("Speech.GlobalSpeechAnimationDelay cannot be set when global speech animation speed is not enabled; set Speech.UseGlobalSpeechAnimationDelay first!");
 		return RuntimeScriptValue();
 	}
-	API_VARSET_PINT(play.talkanim_speed);
+	API_VARSET_PINT(_GP(play).talkanim_speed);
 }
 
 RuntimeScriptValue Sc_Speech_GetPortraitXOffset(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.speech_portrait_x);
+	API_VARGET_INT(_GP(play).speech_portrait_x);
 }
 
 RuntimeScriptValue Sc_Speech_SetPortraitXOffset(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARSET_PINT(play.speech_portrait_x);
+	API_VARSET_PINT(_GP(play).speech_portrait_x);
 }
 
 RuntimeScriptValue Sc_Speech_GetPortraitY(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.speech_portrait_y);
+	API_VARGET_INT(_GP(play).speech_portrait_y);
 }
 
 RuntimeScriptValue Sc_Speech_SetPortraitY(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARSET_PINT(play.speech_portrait_y);
+	API_VARSET_PINT(_GP(play).speech_portrait_y);
 }
 
 RuntimeScriptValue Sc_Speech_GetStyle(const RuntimeScriptValue *params, int32_t param_count) {
@@ -148,11 +148,11 @@ RuntimeScriptValue Sc_Speech_GetStyle(const RuntimeScriptValue *params, int32_t
 extern RuntimeScriptValue Sc_SetSpeechStyle(const RuntimeScriptValue *params, int32_t param_count);
 
 RuntimeScriptValue Sc_Speech_GetSkipKey(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.skip_speech_specific_key);
+	API_VARGET_INT(_GP(play).skip_speech_specific_key);
 }
 
 RuntimeScriptValue Sc_Speech_SetSkipKey(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARSET_PINT(play.skip_speech_specific_key);
+	API_VARSET_PINT(_GP(play).skip_speech_specific_key);
 }
 
 RuntimeScriptValue Sc_Speech_GetSkipStyle(const RuntimeScriptValue *params, int32_t param_count) {
@@ -162,18 +162,18 @@ RuntimeScriptValue Sc_Speech_GetSkipStyle(const RuntimeScriptValue *params, int3
 extern RuntimeScriptValue Sc_SetSkipSpeech(const RuntimeScriptValue *params, int32_t param_count);
 
 RuntimeScriptValue Sc_Speech_GetTextAlignment(const RuntimeScriptValue *params, int32_t param_count) {
-	API_VARGET_INT(play.speech_text_align);
+	API_VARGET_INT(_GP(play).speech_text_align);
 }
 
 RuntimeScriptValue Sc_Speech_SetTextAlignment_Old(const RuntimeScriptValue *params, int32_t param_count) {
-	ASSERT_VARIABLE_VALUE(play.speech_text_align);
-	play.speech_text_align = ReadScriptAlignment(params[0].IValue);
+	ASSERT_VARIABLE_VALUE(_GP(play).speech_text_align);
+	_GP(play).speech_text_align = ReadScriptAlignment(params[0].IValue);
 	return RuntimeScriptValue();
 }
 
 RuntimeScriptValue Sc_Speech_SetTextAlignment(const RuntimeScriptValue *params, int32_t param_count) {
-	ASSERT_VARIABLE_VALUE(play.speech_text_align);
-	play.speech_text_align = (HorAlignment)params[0].IValue;
+	ASSERT_VARIABLE_VALUE(_GP(play).speech_text_align);
+	_GP(play).speech_text_align = (HorAlignment)params[0].IValue;
 	return RuntimeScriptValue();
 }
 
diff --git a/engines/ags/engine/ac/string.cpp b/engines/ags/engine/ac/string.cpp
index 75dda91c4b..4612ec2198 100644
--- a/engines/ags/engine/ac/string.cpp
+++ b/engines/ags/engine/ac/string.cpp
@@ -41,7 +41,7 @@
 
 namespace AGS3 {
 
-extern GameState play;
+
 extern int longestline;
 extern ScriptString myScriptStringImpl;
 
@@ -234,7 +234,7 @@ DynObjectRef CreateNewScriptStringObj(const char *fromText, bool reAllocate) {
 
 size_t break_up_text_into_lines(const char *todis, SplitLines &lines, int wii, int fonnt, size_t max_lines) {
 	if (fonnt == -1)
-		fonnt = play.normal_font;
+		fonnt = _GP(play).normal_font;
 
 	//  char sofar[100];
 	if (todis[0] == '&') {
diff --git a/engines/ags/engine/ac/sys_events.cpp b/engines/ags/engine/ac/sys_events.cpp
index 0239f064e2..0211c7c81f 100644
--- a/engines/ags/engine/ac/sys_events.cpp
+++ b/engines/ags/engine/ac/sys_events.cpp
@@ -39,9 +39,6 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
-
-extern GameState play;
-
 extern volatile unsigned long globalTimerCounter;
 extern int pluginSimulatedClick;
 extern int displayed_room;
@@ -185,7 +182,7 @@ int ags_getch() {
 	}
 
 	// Alt+X, abort (but only once game is loaded)
-	if ((gott == play.abort_key) && (displayed_room >= 0)) {
+	if ((gott == _GP(play).abort_key) && (displayed_room >= 0)) {
 		check_dynamic_sprites_at_exit = 0;
 		quit("!|");
 	}
diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index 025464d840..85bf016897 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -52,9 +52,7 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
-
 extern GameSetup usetup;
-extern GameState play;
 extern ScriptAudioChannel scrAudioChannel[MAX_SOUND_CHANNELS + 1];
 extern ScriptSystem scsystem;
 extern IGraphicsDriver *gfxDriver;
@@ -103,11 +101,11 @@ int System_GetScreenHeight() {
 }
 
 int System_GetViewportHeight() {
-	return game_to_data_coord(play.GetMainViewport().GetHeight());
+	return game_to_data_coord(_GP(play).GetMainViewport().GetHeight());
 }
 
 int System_GetViewportWidth() {
-	return game_to_data_coord(play.GetMainViewport().GetWidth());
+	return game_to_data_coord(_GP(play).GetMainViewport().GetWidth());
 }
 
 const char *System_GetVersion() {
@@ -157,16 +155,16 @@ int System_GetSupportsGammaControl() {
 }
 
 int System_GetGamma() {
-	return play.gamma_adjustment;
+	return _GP(play).gamma_adjustment;
 }
 
 void System_SetGamma(int newValue) {
 	if ((newValue < 0) || (newValue > 200))
 		quitprintf("!System.Gamma: value must be between 0-200 (not %d)", newValue);
 
-	if (play.gamma_adjustment != newValue) {
+	if (_GP(play).gamma_adjustment != newValue) {
 		debug_script_log("Gamma control set to %d", newValue);
-		play.gamma_adjustment = newValue;
+		_GP(play).gamma_adjustment = newValue;
 
 		if (gfxDriver->SupportsGammaControl())
 			gfxDriver->SetGamma(newValue);
@@ -185,14 +183,14 @@ ScriptAudioChannel *System_GetAudioChannels(int index) {
 }
 
 int System_GetVolume() {
-	return play.digital_master_volume;
+	return _GP(play).digital_master_volume;
 }
 
 void System_SetVolume(int newvol) {
 	if ((newvol < 0) || (newvol > 100))
 		quit("!System.Volume: invalid volume - must be from 0-100");
 
-	play.digital_master_volume = newvol;
+	_GP(play).digital_master_volume = newvol;
 #if !AGS_PLATFORM_SCUMMVM
 	auto newvol_f = static_cast<float>(newvol) / 100.0;
 	audio_core_set_master_volume(newvol_f);
diff --git a/engines/ags/engine/ac/translation.cpp b/engines/ags/engine/ac/translation.cpp
index 65a662975f..88a89c1303 100644
--- a/engines/ags/engine/ac/translation.cpp
+++ b/engines/ags/engine/ac/translation.cpp
@@ -42,10 +42,8 @@ using namespace AGS::Shared;
 
 extern GameSetup usetup;
 
-extern GameState play;
 extern char transFileName[MAX_PATH];
 
-
 TreeMap *transtree = nullptr;
 long lang_offs_start = 0;
 char transFileName[MAX_PATH] = "\0";
@@ -154,10 +152,10 @@ bool parse_translation(Stream *language_file, String &parse_error) {
 			temp = language_file->ReadInt32();
 			// text direction
 			if (temp == 1) {
-				play.text_align = kHAlignLeft;
+				_GP(play).text_align = kHAlignLeft;
 				_GP(game).options[OPT_RIGHTLEFTWRITE] = 0;
 			} else if (temp == 2) {
-				play.text_align = kHAlignRight;
+				_GP(play).text_align = kHAlignRight;
 				_GP(game).options[OPT_RIGHTLEFTWRITE] = 1;
 			}
 		} else {
diff --git a/engines/ags/engine/ac/viewport_script.cpp b/engines/ags/engine/ac/viewport_script.cpp
index 23edbfa232..537aa1ac26 100644
--- a/engines/ags/engine/ac/viewport_script.cpp
+++ b/engines/ags/engine/ac/viewport_script.cpp
@@ -34,6 +34,7 @@
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/script/script_api.h"
 #include "ags/engine/script/script_runtime.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -46,14 +47,14 @@ using namespace AGS::Shared;
 //=============================================================================
 
 ScriptCamera *Camera_Create() {
-	auto cam = play.CreateRoomCamera();
+	auto cam = _GP(play).CreateRoomCamera();
 	if (!cam)
 		return NULL;
-	return play.RegisterRoomCamera(cam->GetID());
+	return _GP(play).RegisterRoomCamera(cam->GetID());
 }
 
 void Camera_Delete(ScriptCamera *scam) {
-	play.DeleteRoomCamera(scam->GetID());
+	_GP(play).DeleteRoomCamera(scam->GetID());
 }
 
 int Camera_GetX(ScriptCamera *scam) {
@@ -61,7 +62,7 @@ int Camera_GetX(ScriptCamera *scam) {
 		debug_script_warn("Camera.X: trying to use deleted camera");
 		return 0;
 	}
-	int x = play.GetRoomCamera(scam->GetID())->GetRect().Left;
+	int x = _GP(play).GetRoomCamera(scam->GetID())->GetRect().Left;
 	return game_to_data_coord(x);
 }
 
@@ -71,7 +72,7 @@ void Camera_SetX(ScriptCamera *scam, int x) {
 		return;
 	}
 	x = data_to_game_coord(x);
-	auto cam = play.GetRoomCamera(scam->GetID());
+	auto cam = _GP(play).GetRoomCamera(scam->GetID());
 	cam->LockAt(x, cam->GetRect().Top);
 }
 
@@ -80,7 +81,7 @@ int Camera_GetY(ScriptCamera *scam) {
 		debug_script_warn("Camera.Y: trying to use deleted camera");
 		return 0;
 	}
-	int y = play.GetRoomCamera(scam->GetID())->GetRect().Top;
+	int y = _GP(play).GetRoomCamera(scam->GetID())->GetRect().Top;
 	return game_to_data_coord(y);
 }
 
@@ -90,7 +91,7 @@ void Camera_SetY(ScriptCamera *scam, int y) {
 		return;
 	}
 	y = data_to_game_coord(y);
-	auto cam = play.GetRoomCamera(scam->GetID());
+	auto cam = _GP(play).GetRoomCamera(scam->GetID());
 	cam->LockAt(cam->GetRect().Left, y);
 }
 
@@ -99,7 +100,7 @@ int Camera_GetWidth(ScriptCamera *scam) {
 		debug_script_warn("Camera.Width: trying to use deleted camera");
 		return 0;
 	}
-	int width = play.GetRoomCamera(scam->GetID())->GetRect().GetWidth();
+	int width = _GP(play).GetRoomCamera(scam->GetID())->GetRect().GetWidth();
 	return game_to_data_coord(width);
 }
 
@@ -109,7 +110,7 @@ void Camera_SetWidth(ScriptCamera *scam, int width) {
 		return;
 	}
 	width = data_to_game_coord(width);
-	auto cam = play.GetRoomCamera(scam->GetID());
+	auto cam = _GP(play).GetRoomCamera(scam->GetID());
 	cam->SetSize(Size(width, cam->GetRect().GetHeight()));
 }
 
@@ -118,7 +119,7 @@ int Camera_GetHeight(ScriptCamera *scam) {
 		debug_script_warn("Camera.Height: trying to use deleted camera");
 		return 0;
 	}
-	int height = play.GetRoomCamera(scam->GetID())->GetRect().GetHeight();
+	int height = _GP(play).GetRoomCamera(scam->GetID())->GetRect().GetHeight();
 	return game_to_data_coord(height);
 }
 
@@ -128,7 +129,7 @@ void Camera_SetHeight(ScriptCamera *scam, int height) {
 		return;
 	}
 	height = data_to_game_coord(height);
-	auto cam = play.GetRoomCamera(scam->GetID());
+	auto cam = _GP(play).GetRoomCamera(scam->GetID());
 	cam->SetSize(Size(cam->GetRect().GetWidth(), height));
 }
 
@@ -137,7 +138,7 @@ bool Camera_GetAutoTracking(ScriptCamera *scam) {
 		debug_script_warn("Camera.AutoTracking: trying to use deleted camera");
 		return false;
 	}
-	return !play.GetRoomCamera(scam->GetID())->IsLocked();
+	return !_GP(play).GetRoomCamera(scam->GetID())->IsLocked();
 }
 
 void Camera_SetAutoTracking(ScriptCamera *scam, bool on) {
@@ -145,7 +146,7 @@ void Camera_SetAutoTracking(ScriptCamera *scam, bool on) {
 		debug_script_warn("Camera.AutoTracking: trying to use deleted camera");
 		return;
 	}
-	auto cam = play.GetRoomCamera(scam->GetID());
+	auto cam = _GP(play).GetRoomCamera(scam->GetID());
 	if (on)
 		cam->Release();
 	else
@@ -158,7 +159,7 @@ void Camera_SetAt(ScriptCamera *scam, int x, int y) {
 		return;
 	}
 	data_to_game_coords(&x, &y);
-	play.GetRoomCamera(scam->GetID())->LockAt(x, y);
+	_GP(play).GetRoomCamera(scam->GetID())->LockAt(x, y);
 }
 
 void Camera_SetSize(ScriptCamera *scam, int width, int height) {
@@ -167,7 +168,7 @@ void Camera_SetSize(ScriptCamera *scam, int width, int height) {
 		return;
 	}
 	data_to_game_coords(&width, &height);
-	play.GetRoomCamera(scam->GetID())->SetSize(Size(width, height));
+	_GP(play).GetRoomCamera(scam->GetID())->SetSize(Size(width, height));
 }
 
 RuntimeScriptValue Sc_Camera_Create(const RuntimeScriptValue *params, int32_t param_count) {
@@ -234,14 +235,14 @@ RuntimeScriptValue Sc_Camera_SetSize(void *self, const RuntimeScriptValue *param
 //=============================================================================
 
 ScriptViewport *Viewport_Create() {
-	auto view = play.CreateRoomViewport();
+	auto view = _GP(play).CreateRoomViewport();
 	if (!view)
 		return NULL;
-	return play.RegisterRoomViewport(view->GetID());
+	return _GP(play).RegisterRoomViewport(view->GetID());
 }
 
 void Viewport_Delete(ScriptViewport *scv) {
-	play.DeleteRoomViewport(scv->GetID());
+	_GP(play).DeleteRoomViewport(scv->GetID());
 }
 
 int Viewport_GetX(ScriptViewport *scv) {
@@ -249,7 +250,7 @@ int Viewport_GetX(ScriptViewport *scv) {
 		debug_script_warn("Viewport.X: trying to use deleted viewport");
 		return 0;
 	}
-	int x = play.GetRoomViewport(scv->GetID())->GetRect().Left;
+	int x = _GP(play).GetRoomViewport(scv->GetID())->GetRect().Left;
 	return game_to_data_coord(x);
 }
 
@@ -259,7 +260,7 @@ void Viewport_SetX(ScriptViewport *scv, int x) {
 		return;
 	}
 	x = data_to_game_coord(x);
-	auto view = play.GetRoomViewport(scv->GetID());
+	auto view = _GP(play).GetRoomViewport(scv->GetID());
 	view->SetAt(x, view->GetRect().Top);
 }
 
@@ -268,7 +269,7 @@ int Viewport_GetY(ScriptViewport *scv) {
 		debug_script_warn("Viewport.Y: trying to use deleted viewport");
 		return 0;
 	}
-	int y = play.GetRoomViewport(scv->GetID())->GetRect().Top;
+	int y = _GP(play).GetRoomViewport(scv->GetID())->GetRect().Top;
 	return game_to_data_coord(y);
 }
 
@@ -278,7 +279,7 @@ void Viewport_SetY(ScriptViewport *scv, int y) {
 		return;
 	}
 	y = data_to_game_coord(y);
-	auto view = play.GetRoomViewport(scv->GetID());
+	auto view = _GP(play).GetRoomViewport(scv->GetID());
 	view->SetAt(view->GetRect().Left, y);
 }
 
@@ -287,7 +288,7 @@ int Viewport_GetWidth(ScriptViewport *scv) {
 		debug_script_warn("Viewport.Width: trying to use deleted viewport");
 		return 0;
 	}
-	int width = play.GetRoomViewport(scv->GetID())->GetRect().GetWidth();
+	int width = _GP(play).GetRoomViewport(scv->GetID())->GetRect().GetWidth();
 	return game_to_data_coord(width);
 }
 
@@ -297,7 +298,7 @@ void Viewport_SetWidth(ScriptViewport *scv, int width) {
 		return;
 	}
 	width = data_to_game_coord(width);
-	auto view = play.GetRoomViewport(scv->GetID());
+	auto view = _GP(play).GetRoomViewport(scv->GetID());
 	view->SetSize(Size(width, view->GetRect().GetHeight()));
 }
 
@@ -306,7 +307,7 @@ int Viewport_GetHeight(ScriptViewport *scv) {
 		debug_script_warn("Viewport.Height: trying to use deleted viewport");
 		return 0;
 	}
-	int height = play.GetRoomViewport(scv->GetID())->GetRect().GetHeight();
+	int height = _GP(play).GetRoomViewport(scv->GetID())->GetRect().GetHeight();
 	return game_to_data_coord(height);
 }
 
@@ -316,7 +317,7 @@ void Viewport_SetHeight(ScriptViewport *scv, int height) {
 		return;
 	}
 	height = data_to_game_coord(height);
-	auto view = play.GetRoomViewport(scv->GetID());
+	auto view = _GP(play).GetRoomViewport(scv->GetID());
 	view->SetSize(Size(view->GetRect().GetWidth(), height));
 }
 
@@ -325,11 +326,11 @@ ScriptCamera *Viewport_GetCamera(ScriptViewport *scv) {
 		debug_script_warn("Viewport.Camera: trying to use deleted viewport");
 		return nullptr;
 	}
-	auto view = play.GetRoomViewport(scv->GetID());
+	auto view = _GP(play).GetRoomViewport(scv->GetID());
 	auto cam = view->GetCamera();
 	if (!cam)
 		return nullptr;
-	return play.GetScriptCamera(cam->GetID());
+	return _GP(play).GetScriptCamera(cam->GetID());
 }
 
 void Viewport_SetCamera(ScriptViewport *scv, ScriptCamera *scam) {
@@ -341,14 +342,14 @@ void Viewport_SetCamera(ScriptViewport *scv, ScriptCamera *scam) {
 		debug_script_warn("Viewport.Camera: trying to link deleted camera");
 		return;
 	}
-	auto view = play.GetRoomViewport(scv->GetID());
+	auto view = _GP(play).GetRoomViewport(scv->GetID());
 	// unlink previous camera
 	auto cam = view->GetCamera();
 	if (cam)
 		cam->UnlinkFromViewport(view->GetID());
 	// link new one
 	if (scam != nullptr) {
-		cam = play.GetRoomCamera(scam->GetID());
+		cam = _GP(play).GetRoomCamera(scam->GetID());
 		view->LinkCamera(cam);
 		cam->LinkToViewport(view);
 	} else {
@@ -361,7 +362,7 @@ bool Viewport_GetVisible(ScriptViewport *scv) {
 		debug_script_warn("Viewport.Visible: trying to use deleted viewport");
 		return false;
 	}
-	return play.GetRoomViewport(scv->GetID())->IsVisible();
+	return _GP(play).GetRoomViewport(scv->GetID())->IsVisible();
 }
 
 void Viewport_SetVisible(ScriptViewport *scv, bool on) {
@@ -369,7 +370,7 @@ void Viewport_SetVisible(ScriptViewport *scv, bool on) {
 		debug_script_warn("Viewport.Visible: trying to use deleted viewport");
 		return;
 	}
-	play.GetRoomViewport(scv->GetID())->SetVisible(on);
+	_GP(play).GetRoomViewport(scv->GetID())->SetVisible(on);
 }
 
 int Viewport_GetZOrder(ScriptViewport *scv) {
@@ -377,7 +378,7 @@ int Viewport_GetZOrder(ScriptViewport *scv) {
 		debug_script_warn("Viewport.ZOrder: trying to use deleted viewport");
 		return 0;
 	}
-	return play.GetRoomViewport(scv->GetID())->GetZOrder();
+	return _GP(play).GetRoomViewport(scv->GetID())->GetZOrder();
 }
 
 void Viewport_SetZOrder(ScriptViewport *scv, int zorder) {
@@ -385,16 +386,16 @@ void Viewport_SetZOrder(ScriptViewport *scv, int zorder) {
 		debug_script_warn("Viewport.ZOrder: trying to use deleted viewport");
 		return;
 	}
-	play.GetRoomViewport(scv->GetID())->SetZOrder(zorder);
-	play.InvalidateViewportZOrder();
+	_GP(play).GetRoomViewport(scv->GetID())->SetZOrder(zorder);
+	_GP(play).InvalidateViewportZOrder();
 }
 
 ScriptViewport *Viewport_GetAtScreenXY(int x, int y) {
 	data_to_game_coords(&x, &y);
-	PViewport view = play.GetRoomViewportAt(x, y);
+	PViewport view = _GP(play).GetRoomViewportAt(x, y);
 	if (!view)
 		return nullptr;
-	return play.GetScriptViewport(view->GetID());
+	return _GP(play).GetScriptViewport(view->GetID());
 }
 
 void Viewport_SetPosition(ScriptViewport *scv, int x, int y, int width, int height) {
@@ -404,7 +405,7 @@ void Viewport_SetPosition(ScriptViewport *scv, int x, int y, int width, int heig
 	}
 	data_to_game_coords(&x, &y);
 	data_to_game_coords(&width, &height);
-	play.GetRoomViewport(scv->GetID())->SetRect(RectWH(x, y, width, height));
+	_GP(play).GetRoomViewport(scv->GetID())->SetRect(RectWH(x, y, width, height));
 }
 
 ScriptUserObject *Viewport_ScreenToRoomPoint(ScriptViewport *scv, int scrx, int scry, bool clipViewport) {
@@ -414,7 +415,7 @@ ScriptUserObject *Viewport_ScreenToRoomPoint(ScriptViewport *scv, int scrx, int
 	}
 	data_to_game_coords(&scrx, &scry);
 
-	VpPoint vpt = play.GetRoomViewport(scv->GetID())->ScreenToRoom(scrx, scry, clipViewport);
+	VpPoint vpt = _GP(play).GetRoomViewport(scv->GetID())->ScreenToRoom(scrx, scry, clipViewport);
 	if (vpt.second < 0)
 		return nullptr;
 
@@ -428,8 +429,8 @@ ScriptUserObject *Viewport_RoomToScreenPoint(ScriptViewport *scv, int roomx, int
 		return nullptr;
 	}
 	data_to_game_coords(&roomx, &roomy);
-	Point pt = play.RoomToScreen(roomx, roomy);
-	if (clipViewport && !play.GetRoomViewport(scv->GetID())->GetRect().IsInside(pt.X, pt.Y))
+	Point pt = _GP(play).RoomToScreen(roomx, roomy);
+	if (clipViewport && !_GP(play).GetRoomViewport(scv->GetID())->GetRect().IsInside(pt.X, pt.Y))
 		return nullptr;
 
 	game_to_data_coords(pt.X, pt.Y);
diff --git a/engines/ags/engine/ac/walkablearea.cpp b/engines/ags/engine/ac/walkablearea.cpp
index e96bd93170..8ee8979250 100644
--- a/engines/ags/engine/ac/walkablearea.cpp
+++ b/engines/ags/engine/ac/walkablearea.cpp
@@ -39,7 +39,6 @@ namespace AGS3 {
 using namespace AGS::Shared;
 
 extern RoomStruct thisroom;
-extern GameState play;
 
 extern int displayed_room;
 extern RoomStatus *croom;
@@ -60,8 +59,8 @@ void redo_walkable_areas() {
 	for (hh = 0; hh < walkareabackup->GetHeight(); hh++) {
 		uint8_t *walls_scanline = thisroom.WalkAreaMask->GetScanLineForWriting(hh);
 		for (ww = 0; ww < walkareabackup->GetWidth(); ww++) {
-			//      if (play.walkable_areas_on[_getpixel(thisroom.WalkAreaMask,ww,hh)]==0)
-			if (play.walkable_areas_on[walls_scanline[ww]] == 0)
+			//      if (_GP(play).walkable_areas_on[_getpixel(thisroom.WalkAreaMask,ww,hh)]==0)
+			if (_GP(play).walkable_areas_on[walls_scanline[ww]] == 0)
 				walls_scanline[ww] = 0;
 		}
 	}
diff --git a/engines/ags/engine/ac/walkbehind.cpp b/engines/ags/engine/ac/walkbehind.cpp
index 2c53228b0d..b1f4cb484a 100644
--- a/engines/ags/engine/ac/walkbehind.cpp
+++ b/engines/ags/engine/ac/walkbehind.cpp
@@ -26,6 +26,7 @@
 #include "ags/engine/ac/gamestate.h"
 #include "ags/engine/gfx/graphicsdriver.h"
 #include "ags/shared/gfx/bitmap.h"
+#include "ags/globals.h"
 
 namespace AGS3 {
 
@@ -33,7 +34,6 @@ using namespace AGS::Shared;
 using namespace AGS::Engine;
 
 extern RoomStruct thisroom;
-extern GameState play;
 extern IGraphicsDriver *gfxDriver;
 
 
@@ -49,7 +49,7 @@ int walk_behind_baselines_changed = 0;
 
 void update_walk_behind_images() {
 	int ee, rr;
-	int bpp = (thisroom.BgFrames[play.bg_frame].Graphic->GetColorDepth() + 7) / 8;
+	int bpp = (thisroom.BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth() + 7) / 8;
 	Bitmap *wbbmp;
 	for (ee = 1; ee < MAX_WALK_BEHINDS; ee++) {
 		update_polled_stuff_if_runtime();
@@ -58,13 +58,13 @@ void update_walk_behind_images() {
 			wbbmp = BitmapHelper::CreateTransparentBitmap(
 				(walkBehindRight[ee] - walkBehindLeft[ee]) + 1,
 				(walkBehindBottom[ee] - walkBehindTop[ee]) + 1,
-				thisroom.BgFrames[play.bg_frame].Graphic->GetColorDepth());
+				thisroom.BgFrames[_GP(play).bg_frame].Graphic->GetColorDepth());
 			int yy, startX = walkBehindLeft[ee], startY = walkBehindTop[ee];
 			for (rr = startX; rr <= walkBehindRight[ee]; rr++) {
 				for (yy = startY; yy <= walkBehindBottom[ee]; yy++) {
 					if (thisroom.WalkBehindMask->GetScanLine(yy)[rr] == ee) {
 						for (int ii = 0; ii < bpp; ii++)
-							wbbmp->GetScanLineForWriting(yy - startY)[(rr - startX) * bpp + ii] = thisroom.BgFrames[play.bg_frame].Graphic->GetScanLine(yy)[rr * bpp + ii];
+							wbbmp->GetScanLineForWriting(yy - startY)[(rr - startX) * bpp + ii] = thisroom.BgFrames[_GP(play).bg_frame].Graphic->GetScanLine(yy)[rr * bpp + ii];
 					}
 				}
 			}
@@ -79,7 +79,7 @@ void update_walk_behind_images() {
 		}
 	}
 
-	walkBehindsCachedForBgNum = play.bg_frame;
+	walkBehindsCachedForBgNum = _GP(play).bg_frame;
 }
 
 
diff --git a/engines/ags/engine/debugging/debug.cpp b/engines/ags/engine/debugging/debug.cpp
index 37f11af862..907fa54a18 100644
--- a/engines/ags/engine/debugging/debug.cpp
+++ b/engines/ags/engine/debugging/debug.cpp
@@ -553,7 +553,7 @@ void scriptDebugHook(ccInstance *ccinst, int linenum) {
 int scrlockWasDown = 0;
 
 void check_debug_keys() {
-	if (play.debug_mode) {
+	if (_GP(play).debug_mode) {
 		// do the run-time script debugging
 
 		if (!::AGS::g_events->isKeyPressed(KEY_SCRLOCK) && scrlockWasDown)
diff --git a/engines/ags/engine/device/mousew32.cpp b/engines/ags/engine/device/mousew32.cpp
index eeac9025b8..e3f6af0518 100644
--- a/engines/ags/engine/device/mousew32.cpp
+++ b/engines/ags/engine/device/mousew32.cpp
@@ -192,7 +192,7 @@ void domouse(int str) {
 	int poow = _G(mousecurs)[(int)_G(currentcursor)]->GetWidth();
 	int pooh = _G(mousecurs)[(int)_G(currentcursor)]->GetHeight();
 	//int smx = _G(mousex) - hotxwas, smy = _G(mousey) - hotywas;
-	const Rect &viewport = play.GetMainViewport();
+	const Rect &viewport = _GP(play).GetMainViewport();
 
 	mgetgraphpos();
 	_G(mousex) -= _G(hotx);
@@ -283,23 +283,23 @@ int minstalled() {
 }
 
 void Mouse::AdjustPosition(int &x, int &y) {
-	x = GameScaling.X.UnScalePt(x) - play.GetMainViewport().Left;
-	y = GameScaling.Y.UnScalePt(y) - play.GetMainViewport().Top;
+	x = GameScaling.X.UnScalePt(x) - _GP(play).GetMainViewport().Left;
+	y = GameScaling.Y.UnScalePt(y) - _GP(play).GetMainViewport().Top;
 }
 
 void Mouse::SetGraphicArea() {
-	Rect dst_r = GameScaling.ScaleRange(play.GetMainViewport());
+	Rect dst_r = GameScaling.ScaleRange(_GP(play).GetMainViewport());
 	mgraphconfine(dst_r.Left, dst_r.Top, dst_r.Right, dst_r.Bottom);
 }
 
 void Mouse::SetMoveLimit(const Rect &r) {
-	Rect src_r = OffsetRect(r, play.GetMainViewport().GetLT());
+	Rect src_r = OffsetRect(r, _GP(play).GetMainViewport().GetLT());
 	Rect dst_r = GameScaling.ScaleRange(src_r);
 	msetcursorlimit(dst_r.Left, dst_r.Top, dst_r.Right, dst_r.Bottom);
 }
 
 void Mouse::SetPosition(const Point p) {
-	msetgraphpos(GameScaling.X.ScalePt(p.X + play.GetMainViewport().Left), GameScaling.Y.ScalePt(p.Y + play.GetMainViewport().Top));
+	msetgraphpos(GameScaling.X.ScalePt(p.X + _GP(play).GetMainViewport().Left), GameScaling.Y.ScalePt(p.Y + _GP(play).GetMainViewport().Top));
 }
 
 bool Mouse::IsLockedToWindow() {
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index f6c2938cba..6194e7833e 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -297,7 +297,7 @@ HError InitAndRegisterGameEntities() {
 	InitAndRegisterHotspots();
 	InitAndRegisterRegions();
 	InitAndRegisterRoomObjects();
-	play.CreatePrimaryViewportAndCamera();
+	_GP(play).CreatePrimaryViewportAndCamera();
 
 	RegisterStaticArrays();
 
@@ -387,7 +387,7 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 	actspswb = (Bitmap **)calloc(actSpsCount, sizeof(Bitmap *));
 	actspswbbmp = (IDriverDependantBitmap **)calloc(actSpsCount, sizeof(IDriverDependantBitmap *));
 	actspswbcache = (CachedActSpsData *)calloc(actSpsCount, sizeof(CachedActSpsData));
-	play.charProps.resize(_GP(game).numcharacters);
+	_GP(play).charProps.resize(_GP(game).numcharacters);
 	old_dialog_scripts = ents.OldDialogScripts;
 	old_speech_lines = ents.OldSpeechLines;
 	HError err = InitAndRegisterGameEntities();
@@ -406,8 +406,8 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 		svg_suffix.Format(".%s", _GP(game).saveGameFileExtension);
 	set_save_game_suffix(svg_suffix);
 
-	play.score_sound = _GP(game).scoreClipID;
-	play.fade_effect = _GP(game).options[OPT_FADETYPE];
+	_GP(play).score_sound = _GP(game).scoreClipID;
+	_GP(play).fade_effect = _GP(game).options[OPT_FADETYPE];
 
 	//
 	// 5. Initialize runtime state of certain game objects
@@ -416,7 +416,7 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
 		// labels are not clickable by default
 		guilabels[i].SetClickable(false);
 	}
-	play.gui_draw_order = (int32_t *)calloc(_GP(game).numgui * sizeof(int32_t), 1);
+	_GP(play).gui_draw_order = (int32_t *)calloc(_GP(game).numgui * sizeof(int32_t), 1);
 	update_gui_zorder();
 	calculate_reserved_channel_count();
 
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index c5691798a2..f10f4f0ad2 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -325,8 +325,8 @@ HSaveError OpenSavegame(const String &filename, SavegameDescription &desc, Saveg
 
 // Prepares engine for actual save restore (stops processes, cleans up memory)
 void DoBeforeRestore(PreservedParams &pp) {
-	pp.SpeechVOX = play.want_speech;
-	pp.MusicVOX = play.separate_music_lib;
+	pp.SpeechVOX = _GP(play).want_speech;
+	pp.MusicVOX = _GP(play).separate_music_lib;
 
 	unload_old_room();
 	delete raw_saved_screen;
@@ -369,8 +369,8 @@ void DoBeforeRestore(PreservedParams &pp) {
 		moduleInst[i] = nullptr;
 	}
 
-	play.FreeProperties();
-	play.FreeViewportsAndCameras();
+	_GP(play).FreeProperties();
+	_GP(play).FreeViewportsAndCameras();
 
 	delete roominstFork;
 	delete roominst;
@@ -404,7 +404,7 @@ void DoBeforeRestore(PreservedParams &pp) {
 void RestoreViewportsAndCameras(const RestoredData &r_data) {
 	for (size_t i = 0; i < r_data.Cameras.size(); ++i) {
 		const auto &cam_dat = r_data.Cameras[i];
-		auto cam = play.GetRoomCamera(i);
+		auto cam = _GP(play).GetRoomCamera(i);
 		cam->SetID(cam_dat.ID);
 		if ((cam_dat.Flags & kSvgCamPosLocked) != 0)
 			cam->Lock();
@@ -415,7 +415,7 @@ void RestoreViewportsAndCameras(const RestoredData &r_data) {
 	}
 	for (size_t i = 0; i < r_data.Viewports.size(); ++i) {
 		const auto &view_dat = r_data.Viewports[i];
-		auto view = play.GetRoomViewport(i);
+		auto view = _GP(play).GetRoomViewport(i);
 		view->SetID(view_dat.ID);
 		view->SetVisible((view_dat.Flags & kSvgViewportVisible) != 0);
 		view->SetRect(RectWH(view_dat.Left, view_dat.Top, view_dat.Width, view_dat.Height));
@@ -423,11 +423,11 @@ void RestoreViewportsAndCameras(const RestoredData &r_data) {
 		// Restore camera link
 		int cam_index = view_dat.CamID;
 		if (cam_index < 0) continue;
-		auto cam = play.GetRoomCamera(cam_index);
+		auto cam = _GP(play).GetRoomCamera(cam_index);
 		view->LinkCamera(cam);
 		cam->LinkToViewport(view);
 	}
-	play.InvalidateViewportZOrder();
+	_GP(play).InvalidateViewportZOrder();
 }
 
 // Final processing after successfully restoring from save
@@ -435,20 +435,20 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	// Use a yellow dialog highlight for older game versions
 	// CHECKME: it is dubious that this should be right here
 	if (loaded_game_file_version < kGameVersion_331)
-		play.dialog_options_highlight_color = DIALOG_OPTIONS_HIGHLIGHT_COLOR_DEFAULT;
+		_GP(play).dialog_options_highlight_color = DIALOG_OPTIONS_HIGHLIGHT_COLOR_DEFAULT;
 
 	// Preserve whether the music vox is available
-	play.separate_music_lib = pp.MusicVOX;
+	_GP(play).separate_music_lib = pp.MusicVOX;
 	// If they had the vox when they saved it, but they don't now
-	if ((pp.SpeechVOX < 0) && (play.want_speech >= 0))
-		play.want_speech = (-play.want_speech) - 1;
+	if ((pp.SpeechVOX < 0) && (_GP(play).want_speech >= 0))
+		_GP(play).want_speech = (-_GP(play).want_speech) - 1;
 	// If they didn't have the vox before, but now they do
-	else if ((pp.SpeechVOX >= 0) && (play.want_speech < 0))
-		play.want_speech = (-play.want_speech) - 1;
+	else if ((pp.SpeechVOX >= 0) && (_GP(play).want_speech < 0))
+		_GP(play).want_speech = (-_GP(play).want_speech) - 1;
 
 	// recache queued clips
-	for (int i = 0; i < play.new_music_queue_size; ++i) {
-		play.new_music_queue[i].cachedClip = nullptr;
+	for (int i = 0; i < _GP(play).new_music_queue_size; ++i) {
+		_GP(play).new_music_queue[i].cachedClip = nullptr;
 	}
 
 	// restore these to the ones retrieved from the save game
@@ -481,13 +481,13 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	setup_player_character(_GP(game).playercharacter);
 
 	// Save some parameters to restore them after room load
-	int gstimer = play.gscript_timer;
-	int oldx1 = play.mboundx1, oldx2 = play.mboundx2;
-	int oldy1 = play.mboundy1, oldy2 = play.mboundy2;
+	int gstimer = _GP(play).gscript_timer;
+	int oldx1 = _GP(play).mboundx1, oldx2 = _GP(play).mboundx2;
+	int oldy1 = _GP(play).mboundy1, oldy2 = _GP(play).mboundy2;
 
 	// disable the queue momentarily
-	int queuedMusicSize = play.music_queue_size;
-	play.music_queue_size = 0;
+	int queuedMusicSize = _GP(play).music_queue_size;
+	_GP(play).music_queue_size = 0;
 
 	update_polled_stuff_if_runtime();
 
@@ -497,7 +497,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 
 	update_polled_stuff_if_runtime();
 
-	play.gscript_timer = gstimer;
+	_GP(play).gscript_timer = gstimer;
 	// restore the correct room volume (they might have modified
 	// it with SetMusicVolume)
 	thisroom.Options.MusicVolume = r_data.RoomVolume;
@@ -511,7 +511,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	// ensure that the current cursor is locked
 	_GP(spriteset).Precache(_GP(game).mcurs[r_data.CursorID].pic);
 
-	::AGS::g_vm->set_window_title(play.game_name);
+	::AGS::g_vm->set_window_title(_GP(play).game_name);
 
 	update_polled_stuff_if_runtime();
 
@@ -541,17 +541,17 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 	gui_disabled_style = convert_gui_disabled_style(_GP(game).options[OPT_DISABLEOFF]);
 
 	// restore the queue now that the music is playing
-	play.music_queue_size = queuedMusicSize;
+	_GP(play).music_queue_size = queuedMusicSize;
 
-	if (play.digital_master_volume >= 0)
-		System_SetVolume(play.digital_master_volume);
+	if (_GP(play).digital_master_volume >= 0)
+		System_SetVolume(_GP(play).digital_master_volume);
 
 	// Run audio clips on channels
 	// these two crossfading parameters have to be temporarily reset
-	const int cf_in_chan = play.crossfading_in_channel;
-	const int cf_out_chan = play.crossfading_out_channel;
-	play.crossfading_in_channel = 0;
-	play.crossfading_out_channel = 0;
+	const int cf_in_chan = _GP(play).crossfading_in_channel;
+	const int cf_out_chan = _GP(play).crossfading_out_channel;
+	_GP(play).crossfading_in_channel = 0;
+	_GP(play).crossfading_out_channel = 0;
 
 	{
 		AudioChannelsLock lock;
@@ -579,9 +579,9 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 			}
 		}
 		if ((cf_in_chan > 0) && (lock.GetChannel(cf_in_chan) != nullptr))
-			play.crossfading_in_channel = cf_in_chan;
+			_GP(play).crossfading_in_channel = cf_in_chan;
 		if ((cf_out_chan > 0) && (lock.GetChannel(cf_out_chan) != nullptr))
-			play.crossfading_out_channel = cf_out_chan;
+			_GP(play).crossfading_out_channel = cf_out_chan;
 
 		// If there were synced audio tracks, the time taken to load in the
 		// different channels will have thrown them out of sync, so re-time it
@@ -613,7 +613,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 
 	RestoreViewportsAndCameras(r_data);
 
-	play.ClearIgnoreInput(); // don't keep ignored input after save restore
+	_GP(play).ClearIgnoreInput(); // don't keep ignored input after save restore
 	update_polled_stuff_if_runtime();
 
 	pl_run_plugin_hooks(AGSE_POSTRESTOREGAME, 0);
@@ -626,8 +626,8 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
 		first_room_initialization();
 	}
 
-	if ((play.music_queue_size > 0) && (cachedQueuedMusic == nullptr)) {
-		cachedQueuedMusic = load_music_from_disk(play.music_queue[0], 0);
+	if ((_GP(play).music_queue_size > 0) && (cachedQueuedMusic == nullptr)) {
+		cachedQueuedMusic = load_music_from_disk(_GP(play).music_queue[0], 0);
 	}
 
 	// Test if the old-style audio had playing music and it was properly loaded
@@ -724,9 +724,9 @@ PStream StartSavegame(const String &filename, const String &user_text, const Bit
 }
 
 void DoBeforeSave() {
-	if (play.cur_music_number >= 0) {
+	if (_GP(play).cur_music_number >= 0) {
 		if (IsMusicPlaying() == 0)
-			play.cur_music_number = -1;
+			_GP(play).cur_music_number = -1;
 	}
 
 	if (displayed_room >= 0) {
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index 1e7c40fdef..321357d739 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -216,7 +216,7 @@ HSaveError WriteGameState(PStream out) {
 	}
 
 	// Game state
-	play.WriteForSavegame(out.get());
+	_GP(play).WriteForSavegame(out.get());
 	// Other dynamic values
 	out->WriteInt32(frames_per_second);
 	out->WriteInt32(loopcounter);
@@ -229,15 +229,15 @@ HSaveError WriteGameState(PStream out) {
 
 	// Viewports and cameras
 	int viewcam_flags = 0;
-	if (play.IsAutoRoomViewport())
+	if (_GP(play).IsAutoRoomViewport())
 		viewcam_flags |= kSvgGameAutoRoomView;
 	out->WriteInt32(viewcam_flags);
-	out->WriteInt32(play.GetRoomCameraCount());
-	for (int i = 0; i < play.GetRoomCameraCount(); ++i)
-		WriteCameraState(*play.GetRoomCamera(i), out.get());
-	out->WriteInt32(play.GetRoomViewportCount());
-	for (int i = 0; i < play.GetRoomViewportCount(); ++i)
-		WriteViewportState(*play.GetRoomViewport(i), out.get());
+	out->WriteInt32(_GP(play).GetRoomCameraCount());
+	for (int i = 0; i < _GP(play).GetRoomCameraCount(); ++i)
+		WriteCameraState(*_GP(play).GetRoomCamera(i), out.get());
+	out->WriteInt32(_GP(play).GetRoomViewportCount());
+	for (int i = 0; i < _GP(play).GetRoomViewportCount(); ++i)
+		WriteViewportState(*_GP(play).GetRoomViewport(i), out.get());
 
 	return HSaveError::None();
 }
@@ -246,9 +246,9 @@ void ReadLegacyCameraState(Stream *in, RestoredData &r_data) {
 	// Precreate viewport and camera and save data in temp structs
 	int camx = in->ReadInt32();
 	int camy = in->ReadInt32();
-	play.CreateRoomCamera();
-	play.CreateRoomViewport();
-	const auto &main_view = play.GetMainViewport();
+	_GP(play).CreateRoomCamera();
+	_GP(play).CreateRoomViewport();
+	const auto &main_view = _GP(play).GetMainViewport();
 	RestoredData::CameraData cam_dat;
 	cam_dat.ID = 0;
 	cam_dat.Left = camx;
@@ -306,7 +306,7 @@ HSaveError ReadGameState(PStream in, int32_t cmp_ver, const PreservedParams &pp,
 	}
 
 	// Game state
-	play.ReadFromSavegame(in.get(), svg_ver, r_data);
+	_GP(play).ReadFromSavegame(in.get(), svg_ver, r_data);
 
 	// Other dynamic values
 	r_data.FPS = in->ReadInt32();
@@ -324,7 +324,7 @@ HSaveError ReadGameState(PStream in, int32_t cmp_ver, const PreservedParams &pp,
 		r_data.Cameras[0].Flags = r_data.Camera0_Flags;
 	} else {
 		int viewcam_flags = in->ReadInt32();
-		play.SetAutoRoomViewport((viewcam_flags & kSvgGameAutoRoomView) != 0);
+		_GP(play).SetAutoRoomViewport((viewcam_flags & kSvgGameAutoRoomView) != 0);
 		// TODO: we create viewport and camera objects here because they are
 		// required for the managed pool deserialization, but read actual
 		// data into temp structs because we need to apply it after active
@@ -332,12 +332,12 @@ HSaveError ReadGameState(PStream in, int32_t cmp_ver, const PreservedParams &pp,
 		// See comments to RestoredData struct for further details.
 		int cam_count = in->ReadInt32();
 		for (int i = 0; i < cam_count; ++i) {
-			play.CreateRoomCamera();
+			_GP(play).CreateRoomCamera();
 			ReadCameraState(r_data, in.get());
 		}
 		int view_count = in->ReadInt32();
 		for (int i = 0; i < view_count; ++i) {
-			play.CreateRoomViewport();
+			_GP(play).CreateRoomViewport();
 			ReadViewportState(r_data, in.get());
 		}
 	}
@@ -354,7 +354,7 @@ HSaveError WriteAudio(PStream out) {
 	// Audio types
 	for (size_t i = 0; i < _GP(game).audioClipTypes.size(); ++i) {
 		_GP(game).audioClipTypes[i].WriteToSavegame(out.get());
-		out->WriteInt32(play.default_audio_type_volumes[i]);
+		out->WriteInt32(_GP(play).default_audio_type_volumes[i]);
 	}
 
 	// Audio clips and crossfade
@@ -404,7 +404,7 @@ HSaveError ReadAudio(PStream in, int32_t cmp_ver, const PreservedParams &pp, Res
 	// Audio types
 	for (size_t i = 0; i < _GP(game).audioClipTypes.size(); ++i) {
 		_GP(game).audioClipTypes[i].ReadFromSavegame(in.get());
-		play.default_audio_type_volumes[i] = in->ReadInt32();
+		_GP(play).default_audio_type_volumes[i] = in->ReadInt32();
 	}
 
 	// Audio clips and crossfade
@@ -489,7 +489,7 @@ HSaveError WriteCharacters(PStream out) {
 	for (int i = 0; i < _GP(game).numcharacters; ++i) {
 		_GP(game).chars[i].WriteToFile(out.get());
 		charextra[i].WriteToFile(out.get());
-		Properties::WriteValues(play.charProps[i], out.get());
+		Properties::WriteValues(_GP(play).charProps[i], out.get());
 		if (loaded_game_file_version <= kGameVersion_272)
 			WriteTimesRun272(*_GP(game).intrChar[i], out.get());
 		// character movement path cache
@@ -505,7 +505,7 @@ HSaveError ReadCharacters(PStream in, int32_t cmp_ver, const PreservedParams &pp
 	for (int i = 0; i < _GP(game).numcharacters; ++i) {
 		_GP(game).chars[i].ReadFromFile(in.get());
 		charextra[i].ReadFromFile(in.get());
-		Properties::ReadValues(play.charProps[i], in.get());
+		Properties::ReadValues(_GP(play).charProps[i], in.get());
 		if (loaded_game_file_version <= kGameVersion_272)
 			ReadTimesRun272(*_GP(game).intrChar[i], in.get());
 		// character movement path cache
@@ -648,7 +648,7 @@ HSaveError WriteInventory(PStream out) {
 	out->WriteInt32(_GP(game).numinvitems);
 	for (int i = 0; i < _GP(game).numinvitems; ++i) {
 		_GP(game).invinfo[i].WriteToSavegame(out.get());
-		Properties::WriteValues(play.invProps[i], out.get());
+		Properties::WriteValues(_GP(play).invProps[i], out.get());
 		if (loaded_game_file_version <= kGameVersion_272)
 			WriteTimesRun272(*_GP(game).intrInv[i], out.get());
 	}
@@ -661,7 +661,7 @@ HSaveError ReadInventory(PStream in, int32_t cmp_ver, const PreservedParams &pp,
 		return err;
 	for (int i = 0; i < _GP(game).numinvitems; ++i) {
 		_GP(game).invinfo[i].ReadFromSavegame(in.get());
-		Properties::ReadValues(play.invProps[i], in.get());
+		Properties::ReadValues(_GP(play).invProps[i], in.get());
 		if (loaded_game_file_version <= kGameVersion_272)
 			ReadTimesRun272(*_GP(game).intrInv[i], in.get());
 	}
@@ -898,8 +898,8 @@ HSaveError WriteThisRoom(PStream out) {
 
 	// modified room backgrounds
 	for (int i = 0; i < MAX_ROOM_BGFRAMES; ++i) {
-		out->WriteBool(play.raw_modified[i] != 0);
-		if (play.raw_modified[i])
+		out->WriteBool(_GP(play).raw_modified[i] != 0);
+		if (_GP(play).raw_modified[i])
 			serialize_bitmap(thisroom.BgFrames[i].Graphic.get(), out.get());
 	}
 	out->WriteBool(raw_saved_screen != nullptr);
@@ -942,8 +942,8 @@ HSaveError ReadThisRoom(PStream in, int32_t cmp_ver, const PreservedParams &pp,
 
 	// modified room backgrounds
 	for (int i = 0; i < MAX_ROOM_BGFRAMES; ++i) {
-		play.raw_modified[i] = in->ReadBool();
-		if (play.raw_modified[i])
+		_GP(play).raw_modified[i] = in->ReadBool();
+		if (_GP(play).raw_modified[i])
 			r_data.RoomBkgScene[i].reset(read_serialized_bitmap(in.get()));
 		else
 			r_data.RoomBkgScene[i] = nullptr;
diff --git a/engines/ags/engine/gui/cscidialog.cpp b/engines/ags/engine/gui/cscidialog.cpp
index fc60eeb32b..ca8e720d1b 100644
--- a/engines/ags/engine/gui/cscidialog.cpp
+++ b/engines/ags/engine/gui/cscidialog.cpp
@@ -160,7 +160,7 @@ int CSCIWaitMessage(CSCIMessage *cscim) {
 		cscim->code = 0;
 		smcode = 0;
 		int keywas;
-		if (run_service_key_controls(keywas) && !play.IsIgnoringInput()) {
+		if (run_service_key_controls(keywas) && !_GP(play).IsIgnoringInput()) {
 			if (keywas == 13) {
 				cscim->id = finddefaultcontrol(CNF_DEFAULT);
 				cscim->code = CM_COMMAND;
@@ -180,7 +180,7 @@ int CSCIWaitMessage(CSCIMessage *cscim) {
 		}
 
 		int mbut, mwheelz;
-		if (run_service_mb_controls(mbut, mwheelz) && mbut >= 0 && !play.IsIgnoringInput()) {
+		if (run_service_mb_controls(mbut, mwheelz) && mbut >= 0 && !_GP(play).IsIgnoringInput()) {
 			if (checkcontrols()) {
 				cscim->id = controlid;
 				cscim->code = CM_COMMAND;
@@ -306,7 +306,7 @@ int finddefaultcontrol(int flagmask) {
 }
 
 int GetBaseWidth() {
-	return play.GetUIViewport().GetWidth();
+	return _GP(play).GetUIViewport().GetWidth();
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/main/config.cpp b/engines/ags/engine/main/config.cpp
index e4fb44663d..9d19938b80 100644
--- a/engines/ags/engine/main/config.cpp
+++ b/engines/ags/engine/main/config.cpp
@@ -51,11 +51,8 @@ namespace AGS3 {
 using namespace AGS::Shared;
 using namespace AGS::Engine;
 
-
 extern GameSetup usetup;
 
-extern GameState play;
-
 // Filename of the default config file, the one found in the game installation
 const String DefaultConfigFileName = "acsetup.cfg";
 
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 5c773ac680..7d6ef0c285 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -312,7 +312,7 @@ void engine_init_mouse() {
 }
 
 void engine_locate_speech_pak() {
-	play.want_speech = -2;
+	_GP(play).want_speech = -2;
 
 	if (!usetup.no_speech_pack) {
 		String speech_file = "speech.vox";
@@ -347,11 +347,11 @@ void engine_locate_speech_pak() {
 			}
 			AssetManager::SetDataFile(ResPaths.GamePak.Path); // switch back to the main data pack
 			Debug::Printf(kDbgMsg_Info, "Voice pack found and initialized.");
-			play.want_speech = 1;
+			_GP(play).want_speech = 1;
 		} else if (Path::ComparePaths(ResPaths.DataDir, get_voice_install_dir()) != 0) {
 			// If we have custom voice directory set, we will enable voice-over even if speech.vox does not exist
 			Debug::Printf(kDbgMsg_Info, "Voice pack was not found, but voice installation directory is defined: enabling voice-over.");
-			play.want_speech = 1;
+			_GP(play).want_speech = 1;
 		}
 		ResPaths.SpeechPak.Name = speech_file;
 		ResPaths.SpeechPak.Path = speech_filepath;
@@ -359,14 +359,14 @@ void engine_locate_speech_pak() {
 }
 
 void engine_locate_audio_pak() {
-	play.separate_music_lib = 0;
+	_GP(play).separate_music_lib = 0;
 	String music_file = _GP(game).GetAudioVOXName();
 	String music_filepath = find_assetlib(music_file);
 	if (!music_filepath.IsEmpty()) {
 		if (AssetManager::SetDataFile(music_filepath) == kAssetNoError) {
 			AssetManager::SetDataFile(ResPaths.GamePak.Path);
 			Debug::Printf(kDbgMsg_Info, "%s found and initialized.", music_file.GetCStr());
-			play.separate_music_lib = 1;
+			_GP(play).separate_music_lib = 1;
 			ResPaths.AudioPak.Name = music_file;
 			ResPaths.AudioPak.Path = music_filepath;
 		} else {
@@ -405,8 +405,8 @@ void engine_init_audio() {
 	if (usetup.audio_backend == 0) {
 		// all audio is disabled
 		// and the voice mode should not go to Voice Only
-		play.want_speech = -2;
-		play.separate_music_lib = 0;
+		_GP(play).want_speech = -2;
+		_GP(play).separate_music_lib = 0;
 	}
 }
 
@@ -438,8 +438,8 @@ void engine_init_exit_handler() {
 }
 
 void engine_init_rand() {
-	play.randseed = g_system->getMillis();
-	::AGS::g_vm->setRandomNumberSeed(play.randseed);
+	_GP(play).randseed = g_system->getMillis();
+	::AGS::g_vm->setRandomNumberSeed(_GP(play).randseed);
 }
 
 void engine_init_pathfinder() {
@@ -607,7 +607,7 @@ void show_preload() {
 		if (gfxDriver->UsesMemoryBackBuffer())
 			gfxDriver->GetMemoryBackBuffer()->Clear();
 
-		const Rect &view = play.GetMainViewport();
+		const Rect &view = _GP(play).GetMainViewport();
 		Bitmap *tsc = BitmapHelper::CreateBitmapCopy(splashsc, _GP(game).GetColorDepth());
 		if (!gfxDriver->HasAcceleratedTransform() && view.GetSize() != tsc->GetSize()) {
 			Bitmap *stretched = new Bitmap(view.GetWidth(), view.GetHeight(), tsc->GetColorDepth());
@@ -730,157 +730,157 @@ void engine_init_game_settings() {
 		if (_GP(game).invinfo[ee].flags & IFLG_STARTWITH) playerchar->inv[ee] = 1;
 		else playerchar->inv[ee] = 0;
 	}
-	play.score = 0;
-	play.sierra_inv_color = 7;
+	_GP(play).score = 0;
+	_GP(play).sierra_inv_color = 7;
 	// copy the value set by the editor
 	if (_GP(game).options[OPT_GLOBALTALKANIMSPD] >= 0) {
-		play.talkanim_speed = _GP(game).options[OPT_GLOBALTALKANIMSPD];
+		_GP(play).talkanim_speed = _GP(game).options[OPT_GLOBALTALKANIMSPD];
 		_GP(game).options[OPT_GLOBALTALKANIMSPD] = 1;
 	} else {
-		play.talkanim_speed = -_GP(game).options[OPT_GLOBALTALKANIMSPD] - 1;
+		_GP(play).talkanim_speed = -_GP(game).options[OPT_GLOBALTALKANIMSPD] - 1;
 		_GP(game).options[OPT_GLOBALTALKANIMSPD] = 0;
 	}
-	play.inv_item_wid = 40;
-	play.inv_item_hit = 22;
-	play.messagetime = -1;
-	play.disabled_user_interface = 0;
-	play.gscript_timer = -1;
-	play.debug_mode = _GP(game).options[OPT_DEBUGMODE];
-	play.inv_top = 0;
-	play.inv_numdisp = 0;
-	play.obsolete_inv_numorder = 0;
-	play.text_speed = 15;
-	play.text_min_display_time_ms = 1000;
-	play.ignore_user_input_after_text_timeout_ms = 500;
-	play.ClearIgnoreInput();
-	play.lipsync_speed = 15;
-	play.close_mouth_speech_time = 10;
-	play.disable_antialiasing = 0;
-	play.rtint_enabled = false;
-	play.rtint_level = 0;
-	play.rtint_light = 0;
-	play.text_speed_modifier = 0;
-	play.text_align = kHAlignLeft;
+	_GP(play).inv_item_wid = 40;
+	_GP(play).inv_item_hit = 22;
+	_GP(play).messagetime = -1;
+	_GP(play).disabled_user_interface = 0;
+	_GP(play).gscript_timer = -1;
+	_GP(play).debug_mode = _GP(game).options[OPT_DEBUGMODE];
+	_GP(play).inv_top = 0;
+	_GP(play).inv_numdisp = 0;
+	_GP(play).obsolete_inv_numorder = 0;
+	_GP(play).text_speed = 15;
+	_GP(play).text_min_display_time_ms = 1000;
+	_GP(play).ignore_user_input_after_text_timeout_ms = 500;
+	_GP(play).ClearIgnoreInput();
+	_GP(play).lipsync_speed = 15;
+	_GP(play).close_mouth_speech_time = 10;
+	_GP(play).disable_antialiasing = 0;
+	_GP(play).rtint_enabled = false;
+	_GP(play).rtint_level = 0;
+	_GP(play).rtint_light = 0;
+	_GP(play).text_speed_modifier = 0;
+	_GP(play).text_align = kHAlignLeft;
 	// Make the default alignment to the right with right-to-left text
 	if (_GP(game).options[OPT_RIGHTLEFTWRITE])
-		play.text_align = kHAlignRight;
-
-	play.speech_bubble_width = get_fixed_pixel_size(100);
-	play.bg_frame = 0;
-	play.bg_frame_locked = 0;
-	play.bg_anim_delay = 0;
-	play.anim_background_speed = 0;
-	play.silent_midi = 0;
-	play.current_music_repeating = 0;
-	play.skip_until_char_stops = -1;
-	play.get_loc_name_last_time = -1;
-	play.get_loc_name_save_cursor = -1;
-	play.restore_cursor_mode_to = -1;
-	play.restore_cursor_image_to = -1;
-	play.ground_level_areas_disabled = 0;
-	play.next_screen_transition = -1;
-	play.temporarily_turned_off_character = -1;
-	play.inv_backwards_compatibility = 0;
-	play.gamma_adjustment = 100;
-	play.do_once_tokens.resize(0);
-	play.music_queue_size = 0;
-	play.shakesc_length = 0;
-	play.wait_counter = 0;
-	play.key_skip_wait = SKIP_NONE;
-	play.cur_music_number = -1;
-	play.music_repeat = 1;
-	play.music_master_volume = 100 + LegacyMusicMasterVolumeAdjustment;
-	play.digital_master_volume = 100;
-	play.screen_flipped = 0;
-	play.cant_skip_speech = user_to_internal_skip_speech((SkipSpeechStyle)_GP(game).options[OPT_NOSKIPTEXT]);
-	play.sound_volume = 255;
-	play.speech_volume = 255;
-	play.normal_font = 0;
-	play.speech_font = 1;
-	play.speech_text_shadow = 16;
-	play.screen_tint = -1;
-	play.bad_parsed_word[0] = 0;
-	play.swap_portrait_side = 0;
-	play.swap_portrait_lastchar = -1;
-	play.swap_portrait_lastlastchar = -1;
-	play.in_conversation = 0;
-	play.skip_display = 3;
-	play.no_multiloop_repeat = 0;
-	play.in_cutscene = 0;
-	play.fast_forward = 0;
-	play.totalscore = _GP(game).totalscore;
-	play.roomscript_finished = 0;
-	play.no_textbg_when_voice = 0;
-	play.max_dialogoption_width = get_fixed_pixel_size(180);
-	play.no_hicolor_fadein = 0;
-	play.bgspeech_game_speed = 0;
-	play.bgspeech_stay_on_display = 0;
-	play.unfactor_speech_from_textlength = 0;
-	play.mp3_loop_before_end = 70;
-	play.speech_music_drop = 60;
-	play.room_changes = 0;
-	play.check_interaction_only = 0;
-	play.replay_hotkey_unused = -1;  // StartRecording: not supported.
-	play.dialog_options_x = 0;
-	play.dialog_options_y = 0;
-	play.min_dialogoption_width = 0;
-	play.disable_dialog_parser = 0;
-	play.ambient_sounds_persist = 0;
-	play.screen_is_faded_out = 0;
-	play.player_on_region = 0;
-	play.top_bar_backcolor = 8;
-	play.top_bar_textcolor = 16;
-	play.top_bar_bordercolor = 8;
-	play.top_bar_borderwidth = 1;
-	play.top_bar_ypos = 25;
-	play.top_bar_font = -1;
-	play.screenshot_width = 160;
-	play.screenshot_height = 100;
-	play.speech_text_align = kHAlignCenter;
-	play.auto_use_walkto_points = 1;
-	play.inventory_greys_out = 0;
-	play.skip_speech_specific_key = 0;
-	play.abort_key = 324;  // Alt+X
-	play.fade_to_red = 0;
-	play.fade_to_green = 0;
-	play.fade_to_blue = 0;
-	play.show_single_dialog_option = 0;
-	play.keep_screen_during_instant_transition = 0;
-	play.read_dialog_option_colour = -1;
-	play.speech_portrait_placement = 0;
-	play.speech_portrait_x = 0;
-	play.speech_portrait_y = 0;
-	play.speech_display_post_time_ms = 0;
-	play.dialog_options_highlight_color = DIALOG_OPTIONS_HIGHLIGHT_COLOR_DEFAULT;
-	play.speech_has_voice = false;
-	play.speech_voice_blocking = false;
-	play.speech_in_post_state = false;
-	play.narrator_speech = _GP(game).playercharacter;
-	play.crossfading_out_channel = 0;
-	play.speech_textwindow_gui = _GP(game).options[OPT_TWCUSTOM];
-	if (play.speech_textwindow_gui == 0)
-		play.speech_textwindow_gui = -1;
-	strcpy(play.game_name, _GP(game).gamename);
-	play.lastParserEntry[0] = 0;
-	play.follow_change_room_timer = 150;
+		_GP(play).text_align = kHAlignRight;
+
+	_GP(play).speech_bubble_width = get_fixed_pixel_size(100);
+	_GP(play).bg_frame = 0;
+	_GP(play).bg_frame_locked = 0;
+	_GP(play).bg_anim_delay = 0;
+	_GP(play).anim_background_speed = 0;
+	_GP(play).silent_midi = 0;
+	_GP(play).current_music_repeating = 0;
+	_GP(play).skip_until_char_stops = -1;
+	_GP(play).get_loc_name_last_time = -1;
+	_GP(play).get_loc_name_save_cursor = -1;
+	_GP(play).restore_cursor_mode_to = -1;
+	_GP(play).restore_cursor_image_to = -1;
+	_GP(play).ground_level_areas_disabled = 0;
+	_GP(play).next_screen_transition = -1;
+	_GP(play).temporarily_turned_off_character = -1;
+	_GP(play).inv_backwards_compatibility = 0;
+	_GP(play).gamma_adjustment = 100;
+	_GP(play).do_once_tokens.resize(0);
+	_GP(play).music_queue_size = 0;
+	_GP(play).shakesc_length = 0;
+	_GP(play).wait_counter = 0;
+	_GP(play).key_skip_wait = SKIP_NONE;
+	_GP(play).cur_music_number = -1;
+	_GP(play).music_repeat = 1;
+	_GP(play).music_master_volume = 100 + LegacyMusicMasterVolumeAdjustment;
+	_GP(play).digital_master_volume = 100;
+	_GP(play).screen_flipped = 0;
+	_GP(play).cant_skip_speech = user_to_internal_skip_speech((SkipSpeechStyle)_GP(game).options[OPT_NOSKIPTEXT]);
+	_GP(play).sound_volume = 255;
+	_GP(play).speech_volume = 255;
+	_GP(play).normal_font = 0;
+	_GP(play).speech_font = 1;
+	_GP(play).speech_text_shadow = 16;
+	_GP(play).screen_tint = -1;
+	_GP(play).bad_parsed_word[0] = 0;
+	_GP(play).swap_portrait_side = 0;
+	_GP(play).swap_portrait_lastchar = -1;
+	_GP(play).swap_portrait_lastlastchar = -1;
+	_GP(play).in_conversation = 0;
+	_GP(play).skip_display = 3;
+	_GP(play).no_multiloop_repeat = 0;
+	_GP(play).in_cutscene = 0;
+	_GP(play).fast_forward = 0;
+	_GP(play).totalscore = _GP(game).totalscore;
+	_GP(play).roomscript_finished = 0;
+	_GP(play).no_textbg_when_voice = 0;
+	_GP(play).max_dialogoption_width = get_fixed_pixel_size(180);
+	_GP(play).no_hicolor_fadein = 0;
+	_GP(play).bgspeech_game_speed = 0;
+	_GP(play).bgspeech_stay_on_display = 0;
+	_GP(play).unfactor_speech_from_textlength = 0;
+	_GP(play).mp3_loop_before_end = 70;
+	_GP(play).speech_music_drop = 60;
+	_GP(play).room_changes = 0;
+	_GP(play).check_interaction_only = 0;
+	_GP(play).replay_hotkey_unused = -1;  // StartRecording: not supported.
+	_GP(play).dialog_options_x = 0;
+	_GP(play).dialog_options_y = 0;
+	_GP(play).min_dialogoption_width = 0;
+	_GP(play).disable_dialog_parser = 0;
+	_GP(play).ambient_sounds_persist = 0;
+	_GP(play).screen_is_faded_out = 0;
+	_GP(play).player_on_region = 0;
+	_GP(play).top_bar_backcolor = 8;
+	_GP(play).top_bar_textcolor = 16;
+	_GP(play).top_bar_bordercolor = 8;
+	_GP(play).top_bar_borderwidth = 1;
+	_GP(play).top_bar_ypos = 25;
+	_GP(play).top_bar_font = -1;
+	_GP(play).screenshot_width = 160;
+	_GP(play).screenshot_height = 100;
+	_GP(play).speech_text_align = kHAlignCenter;
+	_GP(play).auto_use_walkto_points = 1;
+	_GP(play).inventory_greys_out = 0;
+	_GP(play).skip_speech_specific_key = 0;
+	_GP(play).abort_key = 324;  // Alt+X
+	_GP(play).fade_to_red = 0;
+	_GP(play).fade_to_green = 0;
+	_GP(play).fade_to_blue = 0;
+	_GP(play).show_single_dialog_option = 0;
+	_GP(play).keep_screen_during_instant_transition = 0;
+	_GP(play).read_dialog_option_colour = -1;
+	_GP(play).speech_portrait_placement = 0;
+	_GP(play).speech_portrait_x = 0;
+	_GP(play).speech_portrait_y = 0;
+	_GP(play).speech_display_post_time_ms = 0;
+	_GP(play).dialog_options_highlight_color = DIALOG_OPTIONS_HIGHLIGHT_COLOR_DEFAULT;
+	_GP(play).speech_has_voice = false;
+	_GP(play).speech_voice_blocking = false;
+	_GP(play).speech_in_post_state = false;
+	_GP(play).narrator_speech = _GP(game).playercharacter;
+	_GP(play).crossfading_out_channel = 0;
+	_GP(play).speech_textwindow_gui = _GP(game).options[OPT_TWCUSTOM];
+	if (_GP(play).speech_textwindow_gui == 0)
+		_GP(play).speech_textwindow_gui = -1;
+	strcpy(_GP(play).game_name, _GP(game).gamename);
+	_GP(play).lastParserEntry[0] = 0;
+	_GP(play).follow_change_room_timer = 150;
 	for (ee = 0; ee < MAX_ROOM_BGFRAMES; ee++)
-		play.raw_modified[ee] = 0;
-	play.game_speed_modifier = 0;
+		_GP(play).raw_modified[ee] = 0;
+	_GP(play).game_speed_modifier = 0;
 	if (debug_flags & DBG_DEBUGMODE)
-		play.debug_mode = 1;
+		_GP(play).debug_mode = 1;
 	gui_disabled_style = convert_gui_disabled_style(_GP(game).options[OPT_DISABLEOFF]);
-	play.shake_screen_yoff = 0;
+	_GP(play).shake_screen_yoff = 0;
 
-	memset(&play.walkable_areas_on[0], 1, MAX_WALK_AREAS + 1);
-	memset(&play.script_timers[0], 0, MAX_TIMERS * sizeof(int32_t));
-	memset(&play.default_audio_type_volumes[0], -1, MAX_AUDIO_TYPES * sizeof(int32_t));
+	memset(&_GP(play).walkable_areas_on[0], 1, MAX_WALK_AREAS + 1);
+	memset(&_GP(play).script_timers[0], 0, MAX_TIMERS * sizeof(int32_t));
+	memset(&_GP(play).default_audio_type_volumes[0], -1, MAX_AUDIO_TYPES * sizeof(int32_t));
 
 	// reset graphical script vars (they're still used by some games)
 	for (ee = 0; ee < MAXGLOBALVARS; ee++)
-		play.globalvars[ee] = 0;
+		_GP(play).globalvars[ee] = 0;
 
 	for (ee = 0; ee < MAXGLOBALSTRINGS; ee++)
-		play.globalstrings[ee][0] = 0;
+		_GP(play).globalstrings[ee][0] = 0;
 
 	if (!usetup.translation.IsEmpty())
 		init_translation(usetup.translation, "", true);
diff --git a/engines/ags/engine/main/engine_setup.cpp b/engines/ags/engine/main/engine_setup.cpp
index 2e86041a01..f375549365 100644
--- a/engines/ags/engine/main/engine_setup.cpp
+++ b/engines/ags/engine/main/engine_setup.cpp
@@ -125,8 +125,8 @@ void convert_objects_to_data_resolution(GameDataVersion filever) {
 void engine_setup_system_gamesize() {
 	scsystem.width = _GP(game).GetGameRes().Width;
 	scsystem.height = _GP(game).GetGameRes().Height;
-	scsystem.viewport_width = game_to_data_coord(play.GetMainViewport().GetWidth());
-	scsystem.viewport_height = game_to_data_coord(play.GetMainViewport().GetHeight());
+	scsystem.viewport_width = game_to_data_coord(_GP(play).GetMainViewport().GetWidth());
+	scsystem.viewport_height = game_to_data_coord(_GP(play).GetMainViewport().GetHeight());
 }
 
 void engine_init_resolution_settings(const Size game_size) {
@@ -140,8 +140,8 @@ void engine_init_resolution_settings(const Size game_size) {
 	convert_objects_to_data_resolution(loaded_game_file_version);
 
 	Rect viewport = RectWH(game_size);
-	play.SetMainViewport(viewport);
-	play.SetUIViewport(viewport);
+	_GP(play).SetMainViewport(viewport);
+	_GP(play).SetUIViewport(viewport);
 	engine_setup_system_gamesize();
 }
 
@@ -338,10 +338,10 @@ void on_coordinates_scaling_changed() {
 	// Reset mouse graphic area and bounds
 	Mouse::SetGraphicArea();
 	// If mouse bounds do not have valid values yet, then limit cursor to viewport
-	if (play.mboundx1 == 0 && play.mboundy1 == 0 && play.mboundx2 == 0 && play.mboundy2 == 0)
-		Mouse::SetMoveLimit(play.GetMainViewport());
+	if (_GP(play).mboundx1 == 0 && _GP(play).mboundy1 == 0 && _GP(play).mboundx2 == 0 && _GP(play).mboundy2 == 0)
+		Mouse::SetMoveLimit(_GP(play).GetMainViewport());
 	else
-		Mouse::SetMoveLimit(Rect(play.mboundx1, play.mboundy1, play.mboundx2, play.mboundy2));
+		Mouse::SetMoveLimit(Rect(_GP(play).mboundx1, _GP(play).mboundy1, _GP(play).mboundx2, _GP(play).mboundy2));
 }
 
 } // namespace AGS3
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index f53af57d99..ffdbbfbe76 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -89,7 +89,6 @@ extern int in_leaves_screen;
 extern int inside_script, in_graph_script;
 extern int no_blocking_functions;
 extern CharacterInfo *playerchar;
-extern GameState play;
 extern int mouse_ifacebut_xoffs, mouse_ifacebut_yoffs;
 extern int cur_mode;
 extern RoomObject *objs;
@@ -161,7 +160,7 @@ static void game_loop_do_late_update() {
 }
 
 static int game_loop_check_ground_level_interactions() {
-	if ((play.ground_level_areas_disabled & GLED_INTERACTION) == 0) {
+	if ((_GP(play).ground_level_areas_disabled & GLED_INTERACTION) == 0) {
 		// check if he's standing on a hotspot
 		int hotspotThere = get_hotspot_at(playerchar->x, playerchar->y);
 		// run Stands on Hotspot event
@@ -171,12 +170,12 @@ static int game_loop_check_ground_level_interactions() {
 		int onRegion = GetRegionIDAtRoom(playerchar->x, playerchar->y);
 		int inRoom = displayed_room;
 
-		if (onRegion != play.player_on_region) {
-			// we need to save this and set play.player_on_region
+		if (onRegion != _GP(play).player_on_region) {
+			// we need to save this and set _GP(play).player_on_region
 			// now, so it's correct going into RunRegionInteraction
-			int oldRegion = play.player_on_region;
+			int oldRegion = _GP(play).player_on_region;
 
-			play.player_on_region = onRegion;
+			_GP(play).player_on_region = onRegion;
 			// Walks Off last region
 			if (oldRegion > 0)
 				RunRegionInteraction(oldRegion, 2);
@@ -184,8 +183,8 @@ static int game_loop_check_ground_level_interactions() {
 			if (onRegion > 0)
 				RunRegionInteraction(onRegion, 1);
 		}
-		if (play.player_on_region > 0)   // player stands on region
-			RunRegionInteraction(play.player_on_region, 0);
+		if (_GP(play).player_on_region > 0)   // player stands on region
+			RunRegionInteraction(_GP(play).player_on_region, 0);
 
 		// one of the region interactions sent us to another room
 		if (inRoom != displayed_room) {
@@ -246,14 +245,14 @@ static void check_mouse_controls() {
 
 		check_skip_cutscene_mclick(mbut);
 
-		if (play.fast_forward || play.IsIgnoringInput()) {
+		if (_GP(play).fast_forward || _GP(play).IsIgnoringInput()) {
 			/* do nothing if skipping cutscene or input disabled */
-		} else if ((play.wait_counter != 0) && (play.key_skip_wait & SKIP_MOUSECLICK) != 0) {
-			play.wait_counter = 0;
-			play.wait_skipped_by = SKIP_MOUSECLICK;
-			play.wait_skipped_by_data = mbut;
+		} else if ((_GP(play).wait_counter != 0) && (_GP(play).key_skip_wait & SKIP_MOUSECLICK) != 0) {
+			_GP(play).wait_counter = 0;
+			_GP(play).wait_skipped_by = SKIP_MOUSECLICK;
+			_GP(play).wait_skipped_by_data = mbut;
 		} else if (is_text_overlay > 0) {
-			if (play.cant_skip_speech & SKIP_MOUSECLICK)
+			if (_GP(play).cant_skip_speech & SKIP_MOUSECLICK)
 				remove_screen_overlay(OVER_TEXTMSG);
 		} else if (!IsInterfaceEnabled()); // blocking cutscene, ignore mouse
 		else if (pl_run_plugin_hooks(AGSE_MOUSECLICK, mbut + 1)) {
@@ -372,10 +371,10 @@ static void check_keyboard_controls() {
 	}
 	// Then, check cutscene skip
 	check_skip_cutscene_keypress(kgn);
-	if (play.fast_forward) {
+	if (_GP(play).fast_forward) {
 		return;
 	}
-	if (play.IsIgnoringInput()) {
+	if (_GP(play).IsIgnoringInput()) {
 		return;
 	}
 	// Now check for in-game controls
@@ -386,18 +385,18 @@ static void check_keyboard_controls() {
 	}
 
 	// debug console
-	if ((kgn == '`') && (play.debug_mode > 0)) {
+	if ((kgn == '`') && (_GP(play).debug_mode > 0)) {
 		display_console = !display_console;
 		return;
 	}
 
 	// skip speech if desired by Speech.SkipStyle
-	if ((is_text_overlay > 0) && (play.cant_skip_speech & SKIP_KEYPRESS)) {
+	if ((is_text_overlay > 0) && (_GP(play).cant_skip_speech & SKIP_KEYPRESS)) {
 		// only allow a key to remove the overlay if the icon bar isn't up
 		if (IsGamePaused() == 0) {
 			// check if it requires a specific keypress
-			if ((play.skip_speech_specific_key > 0) &&
-				(kgn != play.skip_speech_specific_key)) {
+			if ((_GP(play).skip_speech_specific_key > 0) &&
+				(kgn != _GP(play).skip_speech_specific_key)) {
 			} else
 				remove_screen_overlay(OVER_TEXTMSG);
 		}
@@ -405,10 +404,10 @@ static void check_keyboard_controls() {
 		return;
 	}
 
-	if ((play.wait_counter != 0) && (play.key_skip_wait & SKIP_KEYPRESS) != 0) {
-		play.wait_counter = 0;
-		play.wait_skipped_by = SKIP_KEYPRESS;
-		play.wait_skipped_by_data = kgn;
+	if ((_GP(play).wait_counter != 0) && (_GP(play).key_skip_wait & SKIP_KEYPRESS) != 0) {
+		_GP(play).wait_counter = 0;
+		_GP(play).wait_skipped_by = SKIP_KEYPRESS;
+		_GP(play).wait_skipped_by_data = kgn;
 		debug_script_log("Keypress code %d ignored - in Wait", kgn);
 		return;
 	}
@@ -419,7 +418,7 @@ static void check_keyboard_controls() {
 		return;
 	}
 
-	if ((kgn == eAGSKeyCodeCtrlD) && (play.debug_mode > 0)) {
+	if ((kgn == eAGSKeyCodeCtrlD) && (_GP(play).debug_mode > 0)) {
 		// ctrl+D - show info
 		char infobuf[900];
 		int ff;
@@ -428,7 +427,7 @@ static void check_keyboard_controls() {
 			displayed_room, (noWalkBehindsAtAll ? "(has no walk-behinds)" : ""), playerchar->x, playerchar->y,
 			playerchar->view + 1, playerchar->loop, playerchar->frame,
 			(IsGamePaused() == 0) ? "" : "[Game paused.",
-			(play.ground_level_areas_disabled == 0) ? "" : "[Ground areas disabled.",
+			(_GP(play).ground_level_areas_disabled == 0) ? "" : "[Ground areas disabled.",
 			(IsInterfaceEnabled() == 0) ? "[Game in Wait state" : "");
 		for (ff = 0; ff < croom->numobj; ff++) {
 			if (ff >= 8) break; // buffer not big enough for more than 7
@@ -467,19 +466,19 @@ static void check_keyboard_controls() {
 	}
 
 	// if (kgn == key_ctrl_u) {
-	//     play.debug_mode++;
+	//     _GP(play).debug_mode++;
 	//     script_debug(5,0);
-	//     play.debug_mode--;
+	//     _GP(play).debug_mode--;
 	//     return;
 	// }
 
 	if (kgn == eAGSKeyCodeAltV && (::AGS::g_events->getModifierFlags() & KB_CTRL_FLAG)
-			&& (play.wait_counter < 1) && (is_text_overlay == 0) && (restrict_until == 0)) {
+			&& (_GP(play).wait_counter < 1) && (is_text_overlay == 0) && (restrict_until == 0)) {
 		// make sure we can't interrupt a Wait()
 		// and desync the music to cutscene
-		play.debug_mode++;
+		_GP(play).debug_mode++;
 		script_debug(1, 0);
-		play.debug_mode--;
+		_GP(play).debug_mode--;
 
 		return;
 	}
@@ -559,7 +558,7 @@ static void check_room_edges(int numevents_was) {
 		int edgesActivated[4] = { 0, 0, 0, 0 };
 		// Only do it if nothing else has happened (eg. mouseclick)
 		if ((numevents == numevents_was) &&
-			((play.ground_level_areas_disabled & GLED_INTERACTION) == 0)) {
+			((_GP(play).ground_level_areas_disabled & GLED_INTERACTION) == 0)) {
 
 			if (playerchar->x <= thisroom.Edges.Left)
 				edgesActivated[0] = 1;
@@ -570,13 +569,13 @@ static void check_room_edges(int numevents_was) {
 			else if (playerchar->y <= thisroom.Edges.Top)
 				edgesActivated[3] = 1;
 
-			if ((play.entered_edge >= 0) && (play.entered_edge <= 3)) {
+			if ((_GP(play).entered_edge >= 0) && (_GP(play).entered_edge <= 3)) {
 				// once the player is no longer outside the edge, forget the stored edge
-				if (edgesActivated[play.entered_edge] == 0)
-					play.entered_edge = -10;
+				if (edgesActivated[_GP(play).entered_edge] == 0)
+					_GP(play).entered_edge = -10;
 				// if we are walking in from off-screen, don't activate edges
 				else
-					edgesActivated[play.entered_edge] = 0;
+					edgesActivated[_GP(play).entered_edge] = 0;
 			}
 
 			for (int ii = 0; ii < 4; ii++) {
@@ -620,7 +619,7 @@ static void game_loop_update_animated_buttons() {
 }
 
 static void game_loop_do_render_and_check_mouse(IDriverDependantBitmap *extraBitmap, int extraX, int extraY) {
-	if (!play.fast_forward) {
+	if (!_GP(play).fast_forward) {
 		int mwasatx = _G(mousex), mwasaty = _G(mousey);
 
 		// Only do this if we are not skipping a cutscene
@@ -632,7 +631,7 @@ static void game_loop_do_render_and_check_mouse(IDriverDependantBitmap *extraBit
 		// TODO: if we support rotation then we also need to compare full transform!
 		if (displayed_room < 0)
 			return;
-		auto view = play.GetRoomViewportAt(_G(mousex), _G(mousey));
+		auto view = _GP(play).GetRoomViewportAt(_G(mousex), _G(mousey));
 		auto cam = view ? view->GetCamera() : nullptr;
 		if (cam) {
 			// NOTE: all cameras are in same room right now, so their positions are in same coordinate system;
@@ -675,13 +674,13 @@ static void game_loop_update_events() {
 }
 
 static void game_loop_update_background_animation() {
-	if (play.bg_anim_delay > 0) play.bg_anim_delay--;
-	else if (play.bg_frame_locked);
+	if (_GP(play).bg_anim_delay > 0) _GP(play).bg_anim_delay--;
+	else if (_GP(play).bg_frame_locked);
 	else {
-		play.bg_anim_delay = play.anim_background_speed;
-		play.bg_frame++;
-		if ((size_t)play.bg_frame >= thisroom.BgFrameCount)
-			play.bg_frame = 0;
+		_GP(play).bg_anim_delay = _GP(play).anim_background_speed;
+		_GP(play).bg_frame++;
+		if ((size_t)_GP(play).bg_frame >= thisroom.BgFrameCount)
+			_GP(play).bg_frame = 0;
 		if (thisroom.BgFrameCount >= 2) {
 			// get the new frame's palette
 			on_background_frame_change();
@@ -692,8 +691,8 @@ static void game_loop_update_background_animation() {
 static void game_loop_update_loop_counter() {
 	loopcounter++;
 
-	if (play.wait_counter > 0) play.wait_counter--;
-	if (play.shakesc_length > 0) play.shakesc_length--;
+	if (_GP(play).wait_counter > 0) _GP(play).wait_counter--;
+	if (_GP(play).shakesc_length > 0) _GP(play).shakesc_length--;
 
 	if (loopcounter % 5 == 0) {
 		update_ambient_sound_vol();
@@ -745,8 +744,8 @@ void UpdateGameOnce(bool checkControls, IDriverDependantBitmap *extraBitmap, int
 	game_loop_check_problems_at_start();
 
 	// if we're not fading in, don't count the fadeouts
-	if ((play.no_hicolor_fadein) && (_GP(game).options[OPT_FADETYPE] == FADE_NORMAL))
-		play.screen_is_faded_out = 0;
+	if ((_GP(play).no_hicolor_fadein) && (_GP(game).options[OPT_FADETYPE] == FADE_NORMAL))
+		_GP(play).screen_is_faded_out = 0;
 
 	our_eip = 1014;
 
@@ -795,7 +794,7 @@ void UpdateGameOnce(bool checkControls, IDriverDependantBitmap *extraBitmap, int
 	game_loop_update_loop_counter();
 
 	// Immediately start the next frame if we are skipping a cutscene
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		return;
 
 	our_eip = 72;
@@ -813,20 +812,20 @@ static void UpdateMouseOverLocation() {
 	char tempo[STD_BUFFER_SIZE];
 	GetLocationName(game_to_data_coord(_G(mousex)), game_to_data_coord(_G(mousey)), tempo);
 
-	if ((play.get_loc_name_save_cursor >= 0) &&
-		(play.get_loc_name_save_cursor != play.get_loc_name_last_time) &&
+	if ((_GP(play).get_loc_name_save_cursor >= 0) &&
+		(_GP(play).get_loc_name_save_cursor != _GP(play).get_loc_name_last_time) &&
 		(mouse_on_iface < 0) && (ifacepopped < 0)) {
 		// we have saved the cursor, but the mouse location has changed
 		// and it's time to restore it
-		play.get_loc_name_save_cursor = -1;
-		set_cursor_mode(play.restore_cursor_mode_to);
+		_GP(play).get_loc_name_save_cursor = -1;
+		set_cursor_mode(_GP(play).restore_cursor_mode_to);
 
-		if (cur_mode == play.restore_cursor_mode_to) {
+		if (cur_mode == _GP(play).restore_cursor_mode_to) {
 			// make sure it changed -- the new mode might have been disabled
 			// in which case don't change the image
-			set_mouse_cursor(play.restore_cursor_image_to);
+			set_mouse_cursor(_GP(play).restore_cursor_image_to);
 		}
-		debug_script_log("Restore mouse to mode %d cursor %d", play.restore_cursor_mode_to, play.restore_cursor_image_to);
+		debug_script_log("Restore mouse to mode %d cursor %d", _GP(play).restore_cursor_mode_to, _GP(play).restore_cursor_image_to);
 	}
 }
 
@@ -877,7 +876,7 @@ static int UpdateWaitMode() {
 
 	set_default_cursor();
 	guis_need_update = 1;
-	play.disabled_user_interface--;
+	_GP(play).disabled_user_interface--;
 	user_disabled_for = 0;
 
 	switch (was_disabled_for) {
@@ -915,7 +914,7 @@ static int GameTick() {
 }
 
 static void SetupLoopParameters(int untilwhat, const void *udata) {
-	play.disabled_user_interface++;
+	_GP(play).disabled_user_interface++;
 	guis_need_update = 1;
 	// Only change the mouse cursor if it hasn't been specifically changed first
 	// (or if it's speech, always change it)
diff --git a/engines/ags/engine/main/game_start.cpp b/engines/ags/engine/main/game_start.cpp
index a95dd4a806..d62136864c 100644
--- a/engines/ags/engine/main/game_start.cpp
+++ b/engines/ags/engine/main/game_start.cpp
@@ -54,7 +54,6 @@ using namespace AGS::Engine;
 
 extern int our_eip, displayed_room;
 
-extern GameState play;
 extern std::vector<ccInstance *> moduleInst;
 extern int numScriptModules;
 extern CharacterInfo *playerchar;
@@ -134,7 +133,7 @@ void initialize_start_and_play_game(int override_start_room, int loadSaveOnStart
 			_GP(game).options[OPT_ALWAYSSPCH] = oldalways;
 		}
 
-		::AGS::g_vm->setRandomNumberSeed(play.randseed);
+		::AGS::g_vm->setRandomNumberSeed(_GP(play).randseed);
 		if (override_start_room)
 			playerchar->room = override_start_room;
 
diff --git a/engines/ags/engine/main/quit.cpp b/engines/ags/engine/main/quit.cpp
index a7e0e3bd84..bc538f1bb2 100644
--- a/engines/ags/engine/main/quit.cpp
+++ b/engines/ags/engine/main/quit.cpp
@@ -180,7 +180,7 @@ void quit_message_on_exit(const char *qmsg, String &alertis, QuitReason qreason)
 void quit_release_data() {
 	resetRoomStatuses();
 	thisroom.Free();
-	play.Free();
+	_GP(play).Free();
 
 	/*  _CrtMemState memstart;
 	_CrtMemCheckpoint(&memstart);
diff --git a/engines/ags/engine/main/update.cpp b/engines/ags/engine/main/update.cpp
index ebbfc07b73..b1b913af9e 100644
--- a/engines/ags/engine/main/update.cpp
+++ b/engines/ags/engine/main/update.cpp
@@ -57,7 +57,6 @@ using namespace AGS::Engine;
 extern MoveList *mls;
 extern RoomStatus *croom;
 
-extern GameState play;
 extern RoomStruct thisroom;
 extern RoomObject *objs;
 extern ViewStruct *views;
@@ -192,9 +191,9 @@ int do_movelist_move(int16_t *mlnum, int32_t *xx, int32_t *yy) {
 
 
 void update_script_timers() {
-	if (play.gscript_timer > 0) play.gscript_timer--;
+	if (_GP(play).gscript_timer > 0) _GP(play).gscript_timer--;
 	for (int aa = 0; aa < MAX_TIMERS; aa++) {
-		if (play.script_timers[aa] > 1) play.script_timers[aa]--;
+		if (_GP(play).script_timers[aa] > 1) _GP(play).script_timers[aa]--;
 	}
 }
 
@@ -258,37 +257,37 @@ void update_overlay_timers() {
 
 void update_speech_and_messages() {
 	bool is_voice_playing = false;
-	if (play.speech_has_voice) {
+	if (_GP(play).speech_has_voice) {
 		AudioChannelsLock lock;
 		auto *ch = lock.GetChannel(SCHAN_SPEECH);
 		is_voice_playing = ch && ch->is_playing();
 	}
 	// determine if speech text should be removed
-	if (play.messagetime >= 0) {
-		play.messagetime--;
+	if (_GP(play).messagetime >= 0) {
+		_GP(play).messagetime--;
 		// extend life of text if the voice hasn't finished yet
-		if (play.speech_has_voice && !play.speech_in_post_state) {
-			if ((is_voice_playing) && (play.fast_forward == 0)) {
-				if (play.messagetime <= 1)
-					play.messagetime = 1;
+		if (_GP(play).speech_has_voice && !_GP(play).speech_in_post_state) {
+			if ((is_voice_playing) && (_GP(play).fast_forward == 0)) {
+				if (_GP(play).messagetime <= 1)
+					_GP(play).messagetime = 1;
 			} else // if the voice has finished, remove the speech
-				play.messagetime = 0;
+				_GP(play).messagetime = 0;
 		}
 
-		if (play.messagetime < 1 && play.speech_display_post_time_ms > 0 &&
-			play.fast_forward == 0) {
-			if (!play.speech_in_post_state) {
-				play.messagetime = ::lround(play.speech_display_post_time_ms * get_current_fps() / 1000.0f);
+		if (_GP(play).messagetime < 1 && _GP(play).speech_display_post_time_ms > 0 &&
+			_GP(play).fast_forward == 0) {
+			if (!_GP(play).speech_in_post_state) {
+				_GP(play).messagetime = ::lround(_GP(play).speech_display_post_time_ms * get_current_fps() / 1000.0f);
 			}
-			play.speech_in_post_state = !play.speech_in_post_state;
+			_GP(play).speech_in_post_state = !_GP(play).speech_in_post_state;
 		}
 
-		if (play.messagetime < 1) {
-			if (play.fast_forward > 0) {
+		if (_GP(play).messagetime < 1) {
+			if (_GP(play).fast_forward > 0) {
 				remove_screen_overlay(OVER_TEXTMSG);
-			} else if (play.cant_skip_speech & SKIP_AUTOTIMER) {
+			} else if (_GP(play).cant_skip_speech & SKIP_AUTOTIMER) {
 				remove_screen_overlay(OVER_TEXTMSG);
-				play.SetIgnoreInput(play.ignore_user_input_after_text_timeout_ms);
+				_GP(play).SetIgnoreInput(_GP(play).ignore_user_input_after_text_timeout_ms);
 			}
 		}
 	}
@@ -297,12 +296,12 @@ void update_speech_and_messages() {
 // update sierra-style speech
 void update_sierra_speech() {
 	int voice_pos_ms = -1;
-	if (play.speech_has_voice) {
+	if (_GP(play).speech_has_voice) {
 		AudioChannelsLock lock;
 		auto *ch = lock.GetChannel(SCHAN_SPEECH);
 		voice_pos_ms = ch ? ch->get_pos_ms() : -1;
 	}
-	if ((face_talking >= 0) && (play.fast_forward == 0)) {
+	if ((face_talking >= 0) && (_GP(play).fast_forward == 0)) {
 		int updatedFrame = 0;
 
 		if ((facetalkchar->blinkview > 0) && (facetalkAllowBlink)) {
@@ -352,21 +351,21 @@ void update_sierra_speech() {
 			}
 		} else if (facetalkwait > 0) facetalkwait--;
 		// don't animate if the speech has finished
-		else if ((play.messagetime < 1) && (facetalkframe == 0) &&
-			// if play.close_mouth_speech_time = 0, this means animation should play till
+		else if ((_GP(play).messagetime < 1) && (facetalkframe == 0) &&
+			// if _GP(play).close_mouth_speech_time = 0, this means animation should play till
 			// the speech ends; but this should not work in voice mode, and also if the
 			// speech is in the "post" state
-			(play.speech_has_voice || play.speech_in_post_state || play.close_mouth_speech_time > 0))
+			(_GP(play).speech_has_voice || _GP(play).speech_in_post_state || _GP(play).close_mouth_speech_time > 0))
 			;
 		else {
 			// Close mouth at end of sentence: if speech has entered the "post" state,
 			// or if this is a text only mode and close_mouth_speech_time is set
-			if (play.speech_in_post_state ||
-				(!play.speech_has_voice &&
-				(play.messagetime < play.close_mouth_speech_time) &&
-					(play.close_mouth_speech_time > 0))) {
+			if (_GP(play).speech_in_post_state ||
+				(!_GP(play).speech_has_voice &&
+				(_GP(play).messagetime < _GP(play).close_mouth_speech_time) &&
+					(_GP(play).close_mouth_speech_time > 0))) {
 				facetalkframe = 0;
-				facetalkwait = play.messagetime;
+				facetalkwait = _GP(play).messagetime;
 			} else if ((_GP(game).options[OPT_LIPSYNCTEXT]) && (facetalkrepeat > 0)) {
 				// lip-sync speech (and not a thought)
 				facetalkwait = update_lip_sync(facetalkview, facetalkloop, &facetalkframe);
@@ -377,7 +376,7 @@ void update_sierra_speech() {
 				// normal non-lip-sync
 				facetalkframe++;
 				if ((facetalkframe >= views[facetalkview].loops[facetalkloop].numFrames) ||
-					(!play.speech_has_voice && (play.messagetime < 1) && (play.close_mouth_speech_time > 0))) {
+					(!_GP(play).speech_has_voice && (_GP(play).messagetime < 1) && (_GP(play).close_mouth_speech_time > 0))) {
 
 					if ((facetalkframe >= views[facetalkview].loops[facetalkloop].numFrames) &&
 						(views[facetalkview].loops[facetalkloop].RunNextLoop())) {
@@ -410,10 +409,10 @@ void update_sierra_speech() {
 			if (_GP(game).options[OPT_SPEECHTYPE] == 3) {
 				// QFG4-style fullscreen dialog
 				if (facetalk_qfg4_override_placement_x) {
-					view_frame_x = play.speech_portrait_x;
+					view_frame_x = _GP(play).speech_portrait_x;
 				}
 				if (facetalk_qfg4_override_placement_y) {
-					view_frame_y = play.speech_portrait_y;
+					view_frame_y = _GP(play).speech_portrait_y;
 				} else {
 					view_frame_y = (screenover[face_talking].pic->GetHeight() / 2) - (_GP(game).SpriteInfos[thisPic].Height / 2);
 				}
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index ecc64698a4..fdaaa9f6da 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -104,9 +104,7 @@ void set_clip_to_channel(int chanid, SOUNDCLIP *clip) {
 
 volatile bool _audio_doing_crossfade;
 
-
 extern GameSetup usetup;
-extern GameState play;
 extern RoomStruct thisroom;
 extern CharacterInfo *playerchar;
 
@@ -125,8 +123,8 @@ void calculate_reserved_channel_count() {
 }
 
 void update_clip_default_volume(ScriptAudioClip *audioClip) {
-	if (play.default_audio_type_volumes[audioClip->type] >= 0) {
-		audioClip->defaultVolume = play.default_audio_type_volumes[audioClip->type];
+	if (_GP(play).default_audio_type_volumes[audioClip->type] >= 0) {
+		audioClip->defaultVolume = _GP(play).default_audio_type_volumes[audioClip->type];
 	}
 }
 
@@ -134,9 +132,9 @@ void start_fading_in_new_track_if_applicable(int fadeInChannel, ScriptAudioClip
 	int crossfadeSpeed = _GP(game).audioClipTypes[newSound->type].crossfadeSpeed;
 	if (crossfadeSpeed > 0) {
 		update_clip_default_volume(newSound);
-		play.crossfade_in_volume_per_step = crossfadeSpeed;
-		play.crossfade_final_volume_in = newSound->defaultVolume;
-		play.crossfading_in_channel = fadeInChannel;
+		_GP(play).crossfade_in_volume_per_step = crossfadeSpeed;
+		_GP(play).crossfade_final_volume_in = newSound->defaultVolume;
+		_GP(play).crossfading_in_channel = fadeInChannel;
 	}
 }
 
@@ -147,12 +145,12 @@ static void move_track_to_crossfade_channel(int currentChannel, int crossfadeSpe
 	if (!cfade_clip)
 		return;
 
-	play.crossfading_out_channel = SPECIAL_CROSSFADE_CHANNEL;
-	play.crossfade_step = 0;
-	play.crossfade_initial_volume_out = cfade_clip->get_volume();
-	play.crossfade_out_volume_per_step = crossfadeSpeed;
+	_GP(play).crossfading_out_channel = SPECIAL_CROSSFADE_CHANNEL;
+	_GP(play).crossfade_step = 0;
+	_GP(play).crossfade_initial_volume_out = cfade_clip->get_volume();
+	_GP(play).crossfade_out_volume_per_step = crossfadeSpeed;
 
-	play.crossfading_in_channel = fadeInChannel;
+	_GP(play).crossfading_in_channel = fadeInChannel;
 	if (newSound != nullptr) {
 		start_fading_in_new_track_if_applicable(fadeInChannel, newSound);
 	}
@@ -206,7 +204,7 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
 		(lowestPrioritySoFar <= priority)) {
 		stop_or_fade_out_channel(lowestPriorityID, lowestPriorityID, clip);
 		channelToUse = lowestPriorityID;
-	} else if ((channelToUse >= 0) && (play.crossfading_in_channel < 1)) {
+	} else if ((channelToUse >= 0) && (_GP(play).crossfading_in_channel < 1)) {
 		start_fading_in_new_track_if_applicable(channelToUse, clip);
 	}
 	return channelToUse;
@@ -256,53 +254,53 @@ SOUNDCLIP *load_sound_clip(ScriptAudioClip *audioClip, bool repeat) {
 static void audio_update_polled_stuff() {
 	///////////////////////////////////////////////////////////////////////////
 	// Do crossfade
-	play.crossfade_step++;
+	_GP(play).crossfade_step++;
 
 	AudioChannelsLock lock;
 
-	if (play.crossfading_out_channel > 0 && !lock.GetChannelIfPlaying(play.crossfading_out_channel))
-		play.crossfading_out_channel = 0;
+	if (_GP(play).crossfading_out_channel > 0 && !lock.GetChannelIfPlaying(_GP(play).crossfading_out_channel))
+		_GP(play).crossfading_out_channel = 0;
 
-	if (play.crossfading_out_channel > 0) {
-		SOUNDCLIP *ch = lock.GetChannel(play.crossfading_out_channel);
-		int newVolume = ch ? ch->get_volume() - play.crossfade_out_volume_per_step : 0;
+	if (_GP(play).crossfading_out_channel > 0) {
+		SOUNDCLIP *ch = lock.GetChannel(_GP(play).crossfading_out_channel);
+		int newVolume = ch ? ch->get_volume() - _GP(play).crossfade_out_volume_per_step : 0;
 		if (newVolume > 0) {
-			AudioChannel_SetVolume(&scrAudioChannel[play.crossfading_out_channel], newVolume);
+			AudioChannel_SetVolume(&scrAudioChannel[_GP(play).crossfading_out_channel], newVolume);
 		} else {
-			stop_and_destroy_channel(play.crossfading_out_channel);
-			play.crossfading_out_channel = 0;
+			stop_and_destroy_channel(_GP(play).crossfading_out_channel);
+			_GP(play).crossfading_out_channel = 0;
 		}
 	}
 
-	if (play.crossfading_in_channel > 0 && !lock.GetChannelIfPlaying(play.crossfading_in_channel))
-		play.crossfading_in_channel = 0;
+	if (_GP(play).crossfading_in_channel > 0 && !lock.GetChannelIfPlaying(_GP(play).crossfading_in_channel))
+		_GP(play).crossfading_in_channel = 0;
 
-	if (play.crossfading_in_channel > 0) {
-		SOUNDCLIP *ch = lock.GetChannel(play.crossfading_in_channel);
-		int newVolume = ch ? ch->get_volume() + play.crossfade_in_volume_per_step : 0;
-		if (newVolume > play.crossfade_final_volume_in) {
-			newVolume = play.crossfade_final_volume_in;
+	if (_GP(play).crossfading_in_channel > 0) {
+		SOUNDCLIP *ch = lock.GetChannel(_GP(play).crossfading_in_channel);
+		int newVolume = ch ? ch->get_volume() + _GP(play).crossfade_in_volume_per_step : 0;
+		if (newVolume > _GP(play).crossfade_final_volume_in) {
+			newVolume = _GP(play).crossfade_final_volume_in;
 		}
 
-		AudioChannel_SetVolume(&scrAudioChannel[play.crossfading_in_channel], newVolume);
+		AudioChannel_SetVolume(&scrAudioChannel[_GP(play).crossfading_in_channel], newVolume);
 
-		if (newVolume >= play.crossfade_final_volume_in) {
-			play.crossfading_in_channel = 0;
+		if (newVolume >= _GP(play).crossfade_final_volume_in) {
+			_GP(play).crossfading_in_channel = 0;
 		}
 	}
 
 	///////////////////////////////////////////////////////////////////////////
 	// Do audio queue
-	if (play.new_music_queue_size > 0) {
-		for (int i = 0; i < play.new_music_queue_size; i++) {
-			ScriptAudioClip *clip = &_GP(game).audioClips[play.new_music_queue[i].audioClipIndex];
+	if (_GP(play).new_music_queue_size > 0) {
+		for (int i = 0; i < _GP(play).new_music_queue_size; i++) {
+			ScriptAudioClip *clip = &_GP(game).audioClips[_GP(play).new_music_queue[i].audioClipIndex];
 			int channel = find_free_audio_channel(clip, clip->defaultPriority, false);
 			if (channel >= 0) {
-				QueuedAudioItem itemToPlay = play.new_music_queue[i];
+				QueuedAudioItem itemToPlay = _GP(play).new_music_queue[i];
 
-				play.new_music_queue_size--;
-				for (int j = i; j < play.new_music_queue_size; j++) {
-					play.new_music_queue[j] = play.new_music_queue[j + 1];
+				_GP(play).new_music_queue_size--;
+				for (int j = i; j < _GP(play).new_music_queue_size; j++) {
+					_GP(play).new_music_queue[j] = _GP(play).new_music_queue[j + 1];
 				}
 
 				play_audio_clip_on_channel(channel, clip, itemToPlay.priority, itemToPlay.repeat, 0, itemToPlay.cachedClip);
@@ -316,7 +314,7 @@ static void audio_update_polled_stuff() {
 	// NOTE: there's only one speech channel, therefore it's either blocking
 	// or non-blocking at any given time. If it's changed, we'd need to keep
 	// record of every channel, or keep a count of active channels.
-	if (play.IsNonBlockingVoiceSpeech()) {
+	if (_GP(play).IsNonBlockingVoiceSpeech()) {
 		if (!channel_is_playing(SCHAN_SPEECH)) {
 			stop_voice_nonblocking();
 		}
@@ -330,18 +328,18 @@ static void apply_volume_drop_to_clip(SOUNDCLIP *clip) {
 }
 
 static void queue_audio_clip_to_play(ScriptAudioClip *clip, int priority, int repeat) {
-	if (play.new_music_queue_size >= MAX_QUEUED_MUSIC) {
+	if (_GP(play).new_music_queue_size >= MAX_QUEUED_MUSIC) {
 		debug_script_log("Too many queued music, cannot add %s", clip->scriptName.GetCStr());
 		return;
 	}
 
 	SOUNDCLIP *cachedClip = load_sound_clip(clip, (repeat != 0));
 	if (cachedClip != nullptr) {
-		play.new_music_queue[play.new_music_queue_size].audioClipIndex = clip->id;
-		play.new_music_queue[play.new_music_queue_size].priority = priority;
-		play.new_music_queue[play.new_music_queue_size].repeat = (repeat != 0);
-		play.new_music_queue[play.new_music_queue_size].cachedClip = cachedClip;
-		play.new_music_queue_size++;
+		_GP(play).new_music_queue[_GP(play).new_music_queue_size].audioClipIndex = clip->id;
+		_GP(play).new_music_queue[_GP(play).new_music_queue_size].priority = priority;
+		_GP(play).new_music_queue[_GP(play).new_music_queue_size].repeat = (repeat != 0);
+		_GP(play).new_music_queue[_GP(play).new_music_queue_size].cachedClip = cachedClip;
+		_GP(play).new_music_queue_size++;
 	}
 }
 
@@ -351,19 +349,19 @@ ScriptAudioChannel *play_audio_clip_on_channel(int channel, ScriptAudioClip *cli
 	}
 	if (soundfx == nullptr) {
 		debug_script_log("AudioClip.Play: unable to load sound file");
-		if (play.crossfading_in_channel == channel) {
-			play.crossfading_in_channel = 0;
+		if (_GP(play).crossfading_in_channel == channel) {
+			_GP(play).crossfading_in_channel = 0;
 		}
 		return nullptr;
 	}
 	soundfx->_priority = priority;
 
-	if (play.crossfading_in_channel == channel) {
+	if (_GP(play).crossfading_in_channel == channel) {
 		soundfx->set_volume_percent(0);
 	}
 
 	// Mute the audio clip if fast-forwarding the cutscene
-	if (play.fast_forward) {
+	if (_GP(play).fast_forward) {
 		soundfx->set_mute(true);
 
 		// CHECKME!!
@@ -390,7 +388,7 @@ ScriptAudioChannel *play_audio_clip_on_channel(int channel, ScriptAudioClip *cli
 	// NOTE: there is a confusing logic in sound clip classes, that they do not use
 	// any modifiers when begin playing, therefore we must apply this only after
 	// playback was started.
-	if (!play.fast_forward && play.speech_has_voice)
+	if (!_GP(play).fast_forward && _GP(play).speech_has_voice)
 		apply_volume_drop_to_clip(soundfx);
 
 	set_clip_to_channel(channel, soundfx);
@@ -399,23 +397,23 @@ ScriptAudioChannel *play_audio_clip_on_channel(int channel, ScriptAudioClip *cli
 
 void remove_clips_of_type_from_queue(int audioType) {
 	int aa;
-	for (aa = 0; aa < play.new_music_queue_size; aa++) {
-		ScriptAudioClip *clip = &_GP(game).audioClips[play.new_music_queue[aa].audioClipIndex];
+	for (aa = 0; aa < _GP(play).new_music_queue_size; aa++) {
+		ScriptAudioClip *clip = &_GP(game).audioClips[_GP(play).new_music_queue[aa].audioClipIndex];
 		if (clip->type == audioType) {
-			play.new_music_queue_size--;
-			for (int bb = aa; bb < play.new_music_queue_size; bb++)
-				play.new_music_queue[bb] = play.new_music_queue[bb + 1];
+			_GP(play).new_music_queue_size--;
+			for (int bb = aa; bb < _GP(play).new_music_queue_size; bb++)
+				_GP(play).new_music_queue[bb] = _GP(play).new_music_queue[bb + 1];
 			aa--;
 		}
 	}
 }
 
 void update_queued_clips_volume(int audioType, int new_vol) {
-	for (int i = 0; i < play.new_music_queue_size; ++i) {
+	for (int i = 0; i < _GP(play).new_music_queue_size; ++i) {
 		// NOTE: if clip is uncached, the volume will be set from defaults when it is loaded
-		SOUNDCLIP *sndclip = play.new_music_queue[i].cachedClip;
+		SOUNDCLIP *sndclip = _GP(play).new_music_queue[i].cachedClip;
 		if (sndclip) {
-			ScriptAudioClip *clip = &_GP(game).audioClips[play.new_music_queue[i].audioClipIndex];
+			ScriptAudioClip *clip = &_GP(game).audioClips[_GP(play).new_music_queue[i].audioClipIndex];
 			if (clip->type == audioType)
 				sndclip->set_volume_percent(new_vol);
 		}
@@ -465,10 +463,10 @@ void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
 		ch = nullptr;
 	}
 
-	if (play.crossfading_in_channel == chid)
-		play.crossfading_in_channel = 0;
-	if (play.crossfading_out_channel == chid)
-		play.crossfading_out_channel = 0;
+	if (_GP(play).crossfading_in_channel == chid)
+		_GP(play).crossfading_in_channel = 0;
+	if (_GP(play).crossfading_out_channel == chid)
+		_GP(play).crossfading_out_channel = 0;
 	// don't update 'crossFading' here as it is updated in all the cross-fading functions.
 
 	// destroyed an ambient sound channel
@@ -476,7 +474,7 @@ void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
 		ambient[chid].channel = 0;
 
 	if ((chid == SCHAN_MUSIC) && (resetLegacyMusicSettings)) {
-		play.cur_music_number = -1;
+		_GP(play).cur_music_number = -1;
 		current_music_type = 0;
 	}
 }
@@ -576,12 +574,12 @@ void update_ambient_sound_vol() {
 
 		int sourceVolume = thisSound->vol;
 
-		if (play.speech_has_voice) {
+		if (_GP(play).speech_has_voice) {
 			// Negative value means set exactly; positive means drop that amount
-			if (play.speech_music_drop < 0)
-				sourceVolume = -play.speech_music_drop;
+			if (_GP(play).speech_music_drop < 0)
+				sourceVolume = -_GP(play).speech_music_drop;
 			else
-				sourceVolume -= play.speech_music_drop;
+				sourceVolume -= _GP(play).speech_music_drop;
 
 			if (sourceVolume < 0)
 				sourceVolume = 0;
@@ -590,7 +588,7 @@ void update_ambient_sound_vol() {
 		}
 
 		// Adjust ambient volume so it maxes out at overall sound volume
-		int ambientvol = (sourceVolume * play.sound_volume) / 255;
+		int ambientvol = (sourceVolume * _GP(play).sound_volume) / 255;
 
 		int wantvol;
 
@@ -757,44 +755,44 @@ static void play_new_music(int mnum, SOUNDCLIP *music);
 
 void play_next_queued() {
 	// check if there's a queued one to play
-	if (play.music_queue_size > 0) {
+	if (_GP(play).music_queue_size > 0) {
 
-		int tuneToPlay = play.music_queue[0];
+		int tuneToPlay = _GP(play).music_queue[0];
 
 		if (tuneToPlay >= QUEUED_MUSIC_REPEAT) {
 			// Loop it!
-			play.music_repeat++;
+			_GP(play).music_repeat++;
 			play_new_music(tuneToPlay - QUEUED_MUSIC_REPEAT, cachedQueuedMusic);
-			play.music_repeat--;
+			_GP(play).music_repeat--;
 		} else {
 			// Don't loop it!
-			int repeatWas = play.music_repeat;
-			play.music_repeat = 0;
+			int repeatWas = _GP(play).music_repeat;
+			_GP(play).music_repeat = 0;
 			play_new_music(tuneToPlay, cachedQueuedMusic);
-			play.music_repeat = repeatWas;
+			_GP(play).music_repeat = repeatWas;
 		}
 
 		// don't free the memory, as it has been transferred onto the
 		// main music channel
 		cachedQueuedMusic = nullptr;
 
-		play.music_queue_size--;
-		for (int i = 0; i < play.music_queue_size; i++)
-			play.music_queue[i] = play.music_queue[i + 1];
+		_GP(play).music_queue_size--;
+		for (int i = 0; i < _GP(play).music_queue_size; i++)
+			_GP(play).music_queue[i] = _GP(play).music_queue[i + 1];
 
-		if (play.music_queue_size > 0)
-			cachedQueuedMusic = load_music_from_disk(play.music_queue[0], 0);
+		if (_GP(play).music_queue_size > 0)
+			cachedQueuedMusic = load_music_from_disk(_GP(play).music_queue[0], 0);
 	}
 
 }
 
 int calculate_max_volume() {
 	// quieter so that sounds can be heard better
-	int newvol = play.music_master_volume + ((int)thisroom.Options.MusicVolume) * LegacyRoomVolumeFactor;
+	int newvol = _GP(play).music_master_volume + ((int)thisroom.Options.MusicVolume) * LegacyRoomVolumeFactor;
 	if (newvol > 255) newvol = 255;
 	if (newvol < 0) newvol = 0;
 
-	if (play.fast_forward)
+	if (_GP(play).fast_forward)
 		newvol = 0;
 
 	return newvol;
@@ -817,7 +815,7 @@ void apply_volume_drop_modifier(bool applyModifier) {
 
 // Checks if speech voice-over is currently playing, and reapply volume drop to all other active clips
 void update_volume_drop_if_voiceover() {
-	apply_volume_drop_modifier(play.speech_has_voice);
+	apply_volume_drop_modifier(_GP(play).speech_has_voice);
 }
 
 // Update the music, and advance the crossfade on a step
@@ -839,13 +837,13 @@ void update_audio_system_on_game_loop() {
 	}
 
 	// Check if the current music has finished playing
-	if ((play.cur_music_number >= 0) && (play.fast_forward == 0)) {
+	if ((_GP(play).cur_music_number >= 0) && (_GP(play).fast_forward == 0)) {
 		if (IsMusicPlaying() == 0) {
 			// The current music has finished
-			play.cur_music_number = -1;
+			_GP(play).cur_music_number = -1;
 			play_next_queued();
 		} else if ((_GP(game).options[OPT_CROSSFADEMUSIC] > 0) &&
-			(play.music_queue_size > 0) && (!crossFading)) {
+			(_GP(play).music_queue_size > 0) && (!crossFading)) {
 			// want to crossfade, and new tune in the queue
 			auto *ch = lock.GetChannel(SCHAN_MUSIC);
 			if (ch) {
@@ -897,7 +895,7 @@ void stopmusic() {
 	} else
 		stop_and_destroy_channel(SCHAN_MUSIC);
 
-	play.cur_music_number = -1;
+	_GP(play).cur_music_number = -1;
 	current_music_type = 0;
 }
 
@@ -1029,7 +1027,7 @@ static void play_new_music(int mnum, SOUNDCLIP *music) {
 	if (debug_flags & DBG_NOMUSIC)
 		return;
 
-	if ((play.cur_music_number == mnum) && (music == nullptr)) {
+	if ((_GP(play).cur_music_number == mnum) && (music == nullptr)) {
 		debug_script_log("PlayMusic %d but already playing", mnum);
 		return;  // don't play the music if it's already playing
 	}
@@ -1046,24 +1044,24 @@ static void play_new_music(int mnum, SOUNDCLIP *music) {
 		return;
 	}
 
-	if (play.fast_forward) {
+	if (_GP(play).fast_forward) {
 		// while skipping cutscene, don't change the music
-		play.end_cutscene_music = mnum;
+		_GP(play).end_cutscene_music = mnum;
 		return;
 	}
 
 	useChannel = prepare_for_new_music();
-	play.cur_music_number = mnum;
+	_GP(play).cur_music_number = mnum;
 	current_music_type = 0;
 
-	play.current_music_repeating = play.music_repeat;
+	_GP(play).current_music_repeating = _GP(play).music_repeat;
 	// now that all the previous music is unloaded, load in the new one
 
 	SOUNDCLIP *new_clip;
 	if (music != nullptr)
 		new_clip = music;
 	else
-		new_clip = load_music_from_disk(mnum, (play.music_repeat > 0));
+		new_clip = load_music_from_disk(mnum, (_GP(play).music_repeat > 0));
 
 	AudioChannelsLock lock;
 	auto *ch = lock.SetChannel(useChannel, new_clip);
diff --git a/engines/ags/engine/media/audio/queuedaudioitem.h b/engines/ags/engine/media/audio/queuedaudioitem.h
index 9c7fa0ffa5..b23b9d2624 100644
--- a/engines/ags/engine/media/audio/queuedaudioitem.h
+++ b/engines/ags/engine/media/audio/queuedaudioitem.h
@@ -36,10 +36,10 @@ class Stream;
 using namespace AGS; // FIXME later
 
 struct QueuedAudioItem {
-	short audioClipIndex;
-	short priority;
-	bool  repeat;
-	SOUNDCLIP *cachedClip;
+	short audioClipIndex = 0;
+	short priority = 0;
+	bool  repeat = false;
+	SOUNDCLIP *cachedClip = nullptr;
 
 	void ReadFromFile(Shared::Stream *in);
 	void WriteToFile(Shared::Stream *out) const;
diff --git a/engines/ags/engine/script/script.cpp b/engines/ags/engine/script/script.cpp
index 7ed756c82b..a47f45888a 100644
--- a/engines/ags/engine/script/script.cpp
+++ b/engines/ags/engine/script/script.cpp
@@ -54,8 +54,6 @@
 
 namespace AGS3 {
 
-
-extern GameState play;
 extern int gameHasBeenRestored, displayed_room;
 extern unsigned int load_new_game;
 extern RoomObject *objs;
@@ -100,25 +98,25 @@ std::vector<String> guiScriptObjNames;
 
 
 int run_dialog_request(int parmtr) {
-	play.stop_dialog_at_end = DIALOG_RUNNING;
+	_GP(play).stop_dialog_at_end = DIALOG_RUNNING;
 	RunTextScriptIParam(gameinst, "dialog_request", RuntimeScriptValue().SetInt32(parmtr));
 
-	if (play.stop_dialog_at_end == DIALOG_STOP) {
-		play.stop_dialog_at_end = DIALOG_NONE;
+	if (_GP(play).stop_dialog_at_end == DIALOG_STOP) {
+		_GP(play).stop_dialog_at_end = DIALOG_NONE;
 		return -2;
 	}
-	if (play.stop_dialog_at_end >= DIALOG_NEWTOPIC) {
-		int tval = play.stop_dialog_at_end - DIALOG_NEWTOPIC;
-		play.stop_dialog_at_end = DIALOG_NONE;
+	if (_GP(play).stop_dialog_at_end >= DIALOG_NEWTOPIC) {
+		int tval = _GP(play).stop_dialog_at_end - DIALOG_NEWTOPIC;
+		_GP(play).stop_dialog_at_end = DIALOG_NONE;
 		return tval;
 	}
-	if (play.stop_dialog_at_end >= DIALOG_NEWROOM) {
-		int roomnum = play.stop_dialog_at_end - DIALOG_NEWROOM;
-		play.stop_dialog_at_end = DIALOG_NONE;
+	if (_GP(play).stop_dialog_at_end >= DIALOG_NEWROOM) {
+		int roomnum = _GP(play).stop_dialog_at_end - DIALOG_NEWROOM;
+		_GP(play).stop_dialog_at_end = DIALOG_NONE;
 		NewRoom(roomnum);
 		return -2;
 	}
-	play.stop_dialog_at_end = DIALOG_NONE;
+	_GP(play).stop_dialog_at_end = DIALOG_NONE;
 	return -1;
 }
 
@@ -126,7 +124,7 @@ void run_function_on_non_blocking_thread(NonBlockingScriptFunction *funcToRun) {
 
 	update_script_mouse_coords();
 
-	int room_changes_was = play.room_changes;
+	int room_changes_was = _GP(play).room_changes;
 	funcToRun->atLeastOneImplementationExists = false;
 
 	// run modules
@@ -134,13 +132,13 @@ void run_function_on_non_blocking_thread(NonBlockingScriptFunction *funcToRun) {
 	for (int kk = 0; kk < numScriptModules; kk++) {
 		funcToRun->moduleHasFunction[kk] = DoRunScriptFuncCantBlock(moduleInstFork[kk], funcToRun, funcToRun->moduleHasFunction[kk]);
 
-		if (room_changes_was != play.room_changes)
+		if (room_changes_was != _GP(play).room_changes)
 			return;
 	}
 
 	funcToRun->globalScriptHasFunction = DoRunScriptFuncCantBlock(gameinstFork, funcToRun, funcToRun->globalScriptHasFunction);
 
-	if (room_changes_was != play.room_changes)
+	if (room_changes_was != _GP(play).room_changes)
 		return;
 
 	funcToRun->roomHasFunction = DoRunScriptFuncCantBlock(roominstFork, funcToRun, funcToRun->roomHasFunction);
@@ -177,8 +175,8 @@ int run_interaction_event(Interaction *nint, int evnt, int chkAny, int isInv) {
 		return 0;
 	}
 
-	if (play.check_interaction_only) {
-		play.check_interaction_only = 2;
+	if (_GP(play).check_interaction_only) {
+		_GP(play).check_interaction_only = 2;
 		return -1;
 	}
 
@@ -212,12 +210,12 @@ int run_interaction_script(InteractionScripts *nint, int evnt, int chkAny, int i
 		return 0;
 	}
 
-	if (play.check_interaction_only) {
-		play.check_interaction_only = 2;
+	if (_GP(play).check_interaction_only) {
+		_GP(play).check_interaction_only = 2;
 		return -1;
 	}
 
-	int room_was = play.room_changes;
+	int room_was = _GP(play).room_changes;
 
 	RuntimeScriptValue rval_null;
 
@@ -231,7 +229,7 @@ int run_interaction_script(InteractionScripts *nint, int evnt, int chkAny, int i
 
 	int retval = 0;
 	// if the room changed within the action
-	if (room_was != play.room_changes)
+	if (room_was != _GP(play).room_changes)
 		retval = -1;
 
 	return retval;
@@ -430,14 +428,14 @@ int RunTextScript(ccInstance *sci, const char *tsname) {
 		// run module rep_execs
 		// FIXME: in theory the function may be already called for moduleInst[i],
 		// in which case this should not be executed; need to rearrange the code somehow
-		int room_changes_was = play.room_changes;
+		int room_changes_was = _GP(play).room_changes;
 		int restore_game_count_was = gameHasBeenRestored;
 
 		for (int kk = 0; kk < numScriptModules; kk++) {
 			if (!moduleRepExecAddr[kk].IsNull())
 				RunScriptFunctionIfExists(moduleInst[kk], tsname, 0, nullptr);
 
-			if ((room_changes_was != play.room_changes) ||
+			if ((room_changes_was != _GP(play).room_changes) ||
 				(restore_game_count_was != gameHasBeenRestored))
 				return 0;
 		}
@@ -586,7 +584,7 @@ void post_script_cleanup() {
 		RunScriptFunction(script.Instance, script.FnName, script.ParamCount, script.Param1, script.Param2);
 		if (script.Instance == kScInstRoom && script.ParamCount == 1) {
 			// some bogus hack for "on_call" event handler
-			play.roomscript_finished = 1;
+			_GP(play).roomscript_finished = 1;
 		}
 
 		// if they've changed rooms, cancel any further pending scripts
@@ -667,7 +665,7 @@ int run_interaction_commandlist(InteractionCommandList *nicl, int *timesrun, int
 
 	for (i = 0; i < nicl->Cmds.size(); i++) {
 		cmdsrun[0] ++;
-		int room_was = play.room_changes;
+		int room_was = _GP(play).room_changes;
 
 		switch (nicl->Cmds[i].Type) {
 		case 0:  // Do nothing
@@ -716,11 +714,11 @@ int run_interaction_commandlist(InteractionCommandList *nicl, int *timesrun, int
 			break;
 		case 9:
 		{ // Run Dialog
-			int roomWas = play.room_changes;
+			int roomWas = _GP(play).room_changes;
 			RunDialog(IPARAM1);
 			// if they changed room within the dialog script,
 			// the interaction command list is no longer valid
-			if (roomWas != play.room_changes)
+			if (roomWas != _GP(play).room_changes)
 				return -1;
 		}
 		break;
@@ -761,9 +759,9 @@ int run_interaction_commandlist(InteractionCommandList *nicl, int *timesrun, int
 				MoveCharacter(IPARAM1, IPARAM2, IPARAM3);
 			break;
 		case 20: // If Inventory Item was used
-			if (play.usedinv == IPARAM1) {
+			if (_GP(play).usedinv == IPARAM1) {
 				if (_GP(game).options[OPT_NOLOSEINV] == 0)
-					lose_inventory(play.usedinv);
+					lose_inventory(_GP(play).usedinv);
 				if (run_interaction_commandlist(nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
 					return -1;
 			} else
@@ -873,7 +871,7 @@ int run_interaction_commandlist(InteractionCommandList *nicl, int *timesrun, int
 		}
 
 		// if the room changed within the action, nicl is no longer valid
-		if (room_was != play.room_changes)
+		if (room_was != _GP(play).room_changes)
 			return -1;
 	}
 	return 0;
@@ -889,7 +887,7 @@ void can_run_delayed_command() {
 
 void run_unhandled_event(int evnt) {
 
-	if (play.check_interaction_only)
+	if (_GP(play).check_interaction_only)
 		return;
 
 	int evtype = 0;
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index c1c0fdc9f4..8e159ca605 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -23,6 +23,7 @@
 #include "ags/globals.h"
 #include "ags/shared/ac/gamesetupstruct.h"
 #include "ags/shared/ac/spritecache.h"
+#include "ags/engine/ac/gamestate.h"
 
 namespace AGS3 {
 
@@ -33,6 +34,7 @@ Globals::Globals() {
 
 	Common::fill(&_mousecurs[0], &_mousecurs[MAXCURSORS], nullptr);
 
+	_play = new GameState();
 	_game = new GameSetupStruct();
 	_spriteset = new SpriteCache(_game->SpriteInfos);
 }
@@ -40,6 +42,7 @@ Globals::Globals() {
 Globals::~Globals() {
 	g_globals = nullptr;
 	delete _game;
+	delete _play;
 	delete _spriteset;
 }
 
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 30f0d5b426..f85d226b0d 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -42,6 +42,7 @@ class Bitmap;
 
 struct IAGSEditorDebugger;
 struct GameSetupStruct;
+struct GameState;
 class SpriteCache;
 
 class Globals {
@@ -77,7 +78,8 @@ public:
 	 */
 
 	GameSetupStruct *_game = nullptr;
-	SpriteCache *_spriteset;
+	GameState *_play = nullptr;
+	SpriteCache *_spriteset = nullptr;
 
 	 /**@}*/
 
diff --git a/engines/ags/plugins/agsplugin.cpp b/engines/ags/plugins/agsplugin.cpp
index 934d50b2e5..b3d31aaca2 100644
--- a/engines/ags/plugins/agsplugin.cpp
+++ b/engines/ags/plugins/agsplugin.cpp
@@ -251,9 +251,9 @@ void IAGSEngine::DrawText(int32 x, int32 y, int32 font, int32 color, const char
 
 void IAGSEngine::GetScreenDimensions(int32 *width, int32 *height, int32 *coldepth) {
 	if (width != nullptr)
-		width[0] = play.GetMainViewport().GetWidth();
+		width[0] = _GP(play).GetMainViewport().GetWidth();
 	if (height != nullptr)
-		height[0] = play.GetMainViewport().GetHeight();
+		height[0] = _GP(play).GetMainViewport().GetHeight();
 	if (coldepth != nullptr)
 		coldepth[0] = scsystem.coldepth;
 }
@@ -293,7 +293,7 @@ int IAGSEngine::GetNumBackgrounds() {
 	return thisroom.BgFrameCount;
 }
 int IAGSEngine::GetCurrentBackground() {
-	return play.bg_frame;
+	return _GP(play).bg_frame;
 }
 BITMAP *IAGSEngine::GetBackgroundScene(int32 index) {
 	return (BITMAP *)thisroom.BgFrames[index].Graphic->GetAllegroBitmap();
@@ -416,10 +416,10 @@ void IAGSEngine::PollSystem() {
 	domouse(DOMOUSE_NOCURSOR);
 	update_polled_stuff_if_runtime();
 	int mbut, mwheelz;
-	if (run_service_mb_controls(mbut, mwheelz) && mbut >= 0 && !play.IsIgnoringInput())
+	if (run_service_mb_controls(mbut, mwheelz) && mbut >= 0 && !_GP(play).IsIgnoringInput())
 		pl_run_plugin_hooks(AGSE_MOUSECLICK, mbut);
 	int kp;
-	if (run_service_key_controls(kp) && !play.IsIgnoringInput()) {
+	if (run_service_key_controls(kp) && !_GP(play).IsIgnoringInput()) {
 		pl_run_plugin_hooks(AGSE_KEYPRESS, kp);
 	}
 
@@ -431,7 +431,7 @@ AGSCharacter *IAGSEngine::GetCharacter(int32 charnum) {
 	return (AGSCharacter *)&_GP(game).chars[charnum];
 }
 AGSGameOptions *IAGSEngine::GetGameOptions() {
-	return (AGSGameOptions *)&play;
+	return (AGSGameOptions *)&_GP(play);
 }
 AGSColor *IAGSEngine::GetPalette() {
 	return (AGSColor *)&palette[0];
@@ -446,7 +446,7 @@ int IAGSEngine::GetPlayerCharacter() {
 	return _GP(game).playercharacter;
 }
 void IAGSEngine::RoomToViewport(int32 *x, int32 *y) {
-	Point scrp = play.RoomToScreen(x ? data_to_game_coord(*x) : 0, y ? data_to_game_coord(*y) : 0);
+	Point scrp = _GP(play).RoomToScreen(x ? data_to_game_coord(*x) : 0, y ? data_to_game_coord(*y) : 0);
 	if (x)
 		*x = scrp.X;
 	if (y)
@@ -456,7 +456,7 @@ void IAGSEngine::ViewportToRoom(int32 *x, int32 *y) {
 	// NOTE: This is an old function that did not account for custom/multiple viewports
 	// and does not expect to fail, therefore we always use primary viewport here.
 	// (Not sure if it's good though)
-	VpPoint vpt = play.ScreenToRoom(x ? game_to_data_coord(*x) : 0, y ? game_to_data_coord(*y) : 0);
+	VpPoint vpt = _GP(play).ScreenToRoom(x ? game_to_data_coord(*x) : 0, y ? game_to_data_coord(*y) : 0);
 	if (x)
 		*x = vpt.first.X;
 	if (y)
@@ -567,7 +567,7 @@ void IAGSEngine::PlaySoundChannel(int32 channel, int32 soundType, int32 volume,
 	stop_and_destroy_channel(channel);
 	// Not sure if it's right to let it play on *any* channel, but this is plugin so let it go...
 	// we must correctly stop background voice speech if it takes over speech chan
-	if (channel == SCHAN_SPEECH && play.IsNonBlockingVoiceSpeech())
+	if (channel == SCHAN_SPEECH && _GP(play).IsNonBlockingVoiceSpeech())
 		stop_voice_nonblocking();
 
 	SOUNDCLIP *newcha = nullptr;


Commit: ae9a824fba9f5fb4c6534939ae35664f8b3d1629
    https://github.com/scummvm/scummvm/commit/ae9a824fba9f5fb4c6534939ae35664f8b3d1629
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-02-27T15:31:34-08:00

Commit Message:
AGS: Fix text strings changed in game search & replace

Changed paths:
    engines/ags/engine/ac/dialog.cpp
    engines/ags/engine/ac/display.cpp
    engines/ags/engine/ac/game.cpp
    engines/ags/engine/ac/room.cpp
    engines/ags/engine/ac/translation.cpp
    engines/ags/engine/game/game_init.cpp
    engines/ags/engine/game/savegame.cpp
    engines/ags/engine/gfx/graphicsdriver.h
    engines/ags/engine/main/config.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/main/game_start.cpp
    engines/ags/engine/platform/linux/acpllnx.cpp
    engines/ags/shared/game/main_game_file.cpp
    engines/ags/shared/gui/guilistbox.cpp


diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index 4e61043a88..224199470b 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -676,7 +676,7 @@ void DialogOptions::Redraw() {
 		if (areawid < data_to_game_coord(_GP(play).min_dialogoption_width)) {
 			areawid = data_to_game_coord(_GP(play).min_dialogoption_width);
 			if (_GP(play).min_dialogoption_width > _GP(play).max_dialogoption_width)
-				quit("!_GP(game).min_dialogoption_width is larger than _GP(game).max_dialogoption_width");
+				quit("!game.min_dialogoption_width is larger than game.max_dialogoption_width");
 		}
 
 		GET_OPTIONS_HEIGHT
diff --git a/engines/ags/engine/ac/display.cpp b/engines/ags/engine/ac/display.cpp
index 4f086346de..4f2f8b0037 100644
--- a/engines/ags/engine/ac/display.cpp
+++ b/engines/ags/engine/ac/display.cpp
@@ -421,7 +421,7 @@ int GetTextDisplayTime(const char *text, int canberel) {
 		return 0;
 
 	if (_GP(play).text_speed + _GP(play).text_speed_modifier <= 0)
-		quit("!Text speed is zero; unable to display text. Check your _GP(game).text_speed settings.");
+		quit("!Text speed is zero; unable to display text. Check your game.text_speed settings.");
 
 	// Store how many game loops per character of text
 	// This is calculated using a hard-coded 15 for the text speed,
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 490ad6e18e..8cada9fb09 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -224,7 +224,7 @@ int getloctype_index = 0, getloctype_throughgui = 0;
 
 void Game_StopAudio(int audioType) {
 	if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
-		quitprintf("!_GP(game).StopAudio: invalid audio type %d", audioType);
+		quitprintf("!game.StopAudio: invalid audio type %d", audioType);
 	int aa;
 
 	for (aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
@@ -242,7 +242,7 @@ void Game_StopAudio(int audioType) {
 
 int Game_IsAudioPlaying(int audioType) {
 	if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
-		quitprintf("!_GP(game).IsAudioPlaying: invalid audio type %d", audioType);
+		quitprintf("!game.IsAudioPlaying: invalid audio type %d", audioType);
 
 	if (_GP(play).fast_forward)
 		return 0;
@@ -260,20 +260,20 @@ int Game_IsAudioPlaying(int audioType) {
 
 void Game_SetAudioTypeSpeechVolumeDrop(int audioType, int volumeDrop) {
 	if ((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size()))
-		quitprintf("!_GP(game).SetAudioTypeVolume: invalid audio type: %d", audioType);
+		quitprintf("!game.SetAudioTypeVolume: invalid audio type: %d", audioType);
 
-	Debug::Printf("_GP(game).SetAudioTypeSpeechVolumeDrop: type: %d, drop: %d", audioType, volumeDrop);
+	Debug::Printf("game.SetAudioTypeSpeechVolumeDrop: type: %d, drop: %d", audioType, volumeDrop);
 	_GP(game).audioClipTypes[audioType].volume_reduction_while_speech_playing = volumeDrop;
 	update_volume_drop_if_voiceover();
 }
 
 void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
 	if ((volume < 0) || (volume > 100))
-		quitprintf("!_GP(game).SetAudioTypeVolume: volume %d is not between 0..100", volume);
+		quitprintf("!game.SetAudioTypeVolume: volume %d is not between 0..100", volume);
 	if ((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size()))
-		quitprintf("!_GP(game).SetAudioTypeVolume: invalid audio type: %d", audioType);
+		quitprintf("!game.SetAudioTypeVolume: invalid audio type: %d", audioType);
 
-	Debug::Printf("_GP(game).SetAudioTypeVolume: type: %d, volume: %d, change: %d", audioType, volume, changeType);
+	Debug::Printf("game.SetAudioTypeVolume: type: %d, volume: %d, change: %d", audioType, volume, changeType);
 	if ((changeType == VOL_CHANGEEXISTING) ||
 	        (changeType == VOL_BOTH)) {
 		AudioChannelsLock lock;
@@ -628,23 +628,16 @@ void unload_game_file() {
 	_GP(game).Free();
 }
 
-
-
-
-
-
 const char *Game_GetGlobalStrings(int index) {
 	if ((index < 0) || (index >= MAXGLOBALSTRINGS))
-		quit("!_GP(game).GlobalStrings: invalid index");
+		quit("!game.GlobalStrings: invalid index");
 
 	return CreateNewScriptString(_GP(play).globalstrings[index]);
 }
 
 
-
 char gamefilenamebuf[200];
 
-
 // ** GetGameParameter replacement functions
 
 int Game_GetInventoryItemCount() {
@@ -1033,7 +1026,7 @@ void create_savegame_screenshot(Bitmap *&screenShot) {
 			usehit = viewport.GetHeight();
 
 		if ((_GP(play).screenshot_width < 16) || (_GP(play).screenshot_height < 16))
-			quit("!Invalid _GP(game).screenshot_width/height, must be from 16x16 to screen res");
+			quit("!Invalid game.screenshot_width/height, must be from 16x16 to screen res");
 
 		screenShot = CopyScreenIntoBitmap(usewid, usehit);
 
@@ -1054,7 +1047,7 @@ void save_game(int slotn, const char *descript) {
 	}
 
 	if (platform->GetDiskFreeSpaceMB() < 2) {
-		Display("ERROR: There is not enough disk space free to save the _GP(game). Clear some disk space and try again.");
+		Display("ERROR: There is not enough disk space free to save the game. Clear some disk space and try again.");
 		return;
 	}
 
@@ -1615,7 +1608,7 @@ bool try_restore_save(int slot) {
 	bool data_overwritten;
 	HSaveError err = load_game(slot, data_overwritten);
 	if (!err) {
-		String error = String::FromFormat("Unable to restore the saved _GP(game).\n%s",
+		String error = String::FromFormat("Unable to restore the saved game.\n%s",
 		                                  err->FullMessage().GetCStr());
 		// currently AGS cannot properly revert to stable state if some of the
 		// game data was released or overwritten by the data from save file,
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 93e5c04684..09f24de99b 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -226,7 +226,7 @@ const char *Room_GetMessages(int index) {
 
 // Makes sure that room background and walk-behind mask are matching room size
 // in game resolution coordinates; in other words makes graphics appropriate
-// for display in the _GP(game).
+// for display in the game.
 void convert_room_background_to_game_res() {
 	if (!_GP(game).AllowRelativeRes() || !thisroom.IsRelativeRes())
 		return;
@@ -481,7 +481,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
 
 	if ((thisroom.GameID != NO_GAME_ID_IN_ROOM_FILE) &&
 	        (thisroom.GameID != _GP(game).uniqueid)) {
-		quitprintf("!Unable to load '%s'. This room file is assigned to a different _GP(game).", room_filename.GetCStr());
+		quitprintf("!Unable to load '%s'. This room file is assigned to a different game.", room_filename.GetCStr());
 	}
 
 	convert_room_coordinates_to_data_res(&thisroom);
diff --git a/engines/ags/engine/ac/translation.cpp b/engines/ags/engine/ac/translation.cpp
index 88a89c1303..909a0b2ca8 100644
--- a/engines/ags/engine/ac/translation.cpp
+++ b/engines/ags/engine/ac/translation.cpp
@@ -135,7 +135,7 @@ bool parse_translation(Stream *language_file, String &parse_error) {
 			uidfrom = language_file->ReadInt32();
 			read_string_decrypt(language_file, wasgamename, sizeof(wasgamename));
 			if ((uidfrom != _GP(game).uniqueid) || (strcmp(wasgamename, _GP(game).gamename) != 0)) {
-				parse_error.Format("The translation file is not compatible with this _GP(game). The translation is designed for '%s'.",
+				parse_error.Format("The translation file is not compatible with this game. The translation is designed for '%s'.",
 					wasgamename);
 				return false;
 			}
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 6194e7833e..be4d4dacc0 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -122,7 +122,7 @@ String GetGameInitErrorText(GameInitErrorType err) {
 	case kGameInitErr_NoError:
 		return "No error.";
 	case kGameInitErr_NoFonts:
-		return "No fonts specified to be used in this _GP(game).";
+		return "No fonts specified to be used in this game.";
 	case kGameInitErr_TooManyAudioTypes:
 		return "Too many audio types for this engine to handle.";
 	case kGameInitErr_EntityInitFail:
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index f10f4f0ad2..7cb4963fb0 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -69,7 +69,7 @@ namespace AGS3 {
 using namespace Shared;
 using namespace Engine;
 
-// function is currently implemented in _GP(game).cpp
+// function is currently implemented in game.cpp
 HSaveError restore_game_data(Stream *in, SavegameVersion svg_version, const PreservedParams &pp, RestoredData &r_data);
 
 
@@ -132,7 +132,7 @@ String GetSavegameErrorText(SavegameErrorType err) {
 	case kSvgErr_IncompatibleEngine:
 		return "Save was written by incompatible engine, or file is corrupted.";
 	case kSvgErr_GameGuidMismatch:
-		return "Game GUID does not match, saved by a different _GP(game).";
+		return "Game GUID does not match, saved by a different game.";
 	case kSvgErr_ComponentListOpeningTagFormat:
 		return "Failed to parse opening tag of the components list.";
 	case kSvgErr_ComponentListClosingTagMissing:
@@ -154,7 +154,7 @@ String GetSavegameErrorText(SavegameErrorType err) {
 	case kSvgErr_UnsupportedComponentVersion:
 		return "Component data version not supported.";
 	case kSvgErr_GameContentAssertion:
-		return "Saved content does not match current _GP(game).";
+		return "Saved content does not match current game.";
 	case kSvgErr_InconsistentData:
 		return "Inconsistent save data, or file is corrupted.";
 	case kSvgErr_InconsistentPlugin:
diff --git a/engines/ags/engine/gfx/graphicsdriver.h b/engines/ags/engine/gfx/graphicsdriver.h
index 0c0c36ad79..1c7a9da216 100644
--- a/engines/ags/engine/gfx/graphicsdriver.h
+++ b/engines/ags/engine/gfx/graphicsdriver.h
@@ -158,7 +158,7 @@ public:
 	// the final resolution, as opposed to drawing to native-resolution buffer
 	// and scaling to final frame. The effect may be that sprites that are
 	// drawn with additional fractional scaling will appear more detailed than
-	// the rest of the _GP(game). The effect is stronger for the low-res games being
+	// the rest of the game. The effect is stronger for the low-res games being
 	// rendered in the high-res mode.
 	virtual void RenderSpritesAtScreenResolution(bool enabled, int supersampling = 1) = 0;
 	// TODO: move fade-in/out/boxout functions out of the graphics driver!! make everything render through
diff --git a/engines/ags/engine/main/config.cpp b/engines/ags/engine/main/config.cpp
index 9d19938b80..b218a26956 100644
--- a/engines/ags/engine/main/config.cpp
+++ b/engines/ags/engine/main/config.cpp
@@ -198,7 +198,7 @@ int convert_fp_to_scaling(uint32_t scaling) {
 void graphics_mode_get_defaults(bool windowed, ScreenSizeSetup &scsz_setup, GameFrameSetup &frame_setup) {
 	scsz_setup.Size = Size();
 	if (windowed) {
-		// For the windowed we define mode by the scaled _GP(game).
+		// For the windowed we define mode by the scaled game.
 		scsz_setup.SizeDef = kScreenDef_ByGameScaling;
 		scsz_setup.MatchDeviceRatio = false;
 		frame_setup = usetup.Screen.WinGameFrame;
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 7d6ef0c285..a0d19b27cf 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -588,7 +588,7 @@ int engine_check_disk_space() {
 
 int engine_check_font_was_loaded() {
 	if (!font_first_renderer_loaded()) {
-		platform->DisplayAlert("No game fonts found. At least one font is required to run the _GP(game).");
+		platform->DisplayAlert("No game fonts found. At least one font is required to run the game.");
 		proper_exit = 1;
 		return EXIT_ERROR;
 	}
diff --git a/engines/ags/engine/main/game_start.cpp b/engines/ags/engine/main/game_start.cpp
index d62136864c..ac7d9eb290 100644
--- a/engines/ags/engine/main/game_start.cpp
+++ b/engines/ags/engine/main/game_start.cpp
@@ -129,7 +129,7 @@ void initialize_start_and_play_game(int override_start_room, int loadSaveOnStart
 			int oldalways = _GP(game).options[OPT_ALWAYSSPCH];
 			_GP(game).options[OPT_ALWAYSSPCH] = 0;
 			// PSP: This is normal. Don't show a warning.
-			//Display ("WARNING: AGS has detected that you have an incompatible graphics card for this _GP(game). You may experience colour problems during the _GP(game). Try running the game with \"--15bit\" command line parameter and see if that helps.[[Click the mouse to continue.");
+			//Display ("WARNING: AGS has detected that you have an incompatible graphics card for this game. You may experience colour problems during the game. Try running the game with \"--15bit\" command line parameter and see if that helps.[[Click the mouse to continue.");
 			_GP(game).options[OPT_ALWAYSSPCH] = oldalways;
 		}
 
diff --git a/engines/ags/engine/platform/linux/acpllnx.cpp b/engines/ags/engine/platform/linux/acpllnx.cpp
index b9cdbaee22..f9a0706525 100644
--- a/engines/ags/engine/platform/linux/acpllnx.cpp
+++ b/engines/ags/engine/platform/linux/acpllnx.cpp
@@ -157,7 +157,7 @@ unsigned long AGSLinux::GetDiskFreeSpaceMB() {
 }
 
 const char *AGSLinux::GetNoMouseErrorString() {
-	return "This game requires a mouse. You need to configure and setup your mouse to play this _GP(game).\n";
+	return "This game requires a mouse. You need to configure and setup your mouse to play this game.\n";
 }
 
 const char *AGSLinux::GetAllegroFailUserHint() {
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index d59a74672d..8fe501c4ed 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -78,7 +78,7 @@ String GetMainGameFileErrorText(MainGameFileErrorType err) {
 	case kMGFErr_InvalidPropertyValues:
 		return "Errors encountered when reading custom properties.";
 	case kMGFErr_NoGlobalScript:
-		return "No global script in _GP(game).";
+		return "No global script in game.";
 	case kMGFErr_CreateGlobalScriptFailed:
 		return "Failed to load global script.";
 	case kMGFErr_CreateDialogScriptFailed:
diff --git a/engines/ags/shared/gui/guilistbox.cpp b/engines/ags/shared/gui/guilistbox.cpp
index 91a07e07fd..f908df0dac 100644
--- a/engines/ags/shared/gui/guilistbox.cpp
+++ b/engines/ags/shared/gui/guilistbox.cpp
@@ -362,7 +362,7 @@ void GUIListBox::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
 		Items[i] = StrUtil::ReadString(in);
 	// TODO: investigate this, it might be unreasonable to save and read
 	// savegame index like that because list of savegames may easily change
-	// in between writing and restoring the _GP(game). Perhaps clearing and forcing
+	// in between writing and restoring the game. Perhaps clearing and forcing
 	// this list to update on load somehow may make more sense.
 	if (ListBoxFlags & kListBox_SvgIndex)
 		for (int i = 0; i < ItemCount; ++i)




More information about the Scummvm-git-logs mailing list