diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp
index 4924eb762..e95953365 100644
--- a/source/blood/src/blood.cpp
+++ b/source/blood/src/blood.cpp
@@ -708,7 +708,6 @@ static const char* actions[] = {
     "Map_Follow_Mode",
     "See_Coop_View",
     "Mouse_Aiming",
-    "Toggle_Crosshair",
     "Dpad_Select",
     "Dpad_Aiming",
     "Third_Person_View",
diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h
index 0e6013c40..5bb7d6f28 100644
--- a/source/blood/src/blood.h
+++ b/source/blood/src/blood.h
@@ -59,7 +59,6 @@ enum GameFunction_t
 	gamefunc_Map_Follow_Mode,
 	gamefunc_See_Coop_View,
 	gamefunc_Mouse_Aiming,
-	gamefunc_Toggle_Crosshair,
 	gamefunc_Dpad_Select,
 	gamefunc_Dpad_Aiming,
 	gamefunc_Third_Person_View,
diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 7931511d7..ae26ef6d3 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -238,12 +238,6 @@ void ctrlGetInput(void)
         }
     }
 
-    if (buttonMap.ButtonDown(gamefunc_Toggle_Crosshair))
-    {
-        buttonMap.ClearButton(gamefunc_Toggle_Crosshair);
-        cl_crosshair = !cl_crosshair;
-    }
-
     if (gPlayer[myconnectindex].nextWeapon == 0)
     {
     }
diff --git a/source/common/console/c_cvars.cpp b/source/common/console/c_cvars.cpp
index fe951d65c..111bad99c 100644
--- a/source/common/console/c_cvars.cpp
+++ b/source/common/console/c_cvars.cpp
@@ -1519,7 +1519,12 @@ CCMD (toggle)
 			val = var->GetGenericRep (CVAR_Bool);
 			val.Bool = !val.Bool;
 			var->SetGenericRep (val, CVAR_Bool);
-			Printf ("\"%s\" = \"%s\"\n", var->GetName(),
+			auto msg = var->GetToggleMessage(val.Bool);
+			if (msg.IsNotEmpty())
+			{
+				Printf(PRINT_NOTIFY, "%s\n", msg.GetChars());
+			}
+			else Printf ("\"%s\" = \"%s\"\n", var->GetName(),
 				val.Bool ? "true" : "false");
 		}
 	}
diff --git a/source/common/console/c_cvars.h b/source/common/console/c_cvars.h
index 6218cb956..567475c70 100644
--- a/source/common/console/c_cvars.h
+++ b/source/common/console/c_cvars.h
@@ -176,6 +176,13 @@ public:
 	static void ListVars (const char *filter, bool plain);
 	
 	const FString &GetDescription() const { return Description; };
+	const FString& GetToggleMessage(int which) { return ToggleMessages[which]; }
+	void SetToggleMessages(const char* on, const char* off)
+	{
+		ToggleMessages[0] = off;
+		ToggleMessages[1] = on;
+	}
+
 
 protected:
 	virtual void DoSet (UCVarValue value, ECVarType type) = 0;
@@ -192,13 +199,13 @@ protected:
 	FString VarName;
 	FString SafeValue;
 	FString Description;
+	FString ToggleMessages[2];
 	uint32_t Flags;
 	bool inCallback = false;
 
 private:
 	FBaseCVar (const FBaseCVar &var) = delete;
 	FBaseCVar (const char *name, uint32_t flags);
-
 	void (*m_Callback)(FBaseCVar &);
 	FBaseCVar *m_Next;
 
diff --git a/source/common/engine/printf.h b/source/common/engine/printf.h
index 70439f7a1..35efdd5fd 100644
--- a/source/common/engine/printf.h
+++ b/source/common/engine/printf.h
@@ -61,7 +61,7 @@ enum
 	PRINT_TYPES = 1023,		// Bitmask.
 	PRINT_NONOTIFY = 1024,	// Flag - do not add to notify buffer
 	PRINT_NOLOG = 2048,		// Flag - do not print to log file
-	PRINT_NOTIFY = 4096,	// Flag - add to notify buffer
+	PRINT_NOTIFY = 4096,	// Flag - add to game-native notify display - messages without this only go to the generic notification buffer.
 };
 
 enum
diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp
index c207e4840..0092e4c7d 100644
--- a/source/exhumed/src/exhumed.cpp
+++ b/source/exhumed/src/exhumed.cpp
@@ -58,6 +58,7 @@ BEGIN_PS_NS
 
 void uploadCinemaPalettes();
 int32_t registerosdcommands(void);
+void registerinputcommands();
 void InitFonts();
 
 int htimer = 0;
@@ -603,33 +604,14 @@ static const char* actions[] =
     "Strafe_Right",
     "Aim_Up",
     "Aim_Down",
-    "Weapon_1",
-    "Weapon_2",
-    "Weapon_3",
-    "Weapon_4",
-    "Weapon_5",
-    "Weapon_6",
-    "Weapon_7",
-    "Weapon_8",
-    "Weapon_9",
-    "Weapon_10",
-    "Inventory",
-    "Inventory_Left",
-    "Inventory_Right",
-    "TurnAround",
     "SendMessage",
     "Map",
     "Shrink_Screen",
     "Enlarge_Screen",
-    "Center_View",
-    "Holster_Weapon",
     "Show_Opponents_Weapon",
     "Map_Follow_Mode",
     "See_Coop_View",
     "Mouse_Aiming",
-    "Toggle_Crosshair",
-    "Next_Weapon",
-    "Previous_Weapon",
     "Dpad_Select",
     "Dpad_Aiming",
     "Last_Weapon",
@@ -666,7 +648,7 @@ void InitGame()
 
     SetCheats(excheats, countof(excheats));
     registerosdcommands();
