forked from Dusk/ShaguTweaks-ClassicAPI
Merge the ShaguTweaks Extras ClassicAPI modules into ShaguTweaks (3.7)
Bring in 27 module files from Dusk/ShaguTweaks-Extras-ClassicAPI (8e14bb4): action bar, chat, loot, macro, merchant (Buy Em All), minimap & world map, tooltip and unit frame modules, with their textures. Titles are unchanged, so saved toggles carry over. - Categories: Chat -> Chat & Social, World Map -> Minimap & World Map, Merchant & Auction -> Merchant; Macro is a new category. - Texture paths point at ShaguTweaks\img. - Not merged: Raid Frames, Bag Item Click and Bag Search Bar (Bagshui covers them), Auction Alt-Buy (aux covers it). - Chat note when the old ShaguTweaks-extras addon is still enabled. - English translation keys, notices, license copy, asset provenance, README, changelog and version 3.7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
This commit is contained in:
@@ -4,6 +4,31 @@ Changes in this fork (OEVG/ShaguTweaks-ClassicAPI), newest first. Each entry is
|
||||
|
||||
This fork is based on [Dusk/ShaguTweaks-ClassicAPI](https://octowow.st/git/Dusk/ShaguTweaks-ClassicAPI) at commit `4391564` (2026-09-08). Earlier changes are in that repository's history.
|
||||
|
||||
## 3.7 — 2026-11-02 — Extras modules built in
|
||||
|
||||
### Added
|
||||
|
||||
- The modules of Dusk's ShaguTweaks Extras ClassicAPI are now part of this addon, so the separate addon is no longer needed:
|
||||
- **Action Bar:** Dragonflight Gryphons, Floating Actionbar, Hide Macro Text, Mouseover Right, Mouseover Right 2, Reagent Counter, Center Vertical Actionbar, Key-Down Casting, Alt Self-Cast, Show Bags and Show Micro Menu.
|
||||
- **Chat & Social:** Center Text Input Box, Block NPC Spam, Enable Text Shadow, Chat Timestamps and World Chat Hider.
|
||||
- **Loot:** Loot Quality.
|
||||
- **Macro** (new category): Macro Icons and Macro Tweaks.
|
||||
- **Merchant** (new category): Buy Em All.
|
||||
- **Minimap & World Map:** MiniMap Framerate & Latency and Reveal World Map.
|
||||
- **Tooltip & Items:** Cursor Tooltip, Hide Combat Tooltip and Metric Range.
|
||||
- **Unit Frames:** Unit Frame Abbreviated Names.
|
||||
- They keep their names, so saved on/off choices carry over. If the old Extras addon is still enabled, a chat message asks you to disable it.
|
||||
|
||||
### Not merged
|
||||
|
||||
- **Raid Frames** (all its modules).
|
||||
- **Bag Item Click** and **Bag Search Bar:** Bagshui already has a search box and sends items to trade, the auction house and mail on right-click.
|
||||
- **Auction Alt-Buy:** aux already buys out or cancels with Alt+click.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **XP Bar Text:** the numbers are drawn with a shadow instead of an outline, so neighbouring digits no longer run into each other.
|
||||
|
||||
## 3.6 — 2026-11-02 — Map window, Transports button and Loot History
|
||||
|
||||
### Added
|
||||
|
||||
@@ -33,6 +33,10 @@ hashes.
|
||||
| `img/UI-TargetingFrame.tga` | exact-upstream-git-blob-match | `shagu/ShaguTweaks: img/UI-TargetingFrame.tga` | `c3c56c28ff81f99325a8aa5a8d8c5cd4a9c56a95` |
|
||||
| `img/circleparty.tga` | exact-upstream-git-blob-match | `shagu/ShaguTweaks: img/circleparty.tga` | `9634f9ffb07ae4fb047223f83c299a8ba1906dff` |
|
||||
| `img/circleraid.tga` | exact-upstream-git-blob-match | `shagu/ShaguTweaks: img/circleraid.tga` | `c8a5c3e4c572ebd4af426d878bdc8db21d758691` |
|
||||
| `img/df-gryphon-beta.tga` | exact-upstream-git-blob-match | `paokkerkir/ShaguTweaks-extras: img/df-gryphon-beta.tga` (via Dusk/ShaguTweaks-Extras-ClassicAPI) | `bb4262fe7bf811fa7fff958f7f29269b69038280` |
|
||||
| `img/df-gryphon.tga` | exact-upstream-git-blob-match | `paokkerkir/ShaguTweaks-extras: img/df-gryphon.tga` (via Dusk/ShaguTweaks-Extras-ClassicAPI) | `6c7e965613222b0313dc1d56d9aa81414a828eef` |
|
||||
| `img/df-wyvern.tga` | exact-upstream-git-blob-match | `paokkerkir/ShaguTweaks-extras: img/df-wyvern.tga` (via Dusk/ShaguTweaks-Extras-ClassicAPI) | `4414fedcec732ea3096c30598ba7ada25a6af585` |
|
||||
| `img/xpbar.tga` | exact-upstream-git-blob-match | `paokkerkir/ShaguTweaks-extras: img/xpbar.tga` (via Dusk/ShaguTweaks-Extras-ClassicAPI) | `de80c16875cd7d9fa96baf99bd6ae2e1514595f7` |
|
||||
|
||||
## Screenshot manifest
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ apply every listed license to the ShaguTweaks-ClassicAPI repository as a whole.
|
||||
- `ShaguTweaks-mods-MIT.txt` — MIT license for the adapted
|
||||
TokensWorth/ShaguTweaks-mods material documented in
|
||||
`THIRD_PARTY_NOTICES.md`.
|
||||
- `ShaguTweaks-extras-MIT.txt` — MIT license of ShaguTweaks-extras, for the
|
||||
modules merged from ShaguTweaks-Extras-ClassicAPI documented in
|
||||
`THIRD_PARTY_NOTICES.md`.
|
||||
- `pfUI-MIT.txt` — MIT license for the Swing Timer and Wand Timer ported from
|
||||
brues-code/pfUI, documented in `THIRD_PARTY_NOTICES.md`.
|
||||
- `ClassicAPI-GPL-3.0.txt` — verbatim license document from the external
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2025 Eric Mauser (Shagu)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -9,13 +9,15 @@ This is an English-only customization of [Dusk/ShaguTweaks-ClassicAPI](https://o
|
||||
|
||||
> This fork requires ClassicAPI and targets **Turtle WoW-like server/client environments**.
|
||||
|
||||
**Version 3.6.** What changed in each update: see the [changelog](CHANGELOG.md).
|
||||
**Version 3.7.** What changed in each update: see the [changelog](CHANGELOG.md).
|
||||
|
||||
> **Upgrading to 3.7:** the modules of the separate *ShaguTweaks Extras ClassicAPI* addon are now built into ShaguTweaks. **Disable or delete `ShaguTweaks-extras`** after updating, otherwise its older copies replace the built-in ones. Your on/off settings carry over. Raid Frames, Bag Item Click, Bag Search Bar and Auction Alt-Buy were not merged. See [Built-in Extras modules](#built-in-extras-modules).
|
||||
|
||||
## 🔌 Requirements
|
||||
|
||||
- **[ClassicAPI](https://github.com/brues-code/ClassicAPI) — required.** Install both the DLL and its bundled `!!!ClassicAPI` addon, and restart the client after a DLL update. v1.18.10 or newer is recommended (it includes a crash fix for addons that restyle nameplates). The integration was last reviewed against v1.18.13; see [Docs/CLASSICAPI_UPDATES.md](Docs/CLASSICAPI_UPDATES.md).
|
||||
- **[SuperWoW](https://github.com/balakethelock/SuperWoW) — optional / recommended.** Only used as an additional fallback for some cast/GUID information.
|
||||
- **[ShaguTweaks Extras ClassicAPI](https://octowow.st/git/Dusk/ShaguTweaks-Extras-ClassicAPI) — optional / recommended.** Dusk's companion addon with some extra modules and features. It loads alongside this one and adds its own option categories.
|
||||
- **[ShaguTweaks Extras ClassicAPI](https://octowow.st/git/Dusk/ShaguTweaks-Extras-ClassicAPI) — no longer needed.** Its modules are built in since 3.7 (see **Built-in Extras modules** below). Disable or remove it; while it is enabled, its copies replace the built-in ones.
|
||||
|
||||
ClassicAPI is the main API layer.
|
||||
|
||||
@@ -59,6 +61,12 @@ Settings: **Esc → Advanced Options**.
|
||||
- **WorldMap Window:** the map keeps the size you pick with Ctrl+Mousewheel (saved per character), and the title bar drags the window as well. With [OctoBus](https://github.com/Phridgey/OctoBus) installed, a **Transports** button left of the Continent dropdown shows or hides its transport timers.
|
||||
- **Loot History:** `/loothistory` lists recent group-loot rolls with the winner of each item; click an item for everyone's need, greed or pass and their rolls. Ported from [brues-code/pfUI](https://github.com/brues-code/pfUI). Requires ClassicAPI.
|
||||
|
||||
**Built-in Extras modules**
|
||||
|
||||
- The modules of Dusk's [ShaguTweaks Extras ClassicAPI](https://octowow.st/git/Dusk/ShaguTweaks-Extras-ClassicAPI) are part of this addon, each under the matching category: Action Bar, Chat & Social, Loot, Minimap & World Map, Tooltip & Items and Unit Frames, plus two new categories, **Macro** and **Merchant**.
|
||||
- They keep their names, so your saved on/off choices carry over.
|
||||
- Left out: **Raid Frames**; **Bag Item Click** and **Bag Search Bar**, which [Bagshui](https://octowow.st/git/OEVG/Bagshui) already covers; and **Auction Alt-Buy**, which [aux](https://octowow.st/git/Dusk/aux-addon-ClassicAPI) already does (Alt+click a row to buy out or cancel).
|
||||
|
||||
**Performance and stability**
|
||||
|
||||
- Reduced per-frame work, tooltip scans and repeated UI updates.
|
||||
@@ -97,6 +105,13 @@ Modules that use ClassicAPI directly:
|
||||
- Enemy Castbars
|
||||
- Tooltip Details
|
||||
- WorldMap Window
|
||||
- Buy Em All
|
||||
- Key-Down Casting and Alt Self-Cast
|
||||
- Macro Icons and Macro Tweaks
|
||||
- MiniMap Framerate & Latency
|
||||
- Reagent Counter
|
||||
- Reveal World Map
|
||||
- Show Bags and Show Micro Menu
|
||||
|
||||
Other modules can also benefit indirectly from ClassicAPI through shared ShaguTweaks libraries and caches. Per-version notes are in [Docs/CLASSICAPI_UPDATES.md](Docs/CLASSICAPI_UPDATES.md).
|
||||
|
||||
@@ -116,6 +131,17 @@ Names match the in-game options list. Modules marked *(off)* are disabled until
|
||||
- Wand Timer *(off)*
|
||||
- No Toggle Auto-Attack
|
||||
- Reduced Actionbar Size *(off)*
|
||||
- Show Bags *(off)*
|
||||
- Show Micro Menu *(off)*
|
||||
- Key-Down Casting
|
||||
- Alt Self-Cast *(off)*
|
||||
- Reagent Counter *(off)*
|
||||
- Hide Macro Text *(off)*
|
||||
- Mouseover Right *(off)*
|
||||
- Mouseover Right 2 *(off)*
|
||||
- Center Vertical Actionbar *(off)*
|
||||
- Floating Actionbar *(off)*
|
||||
- Dragonflight Gryphons *(off)*
|
||||
|
||||
### Unit Frames
|
||||
|
||||
@@ -125,6 +151,7 @@ Names match the in-game options list. Modules marked *(off)* are disabled until
|
||||
- Unit Frame Class Portraits *(off)*
|
||||
- Unit Frame Energy & Mana Tick *(off)*
|
||||
- Hide Pet Unit Frame *(off)*
|
||||
- Unit Frame Abbreviated Names *(off)*
|
||||
- Movable Unit Frames
|
||||
- Real Health Numbers *(off)*
|
||||
- White Unit Frame Numbers *(off)*
|
||||
@@ -146,6 +173,9 @@ Names match the in-game options list. Modules marked *(off)* are disabled until
|
||||
- Sell Junk
|
||||
- Tooltip Details
|
||||
- Vendor Values
|
||||
- Metric Range
|
||||
- Cursor Tooltip *(off)*
|
||||
- Hide Combat Tooltip *(off)*
|
||||
|
||||
### Chat & Social
|
||||
|
||||
@@ -154,6 +184,11 @@ Names match the in-game options list. Modules marked *(off)* are disabled until
|
||||
- Chat Levels *(off)*
|
||||
- Chat Spam Filter *(off)*
|
||||
- Social Colors
|
||||
- Chat Timestamps *(off)*
|
||||
- Enable Text Shadow *(off)*
|
||||
- Center Text Input Box *(off)*
|
||||
- Block NPC Spam *(off)*
|
||||
- World Chat Hider *(off)*
|
||||
|
||||
### Minimap & World Map
|
||||
|
||||
@@ -166,6 +201,8 @@ Names match the in-game options list. Modules marked *(off)* are disabled until
|
||||
- WorldMap Class Colors
|
||||
- WorldMap Coordinates
|
||||
- WorldMap Window
|
||||
- Reveal World Map
|
||||
- MiniMap Framerate & Latency *(off)*
|
||||
|
||||
### Loot
|
||||
|
||||
@@ -173,6 +210,16 @@ Names match the in-game options list. Modules marked *(off)* are disabled until
|
||||
- Improved Roll Frames *(off)*
|
||||
- Loot Cursor
|
||||
- Loot History (`/loothistory`)
|
||||
- Loot Quality *(off)*
|
||||
|
||||
### Macro
|
||||
|
||||
- Macro Icons
|
||||
- Macro Tweaks
|
||||
|
||||
### Merchant
|
||||
|
||||
- Buy Em All
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -193,6 +240,7 @@ The following original ShaguTweaks modules are not included as standalone module
|
||||
|
||||
- **Chat Links** → integrated into **Chat Tweaks**
|
||||
- **Movable Unit Frames Extended** → integrated into **Movable Unit Frames**
|
||||
- **ShaguTweaks Extras ClassicAPI** → its modules are built in, except Raid Frames, Bag Item Click, Bag Search Bar and Auction Alt-Buy
|
||||
|
||||
## 📜 Project identity & licensing
|
||||
|
||||
@@ -225,8 +273,10 @@ Special thanks to **Jesper_92** for reporting issues that helped improve the add
|
||||
|
||||
Additional code and ideas from **pfUI** and **zUI**.
|
||||
|
||||
Extras modules from **Dusk-92**'s ShaguTweaks-Extras-ClassicAPI, a fork of **paokkerkir**'s ShaguTweaks-extras (MIT, Shagu).
|
||||
|
||||
Swing Timer, Wand Timer and Loot History ported from **brues-code/pfUI** (MIT, Shagu and contributors).
|
||||
|
||||
Extended Movable Unit Frames support adapted from **TokensWorth/ShaguTweaks-mods**, originally released under MIT by **GryllsAddons**. See `THIRD_PARTY_NOTICES.md`.
|
||||
Extended Movable Unit Frames support and several of the Extras modules adapted from **TokensWorth/ShaguTweaks-mods**, originally released under MIT by **GryllsAddons**. See `THIRD_PARTY_NOTICES.md`.
|
||||
|
||||
Released under the original **MIT License**.
|
||||
|
||||
+38
-1
@@ -2,7 +2,7 @@
|
||||
## Title: |cffffcc00Shagu|cffffffffTweaks |cffcccccc[ClassicAPI]
|
||||
## Author: Shagu
|
||||
## Notes: A collection of tweaks and enhancements to the Default UI
|
||||
## Version: 3.6
|
||||
## Version: 3.7
|
||||
## Dependencies: !!!ClassicAPI
|
||||
## SavedVariables: ShaguTweaks_player_cache, ShaguTweaks_social_cache
|
||||
## SavedVariablesPerCharacter: ShaguTweaks_config, ShaguTweaks_cache
|
||||
@@ -86,6 +86,43 @@ mods\swing-timer.lua
|
||||
mods\smaller-errors-frame.lua
|
||||
mods\skip-gossip.lua
|
||||
|
||||
# modules merged from ShaguTweaks Extras ClassicAPI (Dusk)
|
||||
# action bar
|
||||
mods\actionbar-df-gryphon.lua
|
||||
mods\actionbar-right-center.lua
|
||||
mods\actionbar-reagents.lua
|
||||
mods\actionbar-float.lua
|
||||
mods\classic-snowfall.lua
|
||||
mods\reduced-actionbar-bags.lua
|
||||
mods\reduced-actionbar-micromenu.lua
|
||||
mods\actionbar-mouseover-common.lua
|
||||
mods\actionbar-mouseover-bar-right.lua
|
||||
mods\actionbar-mouseover-bar-right2.lua
|
||||
mods\actionbar-hide-macro.lua
|
||||
# merchant
|
||||
mods\buy-em-all.lua
|
||||
# loot
|
||||
mods\loot-quality.lua
|
||||
# tooltip & items
|
||||
mods\metric-range.lua
|
||||
mods\cursor-tooltip.lua
|
||||
mods\hide-combat-tooltip.lua
|
||||
# unit frames
|
||||
mods\unitframes-abbrev-names.lua
|
||||
# minimap & world map
|
||||
mods\minimap-framerate-latency.lua
|
||||
mods\worldmap-reveal.lua
|
||||
# macro
|
||||
mods\macro-icons.lua
|
||||
mods\macro-tweaks.lua
|
||||
# chat & social
|
||||
mods\chat-filter-common.lua
|
||||
mods\chat-npc-spam.lua
|
||||
mods\world-chat-hider.lua
|
||||
mods\chat-input-center.lua
|
||||
mods\chat-text-shadow.lua
|
||||
mods\chat-timestamps.lua
|
||||
|
||||
# compatibility
|
||||
compat-dragonflightui.lua
|
||||
compat-guda.lua
|
||||
|
||||
@@ -33,9 +33,60 @@ from `TokensWorth/ShaguTweaks-mods`:
|
||||
- Copyright (c) 2022 GryllsAddons
|
||||
- License: MIT
|
||||
|
||||
The following modules, merged from ShaguTweaks-Extras-ClassicAPI, also
|
||||
contain code adapted from `TokensWorth/ShaguTweaks-mods` (as recorded by that
|
||||
project):
|
||||
|
||||
- `mods/actionbar-mouseover-common.lua`
|
||||
- `mods/actionbar-mouseover-bar-right.lua`
|
||||
- `mods/actionbar-mouseover-bar-right2.lua`
|
||||
- `mods/actionbar-hide-macro.lua`
|
||||
- `mods/unitframes-abbrev-names.lua`
|
||||
- `mods/cursor-tooltip.lua`
|
||||
- `mods/hide-combat-tooltip.lua`
|
||||
- `mods/minimap-framerate-latency.lua`
|
||||
- `mods/chat-npc-spam.lua`
|
||||
- `mods/world-chat-hider.lua`
|
||||
- `mods/loot-quality.lua`
|
||||
|
||||
The preserved license text is stored in
|
||||
`LICENSES/ShaguTweaks-mods-MIT.txt`.
|
||||
|
||||
## ShaguTweaks-Extras-ClassicAPI
|
||||
|
||||
These modules were merged from Dusk-92's ShaguTweaks-Extras-ClassicAPI
|
||||
(commit `8e14bb4`, 2026-09-18), itself a fork of paokkerkir's
|
||||
ShaguTweaks-extras:
|
||||
|
||||
- Action bar: `actionbar-df-gryphon.lua`, `actionbar-right-center.lua`,
|
||||
`actionbar-reagents.lua`, `actionbar-float.lua`, `classic-snowfall.lua`,
|
||||
`reduced-actionbar-bags.lua`, `reduced-actionbar-micromenu.lua`,
|
||||
`actionbar-mouseover-common.lua`, `actionbar-mouseover-bar-right.lua`,
|
||||
`actionbar-mouseover-bar-right2.lua`, `actionbar-hide-macro.lua`
|
||||
- Merchant: `buy-em-all.lua`
|
||||
- Loot: `loot-quality.lua`
|
||||
- Tooltip & items: `metric-range.lua`, `cursor-tooltip.lua`,
|
||||
`hide-combat-tooltip.lua`
|
||||
- Unit frames: `unitframes-abbrev-names.lua`
|
||||
- Minimap & world map: `minimap-framerate-latency.lua`, `worldmap-reveal.lua`
|
||||
- Macro: `macro-icons.lua`, `macro-tweaks.lua`
|
||||
- Chat & social: `chat-filter-common.lua`, `chat-npc-spam.lua`,
|
||||
`world-chat-hider.lua`, `chat-input-center.lua`, `chat-text-shadow.lua`,
|
||||
`chat-timestamps.lua`
|
||||
|
||||
Their option categories, texture paths and one chat message were changed for
|
||||
the merge; the rest of the code is unchanged. The textures
|
||||
`img/df-gryphon.tga`, `img/df-gryphon-beta.tga`, `img/df-wyvern.tga` and
|
||||
`img/xpbar.tga` come from the same tree (see `Docs/ASSET_PROVENANCE.md`).
|
||||
|
||||
- Sources: https://octowow.st/git/Dusk/ShaguTweaks-Extras-ClassicAPI and
|
||||
https://github.com/paokkerkir/ShaguTweaks-extras
|
||||
- Copyright (c) 2025 Eric Mauser (Shagu)
|
||||
- License: MIT
|
||||
|
||||
The preserved license text is stored in
|
||||
`LICENSES/ShaguTweaks-extras-MIT.txt`.
|
||||
|
||||
## brues-code/pfUI
|
||||
|
||||
The Swing Timer and Wand Timer in `mods/swing-timer.lua` are ported from
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -101,4 +101,70 @@ ShaguTweaks_translation["enUS"] = {
|
||||
["Clear"] = nil,
|
||||
["No loot rolls yet."] = nil,
|
||||
["Loot History requires ClassicAPI."] = nil,
|
||||
["Dragonflight Gryphons"] = nil,
|
||||
["Replaces actionbar gryphons with the dragonflight version."] = nil,
|
||||
["Action Bar"] = nil,
|
||||
["Floating Actionbar"] = nil,
|
||||
["Removes all background textures and lets the actionbar float."] = nil,
|
||||
["Hide Macro Text"] = nil,
|
||||
["Hides macro names on action buttons."] = nil,
|
||||
["Mouseover Right"] = nil,
|
||||
["Hide the right actionbar and show it on mouseover."] = nil,
|
||||
["Mouseover Right 2"] = nil,
|
||||
["Hide the second right actionbar and show it on mouseover."] = nil,
|
||||
["Reagent Counter"] = nil,
|
||||
["Shows a reagent counter on action buttons."] = nil,
|
||||
["Center Vertical Actionbar"] = nil,
|
||||
["Center the vertical actionbar on the right side."] = nil,
|
||||
["Buy Em All"] = nil,
|
||||
["Shift-click vendor items to choose stack, maximum, or a custom purchase amount."] = nil,
|
||||
["Merchant"] = nil,
|
||||
["Center Text Input Box"] = nil,
|
||||
["Move the chat input box to the center of the screen."] = nil,
|
||||
["Chat & Social"] = nil,
|
||||
["Block NPC Spam"] = nil,
|
||||
["Blocks known repetitive NPC say/yell spam messages."] = nil,
|
||||
["Enable Text Shadow"] = nil,
|
||||
["Enable text shadow in all chat frames."] = nil,
|
||||
["Chat Timestamps"] = nil,
|
||||
["Add timestamps to chat messages."] = nil,
|
||||
["Alt Self-Cast"] = nil,
|
||||
["When Key-Down Casting is enabled, hold Alt to cast eligible actions on yourself."] = nil,
|
||||
["Key-Down Casting"] = nil,
|
||||
["Activates action-bar and pet actions when the key is pressed instead of when it is released."] = nil,
|
||||
["Cursor Tooltip"] = nil,
|
||||
["Attaches default tooltips to the cursor."] = nil,
|
||||
["Hide Combat Tooltip"] = nil,
|
||||
["Hides the tooltip in combat. Hold Shift to show it temporarily."] = nil,
|
||||
["Loot Quality"] = nil,
|
||||
["Colors the loot frame using the highest item quality in the current loot window."] = nil,
|
||||
["Loot"] = nil,
|
||||
["Macro Icons"] = nil,
|
||||
["Detect showtooltip and spells in macros to use them on action buttons."] = nil,
|
||||
["Macro"] = nil,
|
||||
["Macro Tweaks"] = nil,
|
||||
["Add /equip, /use and modern ClassicAPI macro commands, remove #showtooltip from chat and hide macro commands from history."] = nil,
|
||||
["Metric Range"] = nil,
|
||||
["Displays tooltip range units in metres instead of yards."] = nil,
|
||||
["MiniMap Framerate & Latency"] = nil,
|
||||
["Adds a small framerate & latency display to the mini map."] = nil,
|
||||
["Minimap & World Map"] = nil,
|
||||
["Framerate"] = nil,
|
||||
["High"] = nil,
|
||||
["fps"] = nil,
|
||||
["Low"] = nil,
|
||||
["Latency"] = nil,
|
||||
["ms"] = nil,
|
||||
["Show Bags"] = nil,
|
||||
["Shows bag and keyring buttons when using the reduced actionbar layout. Hold Ctrl+Shift to move the bag bar."] = nil,
|
||||
["Show Micro Menu"] = nil,
|
||||
["Shows micro menu buttons when using the reduced actionbar layout. Hold Ctrl+Shift to move the micro menu."] = nil,
|
||||
["Unit Frame Abbreviated Names"] = nil,
|
||||
["Abbreviates long target and target-of-target names."] = nil,
|
||||
["World Chat Hider"] = nil,
|
||||
["Hides World channel messages while inside an instance without leaving the channel."] = nil,
|
||||
["Reveal World Map"] = nil,
|
||||
["Reveals unexplored world map areas and shows exploration hints."] = nil,
|
||||
["Reveal Unexplored"] = nil,
|
||||
["Exploration Point"] = nil,
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ ShaguTweaks:RegisterEvent("VARIABLES_LOADED")
|
||||
ShaguTweaks:SetScript("OnEvent", function()
|
||||
-- flag all external modules as unofficial
|
||||
if event == "ADDON_LOADED" then
|
||||
-- Most Extras ClassicAPI modules are built in now. With the old addon
|
||||
-- still enabled, its copies replace the built-in ones (same titles).
|
||||
if arg1 == "ShaguTweaks-extras" then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00ShaguTweaks:|r the Extras modules are now built in. Disable the |cffffcc00ShaguTweaks Extras ClassicAPI|r addon so they only run once.")
|
||||
end
|
||||
|
||||
ShaguTweaks.provider = nil
|
||||
official = false
|
||||
return
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Dragonflight Gryphons"],
|
||||
description = T["Replaces actionbar gryphons with the dragonflight version."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
maintainer = "@shagu (GitHub)",
|
||||
category = T["Action Bar"],
|
||||
config = {
|
||||
["dragonfly.gryphon"] = "retail",
|
||||
},
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
local gryphon = CreateFrame("Frame", nil, MainMenuBarArtFrame)
|
||||
gryphon:SetFrameStrata("HIGH")
|
||||
gryphon:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
gryphon:SetScript("OnEvent", function()
|
||||
-- replace original gryphons by dragonflight versions
|
||||
if module.config["dragonfly.gryphon"] == "retail" and UnitFactionGroup("player") == "Horde" then
|
||||
-- retail horde
|
||||
MainMenuBarLeftEndCap:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\df-wyvern")
|
||||
MainMenuBarRightEndCap:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\df-wyvern")
|
||||
elseif module.config["dragonfly.gryphon"] == "retail" and UnitFactionGroup("player") == "Alliance" then
|
||||
-- retail alliance
|
||||
MainMenuBarLeftEndCap:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\df-gryphon")
|
||||
MainMenuBarRightEndCap:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\df-gryphon")
|
||||
else
|
||||
-- beta artwork
|
||||
MainMenuBarLeftEndCap:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\df-gryphon-beta")
|
||||
MainMenuBarRightEndCap:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\df-gryphon-beta")
|
||||
end
|
||||
|
||||
-- move gryphons above action buttons
|
||||
MainMenuBarLeftEndCap:SetParent(this)
|
||||
MainMenuBarRightEndCap:SetParent(this)
|
||||
end)
|
||||
end
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local hooksecurefunc = ShaguTweaks.hooksecurefunc
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Floating Actionbar"],
|
||||
description = T["Removes all background textures and lets the actionbar float."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
maintainer = "@shagu (GitHub)",
|
||||
category = T["Action Bar"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local texture_removals = {
|
||||
MainMenuXPBarTexture0, MainMenuXPBarTexture1, MainMenuXPBarTexture2, MainMenuXPBarTexture3,
|
||||
ReputationXPBarTexture0, ReputationXPBarTexture1, ReputationXPBarTexture2, ReputationXPBarTexture3,
|
||||
ReputationWatchBarTexture0, ReputationWatchBarTexture1, ReputationWatchBarTexture2, ReputationWatchBarTexture3,
|
||||
MainMenuBarTexture0, MainMenuBarTexture1, MainMenuBarTexture2, MainMenuBarTexture3,
|
||||
BonusActionBarTexture1, BonusActionBarTexture0, BonusActionBarTexture2
|
||||
}
|
||||
|
||||
local actionbars = {
|
||||
"Action", "BonusAction", "MultiBarBottomLeft", "MultiBarBottomRight",
|
||||
"MultiBarLeft", "MultiBarRight", "Shapeshift"
|
||||
}
|
||||
|
||||
module.enable = function(self)
|
||||
MainMenuBar:ClearAllPoints()
|
||||
MainMenuBar:SetPoint("BOTTOM", 0, 8)
|
||||
|
||||
-- align actionbutton textures and add border
|
||||
for _, prefix in ipairs(actionbars) do
|
||||
for i = 1, NUM_ACTIONBAR_BUTTONS do
|
||||
local button = _G[prefix .. "Button" .. i]
|
||||
local texture = _G[prefix.."Button"..i.."NormalTexture"]
|
||||
|
||||
if button and texture then
|
||||
texture:SetWidth(60)
|
||||
texture:SetHeight(60)
|
||||
texture:SetPoint("CENTER", 0, 0)
|
||||
ShaguTweaks.AddBorder(button, 3, { r=.7, g=.7, b=.7, a=1 })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ShaguTweaks.AddBorder(MainMenuBarPerformanceBarFrameButton, { -12, -0.5, -8, 4.5 }, { r=.7, g=.7, b=.7, a=1 })
|
||||
|
||||
-- replace reputation bar texture
|
||||
ReputationWatchStatusBar:SetStatusBarTexture("Interface\\AddOns\\ShaguTweaks\\img\\xpbar")
|
||||
ReputationWatchStatusBarBackground:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\xpbar")
|
||||
ReputationWatchStatusBarBackground:SetVertexColor(0, 0, 0, .5)
|
||||
|
||||
-- replace experience bar texture
|
||||
MainMenuExpBar:SetStatusBarTexture("Interface\\AddOns\\ShaguTweaks\\img\\xpbar")
|
||||
local _, _, _, _, _, background = MainMenuExpBar:GetRegions()
|
||||
if background then
|
||||
background:SetTexture("Interface\\AddOns\\ShaguTweaks\\img\\xpbar")
|
||||
background:SetVertexColor(0, 0, 0, .5)
|
||||
end
|
||||
|
||||
-- center the experience bar when Floating Actionbar is used on its own.
|
||||
-- Reduced Actionbar applies its own matching offset, so avoid shifting it twice.
|
||||
local function UpdateExperiencePosition()
|
||||
local reducedEnabled = ShaguTweaks_config
|
||||
and ShaguTweaks_config[T["Reduced Actionbar Size"]] == 1
|
||||
|
||||
if reducedEnabled then return end
|
||||
|
||||
MainMenuExpBar:ClearAllPoints()
|
||||
if ReputationWatchBar:IsShown() then
|
||||
MainMenuExpBar:SetPoint("TOP", MainMenuBar, "TOP", 0, 0)
|
||||
else
|
||||
MainMenuExpBar:SetPoint("TOP", MainMenuBar, "TOP", 0, 3)
|
||||
end
|
||||
end
|
||||
|
||||
-- keep the custom reputation position without replacing Blizzard's updater
|
||||
local function UpdateReputationPosition()
|
||||
UpdateExperiencePosition()
|
||||
|
||||
ReputationWatchBar:ClearAllPoints()
|
||||
if MainMenuExpBar:IsShown() then
|
||||
ReputationWatchBar:SetPoint("BOTTOM", MainMenuBar, "TOP", 0, -7)
|
||||
else
|
||||
ReputationWatchBar:SetPoint("TOP", MainMenuBar, "TOP", 0, 2)
|
||||
end
|
||||
end
|
||||
|
||||
hooksecurefunc("ReputationWatchBar_Update", UpdateReputationPosition)
|
||||
|
||||
-- Blizzard can restore the default XP bar anchor after login/reloads and
|
||||
-- actionbar visibility changes, so re-apply the fixed floating offset.
|
||||
hooksecurefunc("UIParent_ManageFramePositions", UpdateExperiencePosition)
|
||||
|
||||
UpdateReputationPosition()
|
||||
|
||||
-- hide max level top frame
|
||||
MainMenuBarMaxLevelBar:SetAlpha(0)
|
||||
|
||||
-- remove textures
|
||||
for _, texture in ipairs(texture_removals) do
|
||||
if texture then
|
||||
texture:SetTexture()
|
||||
texture:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Hide Macro Text"],
|
||||
description = T["Hides macro names on action buttons."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Action Bar"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
local function HideMacroText(button)
|
||||
if not button or not button.GetName then return end
|
||||
|
||||
local name = button:GetName()
|
||||
local text = name and _G[name .. "Name"]
|
||||
if text then text:SetAlpha(0) end
|
||||
end
|
||||
|
||||
for i = 1, 24 do
|
||||
HideMacroText(_G["BonusActionButton" .. i])
|
||||
end
|
||||
|
||||
for i = 1, 12 do
|
||||
HideMacroText(_G["ActionButton" .. i])
|
||||
HideMacroText(_G["MultiBarRightButton" .. i])
|
||||
HideMacroText(_G["MultiBarLeftButton" .. i])
|
||||
HideMacroText(_G["MultiBarBottomLeftButton" .. i])
|
||||
HideMacroText(_G["MultiBarBottomRightButton" .. i])
|
||||
end
|
||||
end
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Mouseover Right"],
|
||||
description = T["Hide the right actionbar and show it on mouseover."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Action Bar"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
ShaguTweaks.CreateMouseoverActionBar(MultiBarRight, "SHOW_MULTI_ACTIONBAR_3")
|
||||
end
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Mouseover Right 2"],
|
||||
description = T["Hide the second right actionbar and show it on mouseover."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Action Bar"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
ShaguTweaks.CreateMouseoverActionBar(MultiBarLeft, "SHOW_MULTI_ACTIONBAR_4")
|
||||
end
|
||||
Executable
+138
@@ -0,0 +1,138 @@
|
||||
-- Shared mouseover actionbar helper.
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
|
||||
if not ShaguTweaks.CreateMouseoverActionBar then
|
||||
ShaguTweaks.CreateMouseoverActionBar = function(bar, visibilityFlag)
|
||||
if not bar or not visibilityFlag then return end
|
||||
if bar.ShaguTweaksMouseoverController then return end
|
||||
|
||||
local controller = CreateFrame("Frame", nil, UIParent)
|
||||
local hotspot = CreateFrame("Frame", nil, UIParent)
|
||||
|
||||
controller.bar = bar
|
||||
controller.hotspot = hotspot
|
||||
controller.visibilityFlag = visibilityFlag
|
||||
controller.enabled = false
|
||||
controller.elapsed = 0
|
||||
controller.idle = 0
|
||||
|
||||
bar.ShaguTweaksMouseoverController = controller
|
||||
|
||||
-- One invisible reveal area replaces the original per-button overlay
|
||||
-- frames. It only accepts mouse input while the actionbar is hidden.
|
||||
hotspot:SetAllPoints(bar)
|
||||
hotspot:SetFrameStrata("DIALOG")
|
||||
hotspot:EnableMouse(false)
|
||||
hotspot:Hide()
|
||||
|
||||
local function ReadEnabled()
|
||||
return _G[visibilityFlag] and true or false
|
||||
end
|
||||
|
||||
local function StopWatching()
|
||||
controller:SetScript("OnUpdate", nil)
|
||||
controller.elapsed = 0
|
||||
controller.idle = 0
|
||||
end
|
||||
|
||||
local function StartWatching()
|
||||
if not controller.enabled or not bar:IsShown() then return end
|
||||
|
||||
hotspot:EnableMouse(false)
|
||||
controller.elapsed = 0
|
||||
controller.idle = 0
|
||||
|
||||
controller:SetScript("OnUpdate", function()
|
||||
this.elapsed = this.elapsed + (arg1 or 0)
|
||||
if this.elapsed < .10 then return end
|
||||
|
||||
local step = this.elapsed
|
||||
this.elapsed = 0
|
||||
|
||||
if not this.enabled then
|
||||
StopWatching()
|
||||
hotspot:EnableMouse(false)
|
||||
hotspot:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
if not bar:IsShown() then
|
||||
StopWatching()
|
||||
hotspot:Show()
|
||||
hotspot:EnableMouse(true)
|
||||
return
|
||||
end
|
||||
|
||||
if MouseIsOver(bar) then
|
||||
this.idle = 0
|
||||
return
|
||||
end
|
||||
|
||||
this.idle = this.idle + step
|
||||
if this.idle >= 2 then
|
||||
StopWatching()
|
||||
bar:Hide()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function Sync()
|
||||
controller.enabled = ReadEnabled()
|
||||
|
||||
if not controller.enabled then
|
||||
StopWatching()
|
||||
hotspot:EnableMouse(false)
|
||||
hotspot:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
hotspot:Show()
|
||||
|
||||
if bar:IsShown() then
|
||||
StartWatching()
|
||||
else
|
||||
hotspot:EnableMouse(true)
|
||||
end
|
||||
end
|
||||
|
||||
hotspot:SetScript("OnEnter", function()
|
||||
if not controller.enabled then return end
|
||||
|
||||
hotspot:EnableMouse(false)
|
||||
bar:Show()
|
||||
-- The bar's OnShow script starts the watcher. Avoid a second redundant
|
||||
-- StartWatching() call here.
|
||||
end)
|
||||
|
||||
-- Keep existing scripts intact while observing native/UI-addon visibility
|
||||
-- changes. These wrappers are installed once per bar.
|
||||
local oldOnShow = bar:GetScript("OnShow")
|
||||
bar:SetScript("OnShow", function()
|
||||
if oldOnShow then oldOnShow() end
|
||||
if controller.enabled then StartWatching() end
|
||||
end)
|
||||
|
||||
local oldOnHide = bar:GetScript("OnHide")
|
||||
bar:SetScript("OnHide", function()
|
||||
if oldOnHide then oldOnHide() end
|
||||
|
||||
StopWatching()
|
||||
|
||||
if controller.enabled then
|
||||
hotspot:Show()
|
||||
hotspot:EnableMouse(true)
|
||||
else
|
||||
hotspot:EnableMouse(false)
|
||||
hotspot:Hide()
|
||||
end
|
||||
end)
|
||||
|
||||
controller:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
controller:RegisterEvent("CVAR_UPDATE")
|
||||
controller:SetScript("OnEvent", Sync)
|
||||
|
||||
Sync()
|
||||
end
|
||||
end
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Reagent Counter"],
|
||||
description = T["Shows a reagent counter on action buttons."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
maintainer = "@shagu (GitHub)",
|
||||
category = T["Action Bar"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
local reagent_slots = { }
|
||||
local reagent_counts = { }
|
||||
local bars = { "Action", "BonusAction", "MultiBarBottomLeft", "MultiBarBottomRight", "MultiBarLeft", "MultiBarRight" }
|
||||
|
||||
local reagentcounter = CreateFrame("Frame", "ShaguTweaksReagentCount", UIParent)
|
||||
reagentcounter:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
reagentcounter:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
reagentcounter:RegisterEvent("ACTIONBAR_PAGE_CHANGED")
|
||||
reagentcounter:RegisterEvent("UPDATE_BONUS_ACTIONBAR")
|
||||
reagentcounter:RegisterEvent("UPDATE_MACROS")
|
||||
reagentcounter:RegisterEvent("BAG_UPDATE")
|
||||
|
||||
-- Hidden frames still receive events, but do not execute OnUpdate.
|
||||
-- Wake for one frame only when work is pending.
|
||||
reagentcounter:Hide()
|
||||
reagentcounter:SetScript("OnEvent", function()
|
||||
this.pending = true
|
||||
if event ~= "BAG_UPDATE" then
|
||||
this.rescan = true
|
||||
end
|
||||
this:Show()
|
||||
end)
|
||||
|
||||
reagentcounter:SetScript("OnUpdate", function()
|
||||
if not this.pending then
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- Rebuild the action-slot -> reagent mapping only when the action layout
|
||||
-- changes. ClassicAPI resolves the spell/macro directly, so no tooltip
|
||||
-- scan or localized reagent-name parsing is required.
|
||||
if this.rescan then
|
||||
reagent_counts = {}
|
||||
for slot = 1, 120 do
|
||||
reagentcounter.ScanSlot(slot)
|
||||
end
|
||||
end
|
||||
|
||||
-- Count only reagents referenced by active actions. ClassicAPI reads the
|
||||
-- inventory directly, avoiding a Lua bag scan for every reagent.
|
||||
for itemID in pairs(reagent_counts) do
|
||||
reagent_counts[itemID] = API.GetItemCount(itemID, false, false) or 0
|
||||
end
|
||||
|
||||
-- update all actionbar buttons
|
||||
for _, prefix in ipairs(bars) do
|
||||
for i = 1, NUM_ACTIONBAR_BUTTONS do
|
||||
local button = _G[prefix .. "Button" .. i]
|
||||
if button then
|
||||
local text = _G[button:GetName().."Count"]
|
||||
local slot = ActionButton_GetPagedID(button)
|
||||
|
||||
if text then
|
||||
local itemID = reagent_slots[slot]
|
||||
if itemID then
|
||||
text:SetText(reagent_counts[itemID] or 0)
|
||||
elseif not IsConsumableAction(slot) then
|
||||
text:SetText()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
this.pending = nil
|
||||
this.rescan = nil
|
||||
this:Hide()
|
||||
end)
|
||||
|
||||
reagentcounter.ScanSlot = function(slot)
|
||||
reagent_slots[slot] = nil
|
||||
if not HasAction(slot) then return end
|
||||
|
||||
local actionType, actionID = API.GetActionInfo(slot)
|
||||
local spellID
|
||||
|
||||
if actionType == "spell" then
|
||||
spellID = actionID
|
||||
elseif actionType == "macro" and actionID then
|
||||
local _, _, macroSpellID = API.GetMacroSpell(actionID)
|
||||
spellID = macroSpellID
|
||||
end
|
||||
|
||||
if not spellID then return end
|
||||
|
||||
local reagents = API.GetSpellReagents(spellID)
|
||||
local reagent = reagents and reagents[1]
|
||||
if not reagent or not reagent.itemID then return end
|
||||
|
||||
-- The action button only has one count field. Preserve the historical
|
||||
-- behavior by showing the first reagent's owned stack count.
|
||||
reagent_slots[slot] = reagent.itemID
|
||||
reagent_counts[reagent.itemID] = reagent_counts[reagent.itemID] or 0
|
||||
end
|
||||
|
||||
-- The stock UI clears the Count fontstring on non-consumable actions whenever
|
||||
-- ActionButton_UpdateCount() runs. Re-apply our reagent value afterwards so
|
||||
-- login/reload refreshes cannot erase it a moment later.
|
||||
ShaguTweaks.hooksecurefunc("ActionButton_UpdateCount", function()
|
||||
local button = this
|
||||
if not button or not button.GetName then return end
|
||||
|
||||
local slot = ActionButton_GetPagedID(button)
|
||||
local itemID = slot and reagent_slots[slot]
|
||||
if not itemID then return end
|
||||
|
||||
local text = _G[button:GetName().."Count"]
|
||||
if text then
|
||||
text:SetText(reagent_counts[itemID] or 0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local rgbhex = ShaguTweaks.rgbhex
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Center Vertical Actionbar"],
|
||||
description = T["Center the vertical actionbar on the right side."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
maintainer = "@shagu (GitHub)",
|
||||
category = T["Action Bar"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
MultiBarRight:ClearAllPoints()
|
||||
MultiBarRight:SetPoint("RIGHT", 0, -16)
|
||||
end
|
||||
Executable
+662
@@ -0,0 +1,662 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
local HookScript = ShaguTweaks.HookScript
|
||||
local floor = math.floor
|
||||
local ceil = math.ceil
|
||||
local min = math.min
|
||||
local mod = math.mod or mod
|
||||
|
||||
STBuyEmAll = STBuyEmAll or {}
|
||||
local Buy = STBuyEmAll
|
||||
|
||||
local strings = {
|
||||
enUS = {
|
||||
["Max"] = "Max",
|
||||
["Stack"] = "Stack",
|
||||
["Are you sure you want to buy\n %d × %s?"] = "Are you sure you want to buy\n %d × %s?",
|
||||
["Stack purchase"] = "Stack purchase",
|
||||
["Stack size"] = "Stack size",
|
||||
["Partial stack"] = "Partial stack",
|
||||
["Maximum purchase"] = "Maximum purchase",
|
||||
["You can fit"] = "You can fit",
|
||||
["You can afford"] = "You can afford",
|
||||
["Vendor has"] = "Vendor has",
|
||||
},
|
||||
deDE = {
|
||||
["Are you sure you want to buy\n %d × %s?"] = "Bist du sicher das du\n %d × %s kaufen willst?",
|
||||
["Stack size"] = "Stack größe",
|
||||
["Maximum purchase"] = "Größt möglicher Einkauf",
|
||||
["You can fit"] = "Du hast Platz für",
|
||||
["You can afford"] = "Du kannst dir leisten",
|
||||
["Vendor has"] = "Der Verkäufer hat",
|
||||
},
|
||||
frFR = {
|
||||
["Stack"] = "Pile",
|
||||
["Are you sure you want to buy\n %d × %s?"] = "Voulez-vous vraiment acheter\n %d × %s?",
|
||||
["Stack size"] = "Taille de pile",
|
||||
["Maximum purchase"] = "Achat maximum",
|
||||
["You can fit"] = "Vous pouvez transporter",
|
||||
["You can afford"] = "Vous pouvez payer",
|
||||
["Vendor has"] = "Le marchand a",
|
||||
},
|
||||
}
|
||||
|
||||
local locale = strings[GetLocale()] or strings.enUS
|
||||
local function L(key)
|
||||
return locale[key] or strings.enUS[key] or key
|
||||
end
|
||||
|
||||
BUYEMALL_MAX = L("Max")
|
||||
BUYEMALL_STACK = L("Stack")
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Buy Em All"],
|
||||
description = T["Shift-click vendor items to choose stack, maximum, or a custom purchase amount."],
|
||||
expansions = { ["vanilla"] = true },
|
||||
category = T["Merchant"],
|
||||
enabled = true,
|
||||
})
|
||||
|
||||
local function FamilyMatches(itemFamily, bagFamily)
|
||||
bagFamily = tonumber(bagFamily) or 0
|
||||
itemFamily = tonumber(itemFamily) or 0
|
||||
|
||||
if bagFamily == 0 then return true end
|
||||
if itemFamily == 0 then return false end
|
||||
|
||||
-- Bag families are power-of-two bit flags. Avoid requiring an external bit
|
||||
-- library on 1.12 while still supporting Turtle custom specialty families.
|
||||
return mod(floor(itemFamily / bagFamily), 2) == 1
|
||||
end
|
||||
|
||||
function Buy:CountPurchaseItem()
|
||||
if not self.purchaseItemID then return nil end
|
||||
if API and API.GetItemCount then
|
||||
return API.GetItemCount(self.purchaseItemID, false, false)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Buy:StopPurchaseQueue()
|
||||
if self.purchaseFrame then self.purchaseFrame:Hide() end
|
||||
self.purchaseItemIndex = nil
|
||||
self.purchaseItemID = nil
|
||||
self.purchasePreset = 1
|
||||
self.purchaseLoops = 0
|
||||
self.purchaseAmount = 0
|
||||
self.purchaseLeftover = 0
|
||||
self.purchaseElapsed = 0
|
||||
self.purchaseWaiting = nil
|
||||
self.purchaseExpectedCount = nil
|
||||
self.purchasePendingKind = nil
|
||||
end
|
||||
|
||||
function Buy:AbortPurchaseQueue()
|
||||
self:StopPurchaseQueue()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("Buy Em All: purchase queue stopped because the previous stack was not confirmed. No further stacks were sent.")
|
||||
end
|
||||
|
||||
function Buy:ConfirmPendingPurchase()
|
||||
if not self.purchaseWaiting or not self.purchaseExpectedCount then return false end
|
||||
|
||||
local count = self:CountPurchaseItem()
|
||||
if count == nil or count < self.purchaseExpectedCount then return false end
|
||||
|
||||
if self.purchasePendingKind == "loop" then
|
||||
self.purchaseLoops = self.purchaseLoops - 1
|
||||
elseif self.purchasePendingKind == "leftover" then
|
||||
self.purchaseLeftover = 0
|
||||
end
|
||||
|
||||
self.purchaseWaiting = nil
|
||||
self.purchaseExpectedCount = nil
|
||||
self.purchasePendingKind = nil
|
||||
self.purchaseElapsed = 0
|
||||
return true
|
||||
end
|
||||
|
||||
function Buy:Purchase_OnEvent()
|
||||
self:ConfirmPendingPurchase()
|
||||
end
|
||||
|
||||
function Buy:SendPurchase(quantity, kind)
|
||||
local before = self:CountPurchaseItem()
|
||||
if before == nil then
|
||||
self:AbortPurchaseQueue()
|
||||
return
|
||||
end
|
||||
|
||||
self.purchaseWaiting = true
|
||||
self.purchasePendingKind = kind
|
||||
self.purchaseExpectedCount = before + quantity * (self.purchasePreset or 1)
|
||||
self.purchaseElapsed = 0
|
||||
BuyMerchantItem(self.purchaseItemIndex, quantity)
|
||||
end
|
||||
|
||||
function Buy:Purchase_OnUpdate(elapsed)
|
||||
self.purchaseElapsed = (self.purchaseElapsed or 0) + (elapsed or 0)
|
||||
|
||||
if self.purchaseWaiting then
|
||||
if self.purchaseElapsed >= self.purchaseTimeout then
|
||||
if not self:ConfirmPendingPurchase() then self:AbortPurchaseQueue() end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if self.purchaseElapsed < self.purchaseDelay then return end
|
||||
self.purchaseElapsed = 0
|
||||
|
||||
if self.purchaseLoops and self.purchaseLoops > 0 then
|
||||
self:SendPurchase(self.purchaseAmount, "loop")
|
||||
return
|
||||
end
|
||||
|
||||
if self.purchaseLeftover and self.purchaseLeftover > 0 then
|
||||
self:SendPurchase(self.purchaseLeftover, "leftover")
|
||||
return
|
||||
end
|
||||
|
||||
self:StopPurchaseQueue()
|
||||
end
|
||||
|
||||
function Buy:DoPurchase(amount)
|
||||
if not amount or amount <= 0 or not self.itemIndex or not self.preset or not self.stack then return end
|
||||
if STBuyEmAllFrame then STBuyEmAllFrame:Hide() end
|
||||
|
||||
local numLoops, purchaseAmount, leftover
|
||||
if self.preset > 1 then
|
||||
numLoops = floor(amount / self.preset)
|
||||
purchaseAmount = 1
|
||||
leftover = 0
|
||||
else
|
||||
numLoops = floor(amount / self.stack)
|
||||
purchaseAmount = self.stack
|
||||
leftover = mod(amount, self.stack)
|
||||
end
|
||||
|
||||
self:StopPurchaseQueue()
|
||||
self.purchaseItemIndex = self.itemIndex
|
||||
self.purchaseItemID = self.itemID
|
||||
self.purchasePreset = self.preset
|
||||
self.purchaseLoops = numLoops
|
||||
self.purchaseAmount = purchaseAmount
|
||||
self.purchaseLeftover = leftover
|
||||
self.purchaseElapsed = self.purchaseDelay
|
||||
|
||||
if self.purchaseLoops > 0 or self.purchaseLeftover > 0 then
|
||||
self.purchaseFrame:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function Buy:DoConfirmation(amount)
|
||||
self.confirmAmount = amount
|
||||
local dialog = StaticPopup_Show("STBUYEMALL_CONFIRM", amount, self.itemName)
|
||||
if dialog then dialog.data = amount end
|
||||
end
|
||||
|
||||
function Buy:VerifyPurchase(amount)
|
||||
amount = amount or self.split
|
||||
if not amount or amount <= 0 or not self.preset or self.preset <= 0 then return end
|
||||
|
||||
amount = ceil(amount / self.preset) * self.preset
|
||||
if amount > self.max then amount = self.max end
|
||||
|
||||
if amount > self.stack and amount > self.defaultStack then
|
||||
self:DoConfirmation(amount)
|
||||
else
|
||||
self:DoPurchase(amount)
|
||||
end
|
||||
end
|
||||
|
||||
function Buy:SetStackClick()
|
||||
if not self.stack or self.stack <= 0 then
|
||||
self.stackClick = self.max or self.split or 1
|
||||
return
|
||||
end
|
||||
|
||||
local targetRemainder = self.partialFit or 0
|
||||
if targetRemainder == 0 then targetRemainder = self.stack end
|
||||
|
||||
local currentRemainder = mod(self.split, self.stack)
|
||||
local increase = targetRemainder - currentRemainder
|
||||
if increase <= 0 then increase = increase + self.stack end
|
||||
|
||||
self.stackClick = self.split + increase
|
||||
end
|
||||
|
||||
function Buy:UpdateDisplay(amount)
|
||||
if not self.preset or not self.price or not self.split then return end
|
||||
|
||||
local purchase = ceil((amount or self.split) / self.preset)
|
||||
local cost = purchase * self.price
|
||||
if STBuyEmAllMoneyFrame then
|
||||
MoneyFrame_Update("STBuyEmAllMoneyFrame", cost)
|
||||
end
|
||||
|
||||
if not amount and not self.isUpdating then
|
||||
self.isUpdating = true
|
||||
|
||||
STBuyEmAllText:SetText(self.split)
|
||||
STBuyEmAllLeftButton:Enable()
|
||||
STBuyEmAllRightButton:Enable()
|
||||
|
||||
if self.split >= self.max then
|
||||
STBuyEmAllRightButton:Disable()
|
||||
elseif self.split <= self.preset then
|
||||
STBuyEmAllLeftButton:Disable()
|
||||
end
|
||||
|
||||
self:SetStackClick()
|
||||
STBuyEmAllStackButton:Enable()
|
||||
if not self.stackClick or self.max < self.stackClick then
|
||||
STBuyEmAllStackButton:Disable()
|
||||
end
|
||||
|
||||
self.isUpdating = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Buy:Show(anchor)
|
||||
self.typing = 0
|
||||
STBuyEmAllLeftButton:Disable()
|
||||
STBuyEmAllRightButton:Enable()
|
||||
|
||||
self:SetStackClick()
|
||||
STBuyEmAllStackButton:Enable()
|
||||
if not self.stackClick or self.max < self.stackClick then STBuyEmAllStackButton:Disable() end
|
||||
|
||||
STBuyEmAllFrame:ClearAllPoints()
|
||||
STBuyEmAllFrame:SetPoint("BOTTOMLEFT", anchor, "TOPLEFT", 0, 4)
|
||||
STBuyEmAllFrame:Show()
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
|
||||
function Buy:OnClick()
|
||||
if this == STBuyEmAllOkayButton then
|
||||
self:VerifyPurchase()
|
||||
elseif this == STBuyEmAllCancelButton then
|
||||
STBuyEmAllFrame:Hide()
|
||||
elseif this == STBuyEmAllStackButton then
|
||||
if self.stackClick and self.stackClick <= self.max then
|
||||
self.split = self.stackClick
|
||||
self:UpdateDisplay()
|
||||
if this:IsEnabled() == 1 then self:OnEnter() else GameTooltip:Hide() end
|
||||
end
|
||||
elseif this == STBuyEmAllMaxButton then
|
||||
self.split = self.max
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
end
|
||||
|
||||
function Buy:OnChar()
|
||||
local digit = tonumber(arg1)
|
||||
if digit == nil then return end
|
||||
|
||||
if self.typing == 0 then
|
||||
self.typing = 1
|
||||
self.split = 0
|
||||
end
|
||||
|
||||
local split = (self.split * 10) + digit
|
||||
if split <= self.max then
|
||||
self.split = split
|
||||
end
|
||||
|
||||
if self.split <= 0 then self.split = 1 end
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
|
||||
function Buy:OnKeyDown()
|
||||
if arg1 == "BACKSPACE" or arg1 == "DELETE" then
|
||||
if self.typing == 0 or self.split == 1 then return end
|
||||
|
||||
self.split = floor(self.split / 10)
|
||||
if self.split <= 1 then
|
||||
self.split = 1
|
||||
self.typing = 0
|
||||
end
|
||||
self:UpdateDisplay()
|
||||
elseif arg1 == "ENTER" then
|
||||
self:VerifyPurchase()
|
||||
elseif arg1 == "ESCAPE" then
|
||||
STBuyEmAllFrame:Hide()
|
||||
elseif arg1 == "LEFT" or arg1 == "DOWN" then
|
||||
self:Left_Click()
|
||||
elseif arg1 == "RIGHT" or arg1 == "UP" then
|
||||
self:Right_Click()
|
||||
end
|
||||
end
|
||||
|
||||
function Buy:Left_Click()
|
||||
if self.split <= self.preset then return end
|
||||
|
||||
local decrease = mod(self.split, self.preset)
|
||||
decrease = decrease == 0 and self.preset or decrease
|
||||
self.split = self.split - decrease
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
|
||||
function Buy:Right_Click()
|
||||
local increase = self.preset - mod(self.split, self.preset)
|
||||
if self.split + increase > self.max then return end
|
||||
|
||||
self.split = self.split + increase
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
|
||||
Buy.lines = {
|
||||
stack = {
|
||||
label = L("Stack purchase"),
|
||||
field = "stackClick",
|
||||
{ label = L("Stack size"), field = "stack" },
|
||||
{ label = L("Partial stack"), field = "partialFit" },
|
||||
},
|
||||
max = {
|
||||
label = L("Maximum purchase"),
|
||||
field = "max",
|
||||
{ label = L("You can fit"), field = "fit" },
|
||||
{ label = L("You can afford"), field = "afford" },
|
||||
{
|
||||
label = L("Vendor has"),
|
||||
field = "available",
|
||||
Hide = function()
|
||||
return not Buy.available or Buy.available <= 1
|
||||
end,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
function Buy:CreateTooltip(lines)
|
||||
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT")
|
||||
GameTooltip:SetText(lines.label .. "|cFFFFFFFF - |r" .. GREEN_FONT_COLOR_CODE .. (lines.amount or 0) .. "|r")
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
if not (line.Hide and line.Hide()) then
|
||||
local color = line.amount == lines.amount and GREEN_FONT_COLOR or HIGHLIGHT_FONT_COLOR
|
||||
GameTooltip:AddDoubleLine(line.label, line.amount or 0, 1, 1, 1, color.r, color.g, color.b)
|
||||
end
|
||||
end
|
||||
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
function Buy:OnEnter()
|
||||
local lines = self.lines[this == STBuyEmAllMaxButton and "max" or "stack"]
|
||||
lines.amount = self[lines.field]
|
||||
for _, line in ipairs(lines) do line.amount = self[line.field] end
|
||||
|
||||
self:CreateTooltip(lines)
|
||||
self:UpdateDisplay(lines.amount)
|
||||
end
|
||||
|
||||
function Buy:OnLeave()
|
||||
self:UpdateDisplay()
|
||||
GameTooltip:Hide()
|
||||
end
|
||||
|
||||
function Buy:OnHide()
|
||||
StaticPopup_Hide("STBUYEMALL_CONFIRM")
|
||||
end
|
||||
|
||||
function Buy:FreeBagSpace(itemID)
|
||||
if not itemID then return 0, 1 end
|
||||
|
||||
local stackSize = API.GetItemMaxStackSizeByID(itemID) or 1
|
||||
if stackSize < 1 then stackSize = 1 end
|
||||
|
||||
local itemFamily = API.GetItemFamily(itemID) or 0
|
||||
local capacity = 0
|
||||
|
||||
for bag = 0, 4 do
|
||||
local freeSlots, bagFamily = API.GetContainerNumFreeSlots(bag)
|
||||
if FamilyMatches(itemFamily, bagFamily) then
|
||||
capacity = capacity + (freeSlots or 0) * stackSize
|
||||
end
|
||||
|
||||
local numSlots = GetContainerNumSlots(bag)
|
||||
for slot = 1, numSlots do
|
||||
local bagItemID = API.GetContainerItemID(bag, slot)
|
||||
if bagItemID == itemID then
|
||||
local itemCount = API.GetContainerItemStackCount(bag, slot) or 0
|
||||
if itemCount < stackSize then
|
||||
capacity = capacity + stackSize - itemCount
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return capacity, stackSize
|
||||
end
|
||||
|
||||
function Buy:GetMerchantData(index)
|
||||
local info = API.GetMerchantItemInfo and API.GetMerchantItemInfo(index)
|
||||
if info and info.itemID then
|
||||
local name = API.GetItemNameByID(info.itemID)
|
||||
if not name then
|
||||
name = GetMerchantItemInfo(index)
|
||||
end
|
||||
|
||||
return name, info.itemID, info.price or 0, info.stackCount or 1,
|
||||
info.numAvailable or -1
|
||||
end
|
||||
|
||||
local name, _, price, quantity, numAvailable = GetMerchantItemInfo(index)
|
||||
local itemID = API.GetMerchantItemID and API.GetMerchantItemID(index)
|
||||
return name, itemID, price, quantity, numAvailable
|
||||
end
|
||||
|
||||
function Buy:HandleMerchantClick(anchor, button, ignoreModifiers)
|
||||
if not anchor or not anchor.GetID then return false end
|
||||
|
||||
if MerchantFrame.selectedTab ~= 1
|
||||
or not API.IsShiftKeyDown()
|
||||
or API.IsControlKeyDown()
|
||||
or ignoreModifiers
|
||||
or (ChatFrameEditBox:IsVisible() and button == "LeftButton") then
|
||||
return false
|
||||
end
|
||||
|
||||
self.itemIndex = anchor:GetID()
|
||||
local name, itemID, price, quantity, numAvailable = self:GetMerchantData(self.itemIndex)
|
||||
if not name or not itemID or not price or not quantity or quantity < 1 then return false end
|
||||
|
||||
self.preset = quantity
|
||||
self.price = price
|
||||
self.itemName = name
|
||||
self.itemID = itemID
|
||||
self.available = numAvailable
|
||||
|
||||
local bagCapacity, stack = self:FreeBagSpace(itemID)
|
||||
self.stack = stack
|
||||
self.fit = floor(bagCapacity / quantity) * quantity
|
||||
|
||||
if price > 0 then
|
||||
self.afford = floor(GetMoney() / price) * quantity
|
||||
else
|
||||
self.afford = self.fit
|
||||
end
|
||||
|
||||
self.max = min(self.fit, self.afford)
|
||||
if numAvailable and numAvailable > -1 then
|
||||
self.max = min(self.max, numAvailable * quantity)
|
||||
end
|
||||
|
||||
if self.max <= 0 then return true end
|
||||
if self.max == 1 then
|
||||
if self.originalMerchantItemButton_OnClick then
|
||||
self.originalMerchantItemButton_OnClick("LeftButton", 1)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
self.defaultStack = quantity
|
||||
if self.defaultStack > self.max then self.defaultStack = self.max end
|
||||
|
||||
self.split = self.defaultStack
|
||||
self.partialFit = mod(self.fit, stack)
|
||||
self:SetStackClick()
|
||||
self:Show(anchor)
|
||||
return true
|
||||
end
|
||||
|
||||
function Buy:CreateUI()
|
||||
if STBuyEmAllFrame then return end
|
||||
|
||||
local frame = CreateFrame("Frame", "STBuyEmAllFrame", MerchantFrame)
|
||||
frame:SetWidth(210)
|
||||
frame:SetHeight(110)
|
||||
frame:SetFrameStrata("DIALOG")
|
||||
frame:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 8, edgeSize = 16,
|
||||
insets = { left = 4, right = 4, top = 4, bottom = 4 },
|
||||
})
|
||||
frame:SetBackdropColor(0, 0, 0, .9)
|
||||
frame:EnableKeyboard(true)
|
||||
frame:SetScript("OnChar", function() Buy:OnChar() end)
|
||||
frame:SetScript("OnKeyDown", function() Buy:OnKeyDown() end)
|
||||
frame:SetScript("OnHide", function() Buy:OnHide() end)
|
||||
frame:Hide()
|
||||
|
||||
local title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
title:SetPoint("TOP", frame, "TOP", 0, -10)
|
||||
title:SetText(T["Buy Em All"])
|
||||
|
||||
local amount = frame:CreateFontString("STBuyEmAllText", "ARTWORK", "GameFontHighlight")
|
||||
amount:SetWidth(50)
|
||||
amount:SetPoint("TOP", frame, "TOP", 0, -29)
|
||||
amount:SetText("1")
|
||||
|
||||
local left = CreateFrame("Button", "STBuyEmAllLeftButton", frame, "UIPanelButtonTemplate")
|
||||
left:SetWidth(28)
|
||||
left:SetHeight(22)
|
||||
left:SetPoint("RIGHT", amount, "LEFT", -4, 0)
|
||||
left:SetText("-")
|
||||
left:SetScript("OnClick", function() Buy:Left_Click() end)
|
||||
|
||||
local right = CreateFrame("Button", "STBuyEmAllRightButton", frame, "UIPanelButtonTemplate")
|
||||
right:SetWidth(28)
|
||||
right:SetHeight(22)
|
||||
right:SetPoint("LEFT", amount, "RIGHT", 4, 0)
|
||||
right:SetText("+")
|
||||
right:SetScript("OnClick", function() Buy:Right_Click() end)
|
||||
|
||||
local stack = CreateFrame("Button", "STBuyEmAllStackButton", frame, "UIPanelButtonTemplate")
|
||||
stack:SetWidth(58)
|
||||
stack:SetHeight(22)
|
||||
stack:SetPoint("TOPLEFT", frame, "TOPLEFT", 12, -50)
|
||||
stack:SetText(BUYEMALL_STACK)
|
||||
stack:SetScript("OnClick", function() Buy:OnClick() end)
|
||||
stack:SetScript("OnEnter", function() Buy:OnEnter() end)
|
||||
stack:SetScript("OnLeave", function() Buy:OnLeave() end)
|
||||
|
||||
local maxButton = CreateFrame("Button", "STBuyEmAllMaxButton", frame, "UIPanelButtonTemplate")
|
||||
maxButton:SetWidth(58)
|
||||
maxButton:SetHeight(22)
|
||||
maxButton:SetPoint("LEFT", stack, "RIGHT", 4, 0)
|
||||
maxButton:SetText(BUYEMALL_MAX)
|
||||
maxButton:SetScript("OnClick", function() Buy:OnClick() end)
|
||||
maxButton:SetScript("OnEnter", function() Buy:OnEnter() end)
|
||||
maxButton:SetScript("OnLeave", function() Buy:OnLeave() end)
|
||||
|
||||
local okay = CreateFrame("Button", "STBuyEmAllOkayButton", frame, "UIPanelButtonTemplate")
|
||||
okay:SetWidth(58)
|
||||
okay:SetHeight(22)
|
||||
okay:SetPoint("LEFT", maxButton, "RIGHT", 4, 0)
|
||||
okay:SetText(OKAY)
|
||||
okay:SetScript("OnClick", function() Buy:OnClick() end)
|
||||
|
||||
local cancel = CreateFrame("Button", "STBuyEmAllCancelButton", frame, "UIPanelButtonTemplate")
|
||||
cancel:SetWidth(58)
|
||||
cancel:SetHeight(22)
|
||||
cancel:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, -78)
|
||||
cancel:SetText(CANCEL)
|
||||
cancel:SetScript("OnClick", function() Buy:OnClick() end)
|
||||
|
||||
local money = CreateFrame("Frame", "STBuyEmAllMoneyFrame", frame, "SmallMoneyFrameTemplate")
|
||||
money:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 12, 11)
|
||||
|
||||
MoneyTypeInfo["STBUYEMALL"] = {
|
||||
UpdateFunc = function() return 0 end,
|
||||
showSmallerCoins = "Backpack",
|
||||
fixedWidth = 1,
|
||||
collapse = 1,
|
||||
}
|
||||
money.info = MoneyTypeInfo["STBUYEMALL"]
|
||||
money.moneyType = "STBUYEMALL"
|
||||
money.small = 1
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if IsAddOnLoaded("BuyEmAll") then return end
|
||||
|
||||
local required = {
|
||||
"GetItemCount", "GetItemMaxStackSizeByID", "GetItemFamily",
|
||||
"GetContainerNumFreeSlots", "GetContainerItemID",
|
||||
"GetContainerItemStackCount", "GetMerchantItemInfo",
|
||||
"GetMerchantItemID", "IsShiftKeyDown", "IsControlKeyDown",
|
||||
}
|
||||
for _, name in ipairs(required) do
|
||||
if not API or type(API[name]) ~= "function" then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffff5555ShaguTweaks:|r Buy Em All requires ClassicAPI.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
Buy:CreateUI()
|
||||
|
||||
Buy.purchaseDelay = 0.05
|
||||
Buy.purchaseTimeout = 3.0
|
||||
Buy.purchaseFrame = Buy.purchaseFrame or CreateFrame("Frame", "STBuyEmAllPurchaseQueue", UIParent)
|
||||
Buy.purchaseFrame:Hide()
|
||||
Buy.purchaseFrame:RegisterEvent("BAG_UPDATE")
|
||||
Buy.purchaseFrame:RegisterEvent("MERCHANT_UPDATE")
|
||||
Buy.purchaseFrame:SetScript("OnEvent", function()
|
||||
Buy:Purchase_OnEvent()
|
||||
end)
|
||||
Buy.purchaseFrame:SetScript("OnUpdate", function()
|
||||
Buy:Purchase_OnUpdate(arg1)
|
||||
end)
|
||||
|
||||
StaticPopupDialogs["STBUYEMALL_CONFIRM"] = {
|
||||
text = L("Are you sure you want to buy\n %d × %s?"),
|
||||
button1 = YES,
|
||||
button2 = NO,
|
||||
OnAccept = function()
|
||||
Buy:DoPurchase(Buy.confirmAmount)
|
||||
end,
|
||||
timeout = 0,
|
||||
hideOnEscape = true,
|
||||
}
|
||||
|
||||
HookScript(MerchantFrame, "OnHide", function()
|
||||
STBuyEmAllFrame:Hide()
|
||||
Buy:StopPurchaseQueue()
|
||||
end)
|
||||
|
||||
local function MerchantItemButton_OnClick_Wrapper(button, ignoreModifiers)
|
||||
local anchor = this
|
||||
if Buy:HandleMerchantClick(anchor, button, ignoreModifiers) then return end
|
||||
if Buy.originalMerchantItemButton_OnClick then
|
||||
return Buy.originalMerchantItemButton_OnClick(button, ignoreModifiers)
|
||||
end
|
||||
end
|
||||
|
||||
local function InstallHook()
|
||||
local current = _G.MerchantItemButton_OnClick
|
||||
if type(current) ~= "function" then return end
|
||||
if current == MerchantItemButton_OnClick_Wrapper then return end
|
||||
|
||||
Buy.originalMerchantItemButton_OnClick = current
|
||||
_G.MerchantItemButton_OnClick = MerchantItemButton_OnClick_Wrapper
|
||||
end
|
||||
|
||||
InstallHook()
|
||||
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
frame:RegisterEvent("MERCHANT_SHOW")
|
||||
frame:SetScript("OnEvent", InstallHook)
|
||||
end
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
-- Shared chat-filter dispatcher for the chat filter modules (formerly in Extras).
|
||||
-- Installs one ChatFrame_OnEvent wrapper only when a filter is enabled.
|
||||
|
||||
local Filter = ShaguTweaks.ExtrasChatFilter
|
||||
|
||||
if not Filter then
|
||||
Filter = {
|
||||
handlers = {},
|
||||
order = {},
|
||||
known = {},
|
||||
installed = false,
|
||||
installer = nil,
|
||||
}
|
||||
ShaguTweaks.ExtrasChatFilter = Filter
|
||||
end
|
||||
|
||||
function Filter:Install()
|
||||
if self.installed then return end
|
||||
|
||||
self.installed = true
|
||||
self.original = ChatFrame_OnEvent
|
||||
|
||||
ChatFrame_OnEvent = function(event)
|
||||
for i = 1, table.getn(Filter.order) do
|
||||
local current = Filter.handlers[Filter.order[i]]
|
||||
if current and current(event) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if Filter.original then
|
||||
return Filter.original(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Filter:ScheduleInstall()
|
||||
if self.installed or self.installer then return end
|
||||
|
||||
-- ShaguTweaks enables modules during VARIABLES_LOADED in unspecified table
|
||||
-- order. Install on PLAYER_ENTERING_WORLD so this dispatcher becomes the
|
||||
-- outer wrapper after the core Chat Spam Filter regardless of module order.
|
||||
-- That prevents locally hidden World/NPC messages from entering its cache.
|
||||
self.installer = CreateFrame("Frame")
|
||||
self.installer:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
self.installer:SetScript("OnEvent", function()
|
||||
Filter:Install()
|
||||
this:UnregisterAllEvents()
|
||||
this:Hide()
|
||||
Filter.installer = nil
|
||||
end)
|
||||
end
|
||||
|
||||
function Filter:Register(key, handler)
|
||||
if type(key) ~= "string" or type(handler) ~= "function" then return end
|
||||
|
||||
if not self.known[key] then
|
||||
self.known[key] = true
|
||||
self.order[table.getn(self.order) + 1] = key
|
||||
end
|
||||
|
||||
self.handlers[key] = handler
|
||||
self:ScheduleInstall()
|
||||
end
|
||||
|
||||
function Filter:Unregister(key)
|
||||
self.handlers[key] = nil
|
||||
end
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local hooksecurefunc = ShaguTweaks.hooksecurefunc
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Center Text Input Box"],
|
||||
description = T["Move the chat input box to the center of the screen."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
category = T["Chat & Social"],
|
||||
maintainer = "@shagu (GitHub)",
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local dodge_frames = {
|
||||
MainMenuBarArtFrame, MultiBarBottomLeft, MultiBarBottomRight, PetActionBarFrame, ShapeshiftBarFrame
|
||||
}
|
||||
|
||||
module.enable = function(self)
|
||||
ChatFrameEditBox:SetWidth(300)
|
||||
|
||||
local function UpdateInputPosition()
|
||||
local top = 0
|
||||
for _, frame in ipairs(dodge_frames) do
|
||||
if frame and frame:IsVisible() and frame:GetTop() then
|
||||
top = math.max(top, frame:GetTop())
|
||||
end
|
||||
end
|
||||
|
||||
ChatFrameEditBox:ClearAllPoints()
|
||||
ChatFrameEditBox:SetPoint("BOTTOM", UIParent, "BOTTOM", 0, top)
|
||||
end
|
||||
|
||||
-- Other ShaguTweaks modules also post-hook UIParent_ManageFramePositions.
|
||||
-- Module enable order is intentionally unordered, so install this layout
|
||||
-- hook at PLAYER_ENTERING_WORLD after the normal module pass. This makes
|
||||
-- the edit box position use the final actionbar/pet/stance positions.
|
||||
local installer = CreateFrame("Frame", nil, UIParent)
|
||||
installer:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
installer:SetScript("OnEvent", function()
|
||||
hooksecurefunc("UIParent_ManageFramePositions", UpdateInputPosition)
|
||||
UpdateInputPosition()
|
||||
this:UnregisterAllEvents()
|
||||
this:Hide()
|
||||
end)
|
||||
end
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
local Filter = ShaguTweaks.ExtrasChatFilter
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Block NPC Spam"],
|
||||
description = T["Blocks known repetitive NPC say/yell spam messages."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Chat & Social"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local blockedEvents = {
|
||||
["CHAT_MSG_MONSTER_SAY"] = true,
|
||||
["CHAT_MSG_MONSTER_YELL"] = true,
|
||||
}
|
||||
|
||||
local blockedNPCs = {}
|
||||
local npcNames = {
|
||||
-- enUS
|
||||
"Tansy Sparkpen",
|
||||
"Fara Boltbreaker",
|
||||
"Shellcoin Promoter",
|
||||
|
||||
-- ruRU
|
||||
"Зазывала ярмарки Новолуния",
|
||||
"Томас Миллер",
|
||||
"Уильям",
|
||||
"Донна",
|
||||
"Жюстина Демалье",
|
||||
"Мерлис Малаган",
|
||||
"Лиана Пирс",
|
||||
"Сюзанна",
|
||||
"Джейни Аншип",
|
||||
}
|
||||
|
||||
for _, name in pairs(npcNames) do
|
||||
blockedNPCs[name] = true
|
||||
blockedNPCs[string.lower(name)] = true
|
||||
end
|
||||
|
||||
local blockedPhrases = {
|
||||
{ "shellcoin", "invest" },
|
||||
{ "shells", "trade" },
|
||||
{ "shells", "money" },
|
||||
}
|
||||
|
||||
local function IsBlockedNPC(sender)
|
||||
if not sender then return false end
|
||||
return blockedNPCs[sender] or blockedNPCs[string.lower(sender)] or false
|
||||
end
|
||||
|
||||
local function IsBlockedPhrase(message)
|
||||
if not message then return false end
|
||||
|
||||
local normalized = string.lower(string.gsub(message, "[^A-Za-z0-9]", ""))
|
||||
|
||||
for _, phrase in pairs(blockedPhrases) do
|
||||
local matched = true
|
||||
|
||||
for _, word in pairs(phrase) do
|
||||
if not string.find(normalized, word, 1, true) then
|
||||
matched = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if matched then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function FilterNPCSpam(event)
|
||||
if not blockedEvents[event] then return false end
|
||||
if not arg1 or not arg2 then return false end
|
||||
|
||||
return IsBlockedNPC(arg2) or IsBlockedPhrase(arg1)
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if Filter then
|
||||
Filter:Register("BlockNPCSpam", FilterNPCSpam)
|
||||
end
|
||||
end
|
||||
|
||||
module.disable = function(self)
|
||||
if Filter then
|
||||
Filter:Unregister("BlockNPCSpam")
|
||||
end
|
||||
end
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local rgbhex = ShaguTweaks.rgbhex
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Enable Text Shadow"],
|
||||
description = T["Enable text shadow in all chat frames."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
category = T["Chat & Social"],
|
||||
maintainer = "@shagu (GitHub)",
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
for i=1,NUM_CHAT_WINDOWS do
|
||||
local font, size = _G["ChatFrame"..i]:GetFont()
|
||||
_G["ChatFrame"..i]:SetFont(font, size, "OUTLINE")
|
||||
end
|
||||
end
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local rgbhex = ShaguTweaks.rgbhex
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Chat Timestamps"],
|
||||
description = T["Add timestamps to chat messages."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
category = T["Chat & Social"],
|
||||
maintainer = "@shagu (GitHub)",
|
||||
enabled = false,
|
||||
config = {
|
||||
["chat.timestamp.bracket"] = "[]",
|
||||
["chat.timestamp.format"] = 24,
|
||||
["chat.timestamp.color"] = { r = .8, g = .8, b = .8, a = 1},
|
||||
}
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
-- config shortcuts
|
||||
local bracket = self.config["chat.timestamp.bracket"]
|
||||
local clock = self.config["chat.timestamp.format"]
|
||||
local rgb = self.config["chat.timestamp.color"]
|
||||
|
||||
-- parse config
|
||||
local left = string.sub(bracket, 1, 1) or ""
|
||||
local right = string.sub(bracket, 2, 2) or ""
|
||||
local format = clock == 24 and "%H:%M:%S" or "%I:%M:%S %p"
|
||||
local color = rgbhex({ rgb.r, rgb.g, rgb.b, rgb.a })
|
||||
|
||||
local function InstallTimestampHooks()
|
||||
for i = 1, NUM_CHAT_WINDOWS do
|
||||
local frame = _G["ChatFrame"..i]
|
||||
if frame and not frame.ShaguTweaksExtrasTimestampAddMessage then
|
||||
frame.ShaguTweaksExtrasTimestampAddMessage = frame.AddMessage
|
||||
frame.AddMessage = function(self, msg, a1, a2, a3, a4, a5)
|
||||
if not msg then return end
|
||||
|
||||
msg = color .. left .. date(format) .. right .. "|r " .. msg
|
||||
self:ShaguTweaksExtrasTimestampAddMessage(msg, a1, a2, a3, a4, a5)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Install after the normal ShaguTweaks module pass. Chat Tweaks in the
|
||||
-- main fork also wraps AddMessage, and its module order is intentionally
|
||||
-- unordered. Waiting for PLAYER_ENTERING_WORLD makes timestamps the outer
|
||||
-- wrapper every time, so history consistently stores the displayed time.
|
||||
local installer = CreateFrame("Frame", nil, UIParent)
|
||||
installer:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
installer:SetScript("OnEvent", function()
|
||||
InstallTimestampHooks()
|
||||
this:UnregisterAllEvents()
|
||||
this:Hide()
|
||||
end)
|
||||
end
|
||||
Executable
+299
@@ -0,0 +1,299 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local selfcastTitle = T["Alt Self-Cast"]
|
||||
|
||||
local selfcast = ShaguTweaks:register({
|
||||
title = selfcastTitle,
|
||||
description = T["When Key-Down Casting is enabled, hold Alt to cast eligible actions on yourself."],
|
||||
expansions = { ["vanilla"] = true },
|
||||
category = T["Action Bar"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
selfcast.enable = function(self)
|
||||
-- Runtime behavior is handled by Key-Down Casting below.
|
||||
end
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Key-Down Casting"],
|
||||
description = T["Activates action-bar and pet actions when the key is pressed instead of when it is released."],
|
||||
expansions = { ["vanilla"] = true },
|
||||
category = T["Action Bar"],
|
||||
enabled = true,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
-- Keep this feature independent from No Toggle Auto-Attack.
|
||||
if IsAddOnLoaded("ClassicSnowFall") or IsAddOnLoaded("ClassicSnowfall") then return end
|
||||
|
||||
local function SelfCast()
|
||||
if ShaguTweaks_config and ShaguTweaks_config[selfcastTitle] == 1 then
|
||||
return API.IsAltKeyDown() and 1 or 0
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Preserve the original slash command while using a unique registry key.
|
||||
SLASH_STCLASSICSNOWFALLSELFCAST1 = "/csselfcast"
|
||||
SlashCmdList["STCLASSICSNOWFALLSELFCAST"] = function()
|
||||
ShaguTweaks_config = ShaguTweaks_config or {}
|
||||
if ShaguTweaks_config[selfcastTitle] == 1 then
|
||||
ShaguTweaks_config[selfcastTitle] = 0
|
||||
DEFAULT_CHAT_FRAME:AddMessage("Classic Snowfall ALT SelfCast now disabled.")
|
||||
else
|
||||
ShaguTweaks_config[selfcastTitle] = 1
|
||||
DEFAULT_CHAT_FRAME:AddMessage("Classic Snowfall ALT SelfCast now enabled.")
|
||||
end
|
||||
end
|
||||
|
||||
local function IsBongosReady()
|
||||
return _G.BActionButton and type(_G.BActionButton.GetPagedID) == "function"
|
||||
end
|
||||
|
||||
local function ST_ActionButtonDown(id)
|
||||
local button, pagedID
|
||||
|
||||
if IsBongosReady() then
|
||||
button = _G["BActionButton" .. id]
|
||||
pagedID = _G.BActionButton.GetPagedID(id)
|
||||
if button and button:GetButtonState() == "NORMAL" then
|
||||
button:SetButtonState("PUSHED")
|
||||
end
|
||||
if pagedID then UseAction(pagedID, 0, SelfCast()) end
|
||||
return
|
||||
end
|
||||
|
||||
if BonusActionBarFrame and BonusActionBarFrame:IsShown() then
|
||||
button = _G["BonusActionButton" .. id]
|
||||
else
|
||||
button = _G["ActionButton" .. id]
|
||||
end
|
||||
|
||||
if button and button:GetButtonState() == "NORMAL" then
|
||||
button:SetButtonState("PUSHED")
|
||||
UseAction(ActionButton_GetPagedID(button), 0, SelfCast())
|
||||
end
|
||||
end
|
||||
|
||||
local function ST_ActionButtonUp(id)
|
||||
local button, pagedID
|
||||
|
||||
if IsBongosReady() then
|
||||
button = _G["BActionButton" .. id]
|
||||
pagedID = _G.BActionButton.GetPagedID(id)
|
||||
if button and button:GetButtonState() == "PUSHED" then
|
||||
button:SetButtonState("NORMAL")
|
||||
if MacroFrame_SaveMacro then MacroFrame_SaveMacro() end
|
||||
if pagedID and IsCurrentAction(pagedID) then
|
||||
button:SetChecked(1)
|
||||
else
|
||||
button:SetChecked(0)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if BonusActionBarFrame and BonusActionBarFrame:IsShown() then
|
||||
button = _G["BonusActionButton" .. id]
|
||||
else
|
||||
button = _G["ActionButton" .. id]
|
||||
end
|
||||
|
||||
if button and button:GetButtonState() == "PUSHED" then
|
||||
button:SetButtonState("NORMAL")
|
||||
if MacroFrame_SaveMacro then MacroFrame_SaveMacro() end
|
||||
if IsCurrentAction(ActionButton_GetPagedID(button)) then
|
||||
button:SetChecked(1)
|
||||
else
|
||||
button:SetChecked(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ST_MultiActionButtonDown(bar, id)
|
||||
local button = _G[bar .. "Button" .. id]
|
||||
if button and button:GetButtonState() == "NORMAL" then
|
||||
button:SetButtonState("PUSHED")
|
||||
UseAction(ActionButton_GetPagedID(button), 0, SelfCast())
|
||||
end
|
||||
end
|
||||
|
||||
local function ST_MultiActionButtonUp(bar, id)
|
||||
local button = _G[bar .. "Button" .. id]
|
||||
if button and button:GetButtonState() == "PUSHED" then
|
||||
button:SetButtonState("NORMAL")
|
||||
if MacroFrame_SaveMacro then MacroFrame_SaveMacro() end
|
||||
if IsCurrentAction(ActionButton_GetPagedID(button)) then
|
||||
button:SetChecked(1)
|
||||
else
|
||||
button:SetChecked(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ST_PetActionButtonDown(id)
|
||||
local button = _G["PetActionButton" .. id]
|
||||
if button and button:GetButtonState() == "NORMAL" then
|
||||
button:SetButtonState("PUSHED")
|
||||
CastPetAction(id)
|
||||
end
|
||||
end
|
||||
|
||||
local function ST_PetActionButtonUp(id)
|
||||
local button = _G["PetActionButton" .. id]
|
||||
if button and button:GetButtonState() == "PUSHED" then
|
||||
button:SetButtonState("NORMAL")
|
||||
end
|
||||
end
|
||||
|
||||
-- Vanilla's BONUSACTIONBUTTON bindings are aliases for the pet-action
|
||||
-- handlers. Keep the same relationship while moving the cast to key-down.
|
||||
local function ST_BonusActionButtonDown(id)
|
||||
ST_PetActionButtonDown(id)
|
||||
end
|
||||
|
||||
local function ST_BonusActionButtonUp(id)
|
||||
ST_PetActionButtonUp(id)
|
||||
end
|
||||
|
||||
local function Install()
|
||||
_G.ActionButtonDown = ST_ActionButtonDown
|
||||
_G.ActionButtonUp = ST_ActionButtonUp
|
||||
_G.MultiActionButtonDown = ST_MultiActionButtonDown
|
||||
_G.MultiActionButtonUp = ST_MultiActionButtonUp
|
||||
_G.PetActionButtonDown = ST_PetActionButtonDown
|
||||
_G.PetActionButtonUp = ST_PetActionButtonUp
|
||||
|
||||
if type(_G.BonusActionButtonDown) == "function" then
|
||||
_G.BonusActionButtonDown = ST_BonusActionButtonDown
|
||||
end
|
||||
if type(_G.BonusActionButtonUp) == "function" then
|
||||
_G.BonusActionButtonUp = ST_BonusActionButtonUp
|
||||
end
|
||||
end
|
||||
|
||||
local function HandlersAreInstalled()
|
||||
return _G.ActionButtonDown == ST_ActionButtonDown
|
||||
and _G.ActionButtonUp == ST_ActionButtonUp
|
||||
and _G.MultiActionButtonDown == ST_MultiActionButtonDown
|
||||
and _G.MultiActionButtonUp == ST_MultiActionButtonUp
|
||||
and _G.PetActionButtonDown == ST_PetActionButtonDown
|
||||
and _G.PetActionButtonUp == ST_PetActionButtonUp
|
||||
end
|
||||
|
||||
-- Modified bindings (SHIFT-*, CTRL-*, ALT-*) are routed through ClassicAPI's
|
||||
-- normalized override-binding layer. Vanilla's runOnUp binding path is not
|
||||
-- reliable for every modified-key combination on custom 1.12 clients.
|
||||
-- Normal unmodified keys continue through the original ClassicSnowfall-style
|
||||
-- global handlers above.
|
||||
local bindingOwner = CreateFrame("Frame", "ShaguTweaksKeyDownBindingOwner", UIParent)
|
||||
local proxies = {}
|
||||
local specs = {}
|
||||
|
||||
local function AddSpec(command, kind, a, b)
|
||||
table.insert(specs, { command = command, kind = kind, a = a, b = b })
|
||||
end
|
||||
|
||||
for i = 1, 12 do
|
||||
AddSpec("ACTIONBUTTON" .. i, "action", i)
|
||||
AddSpec("MULTIACTIONBAR1BUTTON" .. i, "multi", "MultiBarBottomLeft", i)
|
||||
AddSpec("MULTIACTIONBAR2BUTTON" .. i, "multi", "MultiBarBottomRight", i)
|
||||
AddSpec("MULTIACTIONBAR3BUTTON" .. i, "multi", "MultiBarRight", i)
|
||||
AddSpec("MULTIACTIONBAR4BUTTON" .. i, "multi", "MultiBarLeft", i)
|
||||
AddSpec("BONUSACTIONBUTTON" .. i, "bonus", i)
|
||||
end
|
||||
|
||||
local function IsModifiedKey(key)
|
||||
if type(key) ~= "string" then return false end
|
||||
local upper = string.upper(key)
|
||||
return string.find(upper, "SHIFT-", 1, true)
|
||||
or string.find(upper, "CTRL-", 1, true)
|
||||
or string.find(upper, "ALT-", 1, true)
|
||||
end
|
||||
|
||||
local function GetProxy(spec, index)
|
||||
local name = "ShaguTweaksKeyDownProxy" .. index
|
||||
if proxies[index] then return proxies[index] end
|
||||
|
||||
local button = CreateFrame("Button", name, bindingOwner)
|
||||
button.spec = spec
|
||||
button:SetScript("OnClick", function()
|
||||
local s = this.spec
|
||||
if s.kind == "action" then
|
||||
ST_ActionButtonDown(s.a)
|
||||
ST_ActionButtonUp(s.a)
|
||||
elseif s.kind == "multi" then
|
||||
ST_MultiActionButtonDown(s.a, s.b)
|
||||
ST_MultiActionButtonUp(s.a, s.b)
|
||||
elseif s.kind == "bonus" then
|
||||
ST_BonusActionButtonDown(s.a)
|
||||
ST_BonusActionButtonUp(s.a)
|
||||
end
|
||||
end)
|
||||
proxies[index] = button
|
||||
return button
|
||||
end
|
||||
|
||||
local refreshingBindings
|
||||
local function RefreshModifiedBindings()
|
||||
if refreshingBindings then return end
|
||||
if not API or not API.overridebindings then return end
|
||||
|
||||
refreshingBindings = true
|
||||
API.ClearOverrideBindings(bindingOwner)
|
||||
|
||||
for index, spec in ipairs(specs) do
|
||||
local key1, key2 = GetBindingKey(spec.command)
|
||||
local proxy = GetProxy(spec, index)
|
||||
|
||||
if IsModifiedKey(key1) then
|
||||
API.SetOverrideBindingClick(bindingOwner, true, key1, proxy:GetName())
|
||||
end
|
||||
if IsModifiedKey(key2) then
|
||||
API.SetOverrideBindingClick(bindingOwner, true, key2, proxy:GetName())
|
||||
end
|
||||
end
|
||||
|
||||
refreshingBindings = nil
|
||||
end
|
||||
|
||||
Install()
|
||||
RefreshModifiedBindings()
|
||||
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:RegisterEvent("PLAYER_LOGIN")
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
frame:RegisterEvent("UPDATE_BINDINGS")
|
||||
frame.guardUntil = 0
|
||||
frame.guardTicker = nil
|
||||
|
||||
local function GuardTick()
|
||||
if not frame.guardUntil or frame.guardUntil <= GetTime() then
|
||||
if frame.guardTicker then
|
||||
frame.guardTicker:Cancel()
|
||||
frame.guardTicker = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if not HandlersAreInstalled() then Install() end
|
||||
end
|
||||
|
||||
local function StartGuard()
|
||||
frame.guardUntil = GetTime() + 8
|
||||
if not frame.guardTicker then
|
||||
frame.guardTicker = C_Timer.NewTicker(0.25, GuardTick)
|
||||
end
|
||||
end
|
||||
|
||||
frame:SetScript("OnEvent", function()
|
||||
Install()
|
||||
RefreshModifiedBindings()
|
||||
|
||||
if event == "PLAYER_LOGIN" or event == "PLAYER_ENTERING_WORLD" then
|
||||
StartGuard()
|
||||
end
|
||||
end)
|
||||
end
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local hooksecurefunc = ShaguTweaks.hooksecurefunc
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Cursor Tooltip"],
|
||||
description = T["Attaches default tooltips to the cursor."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Tooltip & Items"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
if ShaguTweaks.CursorTooltipInstalled then return end
|
||||
ShaguTweaks.CursorTooltipInstalled = true
|
||||
|
||||
-- Keep the native helper for non-GameTooltip callers. The feature only
|
||||
-- needs to move the normal world/UI GameTooltip and should not unexpectedly
|
||||
-- change other tooltip frames that may reuse the same helper.
|
||||
local originalDefaultAnchor = ShaguTweaks.CursorTooltipOriginalDefaultAnchor
|
||||
or _G.GameTooltip_SetDefaultAnchor
|
||||
|
||||
ShaguTweaks.CursorTooltipOriginalDefaultAnchor = originalDefaultAnchor
|
||||
|
||||
local cursor = CreateFrame("Frame", nil, UIParent)
|
||||
cursor:SetWidth(1)
|
||||
cursor:SetHeight(1)
|
||||
cursor:Hide()
|
||||
|
||||
local following = false
|
||||
local lastX, lastY, lastScale
|
||||
|
||||
local function UpdateCursor(force)
|
||||
local scale = UIParent:GetEffectiveScale()
|
||||
if not scale or scale == 0 then scale = UIParent:GetScale() end
|
||||
if not scale or scale == 0 then scale = 1 end
|
||||
|
||||
local x, y = GetCursorPosition()
|
||||
|
||||
-- GetCursorPosition is cheap and required for following the pointer, but
|
||||
-- avoid a frame-layout operation while the mouse is completely still.
|
||||
if not force and x == lastX and y == lastY and scale == lastScale then
|
||||
return
|
||||
end
|
||||
|
||||
lastX, lastY, lastScale = x, y, scale
|
||||
|
||||
-- Reusing the same CENTER anchor updates it in place; ClearAllPoints every
|
||||
-- rendered frame is unnecessary.
|
||||
cursor:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x / scale, y / scale)
|
||||
end
|
||||
|
||||
cursor:SetScript("OnUpdate", function()
|
||||
if not following or not GameTooltip:IsShown() then
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
UpdateCursor(false)
|
||||
end)
|
||||
|
||||
function _G.GameTooltip_SetDefaultAnchor(tooltip, parent)
|
||||
if tooltip ~= GameTooltip then
|
||||
return originalDefaultAnchor(tooltip, parent)
|
||||
end
|
||||
|
||||
-- Cursor Tooltip intentionally uses ANCHOR_CURSOR without marking the
|
||||
-- tooltip as a default anchored tooltip. This matches the upstream mod's
|
||||
-- lifecycle and makes world-unit tooltips disappear immediately when the
|
||||
-- mouseover unit is lost, while keeping the custom cursor positioning.
|
||||
tooltip:SetOwner(parent or UIParent, "ANCHOR_CURSOR")
|
||||
tooltip:ClearAllPoints()
|
||||
|
||||
-- Position once before Show() so there is no first-frame jump from the
|
||||
-- cursor frame's default origin.
|
||||
UpdateCursor(true)
|
||||
|
||||
tooltip:SetPoint("BOTTOMLEFT", cursor, "TOPRIGHT", 12, 12)
|
||||
tooltip:SetClampedToScreen(true)
|
||||
|
||||
following = true
|
||||
end
|
||||
|
||||
-- Default anchoring normally happens before GameTooltip:Show(). Start the
|
||||
-- per-frame cursor tracker only once the tooltip is actually visible.
|
||||
hooksecurefunc(GameTooltip, "Show", function()
|
||||
if not following then return end
|
||||
|
||||
UpdateCursor(true)
|
||||
cursor:Show()
|
||||
end)
|
||||
|
||||
hooksecurefunc(GameTooltip, "Hide", function()
|
||||
following = false
|
||||
cursor:Hide()
|
||||
end)
|
||||
end
|
||||
Executable
+146
@@ -0,0 +1,146 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Hide Combat Tooltip"],
|
||||
description = T["Hides the tooltip in combat. Hold Shift to show it temporarily."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Tooltip & Items"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
if ShaguTweaks.HideCombatTooltipInstalled then return end
|
||||
ShaguTweaks.HideCombatTooltipInstalled = true
|
||||
|
||||
local controller = CreateFrame("Frame", nil, UIParent)
|
||||
local inCombat = UnitAffectingCombat("player") and true or false
|
||||
local shiftDown = API.IsShiftKeyDown() and true or false
|
||||
local guardActive = false
|
||||
local elapsed = 0
|
||||
|
||||
local function Apply()
|
||||
if not inCombat then
|
||||
if GameTooltip:GetAlpha() ~= 1 then
|
||||
GameTooltip:SetAlpha(1)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local alpha = shiftDown and 1 or 0
|
||||
if GameTooltip:GetAlpha() ~= alpha then
|
||||
GameTooltip:SetAlpha(alpha)
|
||||
end
|
||||
end
|
||||
|
||||
local function GuardTick()
|
||||
elapsed = elapsed + (arg1 or 0)
|
||||
if elapsed < .05 then return end
|
||||
elapsed = 0
|
||||
|
||||
-- ClassicAPI provides modifier events, so normal clients don't need to
|
||||
-- query Shift here. This fallback is only for old environments.
|
||||
if not API.modifierstate then
|
||||
shiftDown = API.IsShiftKeyDown() and true or false
|
||||
end
|
||||
|
||||
Apply()
|
||||
end
|
||||
|
||||
local function StartGuard()
|
||||
if guardActive or not inCombat or not GameTooltip:IsShown() then return end
|
||||
|
||||
guardActive = true
|
||||
elapsed = 0
|
||||
controller:SetScript("OnUpdate", GuardTick)
|
||||
end
|
||||
|
||||
local function StopGuard()
|
||||
if not guardActive then return end
|
||||
|
||||
guardActive = false
|
||||
elapsed = 0
|
||||
controller:SetScript("OnUpdate", nil)
|
||||
end
|
||||
|
||||
local function RefreshShift()
|
||||
shiftDown = API.IsShiftKeyDown() and true or false
|
||||
|
||||
if inCombat and GameTooltip:IsShown() then
|
||||
Apply()
|
||||
StartGuard()
|
||||
end
|
||||
end
|
||||
|
||||
local function EnterCombat()
|
||||
inCombat = true
|
||||
shiftDown = API.IsShiftKeyDown() and true or false
|
||||
|
||||
if GameTooltip:IsShown() then
|
||||
Apply()
|
||||
StartGuard()
|
||||
end
|
||||
end
|
||||
|
||||
local function LeaveCombat()
|
||||
inCombat = false
|
||||
StopGuard()
|
||||
Apply()
|
||||
end
|
||||
|
||||
-- Vanilla/Turtle can restore GameTooltip alpha internally while updating a
|
||||
-- visible unit tooltip. A purely event-driven implementation therefore does
|
||||
-- not stay hidden reliably. The guard below exists only while BOTH combat
|
||||
-- and an actual GameTooltip are active, and is throttled to 20 Hz instead of
|
||||
-- running the full upstream logic every rendered frame.
|
||||
local oldOnShow = GameTooltip:GetScript("OnShow")
|
||||
GameTooltip:SetScript("OnShow", function()
|
||||
if oldOnShow then oldOnShow() end
|
||||
|
||||
if inCombat then
|
||||
shiftDown = API.IsShiftKeyDown() and true or false
|
||||
Apply()
|
||||
StartGuard()
|
||||
else
|
||||
Apply()
|
||||
end
|
||||
end)
|
||||
|
||||
local oldOnHide = GameTooltip:GetScript("OnHide")
|
||||
GameTooltip:SetScript("OnHide", function()
|
||||
if oldOnHide then oldOnHide() end
|
||||
StopGuard()
|
||||
end)
|
||||
|
||||
controller:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
controller:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
controller:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
|
||||
if API.modifierstate then
|
||||
controller:RegisterEvent("MODIFIER_STATE_CHANGED")
|
||||
end
|
||||
|
||||
controller:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_REGEN_DISABLED" then
|
||||
EnterCombat()
|
||||
elseif event == "PLAYER_REGEN_ENABLED" then
|
||||
LeaveCombat()
|
||||
elseif event == "MODIFIER_STATE_CHANGED" then
|
||||
RefreshShift()
|
||||
elseif event == "PLAYER_ENTERING_WORLD" then
|
||||
if UnitAffectingCombat("player") then
|
||||
EnterCombat()
|
||||
else
|
||||
LeaveCombat()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if inCombat then
|
||||
EnterCombat()
|
||||
else
|
||||
LeaveCombat()
|
||||
end
|
||||
end
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Loot Quality"],
|
||||
description = T["Colors the loot frame using the highest item quality in the current loot window."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Loot"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local MIN_QUALITY = 2 -- uncommon / green
|
||||
|
||||
local function FindLootTitle()
|
||||
local regions = { LootFrame:GetRegions() }
|
||||
|
||||
for i = 1, table.getn(regions) do
|
||||
local region = regions[i]
|
||||
-- Some Vanilla/Turtle regions expose GetText without being FontStrings.
|
||||
-- Only keep a region that can actually be recolored.
|
||||
if region
|
||||
and type(region.GetText) == "function"
|
||||
and type(region.SetTextColor) == "function"
|
||||
and region:GetText() == ITEMS then
|
||||
return region
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if not self.border then
|
||||
self.border = LootFrame:CreateTexture(nil, "OVERLAY")
|
||||
self.border:SetTexture("Interface\\LootFrame\\UI-LootPanel")
|
||||
self.border:SetPoint("TOPLEFT", LootFrame, "TOPLEFT", -4, 3)
|
||||
self.border:SetPoint("BOTTOMRIGHT", LootFrame, "BOTTOMRIGHT", 5, -3)
|
||||
self.border:SetDrawLayer("ARTWORK")
|
||||
|
||||
self.highlight = LootFrame:CreateTexture(nil, "OVERLAY")
|
||||
self.highlight:SetTexture("Interface\\LootFrame\\UI-LootPanel")
|
||||
self.highlight:SetAllPoints(self.border)
|
||||
self.highlight:SetDrawLayer("ARTWORK")
|
||||
self.highlight:SetBlendMode("ADD")
|
||||
end
|
||||
|
||||
self.title = self.title or FindLootTitle()
|
||||
|
||||
if self.title
|
||||
and not self.titleColor
|
||||
and type(self.title.GetTextColor) == "function" then
|
||||
local r, g, b, a = self.title:GetTextColor()
|
||||
self.titleColor = { r or 1, g or .82, b or 0, a or 1 }
|
||||
end
|
||||
|
||||
local function RestoreTitle()
|
||||
if not self.title or type(self.title.SetTextColor) ~= "function" then return end
|
||||
|
||||
local color = self.titleColor
|
||||
if color then
|
||||
self.title:SetTextColor(color[1], color[2], color[3], color[4])
|
||||
else
|
||||
self.title:SetTextColor(1, .82, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function ResetVisuals()
|
||||
self.border:Hide()
|
||||
self.highlight:Hide()
|
||||
RestoreTitle()
|
||||
end
|
||||
|
||||
local function UpdateQuality()
|
||||
local highestQuality = 0
|
||||
local count = GetNumLootItems() or 0
|
||||
|
||||
for i = 1, count do
|
||||
if LootSlotIsItem(i) then
|
||||
local _, _, _, quality = GetLootSlotInfo(i)
|
||||
quality = tonumber(quality) or 0
|
||||
if quality > highestQuality then
|
||||
highestQuality = quality
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local color = ITEM_QUALITY_COLORS and ITEM_QUALITY_COLORS[highestQuality]
|
||||
if highestQuality < MIN_QUALITY or not color then
|
||||
ResetVisuals()
|
||||
return
|
||||
end
|
||||
|
||||
self.border:SetVertexColor(color.r, color.g, color.b)
|
||||
self.highlight:SetVertexColor(color.r, color.g, color.b)
|
||||
self.border:Show()
|
||||
self.highlight:Show()
|
||||
|
||||
if self.title and type(self.title.SetTextColor) == "function" then
|
||||
self.title:SetTextColor(color.r, color.g, color.b)
|
||||
end
|
||||
end
|
||||
|
||||
self.ResetVisuals = ResetVisuals
|
||||
self.events = self.events or CreateFrame("Frame")
|
||||
self.events:UnregisterAllEvents()
|
||||
self.events:RegisterEvent("LOOT_OPENED")
|
||||
self.events:RegisterEvent("LOOT_SLOT_CLEARED")
|
||||
self.events:RegisterEvent("LOOT_CLOSED")
|
||||
|
||||
self.events:SetScript("OnEvent", function()
|
||||
if event == "LOOT_CLOSED" then
|
||||
ResetVisuals()
|
||||
else
|
||||
UpdateQuality()
|
||||
end
|
||||
end)
|
||||
|
||||
ResetVisuals()
|
||||
end
|
||||
|
||||
module.disable = function(self)
|
||||
if self.events then
|
||||
self.events:UnregisterAllEvents()
|
||||
end
|
||||
|
||||
if self.ResetVisuals then
|
||||
self.ResetVisuals()
|
||||
end
|
||||
end
|
||||
Executable
+144
@@ -0,0 +1,144 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
local libspell = ShaguTweaks.libspell
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Macro Icons"],
|
||||
description = T["Detect showtooltip and spells in macros to use them on action buttons."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
maintainer = "@shagu (GitHub)",
|
||||
category = T["Macro"],
|
||||
enabled = true,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
local gfind = string.gmatch or string.gfind
|
||||
|
||||
local function BuildMacroCache()
|
||||
local cache = {}
|
||||
for macroSlot = 1, 36 do
|
||||
local name, _, body = GetMacroInfo(macroSlot)
|
||||
if name and body then
|
||||
cache[name] = { id = macroSlot, body = body }
|
||||
end
|
||||
end
|
||||
return cache
|
||||
end
|
||||
|
||||
local function GetMacroData(actionSlot, cache)
|
||||
local actionType, id = API.GetActionInfo(actionSlot)
|
||||
if actionType == "macro" and id then
|
||||
local _, _, body = GetMacroInfo(id)
|
||||
if body then return id, body end
|
||||
end
|
||||
|
||||
local macroName = GetActionText(actionSlot)
|
||||
local entry = macroName and cache[macroName]
|
||||
if entry then return entry.id, entry.body end
|
||||
end
|
||||
|
||||
local function ParseSpellText(match)
|
||||
if not match then return end
|
||||
local _, _, spell, rank = string.find(match, "(.+)%((.+)%)")
|
||||
return spell or match, rank
|
||||
end
|
||||
|
||||
local function ResolveSpell(body, macroSlot)
|
||||
if not body then return end
|
||||
|
||||
local customTooltip
|
||||
local fallback
|
||||
|
||||
for line in gfind(body, "[^%\n]+") do
|
||||
-- Explicit #showtooltip always wins.
|
||||
local _, _, match = string.find(line, "^#showtooltip%s+(.+)")
|
||||
if match then
|
||||
return ParseSpellText(match)
|
||||
end
|
||||
|
||||
-- Preserve the historical ShaguTweaks custom override.
|
||||
if not customTooltip then
|
||||
_, _, customTooltip = string.find(line, "%-%-showtooltip%s+(.+)")
|
||||
end
|
||||
|
||||
-- Keep text parsing as a fallback for old/stale macro caches and /pfcast.
|
||||
if not fallback then
|
||||
_, _, fallback = string.find(line, "^/cast%s+(.+)")
|
||||
end
|
||||
if not fallback then
|
||||
_, _, fallback = string.find(line, "^/pfcast%s+(.+)")
|
||||
end
|
||||
if not fallback then
|
||||
_, _, fallback = string.find(line, "CastSpellByName%(%\"(.+)%\"%)")
|
||||
end
|
||||
end
|
||||
|
||||
if customTooltip then
|
||||
return ParseSpellText(customTooltip)
|
||||
end
|
||||
|
||||
-- ClassicAPI already resolves and caches the macro's primary spell.
|
||||
-- This is the correct modern behavior for a bare "#showtooltip" line and
|
||||
-- also covers /castsequence, CastSpellByName and CastSpellNoToggle.
|
||||
if macroSlot then
|
||||
local spell, rank = API.GetMacroSpell(macroSlot)
|
||||
if spell then
|
||||
return spell, rank ~= "" and rank or nil
|
||||
end
|
||||
end
|
||||
|
||||
return ParseSpellText(fallback)
|
||||
end
|
||||
|
||||
local function ButtonMacroScan(bar, macroCache)
|
||||
if not bar:IsVisible() then return end
|
||||
|
||||
local prefix = bar:GetName()
|
||||
prefix = bar == MainMenuBar and "Action" or prefix
|
||||
prefix = bar == BonusActionBarFrame and "BonusAction" or prefix
|
||||
|
||||
for index = 1, 12 do
|
||||
local button = _G[prefix.."Button"..index]
|
||||
local icon = _G[prefix.."Button"..index.."Icon"]
|
||||
if not button then break end
|
||||
|
||||
local actionSlot = ActionButton_GetPagedID(button)
|
||||
local texture = GetActionTexture(actionSlot)
|
||||
button.spellslot, button.booktype = nil, nil
|
||||
|
||||
local macroSlot, body = GetMacroData(actionSlot, macroCache)
|
||||
local spell, rank = ResolveSpell(body, macroSlot)
|
||||
if spell then
|
||||
button.spellslot, button.booktype = libspell.GetSpellIndex(spell, rank)
|
||||
|
||||
if button.spellslot and button.booktype then
|
||||
texture = GetSpellTexture(button.spellslot, button.booktype) or texture
|
||||
end
|
||||
end
|
||||
|
||||
if icon and texture and texture ~= icon:GetTexture() then
|
||||
icon:SetTexture(texture)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local bars = {
|
||||
MainMenuBar, BonusActionBarFrame, MultiBarRight, MultiBarLeft,
|
||||
MultiBarBottomRight, MultiBarBottomLeft
|
||||
}
|
||||
|
||||
local macroicons = CreateFrame("Frame", "ShaguTweaksMacroIcons", UIParent)
|
||||
macroicons:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
macroicons:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
macroicons:RegisterEvent("ACTIONBAR_PAGE_CHANGED")
|
||||
macroicons:RegisterEvent("UPDATE_BONUS_ACTIONBAR")
|
||||
macroicons:RegisterEvent("ACTIONBAR_SHOWGRID")
|
||||
macroicons:RegisterEvent("UPDATE_MACROS")
|
||||
macroicons:SetScript("OnEvent", function()
|
||||
local macroCache = BuildMacroCache()
|
||||
for _, bar in ipairs(bars) do
|
||||
ButtonMacroScan(bar, macroCache)
|
||||
end
|
||||
end)
|
||||
end
|
||||
Executable
+160
@@ -0,0 +1,160 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Macro Tweaks"],
|
||||
description = T["Add /equip, /use and modern ClassicAPI macro commands, remove #showtooltip from chat and hide macro commands from history."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
maintainer = "@shagu (GitHub)",
|
||||
category = T["Macro"],
|
||||
enabled = true,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
-- This interception is intentional: #showtooltip is macro metadata and must
|
||||
-- not be sent as chat text by the Vanilla macro parser.
|
||||
local hookSendChatMessage = _G.SendChatMessage
|
||||
local function IsShowTooltipMetadata(msg)
|
||||
if not msg then return false end
|
||||
|
||||
local line = string.gsub(msg, "^%s+", "")
|
||||
line = string.gsub(line, "%s+$", "")
|
||||
|
||||
return line == "#showtooltip"
|
||||
or string.find(line, "^#showtooltip%s+") ~= nil
|
||||
end
|
||||
|
||||
function _G.SendChatMessage(msg, a1, a2, a3, a4, a5, a6, a7, a8)
|
||||
if IsShowTooltipMetadata(msg) then return end
|
||||
return hookSendChatMessage(msg, a1, a2, a3, a4, a5, a6, a7, a8)
|
||||
end
|
||||
|
||||
-- do not write executed macro commands into chat input history
|
||||
if not ChatFrameEditBox._AddHistoryLine then
|
||||
local userinput
|
||||
|
||||
ChatFrameEditBox._AddHistoryLine = ChatFrameEditBox.AddHistoryLine
|
||||
ChatFrameEditBox.AddHistoryLine = function(self, text)
|
||||
if not userinput and text then
|
||||
if string.find(text, "^/run%s*") then return end
|
||||
if string.find(text, "^/script%s*") then return end
|
||||
if string.find(text, "^/cast%s*") then return end
|
||||
if string.find(text, "^/startattack%s*") then return end
|
||||
if string.find(text, "^/stopattack%s*") then return end
|
||||
if string.find(text, "^/focus%s*") then return end
|
||||
if string.find(text, "^/clearfocus%s*") then return end
|
||||
end
|
||||
ChatFrameEditBox._AddHistoryLine(self, text)
|
||||
end
|
||||
|
||||
local OnEnter = ChatFrameEditBox:GetScript("OnEnterPressed")
|
||||
ChatFrameEditBox:SetScript("OnEnterPressed", function(a1,a2,a3,a4)
|
||||
userinput = true
|
||||
if OnEnter then OnEnter(a1,a2,a3,a4) end
|
||||
userinput = nil
|
||||
end)
|
||||
end
|
||||
|
||||
local function GetBagItemName(bag, slot)
|
||||
local itemID = API.GetContainerItemID(bag, slot)
|
||||
local name = itemID and API.GetItemNameByID(itemID)
|
||||
if name then return name end
|
||||
|
||||
local itemLink = API.GetContainerItemLink(bag, slot)
|
||||
local linkedID = itemLink and API.GetItemIDFromLink(itemLink)
|
||||
local itemName = linkedID and API.GetItemInfo(linkedID)
|
||||
return itemName
|
||||
end
|
||||
|
||||
local function FindItem(item)
|
||||
local wanted = string.lower(item or "")
|
||||
for bag = 4, 0, -1 do
|
||||
for slot = 1, GetContainerNumSlots(bag) do
|
||||
local query = GetBagItemName(bag, slot)
|
||||
if query and string.lower(query) == wanted then
|
||||
return bag, slot
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ClassicAPI exposes several modern macro primitives as Lua functions but
|
||||
-- Vanilla does not register their familiar slash-command equivalents.
|
||||
--
|
||||
-- Use ShaguTweaks-specific command IDs instead of generic STARTATTACK/FOCUS
|
||||
-- keys. A SlashCmdList key can exist without the corresponding /command
|
||||
-- alias, so testing only SlashCmdList.STARTATTACK can silently skip a valid
|
||||
-- registration and leave Vanilla reporting "Type '/help'...".
|
||||
local function TrimUnit(msg)
|
||||
if not msg then return nil end
|
||||
local _, _, unit = string.find(msg, "^%s*(.-)%s*$")
|
||||
return unit ~= "" and unit or nil
|
||||
end
|
||||
|
||||
local function IsSlashRegistered(command)
|
||||
command = string.lower(command)
|
||||
for key, value in pairs(_G) do
|
||||
if type(key) == "string"
|
||||
and string.find(key, "^SLASH_")
|
||||
and type(value) == "string"
|
||||
and string.lower(value) == command then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function RegisterSlashAlias(id, command, handler)
|
||||
if IsSlashRegistered(command) then return end
|
||||
_G["SLASH_" .. id .. "1"] = command
|
||||
_G.SlashCmdList[id] = handler
|
||||
end
|
||||
|
||||
RegisterSlashAlias("SHAGUTWEAKS_STARTATTACK", "/startattack", function(msg)
|
||||
API.StartAttack(TrimUnit(msg))
|
||||
end)
|
||||
|
||||
RegisterSlashAlias("SHAGUTWEAKS_STOPATTACK", "/stopattack", function()
|
||||
API.StopAttack()
|
||||
end)
|
||||
|
||||
RegisterSlashAlias("SHAGUTWEAKS_FOCUS", "/focus", function(msg)
|
||||
API.FocusUnit(TrimUnit(msg))
|
||||
end)
|
||||
|
||||
RegisterSlashAlias("SHAGUTWEAKS_CLEARFOCUS", "/clearfocus", function()
|
||||
API.ClearFocus()
|
||||
end)
|
||||
|
||||
local function EquipOrUse(msg)
|
||||
if not msg or msg == "" then return end
|
||||
|
||||
local bag, slot
|
||||
local _, _, parsedBag, parsedSlot = string.find(msg, "^(%d+)%s+(%d+)$")
|
||||
if parsedBag and parsedSlot then
|
||||
bag, slot = parsedBag, parsedSlot
|
||||
else
|
||||
local _, _, inventorySlot = string.find(msg, "^(%d+)$")
|
||||
if inventorySlot then
|
||||
slot = inventorySlot
|
||||
else
|
||||
bag, slot = FindItem(msg)
|
||||
end
|
||||
end
|
||||
|
||||
bag = bag and tonumber(bag) or nil
|
||||
slot = slot and tonumber(slot) or nil
|
||||
|
||||
if bag and slot then
|
||||
UseContainerItem(bag, slot)
|
||||
elseif slot then
|
||||
UseInventoryItem(slot)
|
||||
end
|
||||
end
|
||||
|
||||
-- Keep the original Macro Tweaks conveniences, but do not steal /equip or
|
||||
-- /use from another addon that already registered either command.
|
||||
RegisterSlashAlias("SHAGUTWEAKS_EQUIP", "/equip", EquipOrUse)
|
||||
RegisterSlashAlias("SHAGUTWEAKS_USE", "/use", EquipOrUse)
|
||||
end
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local HookScript = ShaguTweaks.HookScript
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Metric Range"],
|
||||
description = T["Displays tooltip range units in metres instead of yards."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Tooltip & Items"],
|
||||
enabled = true,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
-- Avoid double processing while the old standalone addon is still enabled.
|
||||
if IsAddOnLoaded("MetricRange") then return end
|
||||
|
||||
local function ConvertText(text)
|
||||
if type(text) ~= "string" then return text end
|
||||
|
||||
-- Intentionally keep the original behavior: only replace the displayed
|
||||
-- unit label. Do not numerically convert the range value.
|
||||
text = string.gsub(text, " yards", " metres")
|
||||
text = string.gsub(text, " yard", " metre")
|
||||
text = string.gsub(text, " yd", " m")
|
||||
return text
|
||||
end
|
||||
|
||||
local function ConvertTooltip(tooltip)
|
||||
if not tooltip or not tooltip.GetName then return end
|
||||
|
||||
local name = tooltip:GetName()
|
||||
if not name then return end
|
||||
|
||||
-- GameTooltip-style frames expose NumLines() in Vanilla. Only inspect the
|
||||
-- lines that actually exist instead of probing 30 left/right font strings
|
||||
-- on every tooltip show. Keep the historical 30-line ceiling as fallback.
|
||||
local lines = 30
|
||||
if tooltip.NumLines then
|
||||
lines = math.min(tooltip:NumLines() or 0, 30)
|
||||
end
|
||||
|
||||
for i = 1, lines do
|
||||
local left = _G[name .. "TextLeft" .. i]
|
||||
if left then
|
||||
local old = left:GetText()
|
||||
if old then
|
||||
local new = ConvertText(old)
|
||||
if new ~= old then left:SetText(new) end
|
||||
end
|
||||
end
|
||||
|
||||
local right = _G[name .. "TextRight" .. i]
|
||||
if right then
|
||||
local old = right:GetText()
|
||||
if old then
|
||||
local new = ConvertText(old)
|
||||
if new ~= old then right:SetText(new) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function HookTooltip(tooltip)
|
||||
if not tooltip then return end
|
||||
HookScript(tooltip, "OnShow", function()
|
||||
ConvertTooltip(tooltip)
|
||||
end)
|
||||
end
|
||||
|
||||
HookTooltip(GameTooltip)
|
||||
HookTooltip(ItemRefTooltip)
|
||||
HookTooltip(ShoppingTooltip1)
|
||||
HookTooltip(ShoppingTooltip2)
|
||||
HookTooltip(ShoppingTooltip3)
|
||||
|
||||
if GameTooltip and GameTooltip:IsVisible() then ConvertTooltip(GameTooltip) end
|
||||
if ItemRefTooltip and ItemRefTooltip:IsVisible() then ConvertTooltip(ItemRefTooltip) end
|
||||
end
|
||||
Executable
+162
@@ -0,0 +1,162 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local floor = math.floor
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["MiniMap Framerate & Latency"],
|
||||
description = T["Adds a small framerate & latency display to the mini map."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Minimap & World Map"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local TARGET_FPS = 60
|
||||
local LOW_LATENCY = tonumber(_G.PERFORMANCEBAR_LOW_LATENCY) or 300
|
||||
local MEDIUM_LATENCY = tonumber(_G.PERFORMANCEBAR_MEDIUM_LATENCY) or 600
|
||||
|
||||
local function CreateStatusFrame(width)
|
||||
local frame = CreateFrame("Frame", nil, Minimap)
|
||||
frame:SetFrameLevel(64)
|
||||
frame:SetWidth(width)
|
||||
frame:SetHeight(23)
|
||||
frame:SetClampedToScreen(true)
|
||||
frame:EnableMouse(true)
|
||||
frame:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true,
|
||||
tileSize = 8,
|
||||
edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 3, bottom = 3 },
|
||||
})
|
||||
frame:SetBackdropBorderColor(.9, .8, .5, 1)
|
||||
frame:SetBackdropColor(.4, .4, .4, 1)
|
||||
|
||||
frame.text = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
frame.text:SetFont(STANDARD_TEXT_FONT, 12, "OUTLINE")
|
||||
frame.text:SetAllPoints(frame)
|
||||
frame.text:SetFontObject(GameFontWhite)
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
local function SetFPSColor(text, fps)
|
||||
local perc = fps / TARGET_FPS
|
||||
if perc < 0 then perc = 0 elseif perc > 1 then perc = 1 end
|
||||
|
||||
local r, g
|
||||
if perc <= .5 then
|
||||
r = 1
|
||||
g = perc * 2
|
||||
else
|
||||
r = (1 - perc) * 2
|
||||
g = 1
|
||||
end
|
||||
|
||||
text:SetTextColor(r, g, 0, 1)
|
||||
end
|
||||
|
||||
local function SetLatencyColor(text, latency)
|
||||
if latency > MEDIUM_LATENCY then
|
||||
text:SetTextColor(1, 0, 0, 1)
|
||||
elseif latency > LOW_LATENCY then
|
||||
text:SetTextColor(1, 1, 0, 1)
|
||||
else
|
||||
text:SetTextColor(0, 1, 0, 1)
|
||||
end
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if self.ticker then
|
||||
self.ticker:Cancel()
|
||||
self.ticker = nil
|
||||
end
|
||||
|
||||
-- Extras is ClassicAPI-first. Do not restore the original permanent
|
||||
-- OnUpdate implementation when the native scheduler is unavailable.
|
||||
if type(_G.C_Timer) ~= "table" or type(_G.C_Timer.NewTicker) ~= "function" then
|
||||
return
|
||||
end
|
||||
|
||||
if not self.fpsFrame then
|
||||
self.fpsFrame = CreateStatusFrame(49)
|
||||
self.latencyFrame = CreateStatusFrame(51)
|
||||
end
|
||||
|
||||
local fpsFrame = self.fpsFrame
|
||||
local latencyFrame = self.latencyFrame
|
||||
local lowFPS, highFPS
|
||||
local lowMS, highMS
|
||||
|
||||
-- Follow the existing ShaguTweaks MiniMap Clock instead of independently
|
||||
-- anchoring to MinimapCluster. MiniMap Square and mover integrations already
|
||||
-- reposition that clock, so these counters follow automatically regardless
|
||||
-- of module initialization order.
|
||||
fpsFrame:ClearAllPoints()
|
||||
latencyFrame:ClearAllPoints()
|
||||
|
||||
if _G.MinimapClock then
|
||||
fpsFrame:SetPoint("RIGHT", _G.MinimapClock, "LEFT", 2, 0)
|
||||
latencyFrame:SetPoint("LEFT", _G.MinimapClock, "RIGHT", -2, 0)
|
||||
else
|
||||
fpsFrame:SetPoint("BOTTOM", Minimap, "BOTTOM", -28, -25)
|
||||
latencyFrame:SetPoint("BOTTOM", Minimap, "BOTTOM", 28, -25)
|
||||
end
|
||||
|
||||
local function Update()
|
||||
local fps = floor(GetFramerate() or 0)
|
||||
local _, _, latency = GetNetStats()
|
||||
latency = floor(latency or 0)
|
||||
|
||||
if fps > 0 and (not lowFPS or fps < lowFPS) then lowFPS = fps end
|
||||
if not highFPS or fps > highFPS then highFPS = fps end
|
||||
if latency > 0 and (not lowMS or latency < lowMS) then lowMS = latency end
|
||||
if not highMS or latency > highMS then highMS = latency end
|
||||
|
||||
fpsFrame.text:SetText(tostring(fps))
|
||||
latencyFrame.text:SetText(tostring(latency))
|
||||
SetFPSColor(fpsFrame.text, fps)
|
||||
SetLatencyColor(latencyFrame.text, latency)
|
||||
end
|
||||
|
||||
fpsFrame:SetScript("OnEnter", function()
|
||||
GameTooltip:ClearLines()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT")
|
||||
GameTooltip:AddLine(T["Framerate"])
|
||||
GameTooltip:AddDoubleLine(T["High"], tostring(highFPS or 0) .. " " .. T["fps"], 1,1,1,1,1,1)
|
||||
GameTooltip:AddDoubleLine(T["Low"], tostring(lowFPS or 0) .. " " .. T["fps"], 1,1,1,1,1,1)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
|
||||
latencyFrame:SetScript("OnEnter", function()
|
||||
GameTooltip:ClearLines()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT")
|
||||
GameTooltip:AddLine(T["Latency"])
|
||||
GameTooltip:AddDoubleLine(T["High"], tostring(highMS or 0) .. " " .. T["ms"], 1,1,1,1,1,1)
|
||||
GameTooltip:AddDoubleLine(T["Low"], tostring(lowMS or 0) .. " " .. T["ms"], 1,1,1,1,1,1)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
|
||||
fpsFrame:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
latencyFrame:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
|
||||
fpsFrame:Show()
|
||||
latencyFrame:Show()
|
||||
Update()
|
||||
|
||||
-- One native ClassicAPI ticker services both displays. Lua only wakes once
|
||||
-- per second instead of running two permanent OnUpdate handlers every frame.
|
||||
self.ticker = _G.C_Timer.NewTicker(1, Update)
|
||||
end
|
||||
|
||||
module.disable = function(self)
|
||||
if self.ticker then
|
||||
self.ticker:Cancel()
|
||||
self.ticker = nil
|
||||
end
|
||||
|
||||
if self.fpsFrame then self.fpsFrame:Hide() end
|
||||
if self.latencyFrame then self.latencyFrame:Hide() end
|
||||
end
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Show Bags"],
|
||||
description = T["Shows bag and keyring buttons when using the reduced actionbar layout. Hold Ctrl+Shift to move the bag bar."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Action Bar"],
|
||||
maintainer = "@shagu (GitHub)",
|
||||
enabled = nil,
|
||||
config = {
|
||||
["panelbag.scale"] = 1,
|
||||
}
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
-- The ShaguTweaks loader initializes module config while iterating an
|
||||
-- unordered table. On a fresh install the Reduced Actionbar key may not have
|
||||
-- been visited yet, so fall back to that module's declared default.
|
||||
local reducedTitle = T["Reduced Actionbar Size"]
|
||||
local reducedEnabled = ShaguTweaks_config and ShaguTweaks_config[reducedTitle]
|
||||
if reducedEnabled == nil then
|
||||
local reducedModule = ShaguTweaks.mods and ShaguTweaks.mods[reducedTitle]
|
||||
reducedEnabled = reducedModule and reducedModule.enabled and 1 or 0
|
||||
end
|
||||
if reducedEnabled ~= 1 then return end
|
||||
|
||||
-- Moving the bag bar is driven by ClassicAPI modifier events and native
|
||||
-- Region:IsMouseOver state. Do not keep a permanent polling fallback here.
|
||||
if not API.modifierstate or not API.regionmouseover then return end
|
||||
|
||||
local frames = {
|
||||
KeyRingButton, CharacterBag3Slot, CharacterBag2Slot, CharacterBag1Slot,
|
||||
CharacterBag0Slot, MainMenuBarBackpackButton,
|
||||
}
|
||||
|
||||
local bagframe = CreateFrame("Button", "ShaguTweaksReducedActionBarBags", UIParent)
|
||||
bagframe:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -8, 48)
|
||||
bagframe:SetWidth(180)
|
||||
bagframe:SetHeight(42)
|
||||
bagframe:SetScale(module.config["panelbag.scale"])
|
||||
|
||||
bagframe:SetFrameStrata("MEDIUM")
|
||||
bagframe:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 8, edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 3, bottom = 3 }
|
||||
})
|
||||
|
||||
bagframe:SetBackdropBorderColor(.9,.8,.5,1)
|
||||
bagframe:SetBackdropColor(.4,.4,.4,1)
|
||||
|
||||
bagframe:SetClampedToScreen(true)
|
||||
bagframe:SetMovable(true)
|
||||
bagframe:EnableMouse(true)
|
||||
bagframe:RegisterForDrag("LeftButton")
|
||||
|
||||
bagframe:SetUserPlaced(true)
|
||||
|
||||
bagframe:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
|
||||
local function UpdateMoveMode()
|
||||
local active = API.IsMouseOver(bagframe)
|
||||
and API.IsShiftKeyDown()
|
||||
and API.IsControlKeyDown()
|
||||
|
||||
if active then
|
||||
if bagframe.mousedisabled then return end
|
||||
bagframe.mousedisabled = true
|
||||
for _, frame in ipairs(frames) do
|
||||
frame:EnableMouse(0)
|
||||
end
|
||||
else
|
||||
if not bagframe.mousedisabled then return end
|
||||
bagframe.mousedisabled = false
|
||||
for _, frame in ipairs(frames) do
|
||||
frame:EnableMouse(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
bagframe:SetScript("OnEnter", UpdateMoveMode)
|
||||
bagframe:SetScript("OnLeave", UpdateMoveMode)
|
||||
|
||||
local modifier = CreateFrame("Frame", nil, bagframe)
|
||||
modifier:RegisterEvent("MODIFIER_STATE_CHANGED")
|
||||
modifier:SetScript("OnEvent", UpdateMoveMode)
|
||||
|
||||
bagframe:SetScript("OnDragStart", function()
|
||||
local shift = API.IsShiftKeyDown()
|
||||
local control = API.IsControlKeyDown()
|
||||
if not shift or not control then return end
|
||||
this:StartMoving()
|
||||
end)
|
||||
|
||||
bagframe:SetScript("OnDragStop", function()
|
||||
this:StopMovingOrSizing()
|
||||
end)
|
||||
|
||||
bagframe:SetScript("OnEvent", function()
|
||||
if this.initialized then return end
|
||||
this.initialized = true
|
||||
|
||||
ShaguTweaks.DarkenFrame(bagframe)
|
||||
|
||||
for id, frame in ipairs(frames) do
|
||||
local anchor = frames[id-1] or bagframe
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint("LEFT", anchor, id == 1 and "LEFT" or "RIGHT", id == 1 and 5 or 2, 0)
|
||||
frame:SetParent(bagframe)
|
||||
frame:SetScale(.8)
|
||||
frame.Show = nil
|
||||
frame:Show()
|
||||
|
||||
-- Child bag buttons normally own the mouse. Re-evaluate move mode when
|
||||
-- entering or leaving one so Ctrl+Shift already held before mouseover is
|
||||
-- handled without polling and without replacing their tooltip scripts.
|
||||
ShaguTweaks.HookScript(frame, "OnEnter", UpdateMoveMode)
|
||||
ShaguTweaks.HookScript(frame, "OnLeave", UpdateMoveMode)
|
||||
end
|
||||
|
||||
UpdateMoveMode()
|
||||
end)
|
||||
end
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Show Micro Menu"],
|
||||
description = T["Shows micro menu buttons when using the reduced actionbar layout. Hold Ctrl+Shift to move the micro menu."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Action Bar"],
|
||||
maintainer = "@shagu (GitHub)",
|
||||
enabled = nil,
|
||||
config = {
|
||||
["panelmicro.scale"] = 1,
|
||||
}
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
-- The ShaguTweaks loader initializes module config while iterating an
|
||||
-- unordered table. On a fresh install the Reduced Actionbar key may not have
|
||||
-- been visited yet, so fall back to that module's declared default.
|
||||
local reducedTitle = T["Reduced Actionbar Size"]
|
||||
local reducedEnabled = ShaguTweaks_config and ShaguTweaks_config[reducedTitle]
|
||||
if reducedEnabled == nil then
|
||||
local reducedModule = ShaguTweaks.mods and ShaguTweaks.mods[reducedTitle]
|
||||
reducedEnabled = reducedModule and reducedModule.enabled and 1 or 0
|
||||
end
|
||||
if reducedEnabled ~= 1 then return end
|
||||
|
||||
-- Moving the micro menu is driven by ClassicAPI modifier events and native
|
||||
-- Region:IsMouseOver state. Do not keep a permanent polling fallback here.
|
||||
if not API.modifierstate or not API.regionmouseover then return end
|
||||
|
||||
local frames = {
|
||||
CharacterMicroButton, SpellbookMicroButton, TalentMicroButton,
|
||||
QuestLogMicroButton, MainMenuMicroButton, SocialsMicroButton,
|
||||
WorldMapMicroButton, HelpMicroButton
|
||||
}
|
||||
|
||||
local microframe = CreateFrame("Button", "ShaguTweaksReducedActionBarMicroMenu", UIParent)
|
||||
microframe:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -8, 8)
|
||||
microframe:SetWidth(225)
|
||||
microframe:SetHeight(44)
|
||||
microframe:SetScale(module.config["panelmicro.scale"])
|
||||
|
||||
microframe:SetFrameStrata("MEDIUM")
|
||||
microframe:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 8, edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 3, bottom = 3 }
|
||||
})
|
||||
|
||||
microframe:SetBackdropBorderColor(.9,.8,.5,1)
|
||||
microframe:SetBackdropColor(.4,.4,.4,1)
|
||||
|
||||
microframe:SetClampedToScreen(true)
|
||||
microframe:SetMovable(true)
|
||||
microframe:EnableMouse(true)
|
||||
microframe:RegisterForDrag("LeftButton")
|
||||
|
||||
microframe:SetUserPlaced(true)
|
||||
|
||||
microframe:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
|
||||
local function UpdateMoveMode()
|
||||
local active = API.IsMouseOver(microframe)
|
||||
and API.IsShiftKeyDown()
|
||||
and API.IsControlKeyDown()
|
||||
|
||||
if active then
|
||||
if microframe.mousedisabled then return end
|
||||
microframe.mousedisabled = true
|
||||
for _, frame in ipairs(frames) do
|
||||
frame:EnableMouse(0)
|
||||
end
|
||||
else
|
||||
if not microframe.mousedisabled then return end
|
||||
microframe.mousedisabled = false
|
||||
for _, frame in ipairs(frames) do
|
||||
frame:EnableMouse(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
microframe:SetScript("OnEnter", UpdateMoveMode)
|
||||
microframe:SetScript("OnLeave", UpdateMoveMode)
|
||||
|
||||
local modifier = CreateFrame("Frame", nil, microframe)
|
||||
modifier:RegisterEvent("MODIFIER_STATE_CHANGED")
|
||||
modifier:SetScript("OnEvent", UpdateMoveMode)
|
||||
|
||||
microframe:SetScript("OnDragStart", function()
|
||||
local shift = API.IsShiftKeyDown()
|
||||
local control = API.IsControlKeyDown()
|
||||
if not shift or not control then return end
|
||||
this:StartMoving()
|
||||
end)
|
||||
|
||||
microframe:SetScript("OnDragStop", function()
|
||||
this:StopMovingOrSizing()
|
||||
end)
|
||||
|
||||
microframe:SetScript("OnEvent", function()
|
||||
if this.initialized then return end
|
||||
this.initialized = true
|
||||
|
||||
ShaguTweaks.DarkenFrame(microframe)
|
||||
|
||||
for id, frame in ipairs(frames) do
|
||||
local anchor = frames[id-1] or microframe
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint("LEFT", anchor, id == 1 and "LEFT" or "RIGHT", id == 1 and 3.5 or -2, id==1 and 10 or 0)
|
||||
frame:SetParent(microframe)
|
||||
frame.Show = nil
|
||||
frame:Show()
|
||||
|
||||
-- Child micro buttons normally own the mouse. Re-evaluate move mode when
|
||||
-- entering or leaving one so Ctrl+Shift already held before mouseover is
|
||||
-- handled without polling and without replacing their tooltip scripts.
|
||||
ShaguTweaks.HookScript(frame, "OnEnter", UpdateMoveMode)
|
||||
ShaguTweaks.HookScript(frame, "OnLeave", UpdateMoveMode)
|
||||
end
|
||||
|
||||
UpdateMoveMode()
|
||||
end)
|
||||
end
|
||||
Executable
+147
@@ -0,0 +1,147 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Unit Frame Abbreviated Names"],
|
||||
description = T["Abbreviates long target and target-of-target names."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Unit Frames"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
local maxLength = 15
|
||||
local cache = {}
|
||||
|
||||
local function AbbrevWord(word)
|
||||
return string.sub(word, 1, 1) .. ". "
|
||||
end
|
||||
|
||||
local function GetShortName(unit)
|
||||
local name = UnitName(unit)
|
||||
if not name then
|
||||
cache[unit] = nil
|
||||
return
|
||||
end
|
||||
|
||||
local cached = cache[unit]
|
||||
if cached and cached.raw == name then
|
||||
return cached.short
|
||||
end
|
||||
|
||||
local short = name
|
||||
|
||||
if strlen(short) > maxLength then
|
||||
short = string.gsub(short, "^(%S+) ", AbbrevWord)
|
||||
end
|
||||
|
||||
if strlen(short) > maxLength then
|
||||
short = string.gsub(short, "(%S+) ", AbbrevWord)
|
||||
end
|
||||
|
||||
cache[unit] = {
|
||||
raw = name,
|
||||
short = short,
|
||||
}
|
||||
|
||||
return short
|
||||
end
|
||||
|
||||
local function GetNameText(frame)
|
||||
if not frame then return end
|
||||
if frame.name then return frame.name end
|
||||
|
||||
if frame.GetName then
|
||||
local frameName = frame:GetName()
|
||||
if frameName then
|
||||
return _G[frameName .. "Name"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateFrame(frame, unit)
|
||||
local text = GetNameText(frame)
|
||||
local name = GetShortName(unit)
|
||||
if not text or not name then return end
|
||||
|
||||
-- Avoid forcing a FontString update when the displayed value is already
|
||||
-- correct. This matters most for the 250 ms target-of-target fallback.
|
||||
if not text.GetText or text:GetText() ~= name then
|
||||
text:SetText(name)
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateTarget()
|
||||
UpdateFrame(TargetFrame, "target")
|
||||
end
|
||||
|
||||
local function UpdateTargetTarget()
|
||||
if TargetofTargetFrame then
|
||||
UpdateFrame(TargetofTargetFrame, "targettarget")
|
||||
end
|
||||
end
|
||||
|
||||
local function EventValid(name)
|
||||
return API.eventutils
|
||||
and _G.C_EventUtils
|
||||
and _G.C_EventUtils.IsEventValid
|
||||
and _G.C_EventUtils.IsEventValid(name)
|
||||
end
|
||||
|
||||
local events = CreateFrame("Frame")
|
||||
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
events:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
|
||||
local hasUnitTarget = EventValid("UNIT_TARGET")
|
||||
local hasUnitName = EventValid("UNIT_NAME_UPDATE")
|
||||
|
||||
if hasUnitTarget then events:RegisterEvent("UNIT_TARGET") end
|
||||
if hasUnitName then events:RegisterEvent("UNIT_NAME_UPDATE") end
|
||||
|
||||
events:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" or event == "PLAYER_TARGET_CHANGED" then
|
||||
UpdateTarget()
|
||||
UpdateTargetTarget()
|
||||
elseif event == "UNIT_TARGET" then
|
||||
if arg1 == "target" then UpdateTargetTarget() end
|
||||
elseif event == "UNIT_NAME_UPDATE" then
|
||||
if arg1 == "target" then
|
||||
UpdateTarget()
|
||||
elseif arg1 == "targettarget" then
|
||||
UpdateTargetTarget()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Vanilla has no UNIT_TARGET event for target-of-target changes. Prefer the
|
||||
-- ClassicAPI native ticker so Lua only runs at the required 250 ms cadence,
|
||||
-- instead of entering a Lua OnUpdate handler every rendered frame. Keep the
|
||||
-- old throttled path only for older environments without C_Timer.NewTicker.
|
||||
if not hasUnitTarget then
|
||||
local function UpdateTargetTargetFallback()
|
||||
if UnitName("targettarget") then
|
||||
UpdateTargetTarget()
|
||||
end
|
||||
end
|
||||
|
||||
if _G.C_Timer and type(_G.C_Timer.NewTicker) == "function" then
|
||||
_G.C_Timer.NewTicker(.25, UpdateTargetTargetFallback)
|
||||
else
|
||||
local fallback = CreateFrame("Frame")
|
||||
fallback.elapsed = 0
|
||||
|
||||
fallback:SetScript("OnUpdate", function()
|
||||
this.elapsed = this.elapsed + (arg1 or 0)
|
||||
if this.elapsed < .25 then return end
|
||||
this.elapsed = 0
|
||||
UpdateTargetTargetFallback()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
UpdateTarget()
|
||||
UpdateTargetTarget()
|
||||
end
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
-- Adapted from TokensWorth/ShaguTweaks-mods (MIT, original copyright GryllsAddons).
|
||||
|
||||
local T = ShaguTweaks.T
|
||||
local Filter = ShaguTweaks.ExtrasChatFilter
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["World Chat Hider"],
|
||||
description = T["Hides World channel messages while inside an instance without leaving the channel."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = nil },
|
||||
category = T["Chat & Social"],
|
||||
enabled = nil,
|
||||
})
|
||||
|
||||
local function IsInsideInstance()
|
||||
if type(IsInInstance) ~= "function" then return false end
|
||||
|
||||
local inside = IsInInstance()
|
||||
return inside == 1 or inside == true
|
||||
end
|
||||
|
||||
local function IsWorldChannel()
|
||||
-- Vanilla 1.12 CHAT_MSG_CHANNEL exposes the base channel name in arg9.
|
||||
-- Custom channel names are not localized, so comparing the actual event is
|
||||
-- both cheaper and safer than querying GetChannelName("world") every time.
|
||||
if arg9 and string.lower(arg9) == "world" then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Conservative fallback for clients that omit arg9: arg4 is the numbered
|
||||
-- channel label (for example "5. World").
|
||||
if arg4 then
|
||||
local full = string.lower(arg4)
|
||||
if full == "world" then return true end
|
||||
|
||||
local _, _, base = string.find(full, "^%d+%.%s*(.+)$")
|
||||
if base == "world" then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function FilterWorldChat(event)
|
||||
if event ~= "CHAT_MSG_CHANNEL" then return false end
|
||||
if not IsInsideInstance() then return false end
|
||||
|
||||
return IsWorldChannel()
|
||||
end
|
||||
|
||||
module.enable = function(self)
|
||||
if Filter then
|
||||
Filter:Register("WorldChatHider", FilterWorldChat)
|
||||
end
|
||||
end
|
||||
|
||||
module.disable = function(self)
|
||||
if Filter then
|
||||
Filter:Unregister("WorldChatHider")
|
||||
end
|
||||
end
|
||||
Executable
+160
@@ -0,0 +1,160 @@
|
||||
local _G = ShaguTweaks.GetGlobalEnv()
|
||||
local T = ShaguTweaks.T
|
||||
local API = ShaguTweaks.API
|
||||
local hooksecurefunc = ShaguTweaks.hooksecurefunc
|
||||
|
||||
local module = ShaguTweaks:register({
|
||||
title = T["Reveal World Map"],
|
||||
description = T["Reveals unexplored world map areas and shows exploration hints."],
|
||||
expansions = { ["vanilla"] = true, ["tbc"] = false },
|
||||
category = T["Minimap & World Map"],
|
||||
maintainer = "@shagu (GitHub)",
|
||||
enabled = true,
|
||||
config = {
|
||||
["map.reveal.marker"] = "on",
|
||||
}
|
||||
})
|
||||
|
||||
module.enable = function(self)
|
||||
-- do not load if another map addon is controlling the world map
|
||||
if Cartographer then return end
|
||||
if METAMAP_TITLE then return end
|
||||
|
||||
local enabled = true
|
||||
local overlayTextures = {}
|
||||
local exploreFrames = {}
|
||||
|
||||
local mapreveal = {}
|
||||
mapreveal.onmap = CreateFrame("CheckButton", "shagutweaks_mapreveal_onmap", WorldMapFrame, "UICheckButtonTemplate")
|
||||
mapreveal.onmap.text = _G["shagutweaks_mapreveal_onmapText"]
|
||||
mapreveal.onmap:SetWidth(14)
|
||||
mapreveal.onmap:SetHeight(14)
|
||||
mapreveal.onmap:SetPoint("TOPLEFT", WorldMapButton, "TOPLEFT", 1, 19)
|
||||
mapreveal.onmap.text:SetPoint("LEFT", mapreveal.onmap, "RIGHT", 2, 0)
|
||||
mapreveal.onmap.text:SetText(T["Reveal Unexplored"])
|
||||
|
||||
mapreveal.onmap:SetScript("OnShow", function()
|
||||
this:SetChecked(enabled)
|
||||
end)
|
||||
|
||||
mapreveal.onmap:SetScript("OnClick", function()
|
||||
enabled = this:GetChecked() and true or false
|
||||
WorldMapFrame_Update()
|
||||
end)
|
||||
|
||||
local function HideRevealLayer()
|
||||
for _, texture in ipairs(overlayTextures) do
|
||||
texture:Hide()
|
||||
end
|
||||
|
||||
for _, frame in ipairs(exploreFrames) do
|
||||
frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function ExploreEnter()
|
||||
WorldMapTooltip:ClearLines()
|
||||
WorldMapTooltip:SetOwner(this, "ANCHOR_TOP")
|
||||
WorldMapTooltip:AddLine(T["Exploration Point"]..":", .3, 1, .8)
|
||||
WorldMapTooltip:AddLine(this.name or "?", 1, 1, 1)
|
||||
WorldMapTooltip:Show()
|
||||
end
|
||||
|
||||
local function ExploreLeave()
|
||||
WorldMapTooltip:Hide()
|
||||
end
|
||||
|
||||
local function GetExploreCenter(overlay)
|
||||
if overlay.hitRectLeft and overlay.hitRectRight
|
||||
and overlay.hitRectTop and overlay.hitRectBottom then
|
||||
return (overlay.hitRectLeft + overlay.hitRectRight) / 2,
|
||||
(overlay.hitRectTop + overlay.hitRectBottom) / 2
|
||||
end
|
||||
|
||||
if overlay.offsetX and overlay.offsetY
|
||||
and overlay.textureWidth and overlay.textureHeight then
|
||||
return overlay.offsetX + overlay.textureWidth / 2,
|
||||
overlay.offsetY + overlay.textureHeight / 2
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateRevealLayer()
|
||||
HideRevealLayer()
|
||||
if not enabled then return end
|
||||
|
||||
-- ClassicAPI reads WorldMapOverlay.dbc directly from the active client,
|
||||
-- including custom Turtle-like zones and their real tile layout.
|
||||
local overlays = API.GetUnexploredMapTextures()
|
||||
if not overlays then return end
|
||||
|
||||
local textureIndex = 0
|
||||
local exploreIndex = 0
|
||||
|
||||
for _, overlay in ipairs(overlays) do
|
||||
if overlay.tiles then
|
||||
for _, tile in ipairs(overlay.tiles) do
|
||||
if tile.file and tile.width and tile.height
|
||||
and tile.offsetX and tile.offsetY then
|
||||
textureIndex = textureIndex + 1
|
||||
|
||||
local texture = overlayTextures[textureIndex]
|
||||
if not texture then
|
||||
texture = WorldMapDetailFrame:CreateTexture(nil, "ARTWORK")
|
||||
overlayTextures[textureIndex] = texture
|
||||
end
|
||||
|
||||
texture:ClearAllPoints()
|
||||
texture:SetWidth(tile.width)
|
||||
texture:SetHeight(tile.height)
|
||||
texture:SetTexCoord(0, tile.texCoordX or 1, 0, tile.texCoordY or 1)
|
||||
texture:SetPoint("TOPLEFT", WorldMapDetailFrame, "TOPLEFT", tile.offsetX, -tile.offsetY)
|
||||
texture:SetTexture(tile.file)
|
||||
texture:SetVertexColor(.4, .4, .4, 1)
|
||||
texture:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.config["map.reveal.marker"] == "on" then
|
||||
local x, y = GetExploreCenter(overlay)
|
||||
if x and y then
|
||||
exploreIndex = exploreIndex + 1
|
||||
|
||||
local explore = exploreFrames[exploreIndex]
|
||||
if not explore then
|
||||
explore = CreateFrame("Frame", nil, WorldMapDetailFrame)
|
||||
explore:SetWidth(16)
|
||||
explore:SetHeight(16)
|
||||
explore:EnableMouse(true)
|
||||
explore:SetFrameLevel(255)
|
||||
explore:SetScript("OnEnter", ExploreEnter)
|
||||
explore:SetScript("OnLeave", ExploreLeave)
|
||||
|
||||
explore.tex = explore:CreateTexture(nil, "OVERLAY")
|
||||
explore.tex:SetTexture("Interface\\WorldMap\\WorldMap-MagnifyingGlass")
|
||||
explore.tex:SetBlendMode("ADD")
|
||||
explore.tex:SetTexCoord(.08, .92, .08, .92)
|
||||
explore.tex:SetAllPoints()
|
||||
|
||||
exploreFrames[exploreIndex] = explore
|
||||
end
|
||||
|
||||
explore:ClearAllPoints()
|
||||
explore:SetPoint("CENTER", WorldMapDetailFrame, "TOPLEFT", x, -y)
|
||||
explore.name = API.GetAreaInfo(overlay.areaID)
|
||||
or overlay.textureName
|
||||
or "?"
|
||||
explore:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Keep Blizzard's normal explored-map rendering untouched and append only
|
||||
-- ClassicAPI's unexplored overlays after the native update.
|
||||
hooksecurefunc("WorldMapFrame_Update", UpdateRevealLayer)
|
||||
|
||||
if WorldMapFrame:IsShown() then
|
||||
UpdateRevealLayer()
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user