[Scummvm-git-logs] scummvm master -> 6c300519b15f3bdbbca6ecf149d70422f0ac494d

antoniou79 noreply at scummvm.org
Sun Apr 3 20:01:47 UTC 2022


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

Summary:
b48c998d72 BLADERUNNER: Increase width of scrollable area in help KIA tab
6994d79e59 BLADERUNNER: Filter Delete key press if no selected saved game
6c300519b1 BLADERUNNER: Use keymapper with proper events for the game


Commit: b48c998d723cd7b90b4ad95b7e1f9a0035705cdf
    https://github.com/scummvm/scummvm/commit/b48c998d723cd7b90b4ad95b7e1f9a0035705cdf
Author: antoniou79 (a.antoniou79 at gmail.com)
Date: 2022-04-03T22:51:33+03:00

Commit Message:
BLADERUNNER: Increase width of scrollable area in help KIA tab

Changed paths:
    engines/bladerunner/ui/kia_section_help.cpp
    engines/bladerunner/ui/kia_section_load.cpp
    engines/bladerunner/ui/kia_section_save.cpp


diff --git a/engines/bladerunner/ui/kia_section_help.cpp b/engines/bladerunner/ui/kia_section_help.cpp
index 170e5590faa..e9fdc9b3d8b 100644
--- a/engines/bladerunner/ui/kia_section_help.cpp
+++ b/engines/bladerunner/ui/kia_section_help.cpp
@@ -32,8 +32,13 @@ namespace BladeRunner {
 
 KIASectionHelp::KIASectionHelp(BladeRunnerEngine *vm) : KIASectionBase(vm) {
 	_uiContainer = new UIContainer(_vm);
+#if BLADERUNNER_ORIGINAL_BUGS
 	_scrollBox   = new UIScrollBox(_vm, nullptr, this, 1024, 0, false, Common::Rect(135, 145, 461, 385), Common::Rect(506, 160, 506, 350));
-
+#else
+	// Increase width of scollable area, to eliminate the (significant) area to the right,
+	// before the scroll bar, where scrolling would not work.
+	_scrollBox   = new UIScrollBox(_vm, nullptr, this, 1024, 0, false, Common::Rect(135, 145, 502, 385), Common::Rect(506, 160, 506, 350));
+#endif
 	_uiContainer->add(_scrollBox);
 }
 
diff --git a/engines/bladerunner/ui/kia_section_load.cpp b/engines/bladerunner/ui/kia_section_load.cpp
index d3000fd9d4c..a42fe075500 100644
--- a/engines/bladerunner/ui/kia_section_load.cpp
+++ b/engines/bladerunner/ui/kia_section_load.cpp
@@ -40,6 +40,10 @@ namespace BladeRunner {
 
 KIASectionLoad::KIASectionLoad(BladeRunnerEngine *vm) : KIASectionBase(vm) {
 	_uiContainer = new UIContainer(_vm);
+	// There is a small area to the right of the save games list, before the scroll bar,
+	// where scrolling does not work.
+	// However, unlike kia_section_help, if we increase the width of the scrollable area here,
+	// we would noticeably mess with the centering of the title label and the saved game names in the list.
 	_scrollBox   = new UIScrollBox(_vm, scrollBoxCallback, this, 1025, 0, true, Common::Rect(155, 158, 461, 346), Common::Rect(506, 160, 506, 350));
 	_uiContainer->add(_scrollBox);
 
diff --git a/engines/bladerunner/ui/kia_section_save.cpp b/engines/bladerunner/ui/kia_section_save.cpp
index 79471507060..168a9c26d34 100644
--- a/engines/bladerunner/ui/kia_section_save.cpp
+++ b/engines/bladerunner/ui/kia_section_save.cpp
@@ -45,6 +45,10 @@ namespace BladeRunner {
 KIASectionSave::KIASectionSave(BladeRunnerEngine *vm) : KIASectionBase(vm) {
 	_uiContainer = new UIContainer(_vm);
 
+	// There is a small area to the right of the save games list, before the scroll bar,
+	// where scrolling does not work.
+	// However, unlike kia_section_help, if we increase the width of the scrollable area here,
+	// we would noticeably mess with the centering of the title label and the saved game names in the list.
 	_scrollBox = new UIScrollBox(_vm, scrollBoxCallback, this, 1024, 0, true, Common::Rect(155, 158, 461, 346), Common::Rect(506, 160, 506, 350));
 	_uiContainer->add(_scrollBox);
 


Commit: 6994d79e591439090ef3db7cf109a899c1093707
    https://github.com/scummvm/scummvm/commit/6994d79e591439090ef3db7cf109a899c1093707
Author: antoniou79 (a.antoniou79 at gmail.com)
Date: 2022-04-03T22:51:33+03:00

Commit Message:
BLADERUNNER: Filter Delete key press if no selected saved game

Also introduce a getValidChar() method to improve modularity and readability

Changed paths:
    engines/bladerunner/ui/ui_input_box.cpp
    engines/bladerunner/ui/ui_input_box.h


diff --git a/engines/bladerunner/ui/ui_input_box.cpp b/engines/bladerunner/ui/ui_input_box.cpp
index 3dbf9f5ae3b..62214804918 100644
--- a/engines/bladerunner/ui/ui_input_box.cpp
+++ b/engines/bladerunner/ui/ui_input_box.cpp
@@ -87,41 +87,52 @@ void UIInputBox::hide() {
 }
 
 void UIInputBox::handleKeyDown(const Common::KeyState &kbd) {
-	if (kbd.ascii != 0) {
-		// The above check for kbd.ascii > 0 gets rid of the tentative warning:
+	if (_isVisible) {
+		uint8 kc = 0;
+		if (getValidChar(kbd.ascii, kc) && _text.size() < _maxLength) {
+			_text += kc;
+		} else if (kbd.keycode == Common::KEYCODE_BACKSPACE) {
+			_text.deleteLastChar();
+		} else if ((kbd.keycode == Common::KEYCODE_RETURN || kbd.keycode == Common::KEYCODE_KP_ENTER)
+			        && !_text.empty()) {
+			if (_valueChangedCallback) {
+				_valueChangedCallback(_callbackData, this);
+			}
+		}
+	}
+}
+
+bool UIInputBox::getValidChar(const uint16 &kAscii16bit, uint8 &targetAscii) {
+	if (kAscii16bit != 0) {
+		// The above check for kAscii16bit > 0 gets rid of the tentative warning:
 		// "Adding \0 to String. This is permitted, but can have unwanted consequences."
 		// which was triggered by the .encode(Common::kDos850) operation below.
 		//
 		// The values that the KeyState::ascii field receives from the SDL backend are actually ISO 8859-1 encoded. They need to be
 		// reencoded to DOS so as to match the game font encoding (although we currently use UIInputBox::charIsValid() to block most
 		// extra characters, so it might not make much of a difference).
-		uint8 kc = (uint8)(Common::U32String(Common::String::format("%c", kbd.ascii), Common::kISO8859_1).encode(Common::kDos850).firstChar());
-		if (_isVisible) {
-			if (charIsValid(kc) && _text.size() < _maxLength) {
-				_text += kc;
-			} else if (kbd.keycode == Common::KEYCODE_BACKSPACE) {
-				_text.deleteLastChar();
-			} else if ((kbd.keycode == Common::KEYCODE_RETURN || kbd.keycode == Common::KEYCODE_KP_ENTER)
-			           && !_text.empty()) {
-				if (_valueChangedCallback) {
-					_valueChangedCallback(_callbackData, this);
-				}
-			}
-		}
+		targetAscii = (uint8)(Common::U32String(Common::String::format("%c", kAscii16bit), Common::kISO8859_1).encode(Common::kDos850).firstChar());
+		return charIsValid(targetAscii);
 	}
+	return false;
 }
 
-bool UIInputBox::charIsValid(uint8 kc) {
+bool UIInputBox::charIsValid(const uint8 &kc) {
 	// The in-game font for text input is KIA6PT which follows IBM PC Code page 437 (CCSID 437)
 	// This code page is identical to Code page 850 for the first 128 codes.
 	// This method is:
-	// 1) filtering out characters not allowed in a DOS filename.
+	// 1) Filtering out characters not allowed in a DOS filename.
 	//    Note, however, that it does allow ',', '.', ';', '=', '[' and ']'
 	//    TODO Is that a bug?
-	// 2) allowing codes for glyphs that exist in KIA6PT up to code 0xA8 (glyph '¿')
+	// 2) Allowing codes for glyphs that exist in KIA6PT up to code 0xA8 (glyph '¿')
 	//    and also the extra codes for 0xAD (glyph '¡') and 0xE1 (glyph 'ß')
 	//    (in order for these extra extended ASCII codes to be included,
 	//     the comparisons in the return clause should be between uint values).
+	// 3) Additionally disallows the '\x7F' character which caused a glyph '⊐' to be printed
+	//    when the Delete key was pressed with no saved game selected,
+	//    ie. the highlighted line on the KIA save screen is "<< NEW SLOT >>".
+	//    The original does not show this glyph either but seems to filter the key earlier (not in this method).
+	//    It's more effective to completely block the glyph in this method, though.
 	return kc >= ' '
 		&& kc != '<'
 		&& kc != '>'
@@ -132,6 +143,7 @@ bool UIInputBox::charIsValid(uint8 kc) {
 		&& kc != '|'
 		&& kc != '?'
 		&& kc != '*'
+		&& kc != (uint8)'\x7F'
 		&& (kc <= (uint8)'\xA8' || kc == (uint8)'\xAD' || kc == (uint8)'\xE1');
 }
 
diff --git a/engines/bladerunner/ui/ui_input_box.h b/engines/bladerunner/ui/ui_input_box.h
index a5ac570ca7c..fe705b5e669 100644
--- a/engines/bladerunner/ui/ui_input_box.h
+++ b/engines/bladerunner/ui/ui_input_box.h
@@ -57,7 +57,8 @@ public:
 	void handleKeyDown(const Common::KeyState &kbd) override;
 
 private:
-	bool charIsValid(uint8 kc);
+	bool getValidChar(const uint16 &kc16bit, uint8 &kc8bit);
+	bool charIsValid(const uint8 &kc16bit);
 };
 
 } // End of namespace BladeRunner


Commit: 6c300519b15f3bdbbca6ecf149d70422f0ac494d
    https://github.com/scummvm/scummvm/commit/6c300519b15f3bdbbca6ecf149d70422f0ac494d
Author: antoniou79 (a.antoniou79 at gmail.com)
Date: 2022-04-03T22:51:33+03:00

Commit Message:
BLADERUNNER: Use keymapper with proper events for the game

Changed paths:
    engines/bladerunner/POTFILES
    engines/bladerunner/bladerunner.cpp
    engines/bladerunner/bladerunner.h
    engines/bladerunner/metaengine.cpp
    engines/bladerunner/ui/kia.cpp
    engines/bladerunner/ui/kia.h
    engines/bladerunner/ui/kia_section_base.h
    engines/bladerunner/ui/kia_section_save.cpp
    engines/bladerunner/ui/kia_section_save.h
    engines/bladerunner/ui/scores.cpp
    engines/bladerunner/ui/scores.h


diff --git a/engines/bladerunner/POTFILES b/engines/bladerunner/POTFILES
index dbadaf46213..6aaf1dfa895 100644
--- a/engines/bladerunner/POTFILES
+++ b/engines/bladerunner/POTFILES
@@ -1,3 +1,4 @@
 engines/bladerunner/bladerunner.cpp
 engines/bladerunner/detection.cpp
 engines/bladerunner/detection_tables.h
+engines/bladerunner/metaengine.cpp
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 85de2fa1160..560aed08dc6 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -76,6 +76,9 @@
 #include "bladerunner/waypoints.h"
 #include "bladerunner/zbuffer.h"
 
+#include "backends/keymapper/action.h"
+#include "backends/keymapper/keymapper.h"
+
 #include "common/array.h"
 #include "common/config-manager.h"
 #include "common/error.h"
@@ -95,6 +98,10 @@
 
 namespace BladeRunner {
 
+const char *BladeRunnerEngine::kGameplayKeymapId = "bladerunner-gameplay";
+const char *BladeRunnerEngine::kKiaKeymapId = "bladerunner-kia";
+const char *BladeRunnerEngine::kCommonKeymapId = "bladerunner-common";
+
 BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *desc)
 	: Engine(syst),
 	  _rnd("bladerunner") {
@@ -233,6 +240,10 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des
 	_currentKeyDown.keycode = Common::KEYCODE_INVALID;
 	_keyRepeatTimeLast = 0;
 	_keyRepeatTimeDelay = 0;
+
+	_activeCustomEvents->clear();
+	_customEventRepeatTimeLast = 0;
+	_customEventRepeatTimeDelay = 0;
 }
 
 BladeRunnerEngine::~BladeRunnerEngine() {
@@ -380,6 +391,21 @@ Common::Error BladeRunnerEngine::run() {
 		}
 		// end of additional code for gracefully handling end-game
 
+		if (getEventManager()->getKeymapper() != nullptr) {
+			if (getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kCommonKeymapId) != nullptr)
+				getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kCommonKeymapId)->setEnabled(true);
+
+			if (getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId) != nullptr)
+				getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId)->setEnabled(true);
+
+			if (getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kKiaKeymapId) != nullptr) {
+				// When disabling a keymap, make sure all their active events in the _activeCustomEvents array
+				// are cleared, because as they won't get an explicit "EVENT_CUSTOM_ENGINE_ACTION_END" event.
+				cleanupPendingRepeatingEvents(BladeRunnerEngine::kKiaKeymapId);
+				getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kKiaKeymapId)->setEnabled(false);
+			}
+		}
+
 		if (_validBootParam) {
 			// clear the flag, so that after a possible game gameOver / end-game
 			// it won't be true again; just to be safe and avoid potential side-effects
@@ -1262,18 +1288,46 @@ void BladeRunnerEngine::walkingReset() {
 	_isInsideScriptActor  = false;
 }
 
+bool BladeRunnerEngine::isAllowedRepeatedCustomEvent(const Common::Event &currevent) {
+	switch (currevent.type) {
+	case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+		switch ((BladeRunnerEngineMappableAction)currevent.customType) {
+		case kMpblActionCutsceneSkip:
+			// fall through
+		case kMpActionDialogueSkip:
+			// fall through
+		case kMpActionToggleKiaOptions:
+			return true;
+
+		default:
+			return false;
+		}
+		break;
+
+	default:
+		break;
+	}
+	return false;
+}
+
 // The original allowed a few keyboard keys to be repeated ("key spamming")
 // namely, Esc, Return and Space during normal gameplay.
-// "Space" spamming results in McCoy quickly switching between combat mode and normal mode, 
-// which is not very useful but it's the original's behavior.
-// Spamming Space, backspace, latin letter keys and symbols is allowed
-// in KIA mode, particularly in the Save Panel when writing the name for a save game.
+// "Spacebar" spamming would result in McCoy quickly switching between combat mode and normal mode,
+// which is not very useful -- by introducing the keymapper with custom action events this behavior is no longer replicated.
+// Spamming Space, backspace, latin letter keys and symbols is allowed in KIA mode,
+// particularly in the Save Panel when typing in the name for a save game.
 // For simplicity, we allow everything after the 0x20 (space ascii code) up to 0xFF.
 // The UIInputBox::charIsValid() will filter out any unsupported characters.
 // F-keys are not repeated.
 bool BladeRunnerEngine::isAllowedRepeatedKey(const Common::KeyState &currKeyState) {
-	return  currKeyState.keycode == Common::KEYCODE_ESCAPE
-	    ||  currKeyState.keycode == Common::KEYCODE_RETURN
+	// Return and KP_Enter keys are repeatable in KIA.
+	// This is noticable when choosing an already saved game to overwrite
+	// and holding down Enter would cause the confirmation dialogue to pop up
+	// and it would subsequently confirm it as well.
+	// TODO if we introduce a custom confirm action for KIA, then that action should be repeatable
+	//      and KEYCODE_RETURN and KEYCODE_KP_ENTER should be removed from this clause;
+	//      the action should be added to the switch cases in isAllowedRepeatedCustomEvent()
+	return  currKeyState.keycode == Common::KEYCODE_RETURN
 	    ||  currKeyState.keycode == Common::KEYCODE_KP_ENTER
 	    ||  currKeyState.keycode == Common::KEYCODE_BACKSPACE
 	    ||  currKeyState.keycode == Common::KEYCODE_SPACE
@@ -1295,7 +1349,7 @@ void BladeRunnerEngine::handleEvents() {
 	// even in the case when no save games for the game exist. In such case the game is supposed
 	// to immediately play the intro video and subsequently start a new game of medium difficulty.
 	// It does not expect the player to enter KIA beforehand, which causes side-effects and unforeseen behavior.
-	// Note: eventually we will support the option to launch into KIA in any case,
+	// NOTE Eventually, we will support the option to launch into KIA in any case,
 	// but not via the "hack" way that is fixed here.
 	if (_gameJustLaunched) {
 		_gameJustLaunched = false;
@@ -1306,12 +1360,109 @@ void BladeRunnerEngine::handleEvents() {
 	Common::EventManager *eventMan = _system->getEventManager();
 	while (eventMan->pollEvent(event)) {
 		switch (event.type) {
+		case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
+			if (shouldDropRogueCustomEvent(event)) {
+				return;
+			}
+			switch ((BladeRunnerEngineMappableAction)event.customType) {
+			case kMpActionToggleCombat:
+				handleMouseAction(event.mouse.x, event.mouse.y, false, false);
+				break;
+
+			case kMpblActionCutsceneSkip:
+				// fall through
+			case kMpActionDialogueSkip:
+				// fall through
+			case kMpActionToggleKiaOptions:
+				// fall through
+			case kMpActionOpenKiaDatabase:
+				// fall through
+			case kMpActionOpenKIATabHelp:
+				// fall through
+			case kMpActionOpenKIATabSaveGame:
+				// fall through
+			case kMpActionOpenKIATabLoadGame:
+				// fall through
+			case kMpActionOpenKIATabCrimeSceneDatabase:
+				// fall through
+			case kMpActionOpenKIATabSuspectDatabase:
+				// fall through
+			case kMpActionOpenKIATabClueDatabase:
+				// fall through
+			case kMpActionOpenKIATabQuitGame:
+				handleCustomEventStop(event);
+				break;
+
+			default:
+				break;
+			}
+			break;
+
+		case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+			if (shouldDropRogueCustomEvent(event)) {
+				return;
+			}
+			// Process the initial/actual custom event only here, filter out repeats
+			// TODO Does this actually filter repeats?
+			if (!event.kbdRepeat) {
+				switch ((BladeRunnerEngineMappableAction)event.customType) {
+				case kMpActionToggleCombat:
+					handleMouseAction(event.mouse.x, event.mouse.y, false, true);
+					break;
+
+				case kMpblActionCutsceneSkip:
+					// fall through
+				case kMpActionDialogueSkip:
+					// fall through
+				case kMpActionToggleKiaOptions:
+					// fall through
+				case kMpActionOpenKiaDatabase:
+					// fall through
+				case kMpActionOpenKIATabHelp:
+					// fall through
+				case kMpActionOpenKIATabSaveGame:
+					// fall through
+				case kMpActionOpenKIATabLoadGame:
+					// fall through
+				case kMpActionOpenKIATabCrimeSceneDatabase:
+					// fall through
+				case kMpActionOpenKIATabSuspectDatabase:
+					// fall through
+				case kMpActionOpenKIATabClueDatabase:
+					// fall through
+				case kMpActionOpenKIATabQuitGame:
+					if (isAllowedRepeatedCustomEvent(event)
+					    && _activeCustomEvents->size() < kMaxCustomConcurrentRepeatableEvents) {
+						if (_activeCustomEvents->empty()) {
+							_customEventRepeatTimeLast = _time->currentSystem();
+							_customEventRepeatTimeDelay = kKeyRepeatInitialDelay;
+						}
+						_activeCustomEvents->push_back(event);
+					}
+					handleCustomEventStart(event);
+					break;
+
+				case kMpActionScrollUp:
+					handleMouseAction(event.mouse.x, event.mouse.y, false, false, -1);
+					break;
+
+				case kMpActionScrollDown:
+					handleMouseAction(event.mouse.x, event.mouse.y, false, false, 1);
+					break;
+
+				default:
+					break;
+				}
+			}
+			break;
+
 		case Common::EVENT_KEYUP:
 			handleKeyUp(event);
 			break;
 
 		case Common::EVENT_KEYDOWN:
-			// Process the actual key press only and filter out repeats
+			// Process the initial/actual key press only here, filter out repeats
+			// TODO Does this actually filter repeats?
 			if (!event.kbdRepeat) {
 				// Only for some keys, allow repeated firing emulation
 				// First hit (fire) has a bigger delay (kKeyRepeatInitialDelay) before repeated events are fired from the same key
@@ -1328,30 +1479,10 @@ void BladeRunnerEngine::handleEvents() {
 			handleMouseAction(event.mouse.x, event.mouse.y, true, false);
 			break;
 
-		case Common::EVENT_RBUTTONUP:
-		case Common::EVENT_MBUTTONUP:
-			handleMouseAction(event.mouse.x, event.mouse.y, false, false);
-			break;
-
 		case Common::EVENT_LBUTTONDOWN:
 			handleMouseAction(event.mouse.x, event.mouse.y, true, true);
 			break;
 
-		case Common::EVENT_RBUTTONDOWN:
-		case Common::EVENT_MBUTTONDOWN:
-			handleMouseAction(event.mouse.x, event.mouse.y, false, true);
-			break;
-
-		// Added by ScummVM team
-		case Common::EVENT_WHEELUP:
-			handleMouseAction(event.mouse.x, event.mouse.y, false, false, -1);
-			break;
-
-		// Added by ScummVM team
-		case Common::EVENT_WHEELDOWN:
-			handleMouseAction(event.mouse.x, event.mouse.y, false, false, 1);
-			break;
-
 		default:
 			; // nothing to do
 		}
@@ -1361,11 +1492,23 @@ void BladeRunnerEngine::handleEvents() {
 	// Some of those may lead to their own internal gameTick() loops (which will call handleEvents()).
 	// Thus, we need to get a new timeNow value here to ensure we're not comparing with a stale version.
 	uint32 timeNow = _time->currentSystem();
-	if (isAllowedRepeatedKey(_currentKeyDown)
-	    && (timeNow - _keyRepeatTimeLast >= _keyRepeatTimeDelay)) {
+	if (!_activeCustomEvents->empty()
+	    && (timeNow - _customEventRepeatTimeLast >= _customEventRepeatTimeDelay)) {
+		_customEventRepeatTimeLast = timeNow;
+		_customEventRepeatTimeDelay = kKeyRepeatSustainDelay;
+		for (ActiveCustomEventsArray::iterator it = _activeCustomEvents->begin(); it != _activeCustomEvents->end(); it++) {
+			// kbdRepeat field will be unused here since we emulate the kbd repeat behavior anyway,
+			// but maybe it's good to set it for consistency
+			it->kbdRepeat = true;
+			// reissue the custom start event
+			handleCustomEventStart(*it);
+		}
+	} else if (isAllowedRepeatedKey(_currentKeyDown)
+	           && (timeNow - _keyRepeatTimeLast >= _keyRepeatTimeDelay)) {
 		// create a "new" keydown event
 		event.type = Common::EVENT_KEYDOWN;
-		// kbdRepeat field will be unused here since we emulate the kbd repeat behavior anyway, but it's good to set it for consistency
+		// kbdRepeat field will be unused here since we emulate the kbd repeat behavior anyway,
+		// but it's good to set it for consistency
 		event.kbdRepeat = true;
 		event.kbd = _currentKeyDown;
 		_keyRepeatTimeLast = timeNow;
@@ -1391,42 +1534,122 @@ void BladeRunnerEngine::handleKeyUp(Common::Event &event) {
 }
 
 void BladeRunnerEngine::handleKeyDown(Common::Event &event) {
-	if (_vqaIsPlaying
-	    && (event.kbd.keycode == Common::KEYCODE_RETURN
-	        || event.kbd.keycode == Common::KEYCODE_KP_ENTER
-	        || event.kbd.keycode == Common::KEYCODE_SPACE
-	        || event.kbd.keycode == Common::KEYCODE_ESCAPE)) {
-		// Note: Original allows Esc, Spacebar and Return/KP_Enter to skip cutscenes
+	if (!playerHasControl() || _isWalkingInterruptible || _actorIsSpeaking || _vqaIsPlaying) {
+		return;
+	}
+
+	if (_kia->isOpen()) {
+		_kia->handleKeyDown(event.kbd);
+		return;
+	}
+
+	if (_spinner->isOpen()) {
+		return;
+	}
+
+	if (_elevator->isOpen()) {
+		return;
+	}
+
+	if (_esper->isOpen()) {
+		return;
+	}
+
+	if (_vk->isOpen()) {
+		return;
+	}
+
+	if (_dialogueMenu->isOpen()) {
+		return;
+	}
+
+	if (_scores->isOpen()) {
+		_scores->handleKeyDown(event.kbd);
+		return;
+	}
+}
+
+// Check if an polled event belongs to a currently disabled keymap and, if so, drop it.
+bool BladeRunnerEngine::shouldDropRogueCustomEvent(const Common::Event &evt) {
+	if (getEventManager()->getKeymapper() != nullptr) {
+		Common::KeymapArray kmpsArr = getEventManager()->getKeymapper()->getKeymaps();
+		for (Common::KeymapArray::iterator kmpsIt = kmpsArr.begin(); kmpsIt != kmpsArr.end(); ++kmpsIt) {
+			if (!(*kmpsIt)->isEnabled()) {
+				Common::Keymap::ActionArray actionsInKm = (*kmpsIt)->getActions();
+				for (Common::Keymap::ActionArray::iterator kmIt = actionsInKm.begin(); kmIt != actionsInKm.end(); ++kmIt) {
+					if ((evt.type != Common::EVENT_INVALID) && (evt.customType == (*kmIt)->event.customType)) {
+						return true;
+					}
+				}
+			}
+		}
+	}
+	return false;
+}
+
+void BladeRunnerEngine::cleanupPendingRepeatingEvents(const Common::String &keymapperId) {
+	// Also clean up any currently repeating key down here.
+	// This prevents a bug where holding down Enter key in save screen with a filled in save game
+	// or a selected game to overwrite, would complete the save and then maintain Enter as a repeating key
+	// in the main gameplay (without ever sending a key up event to stop it).
+	_currentKeyDown.keycode = Common::KEYCODE_INVALID;
+
+	if (getEventManager()->getKeymapper() != nullptr
+	    && getEventManager()->getKeymapper()->getKeymap(keymapperId) != nullptr
+		&& !_activeCustomEvents->empty()) {
+
+		Common::Keymap::ActionArray actionsInKm = getEventManager()->getKeymapper()->getKeymap(keymapperId)->getActions();
+		for (Common::Keymap::ActionArray::iterator kmIt = actionsInKm.begin(); kmIt != actionsInKm.end(); ++kmIt) {
+			for (ActiveCustomEventsArray::iterator actIt = _activeCustomEvents->begin(); actIt != _activeCustomEvents->end(); ++actIt) {
+				if ((actIt->type != Common::EVENT_INVALID) && (actIt->customType == (*kmIt)->event.customType)) {
+					_activeCustomEvents->erase(actIt);
+					if (actIt == _activeCustomEvents->end()) {
+						break;
+					}
+				}
+			}
+		}
+	}
+}
+
+void BladeRunnerEngine::handleCustomEventStop(Common::Event &event) {
+	if (!_activeCustomEvents->empty()) {
+		for (ActiveCustomEventsArray::iterator it = _activeCustomEvents->begin(); it != _activeCustomEvents->end(); it++) {
+			if ((it->type != Common::EVENT_INVALID) && (it->customType == event.customType)) {
+				_activeCustomEvents->erase(it);
+				break;
+			}
+		}
+	}
+
+	if (!playerHasControl() || _isWalkingInterruptible) {
+		return;
+	}
+
+	if (_kia->isOpen()) {
+		_kia->handleCustomEventStop(event);
+		return;
+	}
+}
+
+void BladeRunnerEngine::handleCustomEventStart(Common::Event &event) {
+	if (_vqaIsPlaying && (BladeRunnerEngineMappableAction)event.customType == kMpblActionCutsceneSkip) {
 		_vqaStopIsRequested = true;
 		_vqaIsPlaying = false;
-
 		return;
 	}
 
-	if (_vqaStopIsRequested
-	    && (event.kbd.keycode == Common::KEYCODE_RETURN
-	        || event.kbd.keycode == Common::KEYCODE_KP_ENTER
-	        || event.kbd.keycode == Common::KEYCODE_SPACE
-	        || event.kbd.keycode == Common::KEYCODE_ESCAPE)) {
-		 return;
+	if (_vqaStopIsRequested && (BladeRunnerEngineMappableAction)event.customType == kMpblActionCutsceneSkip) {
+		return;
 	}
 
-	if (_actorIsSpeaking
-	    && (event.kbd.keycode == Common::KEYCODE_RETURN
-	        || event.kbd.keycode == Common::KEYCODE_KP_ENTER
-	        || event.kbd.keycode == Common::KEYCODE_ESCAPE)) {
-		// Note: Original only uses the Return/KP_Enter key here
+	if (_actorIsSpeaking && (BladeRunnerEngineMappableAction)event.customType == kMpActionDialogueSkip) {
 		_actorSpeakStopIsRequested = true;
 		_actorIsSpeaking = false;
-
-		return;
 	}
 
-	if (_actorSpeakStopIsRequested
-	    && (event.kbd.keycode == Common::KEYCODE_RETURN
-	        || event.kbd.keycode == Common::KEYCODE_KP_ENTER
-	        || event.kbd.keycode == Common::KEYCODE_ESCAPE)) {
-		 return;
+	if (_actorSpeakStopIsRequested && (BladeRunnerEngineMappableAction)event.customType == kMpActionDialogueSkip) {
+		return;
 	}
 
 	if (!playerHasControl() || _isWalkingInterruptible || _actorIsSpeaking || _vqaIsPlaying) {
@@ -1434,7 +1657,7 @@ void BladeRunnerEngine::handleKeyDown(Common::Event &event) {
 	}
 
 	if (_kia->isOpen()) {
-		_kia->handleKeyDown(event.kbd);
+		_kia->handleCustomEventStart(event);
 		return;
 	}
 
@@ -1459,41 +1682,38 @@ void BladeRunnerEngine::handleKeyDown(Common::Event &event) {
 	}
 
 	if (_scores->isOpen()) {
-		_scores->handleKeyDown(event.kbd);
+		_scores->handleCustomEventStart(event);
 		return;
 	}
 
-	switch (event.kbd.keycode) {
-		case Common::KEYCODE_F1:
+	switch ((BladeRunnerEngineMappableAction)event.customType) {
+		case kMpActionOpenKIATabHelp:
 			_kia->open(kKIASectionHelp);
 			break;
-		case Common::KEYCODE_F2:
+		case kMpActionOpenKIATabSaveGame:
 			_kia->open(kKIASectionSave);
 			break;
-		case Common::KEYCODE_F3:
+		case kMpActionOpenKIATabLoadGame:
 			_kia->open(kKIASectionLoad);
 			break;
-		case Common::KEYCODE_F4:
+		case kMpActionOpenKIATabCrimeSceneDatabase:
 			_kia->open(kKIASectionCrimes);
 			break;
-		case Common::KEYCODE_F5:
+		case kMpActionOpenKIATabSuspectDatabase:
 			_kia->open(kKIASectionSuspects);
 			break;
-		case Common::KEYCODE_F6:
+		case kMpActionOpenKIATabClueDatabase:
 			_kia->open(kKIASectionClues);
 			break;
-		case Common::KEYCODE_F10:
+		case kMpActionOpenKIATabQuitGame:
 			_kia->open(kKIASectionQuit);
 			break;
-		case Common::KEYCODE_TAB:
+		case kMpActionOpenKiaDatabase:
 			_kia->openLastOpened();
 			break;
-		case Common::KEYCODE_ESCAPE:
+		case kMpActionToggleKiaOptions:
 			_kia->open(kKIASectionSettings);
 			break;
-		case Common::KEYCODE_SPACE:
-			_combat->change();
-			break;
 		default:
 			break;
 	}
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 4bcf079ef1d..ca19a3d81c5 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -30,6 +30,7 @@
 #include "common/sinetables.h"
 #include "common/stream.h"
 #include "common/keyboard.h"
+#include "common/events.h"
 
 #include "engines/engine.h"
 
@@ -110,6 +111,7 @@ public:
 	static const int kArchiveCount = 12; // +2 to original value (10) to accommodate for SUBTITLES.MIX and one extra resource file, to allow for capability of loading all VQAx.MIX and the MODE.MIX file (debug purposes)
 	static const int kActorCount =  100;
 	static const int kActorVoiceOver = kActorCount - 1;
+	static const int kMaxCustomConcurrentRepeatableEvents = 20;
 
 	// Incremental number to keep track of significant revisions of the ScummVM bladerunner engine
 	// that could potentially introduce incompatibilities with old save files or require special actions to restore compatibility
@@ -120,6 +122,10 @@ public:
 	// 2: all time code uses uint32 (since July 17 2019),
 	static const int kBladeRunnerScummVMVersion = 2;
 
+	static const char *kGameplayKeymapId;
+	static const char *kKiaKeymapId;
+	static const char *kCommonKeymapId;
+
 	bool _gameIsRunning;
 	bool _windowIsActive;
 	int  _playerLosesControlCounter;
@@ -265,6 +271,50 @@ public:
 	uint32 _keyRepeatTimeLast;
 	uint32 _keyRepeatTimeDelay;
 
+	uint32 _customEventRepeatTimeLast;
+	uint32 _customEventRepeatTimeDelay;
+	typedef Common::Array<Common::Event> ActiveCustomEventsArray;
+
+	// We do allow keys mapped to the same event,
+	// so eg. a key (Enter) could cause 2 or more events to fire,
+	// However, we should probably restrict the active events
+	// (that can be repeated while holding the mapped keys down)
+	// to a maximum of kMaxCustomConcurrentRepeatableEvents
+	ActiveCustomEventsArray _activeCustomEvents[kMaxCustomConcurrentRepeatableEvents];
+
+	// NOTE We still need keyboard functionality for naming saved games and also for the KIA Easter eggs.
+	//      In KIA keyboard events should be accounted where possible - however some keymaps are still needed
+	//      which is why we have the three separate common, gameplay-only and kia-only keymaps.
+	//      If a valid keyboard key character eg. ("A") for text input (or Easter egg input)
+	//      is also mapped to a common or KIA only custom event, then the custom event will be effected and not the key input.
+	// NOTE We don't use a custom action for left click -- we just use the standard left click action event (kStandardActionLeftClick)
+	// NOTE Dialogue Skip does not work for dialogue replayed when clicking on KIA clues (this is the original's behavior too)
+	// NOTE Toggle KIA options does not work when McCoy is walking towards a character when the player clicks on McCoy
+	//      (this is the original's behavior too).
+	//      "Esc" (by default) or the mapped key to this action still works though.
+	// NOTE A drawback of using customized keymapper for the game is that we can no longer replicate the original's behavior
+	//      whereby holding down <SPACEBAR> would cause McCoy to keep switching quickly between combat mode and normal mode.
+	//      This is because the original, when holding down right mouse button, would just toggle McCoy's mode once.
+	//      We keep the behavior for "right mouse button".
+	//      The continuous fast toggle behavior when holding down <SPACEBAR> feels more like a bug anyway.
+	enum BladeRunnerEngineMappableAction {
+//		kMpActionLeftClick,        // default <left click> (select, walk-to, run-to, look-at, talk-to, use, shoot (combat mode), KIA (click on McCoy))
+		kMpActionToggleCombat,     // default <right click> or <Spacebar>
+		kMpblActionCutsceneSkip,   // default <Return> or <KP_Enter> or <Esc> or <Spacebar>
+		kMpActionDialogueSkip,     // default <Return> or <KP_Enter>
+		kMpActionToggleKiaOptions, // default <Esc> opens/closes KIA, in Options tab
+		kMpActionOpenKiaDatabase,  // default <Tab> - only opens KIA (if closed), in one of the database tabs (the last active one, or else the first)
+		kMpActionOpenKIATabHelp,               // default <F1>
+		kMpActionOpenKIATabSaveGame,           // default <F2>
+		kMpActionOpenKIATabLoadGame,           // default <F3>
+		kMpActionOpenKIATabCrimeSceneDatabase, // default <F4>
+		kMpActionOpenKIATabSuspectDatabase,    // default <F5>
+		kMpActionOpenKIATabClueDatabase,       // default <F6>
+		kMpActionOpenKIATabQuitGame,           // default <F10>
+		kMpActionScrollUp,                     // ScummVM addition (scroll list up)
+		kMpActionScrollDown                    // ScummVM addition (scroll list down)
+	};
+
 private:
 	MIXArchive _archives[kArchiveCount];
 
@@ -326,6 +376,13 @@ public:
 
 	bool isAllowedRepeatedKey(const Common::KeyState &currKeyState);
 
+	void handleCustomEventStart(Common::Event &event);
+	void handleCustomEventStop(Common::Event &event);
+	bool isAllowedRepeatedCustomEvent(const Common::Event &currEvent);
+
+	bool shouldDropRogueCustomEvent(const Common::Event &evt);
+	void cleanupPendingRepeatingEvents(const Common::String &keymapperId);
+
 	void gameWaitForActive();
 	void loopActorSpeaking();
 	void loopQueuedDialogueStillPlaying();
diff --git a/engines/bladerunner/metaengine.cpp b/engines/bladerunner/metaengine.cpp
index 2050d57bfbc..3f159b59681 100644
--- a/engines/bladerunner/metaengine.cpp
+++ b/engines/bladerunner/metaengine.cpp
@@ -23,10 +23,15 @@
 #include "bladerunner/bladerunner.h"
 #include "bladerunner/savefile.h"
 
+#include "backends/keymapper/action.h"
+#include "backends/keymapper/keymap.h"
+#include "backends/keymapper/standard-actions.h"
+
 #include "common/config-manager.h"
 #include "common/system.h"
 #include "common/savefile.h"
 #include "common/serializer.h"
+#include "common/translation.h"
 
 #include "engines/advancedDetector.h"
 
@@ -36,6 +41,7 @@ public:
 
 	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
 	bool hasFeature(MetaEngineFeature f) const override;
+	Common::KeymapArray initKeymaps(const char *target) const override;
 
 	SaveStateList listSaves(const char *target) const override;
 	int getMaximumSaveSlot() const override;
@@ -66,6 +72,141 @@ bool BladeRunnerMetaEngine::hasFeature(MetaEngineFeature f) const {
 		f == kSimpleSavesNames;
 }
 
+Common::KeymapArray BladeRunnerMetaEngine::initKeymaps(const char *target) const {
+	using namespace Common;
+	using namespace BladeRunner;
+
+	Common::String gameId = ConfMan.get("gameid", target);
+	Common::U32String gameDesc;
+	Keymap *commonKeymap;
+	Keymap *gameplayKeymap;
+	Keymap *kiaOnlyKeymap;
+
+	if (gameId == "bladerunner") {
+		gameDesc = "Blade Runner";
+	} else if (gameId == "bladerunner-final") {
+		gameDesc = "Blade Runner (Restored Content)";
+	}
+
+	if (gameDesc.empty()) {
+		return AdvancedMetaEngine::initKeymaps(target);
+	}
+
+	// We use 3 keymaps: common (main game and KIA), gameplay (main game only) and kia (KIA only).
+	// This helps us with disabling unneeded keymaps, which is especially useful in KIA, when typing in a saved game.
+	// In general, Blade Runner by default, can bind a key (eg. spacebar) to multiple actions
+	// (eg. skip cutscene, toggle combat, enter a blank space in save game input field).
+	// We need to be able to disable the conflicting keymaps, while keeping others that should still work in KIA
+	// (eg. "Esc" (by default) toggling KIA should work in normal gameplay and also within KIA).
+	// Another related issue we tackle is that a custom action event does not maintain the keycode and ascii value
+	// (if it was associated with a keyboard key), and there's no obvious way to retrieve those from it.
+	// Thus, a custom action event cannot be somehow utilised to produce keyboard key presses
+	// (again if a keyboard key is mapped to that action), so it cannot by itself be used
+	// for text entering in the save file name input field, or for typing the Easter Egg strings.
+	commonKeymap = new Keymap(Keymap::kKeymapTypeGame, BladeRunnerEngine::kCommonKeymapId, gameDesc + Common::U32String(" - ") + _("common shortcuts"));
+	gameplayKeymap = new Keymap(Keymap::kKeymapTypeGame, BladeRunnerEngine::kGameplayKeymapId, gameDesc + Common::U32String(" - ") + _("main game shortcuts"));
+	kiaOnlyKeymap = new Keymap(Keymap::kKeymapTypeGame, BladeRunnerEngine::kKiaKeymapId, gameDesc + Common::U32String(" - ") + _("KIA only shortcuts"));
+
+	Action *act;
+
+	// Look at backends\keymapper\hardware-input.cpp for the strings that can be used in InputMapping
+	act = new Action(kStandardActionLeftClick, _("Walk / Look / Talk / Select / Shoot"));
+	act->setLeftClickEvent();
+	act->addDefaultInputMapping("MOUSE_LEFT");
+	act->addDefaultInputMapping("JOY_A");
+	commonKeymap->addAction(act);
+
+	act = new Action("COMBAT", _("Toggle Combat"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionToggleCombat);
+	act->addDefaultInputMapping("MOUSE_RIGHT");
+	act->addDefaultInputMapping("MOUSE_MIDDLE");
+	act->addDefaultInputMapping("JOY_B");
+	act->addDefaultInputMapping("SPACE");
+	gameplayKeymap->addAction(act);
+
+	act = new Action("SKIPVIDEO", _("Skip cutscene"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpblActionCutsceneSkip);
+	act->addDefaultInputMapping("ESCAPE");
+	act->addDefaultInputMapping("RETURN");
+	act->addDefaultInputMapping("KP_ENTER");
+	act->addDefaultInputMapping("SPACE");
+	act->addDefaultInputMapping("JOY_Y");
+	gameplayKeymap->addAction(act);
+
+	act = new Action("SKIPDLG", _("Skip dialogue"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionDialogueSkip);
+	act->addDefaultInputMapping("RETURN");
+	act->addDefaultInputMapping("KP_ENTER");
+	act->addDefaultInputMapping("JOY_X");
+	gameplayKeymap->addAction(act);
+
+	act = new Action("KIAOPTS", _("Game Options"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionToggleKiaOptions);
+	act->addDefaultInputMapping("ESCAPE");
+	act->addDefaultInputMapping("JOY_Y");
+	commonKeymap->addAction(act);
+
+	act = new Action("KIADB", _("Open KIA Database"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKiaDatabase);
+	act->addDefaultInputMapping("TAB");
+	act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
+	gameplayKeymap->addAction(act);
+
+	act = new Action("KIASCROLLUP", _("Scroll Up"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionScrollUp);
+	act->addDefaultInputMapping("MOUSE_WHEEL_UP");
+	act->addDefaultInputMapping("JOY_UP");
+	kiaOnlyKeymap->addAction(act);
+
+	act = new Action("KIASCROLLDOWN", _("Scroll Down"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionScrollDown);
+	act->addDefaultInputMapping("MOUSE_WHEEL_DOWN");
+	act->addDefaultInputMapping("JOY_DOWN");
+	kiaOnlyKeymap->addAction(act);
+
+	act = new Action("KIAHLP", _("Help"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKIATabHelp);
+	act->addDefaultInputMapping("F1");
+	commonKeymap->addAction(act);
+
+	act = new Action("KIASAVE", _("Save Game"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKIATabSaveGame);
+	act->addDefaultInputMapping("F2");
+	commonKeymap->addAction(act);
+
+	act = new Action("KIALOAD", _("Load Game"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKIATabLoadGame);
+	act->addDefaultInputMapping("F3");
+	commonKeymap->addAction(act);
+
+	act = new Action("KIACRIMES", _("Crime Scene Database"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKIATabCrimeSceneDatabase);
+	act->addDefaultInputMapping("F4");
+	commonKeymap->addAction(act);
+
+	act = new Action("KIASUSPECTS", _("Suspect Database"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKIATabSuspectDatabase);
+	act->addDefaultInputMapping("F5");
+	commonKeymap->addAction(act);
+
+	act = new Action("KIACLUES", _("Clue Database"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKIATabClueDatabase);
+	act->addDefaultInputMapping("F6");
+	commonKeymap->addAction(act);
+
+	act = new Action("KIAQUIT", _("Quit Game"));
+	act->setCustomEngineActionEvent(BladeRunnerEngine::kMpActionOpenKIATabQuitGame);
+	act->addDefaultInputMapping("F10");
+	commonKeymap->addAction(act);
+
+	KeymapArray keymaps(3);
+	keymaps[0] = commonKeymap;
+	keymaps[1] = gameplayKeymap;
+	keymaps[2] = kiaOnlyKeymap;
+
+	return keymaps;
+}
+
 SaveStateList BladeRunnerMetaEngine::listSaves(const char *target) const {
 	return BladeRunner::SaveFileManager::list(this, target);
 }
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index c573bedf226..48cbe73702d 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -56,6 +56,8 @@
 #include "common/str.h"
 #include "common/keyboard.h"
 #include "common/debug.h"
+#include "backends/keymapper/keymap.h"
+#include "backends/keymapper/keymapper.h"
 
 #include "graphics/scaler.h"
 
@@ -154,6 +156,18 @@ void KIA::openLastOpened() {
 }
 
 void KIA::open(KIASections sectionId) {
+	if (_vm->getEventManager()->getKeymapper() != nullptr) {
+		if ( _vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId) != nullptr) {
+			// When disabling a keymap, make sure all their active events in the _activeCustomEvents array
+			// are cleared, because as they won't get an explicit "EVENT_CUSTOM_ENGINE_ACTION_END" event.
+			_vm->cleanupPendingRepeatingEvents(BladeRunnerEngine::kGameplayKeymapId);
+			_vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId)->setEnabled(false);
+		}
+
+		if (_vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kKiaKeymapId) != nullptr)
+			_vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kKiaKeymapId)->setEnabled(true);
+	}
+
 	if (_currentSectionId == sectionId) {
 		return;
 	}
@@ -476,31 +490,56 @@ void KIA::handleKeyDown(const Common::KeyState &kbd) {
 		}
 	}
 
-	switch (kbd.keycode) {
-	case Common::KEYCODE_ESCAPE:
+	if (_currentSection) {
+		_currentSection->handleKeyDown(kbd);
+	}
+
+	if (_currentSection && _currentSection->_scheduledSwitch) {
+		open(kKIASectionNone);
+	}
+}
+
+void KIA::handleCustomEventStop(const Common::Event &evt) {
+	if (!isOpen()) {
+		return;
+	}
+
+	if (_currentSection) {
+		_currentSection->handleCustomEventStop(evt);
+	}
+}
+
+void KIA::handleCustomEventStart(const Common::Event &evt) {
+	if (!isOpen()) {
+		return;
+	}
+
+	switch ((BladeRunnerEngine::BladeRunnerEngineMappableAction)evt.customType) {
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionToggleKiaOptions:
 		if (!_forceOpen) {
 			open(kKIASectionNone);
 		}
 		break;
 
-	case Common::KEYCODE_F1:
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionOpenKIATabHelp:
 		open(kKIASectionHelp);
 		break;
 
-	case Common::KEYCODE_F2:
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionOpenKIATabSaveGame:
 		if (!_forceOpen) {
 			open(kKIASectionSave);
 		}
 		break;
-	case Common::KEYCODE_F3:
+
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionOpenKIATabLoadGame:
 		open(kKIASectionLoad);
 		break;
 
-	case Common::KEYCODE_F10:
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionOpenKIATabQuitGame:
 		open(kKIASectionQuit);
 		break;
 
-	case Common::KEYCODE_F4:
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionOpenKIATabCrimeSceneDatabase:
 		if (_currentSectionId != kKIASectionCrimes) {
 			if (!_forceOpen) {
 				open(kKIASectionCrimes);
@@ -510,7 +549,7 @@ void KIA::handleKeyDown(const Common::KeyState &kbd) {
 		}
 		break;
 
-	case Common::KEYCODE_F5:
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionOpenKIATabSuspectDatabase:
 		if (_currentSectionId != kKIASectionSuspects) {
 			if (!_forceOpen) {
 				open(kKIASectionSuspects);
@@ -520,7 +559,7 @@ void KIA::handleKeyDown(const Common::KeyState &kbd) {
 		}
 		break;
 
-	case Common::KEYCODE_F6:
+	case BladeRunnerEngine::BladeRunnerEngineMappableAction::kMpActionOpenKIATabClueDatabase:
 		if (_currentSectionId != kKIASectionClues) {
 			if (!_forceOpen) {
 				open(kKIASectionClues);
@@ -532,7 +571,7 @@ void KIA::handleKeyDown(const Common::KeyState &kbd) {
 
 	default:
 		if (_currentSection) {
-			_currentSection->handleKeyDown(kbd);
+			_currentSection->handleCustomEventStart(evt);
 		}
 		break;
 	}
@@ -759,6 +798,18 @@ void KIA::init() {
 }
 
 void KIA::unload() {
+	if (_vm->getEventManager()->getKeymapper() != nullptr) {
+		if ( _vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId) != nullptr)
+			_vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kGameplayKeymapId)->setEnabled(true);
+
+		if (_vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kKiaKeymapId) != nullptr) {
+			// When disabling a keymap, make sure all their active events in the _activeCustomEvents array
+			// are cleared, because as they won't get an explicit "EVENT_CUSTOM_ENGINE_ACTION_END" event.
+			_vm->cleanupPendingRepeatingEvents(BladeRunnerEngine::kKiaKeymapId);
+			_vm->getEventManager()->getKeymapper()->getKeymap(BladeRunnerEngine::kKiaKeymapId)->setEnabled(false);
+		}
+	}
+
 	_thumbnail.free();
 
 	if (!isOpen()) {
diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h
index f98bcd54d6f..51b10368cb8 100644
--- a/engines/bladerunner/ui/kia.h
+++ b/engines/bladerunner/ui/kia.h
@@ -28,6 +28,7 @@
 
 namespace Common {
 struct KeyState;
+struct Event;
 }
 
 namespace BladeRunner {
@@ -143,6 +144,8 @@ public:
 	void handleMouseScroll(int mouseX, int mouseY, int direction); // Added by ScummVM team
 	void handleKeyUp(const Common::KeyState &kbd);
 	void handleKeyDown(const Common::KeyState &kbd);
+	void handleCustomEventStop(const Common::Event &evt);
+	void handleCustomEventStart(const Common::Event &evt);
 
 	void playerReset();
 	void playActorDialogue(int actorId, int sentenceId);
diff --git a/engines/bladerunner/ui/kia_section_base.h b/engines/bladerunner/ui/kia_section_base.h
index 6751f13cdfc..6117eaf4370 100644
--- a/engines/bladerunner/ui/kia_section_base.h
+++ b/engines/bladerunner/ui/kia_section_base.h
@@ -26,6 +26,7 @@
 
 namespace Common {
 struct KeyState;
+struct Event;
 }
 
 namespace Graphics {
@@ -53,6 +54,10 @@ public:
 
 	virtual void handleKeyUp(const Common::KeyState &kbd) {}
 	virtual void handleKeyDown(const Common::KeyState &kbd) {}
+
+	virtual void handleCustomEventStart(const Common::Event &evt) {}
+	virtual void handleCustomEventStop(const Common::Event &evt) {}
+
 	virtual void handleMouseMove(int mouseX, int mouseY) {}
 	virtual void handleMouseDown(bool mainButton) {}
 	virtual void handleMouseUp(bool mainButton) {}
diff --git a/engines/bladerunner/ui/kia_section_save.cpp b/engines/bladerunner/ui/kia_section_save.cpp
index 168a9c26d34..c5893d4cbc7 100644
--- a/engines/bladerunner/ui/kia_section_save.cpp
+++ b/engines/bladerunner/ui/kia_section_save.cpp
@@ -225,6 +225,22 @@ void KIASectionSave::draw(Graphics::Surface &surface) {
 	_buttons->drawTooltip(surface, _mouseX, _mouseY);
 }
 
+bool KIASectionSave::isKeyConfirmModalDialogue(const Common::KeyState &kbd) {
+	if (kbd.keycode == Common::KEYCODE_RETURN || kbd.keycode == Common::KEYCODE_KP_ENTER) {
+		return true;
+	}
+	return false;
+}
+
+bool KIASectionSave::isKeyRequestDeleteEntry(const Common::KeyState &kbd) {
+	if (_selectedLineId != _newSaveLineId
+		    && (    kbd.keycode == Common::KEYCODE_DELETE
+		        || (kbd.keycode == Common::KEYCODE_KP_PERIOD && !(kbd.flags & Common::KBD_NUM)))) {
+		return true;
+	}
+	return false;
+}
+
 void KIASectionSave::handleKeyUp(const Common::KeyState &kbd) {
 	if (_state == kStateNormal) {
 		_uiContainer->handleKeyUp(kbd);
@@ -234,19 +250,17 @@ void KIASectionSave::handleKeyUp(const Common::KeyState &kbd) {
 void KIASectionSave::handleKeyDown(const Common::KeyState &kbd) {
 	if (_state == kStateNormal) {
 		// Delete a saved game entry either with Delete key or numpad's (keypad's) Del key (when Num Lock Off)
-		if (_selectedLineId != _newSaveLineId
-		     && (    kbd.keycode == Common::KEYCODE_DELETE
-		         || (kbd.keycode == Common::KEYCODE_KP_PERIOD && !(kbd.flags & Common::KBD_NUM)))) {
+		if (isKeyRequestDeleteEntry(kbd)) {
 			changeState(kStateDelete);
 		}
 		_uiContainer->handleKeyDown(kbd);
 	} else if (_state == kStateOverwrite) {
-		if (kbd.keycode == Common::KEYCODE_RETURN || kbd.keycode == Common::KEYCODE_KP_ENTER) {
+		if (isKeyConfirmModalDialogue(kbd)) {
 			save();
 			changeState(kStateNormal);
 		}
 	} else if (_state == kStateDelete) {
-		if (kbd.keycode == Common::KEYCODE_RETURN || kbd.keycode == Common::KEYCODE_KP_ENTER) {
+		if (isKeyConfirmModalDialogue(kbd)) {
 			deleteSave();
 			changeState(kStateNormal);
 		}
diff --git a/engines/bladerunner/ui/kia_section_save.h b/engines/bladerunner/ui/kia_section_save.h
index ba5978b89f2..77a71039ef2 100644
--- a/engines/bladerunner/ui/kia_section_save.h
+++ b/engines/bladerunner/ui/kia_section_save.h
@@ -93,6 +93,9 @@ private:
 	void changeState(State state);
 	void save();
 	void deleteSave();
+
+	bool isKeyConfirmModalDialogue(const Common::KeyState &kbd);
+	bool isKeyRequestDeleteEntry(const Common::KeyState &kbd);
 };
 
 } // End of namespace BladeRunner
diff --git a/engines/bladerunner/ui/scores.cpp b/engines/bladerunner/ui/scores.cpp
index 5fed6df71c8..674a54f6a94 100644
--- a/engines/bladerunner/ui/scores.cpp
+++ b/engines/bladerunner/ui/scores.cpp
@@ -105,6 +105,10 @@ void Scores::set(int index, int value) {
 	_lastScoreValue = value;
 }
 
+void Scores::handleCustomEventStart(const Common::Event &evt) {
+	close();
+}
+
 void Scores::handleKeyDown(const Common::KeyState &kbd) {
 	close();
 }
diff --git a/engines/bladerunner/ui/scores.h b/engines/bladerunner/ui/scores.h
index 2d353832663..8430545beba 100644
--- a/engines/bladerunner/ui/scores.h
+++ b/engines/bladerunner/ui/scores.h
@@ -26,6 +26,7 @@
 
 namespace Common {
 struct KeyState;
+struct Event;
 }
 
 namespace BladeRunner {
@@ -64,6 +65,7 @@ public:
 	int query(int index) { return _scores[index]; }
 	void set(int index, int value);
 
+	void handleCustomEventStart(const Common::Event &evt);
 	void handleKeyDown(const Common::KeyState &kbd);
 	int handleMouseUp(int x, int y);
 	int handleMouseDown(int x, int y);




More information about the Scummvm-git-logs mailing list