Skip to content

feat(leftclickcast): Left-Click Cast plugin#389

Merged
chsami merged 13 commits intochsami:developmentfrom
runsonmypc:plugin/left-click-cast-pr
Apr 16, 2026
Merged

feat(leftclickcast): Left-Click Cast plugin#389
chsami merged 13 commits intochsami:developmentfrom
runsonmypc:plugin/left-click-cast-pr

Conversation

@runsonmypc
Copy link
Copy Markdown
Contributor

Summary

Adds the Left-Click Cast plugin (PluginConstants.PERT). It replaces the left-click Attack option on attackable NPCs and Players (wilderness / PvP) with a preconfigured Cast Spell action, dispatched via two same-tick menuAction packets for a zero-delay feel (fallback to Rs2Magic.castOn if the spellbook widget isn't loaded yet).

  • Five independently configurable spell slots with hotkey-driven active-slot switching (Slot 1 active at startup, runtime-only — never persisted).
  • Dedicated hotkey for the master enable/disable toggle, kept in sync with the config panel checkbox via ExternalPluginsChanged.
  • Unified "Chat feedback" toggle gates all in-game chat messages (slot change + enable/disable toggle).
  • "Require magic weapon" option (default on) uses EnumID.WEAPON_STYLES + ParamID.ATTACK_STYLE_NAME to detect casting-capable weapons, mirroring core AttackStylesPlugin logic.
  • Broad spell coverage: modern Strike→Surge, Ancients (Rush/Burst/Blitz/Barrage × 4 elements), Arceuus offensive, non-autocastables (Magic Dart, Saradomin Strike, etc.), and utility target spells (Bind/Snare/Entangle/Tele Block/Tele Other).

Also adds the PERT author prefix to PluginConstants.

Plugin version: 1.3.0, minClientVersion: 2.0.13.

Test plan

  • ./gradlew -PpluginList=LeftClickCastPlugin build succeeds (produces LeftClickCastPlugin-1.3.0.jar).
  • Unit / integration tests pass (./gradlew test).
  • Manually tested via ./gradlew runDebug — left-click cast fires as a single motion on NPCs and Players; slot hotkeys swap the active spell mid-fight; enable-toggle hotkey flips the config panel checkbox; chat feedback toggle gates both event types.
  • Legacy spell config key migrates into slot1Spell on startup when a non-default value is present.

runsonmypc and others added 13 commits April 15, 2026 12:17
Yellow [P] prefix in the #FFFF00 color family, matching the
existing HTML-wrapped prefix convention.
Wraps every target-castable MagicAction (autocast Strike->Surge,
ancient Rush/Burst/Blitz/Barrage, non-autocast combat, Arceuus
offensives, utility target spells) behind a minimal display-name
shape for the plugin config dropdown.
Three items: enabled (master switch), spell (PertTargetSpell
dropdown, default Fire Strike), requireMagicWeapon (staff gate).
onMenuEntryAdded inserts a Cast entry above the Attack entry for
attackable NPCs, gated by the enabled flag, a non-null configured
spell, and (optionally) a magic-weapon equip check via varbit 357.

Cast is dispatched through Rs2Magic.castOn off-thread via
CompletableFuture because Global.sleepUntil is a no-op on the
client thread and would otherwise silently drop the cast.
Covers purpose, config reference, limitations (no rune or
spellbook auto-management, staff-only default, PvP out of scope),
and the full supported-spells list.
net.runelite.api.Varbits is @deprecated in favor of gameval
identifier classes. Same varbit (357), no behavior change.
Two bugs found during manual verification:

1. The static weapon-type set (22/23/26/27) from the design doc was
   wrong — real STAFF value (at least on this client) is 18. Replaced
   with a runtime check that reads the weapon's attack-style struct
   via EnumID.WEAPON_STYLES + ParamID.ATTACK_STYLE_NAME and flags the
   weapon as magic if any style is Casting or Defensive Casting.
   Mirrors core AttackStylesPlugin and auto-covers future weapons.

2. Mutating the Attack entry inside onMenuEntryAdded did not survive
   the game's menu sort pass — entries of type RUNELITE get sorted
   below NPC_* entries, so left-click kept firing Attack/Walk-Here.
   Moved the mutation to onPostMenuSort (fires after the sort) and
   swap the entry to the tail of the array — tail slot is the
   left-click action in RuneLite's menu model.
Scan loop now accepts Attack entries on either NPCs or Players.
Dispatch wraps NPCs in Rs2NpcModel; raw Player is passed through,
which Rs2Magic.castOn handles via its instanceof Player branch.
Adds five `@ConfigItem` spell slots, each with a corresponding Keybind,
grouped under "Spell Slots" and "Hotkeys" config sections. A
`HotkeyListener` per slot is registered on startUp and unregistered on
shutDown; pressing a bound hotkey sets the in-memory active slot and
(optionally) posts a chat message with the new spell's display name.
The menu-sort hot path now reads the active slot's spell instead of the
legacy single `spell` key.

The legacy `spell` key remains defined so existing configs aren't
invalidated, and is migrated into `slot1Spell` on startUp when slot 1
is still at its default — existing users keep their choice without
manual action. Active slot is session-local and resets to slot 1 on
every startUp.

Bumps plugin version to 1.1.0.
Replaces the async Rs2Magic.castOn pipeline (tab switch + 150-300ms
sleep + sleepUntil(isWidgetSelected) + NPC interact) with two
synchronous client.menuAction calls fired back-to-back: one
WIDGET_TARGET to select the spell client-side, one
WIDGET_TARGET_ON_NPC / WIDGET_TARGET_ON_PLAYER to dispatch it on the
hovered target. Both packets queue on the same event-loop tick, so the
server processes selection and cast on the same game tick — indistinguishable
from "I pre-selected the spell and clicked the target".

Falls back to Rs2Magic.castOn when the spellbook widget (group 218) isn't
loaded yet (first cast after login without opening Magic tab), nudging
Rs2Tab.switchTo(InterfaceTab.MAGIC) so subsequent clicks take the fast
path. Also falls back when the selected spell isn't on the active
spellbook (modern vs. ancients mismatch).

Bumps plugin version to 1.2.0.
Adds an `Enable/Disable Hotkey` config item at the top of the Hotkeys
section. Pressing the hotkey flips `config.enabled()` and posts an
`ExternalPluginsChanged` event so the open MicrobotConfigPanel rebuilds
and the inner Enabled checkbox visually flips in sync — the panel
otherwise doesn't subscribe to ConfigChanged for individual checkbox
refresh.

Renames `activeSlotChatMessage` → `chatFeedback` and uses it as a
single gate for all plugin chat output. Both checkbox clicks and the
toggle hotkey route through a shared `@Subscribe onConfigChanged`
handler, so they emit the same chat message via one code path.

Materializes `@ConfigItem` defaults to storage on `startUp` via
`configManager.setDefaultConfiguration(config, false)`. Without this,
MicrobotConfigPanel's checkbox lookup returns null for newly added
keys (`parseBoolean(null) → false`) while the proxy returns the
@configitem default, leaving the UI and runtime out of sync.

Bumps plugin version to 1.3.0.
@chsami chsami merged commit f953f53 into chsami:development Apr 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants