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

dreammaster noreply at scummvm.org
Fri Apr 22 05:20:15 UTC 2022


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

Summary:
b4b4110db6 AGS: Implement play_flc_video
e736059bbe AGS: pass key modifiers as an extra arg to `on_key_press`
e0282d5064 AGS: Implemented key handling switch (old/new mode)
5060848b48 AGS: Support requesting particular audio driver
7fb5f6d21a AGS: Support option for software renderer's output driver
cc9bda9251 AGS: Added NumLock and CapsLock key mods
4057fa05b9 AGS: Updated build version (3.6.0.20)
06880291b2 AGS: Refactor restrict_until and ShouldStayInWaitMode()
415235b74e AGS: expanded Button.Animate to feature all common params
bf5e0b2d08 AGS: Updated AnimatingButton save format


Commit: b4b4110db69c0903fdf47bb90a744b82ae04a881
    https://github.com/scummvm/scummvm/commit/b4b4110db69c0903fdf47bb90a744b82ae04a881
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T19:29:53-07:00

Commit Message:
AGS: Implement play_flc_video

Changed paths:
    engines/ags/engine/media/video/video.cpp


diff --git a/engines/ags/engine/media/video/video.cpp b/engines/ags/engine/media/video/video.cpp
index 026f07b6c07..ebe07db2680 100644
--- a/engines/ags/engine/media/video/video.cpp
+++ b/engines/ags/engine/media/video/video.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "video/avi_decoder.h"
+#include "video/flic_decoder.h"
 #include "video/mpegps_decoder.h"
 #include "video/theora_decoder.h"
 #include "ags/shared/core/platform.h"
