Split smart interact shortcut from single Alt+J send
bind: "interact" now resolves to game_binds.interact (Alt+J) only — a single keystroke. The full ISBoxer-style chain (CTM on -> Alt+J -> sleep walk_delay_ms -> CTM off) lives at bind: "smart_interact". Fixes the doubling bug Grok flagged: bind: "interact" previously expanded into the full chain unconditionally, so the loot_manual and interact_hold example maps fired ctm_on/ctm_off twice and produced unwanted sleep delays. - engine.rs: rename shortcut trigger (was: "interact") - examples/profile.yaml: loot now uses smart_interact; loot_manual and interact_hold keep "interact" as a single Alt+J send - docs/MACROS.md: heading + shortcut comment - docs/NOTES.md: trigger name - Add new test: interact_simple_sends_only_alt_j (1-line single-send) - Existing smart shortcut tests renamed to bind: smart_interact - CHANGELOG.md: trigger split note 93/93 cargo test pass; clippy clean.
This commit is contained in:
parent
1ec8d78f71
commit
8eb5346d92
@ -48,3 +48,6 @@
|
|||||||
clippy clean.
|
clippy clean.
|
||||||
- Docs: `docs/MACROS.md` "Smart interact shortcut" section, `docs/NOTES.md`
|
- Docs: `docs/MACROS.md` "Smart interact shortcut" section, `docs/NOTES.md`
|
||||||
callout, `examples/profile.yaml` updated with comments.
|
callout, `examples/profile.yaml` updated with comments.
|
||||||
|
|
||||||
|
> Note: After this change, `bind: "interact"` is a single Alt+J send (game_binds.interact).
|
||||||
|
> The full chain trigger is now `bind: "smart_interact"`. Examples and tests updated.
|
||||||
|
|||||||
@ -78,11 +78,11 @@ Do not mash. A second interact while they are still pathing is how characters ru
|
|||||||
|
|
||||||
If they stop short, raise `walk_delay_ms` (2500–4000), or use `interact.style: auto` (CTM stays on), or `interact.style: hold`.
|
If they stop short, raise `walk_delay_ms` (2500–4000), or use `interact.style: auto` (CTM stays on), or `interact.style: hold`.
|
||||||
|
|
||||||
## Smart interact shortcut (`bind: interact`)
|
## Smart interact shortcut (`bind: smart_interact`)
|
||||||
|
|
||||||
The example profile's `loot` map (Alt+G) and `interact` map (Alt+I) both use a **smart shortcut**: a single user keypress fires the full chain (CTM on → Interact with Target → sleep `walk_delay_ms` → CTM off). It is what ISBoxer did with a "Mapped Key" — you press one key, the multibox software sends the whole chain.
|
The example profile's `loot` map (Alt+G) and `interact` map (Alt+I) both use a **smart shortcut**: a single user keypress fires the full chain (CTM on → Interact with Target → sleep `walk_delay_ms` → CTM off). It is what ISBoxer did with a "Mapped Key" — you press one key, the multibox software sends the whole chain.
|
||||||
|
|
||||||
In enBoxer the shortcut is `bind: interact` in a step:
|
In enBoxer the shortcut is `bind: smart_interact` in a step:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: loot
|
- name: loot
|
||||||
@ -90,7 +90,7 @@ In enBoxer the shortcut is `bind: interact` in a step:
|
|||||||
steps:
|
steps:
|
||||||
- bind: assist
|
- bind: assist
|
||||||
target: others
|
target: others
|
||||||
- bind: interact # <-- smart shortcut
|
- bind: smart_interact # <-- smart shortcut
|
||||||
target: others
|
target: others
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -40,7 +40,7 @@ Visible-pixel capture: `grim` (default). Overlay: `mpv --wayland-app-id=enboxer-
|
|||||||
|
|
||||||
## Smart interact shortcut
|
## Smart interact shortcut
|
||||||
|
|
||||||
`bind: interact` in any map step expands at compile time into the full chain
|
`bind: smart_interact` in any map step expands at compile time into the full chain
|
||||||
(CTM on → Interact with Target → walk delay → CTM off), driven by
|
(CTM on → Interact with Target → walk delay → CTM off), driven by
|
||||||
`profile.interact` (`style` + `walk_delay_ms`). This is the ISBoxer Mapped
|
`profile.interact` (`style` + `walk_delay_ms`). This is the ISBoxer Mapped
|
||||||
Key analog — one user keypress, four keystrokes dispatched to every captured
|
Key analog — one user keypress, four keystrokes dispatched to every captured
|
||||||
|
|||||||
@ -114,7 +114,7 @@ maps:
|
|||||||
steps:
|
steps:
|
||||||
- bind: assist
|
- bind: assist
|
||||||
target: others
|
target: others
|
||||||
- bind: interact
|
- bind: smart_interact # full chain (use "interact" for a single Alt+J send)
|
||||||
target: others
|
target: others
|
||||||
- name: loot_manual
|
- name: loot_manual
|
||||||
# Same idea as `loot` but composed by hand. Pick this form if you want to
|
# Same idea as `loot` but composed by hand. Pick this form if you want to
|
||||||
@ -127,7 +127,7 @@ maps:
|
|||||||
target: others
|
target: others
|
||||||
- bind: ctm_on
|
- bind: ctm_on
|
||||||
target: others
|
target: others
|
||||||
- bind: interact
|
- bind: interact # single Alt+J send (NOT the smart shortcut)
|
||||||
target: others
|
target: others
|
||||||
- delay_ms: 5000
|
- delay_ms: 5000
|
||||||
- bind: ctm_off
|
- bind: ctm_off
|
||||||
@ -140,7 +140,7 @@ maps:
|
|||||||
steps:
|
steps:
|
||||||
- bind: ctm_on
|
- bind: ctm_on
|
||||||
target: others
|
target: others
|
||||||
- bind: interact
|
- bind: interact # single Alt+J send (NOT the smart shortcut)
|
||||||
target: others
|
target: others
|
||||||
release_steps:
|
release_steps:
|
||||||
- bind: ctm_off
|
- bind: ctm_off
|
||||||
|
|||||||
@ -205,11 +205,14 @@ impl Engine {
|
|||||||
out.push(Action::Sleep(Duration::from_millis(ms)));
|
out.push(Action::Sleep(Duration::from_millis(ms)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// ISBoxer-style Mapped Key shortcut: `bind: "interact"` fires the
|
// ISBoxer-style Mapped Key shortcut: `bind: "smart_interact"` fires
|
||||||
// full CTM-toggle + Interact-with-Target + (optional) wait + CTM-off
|
// the full CTM-toggle + Interact-with-Target + (optional) wait +
|
||||||
// chain driven by profile.interact (style + walk_delay_ms).
|
// CTM-off chain driven by profile.interact (style + walk_delay_ms).
|
||||||
// The user only pressed the map hotkey; the daemon composes the chain.
|
// The user only pressed the map hotkey; the daemon composes the chain.
|
||||||
if step.bind.as_deref() == Some("interact") {
|
// For per-step control, use `bind: "interact"` to send a single
|
||||||
|
// Interact-with-Target keystroke (game_binds.interact = Alt+J) and
|
||||||
|
// compose ctm_on / ctm_off / walk_delay_ms yourself.
|
||||||
|
if step.bind.as_deref() == Some("smart_interact") {
|
||||||
let slots = self.resolve_targets(&step.target, &map_name)?;
|
let slots = self.resolve_targets(&step.target, &map_name)?;
|
||||||
let interact_key = self
|
let interact_key = self
|
||||||
.profile
|
.profile
|
||||||
@ -352,7 +355,7 @@ mod tests {
|
|||||||
target: "others".into(),
|
target: "others".into(),
|
||||||
},
|
},
|
||||||
Step {
|
Step {
|
||||||
bind: Some("interact".into()),
|
bind: Some("smart_interact".into()),
|
||||||
key: None,
|
key: None,
|
||||||
delay_ms: None,
|
delay_ms: None,
|
||||||
target: "others".into(),
|
target: "others".into(),
|
||||||
@ -505,6 +508,37 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interact_simple_sends_only_alt_j() {
|
||||||
|
// After the smart_interact split, `bind: "interact"` is a single
|
||||||
|
// Alt+J send (game_binds.interact = "g"). No CTM toggle, no sleep,
|
||||||
|
// no second send. Use this from manual chains or release_steps.
|
||||||
|
let mut e = sample();
|
||||||
|
let map = crate::profile::Map {
|
||||||
|
name: "interact_simple".into(),
|
||||||
|
hotkey: Hotkey("Alt+U".into()),
|
||||||
|
hold: false,
|
||||||
|
steps: vec![crate::profile::Step {
|
||||||
|
key: None,
|
||||||
|
bind: Some("interact".into()),
|
||||||
|
delay_ms: None,
|
||||||
|
target: "others".into(),
|
||||||
|
}],
|
||||||
|
release_steps: vec![],
|
||||||
|
};
|
||||||
|
e.profile.maps.push(map);
|
||||||
|
let acts = e.fire("Alt+U", Hold::Tap).unwrap();
|
||||||
|
assert_eq!(acts.len(), 1, "exactly one action: {acts:?}");
|
||||||
|
match &acts[0] {
|
||||||
|
Action::Send { key, slots, hold } => {
|
||||||
|
assert_eq!(key, "g");
|
||||||
|
assert_eq!(*hold, Hold::Tap);
|
||||||
|
assert_eq!(slots, &vec![2, 3]);
|
||||||
|
}
|
||||||
|
other => panic!("expected Send, got {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interact_smart_shortcut_standard_emits_full_sequence() {
|
fn interact_smart_shortcut_standard_emits_full_sequence() {
|
||||||
// bind: "interact" should fire CTM-on, Interact with Target,
|
// bind: "interact" should fire CTM-on, Interact with Target,
|
||||||
@ -518,7 +552,7 @@ mod tests {
|
|||||||
hold: false,
|
hold: false,
|
||||||
steps: vec![crate::profile::Step {
|
steps: vec![crate::profile::Step {
|
||||||
key: None,
|
key: None,
|
||||||
bind: Some("interact".into()),
|
bind: Some("smart_interact".into()),
|
||||||
delay_ms: None,
|
delay_ms: None,
|
||||||
target: "others".into(),
|
target: "others".into(),
|
||||||
}],
|
}],
|
||||||
@ -555,7 +589,7 @@ mod tests {
|
|||||||
hold: false,
|
hold: false,
|
||||||
steps: vec![crate::profile::Step {
|
steps: vec![crate::profile::Step {
|
||||||
key: None,
|
key: None,
|
||||||
bind: Some("interact".into()),
|
bind: Some("smart_interact".into()),
|
||||||
delay_ms: None,
|
delay_ms: None,
|
||||||
target: "others".into(),
|
target: "others".into(),
|
||||||
}],
|
}],
|
||||||
@ -585,7 +619,7 @@ mod tests {
|
|||||||
hold: true,
|
hold: true,
|
||||||
steps: vec![crate::profile::Step {
|
steps: vec![crate::profile::Step {
|
||||||
key: None,
|
key: None,
|
||||||
bind: Some("interact".into()),
|
bind: Some("smart_interact".into()),
|
||||||
delay_ms: None,
|
delay_ms: None,
|
||||||
target: "others".into(),
|
target: "others".into(),
|
||||||
}],
|
}],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user