[OFFER][GOG] Sin Slayers: Reign of The 8th
Key expires in 38h from now. First come, first served.
[GAME] [Other] Tom Clancy's Ghost Recon Future Soldier
ubisoft.comWeird behavior when remapping a modified mouse button to a non-modified key
Someone just asked me to remap Control + mouse wheel to PgUp/PgDn for scrolling the dev console in a game called Dungeon Siege.
So I wrote this very simple script, yet it's not working properly.
#Requires AutoHotkey v2.0
#SingleInstance
^WheelUp::PgUp
^WheelDown::PgDn
The documentation says
Conversely, any modifiers included on the left side but not the right side are automatically
released when the key is sent. For example, the following two lines would produce a lowercase
"b" when you press either Shift+A or Ctrl+A:
A::b
^a::b
When testing both in-game, as well as in Chrome, notepad++ and VS Code (I'm using AHK 2.0.23), once you press Control + mouse wheel, it does send PgUp/PgDn but then regular mouse wheels don't work anymore (they're important because they control camera zoom), even though my program to monitor key input shows Control isn't pressed anymore (and both Pg keys stay pressed, could be related to mouse wheels only sending down events?).
So I modified the code to
WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::PgUp
^WheelDown::PgDn
which solved the problem, yet when spamming mouse wheels while holding down Control, about 1-5% of the time, there's a Control that slips through and it changes to a different tab in Chrome, notepad++ and VS Code instead of scrolling.
It seems to work fine in-game as I don't think there's anything bound to Control + PgUp/PgDn but it got me curious, so I also tried using these (separately), but the results were the same:
WheelUp::WheelUp
WheelDown::WheelDown
<^WheelUp::PgUp
<^WheelDown::PgDn
WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{PgUp}")
^WheelDown::Send("{PgDn}")
WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{Blind}{PgUp}")
^WheelDown::Send("{Blind}{PgDn}")
WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{Blind^}{PgUp}")
^WheelDown::Send("{Blind^}{PgDn}")
WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{Control up}{PgUp}")
^WheelDown::Send("{Control up}{PgDn}")
LControl & WheelUp::PgUp
LControl & WheelDown::PgDn
Oh, and I also tried switching to SendEvent, that seemed to be worse.
Everything works fine when remapping a key instead of a mouse button though, like this
^q::PgUp
^w::PgDn
What am I doing wrong?
As a bonus, how would you go if you wanted to send multiple unmodified Pg keys (the console doesn't scroll when pressing Control + Pg) every time the hotkey is fired?
How to differentiate a CtrlBreak triggered by Ctrl + ScrollLock and a CtrlBreak triggered by Ctrl + Pause?
I recently started playing Lost Planet 2 and felt once again the need to add a bunch of macros to the game. One of them is a crouch toggle (LControl).
A feature I have in this script is the hotkeys are disabled when the Steam overlay (ScrollLock) is visible so that you can use your computer normally while using the Steam overlay. However, since the user (and me included) may not remember they had a key toggled on, I also wanted to be able to toggle the Steam overlay without having to toggle off hotkeys beforehand, as a QOL.
I'm not gonna post the entire script but a simplified version of it (without reading from a config file).
#Requires AutoHotkey v2.0
#SingleInstance
class ToggleStates
{
static bCrouch := 0
static bSteamOverlay := 0
}
Init()
Init()
{
RegisterHotkeys()
OnExit((*) => ResetAll())
}
Output(p_sMsg := "")
{
OutputDebug(p_sMsg "`n")
}
RegisterHotkeys()
{
global g_sSteamOverlayKey := "ScrollLock"
global g_sToggleCrouchKey := "LControl"
HotIf((*) => !ToggleStates.bSteamOverlay)
Hotkey("~*" g_sToggleCrouchKey " up", ToggleCrouch, "On")
HotIf()
Hotkey("*" g_sSteamOverlayKey " up", ToggleSteamOverlay, "On")
}
ResetAll()
{
SendKeyUp(g_sToggleCrouchKey)
ToggleStates.bCrouch := 0
}
SendKeyDown(p_sKey)
{
Send("{Blind}{" p_sKey " Down}")
}
SendKeyUp(p_sKey)
{
Send("{Blind}{" p_sKey " up}")
}
TapKey(p_sKey)
{
SendKeyDown(p_sKey)
SetTimer(() => SendKeyUp(p_sKey), -25)
}
ToggleCrouch(*)
{
(ToggleStates.bCrouch ^= 1) ? SendKeyDown(g_sToggleCrouchKey) : SendKeyUp(g_sToggleCrouchKey)
}
ToggleSteamOverlay(*)
{
Output(A_ThisFunc "::" A_ThisHotkey)
Output("Steam overlay toggled " ((ToggleStates.bSteamOverlay ^= 1) ? "on" : "off"))
ResetAll()
TapKey(g_sSteamOverlayKey)
}
#HotIf g_sSteamOverlayKey == "Pause" || g_sSteamOverlayKey == "ScrollLock"
^CtrlBreak up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
#HotIf
*F9::KeyHistory()
*F10::ExitApp()
I had a problem where when Crouch was toggled, pressing the Steam overlay hotkey wouldn't do anything anymore, which I didn't understand because I assumed the * would cover it, right? Well, it turns out Ctrl + ScrollLock actually produces CtrlBreak according to the documentation and the key history, so I added the ^CtrlBreak hotkey at the end (yes, the ^ seems to be necessary, otherwise the hotkey doesn't fire for some reason).
Here's a trace of the key history of the above script when tapping ScrollLock (twice to hide the Steam overlay) then tapping Pause, then physically holding LControl and tapping ScrollLock then Pause:
The oldest are listed first. VK=Virtual Key, SC=Scan Code, Elapsed=Seconds since the previous event. Types: h=Hook Hotkey, s=Suppressed (blocked), i=Ignored because it was generated by an AHK script, a=Artificial, #=Disabled via #HotIf, U=Unicode character (SendInput).
VK SC Type Up/Dn Elapsed Key Window
--------------------------------------------------------------------------------------
91 046 s d 11.75 ScrollLock test_v2.ahk - Visual Studio Code
91 046 h u 0.06 ScrollLock
A2 01D i u 0.00 LControl
91 046 i d 0.00 ScrollLock
91 046 i u 0.03 ScrollLock
13 045 d 1.83 Pause
13 045 u 0.08 Pause
A2 01D d 7.28 LControl
03 046 s d 0.31 CtrlBreak
03 046 h u 0.08 CtrlBreak
A2 01D i u 0.00 LControl
91 046 i d 0.00 ScrollLock
91 046 i u 0.03 ScrollLock
03 146 d 0.86 CtrlBreak
03 146 u 0.08 CtrlBreak
A2 01D h u 0.42 LControl
My problem now is that Ctrl + Pause also triggers CtrlBreak and therefore toggles the Steam overlay, which is not what I want because the Steam overlay hotkey was never bound to Pause in the first place.
The documentation also says While Ctrl is held down, ScrollLock produces the key code of CtrlBreak, but can be differentiated from Pause by scan code. but doesn't give an example).
So I tried to add the following, but 046 is shared both by ScrollLock and LControl + ScrollLock so it overrides the original ScrollLock hotkey.
#HotIf g_sSteamOverlayKey == "ScrollLock"
*sc046 up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
#HotIf g_sSteamOverlayKey == "Pause"
*sc146 up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
#HotIf
If I add something like this, AHK doesn't accept the combination of VK and SC outside of a Send it seems.
vk91sc046 up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
vk03sc046 up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
vk03sc146 up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
I must've spent about 2 hours on this and I'm going crazy, the solution must be so simple yet I just can't see it.
So my first question is pretty much like the title, how do I distinguish between ScrollLock, a CtrlBreak produced by Ctrl + ScrollLock and a CtrlBreak produced by Ctrl + Pause?
As a bonus, how would you proceed if you wanted the Steam overlay hotkey (in AHK) to fire only if it matches exactly how it was set in Steam settings (to a limit of one non-modifier key as supporting something like Ctrl + Shift + F + V would be a nightmare), only accounting for modifiers that are physically held down? I guess I'd have to parse all modifier symbols and read all physically pressed modifiers then make sure every single one matches.
[edit]: something like this seems to work in order to address my first question, I'm just not sure if it's the right approach. I removed the CtrlBreak up hotkey.
#HotIf !GetKeyState("Control", "P") && g_sSteamOverlayKey == "ScrollLock"
*sc046 up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
#HotIf !GetKeyState("Control", "P") && g_sSteamOverlayKey == "Pause"
*sc146 up::
{
Output(ThisHotkey)
ToggleSteamOverlay()
}
#HotIf
[OFFER][GOG] Amazon Prime July leftovers (take 2)
Since the first attempt was unsuccessful, I'm reposting this.
Sir Questionnaire (Jul 17, 2026)
Rebel Galaxy (Jul 18, 2026)
Hot Brass (Jul 24, 2026)
These are GOG keys which expire on the dates mentioned above. First come, first served. Please read the rules before posting and for the love of god, no direct messages.
[OFFER][GOG] Amazon Prime July leftovers
Sir Questionnaire (Jul 17, 2026)
Rebel Galaxy (Jul 18, 2026)
Hot Brass (Jul 24, 2026)
These are GOG keys which expire on the dates mentioned above. First come, first served. Please follow the rules, no DMs.
[OFFER][GOG] Pinball Spire (expires in 30min)
Sorry for the short notice. First come, first served. Make sure you respect the sub rules.
Made a couple of QOL macros for Gothic 1 Classic
After playing the game for a couple of hours, I was annoyed by a couple of things nobody had made mods for, so I made a couple of macros to make my life easier.
It features the following hotkeys, that are obviously only active when the game's window is active, but also automatically disabled/stopped when alt-tabbing or bringing up the Steam overlay.
- autobuy (toggle): the game never tells you Shift + LButton allows you to buy stacks of 100 items, and if you want to buy 2000 arrows for instance, that's 20 times you normally have to press Shift + LButton (can also be used to autosell items and use consumables). The clicking speed is customizable. Automatically disabled when manually pressing Shift or LButton.
- autocook (toggle): your character can only cook meat one at a time, therefore if you have let's say 50 pieces of meat to cook, that'd mean sitting in front of your computer for 250s at best, so you can just start the macro while looking at a pan and come back later.
- autojump (toggle): jumping makes your character move faster >!(until you get access to velocity potions mid/late game)!<, so combining it with autorun is how I move around to cover long distances (bonus points if you draw your weapon while going up). Automatically disabled when manually pressing Jump.
- autorun (toggle): I use this all the time since >!there's no fast travel until late game and!< you have to do a lot of back and forth during the entire game. Automatically disabled when manually pressing Forward.
- fast attack (hold): attacking in this game is a chore and you often fail chaining your swings due to clunky controls not always registering your input (or maybe recurrent bad timing?), so I found a way to attack in the fastest way possible, therefore maximizing my DPS. You just need to make sure your weapon is drawn out beforehand. The drawback is you can't parry while doing it so use it sparingly (there are some situations where you can use it all the time but I'm not gonna spoil).
- walk (toggle): the game normally forces you to hold a key to walk.
All keys are configurable through a config file. Setting the optional keys to blank disables them.
Feedback is welcome.
https://github.com/GenesisFR/GothicMacros
If you're ever gonna play this game, I'd highly suggest using the following mods:
- Union with Gothic2_Control=1 in SystemPack.ini (autobuy doesn't work without it)
- GD3D11
[OFFER][GOG] Amazon Prime June leftovers (round 2)
Still have those keys that went unclaimed in my previous post.
Meganoid (Jun 19, 2026)The Pale Beyond (Jun 26, 2026)
Fantasy General (Jun 29, 2026)
Keys expire on the dates mentioned above. First come, first served.
[OFFER][GOG] Amazon Prime June leftovers
King of Retail (Jun 12, 2026)
Meganoid (Jun 19, 2026)Rebel Galaxy Outlaw (Jun 25, 2026)
Neo Cab (Jun 26, 2026)
The Pale Beyond (Jun 26, 2026)
Fantasy General (Jun 29, 2026)
Keys expire on the dates mentioned above. First come, first served.
[OFFER][GOG] Amazon Prime May leftovers (round 2)
Still have those keys that went unclaimed in my previous post.
Veil of Darkness (May 11, 2026)
Gunslugs: Rogue Tactics (May 15, 2026)
Phantasie Memorial Set (May 25, 2026)
Keys expire on the dates mentioned above. First come, first served.
Veil of Darkness (May 11, 2026)
Mahokenshi - The Samurai Deckbuilder (May 14, 2026)
Gunslugs: Rogue Tactics (May 15, 2026)
Phantasie Memorial Set (May 25, 2026)
Keys expire on the dates mentioned above. First come, first served.