@@ -151,9 +152,10 @@ bool play_theora_video(const char *name, int flags, VideoSkipType skip, bool sho
 }
 
 bool play_flc_video(int numb, int flags, VideoSkipType skip) {
-	// TODO: play_flc_file
-	Display("This games uses Flic videos that ScummVM doesn't yet support");
-	return false;
+	Video::FlicDecoder decoder;
+	String flicName = String::FromFormat("flic%d.flc", numb);
+
+	return play_video(&decoder, flicName, flags, skip, false);
 }
 
 void video_pause() {


Commit: e736059bbea024839a68dc78d7c5f57d7af05d66
    https://github.com/scummvm/scummvm/commit/e736059bbea024839a68dc78d7c5f57d7af05d66
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T20:23:53-07:00

Commit Message:
AGS: pass key modifiers as an extra arg to `on_key_press`

>From upstream b00b82220cbabf44185e042ef33b543fe2ad3543

Changed paths:
    engines/ags/engine/ac/dialog.cpp
    engines/ags/engine/ac/event.cpp
    engines/ags/engine/ac/event.h
    engines/ags/engine/ac/sys_events.cpp
    engines/ags/engine/main/game_run.cpp
    engines/ags/engine/script/non_blocking_script_function.h
    engines/ags/events.cpp
    engines/ags/events.h
    engines/ags/shared/ac/keycode.h


diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index bf0f18ebd52..1601e08573c 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -850,6 +850,7 @@ bool DialogOptions::Run() {
 		} else if (new_custom_render) {
 			_GP(runDialogOptionKeyPressHandlerFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
 			_GP(runDialogOptionKeyPressHandlerFunc).params[1].SetInt32(AGSKeyToScriptKey(gkey));
+			_GP(runDialogOptionKeyPressHandlerFunc).params[2].SetInt32(ki.Mod);
 			run_function_on_non_blocking_thread(&_GP(runDialogOptionKeyPressHandlerFunc));
 			_GP(runDialogOptionTextInputHandlerFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
 			_GP(runDialogOptionTextInputHandlerFunc).params[1].SetInt32(ki.UChar);
diff --git a/engines/ags/engine/ac/event.cpp b/engines/ags/engine/ac/event.cpp
index 75a8e7e15aa..9252e5e5bb3 100644
--- a/engines/ags/engine/ac/event.cpp
+++ b/engines/ags/engine/ac/event.cpp
@@ -134,13 +134,14 @@ void process_event(const EventHappened *evp) {
 	RuntimeScriptValue rval_null;
 	if (evp->type == EV_TEXTSCRIPT) {
 		_G(ccError) = 0;
-		if (evp->data2 > -1000) {
-			RuntimeScriptValue params[]{ evp->data2 };
+		RuntimeScriptValue params[2]{ evp->data2, evp->data3 };
+		if (evp->data3 > -1000)
+			QueueScriptFunction(kScInstGame, _G(tsnames)[evp->data1], 2, params);
+		else if (evp->data2 > -1000)
 			QueueScriptFunction(kScInstGame, _G(tsnames)[evp->data1], 1, params);
-		} else {
+		else
 			QueueScriptFunction(kScInstGame, _G(tsnames)[evp->data1]);
-		}
-	} else if (evp->type == EV_NEWROOM) {
+	}  else if (evp->type == EV_NEWROOM) {
 		NewRoom(evp->data1);
 	} else if (evp->type == EV_RUNEVBLOCK) {
 		Interaction *evpt = nullptr;
diff --git a/engines/ags/engine/ac/event.h b/engines/ags/engine/ac/event.h
index a795c7a88de..7187fb42e9c 100644
--- a/engines/ags/engine/ac/event.h
+++ b/engines/ags/engine/ac/event.h
@@ -63,8 +63,8 @@ void run_on_event(int evtype, RuntimeScriptValue &wparam);
 void run_room_event(int id);
 void run_event_block_inv(int invNum, int event);
 // event list functions
-void setevent(int evtyp, int ev1 = 0, int ev2 = -1000, int ev3 = 0);
-void force_event(int evtyp, int ev1 = 0, int ev2 = -1000, int ev3 = 0);
+void setevent(int evtyp, int ev1 = 0, int ev2 = -1000, int ev3 = -1000);
+void force_event(int evtyp, int ev1 = 0, int ev2 = -1000, int ev3 = -1000);
 void process_event(const EventHappened *evp);
 void runevent_now(int evtyp, int ev1, int ev2, int ev3);
 void processallevents();
diff --git a/engines/ags/engine/ac/sys_events.cpp b/engines/ags/engine/ac/sys_events.cpp
index 8d9bed8072b..0cbf9feec97 100644
--- a/engines/ags/engine/ac/sys_events.cpp
+++ b/engines/ags/engine/ac/sys_events.cpp
@@ -65,7 +65,7 @@ static void(*_on_switchout_callback)(void) = nullptr;
 KeyInput ags_keycode_from_scummvm(const Common::Event &event) {
 	KeyInput ki;
 
-	ki.Key = ::AGS::g_events->scummvm_key_to_ags_key(event);
+	ki.Key = ::AGS::g_events->scummvm_key_to_ags_key(event, ki.Mod);
 
 	return ki;
 }
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index 0981fb9e672..3a3dde4529b 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -499,8 +499,9 @@ static void check_keyboard_controls() {
 
 	if (!keywasprocessed) {
 		int sckey = AGSKeyToScriptKey(kgn);
-		debug_script_log("Running on_key_press keycode %d", sckey);
-		setevent(EV_TEXTSCRIPT, TS_KEYPRESS, sckey);
+		int sckeymod = ki.Mod;
+		debug_script_log("Running on_key_press keycode %d, mod %d", sckey, sckeymod);
+		setevent(EV_TEXTSCRIPT, TS_KEYPRESS, sckey, sckeymod);
 		if (ki.UChar > 0) {
 			debug_script_log("Running on_text_input char %s (%d)", ki.Text, ki.UChar);
 			setevent(EV_TEXTSCRIPT, TS_TEXTINPUT, ki.UChar);
diff --git a/engines/ags/engine/script/non_blocking_script_function.h b/engines/ags/engine/script/non_blocking_script_function.h
index 3b7de5646fb..ed553bbd58b 100644
--- a/engines/ags/engine/script/non_blocking_script_function.h
+++ b/engines/ags/engine/script/non_blocking_script_function.h
@@ -31,9 +31,7 @@ namespace AGS3 {
 struct NonBlockingScriptFunction {
 	const char *functionName;
 	int numParameters;
-	//void* param1;
-	//void* param2;
-	RuntimeScriptValue params[2];
+	RuntimeScriptValue params[3];
 	bool roomHasFunction;
 	bool globalScriptHasFunction;
 	std::vector<bool> moduleHasFunction;
diff --git a/engines/ags/events.cpp b/engines/ags/events.cpp
index 735eac250c4..8981ed26cf3 100644
--- a/engines/ags/events.cpp
+++ b/engines/ags/events.cpp
@@ -22,6 +22,7 @@
 #include "common/system.h"
 #include "ags/events.h"
 #include "ags/globals.h"
+#include "ags/shared/ac/keycode.h"
 
 namespace AGS {
 
@@ -310,13 +311,19 @@ bool EventsManager::ags_key_to_scancode(AGS3::eAGSKeyCode key, Common::KeyCode(&
 	return false;
 }
 
-AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &event) {
+AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &event, int &ags_mod) {
 	if (event.type != Common::EVENT_KEYDOWN)
 		return AGS3::eAGSKeyCodeNone;
 
 	const Common::KeyCode sym = event.kbd.keycode;
 	const uint16 mod = event.kbd.flags;
 
+	// First handle the mods, - these are straightforward
+	ags_mod = 0;
+	if (mod & Common::KBD_SHIFT) ags_mod |= AGS3::eAGSModLShift;
+	if (mod & Common::KBD_CTRL)  ags_mod |= AGS3::eAGSModLCtrl;
+	if (mod & Common::KBD_ALT)   ags_mod |= AGS3::eAGSModLAlt;
+
 	// Ctrl and Alt combinations realign the letter code to certain offset
 	if (sym >= Common::KEYCODE_a && sym <= Common::KEYCODE_z) {
 		if ((mod & Common::KBD_CTRL) != 0) // align letters to code 1
diff --git a/engines/ags/events.h b/engines/ags/events.h
index e2c42595a08..9c902e43156 100644
--- a/engines/ags/events.h
+++ b/engines/ags/events.h
@@ -52,7 +52,7 @@ public:
 	/*
 	 * Converts a ScummVM event to the ags keycode
 	 */
-	static AGS3::eAGSKeyCode scummvm_key_to_ags_key(const Common::Event &event);
+	static AGS3::eAGSKeyCode scummvm_key_to_ags_key(const Common::Event &event, int &ags_mod);
 
 public:
 	EventsManager();
diff --git a/engines/ags/shared/ac/keycode.h b/engines/ags/shared/ac/keycode.h
index d14e6b6fabb..63cfb97a06e 100644
--- a/engines/ags/shared/ac/keycode.h
+++ b/engines/ags/shared/ac/keycode.h
@@ -251,11 +251,22 @@ enum eAGSKeyCode {
 	*/
 };
 
+// AGS key modifiers
+enum eAGSKeyMod {
+	eAGSModLShift = 0x0001,
+	eAGSModRShift = 0x0002,
+	eAGSModLCtrl = 0x0004,
+	eAGSModRCtrl = 0x0008,
+	eAGSModLAlt = 0x0010,
+	eAGSModRAlt = 0x0020
+};
+
 // Combined key code and a textual representation in UTF-8
 struct KeyInput {
 	const static size_t UTF8_ARR_SIZE = 5;
 
 	eAGSKeyCode Key = eAGSKeyCodeNone; // actual key code
+	int         Mod = 0; // key modifiers
 	int         UChar = 0; // full character value (supports unicode)
 	char        Text[UTF8_ARR_SIZE]{}; // character in a string format
 


Commit: e0282d506447f490c61b1e37f3462e48f8c2a096
    https://github.com/scummvm/scummvm/commit/e0282d506447f490c61b1e37f3462e48f8c2a096
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T20:55:38-07:00

Commit Message:
AGS: Implemented key handling switch (old/new mode)

>From upstream b9e23731d0d792d095c1d4e83fa1797a1afeee46

Changed paths:
    engines/ags/engine/ac/dialog.cpp
    engines/ags/engine/ac/sys_events.cpp
    engines/ags/engine/ac/sys_events.h
    engines/ags/engine/main/game_run.cpp
    engines/ags/engine/main/game_run.h
    engines/ags/events.cpp
    engines/ags/events.h
    engines/ags/globals.cpp
    engines/ags/shared/ac/game_struct_defines.h


diff --git a/engines/ags/engine/ac/dialog.cpp b/engines/ags/engine/ac/dialog.cpp
index 1601e08573c..a55597b528b 100644
--- a/engines/ags/engine/ac/dialog.cpp
+++ b/engines/ags/engine/ac/dialog.cpp
@@ -801,6 +801,7 @@ bool DialogOptions::Run() {
 	sys_evt_process_pending();
 
 	const bool new_custom_render = usingCustomRendering && _GP(game).options[OPT_DIALOGOPTIONSAPI] >= 0;
+	const bool old_keyhandle = _GP(game).options[OPT_KEYHANDLEAPI] == 0;
 
 	if (runGameLoopsInBackground) {
 		_GP(play).disabled_user_interface++;
@@ -848,13 +849,19 @@ bool DialogOptions::Run() {
 				}
 			}
 		} else if (new_custom_render) {
-			_GP(runDialogOptionKeyPressHandlerFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
-			_GP(runDialogOptionKeyPressHandlerFunc).params[1].SetInt32(AGSKeyToScriptKey(gkey));
-			_GP(runDialogOptionKeyPressHandlerFunc).params[2].SetInt32(ki.Mod);
-			run_function_on_non_blocking_thread(&_GP(runDialogOptionKeyPressHandlerFunc));
-			_GP(runDialogOptionTextInputHandlerFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
-			_GP(runDialogOptionTextInputHandlerFunc).params[1].SetInt32(ki.UChar);
-			run_function_on_non_blocking_thread(&_GP(runDialogOptionKeyPressHandlerFunc));
+			if (old_keyhandle || (ki.UChar == 0)) {
+				// "dialog_options_key_press"
+				_GP(runDialogOptionKeyPressHandlerFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
+				_GP(runDialogOptionKeyPressHandlerFunc).params[1].SetInt32(AGSKeyToScriptKey(gkey));
+				_GP(runDialogOptionKeyPressHandlerFunc).params[2].SetInt32(ki.Mod);
+				run_function_on_non_blocking_thread(&_GP(runDialogOptionKeyPressHandlerFunc));
+			}
+			if (!old_keyhandle && (ki.UChar > 0)) {
+				// "dialog_options_text_input"
+				_GP(runDialogOptionTextInputHandlerFunc).params[0].SetDynamicObject(&_GP(ccDialogOptionsRendering), &_GP(ccDialogOptionsRendering));
+				_GP(runDialogOptionTextInputHandlerFunc).params[1].SetInt32(ki.UChar);
+				run_function_on_non_blocking_thread(&_GP(runDialogOptionKeyPressHandlerFunc));
+			}
 		}
 		// Allow selection of options by keyboard shortcuts
 		else if (_GP(game).options[OPT_DIALOGNUMBERED] >= kDlgOptKeysOnly &&
diff --git a/engines/ags/engine/ac/sys_events.cpp b/engines/ags/engine/ac/sys_events.cpp
index 0cbf9feec97..c0e2522b1d1 100644
--- a/engines/ags/engine/ac/sys_events.cpp
+++ b/engines/ags/engine/ac/sys_events.cpp
@@ -62,10 +62,10 @@ static void(*_on_switchout_callback)(void) = nullptr;
 // KEYBOARD INPUT
 // ----------------------------------------------------------------------------
 
-KeyInput ags_keycode_from_scummvm(const Common::Event &event) {
+KeyInput ags_keycode_from_scummvm(const Common::Event &event, bool old_keyhandle) {
 	KeyInput ki;
 
-	ki.Key = ::AGS::g_events->scummvm_key_to_ags_key(event, ki.Mod);
+	ki.Key = ::AGS::g_events->scummvm_key_to_ags_key(event, ki.Mod, old_keyhandle);
 
 	return ki;
 }
diff --git a/engines/ags/engine/ac/sys_events.h b/engines/ags/engine/ac/sys_events.h
index 88e64d70d13..7313b641bf9 100644
--- a/engines/ags/engine/ac/sys_events.h
+++ b/engines/ags/engine/ac/sys_events.h
@@ -65,7 +65,10 @@ inline int make_merged_mod(int mod) {
 	return m_mod;
 }
 
-extern KeyInput ags_keycode_from_scummvm(const Common::Event &event);
+// Converts ScummVM key event to eAGSKeyCode if it is in proper range,
+// see comments to eAGSKeyCode for details.
+// Optionally works in bacward compatible mode (old_keyhandle)
+extern KeyInput ags_keycode_from_scummvm(const Common::Event &event, bool old_keyhandle);
 
 // Tells if there are any buffered key events
 extern bool ags_keyevent_ready();
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index 3a3dde4529b..623da50799e 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -243,7 +243,12 @@ int old_key_mod = 0; // for saving previous key mods
 
 // Runs service key controls, returns false if service key combinations were handled
 // and no more processing required, otherwise returns true and provides current keycode and key shifts.
+//
+// * old_keyhandle mode is a backward compatible input handling mode, where
+//   - lone mod keys are not passed further into the engine;
+//   - key + mod combos are merged into one key code for the script callback.
 bool run_service_key_controls(KeyInput &out_key) {
+	const bool old_keyhandle = (_GP(game).options[OPT_KEYHANDLEAPI] == 0);
 	bool handled = false;
 	const bool key_valid = ags_keyevent_ready();
 	const Common::Event key_evt = key_valid ? ags_get_next_keyevent() : Common::Event();
@@ -299,11 +304,10 @@ bool run_service_key_controls(KeyInput &out_key) {
 
 	if (!key_valid)
 		return false; // if there was no key press, finish after handling current mod state
-	if (is_only_mod_key || handled)
-		return false; // rest of engine currently does not use pressed mod keys
-	// change this when it's no longer true (but be mindful about key-skipping!)
+	if (handled || (old_keyhandle && is_only_mod_key))
+		return false; // in backward mode the engine does not react to single mod keys
 
-	KeyInput ki = ags_keycode_from_scummvm(key_evt);
+	KeyInput ki = ags_keycode_from_scummvm(key_evt, old_keyhandle);
 	eAGSKeyCode agskey = ki.Key;
 	if (agskey == eAGSKeyCodeNone)
 		return false; // should skip this key event
@@ -405,6 +409,7 @@ bool run_service_mb_controls(int &mbut, int &mwheelz) {
 
 // Runs default keyboard handling
 static void check_keyboard_controls() {
+	const bool old_keyhandle = _GP(game).options[OPT_KEYHANDLEAPI] == 0;
 	// First check for service engine's combinations (mouse lock, display mode switch, and so forth)
 	KeyInput ki;
 	if (!run_service_key_controls(ki)) {
@@ -500,9 +505,11 @@ static void check_keyboard_controls() {
 	if (!keywasprocessed) {
 		int sckey = AGSKeyToScriptKey(kgn);
 		int sckeymod = ki.Mod;
-		debug_script_log("Running on_key_press keycode %d, mod %d", sckey, sckeymod);
-		setevent(EV_TEXTSCRIPT, TS_KEYPRESS, sckey, sckeymod);
-		if (ki.UChar > 0) {
+		if (old_keyhandle || (ki.UChar == 0)) {
+			debug_script_log("Running on_key_press keycode %d, mod %d", sckey, sckeymod);
+			setevent(EV_TEXTSCRIPT, TS_KEYPRESS, sckey, sckeymod);
+		}
+		if (!old_keyhandle && (ki.UChar > 0)) {
 			debug_script_log("Running on_text_input char %s (%d)", ki.Text, ki.UChar);
 			setevent(EV_TEXTSCRIPT, TS_TEXTINPUT, ki.UChar);
 		}
diff --git a/engines/ags/engine/main/game_run.h b/engines/ags/engine/main/game_run.h
index 102134d22dc..340d70d9635 100644
--- a/engines/ags/engine/main/game_run.h
+++ b/engines/ags/engine/main/game_run.h
@@ -50,9 +50,9 @@ void UpdateGameAudioOnly();
 // Gets current logical game FPS, this is normally a fixed number set in script;
 // in case of "maxed fps" mode this function returns real measured FPS.
 float get_current_fps();
+struct KeyInput;
 // Runs service key controls, returns false if no key was pressed or key input was claimed by the engine,
 // otherwise returns true and provides a keycode.
-struct KeyInput;
 bool run_service_key_controls(KeyInput &kgn);
 // Runs service mouse controls, returns false if mouse input was claimed by the engine,
 // otherwise returns true and provides mouse button code.
diff --git a/engines/ags/events.cpp b/engines/ags/events.cpp
index 8981ed26cf3..42054e9ebe3 100644
--- a/engines/ags/events.cpp
+++ b/engines/ags/events.cpp
@@ -311,7 +311,7 @@ bool EventsManager::ags_key_to_scancode(AGS3::eAGSKeyCode key, Common::KeyCode(&
 	return false;
 }
 
-AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &event, int &ags_mod) {
+AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &event, int &ags_mod, bool old_keyhandle) {
 	if (event.type != Common::EVENT_KEYDOWN)
 		return AGS3::eAGSKeyCodeNone;
 
@@ -324,13 +324,17 @@ AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &eve
 	if (mod & Common::KBD_CTRL)  ags_mod |= AGS3::eAGSModLCtrl;
 	if (mod & Common::KBD_ALT)   ags_mod |= AGS3::eAGSModLAlt;
 
-	// Ctrl and Alt combinations realign the letter code to certain offset
-	if (sym >= Common::KEYCODE_a && sym <= Common::KEYCODE_z) {
+	// Old mode: Ctrl and Alt combinations realign the letter code to certain offset
+	if (old_keyhandle && (sym >= Common::KEYCODE_a && sym <= Common::KEYCODE_z)) {
 		if ((mod & Common::KBD_CTRL) != 0) // align letters to code 1
 			return static_cast<AGS3::eAGSKeyCode>(0 + (sym - Common::KEYCODE_a) + 1);
 		else if ((mod & Common::KBD_ALT) != 0) // align letters to code 301
 			return static_cast<AGS3::eAGSKeyCode>(AGS_EXT_KEY_SHIFT + (sym - Common::KEYCODE_a) + 1);
 	}
+	// New mode: also handle common key range
+	else if (!old_keyhandle && (sym >= Common::KEYCODE_SPACE && sym <= Common::KEYCODE_z)) {
+		return static_cast<AGS3::eAGSKeyCode>(sym);
+	}
 
 	if (event.kbd.ascii >= 32 && event.kbd.ascii <= 127)
 		return static_cast<AGS3::eAGSKeyCode>(event.kbd.ascii);
@@ -406,6 +410,18 @@ AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &eve
 	case Common::KEYCODE_KP_PERIOD:
 	case Common::KEYCODE_DELETE:
 		return AGS3::eAGSKeyCodeDelete;
+	case Common::KEYCODE_LSHIFT:
+		return AGS3::eAGSKeyCodeLShift;
+	case Common::KEYCODE_RSHIFT:
+		return AGS3::eAGSKeyCodeRShift;
+	case Common::KEYCODE_LCTRL:
+		return AGS3::eAGSKeyCodeLCtrl;
+	case Common::KEYCODE_RCTRL:
+		return AGS3::eAGSKeyCodeRCtrl;
+	case Common::KEYCODE_LALT:
+		return AGS3::eAGSKeyCodeLAlt;
+	case Common::KEYCODE_RALT:
+		return AGS3::eAGSKeyCodeRAlt;
 
 	default:
 		return AGS3::eAGSKeyCodeNone;
diff --git a/engines/ags/events.h b/engines/ags/events.h
index 9c902e43156..c1de5f2a1e9 100644
--- a/engines/ags/events.h
+++ b/engines/ags/events.h
@@ -52,7 +52,7 @@ public:
 	/*
 	 * Converts a ScummVM event to the ags keycode
 	 */
-	static AGS3::eAGSKeyCode scummvm_key_to_ags_key(const Common::Event &event, int &ags_mod);
+	static AGS3::eAGSKeyCode scummvm_key_to_ags_key(const Common::Event &event, int &ags_mod, bool old_keyhandle);
 
 public:
 	EventsManager();
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 9a7902cffff..35e7c677a21 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -333,7 +333,7 @@ Globals::Globals() {
 	_renderDialogOptionsFunc = new NonBlockingScriptFunction("dialog_options_render", 1);
 	_getDialogOptionUnderCursorFunc = new NonBlockingScriptFunction("dialog_options_get_active", 1);
 	_runDialogOptionMouseClickHandlerFunc = new NonBlockingScriptFunction("dialog_options_mouse_click", 2);
-	_runDialogOptionKeyPressHandlerFunc = new NonBlockingScriptFunction("dialog_options_key_press", 2);
+	_runDialogOptionKeyPressHandlerFunc = new NonBlockingScriptFunction("dialog_options_key_press", 3);
 	_runDialogOptionTextInputHandlerFunc = new NonBlockingScriptFunction("dialog_options_text_input", 2);
 	_runDialogOptionRepExecFunc = new NonBlockingScriptFunction("dialog_options_repexec", 1);
 	_scsystem = new ScriptSystem();
diff --git a/engines/ags/shared/ac/game_struct_defines.h b/engines/ags/shared/ac/game_struct_defines.h
index 5bde0137f7f..fdf37ddb78c 100644
--- a/engines/ags/shared/ac/game_struct_defines.h
+++ b/engines/ags/shared/ac/game_struct_defines.h
@@ -85,7 +85,8 @@ namespace AGS3 {
 #define OPT_WALKSPEEDABSOLUTE 47 // if movement speeds are independent of walkable mask resolution
 #define OPT_CLIPGUICONTROLS 48 // clip drawn gui control contents to the control's rectangle
 #define OPT_GAMETEXTENCODING 49 // how the text in the game data should be interpreted
-#define OPT_HIGHESTOPTION   OPT_GAMETEXTENCODING
+#define OPT_KEYHANDLEAPI    50 // key handling mode (old/new)
+#define OPT_HIGHESTOPTION   OPT_KEYHANDLEAPI
 #define OPT_NOMODMUSIC      98
 #define OPT_LIPSYNCTEXT     99
 #define PORTRAIT_LEFT       0


Commit: 5060848b483613163a58c7d2466d371607a8c96f
    https://github.com/scummvm/scummvm/commit/5060848b483613163a58c7d2466d371607a8c96f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T21:20:58-07:00

Commit Message:
AGS: Support requesting particular audio driver

>From upstream 076e675a3d031cd0a9d162f914e6d94c93a10d71

Changed paths:
    engines/ags/engine/ac/game_setup.cpp
    engines/ags/engine/ac/game_setup.h
    engines/ags/engine/ac/global_video.cpp
    engines/ags/engine/main/config.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/media/audio/audio.cpp
    engines/ags/engine/platform/base/sys_main.cpp
    engines/ags/engine/platform/base/sys_main.h
    engines/ags/plugins/ags_plugin.cpp


diff --git a/engines/ags/engine/ac/game_setup.cpp b/engines/ags/engine/ac/game_setup.cpp
index a32820938f5..9af8dfbdc20 100644
--- a/engines/ags/engine/ac/game_setup.cpp
+++ b/engines/ags/engine/ac/game_setup.cpp
@@ -25,7 +25,7 @@ namespace AGS3 {
 
 GameSetup::GameSetup() {
 	local_user_conf = false;
-	audio_backend = 1;
+	audio_enabled = true;
 	no_speech_pack = false;
 	textheight = 0;
 	enable_antialiasing = false;
diff --git a/engines/ags/engine/ac/game_setup.h b/engines/ags/engine/ac/game_setup.h
index 393929e7af7..e91d0244e9e 100644
--- a/engines/ags/engine/ac/game_setup.h
+++ b/engines/ags/engine/ac/game_setup.h
@@ -60,8 +60,9 @@ using AGS::Shared::String;
 // that engine may use a "config" object or combo of objects to store
 // current user config, which may also be changed from script, and saved.
 struct GameSetup {
-	int    audio_backend; // abstract option, currently only works as on/off
-	int textheight; // text height used on the certain built-in GUI // TODO: move out to game class?
+	bool  audio_enabled;
+	String audio_driver;
+	int   textheight; // text height used on the certain built-in GUI // TODO: move out to game class?
 	bool  no_speech_pack;
 	bool  enable_antialiasing;
 	bool  disable_exception_handling;
diff --git a/engines/ags/engine/ac/global_video.cpp b/engines/ags/engine/ac/global_video.cpp
index 320ac267186..253c2507d48 100644
--- a/engines/ags/engine/ac/global_video.cpp
+++ b/engines/ags/engine/ac/global_video.cpp
@@ -101,7 +101,7 @@ void PlayVideo(const char *name, int skip, int scr_flags) {
 		flags |= kVideo_EnableAudio;
 
 	// if game audio is disabled, then don't play any sound on the video either
-	if (_GP(usetup).audio_backend == 0)
+	if (!_GP(usetup).audio_enabled)
 		flags &= ~kVideo_EnableAudio;
 
 	if (_G(loaded_game_file_version) < kGameVersion_360_16)
diff --git a/engines/ags/engine/main/config.cpp b/engines/ags/engine/main/config.cpp
index 633d276bc49..e2d6565a2e3 100644
--- a/engines/ags/engine/main/config.cpp
+++ b/engines/ags/engine/main/config.cpp
@@ -227,8 +227,6 @@ void config_defaults() {
 	// Defaults for the window style are max resizing window and "fullscreen desktop"
 	_GP(usetup).Screen.FsSetup = WindowSetup(kWnd_FullDesktop);
 	_GP(usetup).Screen.WinSetup = WindowSetup(kWnd_Windowed);
-	_GP(usetup).audio_backend = 1;
-	_GP(usetup).translation = "";
 }
 
 static void read_legacy_graphics_config(const ConfigTree &cfg) {
@@ -372,7 +370,8 @@ void override_config_ext(ConfigTree &cfg) {
 
 void apply_config(const ConfigTree &cfg) {
 	{
-		_GP(usetup).audio_backend = INIreadint(cfg, "sound", "enabled", _GP(usetup).audio_backend);
+		_GP(usetup).audio_enabled = INIreadint(cfg, "sound", "enabled", _GP(usetup).audio_enabled);
+		_GP(usetup).audio_driver = INIreadstring(cfg, "sound", "driver");
 
 		// Legacy graphics settings has to be translated into new options;
 		// they must be read first, to let newer options override them, if ones are present
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index a9760d20a05..3087a9a83a2 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -326,24 +326,22 @@ void engine_init_timer() {
 
 void engine_init_audio() {
 #if !AGS_PLATFORM_SCUMMVM
-	if (usetup.audio_backend != 0)
-    {
-        Debug::Printf("Initializing audio");
-        try {
-            audio_core_init(); // audio core system
-        } catch(std::runtime_error) {
-            Debug::Printf("Failed to initialize audio, disabling.");
-            usetup.audio_backend = 0;
-        }
-    }
+	if (usetup.audio_backend != 0) {
+		Debug::Printf("Initializing audio");
+		try {
+			audio_core_init(); // audio core system
+		} catch (std::runtime_error ex) {
+			Debug::Printf(kDbgMsg_Error, "Failed to initialize audio: %s", ex.what());
+			usetup.audio_backend = 0;
+		}
+	}
 #endif
 
-	_G(our_eip) = -181;
-
-	if (_GP(usetup).audio_backend == 0) {
+	if (!_GP(usetup).audio_enabled) {
 		// all audio is disabled
 		_GP(play).voice_avail = false;
 		_GP(play).separate_music_lib = false;
+		Debug::Printf(kDbgMsg_Info, "Audio is disabled");
 	}
 }
 
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index aa99c4c9976..ebaae24baa1 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -34,6 +34,7 @@
 #include "ags/engine/media/audio/sound.h"
 #include "ags/engine/debugging/debug_log.h"
 #include "ags/engine/debugging/debugger.h"
+#include "ags/engine/platform/base/sys_main.h"
 #include "ags/shared/ac/common.h"
 #include "ags/engine/ac/file.h"
 #include "ags/engine/ac/global_audio.h"
@@ -189,7 +190,7 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
 }
 
 bool is_audiotype_allowed_to_play(AudioFileType type) {
-	return _GP(usetup).audio_backend != 0;
+	return _GP(usetup).audio_enabled;
 }
 
 SOUNDCLIP *load_sound_clip(ScriptAudioClip *audioClip, bool repeat) {
@@ -592,6 +593,8 @@ void shutdown_sound() {
 #if !AGS_PLATFORM_SCUMMVM
 	audio_core_shutdown(); // audio core system
 #endif
+	sys_audio_shutdown(); // backend
+	_GP(usetup).audio_enabled = false;
 }
 
 // the sound will only be played if there is a free channel or
diff --git a/engines/ags/engine/platform/base/sys_main.cpp b/engines/ags/engine/platform/base/sys_main.cpp
index 79dfb565513..19f8114e490 100644
--- a/engines/ags/engine/platform/base/sys_main.cpp
+++ b/engines/ags/engine/platform/base/sys_main.cpp
@@ -82,6 +82,37 @@ void sys_get_desktop_modes(std::vector<AGS::Engine::DisplayMode> &dms) {
 #endif
 }
 
+// ----------------------------------------------------------------------------
+// AUDIO UTILS
+// ----------------------------------------------------------------------------
+
+bool sys_audio_init(const AGS::Shared::String &driver_name) {
+#ifdef AGS_PLATFORM_SCUMMVM
+	return true;
+#else
+	bool res = false;
+	if (!driver_name.IsEmpty()) {
+		res = SDL_AudioInit(driver_name.GetCStr()) == 0;
+		if (!res)
+			Debug::Printf(kDbgMsg_Error, "Failed to initialize audio driver %s; error: %s",
+				driver_name.GetCStr(), SDL_GetError());
+	}
+	if (!res) {
+		res = SDL_InitSubSystem(SDL_INIT_AUDIO) == 0;
+		if (!res)
+			Debug::Printf(kDbgMsg_Error, "Failed to initialize audio backend: %s", SDL_GetError());
+	}
+	if (res)
+		Debug::Printf(kDbgMsg_Info, "Audio driver: %s", SDL_GetCurrentAudioDriver());
+	return res;
+#endif
+}
+
+void sys_audio_shutdown() {
+#ifndef AGS_PLATFORM_SCUMMVM
+	SDL_QuitSubSystem(SDL_INIT_AUDIO);
+#endif
+}
 
 // ----------------------------------------------------------------------------
 // WINDOW UTILS
diff --git a/engines/ags/engine/platform/base/sys_main.h b/engines/ags/engine/platform/base/sys_main.h
index c248010818e..aa08868acfe 100644
--- a/engines/ags/engine/platform/base/sys_main.h
+++ b/engines/ags/engine/platform/base/sys_main.h
@@ -28,6 +28,7 @@
 //=============================================================================
 
 #include "ags/shared/core/platform.h"
+#include "ags/shared/util/string.h"
 #include "ags/lib/std/vector.h"
 #include "ags/engine/gfx/gfx_defines.h"
 
@@ -51,6 +52,13 @@ int sys_get_desktop_resolution(int &width, int &height);
 // Queries supported desktop modes.
 void sys_get_desktop_modes(std::vector<AGS::Engine::DisplayMode> &dms);
 
+// Audio utilities.
+//
+// Tries to init the audio backend; optionally requests particular driver
+bool sys_audio_init(const AGS::Shared::String &driver_name = "");
+// Shutdown audio backend
+void sys_audio_shutdown();
+
 // Window utilities.
 //
 struct SDL_Window;
diff --git a/engines/ags/plugins/ags_plugin.cpp b/engines/ags/plugins/ags_plugin.cpp
index c2f52d20124..f1d8bced28e 100644
--- a/engines/ags/plugins/ags_plugin.cpp
+++ b/engines/ags/plugins/ags_plugin.cpp
@@ -599,7 +599,6 @@ int IAGSEngine::IsSpriteAlphaBlended(int32 slot) {
 // disable AGS's sound engine
 void IAGSEngine::DisableSound() {
 	shutdown_sound();
-	_GP(usetup).audio_backend = 0;
 }
 int IAGSEngine::CanRunScriptFunctionNow() {
 	if (_G(inside_script))


Commit: 7fb5f6d21a605d42024cdda4a130a88f68cc46f0
    https://github.com/scummvm/scummvm/commit/7fb5f6d21a605d42024cdda4a130a88f68cc46f0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T21:31:00-07:00

Commit Message:
AGS: Support option for software renderer's output driver

>From upstream d45f77548675f06056143d6989929870338b34a1

Changed paths:
    engines/ags/engine/ac/game_setup.h
    engines/ags/engine/main/config.cpp
    engines/ags/engine/main/engine.cpp
    engines/ags/engine/platform/base/sys_main.cpp
    engines/ags/engine/platform/base/sys_main.h


diff --git a/engines/ags/engine/ac/game_setup.h b/engines/ags/engine/ac/game_setup.h
index e91d0244e9e..ccef879bbad 100644
--- a/engines/ags/engine/ac/game_setup.h
+++ b/engines/ags/engine/ac/game_setup.h
@@ -96,6 +96,7 @@ struct GameSetup {
 	ScreenRotation rotation;
 
 	DisplayModeSetup Screen;
+	String software_render_driver;
 
 	GameSetup();
 };
diff --git a/engines/ags/engine/main/config.cpp b/engines/ags/engine/main/config.cpp
index e2d6565a2e3..a95d9b9d2b6 100644
--- a/engines/ags/engine/main/config.cpp
+++ b/engines/ags/engine/main/config.cpp
@@ -400,6 +400,8 @@ void apply_config(const ConfigTree &cfg) {
 		_GP(usetup).Screen.Params.VSync = INIreadint(cfg, "graphics", "vsync") > 0;
 		_GP(usetup).RenderAtScreenRes = INIreadint(cfg, "graphics", "render_at_screenres") > 0;
 		_GP(usetup).Supersampling = INIreadint(cfg, "graphics", "supersampling", 1);
+		_GP(usetup).software_render_driver = INIreadstring(cfg, "graphics", "software_driver");
+
 #ifdef TODO
 		_GP(usetup).rotation = (ScreenRotation)INIreadint(cfg, "graphics", "rotation", _GP(usetup).rotation);
 		String rotation_str = INIreadstring(cfg, "graphics", "rotation", "unlocked");
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 3087a9a83a2..0941335660e 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -1216,6 +1216,8 @@ int initialize_engine(const ConfigTree &startup_opts) {
 bool engine_try_set_gfxmode_any(const DisplayModeSetup &setup) {
 	engine_shutdown_gfxmode();
 
+	sys_renderer_set_output(_GP(usetup).software_render_driver);
+
 	const Size init_desktop = get_desktop_size();
 	bool res = graphics_mode_init_any(GraphicResolution(_GP(game).GetGameRes(), _GP(game).color_depth * 8),
 		setup, ColorDepthOption(_GP(game).GetColorDepth()));
diff --git a/engines/ags/engine/platform/base/sys_main.cpp b/engines/ags/engine/platform/base/sys_main.cpp
index 19f8114e490..71d22db0b2d 100644
--- a/engines/ags/engine/platform/base/sys_main.cpp
+++ b/engines/ags/engine/platform/base/sys_main.cpp
@@ -82,6 +82,12 @@ void sys_get_desktop_modes(std::vector<AGS::Engine::DisplayMode> &dms) {
 #endif
 }
 
+void sys_renderer_set_output(const AGS::Shared::String &name) {
+#ifndef AGS_PLATFORM_SCUMMVM
+	SDL_SetHint(SDL_HINT_RENDER_DRIVER, name.GetCStr());
+#endif
+}
+
 // ----------------------------------------------------------------------------
 // AUDIO UTILS
 // ----------------------------------------------------------------------------
diff --git a/engines/ags/engine/platform/base/sys_main.h b/engines/ags/engine/platform/base/sys_main.h
index aa08868acfe..5436adb5a51 100644
--- a/engines/ags/engine/platform/base/sys_main.h
+++ b/engines/ags/engine/platform/base/sys_main.h
@@ -51,6 +51,8 @@ void sys_set_background_mode(bool on);
 int sys_get_desktop_resolution(int &width, int &height);
 // Queries supported desktop modes.
 void sys_get_desktop_modes(std::vector<AGS::Engine::DisplayMode> &dms);
+// Sets output driver for the backend's renderer
+void sys_renderer_set_output(const AGS::Shared::String &name);
 
 // Audio utilities.
 //


Commit: cc9bda925140d1a45f257ba2a3cd92aa5a128a05
    https://github.com/scummvm/scummvm/commit/cc9bda925140d1a45f257ba2a3cd92aa5a128a05
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T21:35:00-07:00

Commit Message:
AGS: Added NumLock and CapsLock key mods

>From upstream c845ac3450159fd54fec91e19dc857860fc8ac38

Changed paths:
    engines/ags/events.cpp
    engines/ags/shared/ac/keycode.h


diff --git a/engines/ags/events.cpp b/engines/ags/events.cpp
index 42054e9ebe3..c76b4d5b582 100644
--- a/engines/ags/events.cpp
+++ b/engines/ags/events.cpp
@@ -323,6 +323,8 @@ AGS3::eAGSKeyCode EventsManager::scummvm_key_to_ags_key(const Common::Event &eve
 	if (mod & Common::KBD_SHIFT) ags_mod |= AGS3::eAGSModLShift;
 	if (mod & Common::KBD_CTRL)  ags_mod |= AGS3::eAGSModLCtrl;
 	if (mod & Common::KBD_ALT)   ags_mod |= AGS3::eAGSModLAlt;
+	if (mod & Common::KBD_NUM)   ags_mod |= AGS3::eAGSModNum;
+	if (mod & Common::KBD_CAPS)  ags_mod |= AGS3::eAGSModCaps;
 
 	// Old mode: Ctrl and Alt combinations realign the letter code to certain offset
 	if (old_keyhandle && (sym >= Common::KEYCODE_a && sym <= Common::KEYCODE_z)) {
diff --git a/engines/ags/shared/ac/keycode.h b/engines/ags/shared/ac/keycode.h
index 63cfb97a06e..f0198ac2a1e 100644
--- a/engines/ags/shared/ac/keycode.h
+++ b/engines/ags/shared/ac/keycode.h
@@ -258,7 +258,9 @@ enum eAGSKeyMod {
 	eAGSModLCtrl = 0x0004,
 	eAGSModRCtrl = 0x0008,
 	eAGSModLAlt = 0x0010,
-	eAGSModRAlt = 0x0020
+	eAGSModRAlt = 0x0020,
+	eAGSModNum = 0x0040,
+	eAGSModCaps = 0x0080
 };
 
 // Combined key code and a textual representation in UTF-8


Commit: 4057fa05b99f02d68aec675bc91d8b7af5ca51d4
    https://github.com/scummvm/scummvm/commit/4057fa05b99f02d68aec675bc91d8b7af5ca51d4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T21:36:00-07:00

Commit Message:
AGS: Updated build version (3.6.0.20)

>From upstream ca98a8705ce101212cc8385ffcee7934340a36b0

Changed paths:
    engines/ags/shared/core/def_version.h


diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index a47916eb3d3..765bd984d7b 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
 #ifndef AGS_SHARED_CORE_DEFVERSION_H
 #define AGS_SHARED_CORE_DEFVERSION_H
 
-#define ACI_VERSION_STR      "3.6.0.19"
+#define ACI_VERSION_STR      "3.6.0.20"
 #if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF  3.6.0.19
+#define ACI_VERSION_MSRC_DEF  3.6.020
 #endif
 
 #define SPECIAL_VERSION ""


Commit: 06880291b28e5a9b0648bab069101fd8e454a1c9
    https://github.com/scummvm/scummvm/commit/06880291b28e5a9b0648bab069101fd8e454a1c9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T21:49:13-07:00

Commit Message:
AGS: Refactor restrict_until and ShouldStayInWaitMode()

>From upstream 434dd136b4e6628545677a17fc17c847efff1c71

Changed paths:
    engines/ags/engine/ac/runtime_defines.h
    engines/ags/engine/main/game_run.cpp
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/runtime_defines.h b/engines/ags/engine/ac/runtime_defines.h
index 1e27e341e36..cc127f5fed6 100644
--- a/engines/ags/engine/ac/runtime_defines.h
+++ b/engines/ags/engine/ac/runtime_defines.h
@@ -118,7 +118,6 @@ const int LegacyRoomVolumeFactor = 30;
 #define FOR_SCRIPT    2
 #define FOR_EXITLOOP  3
 #define CHMLSOFFS (MAX_ROOM_OBJECTS+1)    // reserve this many movelists for objects & stuff
-#define abort_all_conditions _G(restrict_until)
 #define MAX_SCRIPT_AT_ONCE 10
 #define EVENT_NONE       0
 #define EVENT_INPROGRESS 1
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index 623da50799e..e98e7f79fcb 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -72,7 +72,7 @@ namespace AGS3 {
 
 using namespace AGS::Shared;
 
-static int ShouldStayInWaitMode();
+static bool ShouldStayInWaitMode();
 
 #define UNTIL_ANIMEND   1
 #define UNTIL_MOVEEND   2
@@ -155,7 +155,7 @@ static int game_loop_check_ground_level_interactions() {
 		// if in a Wait loop which is no longer valid (probably
 		// because the Region interaction did a NewRoom), abort
 		// the rest of the loop
-		if ((_G(restrict_until)) && (!ShouldStayInWaitMode())) {
+		if ((_G(restrict_until).type > 0) && (!ShouldStayInWaitMode())) {
 			// cancel the Rep Exec and Stands on Hotspot events that
 			// we just added -- otherwise the event queue gets huge
 			_GP(events).resize(_G(numEventsAtStartOfFunction));
@@ -382,7 +382,7 @@ bool run_service_key_controls(KeyInput &out_key) {
 	}
 
 	if (((agskey == eAGSKeyCodeCtrlV) && (cur_key_mods & Common::KBD_ALT) != 0)
-	        && (_GP(play).wait_counter < 1) && (_GP(play).text_overlay_on == 0) && (_G(restrict_until) == 0)) {
+			&& (_GP(play).wait_counter < 1) && (_GP(play).text_overlay_on == 0) && (_G(restrict_until).type == 0)) {
 		// make sure we can't interrupt a Wait()
 		// and desync the music to cutscene
 		_GP(play).debug_mode++;
@@ -832,56 +832,67 @@ static void UpdateMouseOverLocation() {
 }
 
 // Checks if user interface should remain disabled for now
-static int ShouldStayInWaitMode() {
-	if (_G(restrict_until) == 0)
+// Checks if user interface should remain disabled for now
+static bool ShouldStayInWaitMode() {
+	if (_G(restrict_until).type == 0)
 		quit("end_wait_loop called but game not in loop_until state");
-	int retval = _G(restrict_until);
-
-	if (_G(restrict_until) == UNTIL_MOVEEND) {
-		const int16 *wkptr = (const int16 *)_G(user_disabled_data);
-		if (wkptr[0] < 1) retval = 0;
-	} else if (_G(restrict_until) == UNTIL_CHARIS0) {
-		const char *chptr = (const char *)_G(user_disabled_data);
-		if (chptr[0] == 0) retval = 0;
-	} else if (_G(restrict_until) == UNTIL_NEGATIVE) {
-		const int16 *wkptr = (const int16 *)_G(user_disabled_data);
-		if (wkptr[0] < 0) retval = 0;
-	} else if (_G(restrict_until) == UNTIL_INTISNEG) {
-		const int *wkptr = (const int *)_G(user_disabled_data);
-		if (wkptr[0] < 0) retval = 0;
-	} else if (_G(restrict_until) == UNTIL_NOOVERLAY) {
-		if (_GP(play).text_overlay_on == 0) retval = 0;
-	} else if (_G(restrict_until) == UNTIL_INTIS0) {
-		const int *wkptr = (const int *)_G(user_disabled_data);
-		if (wkptr[0] == 0) retval = 0;
-	} else if (_G(restrict_until) == UNTIL_SHORTIS0) {
-		const int16 *wkptr = (const int16 *)_G(user_disabled_data);
-		if (wkptr[0] == 0) retval = 0;
-	} else quit("loop_until: unknown until event");
-
-	return retval;
+
+	switch (_G(restrict_until).type) {
+	case UNTIL_MOVEEND: {
+		short *wkptr = (short *)_G(restrict_until).data_ptr;
+		return !(wkptr[0] < 1);
+	}
+	case UNTIL_CHARIS0: {
+		char *chptr = (char *)_G(restrict_until).data_ptr;
+		return !(chptr[0] == 0);
+	}
+	case UNTIL_NEGATIVE: {
+		short *wkptr = (short *)_G(restrict_until).data_ptr;
+		return !(wkptr[0] < 0);
+	}
+	case UNTIL_INTISNEG: {
+		int *wkptr = (int *)_G(restrict_until).data_ptr;
+		return !(wkptr[0] < 0);
+	}
+	case UNTIL_NOOVERLAY: {
+		return !(_GP(play).text_overlay_on == 0);
+	}
+	case UNTIL_INTIS0: {
+		int *wkptr = (int *)_G(restrict_until).data_ptr;
+		return !(wkptr[0] == 0);
+	}
+	case UNTIL_SHORTIS0: {
+		short *wkptr = (short *)_G(restrict_until).data_ptr;
+		return !(wkptr[0] == 0);
+	}
+	default:
+		quit("loop_until: unknown until event");
+	}
+
+	return true; // should stay in wait
 }
 
 static int UpdateWaitMode() {
-	if (_G(restrict_until) == 0) {
+	if (_G(restrict_until).type == 0) {
 		return RETURN_CONTINUE;
 	}
 
-	_G(restrict_until) = ShouldStayInWaitMode();
+	if (!ShouldStayInWaitMode())
+		_G(restrict_until).type = 0;
 	_G(our_eip) = 77;
 
-	if (_G(restrict_until) != 0) {
+	if (_G(restrict_until).type > 0) {
 		return RETURN_CONTINUE;
 	}
 
-	auto was_disabled_for = _G(user_disabled_for);
+	auto was_disabled_for = _G(restrict_until).disabled_for;
 
 	set_default_cursor();
 	if (GUI::Options.DisabledStyle != kGuiDis_Unchanged) { // If GUI looks change when disabled, then update them all
 		GUI::MarkAllGUIForUpdate();
 	}
 	_GP(play).disabled_user_interface--;
-	_G(user_disabled_for) = 0;
+	_G(restrict_until).disabled_for = 0;
 
 	switch (was_disabled_for) {
 	// case FOR_ANIMATION:
@@ -893,7 +904,7 @@ static int UpdateWaitMode() {
 		quit("err: for_script obsolete (v2.1 and earlier only)");
 		break;
 	default:
-		quit("Unknown _G(user_disabled_for) in end _G(restrict_until)");
+		quit("Unknown user_disabled_for in end _G(restrict_until)");
 	}
 
 	// we shouldn't get here.
@@ -932,9 +943,9 @@ static void SetupLoopParameters(int untilwhat, const void *udata) {
 	        (_G(cur_mode) != CURS_WAIT))
 		set_mouse_cursor(CURS_WAIT);
 
-	_G(restrict_until) = untilwhat;
-	_G(user_disabled_data) = udata;
-	_G(user_disabled_for) = FOR_EXITLOOP;
+	_G(restrict_until).type = untilwhat;
+	_G(restrict_until).data_ptr = udata;
+	_G(restrict_until).disabled_for = FOR_EXITLOOP;
 }
 
 // This function is called from lot of various functions
@@ -947,8 +958,6 @@ static void GameLoopUntilEvent(int untilwhat, const void *daaa) {
 	// remember the state of these vars in case a higher level
 	// call needs them
 	auto cached_restrict_until = _G(restrict_until);
-	auto cached_user_disabled_data = _G(user_disabled_data);
-	auto cached_user_disabled_for = _G(user_disabled_for);
 
 	SetupLoopParameters(untilwhat, daaa);
 	while (GameTick() == 0 && !_G(abort_engine)) {}
@@ -956,8 +965,6 @@ static void GameLoopUntilEvent(int untilwhat, const void *daaa) {
 	_G(our_eip) = 78;
 
 	_G(restrict_until) = cached_restrict_until;
-	_G(user_disabled_data) = cached_user_disabled_data;
-	_G(user_disabled_for) = cached_user_disabled_for;
 }
 
 void GameLoopUntilValueIsZero(const int8 *value) {
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 65ef6b648aa..b6fa0207f88 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -812,11 +812,14 @@ public:
 	 * @{
 	 */
 
-	// Following 3 parameters instruct the engine to run game loops until
-	// certain condition is not fullfilled.
-	int _restrict_until = 0;
-	int _user_disabled_for = 0;
-	const void *_user_disabled_data = nullptr;
+	 // Following struct instructs the engine to run game loops until
+	 // certain condition is not fullfilled.
+	struct RestrictUntil {
+		int type = 0; // type of condition, UNTIL_* constant
+		int disabled_for = 0; // FOR_* constant
+		// pointer to the test variable
+		const void *data_ptr = nullptr;
+	} _restrict_until;
 
 	unsigned int _loopcounter = 0;
 	unsigned int _lastcounter = 0;


Commit: 415235b74e304520e4466ed969b24163a9ce1d66
    https://github.com/scummvm/scummvm/commit/415235b74e304520e4466ed969b24163a9ce1d66
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T22:07:41-07:00

Commit Message:
AGS: expanded Button.Animate to feature all common params

>From upstream 0853d4146276f86c7418dc02360deb49a0ef324a

Changed paths:
    engines/ags/engine/ac/button.cpp
    engines/ags/engine/ac/button.h
    engines/ags/engine/gui/animating_gui_button.h
    engines/ags/engine/main/game_run.cpp
    engines/ags/engine/main/game_run.h
    engines/ags/engine/script/script_api.h
    engines/ags/globals.h


diff --git a/engines/ags/engine/ac/button.cpp b/engines/ags/engine/ac/button.cpp
index 937138758df..f8e09f35dbb 100644
--- a/engines/ags/engine/ac/button.cpp
+++ b/engines/ags/engine/ac/button.cpp
@@ -34,6 +34,7 @@
 #include "ags/engine/script/script_api.h"
 #include "ags/engine/script/script_runtime.h"
 #include "ags/engine/ac/dynobj/script_string.h"
+#include "ags/engine/main/game_run.h"
 #include "ags/globals.h"
 
 namespace AGS3 {
@@ -42,25 +43,50 @@ using namespace AGS::Shared;
 
 // *** BUTTON FUNCTIONS
 
-void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat) {
+void Button_AnimateEx(GUIButton *butt, int view, int loop, int speed, int repeat, int blocking, int direction, int sframe) {
 	int guin = butt->ParentId;
 	int objn = butt->Id;
 
+	if (direction == FORWARDS)
+		direction = 0;
+	else if (direction == BACKWARDS)
+		direction = 1;
+	if (blocking == BLOCKING)
+		blocking = 1;
+	else if (blocking == IN_BACKGROUND)
+		blocking = 0;
+
 	if ((view < 1) || (view > _GP(game).numviews))
 		quit("!AnimateButton: invalid view specified");
 	view--;
-
 	if ((loop < 0) || (loop >= _GP(views)[view].numLoops))
 		quit("!AnimateButton: invalid loop specified for view");
+	if (sframe < 0 || sframe >= _GP(views)[view].loops[loop].numFrames)
+		quit("!AnimateButton: invalid starting frame number specified");
+	if ((repeat < 0) || (repeat > 1))
+		quit("!AnimateButton: invalid repeat value");
+	if ((blocking < 0) || (blocking > 1))
+		quit("!AnimateButton: invalid blocking value");
+	if ((direction < 0) || (direction > 1))
+		quit("!AnimateButton: invalid direction");
 
 	// if it's already animating, stop it
 	FindAndRemoveButtonAnimation(guin, objn);
 
+	// Prepare button
 	int buttonId = _GP(guis)[guin].GetControlID(objn);
-
 	_GP(guibuts)[buttonId].PushedImage = 0;
 	_GP(guibuts)[buttonId].MouseOverImage = 0;
 
+	// reverse animation starts at the *previous frame*
+	if (direction) {
+		if (--sframe < 0)
+			sframe = _GP(views)[view].loops[loop].numFrames - (-sframe);
+		sframe++; // set on next frame, first call to Update will decrement
+	} else {
+		sframe--; // set on prev frame, first call to Update will increment
+	}
+
 	AnimatingGUIButton abtn;
 	abtn.ongui = guin;
 	abtn.onguibut = objn;
@@ -69,7 +95,9 @@ void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat)
 	abtn.loop = loop;
 	abtn.speed = speed;
 	abtn.repeat = repeat;
-	abtn.frame = -1;
+	abtn.blocking = blocking;
+	abtn.direction = direction;
+	abtn.frame = sframe;
 	abtn.wait = 0;
 	_GP(animbuts).push_back(abtn);
 	// launch into the first frame
@@ -78,6 +106,14 @@ void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat)
 			butt->GetScriptName().GetCStr(), view, loop);
 		StopButtonAnimation(_GP(animbuts).size() - 1);
 	}
+
+	// Blocking animate
+	if (blocking)
+		GameLoopUntilButAnimEnd(guin, objn);
+}
+
+void Button_Animate(GUIButton *butt, int view, int loop, int speed, int repeat) {
+	Button_AnimateEx(butt, view, loop, speed, repeat, IN_BACKGROUND, FORWARDS, 0);
 }
 
 const char *Button_GetText_New(GUIButton *butt) {
@@ -216,21 +252,37 @@ int UpdateAnimatingButton(int bu) {
 	}
 	ViewStruct *tview = &_GP(views)[abtn.view];
 
-	abtn.frame++;
-
-	if (abtn.frame >= tview->loops[abtn.loop].numFrames) {
-		if (tview->loops[abtn.loop].RunNextLoop()) {
-			// go to next loop
-			abtn.loop++;
-			abtn.frame = 0;
-		} else if (abtn.repeat) {
-			abtn.frame = 0;
-			// multi-loop anim, go back
-			while ((abtn.loop > 0) &&
-				(tview->loops[abtn.loop - 1].RunNextLoop()))
+	if (abtn.direction) { // backwards
+		abtn.frame--;
+		if (abtn.frame < 0) {
+			if ((abtn.loop > 0) && tview->loops[abtn.loop - 1].RunNextLoop()) {
+				// go to next loop
 				abtn.loop--;
-		} else
-			return 1;
+				abtn.frame = tview->loops[abtn.loop].numFrames - 1;
+			} else if (abtn.repeat) {
+				// multi-loop anim, go back
+				while (tview->loops[abtn.loop].RunNextLoop())
+					abtn.loop++;
+				abtn.frame = tview->loops[abtn.loop].numFrames - 1;
+			} else
+				return 1;
+		}
+	} else { // forwards
+		abtn.frame++;
+		if (abtn.frame >= tview->loops[abtn.loop].numFrames) {
+			if (tview->loops[abtn.loop].RunNextLoop()) {
+				// go to next loop
+				abtn.loop++;
+				abtn.frame = 0;
+			} else if (abtn.repeat) {
+				abtn.frame = 0;
+				// multi-loop anim, go back
+				while ((abtn.loop > 0) &&
+					(tview->loops[abtn.loop - 1].RunNextLoop()))
+					abtn.loop--;
+			} else
+				return 1;
+		}
 	}
 
 	CheckViewFrame(abtn.view, abtn.loop, abtn.frame);
@@ -256,7 +308,7 @@ void RemoveAllButtonAnimations() {
 
 // Returns the index of the AnimatingGUIButton object corresponding to the
 // given button ID; returns -1 if no such animation exists
-int FindAnimatedButton(int guin, int objn) {
+int FindButtonAnimation(int guin, int objn) {
 	for (size_t i = 0; i < _GP(animbuts).size(); ++i) {
 		if (_GP(animbuts)[i].ongui == guin && _GP(animbuts)[i].onguibut == objn)
 			return i;
@@ -265,7 +317,7 @@ int FindAnimatedButton(int guin, int objn) {
 }
 
 void FindAndRemoveButtonAnimation(int guin, int objn) {
-	int idx = FindAnimatedButton(guin, objn);
+	int idx = FindButtonAnimation(guin, objn);
 	if (idx >= 0)
 		StopButtonAnimation(idx);
 }
@@ -277,24 +329,24 @@ void Button_Click(GUIButton *butt, int mbut) {
 }
 
 bool Button_IsAnimating(GUIButton *butt) {
-	return FindAnimatedButton(butt->ParentId, butt->Id) >= 0;
+	return FindButtonAnimation(butt->ParentId, butt->Id) >= 0;
 }
 
 // NOTE: in correspondance to similar functions for Character & Object,
 // GetView returns (view index + 1), while GetLoop and GetFrame return
 // zero-based index and 0 in case of no animation.
 int Button_GetAnimView(GUIButton *butt) {
-	int idx = FindAnimatedButton(butt->ParentId, butt->Id);
+	int idx = FindButtonAnimation(butt->ParentId, butt->Id);
 	return idx >= 0 ? _GP(animbuts)[idx].view + 1 : 0;
 }
 
 int Button_GetAnimLoop(GUIButton *butt) {
-	int idx = FindAnimatedButton(butt->ParentId, butt->Id);
+	int idx = FindButtonAnimation(butt->ParentId, butt->Id);
 	return idx >= 0 ? _GP(animbuts)[idx].loop : 0;
 }
 
 int Button_GetAnimFrame(GUIButton *butt) {
-	int idx = FindAnimatedButton(butt->ParentId, butt->Id);
+	int idx = FindButtonAnimation(butt->ParentId, butt->Id);
 	return idx >= 0 ? _GP(animbuts)[idx].frame : 0;
 }
 
@@ -320,6 +372,10 @@ RuntimeScriptValue Sc_Button_Animate(void *self, const RuntimeScriptValue *param
 	API_OBJCALL_VOID_PINT4(GUIButton, Button_Animate);
 }
 
+RuntimeScriptValue Sc_Button_AnimateEx(void *self, const RuntimeScriptValue *params, int32_t param_count) {
+	API_OBJCALL_VOID_PINT7(GUIButton, Button_AnimateEx);
+}
+
 // const char* | GUIButton *butt
 RuntimeScriptValue Sc_Button_GetText_New(void *self, const RuntimeScriptValue *params, int32_t param_count) {
 	API_CONST_OBJCALL_OBJ(GUIButton, const char, _GP(myScriptStringImpl), Button_GetText_New);
@@ -430,6 +486,7 @@ RuntimeScriptValue Sc_Button_GetView(void *self, const RuntimeScriptValue *param
 
 void RegisterButtonAPI() {
 	ccAddExternalObjectFunction("Button::Animate^4", Sc_Button_Animate);
+	ccAddExternalObjectFunction("Button::Animate^7", Sc_Button_AnimateEx);
 	ccAddExternalObjectFunction("Button::Click^1", Sc_Button_Click);
 	ccAddExternalObjectFunction("Button::GetText^1", Sc_Button_GetText);
 	ccAddExternalObjectFunction("Button::SetText^1", Sc_Button_SetText);
diff --git a/engines/ags/engine/ac/button.h b/engines/ags/engine/ac/button.h
index b0ecbcc652b..733d0769f34 100644
--- a/engines/ags/engine/ac/button.h
+++ b/engines/ags/engine/ac/button.h
@@ -53,6 +53,7 @@ size_t      GetAnimatingButtonCount();
 AnimatingGUIButton *GetAnimatingButtonByIndex(int idxn);
 void        AddButtonAnimation(const AnimatingGUIButton &abtn);
 void		StopButtonAnimation(int idxn);
+int         FindButtonAnimation(int guin, int objn);
 void		FindAndRemoveButtonAnimation(int guin, int objn);
 void        RemoveAllButtonAnimations();
 
diff --git a/engines/ags/engine/gui/animating_gui_button.h b/engines/ags/engine/gui/animating_gui_button.h
index 48242d27d5f..e63d0c9a7f2 100644
--- a/engines/ags/engine/gui/animating_gui_button.h
+++ b/engines/ags/engine/gui/animating_gui_button.h
@@ -39,7 +39,8 @@ struct AnimatingGUIButton {
 	short buttonid = 0, ongui = 0, onguibut = 0;
 	// current animation status
 	short view = 0, loop = 0, frame = 0;
-	short speed = 0, repeat = 0, wait = 0;
+	short speed = 0, repeat = 0, blocking = 0,
+		direction = 0, wait = 0;
 
 	void ReadFromFile(Shared::Stream *in);
 	void WriteToFile(Shared::Stream *out);
diff --git a/engines/ags/engine/main/game_run.cpp b/engines/ags/engine/main/game_run.cpp
index e98e7f79fcb..55168765ea5 100644
--- a/engines/ags/engine/main/game_run.cpp
+++ b/engines/ags/engine/main/game_run.cpp
@@ -82,6 +82,7 @@ static bool ShouldStayInWaitMode();
 #define UNTIL_INTIS0    6
 #define UNTIL_SHORTIS0  7
 #define UNTIL_INTISNEG  8
+#define UNTIL_ANIMBTNEND 9
 
 static void ProperExit() {
 	_G(want_exit) = 0;
@@ -865,6 +866,10 @@ static bool ShouldStayInWaitMode() {
 		short *wkptr = (short *)_G(restrict_until).data_ptr;
 		return !(wkptr[0] == 0);
 	}
+	case UNTIL_ANIMBTNEND: {
+		// still animating?
+		return FindButtonAnimation(_G(restrict_until).data1, _G(restrict_until).data2) >= 0;
+	}
 	default:
 		quit("loop_until: unknown until event");
 	}
@@ -932,7 +937,7 @@ static int GameTick() {
 	return res;
 }
 
-static void SetupLoopParameters(int untilwhat, const void *udata) {
+static void SetupLoopParameters(int untilwhat, const void *data_ptr = nullptr, int data1 = 0, int data2 = 0) {
 	_GP(play).disabled_user_interface++;
 	if (GUI::Options.DisabledStyle != kGuiDis_Unchanged) { // If GUI looks change when disabled, then update them all
 		GUI::MarkAllGUIForUpdate();
@@ -940,17 +945,19 @@ static void SetupLoopParameters(int untilwhat, const void *udata) {
 	// Only change the mouse cursor if it hasn't been specifically changed first
 	// (or if it's speech, always change it)
 	if (((_G(cur_cursor) == _G(cur_mode)) || (untilwhat == UNTIL_NOOVERLAY)) &&
-	        (_G(cur_mode) != CURS_WAIT))
+		(_G(cur_mode) != CURS_WAIT))
 		set_mouse_cursor(CURS_WAIT);
 
 	_G(restrict_until).type = untilwhat;
-	_G(restrict_until).data_ptr = udata;
+	_G(restrict_until).data_ptr = data_ptr;
+	_G(restrict_until).data1 = data1;
+	_G(restrict_until).data2 = data2;
 	_G(restrict_until).disabled_for = FOR_EXITLOOP;
 }
 
 // This function is called from lot of various functions
 // in the game core, character, room object etc
-static void GameLoopUntilEvent(int untilwhat, const void *daaa) {
+static void GameLoopUntilEvent(int untilwhat, const void *data_ptr = nullptr, int data1 = 0, int data2 = 0) {
 	// blocking cutscene - end skipping
 	EndSkippingUntilCharStops();
 
@@ -959,8 +966,8 @@ static void GameLoopUntilEvent(int untilwhat, const void *daaa) {
 	// call needs them
 	auto cached_restrict_until = _G(restrict_until);
 
-	SetupLoopParameters(untilwhat, daaa);
-	while (GameTick() == 0 && !_G(abort_engine)) {}
+	SetupLoopParameters(untilwhat, data_ptr, data1, data2);
+	while (GameTick() == 0);
 
 	_G(our_eip) = 78;
 
@@ -996,10 +1003,12 @@ void GameLoopUntilNotMoving(const short *move) {
 }
 
 void GameLoopUntilNoOverlay() {
-	GameLoopUntilEvent(UNTIL_NOOVERLAY, nullptr);
+	GameLoopUntilEvent(UNTIL_NOOVERLAY);
 }
 
-
+void GameLoopUntilButAnimEnd(int guin, int objn) {
+	GameLoopUntilEvent(UNTIL_ANIMBTNEND, nullptr, guin, objn);
+}
 
 void RunGameUntilAborted() {
 	// skip ticks to account for time spent starting _GP(game).
diff --git a/engines/ags/engine/main/game_run.h b/engines/ags/engine/main/game_run.h
index 340d70d9635..0928801a596 100644
--- a/engines/ags/engine/main/game_run.h
+++ b/engines/ags/engine/main/game_run.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef AGS_ENGINE_MAIN__GAMERUN_H
-#define AGS_ENGINE_MAIN__GAMERUN_H
+#ifndef AGS_ENGINE_MAIN_GAME_RUN_H
+#define AGS_ENGINE_MAIN_GAME_RUN_H
 
 namespace AGS3 {
 
@@ -40,6 +40,7 @@ void GameLoopUntilValueIsNegative(const short *value);
 void GameLoopUntilValueIsNegative(const int *value);
 void GameLoopUntilNotMoving(const short *move);
 void GameLoopUntilNoOverlay();
+void GameLoopUntilButAnimEnd(int guin, int objn);
 
 // Run the actual game until it ends, or aborted by player/error; loops GameTick() internally
 void RunGameUntilAborted();
diff --git a/engines/ags/engine/script/script_api.h b/engines/ags/engine/script/script_api.h
index 8f4887e7f95..0c49950028f 100644
--- a/engines/ags/engine/script/script_api.h
+++ b/engines/ags/engine/script/script_api.h
@@ -427,6 +427,11 @@ inline const char *ScriptVSprintf(char *buffer, size_t buf_length, const char *f
 	METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue); \
 	return RuntimeScriptValue((int32_t)0)
 
+#define API_OBJCALL_VOID_PINT7(CLASS, METHOD) \
+    ASSERT_OBJ_PARAM_COUNT(METHOD, 7); \
+    METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue, params[6].IValue); \
+    return RuntimeScriptValue((int32_t)0)
+
 #define API_OBJCALL_VOID_PFLOAT(CLASS, METHOD) \
 	ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
 	METHOD((CLASS*)self, params[0].FValue); \
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index b6fa0207f88..53319d6df42 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -819,6 +819,9 @@ public:
 		int disabled_for = 0; // FOR_* constant
 		// pointer to the test variable
 		const void *data_ptr = nullptr;
+		// other values used for a test, depend on type
+		int data1 = 0;
+		int data2 = 0;
 	} _restrict_until;
 
 	unsigned int _loopcounter = 0;


Commit: bf5e0b2d083ded5bfb9b8bad5af26c15373de707
    https://github.com/scummvm/scummvm/commit/bf5e0b2d083ded5bfb9b8bad5af26c15373de707
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-04-21T22:18:33-07:00

Commit Message:
AGS: Updated AnimatingButton save format

>From upstream 7920a8719db3bc39b1187a076184d9a6d66b1310

Changed paths:
    engines/ags/engine/game/savegame_components.cpp
    engines/ags/engine/game/savegame_v321.cpp
    engines/ags/engine/gui/animating_gui_button.cpp
    engines/ags/engine/gui/animating_gui_button.h
    engines/ags/shared/gui/gui_defines.h


diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index 8111b8fbede..fb8f2f66648 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -635,9 +635,10 @@ HSaveError ReadGUI(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Resto
 	int anim_count = in->ReadInt32();
 	for (int i = 0; i < anim_count; ++i) {
 		AnimatingGUIButton abut;
-		abut.ReadFromFile(in);
+		abut.ReadFromFile(in, cmp_ver);
 		AddButtonAnimation(abut);
-	}	return err;
+	}
+	return err;
 }
 
 HSaveError WriteInventory(Stream *out) {
@@ -1047,7 +1048,7 @@ ComponentHandler ComponentHandlers[] = {
 	},
 	{
 		"GUI",
-		kGuiSvgVersion_350,
+		kGuiSvgVersion_36020,
 		kGuiSvgVersion_Initial,
 		WriteGUI,
 		ReadGUI
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index a5e0792cd15..6631ca7264d 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -216,7 +216,7 @@ void ReadAnimatedButtons_Aligned(Stream *in, int num_abuts) {
 	AlignedStream align_s(in, Shared::kAligned_Read);
 	for (int i = 0; i < num_abuts; ++i) {
 		AnimatingGUIButton abtn;
-		abtn.ReadFromFile(&align_s);
+		abtn.ReadFromFile(&align_s, 0);
 		AddButtonAnimation(abtn);
 		align_s.Reset();
 	}
diff --git a/engines/ags/engine/gui/animating_gui_button.cpp b/engines/ags/engine/gui/animating_gui_button.cpp
index 9f3cdfb1508..ded337e5c37 100644
--- a/engines/ags/engine/gui/animating_gui_button.cpp
+++ b/engines/ags/engine/gui/animating_gui_button.cpp
@@ -26,7 +26,7 @@ namespace AGS3 {
 
 using AGS::Shared::Stream;
 
-void AnimatingGUIButton::ReadFromFile(Stream *in) {
+void AnimatingGUIButton::ReadFromFile(Stream *in, int cmp_ver) {
 	buttonid = in->ReadInt16();
 	ongui = in->ReadInt16();
 	onguibut = in->ReadInt16();
@@ -34,11 +34,21 @@ void AnimatingGUIButton::ReadFromFile(Stream *in) {
 	loop = in->ReadInt16();
 	frame = in->ReadInt16();
 	speed = in->ReadInt16();
-	repeat = in->ReadInt16();
+	uint16_t anim_flags = in->ReadInt16(); // was repeat (0,1)
 	wait = in->ReadInt16();
+
+	if (cmp_ver < 2) anim_flags &= 0x1; // restrict to repeat only
+	repeat = anim_flags & 0x1;
+	blocking = (anim_flags >> 1) & 0x1;
+	direction = (anim_flags >> 2) & 0x1;
 }
 
 void AnimatingGUIButton::WriteToFile(Stream *out) {
+	uint16_t anim_flags =
+		(repeat & 0x1) |
+		(blocking & 0x1) << 1 |
+		(direction & 0x1) << 2;
+
 	out->WriteInt16(buttonid);
 	out->WriteInt16(ongui);
 	out->WriteInt16(onguibut);
@@ -46,7 +56,7 @@ void AnimatingGUIButton::WriteToFile(Stream *out) {
 	out->WriteInt16(loop);
 	out->WriteInt16(frame);
 	out->WriteInt16(speed);
-	out->WriteInt16(repeat);
+	out->WriteInt16(anim_flags); // was repeat (0,1)
 	out->WriteInt16(wait);
 }
 
diff --git a/engines/ags/engine/gui/animating_gui_button.h b/engines/ags/engine/gui/animating_gui_button.h
index e63d0c9a7f2..c6290318a57 100644
--- a/engines/ags/engine/gui/animating_gui_button.h
+++ b/engines/ags/engine/gui/animating_gui_button.h
@@ -42,7 +42,7 @@ struct AnimatingGUIButton {
 	short speed = 0, repeat = 0, blocking = 0,
 		direction = 0, wait = 0;
 
-	void ReadFromFile(Shared::Stream *in);
+	void ReadFromFile(Shared::Stream *in, int cmp_ver);
 	void WriteToFile(Shared::Stream *out);
 };
 
diff --git a/engines/ags/shared/gui/gui_defines.h b/engines/ags/shared/gui/gui_defines.h
index d88a61c29f4..10621fbaee2 100644
--- a/engines/ags/shared/gui/gui_defines.h
+++ b/engines/ags/shared/gui/gui_defines.h
@@ -184,7 +184,8 @@ enum GUITextBoxFlags {
 // TODO: move to the engine code
 enum GuiSvgVersion {
 	kGuiSvgVersion_Initial = 0,
-	kGuiSvgVersion_350 = 1
+	kGuiSvgVersion_350,
+	kGuiSvgVersion_36020
 };
 
 enum GuiDisableStyle {




More information about the Scummvm-git-logs mailing list