-
+    registerinputcommands();
     if (nNetPlayerCount == -1)
     {
         nNetPlayerCount = nCfgNetPlayers - 1;
diff --git a/source/exhumed/src/exhumed.h b/source/exhumed/src/exhumed.h
index 818d1f2fe..a9ea3bf5b 100644
--- a/source/exhumed/src/exhumed.h
+++ b/source/exhumed/src/exhumed.h
@@ -58,33 +58,14 @@ enum GameFunction_t
 	gamefunc_Strafe_Right,
 	gamefunc_Aim_Up,
 	gamefunc_Aim_Down,
-	gamefunc_Weapon_1,
-	gamefunc_Weapon_2,
-	gamefunc_Weapon_3,
-	gamefunc_Weapon_4,
-	gamefunc_Weapon_5,
-	gamefunc_Weapon_6,
-	gamefunc_Weapon_7,
-	gamefunc_Weapon_8,
-	gamefunc_Weapon_9,
-	gamefunc_Weapon_10,
-	gamefunc_Inventory,
-	gamefunc_Inventory_Left,
-	gamefunc_Inventory_Right,
-	gamefunc_TurnAround,
 	gamefunc_SendMessage,
 	gamefunc_Map,
 	gamefunc_Shrink_Screen,
 	gamefunc_Enlarge_Screen,
-	gamefunc_Center_View,
-	gamefunc_Holster_Weapon,
 	gamefunc_Show_Opponents_Weapon,
 	gamefunc_Map_Follow_Mode,
 	gamefunc_See_Coop_View,
 	gamefunc_Mouse_Aiming,
-	gamefunc_Toggle_Crosshair,
-	gamefunc_Next_Weapon,
-	gamefunc_Previous_Weapon,
 	gamefunc_Dpad_Select,
 	gamefunc_Dpad_Aiming,
 	gamefunc_Last_Weapon,
@@ -321,6 +302,7 @@ struct GameInterface : ::GameInterface
     bool SaveGame(FSaveGameNode* sv) override;
     bool CanSave() override;
     ReservedSpace GetReservedScreenSpace(int viewsize) override { return { 0, 24 }; }
+	void clearlocalinputstate();
 
     FString statFPS() override;
 	::GameStats getStats() override;
diff --git a/source/exhumed/src/input.cpp b/source/exhumed/src/input.cpp
index be241025b..4a079a066 100644
--- a/source/exhumed/src/input.cpp
+++ b/source/exhumed/src/input.cpp
@@ -27,6 +27,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_PS_NS
 
+int WeaponToSend, BitsToSend;
+
 int nNetMoves = 0;
 
 short nInputStack = 0;
@@ -76,18 +78,15 @@ void ClearSpaceBar(short nPlayer)
 
 void GetLocalInput()
 {
-    int i;
-    for (i = 6; i >= 0; i--)
-    {
-        if (buttonMap.ButtonDown(gamefunc_Weapon_1+i))
-            break;
-    }
-    i++;
+    int i = WeaponToSend;
+    if (WeaponToSend == PlayerList[nLocalPlayer].nCurrentWeapon)
+        WeaponToSend = 0;
 
     if (PlayerList[nLocalPlayer].nHealth)
     {
         lLocalButtons = (buttonMap.ButtonDown(gamefunc_Crouch) << 4) | (buttonMap.ButtonDown(gamefunc_Fire) << 3)
-                      | (buttonMap.ButtonDown(gamefunc_Jump)<<0) | (i<<13);
+            | (buttonMap.ButtonDown(gamefunc_Jump) << 0);
+        lLocalCodes |= (i << 13);
     }
     else
     {
@@ -210,27 +209,57 @@ void CheckKeys2()
         lMapZoom = clamp(lMapZoom, 48, 2048);
     }
 
-    if (PlayerList[nLocalPlayer].nHealth > 0)
+    if (PlayerList[nLocalPlayer].nHealth <= 0)
     {
-        if (buttonMap.ButtonDown(gamefunc_Inventory_Left))
-        {
-            SetPrevItem(nLocalPlayer);
-            buttonMap.ClearButton(gamefunc_Inventory_Left);
-        }
-        if (buttonMap.ButtonDown(gamefunc_Inventory_Right))
-        {
-            SetNextItem(nLocalPlayer);
-            buttonMap.ClearButton(gamefunc_Inventory_Right);
-        }
-        if (buttonMap.ButtonDown(gamefunc_Inventory))
-        {
-            UseCurItem(nLocalPlayer);
-            buttonMap.ClearButton(gamefunc_Inventory);
-        }
-    }
-    else {
         SetAirFrame();
     }
 }
 
+
+//---------------------------------------------------------------------------
+//
+// CCMD based input. The basics are from Randi's ZDuke but this uses dynamic
+// registration to only have the commands active when this game module runs.
+//
+//---------------------------------------------------------------------------
+
+static int ccmd_slot(CCmdFuncPtr parm)
+{
+    if (parm->numparms != 1) return CCMD_SHOWHELP;
+
+    auto slot = atoi(parm->parms[0]);
+    if (slot >= 1 && slot <= 7)
+    {
+        WeaponToSend = slot;
+        return CCMD_OK;
+    }
+    return CCMD_SHOWHELP;
+}
+
+int ccmd_centerview(CCmdFuncPtr parm);
+
+
+void registerinputcommands()
+{
+    C_RegisterFunction("slot", "slot <weaponslot>: select a weapon from the given slot (1-10)", ccmd_slot);
+    //C_RegisterFunction("pause", nullptr, [](CCmdFuncPtr)->int { BitsToSend |= SKB_PAUSE; return CCMD_OK; });
+    C_RegisterFunction("centerview", nullptr, ccmd_centerview);
+    C_RegisterFunction("invprev", nullptr, [](CCmdFuncPtr)->int { if (PlayerList[nLocalPlayer].nHealth > 0) SetPrevItem(nLocalPlayer); return CCMD_OK; });
+    C_RegisterFunction("invnext", nullptr, [](CCmdFuncPtr)->int { if (PlayerList[nLocalPlayer].nHealth > 0) SetNextItem(nLocalPlayer); return CCMD_OK; });
+    C_RegisterFunction("invuse", nullptr, [](CCmdFuncPtr)->int { if (PlayerList[nLocalPlayer].nHealth > 0) UseCurItem(nLocalPlayer); return CCMD_OK; });
+    // todo: 
+    //C_RegisterFunction("weapprev", nullptr, [](CCmdFuncPtr)->int { WeaponToSend = 11; return CCMD_OK; });
+    //C_RegisterFunction("weapnext", nullptr, [](CCmdFuncPtr)->int { WeaponToSend = 12; return CCMD_OK; });
+    //C_RegisterFunction("turnaround", nullptr, [](CCmdFuncPtr)->int { BitsToSend |= SKB_TURNAROUND; return CCMD_OK; });
+
+}
+
+// This is called from ImputState::ClearAllInput and resets all static state being used here.
+void GameInterface::clearlocalinputstate()
+{
+    WeaponToSend = 0;
+    BitsToSend = 0;
+
+}
+
   END_PS_NS
diff --git a/source/exhumed/src/player.cpp b/source/exhumed/src/player.cpp
index 9e5b84e75..6f13ee940 100644
--- a/source/exhumed/src/player.cpp
+++ b/source/exhumed/src/player.cpp
@@ -292,13 +292,6 @@ void PlayerInterruptKeys()
         bPlayerPan = true;
         nDestVertPan[nLocalPlayer] = PlayerList[nLocalPlayer].q16horiz;
     }
-    else if (buttonMap.ButtonDown(gamefunc_Center_View))
-    {
-        bLockPan = false;
-        bPlayerPan = false;
-        PlayerList[nLocalPlayer].q16horiz = fix16_from_int(92);
-        nDestVertPan[nLocalPlayer] = fix16_from_int(92);
-    }
 
     // loc_1C048:
     if (totalvel[nLocalPlayer] > 20) {
@@ -3091,6 +3084,16 @@ loc_1BD2E:
     }
 }
 
+int ccmd_centerview(CCmdFuncPtr parm)
+{
+    return CCMD_OK;
+    bLockPan = false;
+    bPlayerPan = false;
+    PlayerList[nLocalPlayer].q16horiz = fix16_from_int(92);
+    nDestVertPan[nLocalPlayer] = fix16_from_int(92);
+    return CCMD_OK;
+}
+
 
 static SavegameHelper sgh("player",
     SV(lPlayerXVel),
diff --git a/source/exhumed/src/status.cpp b/source/exhumed/src/status.cpp
index b1a5b55d3..80e3ff03d 100644
--- a/source/exhumed/src/status.cpp
+++ b/source/exhumed/src/status.cpp
@@ -58,7 +58,6 @@ static short nAnimsFree = 0;
 
 short statusmask[MAXXDIM];
 
-short message_timer = 0;
 char message_text[80];
 int magicperline;
 int airperline;
@@ -147,7 +146,6 @@ void InitStatus()
     nItemSeq = -1;
     nAnimsFree = kMaxStatusAnims;
     statusx = xdim - 320;
-    message_timer = 0;
     statusy = ydim - 200;
 }
 
@@ -464,15 +462,6 @@ void MoveStatus()
         }
     }
 
-    if (message_timer)
-    {
-        message_timer -= 4;
-        if (message_timer <= 0)
-        {
-            message_timer = 0;
-        }
-    }
-
     nHealthFrame++;
     if (nHealthFrame >= nHealthFrames) {
         nHealthFrame = 0;
@@ -908,7 +897,6 @@ void DrawStatusBar()
 static SavegameHelper sgh("status",
     SV(nMaskY),
     SV(nAnimsFree),
-    SV(message_timer),
     SV(magicperline),
     SV(airperline),
     SV(healthperline),
diff --git a/source/games/duke/src/constants.h b/source/games/duke/src/constants.h
index d2c79dc0b..0e1b87022 100644
--- a/source/games/duke/src/constants.h
+++ b/source/games/duke/src/constants.h
@@ -31,7 +31,6 @@ enum GameFunction_t
 	gamefunc_Map_Follow_Mode, // CCMD
 	gamefunc_See_Coop_View, // CCMD
 	gamefunc_Mouse_Aiming, // CCMD
-	gamefunc_Toggle_Crosshair, // CCMD
 	gamefunc_Quick_Kick,
 	gamefunc_Dpad_Select,
 	gamefunc_Dpad_Aiming,
diff --git a/source/games/duke/src/game.cpp b/source/games/duke/src/game.cpp
index 3dbcc0a36..f53746dd1 100644
--- a/source/games/duke/src/game.cpp
+++ b/source/games/duke/src/game.cpp
@@ -262,7 +262,6 @@ static void SetupGameButtons()
 		"Map_Follow_Mode",
 		"See_Coop_View",
 		"Mouse_Aiming",
-		"Toggle_Crosshair",
 		"Quick_Kick",
 		"Dpad_Select",
 		"Dpad_Aiming",
diff --git a/source/games/duke/src/gamedef.cpp b/source/games/duke/src/gamedef.cpp
index eecfb78b4..210a548bc 100644
--- a/source/games/duke/src/gamedef.cpp
+++ b/source/games/duke/src/gamedef.cpp
@@ -1850,6 +1850,9 @@ void ConCompiler::compilecon(const char *filenam)
 		Printf(TEXTCOLOR_ORANGE "Found %d warning(s), %d error(s).\n", warningcount, errorcount);
 	if (errorcount > 0) I_FatalError("Failed to compile %s", filenam);
 
+
+	// Install the crosshair toggle messages in the CVAR.
+	cl_crosshair.SetToggleMessages(quoteMgr.GetRawQuote(QUOTE_CROSSHAIR_OFF), quoteMgr.GetRawQuote(QUOTE_CROSSHAIR_OFF-1));
 }
 
 //==========================================================================
diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp
index 17deccf69..000875374 100644
--- a/source/games/duke/src/input.cpp
+++ b/source/games/duke/src/input.cpp
@@ -117,13 +117,6 @@ void nonsharedkeys(void)
 		FTA(QUOTE_WEAPON_MODE_OFF - ud.showweapons, &ps[screenpeek]);
 	}
 
-	if (buttonMap.ButtonDown(gamefunc_Toggle_Crosshair))
-	{
-		buttonMap.ClearButton(gamefunc_Toggle_Crosshair);
-		cl_crosshair = !cl_crosshair;
-		FTA(QUOTE_CROSSHAIR_OFF - cl_crosshair, &ps[screenpeek]);
-	}
-
 	if (ud.overhead_on && buttonMap.ButtonDown(gamefunc_Map_Follow_Mode))
 	{
 		buttonMap.ClearButton(gamefunc_Map_Follow_Mode);
diff --git a/wadsrc/static/engine/origbinds.txt b/wadsrc/static/engine/origbinds.txt
index 791e3ce74..6847730d2 100644
--- a/wadsrc/static/engine/origbinds.txt
+++ b/wadsrc/static/engine/origbinds.txt
@@ -16,5 +16,5 @@ KP5 "centerview"
 KP- "+Shrink_Screen"
 KP+ "+Enlarge_Screen"
 U "+Mouse_Aiming"
-I "+Toggle_Crosshair"
+I "toggle cl_crosshair"
 CapsLock "toggle cl_autorun"
diff --git a/wadsrc/static/filter/blood/engine/defbinds.txt b/wadsrc/static/filter/blood/engine/defbinds.txt
index c8b1c3a1b..3e57e634e 100644
--- a/wadsrc/static/filter/blood/engine/defbinds.txt
+++ b/wadsrc/static/filter/blood/engine/defbinds.txt
@@ -1,5 +1,5 @@
 U "+Mouse_Aiming"
-I "+Toggle_Crosshair"
+I "toggle cl_crosshair"
 Scroll "+Holster_Weapon"
 B "BeastVision"
 C "CrystalBall"
diff --git a/wadsrc/static/filter/exhumed/engine/leftbinds.txt b/wadsrc/static/filter/exhumed/engine/leftbinds.txt
index 93d33ea04..028bf5337 100644
--- a/wadsrc/static/filter/exhumed/engine/leftbinds.txt
+++ b/wadsrc/static/filter/exhumed/engine/leftbinds.txt
@@ -47,7 +47,7 @@ Tab "+Map"
 F "+Map_Follow_Mode"
 K "+See_Coop_View"
 U "+Mouse_Aiming"
-I "+Toggle_Crosshair"
+I "toggle cl_crosshair"
 //R "+Steroids"
 //Q "+Quick_Kick"
 ' "+Next_Weapon"
diff --git a/wadsrc/static/filter/exhumed/engine/origbinds.txt b/wadsrc/static/filter/exhumed/engine/origbinds.txt
index 672d16330..6248bc627 100644
--- a/wadsrc/static/filter/exhumed/engine/origbinds.txt
+++ b/wadsrc/static/filter/exhumed/engine/origbinds.txt
@@ -54,7 +54,7 @@ KP5 "+Center_View"
 F "+Map_Follow_Mode"
 K "+See_Coop_View"
 U "+Mouse_Aiming"
-I "+Toggle_Crosshair"
+I "toggle cl_crosshair"
 //R "+Steroids"
 //` "+Quick_Kick"
 ' "+Next_Weapon"
diff --git a/wadsrc/static/filter/shadowwarrior/engine/defbinds.txt b/wadsrc/static/filter/shadowwarrior/engine/defbinds.txt
index efbd4d234..e9d6d2d71 100644
--- a/wadsrc/static/filter/shadowwarrior/engine/defbinds.txt
+++ b/wadsrc/static/filter/shadowwarrior/engine/defbinds.txt
@@ -1,5 +1,5 @@
 //
-I "+Toggle_Crosshair"
+I "toggle cl_crosshair"
 Mouse2 "MedKit"
 M "MedKit"
 B "Smoke_Bomb"