13 Commits
Author SHA1 Message Date
OEVGandClaude Opus 5.5 c5b3df38a1 Version 3.6
Set ## Version to 3.6 in the toc and mark the release in the changelog
and README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 04:55:19 +00:00
OEVGandClaude Opus 5.5 15712e82c7 WorldMap Window: keep the size, drag by the title, OctoBus Transports button; add Loot History
- The map keeps the scale picked with Ctrl+Mousewheel (saved per
  character, .4 to 2). It was reset to .85 on every open and by the
  Turtle WoW maximize wrapper.
- The title bar drags the window: on Turtle the title sits 17 units above
  the frame, so the frame's click area now extends over it.
- With OctoBus loaded, a Transports button left of the Continent dropdown
  runs OctoBus' own toggle. Its window is lifted above the map while both
  are open and restored when the map closes.
- New Loot History module (/loothistory), ported from brues-code/pfUI and
  rebuilt with stock 1.12 widgets, on ClassicAPI's C_LootHistory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 04:08:31 +00:00
OEVGandClaude Opus 5.5 f3fd1d5c56 README: add Swing Timer, Wand Timer and today's other changes
The feature list now covers Swing Timer and Wand Timer (with the queue
colors and the ClassicAPI requirement), Hide Pet Unit Frame, the XP
numbers, the World Map raid colors, the bounded player cache and Range
Color's 0.2-second check. Swing Timer and Wand Timer join the list of
modules that use ClassicAPI directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 03:11:44 +00:00
OEVGandClaude Opus 5.5 0a0cd055c4 Swing Timer: color the main-hand bar for queued Heroic Strike, Cleave and Maul
Ported from pfUI's swing timer. UNIT_SPELLCAST_SENT with
C_Spell.IsNextMeleeSpell and the spell name sets the queue; it clears on
UNIT_SPELLCAST_SUCCEEDED or _FAILED, on leaving combat, or when the
ability's action button goes dark after being lit (Esc or a second press
un-queues it without an event). Heroic Strike is light blue instead of
pfUI's yellow, which would not stand out on the gold bar; Cleave green
and Maul orange as in pfUI. Out-of-range gray still wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 01:57:32 +00:00
OEVGandClaude Opus 5.5 151abf3280 Add Swing Timer and Wand Timer, ported from pfUI
Two Action Bar options, off by default, ported from brues-code/pfUI's
swing timer and restyled as classic cast bars: dark background,
UI-StatusBar fill, the cast bar border and spark, and the weapon's icon
on the left like Improved Castbar's spell icon.

- Swing Timer: main hand and off hand (shields are skipped).
- Wand Timer: wand Shoot, and Auto Shot for bows, guns and crossbows,
  red during Auto Shot's last half second.

ClassicAPI's PLAYER_SWING drives the timers and PLAYER_SWING_RANGE_UPDATE
grays a bar while the target is out of reach. Auto-attack state uses the
1.12 PLAYER_ENTER_COMBAT/PLAYER_LEAVE_COMBAT and START/STOP_AUTOREPEAT_SPELL
events, plus START/STOP_AUTOATTACK on ClassicAPI v1.18.14+. Without
PLAYER_SWING (before v1.15.10) both options do nothing.

The bars stack just above the cast bar. Holding Ctrl+Shift+Alt shows a
preview to drag; right-click resets. The position is saved per character.

pfUI's MIT license is preserved in LICENSES/pfUI-MIT.txt and credited in
THIRD_PARTY_NOTICES.md and the README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 01:48:51 +00:00
OEVGandClaude Opus 5.5 a482808743 Add Hide Pet Unit Frame and show XP numbers on the XP bar
Hide Pet Unit Frame (Unit Frames, off by default) parents PetFrame to a
frame that is never shown. PetFrame_Update shows the frame again on
every pet change, so a plain Hide() would not last; with the hidden
parent the stock pet code runs unchanged and nothing draws.

XP Bar Text shows current/needed XP like the reputation bar
("1555/3000  51%"), followed by the rested bonus as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 01:25:13 +00:00
OEVGandClaude Opus 5.5 f5f651936c Enlarged Minimap: run the buff anchor check only while BuffFrame is shown
The per-frame check that keeps the buff rows clear of the enlarged
minimap now lives on a child frame of BuffFrame, so it only runs while
the stock buffs are visible. Buff addons such as VCB hide BuffFrame and
TemporaryEnchantFrame; the stock BuffFrame_Enchant_OnUpdate re-anchoring
stops with them, and the check was moving hidden frames every frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 00:45:25 +00:00
OEVGandClaude Opus 5.5 4ef534fabb Fix raid colors on the world map and drop two per-frame checks
WorldMap Class Colors assumed WorldMapRaidN shows raidN. In a raid the
map packs its icons and stores the real unit on each icon, so icons
showed other players' class colors. Use the icon's unit, or its name
for battleground team members outside the raid. The updaters are now
children of the maps, so they only run while a map is open.

Class Portraits checked the target-of-target GUID on every frame. Check
when the stock handler reports a new name, and otherwise five times a
second.

Enlarged Minimap is unchanged: the stock UI re-anchors the buff frame
every frame, so its check has to as well. Documented in
Docs/PERFORMANCE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 00:32:33 +00:00
OEVGandClaude Opus 5.5 c7d0f9c5f4 Add a changelog and link it from the README
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 00:18:50 +00:00
OEVGandClaude Opus 5.5 1ae8398a2d Bound the player cache and stop per-frame range checks
- libunitscan: expire the shared player cache once per session (180 days
  for known classes, 90 for class-less, at most 10,000 players). Every
  sighting now stamps an age, so roster and mouseover entries can expire.
- libunitscan: keep a player's GUID only while the class is unknown and
  resolve such entries later with GetPlayerInfoByGUID.
- libunitscan: skip the GUID lookup for known chat speakers seen within
  the hour, throttle full guild rescans to once a minute, skip UnitGUID
  when UnitClass answered, and query ClassicAPI's player cache once per
  miss instead of twice.
- Social Colors: drop its separate 180-day cleanup.
- Range Color: use its own 0.2 s timer. Sharing Blizzard's rangeTimer ran
  the check every frame for every ranged button.
- Docs/PERFORMANCE.md: document the cache rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-02 00:09:59 +00:00
OEVGandClaude Sonnet 5.5 e4c7741226 Tidy README and review ClassicAPI v1.18.13 changes
- Debuff Timer: hide the native Cooldown's own countdown numbers so the
  countdownForCooldowns CVar cannot double the overlay text.
- README: merge the duplicated feature lists, match module names and
  default states to the in-game options, link the extra Docs, and state
  the recommended ClassicAPI version.
- Docs/CLASSICAPI_UPDATES.md: record the v1.17.7 to v1.18.13 review.
- .gitignore: ignore editor and merge leftovers.
- ShaguTweaks.toc: drop the Russian note from the English-only fork.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
2026-11-01 00:15:11 +00:00
OEVG d705a9b221 Update README.md 2026-10-30 20:10:17 +00:00
OEVG 48d45f8fe6 Apply Patch 2026-10-30 20:06:32 +00:00
72 changed files with 2729 additions and 15475 deletions
+8
View File
@@ -0,0 +1,8 @@
* text=auto eol=lf
*.tga -text
*.blp -text
*.jpg -text
*.jpeg -text
*.png -text
*.gif -text
*.zip -text
+10
View File
@@ -0,0 +1,10 @@
*.bak
*.tmp
*.orig
*.rej
*.log
.DS_Store
Thumbs.db
desktop.ini
.vscode/
.idea/
+109
View File
@@ -0,0 +1,109 @@
# Changelog
Changes in this fork (OEVG/ShaguTweaks-ClassicAPI), newest first. Entries are dated and list the commit. Version numbers start at 3.0; the version is in `ShaguTweaks.toc` (`## Version`).
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.
## Version 3.6 — 2026-11-02
Version 3.6 is the addon as of this entry, with every change listed below.
## 2026-11-02 — Map window, Transports button and Loot History
### Added
- **Loot History** (Loot, on): `/loothistory` opens a window with the recent group-loot rolls. Each item shows who won it with which roll; click it to see everyone's need, greed or pass and their rolls. A Clear button empties the list. Ported from brues-code/pfUI's loot history and drawn with the classic dialog look. Needs ClassicAPI's `C_LootHistory`, which keeps the last 128 rolled items through `/reload`.
- **WorldMap Window:** when [OctoBus](https://github.com/Phridgey/OctoBus) is installed, a **Transports** button left of the Continent dropdown shows or hides the OctoBus window, like a left-click on its minimap icon. While the map is open, the OctoBus window is drawn above it.
### Fixed
- **WorldMap Window:** the map keeps the size picked with Ctrl+Mousewheel after closing and reopening it, and after a reload. It used to go back to the default size every time it opened.
- **WorldMap Window:** the title bar drags the window too. On Turtle WoW the title is drawn above the window's edge, so only the dropdown row could be dragged before.
## 2026-11-01 — Swing Timer and Wand Timer
### Added
- **Swing Timer** (Action Bar, off by default): main-hand and off-hand swing timers styled like the classic cast bar, with the weapon's icon on the left. They sit just above the cast bar; hold Ctrl+Shift+Alt to drag them, right-click to reset. A bar turns gray while the target is out of reach.
- **Swing Timer queue colors:** the main-hand bar shows a queued Heroic Strike in light blue and Cleave in green (warriors), and Maul in orange (druids). It goes back to gold when the swing goes off, is refused, or is cancelled.
- **Wand Timer** (Action Bar, off by default): the time until your next wand shot in the same style. It also times Auto Shot for bows, guns and crossbows, and turns red during the last half second, when moving would cancel the shot.
- Both are ported from brues-code/pfUI's swing timer and need ClassicAPI v1.15.10 or newer (`PLAYER_SWING`).
## 2026-11-01 — Hide Pet Unit Frame and XP numbers
### Added
- **Hide Pet Unit Frame** (Unit Frames, off by default): hides the pet frame below the player frame for pet classes.
### Changed
- **XP Bar Text:** hovering the experience bar shows the current and needed XP, like the reputation bar (`1555/3000`), followed by the percentage and the rested bonus.
## 2026-11-01 — World map colors and per-frame checks
### Fixed
- **WorldMap Class Colors:** raid members on the world map and battlefield minimap show their own class color. In a raid the map packs its icons (the first icon is the first raid member on that map, not `raid1`), so icons used to show other players' colors, such as a priest in paladin pink. Battleground team members outside your raid are colored from the player cache.
### Changed
- **WorldMap Class Colors:** runs only while the world map or battlefield minimap is open.
- **Unit Frame Class Portraits:** the target-of-target check runs five times a second instead of every frame. A new target of target still gets its class icon at once.
- **Enlarged Minimap:** the check that keeps the buff rows clear of the bigger minimap runs only while the default buff frame is shown. With a buff addon that hides it, such as VCB, the check stops.
## 2026-11-01 — Performance (`1ae8398`)
### Changed
- **Player cache:** the shared cache (`ShaguTweaks_player_cache`) now expires once per session. Players with a known class are kept 180 days after they were last seen, players whose class was never learned 90 days, and the cache holds at most 10,000 players (the oldest sightings go first). Before this, most entries never expired and one account's file reached 4.1 MB. See [Docs/PERFORMANCE.md](Docs/PERFORMANCE.md).
- **Player cache:** every sighting (mouseover, target, nameplate, party, raid, guild, friends, `/who`, chat) refreshes a player's age. Guild members and friends are refreshed each session, so they do not expire.
- **Player cache:** a player's GUID is saved only while the class is unknown. Such players are resolved later with `GetPlayerInfoByGUID`.
- **Chat:** a speaker whose class is known and who spoke within the hour no longer triggers a GUID lookup.
- **Guild roster:** a full rescan runs at most once a minute unless the member count changed.
- **Range Color:** the range check runs every 0.2 seconds per button. It used to run on every frame for every ranged button.
- **Range Color:** Blizzard's own range check works again, so hotkey numbers turn red out of range, as in the default UI.
### Removed
- Social Colors' separate 180-day cache cleanup. Expiry now lives in `libs/libunitscan.lua` and runs whether or not Social Colors is enabled.
### Upgrade note
- The first login after updating can remove many old cache entries. Class colors and chat levels come back as soon as those players are seen again.
## 2026-10-31 — ClassicAPI v1.18.13 review and tidy-up (`e4c7741`)
### Fixed
- **Debuff Timer:** native cooldown frames hide ClassicAPI's own countdown numbers, so the timer text is not doubled when the `countdownForCooldowns` CVar is on.
### Changed
- README rewritten: one feature list, module names and default states matching the in-game options, the recommended ClassicAPI version, and links to the docs.
- [Docs/CLASSICAPI_UPDATES.md](Docs/CLASSICAPI_UPDATES.md) records the review of ClassicAPI v1.17.7 to v1.18.13. ClassicAPI v1.18.10 or newer is recommended for its nameplate crash fix.
- `.gitignore` covers editor and merge leftovers.
### Removed
- The Russian `## Notes-ruRU` line in `ShaguTweaks.toc`.
## 2026-10-30 — OEVG customization (`48d45f8`, `d705a9b`)
### Added
- **Modern Class Colors:** an optional palette for all nine Vanilla classes, including blue shamans, with class-tinted health artwork and precolored rage, energy and focus bars. Mana uses the light-blue retail fill on every supported frame, including the druid's mana bar in bear and cat form. See [Docs/MODERN_MANA.md](Docs/MODERN_MANA.md).
- **White Unit Frame Numbers:** health, percentage and power numbers in white instead of gold.
- **Unit Frame Big Health:** the resting glow is limited to the portrait.
### Changed
- **Real Health Numbers:** percentage on the left and current health on the right. The numbers copy the character name's font, size and shadow. Unknown target health stays percentage-only.
- **Movable Unit Frames:** hold Ctrl+Shift+Alt to drag.
- **Tooltip Details:** guild ranks and the leader marker are restored, with optional modern class colors and health texture. Cached colors no longer leak between tooltips.
- **Range Color:** spells without a range no longer use the legacy fallback, and wrong friendly or hostile targets and stale red or no-mana tints are handled.
- **ClassicAPI integration:** native cooldown frames, filtered unit events, corrected chat identity and payload handling, and fewer duplicate player-cache records when ClassicAPI's own player cache is on. See [Docs/CLASSICAPI_UPDATES.md](Docs/CLASSICAPI_UPDATES.md).
### Removed
- All non-English locale and translation files. The addon loads English data only.
- The **Unit Frame Health Colors** module (`mods/health-color.lua`).
+124
View File
@@ -0,0 +1,124 @@
# ClassicAPI integration updates
These changes use newer ClassicAPI features while retaining older fallback paths.
ClassicAPI v1.18.13 was the latest release reviewed on 2026-10-31 (the previous
review covered v1.17.6 on 2026-10-24). Update both ClassicAPI's DLL and its
bundled `!!!ClassicAPI` addon together, following its installation
instructions; a DLL update needs a full client restart.
The integration code below was written against v1.17.6. The v1.17.7 to v1.18.13
review found nothing that needed a rewrite; see
[Reviewed since v1.17.6](#reviewed-since-v1176).
## Changes
- **Health correctness:** `UnitHealthMissing` returns a deficit in the same units
as the health getters. It can be a percentage, so it is no longer used to infer
absolute health. Direct health and the existing combat-estimation cache remain.
- **Chat identity:** `GetPlayerInfoByGUID` supplies the name in return position 6.
The synchronous class fallback now checks that name against the requested player.
- **Cooldown rendering:** Target Debuff Timer creates native `Cooldown` frames on
ClassicAPI 1.17.1+, retaining `Model` frames on older builds. Numeric timers and
the existing timer dispatcher are preserved.
- **Unit event filtering:** health/power overlays register only player, target and
pet unit events; the health estimator and target castbar register only target
unit events. Older clients without `RegisterUnitEvent` use plain registration
and their existing handler-side checks.
- **Chat payload:** spam filtering and passive sender collection read
`GetCurrentChatEvent` when available. The filter uses local payload values and
does not overwrite shared `arg1`/`arg2` globals. The older global path remains.
- **Player-cache duplication:** when native persistence is already enabled and
`C_PlayerCache.RememberPlayer` succeeds, new chat-only sightings do not also
create ShaguTweaks SavedVariables records. Native class-only lookups can avoid
creating another record. Existing records, raw levels and social history stay
intact. With persistence off or native storage unavailable, the original Lua
cache behavior remains. The addon never enables native persistence itself.
## Reviewed since v1.17.6
156 ClassicAPI commits (v1.17.7 to v1.18.13) were read against the modules in
this addon.
**Changed here**
- **Debuff Timer:** the native `Cooldown` frame it creates now calls
`SetHideCountdownNumbers(true)` when available. ClassicAPI can draw its own
countdown numbers when the `countdownForCooldowns` CVar is `1` (default `0`),
which would have doubled the timer text this module draws itself.
**Worth knowing, no code change**
- **Nameplate crash fix (v1.18.10):** world-tick subscribers now run after the
UI draw. Before this, an addon that restyled a nameplate from
`NAME_PLATE_CREATED` or a `C_Timer` callback could crash the client. No
crash was traced to this addon, but v1.18.10 or newer is recommended for the
Nameplates modules.
- **Range Color:** since v1.18.0, `C_Spell.IsSpellInRange` returns `nil` for a
unit the spell cannot be cast at. The module already treats `nil` as "no
range to fail", so it needs nothing.
- **Cooldown Numbers:** action, bag and spellbook cooldowns are still 1.12
indicator Models, and `CooldownFrame_SetTimer` still serves them. The
module's hook is still required; the native countdown numbers do not replace
it.
- **Filtered unit events:** `RegisterUnitEvent` now also filters custom events
that ClassicAPI fires itself (v1.18.9). The vanilla events used here were
already filtered.
**Not adopted**
- `UNIT_POWER_UPDATE`, `UNIT_POWER_FREQUENT`, `UNIT_MAXPOWER` (v1.18.10): the
legacy `UNIT_MANA` / `UNIT_ENERGY` / `UNIT_RAGE` events still fire and the
overlays depend on their payloads. Switching would add a code path without a
measured gain.
- `UNIT_AURA_DELTA` and aura instance IDs (v1.17.10): the debuff library
refreshes on `UNIT_AURA` for the target and reads auras by index, so it does
not need change sets.
- `UNIT_TARGET`, `UnitFacing`, `wipe`, the `Cooldown` countdown formatter and
the other new APIs: no module here needs them.
Nothing from this review was tested in game.
## Measuring performance
On current ClassicAPI, the following prints ShaguTweaks' Lua memory in KiB:
```lua
/script UpdateAddOnMemoryUsage(); DEFAULT_CHAT_FRAME:AddMessage(GetAddOnMemoryUsage("ShaguTweaks"))
```
Memory readings include uncollected objects. For comparisons, collect through
GentleGC first and measure at the same point after login and after playing.
Texture/native allocations are not equivalent to this Lua-memory counter.
For a short CPU investigation:
1. Run `/console scriptProfile 1`, then `/reload`.
2. Run `/script ResetCPUUsage()` and play through a representative encounter.
3. Read the total CPU time in milliseconds:
`/script UpdateAddOnCPUUsage(); DEFAULT_CHAT_FRAME:AddMessage(GetAddOnCPUUsage("ShaguTweaks"))`
4. Run `/console scriptProfile 0`, then `/reload` when finished.
Profiling adds overhead. Compare the same workload and measurement duration;
the cumulative CPU number alone is not an FPS measurement. Profiling is not
turned on by this addon.
## Verification
Lua 5.1 syntax checks cover all addon Lua files. Mocked regression checks cover:
unknown percentage health, valid cached estimates, direct health, native and
fallback unit-event registration, sixth-return chat identity and name mismatch,
native chat payload with corrupted globals, duplicate filtering, native/legacy
cooldown creation, and persistence enabled/disabled while retaining levels and
history. Existing health overlays and English-only load-list checks also pass.
These checks do not replace in-game rendering, event-order and combat testing;
no memory/FPS improvement is claimed without a live measurement.
## Primary references
- [ClassicAPI v1.18.13](https://github.com/brues-code/ClassicAPI/releases/tag/v1.18.13)
- [API reference (v1.18.13)](https://github.com/brues-code/ClassicAPI/blob/v1.18.13/docs/API.md)
- [ClassicAPI v1.17.6](https://github.com/brues-code/ClassicAPI/releases/tag/v1.17.6)
- [Nameplate crash fix](https://github.com/brues-code/ClassicAPI/commit/4eb214f)
- [Native cooldown rendering](https://github.com/brues-code/ClassicAPI/commit/b83bb5b52b11ffd51dd59aa7465b1185222f2bd4)
- [Reliable chat payload](https://github.com/brues-code/ClassicAPI/commit/b5aed30a5db5ef033dd9a6619d8f74310787c4c9)
- [Native player-name lookup](https://github.com/brues-code/ClassicAPI/commit/c7bb90895f8802ba837833d5ad1f9acbe0afece4)
+55
View File
@@ -0,0 +1,55 @@
# Modern power bars
Modern Class Colors uses precolored retail resource fills, with frame-specific variants of ModernPowerMana.tga, ModernPowerRage.tga, ModernPowerFocus.tga and ModernPowerEnergy.tga. Each bar uses white vertex tint (1, 1, 1), preserving the native artwork's colors and gradient. Flat PowerBarColor tints multiplied by the old grayscale texture made mana too dark.
## Verified source
- Blizzard client build 12.1.5.70448, interface/hud/uiunitframe2x.blp, FileDataID 4642466.
- Download: https://wago.tools/api/casc/4642466?version=12.1.5.70448
- Atlas coordinates: https://github.com/Ketho/BlizzardInterfaceResources/blob/f3b334b33bd19fa6a0250f7922ecfa83db7fe356/Resources/AtlasInfo.lua
- Retail application uses the resource atlas and white tint: https://github.com/Gethe/wow-ui-source/blob/live/Interface/AddOns/Blizzard_UnitFrame/Mainline/UnitFrame.lua
## Frame-specific fitting
Player bars use the player artwork. Warlock mana uses the rectangular ClassResource variant seen in retail warlock frames; rogue/druid energy uses the corresponding energy variant. Other player resources retain the standard player artwork. These are visual choices for the Vanilla client; retail specialization/resource mechanics are not imported.
Target bars use Target artwork. Pet and target-of-target bars use the smaller TargetofTarget artwork. Party bars use Party artwork from interface/hud/uipartyframe.blp (FileDataID 4681512), downloaded from https://wago.tools/api/casc/4681512?version=12.1.5.70448 . Both sheets use the same build and pinned atlas metadata above.
The crops include the native dark edges and endcaps. Bilinear resizing (instead of Lanczos) avoids sharpening overshoot, and export dimensions are closer to the smaller Vanilla bars. White vertex tint is retained. A solid black BACKGROUND texture behind each styled power bar prevents the underlying Vanilla backing from tinting transparent edges and provides a neutral empty portion; it hides for missing units and unknown resource types. Frame sizes, positions, health artwork and gold text are unchanged. Animated retail sparks, retail frame borders and masks are not included, so a pixel-perfect match to every BetterBlizzFrames profile is not promised.
Exact source crops use exclusive right/bottom edges:
| Asset | Sheet | Left | Top | Right | Bottom | Export |
| --- | --- | --- | --- | --- | --- | --- |
| ModernPowerEnergy.tga | uiunitframe2x | 1667 | 479 | 1915 | 499 | 128x16 |
| ModernPowerFocus.tga | uiunitframe2x | 387 | 501 | 635 | 521 | 128x16 |
| ModernPowerMana.tga | uiunitframe2x | 637 | 501 | 885 | 521 | 128x16 |
| ModernPowerRage.tga | uiunitframe2x | 1137 | 501 | 1385 | 521 | 128x16 |
| ModernPowerEnergyClass.tga | uiunitframe2x | 1637 | 501 | 1885 | 521 | 128x16 |
| ModernPowerManaClass.tga | uiunitframe2x | 387 | 523 | 635 | 543 | 128x16 |
| ModernPowerEnergyTarget.tga | uiunitframe2x | 1666 | 415 | 1934 | 435 | 256x16 |
| ModernPowerFocusTarget.tga | uiunitframe2x | 387 | 457 | 655 | 477 | 256x16 |
| ModernPowerManaTarget.tga | uiunitframe2x | 657 | 457 | 925 | 477 | 256x16 |
| ModernPowerRageTarget.tga | uiunitframe2x | 1197 | 457 | 1465 | 477 | 256x16 |
| ModernPowerEnergySmall.tga | uiunitframe2x | 1607 | 151 | 1755 | 165 | 128x16 |
| ModernPowerFocusSmall.tga | uiunitframe2x | 1757 | 151 | 1905 | 165 | 128x16 |
| ModernPowerManaSmall.tga | uiunitframe2x | 1857 | 337 | 2005 | 351 | 128x16 |
| ModernPowerRageSmall.tga | uiunitframe2x | 1163 | 441 | 1311 | 455 | 128x16 |
| ModernPowerEnergyParty.tga | uipartyframe | 70 | 179 | 144 | 186 | 128x8 |
| ModernPowerFocusParty.tga | uipartyframe | 146 | 179 | 220 | 186 | 128x8 |
| ModernPowerManaParty.tga | uipartyframe | 1 | 191 | 75 | 198 | 128x8 |
| ModernPowerRageParty.tga | uipartyframe | 153 | 191 | 227 | 198 | 128x8 |
BetterBlizzFrames can retain the native game artwork when custom power textures are disabled, or replace it with a user-selected SharedMedia texture. These assets reproduce native retail fills, not an arbitrary BetterBlizzFrames saved profile.
Unknown/custom resource types retain their original texture and tint. Shapeshifts and target changes reselect the appropriate resource texture. Disabling Modern and reloading restores stock behavior. Gold numbers are unchanged.
Restart WoW after installation so new texture files are available. Lua 5.1 mock checks cover all four resource transitions, frame-family and class-variant selection, white tint enforcement, pet/party bars, missing-unit and unknown-resource background cleanup, disabled behavior and repeated initialization. Actual in-game rendering remains to be checked.
## Rectangular mana slots
All Modern mana bars now use ModernPowerManaClass.tga, the native rectangular retail mana fill. The regular retail target/small/party art has angled transparent corners that leave wedges inside Vanilla's rectangular mana slots. Sharing the rectangular fill fixes that gap for all classes and supported frames, while preserving white tint, the retail blue gradient, frame dimensions and text. Other resources retain their frame-specific variants.
## Druid secondary mana bar
PlayerFrameAlternatePowerBar uses the same rectangular light-blue mana fill and white tint. Its resource type is fixed to mana while the primary player bar changes to rage or energy. This is independent of the white-number option. The temporary deep-blue tint experiment was reverted.
+76
View File
@@ -0,0 +1,76 @@
# Performance notes
## Player cache (`ShaguTweaks_player_cache`)
The account-wide player cache feeds Social Colors, Chat Levels and the
nameplate/chat class colors. It is read at every login and `/reload` and
written at every logout, so its size is a direct load-time and memory cost.
Earlier versions let it grow without limit; one account reached 4.1 MB.
`libs/libunitscan.lua` now owns expiry and runs it once per session, at the
first loading screen:
| Entry | Kept for |
|---|---|
| Player whose class is known | 180 days after last sighting |
| Player whose class was never learned | 90 days after last sighting |
| Whole cache | at most 10,000 players; the oldest sightings go first |
- Every sighting refreshes the age: mouseover, target, nameplate, party,
raid, guild roster, friend list, `/who` and chat.
- Guild members and friends are refreshed by their roster events every
session, so they do not expire while they stay in the guild or friend list.
- Entries saved before this change have no age. They are dated from the
cache's existing grace start (`players_cleanup`), as the old Social Colors
cleanup did. The first login after the update can remove many entries.
- A player's GUID is saved only while the class is unknown. Such entries are
resolved later with `GetPlayerInfoByGUID` when the engine knows the GUID.
The separate 180-day cleanup in Social Colors is removed. It ran only every
180 days and only while that module was enabled.
## Event handling
- Chat: a speaker whose class is known and who spoke within the hour needs
no GUID lookup or write.
- Guild roster: a full rescan runs at most once a minute unless the member
count changed. Many addons request the roster every few seconds.
- Mouseover, target, nameplate and group sightings skip `UnitGUID` when
`UnitClass` already gave the class.
- A name missing from the cache queries ClassicAPI's player cache once
instead of twice.
## Range Color
The module shared Blizzard's `rangeTimer` and tested it against 0.2 seconds.
Once the timer had reset, that test was always true, so the full range check
ran on every frame for every ranged action button. It now keeps its own
0.2-second timer. Blizzard's own range check runs again, so the hotkey text
turns red out of range, as in the default UI.
## Map and portrait checks
- WorldMap Class Colors updates its icons ten times a second, and only while
the world map or battlefield minimap is open. It used to run a check on
every frame with both maps closed.
- Class Portraits checks the target of target five times a second, plus at
once when the stock frame reports a new name. It used to call `UnitGUID`
on every frame.
## Not changed
- Enlarged Minimap still checks the buff anchors every frame while the
default buff frame is shown. The stock `BuffFrame_Enchant_OnUpdate` moves
the buff frame back on every frame, so a less frequent check would let the
buffs jump under the minimap. It writes only when an anchor has actually
moved. The check is a child of `BuffFrame`, so it stops when a buff addon
such as VCB hides the default buffs (the stock re-anchoring stops then too).
- Health Numbers and Big Health use one-shot deferred updates only.
- Both castbars update only while visible; Energy Tick hides at full mana
out of combat.
- Free Slot Count counts slots once per `BAG_UPDATE_DELAYED`. Disable it if a
bag addon already shows the count.
Nothing here has been measured in game yet. To compare before and after, see
[Measuring performance](CLASSICAPI_UPDATES.md#measuring-performance).
+187
View File
@@ -0,0 +1,187 @@
# 🧩 ShaguTweaks — ClassicAPI
A lightweight fork of [ShaguTweaks](https://github.com/shagu/ShaguTweaks) for **Turtle WoW-like servers**.
Focused on **stability, compatibility and performance** while keeping the original ShaguTweaks experience.
> This fork requires ClassicAPI and targets **Turtle WoW-like server/client environments**.
## 🔌 Requirements
- **[ClassicAPI](https://github.com/brues-code/ClassicAPI) — required**
- **[SuperWoW](https://github.com/balakethelock/SuperWoW) — optional / recommended**
ClassicAPI is the main API layer. SuperWoW is only used as an additional fallback for some cast/GUID information.
## 📦 Installation
1. Install **ClassicAPI**.
2. Optionally install **SuperWoW**.
3. Rename the addon folder to `ShaguTweaks`.
4. Copy it to `World of Warcraft\Interface\AddOns\ShaguTweaks`.
5. Restart the game.
Settings: **Esc → Advanced Options**.
## ✨ Main changes
- ClassicAPI-backed integration for casts, auras, items and unit data.
- Reduced per-frame work, tooltip scans and repeated UI updates.
- Event-driven target and nameplate castbars where possible.
- Safer GUID-based handling for same-name enemies.
- Better Turtle WoW-like server compatibility for custom items, spells and UI changes.
- Safer hooks and fewer destructive global overrides.
- Expanded Item Rarity Borders support for merchant, quest, mail, trade, crafting and loot frames.
- Added independent Item Rarity Glows using the shared item rarity engine.
- Expanded Movable Unit Frames to handle Player, Target, Party, Minimap, Buffs, Debuffs and Weapon Buffs through one shared Ctrl+Shift mover and alignment grid.
- Restored Real Health Numbers as an independent module, with direct values when available and a lazy Vanilla health estimator fallback for percentage-only targets.
- Unit Frame Big Health is presentation-only again and no longer forces numeric health/power text.
- Various stability fixes across legacy ShaguTweaks modules.
## 🔷 Mods using ClassicAPI directly
- Actionbar Range Color
- AoE Loot
- Auto Dismount
- Chat Tweaks
- Equip Compare
- Free Slots
- Improved Castbar
- Item Rarity Borders
- Item Rarity Glows
- Movable Unit Frames
- Real Health Numbers
- Nameplate Class Colors
- Nameplate Castbar
- No Toggle
- Sell Junk
- Skip Gossip Text
- Enemy Target Castbar
- Tooltip Details
- WorldMap Window
Other modules can also benefit indirectly from ClassicAPI through shared ShaguTweaks libraries and caches.
## ⚙️ Modules
### Action Bar
- Auto Dismount
- Auto Stance
- Actionbar Range Color
- Cooldown Numbers
- Hide Gryphons
- Improved Castbar
- No Toggle Auto-Attack
- Reduced Actionbar
### Unit Frames
- Big Health
- Class Colors
- Class Portraits
- Energy & Mana Tick
- Health Colors
- Movable Unit Frames
- Real Health Numbers
- Enemy Target Castbar
- Target Debuff Timer
### Nameplates
- Nameplate Scale
- Nameplate Class Colors
- Nameplate Castbar
### Tooltip & Items
- Equip Compare
- Free Slots
- Item Rarity Borders
- Item Rarity Glows
- Sell Junk
- Tooltip Details
- Vendor Values
### Chat & Social
- Blue Shaman Class Colors
- Chat Tweaks
- Chat Levels
- Chat Spam Filter
- Social Colors
### Minimap & World Map
- Clean Minimap
- Minimap Clock
- Minimap Square
- Minimap Tweaks
- Minimap Zoom
- Hide Tracking Icon
- WorldMap Class Colors
- WorldMap Coordinates
- WorldMap Window
### Loot
- AoE Loot
- Improved Roll Frames
- Loot Cursor
### Interface
- Darkened UI
- Hide Errors
- Smaller Error Frame
- XP Bar Text
### General
- Super WoW Compatibility
- Turtle WoW Compatibility
- Skip Gossip Text
## 🔧 Compatibility
- ClassicAPI integration
- Turtle WoW-like server integration
- SuperWoW integration
## 🧹 Removed / integrated modules
The following original ShaguTweaks modules are not included as standalone modules because their functionality is either integrated elsewhere or no longer required:
- **Chat Links** → integrated into **Chat Tweaks**
- **Movable Unit Frames Extended** → integrated into **Movable Unit Frames**
## 📜 Project identity & licensing
This is an independent community project. Compatibility with **World of Warcraft**, **Turtle WoW-like environments**, **ClassicAPI**, **SuperWoW**, or other referenced projects does not imply affiliation, endorsement, sponsorship, or ownership by their respective rights holders or maintainers.
**World of Warcraft** and **Blizzard Entertainment** names, marks, and game assets remain the property of their respective rights holders.
For detailed provenance and licensing information, see:
- [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)
- [PROJECT_IDENTITY.md](PROJECT_IDENTITY.md)
- [Docs/ASSET_PROVENANCE.md](Docs/ASSET_PROVENANCE.md)
- [LICENSES/](LICENSES/)
## 🙏 Credits
Original addon and modules by **Shagu**.
Additional maintenance and Turtle WoW work by **paokkerkir**.
ClassicAPI compatibility fork maintained by **Dusk-92**.
Special thanks to **Sandrea** for providing **AoELoot**, adapted here as the **AoE Loot** module.
Special thanks to **Jesper_92** for reporting issues that helped improve the addon.
Additional code and ideas from **pfUI** and **zUI**.
Extended Movable Unit Frames support adapted from **TokensWorth/ShaguTweaks-mods**, originally released under MIT by **GryllsAddons**. See `THIRD_PARTY_NOTICES.md`.
Released under the original **MIT License**.
+2
View File
@@ -11,6 +11,8 @@ 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`.
- `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
ClassicAPI repository. ClassicAPI is not bundled here.
- `SuperWoW-LICENSE.txt` — verbatim upstream license from the external
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2021 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.
+97 -56
View File
@@ -1,17 +1,19 @@
# 🧩 ShaguTweaks — ClassicAPI
A lightweight fork of [ShaguTweaks](https://github.com/shagu/ShaguTweaks) for **Turtle WoW-like servers**.
A lightweight fork of [ShaguTweaks](https://github.com/shagu/ShaguTweaks) for **Turtle WoW-like servers**, focused on **stability, compatibility and performance** while keeping the original ShaguTweaks experience.
Focused on **stability, compatibility and performance** while keeping the original ShaguTweaks experience.
This is an English-only customization of [Dusk/ShaguTweaks-ClassicAPI](https://octowow.st/git/Dusk/ShaguTweaks-ClassicAPI), based on ShaguTweaks, for the Vanilla client environment with ClassicAPI.
> 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).
## 🔌 Requirements
- **[ClassicAPI](https://github.com/brues-code/ClassicAPI) — required**
- **[SuperWoW](https://github.com/balakethelock/SuperWoW) — optional / recommended**
- **[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.
ClassicAPI is the main API layer. SuperWoW is only used as an additional fallback for some cast/GUID information.
ClassicAPI is the main API layer.
## 📦 Installation
@@ -23,69 +25,107 @@ ClassicAPI is the main API layer. SuperWoW is only used as an additional fallbac
Settings: **Esc → Advanced Options**.
## ✨ Main changes
## ✨ What's different in this fork
**Unit frames**
- **Modern Class Colors:** optional palette for all nine Vanilla classes, including blue shamans. Listed directly below **Unit Frame Class Colors**.
- **Modern textures:** class-tinted health artwork and precolored rage, energy and focus artwork. Mana uses the light-blue rectangular retail fill on every supported frame, including the druid's separate mana bar in bear/cat forms.
- **Real Health Numbers:** an independent module. Percentage on the left and current health on the right, inset 4 pixels; power numbers remain centered. It uses direct values when available and a lazy Vanilla health estimator for percentage-only targets, so unknown absolute target health stays percentage-only.
- **Number styling:** health, percentage and power numbers copy the corresponding character name's font, size and shadow. Gold is the default; **White Unit Frame Numbers** under Unit Frames switches the numbers to white independently of Modern Class Colors.
- **Unit Frame Big Health** is presentation-only and no longer forces numeric health/power text. With it, the resting pulse is limited to the portrait, and the full status artwork is restored outside resting or in combat.
- **Movable Unit Frames:** Player, Target, Party, Minimap, Buffs, Debuffs and Weapon Buffs share one mover and alignment grid. Hold **Ctrl+Shift+Alt** to drag; right-click while unlocked to reset.
- **Hide Pet Unit Frame:** hides the pet frame below the player frame for pet classes. The stock pet code keeps running, so nothing breaks when the pet is summoned, dismissed or dies.
**Swing Timer and Wand Timer**
- **Swing Timer:** main-hand and off-hand swing timers styled like the classic cast bar, with the weapon's icon on the left. They sit just above the cast bar; hold **Ctrl+Shift+Alt** to drag them and right-click to reset.
- **Wand Timer:** the time until your next wand shot in the same style. It also times Auto Shot for bows, guns and crossbows, turning red during the last half second, when moving would cancel the shot.
- The timers follow the server's real attack timers through ClassicAPI's `PLAYER_SWING` event, including parry haste and on-next-swing abilities. A bar turns gray while the target is out of reach.
- The main-hand bar shows a queued **Heroic Strike** in light blue and **Cleave** in green (warriors), and **Maul** in orange (druids).
- Ported from [brues-code/pfUI](https://github.com/brues-code/pfUI)'s swing timer. Requires ClassicAPI v1.15.10 or newer.
**Tooltips, items and action bars**
- **Tooltip Details:** restored guild ranks and leader marker; optional modern class colors and health texture. NPC interaction and tooltip-clear handling prevent cached colors leaking between tooltips.
- **Item Rarity Borders** cover merchant, quest, mail, trade, crafting and loot frames, and **Item Rarity Glows** add an independent glow using the same shared rarity engine.
- **Range Color:** self/rangeless spells no longer use a misleading legacy fallback; invalid friendly/hostile target checks and stale red/no-mana tint are handled. No additional AoE-specific exceptions have been added. The range check runs five times a second instead of every frame.
- **XP Bar Text:** hovering the experience bar shows current and needed XP like the reputation bar (`1555/3000`), then the percentage and the rested bonus.
- **WorldMap Class Colors:** raid members on the world map and battlefield minimap show their own class color.
- **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.
**Performance and stability**
- ClassicAPI-backed integration for casts, auras, items and unit data.
- Reduced per-frame work, tooltip scans and repeated UI updates.
- **Bounded player cache:** the shared player cache expires old entries and holds at most 10,000 players, so the saved variables file no longer grows without limit. See [Docs/PERFORMANCE.md](Docs/PERFORMANCE.md).
- Event-driven target and nameplate castbars where possible.
- Safer GUID-based handling for same-name enemies.
- Better Turtle WoW-like server compatibility for custom items, spells and UI changes.
- Safer hooks and fewer destructive global overrides.
- Expanded Item Rarity Borders support for merchant, quest, mail, trade, crafting and loot frames.
- Added independent Item Rarity Glows using the shared item rarity engine.
- Expanded Movable Unit Frames to handle Player, Target, Party, Minimap, Buffs, Debuffs and Weapon Buffs through one shared Ctrl+Shift mover and alignment grid.
- Restored Real Health Numbers as an independent module, with direct values when available and a lazy Vanilla health estimator fallback for percentage-only targets.
- Unit Frame Big Health is presentation-only again and no longer forces numeric health/power text.
- Various stability fixes across legacy ShaguTweaks modules.
- **English-only loading:** only English data and translations are included, avoiding unused locale allocations.
- Better Turtle WoW-like server compatibility for custom items, spells and UI changes, plus various stability fixes across legacy ShaguTweaks modules.
## 🔷 Mods using ClassicAPI directly
## 🔷 ClassicAPI integration
- Actionbar Range Color
ClassicAPI backs casts, auras, items and unit data: native cooldown support, filtered unit events, corrected chat identity/payload handling, and reduced duplicate player-cache records when native persistence is already enabled. Older fallbacks are retained for clients without the newer features.
Modules that use ClassicAPI directly:
- Range Color
- AoE Loot
- Auto Dismount
- Chat Tweaks
- Equip Compare
- Free Slots
- Free Slot Count
- Improved Castbar
- Item Rarity Borders
- Item Rarity Glows
- Loot History
- Movable Unit Frames
- Real Health Numbers
- Nameplate Class Colors
- Nameplate Castbar
- No Toggle
- No Toggle Auto-Attack
- Sell Junk
- Skip Gossip Text
- Enemy Target Castbar
- Swing Timer
- Wand Timer
- Enemy Castbars
- Tooltip Details
- WorldMap Window
Other modules can also benefit indirectly from ClassicAPI through shared ShaguTweaks libraries and caches.
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).
## ⚙️ Modules
Names match the in-game options list. Modules marked *(off)* are disabled until you enable them.
### Action Bar
- Auto Dismount
- Auto Stance
- Actionbar Range Color
- Range Color *(off)*
- Cooldown Numbers
- Hide Gryphons
- Improved Castbar
- Hide Gryphons *(off)*
- Improved Castbar *(off)*
- Swing Timer *(off)*
- Wand Timer *(off)*
- No Toggle Auto-Attack
- Reduced Actionbar
- Reduced Actionbar Size *(off)*
### Unit Frames
- Big Health
- Class Colors
- Class Portraits
- Energy & Mana Tick
- Health Colors
- Unit Frame Big Health *(off)*
- Unit Frame Class Colors *(off)*
- Modern Class Colors *(off)*
- Unit Frame Class Portraits *(off)*
- Unit Frame Energy & Mana Tick *(off)*
- Hide Pet Unit Frame *(off)*
- Movable Unit Frames
- Real Health Numbers
- Enemy Target Castbar
- Target Debuff Timer
- Real Health Numbers *(off)*
- White Unit Frame Numbers *(off)*
- Enemy Castbars
- Debuff Timer
### Nameplates
@@ -96,28 +136,28 @@ Other modules can also benefit indirectly from ClassicAPI through shared ShaguTw
### Tooltip & Items
- Equip Compare
- Free Slots
- Free Slot Count
- Item Rarity Borders
- Item Rarity Glows
- Item Rarity Glows *(off)*
- Sell Junk
- Tooltip Details
- Vendor Values
### Chat & Social
- Blue Shaman Class Colors
- Blue Shaman Class Colors *(off)*
- Chat Tweaks
- Chat Levels
- Chat Spam Filter
- Chat Levels *(off)*
- Chat Spam Filter *(off)*
- Social Colors
### Minimap & World Map
- Clean Minimap
- Minimap Clock
- Minimap Square
- Minimap Tweaks
- Minimap Zoom
- Minimap Clean *(off)*
- MiniMap Clock
- MiniMap Square *(off)*
- MiniMap Tweaks
- Enlarged Minimap *(off)*
- Hide Tracking Icon
- WorldMap Class Colors
- WorldMap Coordinates
@@ -125,28 +165,23 @@ Other modules can also benefit indirectly from ClassicAPI through shared ShaguTw
### Loot
- AoE Loot
- Improved Roll Frames
- AoE Loot *(off)*
- Improved Roll Frames *(off)*
- Loot Cursor
- Loot History (`/loothistory`)
### Interface
- Darkened UI
- Hide Errors
- Smaller Error Frame
- XP Bar Text
- Darkened UI *(off)*
- Hide Errors *(off)*
- Smaller Error Frame *(off)*
- XP Bar Text (current/needed XP, percentage and rested bonus on hover)
### General
- Super WoW Compatibility
- Turtle WoW Compatibility
- Skip Gossip Text
## 🔧 Compatibility
- ClassicAPI integration
- Turtle WoW-like server integration
- SuperWoW integration
- Skip Gossip Text *(off)*
## 🧹 Removed / integrated modules
@@ -163,10 +198,14 @@ This is an independent community project. Compatibility with **World of Warcraft
For detailed provenance and licensing information, see:
- [CHANGELOG.md](CHANGELOG.md)
- [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)
- [PROJECT_IDENTITY.md](PROJECT_IDENTITY.md)
- [Docs/ASSET_PROVENANCE.md](Docs/ASSET_PROVENANCE.md)
- [LICENSES/](LICENSES/)
- [Docs/MODERN_MANA.md](Docs/MODERN_MANA.md)
- [Docs/PERFORMANCE.md](Docs/PERFORMANCE.md) — player cache limits and other performance notes
- [Docs/UPSTREAM_README.md](Docs/UPSTREAM_README.md) — the Dusk fork's README, kept for reference
- [LICENSES/](LICENSES)
## 🙏 Credits
@@ -182,6 +221,8 @@ Special thanks to **Jesper_92** for reporting issues that helped improve the add
Additional code and ideas from **pfUI** and **zUI**.
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`.
Released under the original **MIT License**.
+4 -16
View File
@@ -2,30 +2,16 @@
## Title: |cffffcc00Shagu|cffffffffTweaks |cffcccccc[ClassicAPI]
## Author: Shagu
## Notes: A collection of tweaks and enhancements to the Default UI
## Notes-ruRU: Коллекция настроек и улучшений стандартного пользовательского интерфейса.
## Version: GIT
## Version: 3.6
## Dependencies: !!!ClassicAPI
## SavedVariables: ShaguTweaks_player_cache, ShaguTweaks_social_cache
## SavedVariablesPerCharacter: ShaguTweaks_config, ShaguTweaks_cache
# locales
locales\locales_deDE.lua
locales\locales_enUS.lua
locales\locales_esES.lua
locales\locales_frFR.lua
locales\locales_koKR.lua
locales\locales_ruRU.lua
locales\locales_zhCN.lua
# translations
locales\translations_deDE.lua
locales\translations_enUS.lua
locales\translations_esES.lua
locales\translations_frFR.lua
locales\translations_koKR.lua
locales\translations_ruRU.lua
locales\translations_zhCN.lua
locales\translations_zhTW.lua
# basic
main.lua
@@ -72,7 +58,6 @@ mods\target-castbar.lua
mods\target-debufftimer.lua
mods\social-colors.lua
mods\item-colors.lua
mods\health-color.lua
mods\auto-dismount.lua
mods\auto-stance.lua
mods\tooltip-details.lua
@@ -86,6 +71,7 @@ mods\unitframes-bighealth.lua
mods\health-numbers.lua
mods\unitframes-classportrait.lua
mods\unitframes-energy-tick.lua
mods\unitframes-hide-pet.lua
mods\cooldown-numbers.lua
mods\turtle-wow.lua
mods\superwow.lua
@@ -93,8 +79,10 @@ mods\reduced-actionbar.lua
mods\no-toggle.lua
mods\aoe-loot.lua
mods\loot-cursor.lua
mods\loot-history.lua
mods\improved-roll-frames.lua
mods\improved-castbar.lua
mods\swing-timer.lua
mods\smaller-errors-frame.lua
mods\skip-gossip.lua
+48
View File
@@ -36,6 +36,20 @@ from `TokensWorth/ShaguTweaks-mods`:
The preserved license text is stored in
`LICENSES/ShaguTweaks-mods-MIT.txt`.
## brues-code/pfUI
The Swing Timer and Wand Timer in `mods/swing-timer.lua` are ported from
pfUI's swing timer module and restyled, and the Loot History window in
`mods/loot-history.lua` is ported from pfUI's loot history module and rebuilt
with stock 1.12 widgets:
- Upstream modules: `modules/swingtimer.lua`, `modules/loothistory.lua`
- Source: https://github.com/brues-code/pfUI
- Copyright (c) 2016-2021 Eric Mauser (Shagu) and contributors
- License: MIT
The preserved license text is stored in `LICENSES/pfUI-MIT.txt`.
## ClassicAPI
ClassicAPI is a required external runtime dependency used by
@@ -127,3 +141,37 @@ documentation summarizes them.
When replacing or substantially rewriting third-party-derived material, update
the provenance record rather than erasing the earlier history.
## OEVG2 customization and BetterBlizzFrames texture
This repository imports a source snapshot of Dusk/ShaguTweaks-ClassicAPI downloaded on 2026-10-24 and adds English-only loading and unit-frame customizations described in README.md.
`img/BlizzardRetailBar.tga` was obtained from:
https://github.com/Bodify/BetterBlizzFrames/blob/main/media/blizzTex/BlizzardRetailBar.tga
BetterBlizzFrames' repository license states **All Rights Reserved**, copyright (c) 2023-2026 Bodify. This asset is not covered by this repository's original ShaguTweaks MIT license. The source project's notice does not establish separate provenance or permissions for any underlying game artwork.
## Modern mana bar texture
`img/BlizzardRetailBarCrop2.tga` is copied unchanged from BetterBlizzFrames:
https://github.com/Bodify/BetterBlizzFrames/blob/main/media/blizzTex/BlizzardRetailBarCrop2.tga
Source Git blob: `bb412b3fa538a6c91671d06ccbfd1e820f4c2876`.
BetterBlizzFrames: All Rights Reserved, Copyright (c) 2023-2026 Bodify.
This asset retains its original rights and is not relicensed under MIT.
The mana tint follows BetterBlizzFrames' default `PowerBarColor.MANA` lookup:
RGB `(0, 0, 1)`. The selected texture is its `Blizzard Retail Bar Crop 2` preset.
## Native retail resource artwork
`img/ModernPowerMana.tga`, `img/ModernPowerRage.tga`, `img/ModernPowerFocus.tga` and `img/ModernPowerEnergy.tga` are cropped/resized exports of Blizzard's `interface/hud/uiunitframe2x.blp` (FileDataID 4642466), client build 12.1.5.70448, retrieved from https://wago.tools/api/casc/4642466?version=12.1.5.70448 .
Matching atlas metadata and conversion details are recorded in `Docs/MODERN_MANA.md`. These are Blizzard game assets, not original artwork or assets relicensed under the software's MIT license. The previous BetterBlizzFrames grayscale asset attribution remains part of the history.
### Frame-specific resource exports
Additional img/ModernPower*Class.tga, *Target.tga, *Small.tga and *Party.tga variants use the matching Blizzard atlas crops documented in Docs/MODERN_MANA.md. Player/target/small variants come from FileDataID 4642466; party variants come from FileDataID 4681512, interface/hud/uipartyframe.blp. Both were retrieved for build 12.1.5.70448 from wago.tools. All resource exports now use bilinear resizing to compact power-of-two TGA dimensions. These remain Blizzard game artwork; no MIT relicensing is asserted.
+31
View File
@@ -824,3 +824,34 @@ API.UnitInRange = function(unit)
return false, false
end
-- Native cooldown rendering landed in ClassicAPI v1.17.1.
API.nativecooldowns = API.classicapi_version >= 11701
API.RegisterUnitEvent = function(frame, eventName, unit1, unit2, unit3)
if type(frame.RegisterUnitEvent) == "function" then
return frame:RegisterUnitEvent(eventName, unit1, unit2, unit3)
end
return frame:RegisterEvent(eventName)
end
API.GetCurrentChatEvent = function()
if type(_G.GetCurrentChatEvent) == "function" then
return _G.GetCurrentChatEvent()
end
end
-- Never opt into persistent storage on the user's behalf.
API.HasPersistentPlayerCache = function()
return API.playercache and type(_G.C_PlayerCache.IsEnabled) == "function"
and type(_G.C_PlayerCache.RememberPlayer) == "function"
and _G.C_PlayerCache.IsEnabled()
end
API.RememberPlayerInfo = function(name, class, guid)
if API.HasPersistentPlayerCache() and name and class and guid then
return _G.C_PlayerCache.RememberPlayer(guid, name, class) and true or false
end
return false
end
-1
View File
@@ -105,7 +105,6 @@ Block("Real Health Numbers")
Block("Unit Frame Big Health")
Block("Reduced Actionbar Size")
Block("Unit Frame Class Colors")
Block("Unit Frame Health Colors")
Block("Unit Frame Class Portraits")
Block("Enemy Castbars")
+13 -1
View File
@@ -234,7 +234,19 @@ settings.load = function(self)
settings.category[category].text:SetAllPoints()
settings.category[category].text:SetText(category)
for title, module in ShaguTweaks.spairs(entries) do
local orderedTitles = {}
for title in ShaguTweaks.spairs(entries) do
if title ~= "Modern Class Colors" then table.insert(orderedTitles, title) end
end
if entries["Modern Class Colors"] then
local position = table.getn(orderedTitles) + 1
for i, title in ipairs(orderedTitles) do
if title == T["Unit Frame Class Colors"] then position = math.min(i + 2, position); break end
end
table.insert(orderedTitles, position, "Modern Class Colors")
end
for _, title in ipairs(orderedTitles) do
local module = entries[title]
if not settings.entries[title] then
settings.entries[title] = CreateFrame("CheckButton", "AdvancedSettingsGUI" .. title, settings.category[category], "OptionsCheckButtonTemplate")
settings.entries[title]:SetHeight(24)
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+4 -29
View File
@@ -39,8 +39,8 @@ local function StopSampling()
end
local function StartSampling()
libhealth:RegisterEvent("UNIT_COMBAT")
libhealth:RegisterEvent("UNIT_HEALTH")
ShaguTweaks.API.RegisterUnitEvent(libhealth, "UNIT_COMBAT", "target")
ShaguTweaks.API.RegisterUnitEvent(libhealth, "UNIT_HEALTH", "target")
end
local function SelectTarget()
@@ -76,27 +76,8 @@ local function GetCachedHealth(unitstr, cur, max)
return cur, max
end
-- ClassicAPI can expose the real health deficit even when stock UnitHealth()
-- still reports a percentage. Use that as an immediate estimate when possible.
-- At 100% there is no deficit to derive a maximum from, so the historical
-- combat estimator/cache remains useful.
local function GetMissingHealthEstimate(unitstr, cur, max)
if max ~= 100 or cur >= 100 then return end
if type(_G.UnitHealthMissing) ~= "function" then return end
local missing = _G.UnitHealthMissing(unitstr)
local lostPercent = 100 - cur
if not missing or missing <= 0 or lostPercent <= 0 then return end
local estimatedMax = ceil(missing / lostPercent * 100)
if estimatedMax < 100 then return end
local estimatedCur = estimatedMax - missing
if estimatedCur < 0 then estimatedCur = 0 end
return estimatedCur, estimatedMax, true
end
-- UnitHealthMissing follows the same units as UnitHealth/UnitHealthMax.
-- A percentage deficit cannot establish absolute HP; use the combat cache.
function libhealth:GetUnitHealth(unitstr)
-- Preserve the old public library behavior for external consumers while
-- keeping the estimator lazy: the first actual query activates it.
@@ -112,12 +93,6 @@ function libhealth:GetUnitHealth(unitstr)
return cur, max, true
end
local missingCur, missingMax, missingKnown =
GetMissingHealthEstimate(unitstr, cur, max)
if missingKnown then
return missingCur, missingMax, true
end
return GetCachedHealth(unitstr, cur, max)
end
+216 -70
View File
@@ -5,7 +5,15 @@ local API = ShaguTweaks.API
local units = { players = {}, mobs = {} }
local queue = { }
local PASSIVE_CACHE_TTL = 90 * 24 * 60 * 60
-- Player cache limits. ShaguTweaks_player_cache is parsed at every login and
-- written at every logout, so its size is a direct load-time and memory cost.
local DAY = 24 * 60 * 60
local PASSIVE_CACHE_TTL = 90 * DAY -- players whose class was never learned
local KNOWN_CACHE_TTL = 180 * DAY -- every other player, counted from last sighting
local MAX_CACHED_PLAYERS = 10000 -- oldest sightings go first above this
local CHAT_REFRESH = 60 * 60 -- re-resolve a known chat speaker at most hourly
local GUILD_SCAN_INTERVAL = 60 -- seconds between full guild roster scans
local libunitscan = CreateFrame("Frame", "ShaguTweaksUnitScan", UIParent)
@@ -289,21 +297,46 @@ end
ShaguTweaks.GetPlayerCache = EnsurePlayerCache
local function EnrichPlayerFromClassicAPI(name)
if not name or not API or not API.GetCachedPlayerInfoByName then return end
-- Same player name, ignoring case. Compare first so the common exact match
-- does not create two lowercase strings.
local function SameName(a, b)
return a == b or string.lower(a) == string.lower(b)
end
local _, class, _, _, _, _, _, guid = API.GetCachedPlayerInfoByName(name)
class = class and class ~= "" and class or nil
guid = guid and guid ~= "" and guid or nil
if not class and not guid then return end
-- New player records carry a timestamp from the start, so the login cleanup
-- can age every entry, not only chat-only ones.
local function GetOrCreatePlayer(name)
local data = units.players[name]
if not data then
data = { lastseen_ts = time() }
units.players[name] = data
end
return data
end
-- Keep ShaguTweaks keyed by the exact name it already knows. This avoids
-- duplicate SavedVariable entries if another cache normalizes name casing.
units.players[name] = units.players[name] or {}
units.players[name].class = class or units.players[name].class
units.players[name].guid = guid or units.players[name].guid
-- Store a class. A GUID is kept only while the class is unknown: it is then
-- the one way to learn it later through GetPlayerInfoByGUID. Known players
-- need no GUID in SavedVariables, and nothing else reads it.
local function SetPlayerClass(data, class, guid)
if class then data.class = class end
if data.class then
data.guid = nil
elseif guid then
data.guid = guid
end
end
return name
-- A class-less entry that kept its GUID: ask the engine's name cache.
local function EnrichPlayerFromStoredGUID(name, data)
if not data or data.class or not data.guid then return end
if not API or not API.GetPlayerInfoByGUID then return end
local _, class, _, _, _, apiName = API.GetPlayerInfoByGUID(data.guid)
if not class or class == "" or not apiName or apiName == "" then return end
if not SameName(apiName, name) then return end
SetPlayerClass(data, class)
return true
end
local function EnrichPlayerFromCurrentChat(name)
@@ -316,14 +349,12 @@ local function EnrichPlayerFromCurrentChat(name)
local guid = API.GetCurrentChatGUID()
if not guid or guid == "" then return end
local apiName, class = API.GetPlayerInfoByGUID(guid)
local _, class, _, _, _, apiName = API.GetPlayerInfoByGUID(guid)
class = class and class ~= "" and class or nil
if not apiName or apiName == "" or not class then return end
if string.lower(apiName) ~= string.lower(name) then return end
if not SameName(apiName, name) then return end
units.players[name] = units.players[name] or {}
units.players[name].class = class
units.players[name].guid = guid
SetPlayerClass(GetOrCreatePlayer(name), class, guid)
return name
end
@@ -333,21 +364,41 @@ function ShaguTweaks.GetUnitData(name, active)
EnsurePlayerCache()
if not units["players"][name] then
EnrichPlayerFromClassicAPI(name)
if not units["players"][name] then
EnrichPlayerFromCurrentChat(name)
local data = units.players[name]
if (not data or not data.class) and not EnrichPlayerFromStoredGUID(name, data) then
-- ClassicAPI's player cache by name. Query it once: the persistent-storage
-- check below and the plain enrichment both use the same result.
local class, guid, resolvedName, _
if API and API.GetCachedPlayerInfoByName then
_, class, _, _, _, resolvedName, _, guid = API.GetCachedPlayerInfoByName(name)
class = class and class ~= "" and class or nil
guid = guid and guid ~= "" and guid or nil
end
elseif not units["players"][name].class then
EnrichPlayerFromClassicAPI(name)
if not units["players"][name].class then
-- Persistent ClassicAPI data can answer class-only queries without making
-- another SavedVariables record. Keep our level and social history fields.
if class and resolvedName and SameName(resolvedName, name)
and API.HasPersistentPlayerCache and API.HasPersistentPlayerCache()
and API.RememberPlayerInfo(resolvedName, class, guid) then
return class, data and data.level, data and data.elite, true
end
-- Keep ShaguTweaks keyed by the exact name it already knows. This avoids
-- duplicate SavedVariable entries if another cache normalizes name casing.
if class or guid then
data = GetOrCreatePlayer(name)
SetPlayerClass(data, class, guid)
end
if not data or not data.class then
EnrichPlayerFromCurrentChat(name)
data = units.players[name]
end
end
if units["players"][name] then
local ret = units["players"][name]
return ret.class, ret.level, ret.elite, true
if data then
return data.class, data.level, data.elite, true
end
if units["mobs"][name] then
@@ -359,13 +410,28 @@ function ShaguTweaks.GetUnitData(name, active)
end
end
local function AddData(db, name, class, level, elite, guid)
local function AddData(db, name, class, level, elite, guid, now)
if not name then return end
units[db][name] = units[db][name] or {}
units[db][name].class = class or units[db][name].class
units[db][name].level = level or units[db][name].level
units[db][name].elite = elite or units[db][name].elite
units[db][name].guid = guid or units[db][name].guid
local data = units[db][name]
if not data then
data = {}
units[db][name] = data
end
if level then data.level = level end
if elite then data.elite = elite end
if db == "players" then
SetPlayerClass(data, class, guid)
-- Every sighting refreshes the cache age. "lastseen" stays the chat and
-- friend-list date that Social Colors shows; this is only for expiry.
data.lastseen_ts = now or time()
else
if class then data.class = class end
if guid then data.guid = guid end
end
queue[name] = nil
end
@@ -374,6 +440,17 @@ local function RememberPlayer(name)
EnsurePlayerCache()
local now = time()
local data = units.players[name]
-- Busy channels repeat the same speakers. A speaker whose class is known
-- and who was already seen in chat within the hour needs no GUID lookup
-- and no write.
if data and data.class and type(data.lastseen) == "number"
and now - data.lastseen < CHAT_REFRESH then
return
end
-- ClassicAPI exposes the GUID of the CHAT_MSG_* sender synchronously.
-- Resolve class from the engine's existing name cache only: this is a pure
-- cache read and never sends a /who or any other network query.
@@ -386,26 +463,70 @@ local function RememberPlayer(name)
class = apiClass and apiClass ~= "" and apiClass or nil
end
units.players[name] = units.players[name] or {}
units.players[name].class = class or units.players[name].class
units.players[name].guid = guid or units.players[name].guid
local now = time()
units.players[name].lastseen = now
units.players[name].lastseen_ts = now
-- Skip only NEW chat-only records after native persistent storage succeeds.
-- Existing records may contain levels/history and must retain timestamps.
if not data and API and API.RememberPlayerInfo
and API.RememberPlayerInfo(name, class, guid) then return end
data = GetOrCreatePlayer(name)
SetPlayerClass(data, class, guid)
data.lastseen = now
data.lastseen_ts = now
end
local function PrunePassivePlayers()
-- One pass per session, at the first PLAYER_ENTERING_WORLD.
local function PrunePlayers(realmdb)
local players = units.players
local now = time()
for name, data in pairs(units.players) do
-- Normalize any empty legacy/API class token back to an unknown value.
if data and data.class == "" then data.class = nil end
-- Entries written before every sighting carried a timestamp have no age.
-- Date them from the cache's grace start, as the old Social Colors cleanup
-- did, so they expire on schedule unless the player is seen again.
local graceStart = tonumber(realmdb.players_cleanup)
if not graceStart then
graceStart = now
realmdb.players_cleanup = now
end
-- Keep every player whose class was learned. Only anonymous/passive chat
-- entries expire, and only when they carry our numeric last-seen timestamp.
if data and not data.class and data.lastseen_ts and
now - data.lastseen_ts > PASSIVE_CACHE_TTL then
units.players[name] = nil
local count = 0
for name, data in pairs(players) do
if type(data) ~= "table" then
players[name] = nil
else
-- Normalize any empty legacy/API class token back to an unknown value.
if data.class == "" then data.class = nil end
if data.class then data.guid = nil end
local seen = GetPlayerTimestamp(data)
if not seen then
seen = graceStart
data.lastseen_ts = graceStart
end
local ttl = data.class and KNOWN_CACHE_TTL or PASSIVE_CACHE_TTL
if now - seen > ttl then
players[name] = nil
else
count = count + 1
end
end
end
-- Hard cap: drop the oldest sightings. Guild members and friends are
-- refreshed by their roster events every session, so they stay.
if count > MAX_CACHED_PLAYERS then
local stamps, n = {}, 0
for _, data in pairs(players) do
n = n + 1
stamps[n] = GetPlayerTimestamp(data) or 0
end
table.sort(stamps)
local cutoff = stamps[n - MAX_CACHED_PLAYERS]
for name, data in pairs(players) do
if (GetPlayerTimestamp(data) or 0) <= cutoff then
players[name] = nil
end
end
end
end
@@ -428,6 +549,15 @@ local passiveChatEvents = {
}
local useNameplateUnitEvents = API and API.nameplateevents
local prunedThisSession = false
local lastGuildScan, lastGuildCount
-- A GUID is only stored for players whose class is unknown, so skip the call
-- when UnitClass already answered.
local function GUIDIfClassless(unit, class)
if class or not API or not API.UnitGUID then return end
return API.UnitGUID(unit)
end
libunitscan:RegisterEvent("PLAYER_ENTERING_WORLD")
libunitscan:RegisterEvent("PLAYER_LOGOUT")
@@ -446,6 +576,9 @@ for chatEvent in pairs(passiveChatEvents) do
end
libunitscan:SetScript("OnEvent", function()
local event = event
local chatEvent, _, chatSender = API.GetCurrentChatEvent()
if chatEvent then event = chatEvent end
if event == "PLAYER_LOGOUT" then
-- SavedVariables are written after this event. Re-run the migration here so
-- even a legacy addon path that recreated ShaguTweaks_cache.players during
@@ -458,18 +591,21 @@ libunitscan:SetScript("OnEvent", function()
-- Ensure the saved database is already bound even if another module queried
-- it earlier while restoring UI state. From here onward SavedVariables are
-- settled, so future cache reads can skip migration checks completely.
EnsurePlayerCache()
local _, realmdb = EnsurePlayerCache()
cacheMigrationFinalized = true
-- One cheap cleanup pass at login. Known classes are kept forever.
PrunePassivePlayers()
-- PLAYER_ENTERING_WORLD also fires at every loading screen. Expire and
-- cap the cache once per session only.
if not prunedThisSession then
prunedThisSession = true
PrunePlayers(realmdb)
end
-- update own character details
local name = UnitName("player")
local _, class = UnitClass("player")
local level = UnitLevel("player")
local guid = API and API.UnitGUID and API.UnitGUID("player")
AddData("players", name, class, level, nil, guid)
AddData("players", name, class, level, nil, GUIDIfClassless("player", class))
elseif event == "NAME_PLATE_UNIT_ADDED" then
-- ClassicAPI exposes an exact nameplateN unit token here. Cache players
@@ -481,59 +617,70 @@ libunitscan:SetScript("OnEvent", function()
local name = UnitName(unit)
local _, class = UnitClass(unit)
local level = UnitLevel(unit)
local guid = API and API.UnitGUID and API.UnitGUID(unit)
AddData("players", name, class, level, nil, guid)
AddData("players", name, class, level, nil, GUIDIfClassless(unit, class))
end
elseif passiveChatEvents[event] then
-- Passive cache only: remember speakers already delivered by the client.
-- No /who, no TargetByName queue, no timer and no additional server query.
RememberPlayer(arg2)
RememberPlayer(chatSender or arg2)
elseif event == "FRIENDLIST_UPDATE" then
local name, class, level
local now = time()
for i = 1, GetNumFriends() do
name, level, class = GetFriendInfo(i)
class = L["class"][class] or nil
-- friendlist updates due to friend going off-line return level 0, let's not overwrite good older values
level = level > 0 and level or nil
AddData("players", name, class, level)
AddData("players", name, class, level, nil, nil, now)
end
elseif event == "GUILD_ROSTER_UPDATE" then
local name, class, level, _
for i = 1, GetNumGuildMembers() do
name, _, _, level, class = GetGuildRosterInfo(i)
class = L["class"][class] or nil
AddData("players", name, class, level)
-- Many addons request the roster every few seconds. A class and level
-- cache gains nothing from rescanning a large guild each time, so scan
-- at most once a minute unless the member count changed.
local count = GetNumGuildMembers()
local now = time()
if count > 0 and not (lastGuildScan and count == lastGuildCount
and now - lastGuildScan < GUILD_SCAN_INTERVAL) then
lastGuildScan, lastGuildCount = now, count
local name, class, level, _
for i = 1, count do
name, _, _, level, class = GetGuildRosterInfo(i)
class = L["class"][class] or nil
AddData("players", name, class, level, nil, nil, now)
end
end
elseif event == "RAID_ROSTER_UPDATE" then
local name, class, SubGroup, level, _
local now = time()
for i = 1, GetNumRaidMembers() do
name, _, SubGroup, level, class = GetRaidRosterInfo(i)
class = L["class"][class] or nil
local guid = API and API.UnitGUID and API.UnitGUID("raid" .. i)
AddData("players", name, class, level, nil, guid)
AddData("players", name, class, level, nil, GUIDIfClassless("raid" .. i, class), now)
end
elseif event == "PARTY_MEMBERS_CHANGED" then
local name, class, level, unit, _
local now = time()
for i = 1, GetNumPartyMembers() do
unit = "party" .. i
_, class = UnitClass(unit)
name = UnitName(unit)
level = UnitLevel(unit)
local guid = API and API.UnitGUID and API.UnitGUID(unit)
AddData("players", name, class, level, nil, guid)
AddData("players", name, class, level, nil, GUIDIfClassless(unit, class), now)
end
elseif event == "WHO_LIST_UPDATE" then
local name, class, level, _
local now = time()
for i = 1, GetNumWhoResults() do
name, _, level, _, class, _ = GetWhoInfo(i)
class = L["class"][class] or nil
AddData("players", name, class, level)
AddData("players", name, class, level, nil, nil, now)
end
elseif event == "UPDATE_MOUSEOVER_UNIT" or event == "PLAYER_TARGET_CHANGED" then
@@ -543,8 +690,7 @@ libunitscan:SetScript("OnEvent", function()
_, class = UnitClass(scan)
level = UnitLevel(scan)
name = UnitName(scan)
local guid = API and API.UnitGUID and API.UnitGUID(scan)
AddData("players", name, class, level, nil, guid)
AddData("players", name, class, level, nil, GUIDIfClassless(scan, class))
else
_, class = UnitClass(scan)
elite = UnitClassification(scan)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-86
View File
@@ -1,86 +0,0 @@
ShaguTweaks_translation = ShaguTweaks_translation or {}
ShaguTweaks_translation["deDE"] = {
["Adds a castbar to the nameplate based on combat log estimations."] = nil,
["Adds a “Sell Junk” button to every merchant window, that sells all grey items."] = nil,
["Adds a small 24h clock to the mini map."] = nil,
["Adds class colors to the player, target and party unit frames."] = nil,
["Adds compatibility to Turtle WoW's custom changes."] = nil,
["Adds coordinates to the bottom of the World Map."] = nil,
["Advanced Options"] = nil,
["Allows to scroll using the mouse wheel, enables sticky chat channels and repeats message on arrow up."] = nil,
["Auto Dismount"] = nil,
["Automatically dismounts whenever a spell is casted."] = nil,
["Automatically switch to the required warrior or druid stance on spell cast."] = nil,
["Auto Stance"] = nil,
["Blue Shaman Class Colors"] = nil,
["Change health text color based on its value."] = nil,
["Changes the class color code of shamans to blue, as known from TBC+."] = nil,
["Changes the nameplate health bar color to the class color."] = nil,
["Chat Hyperlinks"] = nil,
["Chat Tweaks"] = nil,
["Clock"] = nil,
["Cooldown Numbers"] = nil,
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = nil,
["Cursor"] = nil,
["Darkened UI"] = nil,
["Debuff Timer"] = nil,
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = nil,
["Display the remaining duration as text on every cooldown."] = nil,
["Draw the mini map in a squared shape instead of a round one."] = nil,
["Enemy Castbars"] = nil,
["Equip Compare"] = nil,
["Estimates health numbers, and shows numbers on player, pet and target unit frames."] = nil,
["General"] = nil,
["Hide Errors"] = nil,
["Hide Gryphons"] = nil,
["Hides and ignores all Lua errors produced by broken addons."] = nil,
["Hides the gryphons left and right of the action bar."] = nil,
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = nil,
["Increases the healthbar of the player and target unitframe."] = nil,
["Item Rarity Borders"] = nil,
["Localtime"] = nil,
["Makes all nameplates honor the UI-Scale setting."] = nil,
["MiniMap Clock"] = nil,
["MiniMap Square"] = nil,
["MiniMap Tweaks"] = nil,
["Movable Unit Frames"] = nil,
["N/A"] = nil,
["Nameplate Castbar"] = nil,
["Nameplate Class Colors"] = nil,
["Nameplate Scale"] = nil,
["Nameplates"] = nil,
["Player"] = nil,
["Player and Target unit frames can be moved while <Shift> and <Ctrl> are pressed together."] = nil,
["Real Health Numbers"] = nil,
["Reduced Actionbar Size"] = nil,
["Reduces the actionbar size by removing several items such as the bag panel and microbar"] = nil,
["Replace unitframe portraits with class icons."] = nil,
["Sell Grey Items"] = nil,
["Sell Junk"] = nil,
["Servertime"] = nil,
["Show class colored circles on world and battlefield map."] = nil,
["Show class colors in Who, Guild, Friends and Chat."] = nil,
["Show debuff durations on the target unit frame."] = nil,
["Show item rarity as the border color on bags, bank, character and inspect frames."] = nil,
["Shows an enemy castbar on target unit frame."] = nil,
["Shows currently equipped items on tooltips while the shift key is pressed."] = nil,
["Shows the vendor sell values on all item tooltips."] = nil,
["Social & Chat"] = nil,
["Social Colors"] = nil,
["Tooltip Details"] = nil,
["Tooltip & Items"] = nil,
["Turns the entire interface into darker colors."] = nil,
["Turns the world map into a movable window. The map can be scaled with <Ctrl> + Mousewheel."] = nil,
["Turtle WoW Compatibility"] = nil,
["Unit Frame Big Health"] = nil,
["Unit Frame Class Colors"] = nil,
["Unit Frame Class Portraits"] = nil,
["Unit Frame Health Colors"] = nil,
["Unit Frames"] = nil,
["Vendor Values"] = nil,
["WorldMap Class Colors"] = nil,
["WorldMap Coordinates"] = nil,
["WorldMap Window"] = nil,
["World & MiniMap"] = nil,
["Your vendor trash has been sold and you earned"] = nil,
}
+19 -1
View File
@@ -33,8 +33,10 @@ ShaguTweaks_translation["enUS"] = {
["General"] = nil,
["Hide Errors"] = nil,
["Hide Gryphons"] = nil,
["Hide Pet Unit Frame"] = nil,
["Hides and ignores all Lua errors produced by broken addons."] = nil,
["Hides the gryphons left and right of the action bar."] = nil,
["Hides the Pet unitframe below the player in pet classes."] = nil,
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = nil,
["Increases the healthbar of the player and target unitframe."] = nil,
["Item Rarity Borders"] = nil,
@@ -63,6 +65,7 @@ ShaguTweaks_translation["enUS"] = {
["Show debuff durations on the target unit frame."] = nil,
["Show item rarity as the border color on bags, bank, character and inspect frames."] = nil,
["Shows an enemy castbar on target unit frame."] = nil,
["Shows current and needed XP, percentage and rested bonus while hovering over the experience bar."] = nil,
["Shows currently equipped items on tooltips while the shift key is pressed."] = nil,
["Shows the vendor sell values on all item tooltips."] = nil,
["Social & Chat"] = nil,
@@ -75,7 +78,6 @@ ShaguTweaks_translation["enUS"] = {
["Unit Frame Big Health"] = nil,
["Unit Frame Class Colors"] = nil,
["Unit Frame Class Portraits"] = nil,
["Unit Frame Health Colors"] = nil,
["Unit Frames"] = nil,
["Vendor Values"] = nil,
["WorldMap Class Colors"] = nil,
@@ -83,4 +85,20 @@ ShaguTweaks_translation["enUS"] = {
["WorldMap Window"] = nil,
["World & MiniMap"] = nil,
["Your vendor trash has been sold and you earned"] = nil,
["Swing Timer"] = nil,
["Wand Timer"] = nil,
["Main Hand"] = nil,
["Off Hand"] = nil,
["Ranged"] = nil,
["Shows main-hand and off-hand swing timers above the cast bar, with the weapon's icon. Hold <Ctrl>, <Shift> and <Alt> to move them; right-click to reset."] = nil,
["Shows the time until your next wand shot (or Auto Shot) above the cast bar, with the weapon's icon."] = nil,
["Transports"] = nil,
["Shows or hides the OctoBus transport timers."] = nil,
["Loot History"] = nil,
["Adds /loothistory: a window with the need, greed and pass rolls of recent group loot and who won each item. Requires ClassicAPI."] = nil,
["Retrieving item information..."] = nil,
["All players passed"] = nil,
["Clear"] = nil,
["No loot rolls yet."] = nil,
["Loot History requires ClassicAPI."] = nil,
}
-86
View File
@@ -1,86 +0,0 @@
ShaguTweaks_translation = ShaguTweaks_translation or {}
ShaguTweaks_translation["esES"] = {
["Adds a castbar to the nameplate based on combat log estimations."] = nil,
["Adds a “Sell Junk” button to every merchant window, that sells all grey items."] = nil,
["Adds a small 24h clock to the mini map."] = nil,
["Adds class colors to the player, target and party unit frames."] = nil,
["Adds compatibility to Turtle WoW's custom changes."] = nil,
["Adds coordinates to the bottom of the World Map."] = nil,
["Advanced Options"] = nil,
["Allows to scroll using the mouse wheel, enables sticky chat channels and repeats message on arrow up."] = nil,
["Auto Dismount"] = nil,
["Automatically dismounts whenever a spell is casted."] = nil,
["Automatically switch to the required warrior or druid stance on spell cast."] = nil,
["Auto Stance"] = nil,
["Blue Shaman Class Colors"] = nil,
["Change health text color based on its value."] = nil,
["Changes the class color code of shamans to blue, as known from TBC+."] = nil,
["Changes the nameplate health bar color to the class color."] = nil,
["Chat Hyperlinks"] = nil,
["Chat Tweaks"] = nil,
["Clock"] = nil,
["Cooldown Numbers"] = nil,
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = nil,
["Cursor"] = nil,
["Darkened UI"] = nil,
["Debuff Timer"] = nil,
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = nil,
["Display the remaining duration as text on every cooldown."] = nil,
["Draw the mini map in a squared shape instead of a round one."] = nil,
["Enemy Castbars"] = nil,
["Equip Compare"] = nil,
["Estimates health numbers, and shows numbers on player, pet and target unit frames."] = nil,
["General"] = nil,
["Hide Errors"] = nil,
["Hide Gryphons"] = nil,
["Hides and ignores all Lua errors produced by broken addons."] = nil,
["Hides the gryphons left and right of the action bar."] = nil,
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = nil,
["Increases the healthbar of the player and target unitframe."] = nil,
["Item Rarity Borders"] = nil,
["Localtime"] = nil,
["Makes all nameplates honor the UI-Scale setting."] = nil,
["MiniMap Clock"] = nil,
["MiniMap Square"] = nil,
["MiniMap Tweaks"] = nil,
["Movable Unit Frames"] = nil,
["N/A"] = nil,
["Nameplate Castbar"] = nil,
["Nameplate Class Colors"] = nil,
["Nameplate Scale"] = nil,
["Nameplates"] = nil,
["Player"] = nil,
["Player and Target unit frames can be moved while <Shift> and <Ctrl> are pressed together."] = nil,
["Real Health Numbers"] = nil,
["Reduced Actionbar Size"] = nil,
["Reduces the actionbar size by removing several items such as the bag panel and microbar"] = nil,
["Replace unitframe portraits with class icons."] = nil,
["Sell Grey Items"] = nil,
["Sell Junk"] = nil,
["Servertime"] = nil,
["Show class colored circles on world and battlefield map."] = nil,
["Show class colors in Who, Guild, Friends and Chat."] = nil,
["Show debuff durations on the target unit frame."] = nil,
["Show item rarity as the border color on bags, bank, character and inspect frames."] = nil,
["Shows an enemy castbar on target unit frame."] = nil,
["Shows currently equipped items on tooltips while the shift key is pressed."] = nil,
["Shows the vendor sell values on all item tooltips."] = nil,
["Social & Chat"] = nil,
["Social Colors"] = nil,
["Tooltip Details"] = nil,
["Tooltip & Items"] = nil,
["Turns the entire interface into darker colors."] = nil,
["Turns the world map into a movable window. The map can be scaled with <Ctrl> + Mousewheel."] = nil,
["Turtle WoW Compatibility"] = nil,
["Unit Frame Big Health"] = nil,
["Unit Frame Class Colors"] = nil,
["Unit Frame Class Portraits"] = nil,
["Unit Frame Health Colors"] = nil,
["Unit Frames"] = nil,
["Vendor Values"] = nil,
["WorldMap Class Colors"] = nil,
["WorldMap Coordinates"] = nil,
["WorldMap Window"] = nil,
["World & MiniMap"] = nil,
["Your vendor trash has been sold and you earned"] = nil,
}
-86
View File
@@ -1,86 +0,0 @@
ShaguTweaks_translation = ShaguTweaks_translation or {}
ShaguTweaks_translation["frFR"] = {
["Adds a castbar to the nameplate based on combat log estimations."] = nil,
["Adds a “Sell Junk” button to every merchant window, that sells all grey items."] = nil,
["Adds a small 24h clock to the mini map."] = nil,
["Adds class colors to the player, target and party unit frames."] = nil,
["Adds compatibility to Turtle WoW's custom changes."] = nil,
["Adds coordinates to the bottom of the World Map."] = nil,
["Advanced Options"] = nil,
["Allows to scroll using the mouse wheel, enables sticky chat channels and repeats message on arrow up."] = nil,
["Auto Dismount"] = nil,
["Automatically dismounts whenever a spell is casted."] = nil,
["Automatically switch to the required warrior or druid stance on spell cast."] = nil,
["Auto Stance"] = nil,
["Blue Shaman Class Colors"] = nil,
["Change health text color based on its value."] = nil,
["Changes the class color code of shamans to blue, as known from TBC+."] = nil,
["Changes the nameplate health bar color to the class color."] = nil,
["Chat Hyperlinks"] = nil,
["Chat Tweaks"] = nil,
["Clock"] = nil,
["Cooldown Numbers"] = nil,
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = nil,
["Cursor"] = nil,
["Darkened UI"] = nil,
["Debuff Timer"] = nil,
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = nil,
["Display the remaining duration as text on every cooldown."] = nil,
["Draw the mini map in a squared shape instead of a round one."] = nil,
["Enemy Castbars"] = nil,
["Equip Compare"] = nil,
["Estimates health numbers, and shows numbers on player, pet and target unit frames."] = nil,
["General"] = nil,
["Hide Errors"] = nil,
["Hide Gryphons"] = nil,
["Hides and ignores all Lua errors produced by broken addons."] = nil,
["Hides the gryphons left and right of the action bar."] = nil,
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = nil,
["Increases the healthbar of the player and target unitframe."] = nil,
["Item Rarity Borders"] = nil,
["Localtime"] = nil,
["Makes all nameplates honor the UI-Scale setting."] = nil,
["MiniMap Clock"] = nil,
["MiniMap Square"] = nil,
["MiniMap Tweaks"] = nil,
["Movable Unit Frames"] = nil,
["N/A"] = nil,
["Nameplate Castbar"] = nil,
["Nameplate Class Colors"] = nil,
["Nameplate Scale"] = nil,
["Nameplates"] = nil,
["Player"] = nil,
["Player and Target unit frames can be moved while <Shift> and <Ctrl> are pressed together."] = nil,
["Real Health Numbers"] = nil,
["Reduced Actionbar Size"] = nil,
["Reduces the actionbar size by removing several items such as the bag panel and microbar"] = nil,
["Replace unitframe portraits with class icons."] = nil,
["Sell Grey Items"] = nil,
["Sell Junk"] = nil,
["Servertime"] = nil,
["Show class colored circles on world and battlefield map."] = nil,
["Show class colors in Who, Guild, Friends and Chat."] = nil,
["Show debuff durations on the target unit frame."] = nil,
["Show item rarity as the border color on bags, bank, character and inspect frames."] = nil,
["Shows an enemy castbar on target unit frame."] = nil,
["Shows currently equipped items on tooltips while the shift key is pressed."] = nil,
["Shows the vendor sell values on all item tooltips."] = nil,
["Social & Chat"] = nil,
["Social Colors"] = nil,
["Tooltip Details"] = nil,
["Tooltip & Items"] = nil,
["Turns the entire interface into darker colors."] = nil,
["Turns the world map into a movable window. The map can be scaled with <Ctrl> + Mousewheel."] = nil,
["Turtle WoW Compatibility"] = nil,
["Unit Frame Big Health"] = nil,
["Unit Frame Class Colors"] = nil,
["Unit Frame Class Portraits"] = nil,
["Unit Frame Health Colors"] = nil,
["Unit Frames"] = nil,
["Vendor Values"] = nil,
["WorldMap Class Colors"] = nil,
["WorldMap Coordinates"] = nil,
["WorldMap Window"] = nil,
["World & MiniMap"] = nil,
["Your vendor trash has been sold and you earned"] = nil,
}
-86
View File
@@ -1,86 +0,0 @@
ShaguTweaks_translation = ShaguTweaks_translation or {}
ShaguTweaks_translation["koKR"] = {
["Adds a castbar to the nameplate based on combat log estimations."] = nil,
["Adds a “Sell Junk” button to every merchant window, that sells all grey items."] = nil,
["Adds a small 24h clock to the mini map."] = nil,
["Adds class colors to the player, target and party unit frames."] = nil,
["Adds compatibility to Turtle WoW's custom changes."] = nil,
["Adds coordinates to the bottom of the World Map."] = nil,
["Advanced Options"] = nil,
["Allows to scroll using the mouse wheel, enables sticky chat channels and repeats message on arrow up."] = nil,
["Auto Dismount"] = nil,
["Automatically dismounts whenever a spell is casted."] = nil,
["Automatically switch to the required warrior or druid stance on spell cast."] = nil,
["Auto Stance"] = nil,
["Blue Shaman Class Colors"] = nil,
["Change health text color based on its value."] = nil,
["Changes the class color code of shamans to blue, as known from TBC+."] = nil,
["Changes the nameplate health bar color to the class color."] = nil,
["Chat Hyperlinks"] = nil,
["Chat Tweaks"] = nil,
["Clock"] = nil,
["Cooldown Numbers"] = nil,
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = nil,
["Cursor"] = nil,
["Darkened UI"] = nil,
["Debuff Timer"] = nil,
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = nil,
["Display the remaining duration as text on every cooldown."] = nil,
["Draw the mini map in a squared shape instead of a round one."] = nil,
["Enemy Castbars"] = nil,
["Equip Compare"] = nil,
["Estimates health numbers, and shows numbers on player, pet and target unit frames."] = nil,
["General"] = nil,
["Hide Errors"] = nil,
["Hide Gryphons"] = nil,
["Hides and ignores all Lua errors produced by broken addons."] = nil,
["Hides the gryphons left and right of the action bar."] = nil,
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = nil,
["Increases the healthbar of the player and target unitframe."] = nil,
["Item Rarity Borders"] = nil,
["Localtime"] = nil,
["Makes all nameplates honor the UI-Scale setting."] = nil,
["MiniMap Clock"] = nil,
["MiniMap Square"] = nil,
["MiniMap Tweaks"] = nil,
["Movable Unit Frames"] = nil,
["N/A"] = nil,
["Nameplate Castbar"] = nil,
["Nameplate Class Colors"] = nil,
["Nameplate Scale"] = nil,
["Nameplates"] = nil,
["Player"] = nil,
["Player and Target unit frames can be moved while <Shift> and <Ctrl> are pressed together."] = nil,
["Real Health Numbers"] = nil,
["Reduced Actionbar Size"] = nil,
["Reduces the actionbar size by removing several items such as the bag panel and microbar"] = nil,
["Replace unitframe portraits with class icons."] = nil,
["Sell Grey Items"] = nil,
["Sell Junk"] = nil,
["Servertime"] = nil,
["Show class colored circles on world and battlefield map."] = nil,
["Show class colors in Who, Guild, Friends and Chat."] = nil,
["Show debuff durations on the target unit frame."] = nil,
["Show item rarity as the border color on bags, bank, character and inspect frames."] = nil,
["Shows an enemy castbar on target unit frame."] = nil,
["Shows currently equipped items on tooltips while the shift key is pressed."] = nil,
["Shows the vendor sell values on all item tooltips."] = nil,
["Social & Chat"] = nil,
["Social Colors"] = nil,
["Tooltip Details"] = nil,
["Tooltip & Items"] = nil,
["Turns the entire interface into darker colors."] = nil,
["Turns the world map into a movable window. The map can be scaled with <Ctrl> + Mousewheel."] = nil,
["Turtle WoW Compatibility"] = nil,
["Unit Frame Big Health"] = nil,
["Unit Frame Class Colors"] = nil,
["Unit Frame Class Portraits"] = nil,
["Unit Frame Health Colors"] = nil,
["Unit Frames"] = nil,
["Vendor Values"] = nil,
["WorldMap Class Colors"] = nil,
["WorldMap Coordinates"] = nil,
["WorldMap Window"] = nil,
["World & MiniMap"] = nil,
["Your vendor trash has been sold and you earned"] = nil,
}
-86
View File
@@ -1,86 +0,0 @@
ShaguTweaks_translation = ShaguTweaks_translation or {}
ShaguTweaks_translation["ruRU"] = {
["Adds a castbar to the nameplate based on combat log estimations."] = "Добавляет панель применения к индикатору здоровья на основе оценок журнала боя.",
["Adds a “Sell Junk” button to every merchant window, that sells all grey items."] = "Добавляет кнопку «Продать серые предметы» в каждое окно торговца, в котором продаются все серые предметы.",
["Adds a small 24h clock to the mini map."] = "Добавляет небольшие 24-часовые часы на мини-карту.",
["Adds class colors to the player, target and party unit frames."] = "Добавить цвета классов к окнам игрока, цели и отряда группы.",
["Adds compatibility to Turtle WoW's custom changes."] = "Добавляет совместимость с пользовательскими изменениями Turtle WoW.",
["Adds coordinates to the bottom of the World Map."] = "Добавляет координаты внизу карты мира.",
["Advanced Options"] = "Расширенные настройки",
["Allows to scroll using the mouse wheel, enables sticky chat channels and repeats message on arrow up."] = "Позволяет прокручивать колесо мыши, включает закрепленные каналы чата и повторяет сообщение при нажатии стрелки вверх.",
["Auto Dismount"] = "Авто-спешивание",
["Automatically dismounts whenever a spell is casted."] = "Автоматическое спешивание при каждом произнесении заклинания.",
["Automatically switch to the required warrior or druid stance on spell cast."] = "Автоматически переключаться на нужную стойку воина или друида при произнесении заклинания.",
["Auto Stance"] = "Авто-стойка",
["Blue Shaman Class Colors"] = "Синий класс шамана",
["Change health text color based on its value."] = "Изменить цвет текста о состоянии здоровья в зависимости от его значения.",
["Changes the class color code of shamans to blue, as known from TBC+."] = "Изменяет цветовой код класса шаманов на синий, как известно из TBC+.",
["Changes the nameplate health bar color to the class color."] = "Изменяет цвет индикатора здоровья на цвет класса.",
["Chat Hyperlinks"] = "Гиперссылки чата",
["Chat Tweaks"] = "Улучшения чата",
["Clock"] = "Часы",
["Cooldown Numbers"] = "Продолжительность перезарядки",
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = "Копирование URL-адреса веб-сайтов из чата, преобразование CLINKов в реальные предметы и обрабатывание ссылок на квесты и игроков.",
["Cursor"] = "Курсор",
["Darkened UI"] = "Затемненный интерфейс",
["Debuff Timer"] = "Таймер дебаффов",
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = "Отображение здоровья, цвета класса, названия гильдии, ранга гильдии и текущей цели во всплывающих подсказках юнитов.",
["Display the remaining duration as text on every cooldown."] = "Отображать оставшуюся продолжительность в виде текста при перезарядке.",
["Draw the mini map in a squared shape instead of a round one."] = "Мини-карта квадратной формы вместо круглой.",
["Enemy Castbars"] = "Панель применения противника",
["Equip Compare"] = "Сравнение экипировки",
["Estimates health numbers, and shows numbers on player, pet and target unit frames."] = "Оценивает количество здоровья и показывает цифры на окнах игрока, питомца и цели.",
["General"] = "Общие",
["Hide Errors"] = "Скрыть ошибки",
["Hide Gryphons"] = "Спрятать грифонов",
["Hides and ignores all Lua errors produced by broken addons."] = "Скрывает и игнорирует все ошибки Lua, вызванные неработающими аддонами.",
["Hides the gryphons left and right of the action bar."] = "Скрывает грифонов слева и справа от панели действий.",
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = "Скрывает ненужные кнопки мини-карты и позволяет масштабировать ее с помощью колеса мыши.",
["Increases the healthbar of the player and target unitframe."] = "Увеличивает полосу здоровья игрока и цели.",
["Item Rarity Borders"] = "Границы редкости предметов",
["Localtime"] = "Местное время",
["Makes all nameplates honor the UI-Scale setting."] = "Все индикаторы здоровья будут соответствовать настройке UI-Scale.",
["MiniMap Clock"] = "Часы на мини-карте",
["MiniMap Square"] = "Квадратная мини-карта",
["MiniMap Tweaks"] = "Улучшения мини-карты",
["Movable Unit Frames"] = "Передвижные окна",
["N/A"] = "Н/Д",
["Nameplate Castbar"] = "Панель применения индикатора здоровья",
["Nameplate Class Colors"] = "Классы индикатора здоровья",
["Nameplate Scale"] = "Масштаб индикаторов здоровья",
["Nameplates"] = nil,
["Player"] = "Игрок",
["Player and Target unit frames can be moved while <Shift> and <Ctrl> are pressed together."] = "Окна игрока и цели можно перемещать, удерживая клавиши <Shift> и <Ctrl> вместе.",
["Real Health Numbers"] = "Реальные цифры здоровья",
["Reduced Actionbar Size"] = "Уменьшить панель действий",
["Reduces the actionbar size by removing several items such as the bag panel and microbar"] = "Уменьшает размер панели действий за счет удаления нескольких элементов, таких как панель сумки и микропанель.",
["Replace unitframe portraits with class icons."] = "Заменить портреты окон юнитов значками классов.",
["Sell Grey Items"] = "Продать серые предметы",
["Sell Junk"] = "Продать серые предметы",
["Servertime"] = "Серверное время",
["Show class colored circles on world and battlefield map."] = "Показать цветные круги классов на карте мира и поле боя.",
["Show class colors in Who, Guild, Friends and Chat."] = "Показать цвета классов в разделах «Кто», «Гильдия», «Друзья» и «Чат».",
["Show debuff durations on the target unit frame."] = "Показать продолжительность дебаффов в окне цели.",
["Show item rarity as the border color on bags, bank, character and inspect frames."] = "Показать редкость предмета в качестве цвета рамки на сумках, банке, окне персонажа и осмотра.",
["Shows an enemy castbar on target unit frame."] = "Показать панель применения противника в окне юнита цели.",
["Shows currently equipped items on tooltips while the shift key is pressed."] = "Показывает экипированные в данный момент предметы во всплывающих подсказках, пока нажата клавиша Shift.",
["Shows the vendor sell values on all item tooltips."] = "Показывает стоимость продажи продавцу во всплывающих подсказках ко всем предметам.",
["Social & Chat"] = "Общение и чат",
["Social Colors"] = "Социальные цвета",
["Tooltip Details"] = "Подробные подсказки",
["Tooltip & Items"] = "Подсказки и предметы",
["Turns the entire interface into darker colors."] = "Меняет весь интерфейс в более темные цвета.",
["Turns the world map into a movable window. The map can be scaled with <Ctrl> + Mousewheel."] = "Превращает карту мира в подвижное окно. Карту можно масштабировать с помощью <Ctrl> + Колесо мыши.",
["Turtle WoW Compatibility"] = "Совместимость с Turtle WoW",
["Unit Frame Big Health"] = "Большое здоровье",
["Unit Frame Class Colors"] = "Цветные классы",
["Unit Frame Class Portraits"] = "Классовые портреты",
["Unit Frame Health Colors"] = "Цветное здоровье",
["Unit Frames"] = "Окна юнитов",
["Vendor Values"] = "Стоимость продажи",
["WorldMap Class Colors"] = "Классы на карте мира",
["WorldMap Coordinates"] = "Координаты на карте мира",
["WorldMap Window"] = "Окно карты мира",
["World & MiniMap"] = "Карта мира и мини-карта",
["Your vendor trash has been sold and you earned"] = "Ваш хлам продан, и вы заработали",
}
-86
View File
@@ -1,86 +0,0 @@
ShaguTweaks_translation = ShaguTweaks_translation or {}
ShaguTweaks_translation["zhCN"] = {
["Adds a castbar to the nameplate based on combat log estimations."] = "添加基于战斗日志的施法条。",
["Adds a “Sell Junk” button to every merchant window, that sells all grey items."] = "商人窗口添加:“出售垃圾”按钮,将灰色物品出售给商人。",
["Adds a small 24h clock to the mini map."] = "添加一个24小时的时钟到小地图。",
["Adds class colors to the player, target and party unit frames."] = "添加玩家、目标和队伍单位的职业颜色。",
["Adds compatibility to Turtle WoW's custom changes."] = "添加乌龟服的兼容修改。",
["Adds coordinates to the bottom of the World Map."] = "添加坐标到世界地图的底部。",
["Advanced Options"] = "高级选项",
["Allows to scroll using the mouse wheel, enables sticky chat channels and repeats message on arrow up."] = "允许使用鼠标滚轮,启用粘性频道,向上箭头重复消息。",
["Auto Dismount"] = "自动下坐骑",
["Automatically dismounts whenever a spell is casted."] = "当施放任何法术时自动取消坐骑。",
["Automatically switch to the required warrior or druid stance on spell cast."] = "自动切换技能所需姿态(战士/德鲁伊)。",
["Auto Stance"] = "自动切换姿态",
["Blue Shaman Class Colors"] = "蓝色萨满职业颜色",
["Change health text color based on its value."] = "根据值改变血量文本颜色。",
["Changes the class color code of shamans to blue, as known from TBC+."] = "修改了萨满职业的颜色为蓝色,与TBC+相同。",
["Changes the nameplate health bar color to the class color."] = "修改姓名板的血量条颜色为职业颜色。",
["Chat Hyperlinks"] = "聊天超链接",
["Chat Tweaks"] = "聊天增强",
["Clock"] = "时钟",
["Cooldown Numbers"] = "冷却倒计时",
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = "复制聊天中的网址,将CLINK转换为实际的物品、任务和玩家链接。",
["Cursor"] = "鼠标指针",
["Darkened UI"] = "黑暗风格UI",
["Debuff Timer"] = "Debuff计时",
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = "提示框显示当前目标血量、职业颜色、公会名称、公会等级、荣誉排名。",
["Display the remaining duration as text on every cooldown."] = "显示剩余时间的文本,如:10s,1m,1h,1d。",
["Draw the mini map in a squared shape instead of a round one."] = "小地图以方形显示,而不是圆形。",
["Enemy Castbars"] = "敌对目标施法条",
["Equip Compare"] = "装备比较",
["Estimates health numbers, and shows numbers on player, pet and target unit frames."] = "显示玩家、宠物和目标单位的血量。",
["General"] = "常规",
["Hide Errors"] = "隐藏LUA错误",
["Hide Gryphons"] = "隐藏动作条狮鹫",
["Hides and ignores all Lua errors produced by broken addons."] = "隐藏并忽略所有插件脚本产生的LUA错误。",
["Hides the gryphons left and right of the action bar."] = "隐藏动作条左右两边狮鹫。",
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = "隐藏小地图缩放按钮,改用使用鼠标滚轮进行缩放。",
["Increases the healthbar of the player and target unitframe."] = "增加玩家和目标单位的血量条的宽度。",
["Item Rarity Borders"] = "物品品质边框着色",
["Localtime"] = "本地时间",
["Makes all nameplates honor the UI-Scale setting."] = "名字板跟随UI缩放设置。",
["MiniMap Clock"] = "小地图时钟",
["MiniMap Square"] = "方形小地图",
["MiniMap Tweaks"] = "小地图增强",
["Movable Unit Frames"] = "头像框架可移动",
["N/A"] = "N/A",
["Nameplate Castbar"] = "姓名板施法条",
["Nameplate Class Colors"] = "姓名板职业着色",
["Nameplate Scale"] = "姓名板缩放",
["Nameplates"] = nil,
["Player"] = "玩家",
["Player and Target unit frames can be moved while <Shift> and <Ctrl> are pressed together."] = "当<Shift>和<Ctrl>键同时按下时,玩家与目标单位的头像可移动。",
["Real Health Numbers"] = "显示预估生命值",
["Reduced Actionbar Size"] = "减少动作条大小",
["Reduces the actionbar size by removing several items such as the bag panel and microbar"] = "减少底部动作条大小,移除动作条上的按钮。",
["Replace unitframe portraits with class icons."] = "单位头像替换为职业图标。",
["Sell Grey Items"] = "售卖灰色物品",
["Sell Junk"] = "售卖垃圾物品",
["Servertime"] = "服务器时间",
["Show class colored circles on world and battlefield map."] = "在世界地图和战场地图上显示团队成员职业颜色的圆圈。",
["Show class colors in Who, Guild, Friends and Chat."] = "显示职业颜色在Who、公会、好友和聊天等列表中。",
["Show debuff durations on the target unit frame."] = "目标单位显示debuff的持续时间。",
["Show item rarity as the border color on bags, bank, character and inspect frames."] = "在背包、银行、角色、查看窗口中显示物品品质边框颜色。",
["Shows an enemy castbar on target unit frame."] = "显示敌对目标单位的施法条。",
["Shows currently equipped items on tooltips while the shift key is pressed."] = "按下<Shift>键时与当前与装备物品在进行比较。",
["Shows the vendor sell values on all item tooltips."] = "物品的提示框中显示物品的售价。",
["Social & Chat"] = "社交&聊天",
["Social Colors"] = "显示职业颜色",
["Tooltip Details"] = "提示框详情",
["Tooltip & Items"] = "提示框&物品",
["Turns the entire interface into darker colors."] = "将整个用户界面元素改为暗黑向。",
["Turns the world map into a movable window. The map can be scaled with <Ctrl> + Mousewheel."] = "让世界地图窗口可移动,可以按住<Ctrl>并滚动鼠标滚轮进行缩放。",
["Turtle WoW Compatibility"] = "乌龟服兼容修改",
["Unit Frame Big Health"] = "单位框架大生命文本",
["Unit Frame Class Colors"] = "单位框架职业着色",
["Unit Frame Class Portraits"] = "单位框架职业图标头像",
["Unit Frame Health Colors"] = "单位框架血条颜色",
["Unit Frames"] = "单位框架",
["Vendor Values"] = "售卖价格",
["WorldMap Class Colors"] = "世界地图职业着色",
["WorldMap Coordinates"] = "世界地图显示坐标",
["WorldMap Window"] = "世界地图窗口",
["World & MiniMap"] = "地图&小地图",
["Your vendor trash has been sold and you earned"] = "你的垃圾物品已出售,你获得了:",
}
-86
View File
@@ -1,86 +0,0 @@
ShaguTweaks_translation = ShaguTweaks_translation or {}
ShaguTweaks_translation["zhTW"] = {
["Adds a castbar to the nameplate based on combat log estimations."] = nil,
["Adds a “Sell Junk” button to every merchant window, that sells all grey items."] = nil,
["Adds a small 24h clock to the mini map."] = nil,
["Adds class colors to the player, target and party unit frames."] = nil,
["Adds compatibility to Turtle WoW's custom changes."] = nil,
["Adds coordinates to the bottom of the World Map."] = nil,
["Advanced Options"] = nil,
["Allows to scroll using the mouse wheel, enables sticky chat channels and repeats message on arrow up."] = nil,
["Auto Dismount"] = nil,
["Automatically dismounts whenever a spell is casted."] = nil,
["Automatically switch to the required warrior or druid stance on spell cast."] = nil,
["Auto Stance"] = nil,
["Blue Shaman Class Colors"] = nil,
["Change health text color based on its value."] = nil,
["Changes the class color code of shamans to blue, as known from TBC+."] = nil,
["Changes the nameplate health bar color to the class color."] = nil,
["Chat Hyperlinks"] = nil,
["Chat Tweaks"] = nil,
["Clock"] = nil,
["Cooldown Numbers"] = nil,
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = nil,
["Cursor"] = nil,
["Darkened UI"] = nil,
["Debuff Timer"] = nil,
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = nil,
["Display the remaining duration as text on every cooldown."] = nil,
["Draw the mini map in a squared shape instead of a round one."] = nil,
["Enemy Castbars"] = nil,
["Equip Compare"] = nil,
["Estimates health numbers, and shows numbers on player, pet and target unit frames."] = nil,
["General"] = nil,
["Hide Errors"] = nil,
["Hide Gryphons"] = nil,
["Hides and ignores all Lua errors produced by broken addons."] = nil,
["Hides the gryphons left and right of the action bar."] = nil,
["Hides unnecessary mini map buttons and allows to zoom using the mouse wheel."] = nil,
["Increases the healthbar of the player and target unitframe."] = nil,
["Item Rarity Borders"] = nil,
["Localtime"] = nil,
["Makes all nameplates honor the UI-Scale setting."] = nil,
["MiniMap Clock"] = nil,
["MiniMap Square"] = nil,
["MiniMap Tweaks"] = nil,
["Movable Unit Frames"] = nil,
["N/A"] = nil,
["Nameplate Castbar"] = nil,
["Nameplate Class Colors"] = nil,
["Nameplate Scale"] = nil,
["Nameplates"] = nil,
["Player"] = nil,
["Player and Target unit frames can be moved while <Shift> and <Ctrl> are pressed together."] = nil,
["Real Health Numbers"] = nil,
["Reduced Actionbar Size"] = nil,
["Reduces the actionbar size by removing several items such as the bag panel and microbar"] = nil,
["Replace unitframe portraits with class icons."] = nil,
["Sell Grey Items"] = nil,
["Sell Junk"] = nil,
["Servertime"] = nil,
["Show class colored circles on world and battlefield map."] = nil,
["Show class colors in Who, Guild, Friends and Chat."] = nil,
["Show debuff durations on the target unit frame."] = nil,
["Show item rarity as the border color on bags, bank, character and inspect frames."] = nil,
["Shows an enemy castbar on target unit frame."] = nil,
["Shows currently equipped items on tooltips while the shift key is pressed."] = nil,
["Shows the vendor sell values on all item tooltips."] = nil,
["Social & Chat"] = nil,
["Social Colors"] = nil,
["Tooltip Details"] = nil,
["Tooltip & Items"] = nil,
["Turns the entire interface into darker colors."] = nil,
["Turns the world map into a movable window. The map can be scaled with <Ctrl> + Mousewheel."] = nil,
["Turtle WoW Compatibility"] = nil,
["Unit Frame Big Health"] = nil,
["Unit Frame Class Colors"] = nil,
["Unit Frame Class Portraits"] = nil,
["Unit Frame Health Colors"] = nil,
["Unit Frames"] = nil,
["Vendor Values"] = nil,
["WorldMap Class Colors"] = nil,
["WorldMap Coordinates"] = nil,
["WorldMap Window"] = nil,
["World & MiniMap"] = nil,
["Your vendor trash has been sold and you earned"] = nil,
}
+3
View File
@@ -54,6 +54,9 @@ ShaguTweaks:SetScript("OnEvent", function()
-- initialize empty config
if not ShaguTweaks_config then ShaguTweaks_config = {} end
-- Gold numbers belong to Real Health Numbers; retire the duplicate toggle.
ShaguTweaks_config["Unit Frame Health Colors"] = nil
-- initialize overwrites
ShaguTweaks_config.overwrites = ShaguTweaks_config.overwrites or {}
+58 -23
View File
@@ -11,39 +11,67 @@ local module = ShaguTweaks:register({
enabled = nil,
})
-- Modern APIs return booleans; legacy range APIs return 1/0/nil.
-- In Lua, numeric zero is truthy, so normalize both contracts explicitly.
local function NormalizeRange(value)
if value == true or value == 1 then return 1 end
if value == false or value == 0 then return 0 end
end
local function GetActionRange(action)
-- ClassicAPI can resolve a real spell/item action and run the same
-- range-only engine test directly against the target unit. This avoids
-- relying on the old action-slot heuristic for normal spell buttons.
if not UnitExists("target") then return nil end
if API and API.GetActionInfo then
local actionType, id = API.GetActionInfo(action)
if actionType == "spell" and id and API.IsSpellInRange then
local inRange = API.IsSpellInRange(id, "target")
if inRange ~= nil then
return inRange and 1 or 0
end
elseif actionType == "item" and id and API.IsItemInRange then
local inRange = API.IsItemInRange(id, "target")
if inRange ~= nil then
return inRange and 1 or 0
if actionType == "spell" and id and API.spellrange
and API.IsSpellInRange then
-- ClassicAPI's check is geometric: it does not reject the wrong
-- faction. A friendly buff should not turn red against an enemy.
local spells = _G.C_Spell
if spells then
if type(spells.IsSpellHelpful) == "function"
and spells.IsSpellHelpful(id) and type(UnitCanAssist) == "function"
and not UnitCanAssist("player", "target") then return nil end
if type(spells.IsSpellHarmful) == "function"
and spells.IsSpellHarmful(id) and type(UnitCanAttack) == "function"
and not UnitCanAttack("player", "target") then return nil end
end
-- nil is authoritative: self/rangeless spells have no range to
-- fail. Never turn it into an old action-slot false result.
return NormalizeRange(API.IsSpellInRange(id, "target"))
elseif actionType == "item" and id and API.itemrange
and API.IsItemInRange then
return NormalizeRange(API.IsItemInRange(id, "target"))
end
end
-- Macros, unresolved bag-item actions and older ClassicAPI versions keep
-- Vanilla's native action-slot range check as the compatibility fallback.
return IsActionInRange(action)
-- Macros, unresolved actions and clients without the native capability
-- retain the action-slot compatibility fallback.
return NormalizeRange(IsActionInRange(action))
end
local RANGE_INTERVAL = TOOLTIP_UPDATE_TIME or 0.2
module.enable = function(self)
hooksecurefunc("ActionButton_OnUpdate", function(elapsed)
-- Blizzard sets rangeTimer only for actions that have a range. Use our
-- own countdown instead of sharing it: decrementing rangeTimer here and
-- testing "<= 0.2" ran this check on every frame for every ranged
-- button, and kept Blizzard's own range check from ever firing.
if this.rangeTimer then
this.rangeTimer = this.rangeTimer - elapsed
if this.rangeTimer <= 0.2 then
local timer = (this.shaguRangeTimer or 0) - (elapsed or 0)
if timer > 0 then
this.shaguRangeTimer = timer
else
this.shaguRangeTimer = RANGE_INTERVAL
local action = ActionButton_GetPagedID(this)
local inRange = GetActionRange(action)
local icon = _G[this:GetName() .. "Icon"]
-- Look the icon up once instead of building its name each time.
local icon = this.shaguRangeIcon
if not icon then
icon = _G[this:GetName() .. "Icon"]
this.shaguRangeIcon = icon
end
if icon then
if inRange == 0 then
@@ -51,12 +79,19 @@ module.enable = function(self)
this.r, this.g, this.b, this.a = 0.85, 0.18, 0.18, 1
end
icon:SetVertexColor(this.r, this.g, this.b, this.a)
elseif IsUsableAction(action) then
icon:SetVertexColor(1, 1, 1, 1)
else
-- Restore the normal usability tint even if the old
-- red action is now unusable or short of mana.
local usable, noMana = IsUsableAction(action)
if usable and usable ~= 0 then
icon:SetVertexColor(1, 1, 1, 1)
elseif noMana and noMana ~= 0 then
icon:SetVertexColor(0.5, 0.5, 1, 1)
else
icon:SetVertexColor(0.4, 0.4, 0.4, 1)
end
end
end
this.rangeTimer = TOOLTIP_UPDATE_TIME
end
end
end, true)
+10 -5
View File
@@ -91,24 +91,29 @@ module.enable = function(self)
local Original_ChatFrame_OnEvent = ChatFrame_OnEvent
ChatFrame_OnEvent = function(event)
local message, sender = arg1, arg2
local chatEvent, chatMessage, chatSender = API.GetCurrentChatEvent()
if chatEvent then
event, message, sender = chatEvent, chatMessage, chatSender
end
-- suppress "#showtooltip" macro errors leaking into chat
if strfind(arg1 or "", "^#showtooltip") then
if strfind(message or "", "^#showtooltip") then
return
end
-- suppress BigWigs "Casted X on Y" cast announcements
if event == "CHAT_MSG_SAY" and strfind(arg1 or "", "^Casted %u[%a%s]+ on %u[%a%s]+") then
if event == "CHAT_MSG_SAY" and strfind(message or "", "^Casted %u[%a%s]+ on %u[%a%s]+") then
return
end
-- hide repeated messages in say/yell/channel chat, and emotes from
-- strangers (guildmates/friends are never filtered on emote)
if arg1 and arg2 and arg2 ~= GetPlayerName("player") then
if message and sender and sender ~= GetPlayerName("player") then
local isChatType = event == "CHAT_MSG_SAY" or event == "CHAT_MSG_CHANNEL" or event == "CHAT_MSG_YELL"
local isStrangerEmote = canFilterEmotes and event == "CHAT_MSG_EMOTE"
and not (IsGuildMate(arg2) or IsFriendOf(arg2))
and not (IsGuildMate(sender) or IsFriendOf(sender))
if (isChatType or isStrangerEmote) and IsDuplicate(this, arg1) then
if (isChatType or isStrangerEmote) and IsDuplicate(this, message) then
return
end
end
-53
View File
@@ -1,53 +0,0 @@
local T = ShaguTweaks.T
local GetColorGradient = ShaguTweaks.GetColorGradient
local hooksecurefunc = hooksecurefunc or ShaguTweaks.hooksecurefunc
local module = ShaguTweaks:register({
title = T["Unit Frame Health Colors"],
description = T["Change health text color based on its value."],
expansions = { ["vanilla"] = true },
category = T["Unit Frames"],
enabled = nil,
})
local function SetTextColorIfChanged(fontString, r, g, b, a)
local cr, cg, cb, ca = fontString:GetTextColor()
if cr ~= r or cg ~= g or cb ~= b or ca ~= a then
fontString:SetTextColor(r, g, b, a)
end
end
module.enable = function(self)
local function UpdateManaTextColor(uf)
if not uf then uf = this end
if not uf or not uf.manabar or not uf.manabar.TextString then return end
local name = uf.manabar:GetName()
if name and not strfind(name, "Health") then
local r, g, b = uf.manabar:GetStatusBarColor()
SetTextColorIfChanged(uf.manabar.TextString,
(r + 2) / 3, (g + 2) / 3, (b + 2) / 3, 1)
end
end
local function UpdateHealthTextColor(sb)
if not sb then sb = this end
if not sb or not sb.TextString or not sb.unit then return end
local name = sb:GetName()
if not name or not strfind(name, "Health") then return end
local _, max = sb:GetMinMaxValues()
local cur = sb:GetValue()
local percent = max > 0 and floor(cur / max * 100) or 0
local r, g, b = GetColorGradient(percent / 100)
SetTextColorIfChanged(sb.TextString,
(r + 1) / 2, (g + 1) / 2, (b + 1) / 2, .75)
end
-- Append our color adjustments instead of replacing Blizzard's global
-- functions. This keeps the module cooperative with other UI addons.
hooksecurefunc("UnitFrame_UpdateManaType", UpdateManaTextColor)
hooksecurefunc("TextStatusBar_UpdateTextString", UpdateHealthTextColor)
end
+107 -21
View File
@@ -11,6 +11,14 @@ local module = ShaguTweaks:register({
enabled = nil,
})
local function SetNumberColor(text)
if ShaguTweaks.WhiteUnitFrameNumbers then
text:SetTextColor(1, 1, 1, 1)
else
text:SetTextColor(1, 0.82, 0, 1)
end
end
module.enable = function(self)
if ShaguTweaks.RealHealthNumbersInstalled then return end
ShaguTweaks.RealHealthNumbersInstalled = true
@@ -47,10 +55,11 @@ module.enable = function(self)
if not parent then return end
local text = parent:CreateFontString(nil, "OVERLAY", "GameFontWhite")
text:SetFont(STANDARD_TEXT_FONT, fontSize or 10, "OUTLINE")
-- Match the character name: plain glyphs instead of a heavy black outline.
text:SetFont(STANDARD_TEXT_FONT, fontSize or 10, "")
text:SetHeight(32)
text:SetJustifyH("CENTER")
text:SetTextColor(1, 1, 1, 1)
SetNumberColor(text)
return text
end
@@ -100,15 +109,47 @@ module.enable = function(self)
end
end
local function AnchorHealth(text, bar, y)
if not text or not bar then return end
if not text.percentText then
text.percentText = CreateValueText(text:GetParent(), select(2, text:GetFont()))
end
text:ClearAllPoints()
text:SetJustifyH("RIGHT")
text:SetPoint("RIGHT", bar, "RIGHT", -4, y or 0)
text.percentText:ClearAllPoints()
text.percentText:SetJustifyH("LEFT")
text.percentText:SetPoint("LEFT", bar, "LEFT", 4, y or 0)
end
local function MatchNameStyle(text, name)
if not text or not name or not name.GetFont then return end
local font, size, flags = name:GetFont()
if font and size then text:SetFont(font, size, flags or "") end
if name.GetShadowColor then
text:SetShadowColor(name:GetShadowColor())
end
if name.GetShadowOffset then
text:SetShadowOffset(name:GetShadowOffset())
end
if text.percentText then MatchNameStyle(text.percentText, name) end
end
local function PositionTexts()
-- Keep the stock/modern texture choice with the numeric frame overlays.
local texture = (ShaguTweaks.ModernClassColors and "Interface\\AddOns\\ShaguTweaks\\img\\BlizzardRetailBar" or "Interface\\TargetingFrame\\UI-StatusBar")
for _, name in ipairs({"PlayerFrameHealthBar", "TargetFrameHealthBar", "PetFrameHealthBar"}) do
local bar = _G[name]
if bar then bar:SetStatusBarTexture(texture) end
end
local playerHealthY = PlayerFrameHealthBar:GetHeight() >= 20 and -7 or 0.5
local targetHealthY = TargetFrameHealthBar:GetHeight() >= 20 and -7 or 0.5
AnchorText(self.valueTexts.playerHealth, PlayerFrameHealthBar, playerHealthY)
AnchorHealth(self.valueTexts.playerHealth, PlayerFrameHealthBar, playerHealthY)
AnchorText(self.valueTexts.playerMana, PlayerFrameManaBar, 0.5)
AnchorText(self.valueTexts.targetHealth, TargetFrameHealthBar, targetHealthY)
AnchorHealth(self.valueTexts.targetHealth, TargetFrameHealthBar, targetHealthY)
AnchorText(self.valueTexts.targetMana, TargetFrameManaBar, 0.5)
AnchorText(self.valueTexts.petHealth, PetFrameHealthBar, 0)
AnchorHealth(self.valueTexts.petHealth, PetFrameHealthBar, 0)
AnchorText(self.valueTexts.petMana, PetFrameManaBar, -2)
if _G.PlayerFrameAlternatePowerBar then
@@ -118,6 +159,17 @@ module.enable = function(self)
0
)
end
-- Read final name styling after other unit-frame modules initialize.
local playerName = (PlayerFrame and PlayerFrame.name) or _G.PlayerName
local targetName = (TargetFrame and TargetFrame.name) or _G.TargetFrameName
local petName = (PetFrame and PetFrame.name) or _G.PetName or playerName
MatchNameStyle(self.valueTexts.playerHealth, playerName)
MatchNameStyle(self.valueTexts.playerMana, playerName)
MatchNameStyle(self.valueTexts.playerAlternatePower, playerName)
MatchNameStyle(self.valueTexts.targetHealth, targetName)
MatchNameStyle(self.valueTexts.targetMana, targetName)
MatchNameStyle(self.valueTexts.petHealth, petName)
MatchNameStyle(self.valueTexts.petMana, petName)
end
local function SetValue(text, value)
@@ -129,12 +181,15 @@ module.enable = function(self)
return
end
SetNumberColor(text)
text:SetText(tostring(Abbreviate(value)))
text:Show()
end
local function UpdateHealth(text, unit, useEstimator)
if not text then return end
local percentText = text.percentText
if percentText then percentText:SetText(""); percentText:Hide() end
if not UnitExists(unit) then
text:SetText("")
@@ -164,12 +219,24 @@ module.enable = function(self)
end
if useEstimator and not known and max == 100 then
text:SetText(tostring(cur or 0) .. "%")
text:Show()
text:SetText("")
text:Hide()
if percentText then
percentText:SetText(tostring(cur or 0) .. "%")
percentText:Show()
end
return
end
SetValue(text, cur or 0)
local percent = math.floor(math.max(0, math.min(1, (cur or 0) / max)) * 100 + 0.5)
SetNumberColor(text)
text:SetText(tostring(Abbreviate(cur or 0)))
if percentText then
SetNumberColor(percentText)
percentText:SetText(percent .. "%")
percentText:Show()
end
text:Show()
end
local function UpdatePower(text, unit)
@@ -234,19 +301,19 @@ module.enable = function(self)
events:RegisterEvent("PLAYER_ENTERING_WORLD")
events:RegisterEvent("PLAYER_TARGET_CHANGED")
events:RegisterEvent("PET_UI_UPDATE")
events:RegisterEvent("UNIT_HEALTH")
events:RegisterEvent("UNIT_MAXHEALTH")
events:RegisterEvent("UNIT_MANA")
events:RegisterEvent("UNIT_MAXMANA")
events:RegisterEvent("UNIT_RAGE")
events:RegisterEvent("UNIT_MAXRAGE")
events:RegisterEvent("UNIT_ENERGY")
events:RegisterEvent("UNIT_MAXENERGY")
events:RegisterEvent("UNIT_FOCUS")
events:RegisterEvent("UNIT_MAXFOCUS")
events:RegisterEvent("UNIT_HAPPINESS")
events:RegisterEvent("UNIT_MAXHAPPINESS")
events:RegisterEvent("UNIT_DISPLAYPOWER")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_HEALTH", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_MAXHEALTH", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_MANA", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_MAXMANA", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_RAGE", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_MAXRAGE", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_ENERGY", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_MAXENERGY", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_FOCUS", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_MAXFOCUS", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_HAPPINESS", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_MAXHAPPINESS", "player", "target", "pet")
ShaguTweaks.API.RegisterUnitEvent(events, "UNIT_DISPLAYPOWER", "player", "target", "pet")
events:SetScript("OnEvent", function()
if event == "PLAYER_ENTERING_WORLD" then
@@ -296,3 +363,22 @@ module.enable = function(self)
UpdateAlternatePower()
end)
end
-- Kept separate from Modern Class Colors so either texture style can use white.
local whiteNumbers = ShaguTweaks:register({
title = T["White Unit Frame Numbers"],
description = T["Use white instead of the default gold for health, percentage and power numbers. Requires Real Health Numbers."],
expansions = { ["vanilla"] = true },
category = T["Unit Frames"],
enabled = nil,
})
whiteNumbers.enable = function(self)
ShaguTweaks.WhiteUnitFrameNumbers = true
-- Module initialization order is unspecified. Existing texts are recolored
-- here; texts created later read the flag through SetNumberColor.
for _, text in pairs(module.valueTexts or {}) do
SetNumberColor(text)
if text.percentText then SetNumberColor(text.percentText) end
end
end
+393
View File
@@ -0,0 +1,393 @@
-- Loot History: a window with the recent group-loot rolls and who won them.
-- Ported from brues-code/pfUI's loothistory module (MIT, Shagu and
-- contributors), rebuilt with stock 1.12 widgets for the classic look.
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local module = ShaguTweaks:register({
title = T["Loot History"],
description = T["Adds /loothistory: a window with the need, greed and pass rolls of recent group loot and who won each item. Requires ClassicAPI."],
expansions = { ["vanilla"] = true },
category = T["Loot"],
enabled = true,
})
local WIDTH, HEIGHT = 340, 400
local ROW_H, ROWS = 20, 16
local PLAYER_INDENT = 22
-- rollType from C_LootHistory.GetPlayerInfo: 0 pass, 1 need, 2 greed.
local ROLL_PASS, ROLL_NEED, ROLL_GREED = 0, 1, 2
local ROLL_TEX = {
[ROLL_NEED] = "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
[ROLL_GREED] = "Interface\\Buttons\\UI-GroupLoot-Coin-Up",
[ROLL_PASS] = "Interface\\Buttons\\UI-GroupLoot-Pass-Up",
}
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
local function Supported()
local lh = _G.C_LootHistory
return type(lh) == "table" and type(lh.GetNumItems) == "function"
and type(lh.GetItem) == "function" and type(lh.GetPlayerInfo) == "function"
end
-- name, quality and icon of an item link; nil while it is not cached yet.
local function ItemVisual(link)
local name, quality, texture, _
if _G.C_Item and type(_G.C_Item.GetItemInfo) == "function" then
name, _, quality, _, _, _, _, _, _, texture = _G.C_Item.GetItemInfo(link)
else
name, _, quality, _, _, _, _, _, texture = _G.GetItemInfo(link)
end
return name, quality, texture
end
local function QualityColor(quality)
if quality and GetItemQualityColor then
local r, g, b = GetItemQualityColor(quality)
if r then return r, g, b end
end
local c = quality and ITEM_QUALITY_COLORS and ITEM_QUALITY_COLORS[quality]
if c then return c.r, c.g, c.b end
return 1, 1, 1
end
local function ClassColor(class)
local c = class and RAID_CLASS_COLORS and RAID_CLASS_COLORS[class]
if c then return c.r, c.g, c.b end
return .8, .8, .8
end
-- While a roll is open everyone is listed. Once decided, only players who
-- rolled and yourself stay, as in the later clients' loot history.
local function ShouldDisplayPlayer(isDone, roll, isMe)
return isMe or (roll and roll > 0) or not isDone
end
local frame, scroll, rows, empty
local expanded = {} -- keyed on the stable rollID
local list = {}
local pendingItems
local Update
local function ToggleExpanded(rollID)
if not rollID then return end
expanded[rollID] = not expanded[rollID]
Update()
end
local function BuildList()
local lh = _G.C_LootHistory
local n = 0
for i = 1, lh.GetNumItems() do
local rollID, _, numPlayers, isDone = lh.GetItem(i)
n = n + 1
list[n] = list[n] or {}
list[n].item, list[n].player = i, nil
if rollID and expanded[rollID] then
for p = 1, numPlayers or 0 do
local _, _, _, roll, _, isMe = lh.GetPlayerInfo(i, p)
if ShouldDisplayPlayer(isDone, roll, isMe) then
n = n + 1
list[n] = list[n] or {}
list[n].item, list[n].player = i, p
end
end
end
end
for i = n + 1, table.getn(list) do list[i] = nil end
return n
end
local function ShowItemRow(row, index)
local lh = _G.C_LootHistory
local rollID, link, numPlayers, isDone, winner = lh.GetItem(index)
row.rollID, row.link = rollID, link
row.player:Hide()
row.itemrow:Show()
local r = row.itemrow
if numPlayers and numPlayers > 0 then
if expanded[rollID] then
r.toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
r.toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
else
r.toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
r.toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
end
r.toggle:Show()
else
r.toggle:Hide()
end
local name, quality, texture
if link then name, quality, texture = ItemVisual(link) end
if name then
r.icon:SetTexture(texture or QUESTIONMARK)
r.name:SetText(name)
r.name:SetTextColor(QualityColor(quality))
else
pendingItems = true
r.icon:SetTexture(QUESTIONMARK)
r.name:SetText(T["Retrieving item information..."])
r.name:SetTextColor(1, .3, .3)
end
-- winner summary on a decided, collapsed row
if isDone and not expanded[rollID] then
if winner then
local wname, wclass, wtype, wroll = lh.GetPlayerInfo(index, winner)
r.winicon:SetTexture(ROLL_TEX[wtype] or ROLL_TEX[ROLL_NEED])
r.winroll:SetText(wroll and wroll > 0 and tostring(wroll) or "")
r.winname:SetText(wname or UNKNOWN)
r.winname:SetTextColor(ClassColor(wclass))
else
r.winicon:SetTexture(ROLL_TEX[ROLL_PASS])
r.winroll:SetText("")
r.winname:SetText(T["All players passed"])
r.winname:SetTextColor(1, .4, .4)
end
r.winicon:Show()
r.winroll:Show()
r.winname:Show()
else
r.winicon:Hide()
r.winroll:Hide()
r.winname:Hide()
end
end
local function ShowPlayerRow(row, index, player)
local name, class, rollType, roll, isWinner = _G.C_LootHistory.GetPlayerInfo(index, player)
row.rollID, row.link = nil, nil
row.itemrow:Hide()
row.player:Show()
local p = row.player
p.name:SetText(name or UNKNOWN)
p.name:SetTextColor(ClassColor(class))
if rollType and ROLL_TEX[rollType] then
p.rollicon:SetTexture(ROLL_TEX[rollType])
p.rollicon:Show()
else
-- not voted yet
p.rollicon:Hide()
end
p.rolltext:SetText(roll and roll > 0 and tostring(roll) or "")
if isWinner then p.winmark:Show() else p.winmark:Hide() end
end
Update = function()
if not frame or not frame:IsShown() then return end
pendingItems = nil
local total = BuildList()
FauxScrollFrame_Update(scroll, total, ROWS, ROW_H)
local offset = FauxScrollFrame_GetOffset(scroll) or 0
for i = 1, ROWS do
local row = rows[i]
local entry = list[i + offset]
if entry then
if entry.player then
ShowPlayerRow(row, entry.item, entry.player)
else
ShowItemRow(row, entry.item)
end
row:Show()
else
row.rollID, row.link = nil, nil
row:Hide()
end
end
if total == 0 then empty:Show() else empty:Hide() end
end
local function CreateRow(parent, i)
local row = CreateFrame("Button", nil, parent)
row:SetHeight(ROW_H)
row:SetPoint("TOPLEFT", scroll, "TOPLEFT", 0, -(i - 1) * ROW_H)
row:SetPoint("RIGHT", scroll, "RIGHT", 0, 0)
local highlight = row:CreateTexture(nil, "HIGHLIGHT")
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(row)
highlight:SetAlpha(.5)
-- item row parts
local r = CreateFrame("Frame", nil, row)
r:SetAllPoints(row)
row.itemrow = r
r.toggle = CreateFrame("Button", nil, r)
r.toggle:SetWidth(14)
r.toggle:SetHeight(14)
r.toggle:SetPoint("LEFT", 2, 0)
r.toggle:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight", "ADD")
r.toggle:SetScript("OnClick", function() ToggleExpanded(row.rollID) end)
r.icon = r:CreateTexture(nil, "ARTWORK")
r.icon:SetWidth(16)
r.icon:SetHeight(16)
r.icon:SetPoint("LEFT", 20, 0)
r.icon:SetTexCoord(.08, .92, .08, .92)
r.winicon = r:CreateTexture(nil, "OVERLAY")
r.winicon:SetWidth(16)
r.winicon:SetHeight(16)
r.winicon:SetPoint("RIGHT", r, "RIGHT", -2, 0)
r.winroll = r:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
r.winroll:SetPoint("RIGHT", r.winicon, "LEFT", -2, 0)
r.winname = r:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
r.winname:SetPoint("RIGHT", r.winroll, "LEFT", -4, 0)
r.winname:SetWidth(90)
r.winname:SetJustifyH("RIGHT")
r.name = r:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
r.name:SetPoint("LEFT", r.icon, "RIGHT", 5, 0)
r.name:SetPoint("RIGHT", r, "RIGHT", -120, 0)
r.name:SetHeight(ROW_H)
r.name:SetJustifyH("LEFT")
-- player row parts
local p = CreateFrame("Frame", nil, row)
p:SetAllPoints(row)
row.player = p
p.winmark = p:CreateTexture(nil, "OVERLAY")
p.winmark:SetWidth(16)
p.winmark:SetHeight(16)
p.winmark:SetTexture(WINMARK)
p.winmark:SetPoint("LEFT", PLAYER_INDENT, 0)
p.name = p:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
p.name:SetPoint("LEFT", PLAYER_INDENT + 18, 0)
p.name:SetJustifyH("LEFT")
p.rollicon = p:CreateTexture(nil, "OVERLAY")
p.rollicon:SetWidth(16)
p.rollicon:SetHeight(16)
p.rollicon:SetPoint("RIGHT", p, "RIGHT", -2, 0)
p.rolltext = p:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
p.rolltext:SetPoint("RIGHT", p.rollicon, "LEFT", -3, 0)
row:SetScript("OnClick", function() ToggleExpanded(this.rollID) end)
row:SetScript("OnEnter", function()
if this.link then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetHyperlink(this.link)
GameTooltip:Show()
end
end)
row:SetScript("OnLeave", function() GameTooltip:Hide() end)
return row
end
local function CreateWindow()
frame = CreateFrame("Frame", "ShaguTweaksLootHistory", UIParent)
frame:SetFrameStrata("DIALOG")
frame:SetWidth(WIDTH)
frame:SetHeight(HEIGHT)
frame:SetPoint("CENTER", 0, 0)
frame:SetMovable(true)
frame:SetClampedToScreen(true)
frame:EnableMouse(true)
frame:RegisterForDrag("LeftButton")
frame:SetScript("OnDragStart", function() this:StartMoving() end)
frame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
frame:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true, tileSize = 32, edgeSize = 32,
insets = { left = 11, right = 12, top = 12, bottom = 11 },
})
frame:Hide()
table.insert(UISpecialFrames, "ShaguTweaksLootHistory")
local header = frame:CreateTexture(nil, "ARTWORK")
header:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
header:SetWidth(256)
header:SetHeight(64)
header:SetPoint("TOP", 0, 12)
local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
title:SetPoint("TOP", header, "TOP", 0, -14)
title:SetText(T["Loot History"])
local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
close:SetPoint("TOPRIGHT", -5, -5)
local clear = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
clear:SetWidth(80)
clear:SetHeight(22)
clear:SetPoint("BOTTOMRIGHT", -16, 14)
clear:SetText(T["Clear"])
clear:SetScript("OnClick", function()
if type(_G.C_LootHistory.Clear) == "function" then
_G.C_LootHistory.Clear()
end
Update()
end)
if type(_G.C_LootHistory.Clear) ~= "function" then clear:Hide() end
scroll = CreateFrame("ScrollFrame", "ShaguTweaksLootHistoryScroll", frame, "FauxScrollFrameTemplate")
scroll:SetPoint("TOPLEFT", 18, -32)
scroll:SetPoint("BOTTOMRIGHT", -40, 44)
scroll:SetScript("OnVerticalScroll", function()
FauxScrollFrame_OnVerticalScroll(ROW_H, Update)
end)
rows = {}
for i = 1, ROWS do rows[i] = CreateRow(frame, i) end
empty = frame:CreateFontString(nil, "OVERLAY", "GameFontDisable")
empty:SetPoint("CENTER", 0, 0)
empty:SetText(T["No loot rolls yet."])
frame:SetScript("OnShow", Update)
-- Only redraw while the window is open; the history itself is kept by
-- ClassicAPI (last 128 items, through /reload, cleared at logout).
frame:RegisterEvent("LOOT_HISTORY_FULL_UPDATE")
frame:RegisterEvent("LOOT_HISTORY_ROLL_COMPLETE")
frame:RegisterEvent("LOOT_HISTORY_ROLL_CHANGED")
frame:RegisterEvent("GET_ITEM_INFO_RECEIVED")
frame:SetScript("OnEvent", function()
if event == "GET_ITEM_INFO_RECEIVED" and not pendingItems then return end
Update()
end)
end
local function Toggle()
if not Supported() then
DEFAULT_CHAT_FRAME:AddMessage("|cffffcc00ShaguTweaks:|r " .. T["Loot History requires ClassicAPI."])
return
end
if not frame then CreateWindow() end
if frame:IsShown() then frame:Hide() else frame:Show() end
end
module.enable = function(self)
-- pfUI ships the same command; leave it alone when pfUI already owns it.
if SlashCmdList["PFLOOTHISTORY"] then return end
SLASH_SHAGULOOTHISTORY1 = "/loothistory"
SlashCmdList["SHAGULOOTHISTORY"] = Toggle
ShaguTweaks.ToggleLootHistory = Toggle
end
+9 -3
View File
@@ -124,8 +124,9 @@ module.enable = function(self)
ShaguTweaks.ScheduleMinimapClamp()
-- BuffFrame is anchored to UIParent TOPRIGHT/TOPRIGHT x=-205 y=-13 by vanilla.
-- The C engine can restore these anchors after aura layout updates, so keep a
-- lightweight per-frame verifier and only write when an anchor actually moved.
-- The stock BuffFrame_Enchant_OnUpdate re-anchors BuffFrame on every frame,
-- so keep a lightweight per-frame verifier and only write when an anchor
-- actually moved.
local extra = MinimapCluster:GetWidth() * (scale - 1.0)
local targetX = -205 - extra
local targetY = -13
@@ -140,8 +141,13 @@ module.enable = function(self)
-- Reuse a single verifier if enable() is called again. This prevents stacked
-- OnUpdate frames from fighting over the same anchors.
-- The verifier is a child of BuffFrame, so it only runs while the stock
-- buffs are shown. Buff addons such as VCB hide BuffFrame and
-- TemporaryEnchantFrame; the stock per-frame re-anchoring
-- (BuffFrame_Enchant_OnUpdate on TemporaryEnchantFrame) stops with them,
-- and so does this check.
if not self.enforcer then
self.enforcer = CreateFrame("Frame")
self.enforcer = CreateFrame("Frame", nil, BuffFrame)
end
local enforcer = self.enforcer
+2 -2
View File
@@ -8,7 +8,7 @@ local API = ShaguTweaks.API
local module = ShaguTweaks:register({
title = T["Movable Unit Frames"],
description = T["Player, Target, Party, Minimap, Clock, Timer, Buffs, Weapon Buffs and Debuffs can be moved while <Shift> and <Ctrl> are pressed together. Right-click a frame to reset its position."],
description = T["Player, Target, Party, Minimap, Clock, Timer, Buffs, Weapon Buffs and Debuffs can be moved while <Ctrl>, <Shift> and <Alt> are pressed together. Right-click a frame to reset its position."],
expansions = { ["vanilla"] = true },
category = T["Unit Frames"],
enabled = true,
@@ -631,7 +631,7 @@ module.enable = function(self)
end
local function UpdateLockState()
if API.IsShiftKeyDown() and API.IsControlKeyDown() then
if API.IsShiftKeyDown() and API.IsControlKeyDown() and API.IsAltKeyDown() then
UnlockAll()
else
LockAll()
+2 -46
View File
@@ -10,7 +10,6 @@ local GetExpansion = ShaguTweaks.GetExpansion
local cmatch = ShaguTweaks.cmatch
local rgbhex = ShaguTweaks.rgbhex
local friendinfo = gsub(gsub(FRIENDS_LEVEL_TEMPLATE,"%%s","%%s %%s"),"%%d","%%s")
local PLAYER_CACHE_MAX_AGE = 180 * 24 * 60 * 60
local function TouchPlayer(entry)
if not entry then return end
@@ -56,43 +55,6 @@ local function GetCachedFriendDisplay(data, name)
return cname, clevel
end
local function CleanupPlayerDB(playerdb, realmdb)
if not time then return end
local now = time()
local lastCleanup = tonumber(realmdb.players_cleanup) or 0
-- Start the 180-day grace period without touching existing entries.
if lastCleanup == 0 then
realmdb.players_cleanup = now
return
end
if now - lastCleanup < PLAYER_CACHE_MAX_AGE then return end
for name, data in pairs(playerdb) do
if type(data) ~= "table" then
playerdb[name] = nil
else
-- The shared cache also contains libunitscan-only entries. Prefer the
-- numeric Social Colors timestamp, then the passive scanner timestamp.
-- Entries without either timestamp have survived a full grace period and
-- can safely expire just like the old per-character cache did.
local lastSeen = type(data.lastseen) == "number" and data.lastseen or nil
local lastSeenTs = tonumber(data.lastseen_ts)
if lastSeenTs and (not lastSeen or lastSeenTs > lastSeen) then
lastSeen = lastSeenTs
end
if not lastSeen or now - lastSeen >= PLAYER_CACHE_MAX_AGE then
playerdb[name] = nil
end
end
end
realmdb.players_cleanup = now
end
local module = ShaguTweaks:register({
title = T["Social Colors"],
description = T["Show class colors in Who, Guild, Friends and Chat."],
@@ -182,16 +144,10 @@ module.enable = function(self)
HookChatColors()
local socialmod = CreateFrame("Frame", "ShaguTweaksSocialMod", UIParent)
socialmod:RegisterEvent("PLAYER_ENTERING_WORLD")
-- Cache expiry lives in libs/libunitscan.lua, which runs whether or not
-- this module is enabled.
socialmod:RegisterEvent("CHAT_MSG_SYSTEM")
socialmod:SetScript("OnEvent", function()
if event == "PLAYER_ENTERING_WORLD" then
local playerdb, realmdb = GetPlayerCache()
CleanupPlayerDB(playerdb, realmdb)
this:UnregisterEvent("PLAYER_ENTERING_WORLD")
return
end
local currentdb = GetPlayerCache()
local name = cmatch(arg1, _G.ERR_FRIEND_ONLINE_SS) or cmatch(arg1, _G.ERR_FRIEND_OFFLINE_S)
if name and currentdb[name] then
+570
View File
@@ -0,0 +1,570 @@
-- Swing Timer and Wand Timer
--
-- Ported from brues-code/pfUI's swingtimer module (MIT, Shagu and
-- contributors) and restyled as classic cast bars with the weapon icon.
-- ClassicAPI's PLAYER_SWING event reports the server's attack timers
-- (white hits, Heroic Strike/Maul, parry haste, extra attacks, Auto Shot
-- and wand Shoot), so nothing here guesses from the combat log.
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API or {}
local swing = ShaguTweaks:register({
title = T["Swing Timer"],
description = T["Shows main-hand and off-hand swing timers above the cast bar, with the weapon's icon. Hold <Ctrl>, <Shift> and <Alt> to move them; right-click to reset."],
expansions = { ["vanilla"] = true },
category = T["Action Bar"],
enabled = false,
})
local wand = ShaguTweaks:register({
title = T["Wand Timer"],
description = T["Shows the time until your next wand shot (or Auto Shot) above the cast bar, with the weapon's icon."],
expansions = { ["vanilla"] = true },
category = T["Action Bar"],
enabled = false,
})
local WIDTH, HEIGHT = 195, 13
local SPACING = 24 -- one bar plus its cast bar border
local GRACE = .15 -- bridge the frame or two until the next PLAYER_SWING
local THROTTLE = .02
local AUTOSHOT_WINDUP = .5
local COLORS = {
mainhand = { 1, .7, 0 }, -- the cast bar's own gold
offhand = { 0, 1, 0 }, -- the channel bar's green
ranged = { .3, .6, 1 },
far = { .5, .5, .5 }, -- target out of range
windup = { 1, 0, 0 }, -- Auto Shot wind-up: don't move
-- queued on-next-swing abilities, on the main-hand bar
hs = { .4, .8, 1 }, -- Heroic Strike: light blue (gold would hide pfUI's yellow)
cleave = { .2, .9, .2 }, -- Cleave: green, as in pfUI
maul = { 1, .55, 0 }, -- Maul: orange, as in pfUI
}
-- Rank 1 spell IDs; every rank shares the localized name.
local QUEUE_SPELLS = { hs = 78, cleave = 845, maul = 6807 }
local SLOTS = { mainhand = 16, offhand = 17, ranged = 18 }
local ORDER = { "mainhand", "offhand", "ranged" }
local PREVIEW = { mainhand = T["Main Hand"], offhand = T["Off Hand"], ranged = T["Ranged"] }
-- Everything lives in one table: Lua 5.0 allows only 32 upvalues per function.
local S = {
melee = false, ranged = false,
autoattack = false, autorepeat = false, combat = false,
hunter = false, preview = false, elapsed = 0,
far = {},
bars = {},
-- Heroic Strike / Cleave / Maul queue
queue = false, -- this class can queue one of them
queued = nil, -- "hs", "cleave", "maul" or nil
queuedSeen = false, -- its action button has been seen lit
queueNames = {}, -- localized name -> kind
queueSlots = {}, -- kind -> action slots holding it
nextMelee = {}, -- spellID -> kind or false
}
-- Classify a spell ID as "hs", "cleave", "maul" or nil.
local function QueueKind(spellID)
if not spellID or spellID <= 0 then return end
local known = S.nextMelee[spellID]
if known ~= nil then return known or nil end
local kind = false
if _G.C_Spell.IsNextMeleeSpell(spellID) then
kind = S.queueNames[_G.C_Spell.GetSpellName(spellID) or ""] or false
end
S.nextMelee[spellID] = kind
return kind or nil
end
-- Find the action buttons that hold each ability. A queued ability lights
-- its button (IsCurrentAction); Esc or a second press un-queues it without
-- any event, so the lit buttons are how a cancel is noticed.
local function ScanQueueSlots()
S.queueSlots = { hs = {}, cleave = {}, maul = {} }
if not S.queue then return end
for slot = 1, 120 do
local kind, id = GetActionInfo(slot)
local name
if kind == "spell" then
name = _G.C_Spell.GetSpellName(id)
elseif kind == "macro" then
name = GetMacroSpell(id)
end
local queueKind = name and S.queueNames[name]
if queueKind then table.insert(S.queueSlots[queueKind], slot) end
end
end
local function ClearQueue()
S.queued = nil
S.queuedSeen = false
end
-- Keep the queued color while a button of that ability is lit. Once seen
-- lit and then dark, it was cancelled. Never seen lit (cast from a macro or
-- keybind not on the bars): keep it until the spell goes off or fails.
local function ReconcileQueue()
if not S.queued then return end
local slots = S.queueSlots[S.queued]
if not slots then return end
for i = 1, table.getn(slots) do
if IsCurrentAction(slots[i]) then
S.queuedSeen = true
return
end
end
if S.queuedSeen then ClearQueue() end
end
local function Supported()
if not _G.C_SwingTimer or not _G.Enum or not _G.Enum.PlayerSwingType then return end
local utils = _G.C_EventUtils
if utils and utils.IsEventValid and not utils.IsEventValid("PLAYER_SWING") then return end
return true
end
local function ModifiersDown()
local shift = API.IsShiftKeyDown or IsShiftKeyDown
local ctrl = API.IsControlKeyDown or IsControlKeyDown
local alt = API.IsAltKeyDown or IsAltKeyDown
return shift() and ctrl() and alt()
end
local function HasOffhandWeapon()
local items = _G.C_Item
if items and items.GetItemInventoryType then
local invtype = items.GetItemInventoryType({ equipmentSlotIndex = SLOTS.offhand })
return invtype == 13 or invtype == 22 -- one-hand, off-hand weapon
end
local link = GetInventoryItemLink("player", SLOTS.offhand)
local _, _, id = link and string.find(link, "item:(%d+)")
if not id then return false end
local _, _, _, _, _, _, _, equip = GetItemInfo(tonumber(id))
return equip == "INVTYPE_WEAPON" or equip == "INVTYPE_WEAPONOFFHAND"
end
local function UpdateSpeeds()
local mh, oh = UnitAttackSpeed("player")
S.bars.mainhand.speed = (mh and mh > 0) and mh or S.bars.mainhand.speed or 0
S.bars.offhand.speed = (HasOffhandWeapon() and oh and oh > 0) and oh or 0
local ra = UnitRangedDamage("player")
S.bars.ranged.speed = (ra and ra > 0) and ra or 0
end
local function UpdateIcon(bar)
local texture = GetInventoryItemTexture("player", bar.slot)
if texture then
bar.icon.texture:SetTexture(texture)
bar.icon:Show()
else
bar.icon:Hide()
end
end
local function Wanted(bar)
if bar.key == "ranged" then return S.ranged end
return S.melee
end
-- Stack the shown bars upward from the container, in a fixed order.
local function Layout()
local slot = 0
for i = 1, 3 do
local bar = S.bars[ORDER[i]]
if bar:IsShown() then
bar:ClearAllPoints()
bar:SetPoint("BOTTOM", S.container, "BOTTOM", 0, slot * SPACING)
slot = slot + 1
end
end
end
local function SetProgress(bar, progress, r, g, b)
bar:SetValue(progress)
bar:SetStatusBarColor(r, g, b)
bar.spark:SetPoint("CENTER", bar, "LEFT", progress * WIDTH, 2)
end
local function StopBar(bar)
bar.active = false
bar.timer = 0
bar.graceAt = nil
if not S.preview then bar:Hide() end
end
local function StartBar(bar, remaining)
if not Wanted(bar) or S.preview then return end
UpdateSpeeds()
if bar.speed <= 0 then return end
-- remaining can be shorter than the weapon speed (parry haste, Auto Shot
-- wind-up): keep the full swing as the bar length and start part-way.
bar.max = math.max(bar.speed, remaining)
bar.timer = remaining
bar.graceAt = nil
bar.active = true
UpdateIcon(bar)
if not bar:IsShown() then
bar:Show()
Layout()
end
S.driver:Show()
end
local function ResetAll()
for i = 1, 3 do StopBar(S.bars[ORDER[i]]) end
Layout()
end
local function Tick(bar, delta, now)
if not bar.active then return end
bar.timer = bar.timer - delta
if bar.timer <= 0 then
bar.timer = 0
-- Keep a full bar while auto-attack or Shoot is still on (the target may
-- just be out of reach); hide it once the attacks stopped.
local running = bar.key == "ranged" and S.autorepeat or (bar.key ~= "ranged" and S.autoattack and S.combat)
if not bar.graceAt then
bar.graceAt = now + GRACE
elseif now >= bar.graceAt and not running then
StopBar(bar)
Layout()
return
end
end
local r, g, b = unpack(COLORS[bar.key])
if bar.key == "mainhand" and S.queued then
ReconcileQueue()
if S.queued then r, g, b = unpack(COLORS[S.queued]) end
end
if bar.key == "ranged" and S.hunter and bar.timer > 0 and bar.timer <= AUTOSHOT_WINDUP then
r, g, b = unpack(COLORS.windup)
end
if S.far[bar.swingType] then
r, g, b = unpack(COLORS.far)
end
SetProgress(bar, 1 - bar.timer / bar.max, r, g, b)
local text = string.format("%.1f", math.floor(bar.timer * 10) / 10)
if bar.lastText ~= text then
bar.lastText = text
bar.text:SetText(text)
end
end
local function OnUpdate()
S.elapsed = S.elapsed + arg1
if S.elapsed < THROTTLE then return end
local delta = S.elapsed
S.elapsed = 0
local now = GetTime()
local any = false
for i = 1, 3 do
local bar = S.bars[ORDER[i]]
Tick(bar, delta, now)
any = any or bar.active
end
if not any then this:Hide() end
end
-- Classic cast bar look: dark background, UI-StatusBar fill, the cast bar
-- border and spark, and an icon frame like Improved Castbar's.
local function CreateBar(key)
local bar = CreateFrame("StatusBar", "ShaguTweaksSwingTimer_" .. key, S.container)
bar:SetWidth(WIDTH)
bar:SetHeight(HEIGHT)
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetMinMaxValues(0, 1)
bar:SetValue(0)
bar:Hide()
bar.bg = bar:CreateTexture(nil, "BACKGROUND")
bar.bg:SetAllPoints(bar)
bar.bg:SetTexture(0, 0, 0, .5)
bar.border = bar:CreateTexture(nil, "ARTWORK")
bar.border:SetTexture("Interface\\CastingBar\\UI-CastingBar-Border")
bar.border:SetWidth(256)
bar.border:SetHeight(64)
bar.border:SetPoint("TOP", bar, "TOP", 0, 28)
bar.spark = bar:CreateTexture(nil, "OVERLAY")
bar.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
bar.spark:SetBlendMode("ADD")
bar.spark:SetWidth(32)
bar.spark:SetHeight(32)
bar.spark:SetPoint("CENTER", bar, "LEFT", 0, 2)
bar.text = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
bar.text:SetPoint("CENTER", bar, "CENTER", 0, 3)
bar.icon = CreateFrame("Frame", nil, bar)
bar.icon:SetPoint("RIGHT", bar, "LEFT", -10, 2)
bar.icon:SetWidth(28)
bar.icon:SetHeight(28)
bar.icon:SetBackdrop({
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 8, edgeSize = 12,
insets = { left = 2, right = 2, top = 2, bottom = 2 }
})
if ShaguTweaks.DarkMode then
bar.icon:SetBackdropBorderColor(.3, .3, .3, .9)
end
bar.icon.texture = bar.icon:CreateTexture(nil, "BACKGROUND")
bar.icon.texture:SetPoint("CENTER", 0, 0)
bar.icon.texture:SetWidth(24)
bar.icon.texture:SetHeight(24)
bar.icon.texture:SetTexCoord(.08, .92, .08, .92)
bar.key = key
bar.slot = SLOTS[key]
bar.swingType = _G.Enum.PlayerSwingType[key == "mainhand" and "MainHand" or key == "offhand" and "OffHand" or "Ranged"]
bar.speed, bar.timer, bar.max = 0, 0, 1
return bar
end
-- Hold Ctrl+Shift+Alt to show every enabled bar and drag them as one.
local function SetPreview(on)
if S.preview == on then return end
S.preview = on
S.container:EnableMouse(on)
for i = 1, 3 do
local bar = S.bars[ORDER[i]]
if on and Wanted(bar) then
UpdateIcon(bar)
local r, g, b = unpack(COLORS[bar.key])
SetProgress(bar, 1, r, g, b)
bar.text:SetText(PREVIEW[bar.key])
bar.lastText = nil
bar:Show()
elseif not bar.active then
bar:Hide()
end
end
if on then
S.container:SetHeight(HEIGHT + 2 * SPACING)
else
S.container:StopMovingOrSizing()
S.container:SetHeight(HEIGHT)
end
Layout()
end
local function RestorePosition()
local pos = ShaguTweaks_config and ShaguTweaks_config["SwingTimerPosition"]
S.container:ClearAllPoints()
if pos and pos[1] and pos[2] then
S.container:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", pos[1], pos[2])
else
-- default: stacked just above the cast bar
S.container:SetPoint("BOTTOM", CastingBarFrame, "TOP", 0, 20)
end
end
local function CreateCore()
if S.container then return true end
if not Supported() then return end
S.container = CreateFrame("Frame", "ShaguTweaksSwingTimer", UIParent)
S.container:SetWidth(WIDTH)
S.container:SetHeight(HEIGHT)
S.container:SetMovable(true)
S.container:SetClampedToScreen(true)
S.container:EnableMouse(false)
S.container:SetScript("OnMouseDown", function()
if arg1 == "LeftButton" then this:StartMoving() end
end)
S.container:SetScript("OnMouseUp", function()
this:StopMovingOrSizing()
if arg1 == "RightButton" then
ShaguTweaks_config["SwingTimerPosition"] = nil
else
ShaguTweaks_config["SwingTimerPosition"] = { this:GetLeft(), this:GetBottom() }
end
RestorePosition()
end)
for i = 1, 3 do S.bars[ORDER[i]] = CreateBar(ORDER[i]) end
S.driver = CreateFrame("Frame", nil, UIParent)
S.driver:Hide()
S.driver:SetScript("OnUpdate", OnUpdate)
S.events = CreateFrame("Frame")
S.events:SetScript("OnEvent", function()
if event == "PLAYER_SWING" then
local swingType = _G.Enum.PlayerSwingType
if arg2 == swingType.MainHand then
StartBar(S.bars.mainhand, arg1)
elseif arg2 == swingType.OffHand then
StartBar(S.bars.offhand, arg1)
elseif arg2 == swingType.Ranged then
StartBar(S.bars.ranged, arg1)
end
elseif event == "PLAYER_SWING_RANGE_UPDATE" then
-- arg1 swing type, arg2 in range, arg3 a check was possible
S.far[arg1] = arg3 and not arg2
elseif event == "PLAYER_TARGET_CHANGED" then
for i = 1, 3 do
local swingType = S.bars[ORDER[i]].swingType
S.far[swingType] = _G.C_SwingTimer.IsTargetWithinSwingRange(swingType) == false
end
elseif event == "PLAYER_ENTER_COMBAT" or event == "START_AUTOATTACK" then
S.autoattack = true
elseif event == "PLAYER_LEAVE_COMBAT" or event == "STOP_AUTOATTACK" then
S.autoattack = false
elseif event == "START_AUTOREPEAT_SPELL" then
S.autorepeat = true
elseif event == "STOP_AUTOREPEAT_SPELL" then
S.autorepeat = false
elseif event == "PLAYER_REGEN_DISABLED" then
S.combat = true
elseif event == "PLAYER_REGEN_ENABLED" then
S.combat = false
ClearQueue()
elseif event == "UNIT_SPELLCAST_SENT" then
-- arg1 unit, arg4 spellID: Heroic Strike, Cleave or Maul queued
if arg1 == "player" and S.queue then
local kind = QueueKind(arg4)
if kind then
S.queued = kind
S.queuedSeen = false
end
end
elseif event == "UNIT_SPELLCAST_SUCCEEDED" or event == "UNIT_SPELLCAST_FAILED" then
-- arg1 unit, arg3 spellID: the queued swing went off or was refused
if arg1 == "player" and S.queued and QueueKind(arg3) then
ClearQueue()
end
elseif event == "ACTIONBAR_SLOT_CHANGED" then
if S.queue then ScanQueueSlots() end
elseif event == "PLAYER_EQUIPMENT_CHANGED" or event == "UNIT_INVENTORY_CHANGED" then
if event == "PLAYER_EQUIPMENT_CHANGED" and arg1 ~= 16 and arg1 ~= 17 and arg1 ~= 18 then return end
UpdateSpeeds()
for i = 1, 3 do
local bar = S.bars[ORDER[i]]
UpdateIcon(bar)
if bar.active and bar.speed <= 0 then StopBar(bar) end
end
Layout()
elseif event == "MODIFIER_STATE_CHANGED" then
SetPreview(ModifiersDown() and true or false)
elseif event == "PLAYER_DEAD" then
ClearQueue()
ResetAll()
elseif event == "PLAYER_ENTERING_WORLD" then
local _, class = UnitClass("player")
S.hunter = class == "HUNTER"
UpdateSpeeds()
RestorePosition()
end
end)
local events = {
"PLAYER_SWING", "PLAYER_SWING_RANGE_UPDATE", "PLAYER_TARGET_CHANGED",
"PLAYER_ENTER_COMBAT", "PLAYER_LEAVE_COMBAT",
"START_AUTOREPEAT_SPELL", "STOP_AUTOREPEAT_SPELL",
"PLAYER_REGEN_DISABLED", "PLAYER_REGEN_ENABLED",
"PLAYER_DEAD", "PLAYER_ENTERING_WORLD", "MODIFIER_STATE_CHANGED",
-- ClassicAPI v1.18.14+; the 1.12 combat events above cover older versions
"START_AUTOATTACK", "STOP_AUTOATTACK",
}
local utils = _G.C_EventUtils
for i = 1, table.getn(events) do
if not utils or not utils.IsEventValid or utils.IsEventValid(events[i]) then
S.events:RegisterEvent(events[i])
end
end
-- ClassicAPI's per-slot equipment event when present, else the 1.12 one
if utils and utils.IsEventValid and utils.IsEventValid("PLAYER_EQUIPMENT_CHANGED") then
S.events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
else
S.events:RegisterEvent("UNIT_INVENTORY_CHANGED")
end
local _, class = UnitClass("player")
S.hunter = class == "HUNTER"
S.combat = UnitAffectingCombat("player") and true or false
UpdateSpeeds()
RestorePosition()
return true
end
-- The range event fires only on a change, so read the state once.
local function EnableRangeCheck(bar)
_G.C_SwingTimer.EnableRangeCheck(bar.swingType, true)
S.far[bar.swingType] = _G.C_SwingTimer.IsTargetWithinSwingRange(bar.swingType) == false
end
-- Heroic Strike / Cleave (warriors) and Maul (druids) queue colors.
local function EnableQueueColors()
local spell = _G.C_Spell
if not spell or not spell.IsNextMeleeSpell or not spell.GetSpellName or not GetActionInfo then return end
local _, class = UnitClass("player")
local kinds = class == "WARRIOR" and { "hs", "cleave" } or class == "DRUID" and { "maul" }
if not kinds then return end
for i = 1, table.getn(kinds) do
local name = spell.GetSpellName(QUEUE_SPELLS[kinds[i]])
if name then S.queueNames[name] = kinds[i] end
end
S.queue = true
ScanQueueSlots()
-- player-only where ClassicAPI can filter: SUCCEEDED fires for every unit
local events = S.events
local unitEvents = { "UNIT_SPELLCAST_SENT", "UNIT_SPELLCAST_SUCCEEDED", "UNIT_SPELLCAST_FAILED" }
for i = 1, table.getn(unitEvents) do
if events.RegisterUnitEvent then
events:RegisterUnitEvent(unitEvents[i], "player")
else
events:RegisterEvent(unitEvents[i])
end
end
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
end
swing.enable = function(self)
if not CreateCore() then return end
S.melee = true
EnableRangeCheck(S.bars.mainhand)
EnableRangeCheck(S.bars.offhand)
EnableQueueColors()
end
wand.enable = function(self)
if not CreateCore() then return end
S.ranged = true
EnableRangeCheck(S.bars.ranged)
end
+1 -1
View File
@@ -252,7 +252,7 @@ module.enable = function(self)
listener:RegisterEvent("PLAYER_TARGET_CHANGED")
for index = 1, table.getn(castEvents) do
listener:RegisterEvent(castEvents[index])
API.RegisterUnitEvent(listener, castEvents[index], "target")
end
listener:SetScript("OnEvent", function()
+8 -1
View File
@@ -202,8 +202,15 @@ module.enable = function(self)
local button = _G["TargetFrameDebuff"..i]
if button and not button.cd then
button.cd = CreateFrame("Model", "TargetFrameDebuff"..i.."Cooldown", button, "CooldownFrameTemplate")
local frameType = API.nativecooldowns and "Cooldown" or "Model"
button.cd = CreateFrame(frameType, "TargetFrameDebuff"..i.."Cooldown", button, "CooldownFrameTemplate")
button.cd.noCooldownCount = true
-- The timer text is drawn by the overlay below. A native Cooldown
-- would otherwise add a second set of numbers when the
-- countdownForCooldowns CVar is turned on.
if type(button.cd.SetHideCountdownNumbers) == "function" then
button.cd:SetHideCountdownNumbers(true)
end
button.cd:SetAllPoints()
button.cd:SetScale(.6)
button.cd:SetAlpha(.8)
+89 -20
View File
@@ -5,12 +5,35 @@ local rgbhex = ShaguTweaks.rgbhex
local Abbreviate = ShaguTweaks.Abbreviate
local API = ShaguTweaks.API
local DEFAULT_HEALTH_TEXTURE = "Interface\\TargetingFrame\\UI-StatusBar"
local MODERN_HEALTH_TEXTURE = "Interface\\AddOns\\ShaguTweaks\\img\\BlizzardRetailBar"
local function SetTooltipHealthTexture(texture)
GameTooltipStatusBar:SetStatusBarTexture(texture)
if GameTooltipStatusBar.bg then
GameTooltipStatusBar.bg:SetTexture(texture)
end
end
local tooltipSetStatusBarColor
local function GetUnit()
local guid = API.GetTooltipUnitGUID(GameTooltip)
if not guid then return end
return API.UnitTokenFromGUID(guid)
local unit = API.UnitTokenFromGUID(guid)
-- Positional tokens can be recycled between tooltip and unit queries.
if unit and API.UnitGUID and API.UnitGUID(unit) ~= guid then return end
return unit
end
local function GetTargetUnit(unit)
-- The interaction-only NPC token has no valid "npctarget" derivative.
if unit == "npc" then return end
local targetUnit = unit .. "target"
-- A valid base token need not support target suffixes. Probe defensively
-- before passing a derived token to the rest of the unit API.
local ok, exists = pcall(UnitExists, targetUnit)
if ok and exists then return targetUnit end
end
local function ClearTooltipBarColor()
@@ -55,6 +78,7 @@ end
local function ResetTooltipState()
ClearTooltipBarColor()
SetTooltipHealthTexture(DEFAULT_HEALTH_TEXTURE)
if GameTooltipStatusBar.bg then
GameTooltipStatusBar.bg:SetVertexColor(.1, .1, 0, .8)
@@ -68,29 +92,36 @@ end
local updating = false
local function UpdateTooltip()
if updating then return end
updating = true
local function UpdateTooltipContents()
-- ClassicAPI keeps the exact tooltip GUID and resolves it back to a live
-- unit token natively. No party/raid scans or display-name fallback.
local unit = GetUnit()
if not unit then
updating = false
ResetTooltipState()
return
end
-- Resolve at display time because module initialization order is unspecified.
SetTooltipHealthTexture(ShaguTweaks.ModernClassColors
and MODERN_HEALTH_TEXTURE or DEFAULT_HEALTH_TEXTURE)
-- A newly resolved unit chooses its own class/reaction color below. Clear the
-- previous enforcement first so a stale color cannot leak between tooltips.
ClearTooltipBarColor()
local pvpname = UnitPVPName(unit)
local name = UnitName(unit)
local target = UnitName(unit .. "target")
local _, targetClass = UnitClass(unit .. "target")
local targetReaction = UnitReaction("player", unit .. "target")
local targetUnit = GetTargetUnit(unit)
local target, targetClass, targetReaction
if targetUnit then
target = UnitName(targetUnit)
local ignored
ignored, targetClass = UnitClass(targetUnit)
targetReaction = UnitReaction("player", targetUnit)
end
local _, class = UnitClass(unit)
local guild = GetGuildInfo(unit)
local guild, rankstr, rankid = GetGuildInfo(unit)
local reaction = UnitReaction(unit, "player")
local pvptitle = name and gsub(pvpname or name, " " .. name, "", 1) or nil
@@ -98,9 +129,13 @@ local function UpdateTooltip()
if UnitIsPlayer(unit) and class then
local color = RAID_CLASS_COLORS[class]
if color then
SetTooltipBarColor(color.r, color.g, color.b, 1)
-- Match the optional unit-frame palette without changing name text
-- or NPC reaction colors. The health-bar background follows its tint.
local modern = ShaguTweaks.ModernClassColors
local barColor = (modern and modern[class]) or color
SetTooltipBarColor(barColor.r, barColor.g, barColor.b, 1)
GameTooltipStatusBar.bg:SetVertexColor(
color.r * 0.15, color.g * 0.15, color.b * 0.15, 0.8)
barColor.r * 0.15, barColor.g * 0.15, barColor.b * 0.15, 0.8)
GameTooltipTextLeft1:SetText(
rgbhex(color.r, color.g, color.b, color.a) .. name)
else
@@ -120,18 +155,35 @@ local function UpdateTooltip()
end
end
-- Guild name only (no rank)
if guild then
local guildAlready = false
-- Restore the original guild rank and guild-leader marker. Replace the
-- native/existing guild line so repeated refreshes do not duplicate it.
if guild and guild ~= "" then
local guildName = "<" .. guild .. ">"
local guildText = guildName
if rankid == 0 then guildText = guildText .. "|cffffcc00*|r" end
if rankstr and rankstr ~= "" then
guildText = guildText .. " |cffaaaaaa(" .. rankstr .. ")|r"
end
local guildLine
for i = 2, GameTooltip:NumLines() do
local left = _G["GameTooltipTextLeft" .. i]
if left and left:GetText() == "<" .. guild .. ">" then
guildAlready = true
break
local text = left and left:GetText()
if text then
local plain = string.gsub(text, "|c%x%x%x%x%x%x%x%x", "")
plain = string.gsub(plain, "|r", "")
if plain == guild or string.sub(plain, 1, string.len(guildName)) == guildName then
guildLine = left
break
end
end
end
if not guildAlready then
GameTooltip:AddLine("<" .. guild .. ">", 0.3, 1, 0.5)
if guildLine then
guildLine:SetText(guildText)
guildLine:SetTextColor(0.3, 1, 0.5)
else
GameTooltip:AddLine(guildText, 0.3, 1, 0.5)
end
end
@@ -148,7 +200,7 @@ local function UpdateTooltip()
end
if target and not alreadyAdded then
if UnitIsPlayer(unit .. "target") and targetClass then
if UnitIsPlayer(targetUnit) and targetClass then
local color = RAID_CLASS_COLORS[targetClass]
if color then
GameTooltip:AddLine(target, color.r, color.g, color.b)
@@ -167,7 +219,24 @@ local function UpdateTooltip()
UpdateHealthText()
GameTooltip:Show()
end
local function UpdateTooltip()
if updating then return end
updating = true
local ok, err = pcall(UpdateTooltipContents)
-- Always release the guard before cleanup/error reporting. One bad unit or
-- addon hook must not disable tooltip updates for the rest of the session.
updating = false
if not ok then
ResetTooltipState()
local handler = geterrorhandler and geterrorhandler()
if type(handler) == "function" then
handler(err)
else
error(err)
end
end
end
local module = ShaguTweaks:register({
+7 -1
View File
@@ -45,7 +45,8 @@ module.enable = function(self)
WorldMapFrame:SetMovable(true)
WorldMapFrame:EnableMouse(true)
WorldMapFrame:SetScale(.85)
-- Keep the size picked with Ctrl + Mousewheel (saved by WorldMap Window).
WorldMapFrame:SetScale(ShaguTweaks.GetWorldMapScale and ShaguTweaks.GetWorldMapScale() or .85)
WorldMapFrame:ClearAllPoints()
WorldMapFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 30)
WorldMapFrame:SetWidth(WorldMapButton:GetWidth() + 15)
@@ -54,6 +55,11 @@ module.enable = function(self)
-- overwrite wrong title position set by turtlewow
WorldMapFrameTitle:SetPoint("TOP", WorldMapFrame, 0, 17)
-- The title now sits above the frame: let the title bar drag it too.
if ShaguTweaks.UpdateWorldMapDragArea then
ShaguTweaks.UpdateWorldMapDragArea()
end
BlackoutWorld:Hide()
end
end
+34
View File
@@ -62,6 +62,40 @@ module.enable = function(self)
PlayerStatusTexture:SetTexture(addonpath .. "\\img\\UI-Player-Status")
-- The additive status artwork contains both the portrait ring and bar edge.
-- Crop only the latter while resting, shrinking the displayed width by the
-- same ratio so the portrait ring keeps its original size and position.
-- Native status code continues to own visibility, tint, alpha and pulsing.
local statusCoords = {PlayerStatusTexture:GetTexCoord()}
local statusWidth = PlayerStatusTexture:GetWidth()
local portraitRight = 72 / 256 -- right edge of the portrait in UI-Player-Status
local restingCrop = false
local function UpdateRestingGlow()
local resting = IsResting()
and not UnitAffectingCombat("player")
local left, right = statusCoords[1], statusCoords[5]
if not left or not right or right <= left
or portraitRight <= left or portraitRight >= right then return end
if resting and not restingCrop then
PlayerStatusTexture:SetTexCoord(
left, portraitRight, statusCoords[2], statusCoords[4])
PlayerStatusTexture:SetWidth(
statusWidth * (portraitRight - left) / (right - left))
restingCrop = true
elseif not resting and restingCrop then
PlayerStatusTexture:SetTexCoord(
left, right, statusCoords[2], statusCoords[4])
PlayerStatusTexture:SetWidth(statusWidth)
restingCrop = false
end
end
hooksecurefunc("PlayerFrame_UpdateStatus", UpdateRestingGlow)
UpdateRestingGlow()
TargetFrameTexture:SetTexture(normalTexture)
TargetFrameHealthBar:SetPoint("TOPRIGHT", -106, -22)
TargetFrameHealthBar:SetHeight(30)
+141 -1
View File
@@ -14,7 +14,8 @@ local module = ShaguTweaks:register({
local function GetUnitClassColor(unit)
local _, class = UnitClass(unit)
return RAID_CLASS_COLORS[class] or FALLBACK_CLASS_COLOR
local palette = ShaguTweaks.ModernClassColors or RAID_CLASS_COLORS
return palette[class] or FALLBACK_CLASS_COLOR
end
local function SetTextColorIfChanged(fontString, r, g, b, a)
@@ -98,3 +99,142 @@ module.enable = function(self)
UpdateTargetClassColor()
UpdatePartyColors()
end
-- Retail resource artwork contains its own colors, including light-blue mana.
-- A white tint preserves those native colors and gradients.
local MODERN_POWER_TEXTURES = {
[0] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerMana",
[1] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerRage",
[2] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerFocus",
[3] = "Interface\\AddOns\\ShaguTweaks\\img\\ModernPowerEnergy",
}
local DEFAULT_BAR_TEXTURE = "Interface\\TargetingFrame\\UI-StatusBar"
local function SetupModernPowerBars()
local bars = {}
local function ApplyPowerStyle(bar)
local state = bars[bar]
if not state then return end
local unit = bar.unit or state.unit
if not ShaguTweaks.ModernClassColors or not UnitExists(unit) then
state.background:Hide()
return
end
-- The druid's secondary bar stays mana while the primary bar is rage/energy.
local powerType = state.powerType
if powerType == nil then powerType = UnitPowerType(unit) end
local texture = MODERN_POWER_TEXTURES[powerType]
if texture then
local suffix = state.family
if suffix == "Player" then
suffix = ""
local _, class = UnitClass(unit)
-- Keep the retail energy variant for combo-point users.
if powerType == 3 and (class == "ROGUE" or class == "DRUID") then
suffix = "Class"
end
end
-- Vanilla mana slots are rectangular. Retail target/small-frame fills
-- have cut-out corners that leave a visible wedge inside those slots.
-- Use the native rectangular mana artwork for every mana frame.
if powerType == 0 then suffix = "Class" end
bar:SetStatusBarTexture(texture .. suffix)
state.background:Show()
-- Call the method captured before our hook to avoid recursive updates.
state.setColor(bar, 1, 1, 1, 1)
else
-- Unknown/custom power types retain their original texture and tint.
bar:SetStatusBarTexture(state.originalTexture)
state.background:Hide()
end
end
local definitions = {
{"PlayerFrameManaBar", "player", "Player"},
{"PlayerFrameAlternatePowerBar", "player", "Player", 0},
{"TargetFrameManaBar", "target", "Target"},
{"PetFrameManaBar", "pet", "Small"},
{"TargetofTargetManaBar", "targettarget", "Small"},
}
for id = 1, MAX_PARTY_MEMBERS do
table.insert(definitions, {"PartyMemberFrame" .. id .. "ManaBar", "party" .. id, "Party"})
end
for _, definition in ipairs(definitions) do
local bar = _G[definition[1]]
if bar then
local texture = bar:GetStatusBarTexture()
-- Opaque black behind the native alpha edges prevents the old frame's
-- colored backing from changing the retail gradient's apparent color.
local background = bar:CreateTexture(nil, "BACKGROUND")
background:SetAllPoints(bar)
background:SetTexture(0, 0, 0)
background:Hide()
bars[bar] = {
unit = definition[2],
family = definition[3],
powerType = definition[4],
background = background,
setColor = bar.SetStatusBarColor,
originalTexture = (texture and texture:GetTexture()) or DEFAULT_BAR_TEXTURE,
}
hooksecurefunc(bar, "SetStatusBarColor", function() ApplyPowerStyle(bar) end)
end
end
local function RefreshPowerBars()
for bar in pairs(bars) do ApplyPowerStyle(bar) end
end
-- Vanilla refreshes power type here. Appending preserves the normal frame
-- updates, including shapeshifts and targets with a different power type.
hooksecurefunc("UnitFrame_UpdateManaType", RefreshPowerBars)
RefreshPowerBars()
return RefreshPowerBars
end
local modern = ShaguTweaks:register({
title = "Modern Class Colors",
description = "Use modern class colors and retail health/power textures. Enable Unit Frame Class Colors for class-colored unit frames.",
expansions = { ["vanilla"] = true },
category = T["Unit Frames"],
enabled = nil,
})
modern.enable = function(self)
if self.installed then return end
self.installed = true
ShaguTweaks.ModernClassColors = {
DRUID = {r=1, g=0.49, b=0.04},
HUNTER = {r=0.67, g=0.83, b=0.45},
MAGE = {r=0.25, g=0.78, b=0.92},
PALADIN = {r=0.96, g=0.55, b=0.73},
PRIEST = {r=1, g=1, b=1},
ROGUE = {r=1, g=0.96, b=0.41},
SHAMAN = {r=0, g=0.44, b=0.87},
WARLOCK = {r=0.53, g=0.53, b=0.93},
WARRIOR = {r=0.78, g=0.61, b=0.43},
}
local RefreshPowerBars = SetupModernPowerBars()
-- Refresh after every module has initialized, regardless of enable order.
local deferred = CreateFrame("Frame")
deferred:SetScript("OnUpdate", function()
this:SetScript("OnUpdate", nil)
this:Hide()
RefreshPowerBars()
for _, prefix in ipairs({"PlayerFrame", "TargetFrame", "PetFrame", "TargetofTarget", "PartyMemberFrame1", "PartyMemberFrame2", "PartyMemberFrame3", "PartyMemberFrame4"}) do
local bar = _G[prefix .. "HealthBar"]
if bar then bar:SetStatusBarTexture("Interface\\AddOns\\ShaguTweaks\\img\\BlizzardRetailBar") end
end
if not PlayerFrameNameBackground then return end
local color = GetUnitClassColor("player")
PlayerFrameNameBackground:SetVertexColor(color.r, color.g, color.b, 1)
if TargetFrame_CheckFaction then TargetFrame_CheckFaction() end
UpdatePartyColors()
-- Big Health reads its tint from the name background.
if PlayerFrameHealthBar then
local r, g, b, a = PlayerFrameHealthBar:GetStatusBarColor()
PlayerFrameHealthBar:SetStatusBarColor(r, g, b, a)
end
end)
end
+12 -1
View File
@@ -82,11 +82,22 @@ module.enable = function(self)
local lasttotguid = nil
if type(TargetofTarget_OnUpdate) == "function" then
local originalTargetofTargetOnUpdate = TargetofTarget_OnUpdate
-- The stock handler replaces the portrait when CURRENT_TARGETTARGET (the
-- target-of-target's name) changes; reapply the class icon in that same
-- frame. A GUID check five times a second covers same-name units and
-- clients whose handler does not track that global, instead of calling
-- UnitGUID on every frame.
local totelapsed = 0
TargetofTarget_OnUpdate = function(elapsed)
local before = CURRENT_TARGETTARGET
originalTargetofTargetOnUpdate(elapsed)
totelapsed = totelapsed + (elapsed or arg1 or 0)
if CURRENT_TARGETTARGET == before and totelapsed < .2 then return end
totelapsed = 0
local guid = API.UnitGUID and API.UnitGUID("targettarget") or UnitName("targettarget")
if guid ~= lasttotguid then
if guid ~= lasttotguid or CURRENT_TARGETTARGET ~= before then
lasttotguid = guid
UpdatePortraits(TargetofTargetFrame)
end
+24
View File
@@ -0,0 +1,24 @@
local T = ShaguTweaks.T
local module = ShaguTweaks:register({
title = T["Hide Pet Unit Frame"],
description = T["Hides the Pet unitframe below the player in pet classes."],
expansions = { ["vanilla"] = true },
category = T["Unit Frames"],
enabled = false,
})
module.enable = function(self)
if not PetFrame then return end
-- PetFrame_Update shows the frame again whenever the pet changes, so a
-- plain Hide() does not last. Parent it to a frame that is never shown:
-- the stock pet code keeps running unchanged, and the frame, its bars and
-- its child overlays (such as Real Health Numbers' text) never draw.
if not self.hidden then
self.hidden = CreateFrame("Frame")
self.hidden:Hide()
end
PetFrame:SetParent(self.hidden)
end
+100 -73
View File
@@ -21,102 +21,129 @@ for _, name in pairs(tocs) do
end
local partyUnits = { "party1", "party2", "party3", "party4" }
local UPDATE_INTERVAL = .1
local function SetAllPointsOffset(frame, parent, offset)
frame:SetPoint("TOPLEFT", parent, "TOPLEFT", offset, -offset)
frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", -offset, offset)
end
local function IsMapVisible()
local worldmap = _G.WorldMapFrame
local battlefield = _G.BattlefieldMinimap
return (worldmap and worldmap:IsVisible()) or (battlefield and battlefield:IsVisible())
-- Icon names for one map ("WorldMap" or "BattlefieldMinimap"), each with the
-- unit the icon shows when the map does not say otherwise.
local function BuildButtons(prefix)
local buttons = {}
for i = 1, 4 do
table.insert(buttons, { prefix .. "Party" .. i, "party" .. i })
end
for i = 1, 40 do
table.insert(buttons, { prefix .. "Raid" .. i, "raid" .. i })
end
return buttons
end
local function UpdateWorldMapColors()
-- This module used to iterate up to 88 map buttons all the time, even when
-- both maps were closed. Keep the OnUpdate compatibility path, but make the
-- expensive work conditional on a visible map.
if not IsMapVisible() then return end
-- In a raid the map packs its raid icons: WorldMapRaid1 is the first raid
-- member who is on this map (yourself excluded), not raid1. The map stores
-- the real unit on the icon as .unit, and battleground team members outside
-- the raid only as .name. Using raidN for WorldMapRaidN showed other players'
-- class colors.
local function ResolveIcon(frame, fallback)
if frame.name then
return nil, frame.name
elseif frame.unit and UnitExists(frame.unit) then
return frame.unit, UnitName(frame.unit)
elseif not frame.unit and UnitExists(fallback) then
return fallback, UnitName(fallback)
end
end
-- throttle to one update per .1 second
if (this.tick or 1) > GetTime() then return else this.tick = GetTime() + .1 end
local function UpdateIcon(frame, fallback)
local unit, name = ResolveIcon(frame, fallback)
if not unit and not name then return end
local frame, icon
local icon = _G[frame:GetName() .. "Icon"]
if icon then icon:SetTexture() end
-- initialize all button names
if not this.buttons then
this.buttons = {}
for i = 1, 4 do
icon = string.format("WorldMapParty%d", i)
this.buttons[icon] = string.format("party%d", i)
end
for i = 1, 4 do
icon = string.format("BattlefieldMinimapParty%d", i)
this.buttons[icon] = string.format("party%d", i)
end
for i = 1, 40 do
icon = string.format("BattlefieldMinimapRaid%d", i)
this.buttons[icon] = string.format("raid%d", i)
end
for i = 1, 40 do
icon = string.format("WorldMapRaid%d", i)
this.buttons[icon] = string.format("raid%d", i)
end
-- create icon if not yet existing
if not frame.texture then
frame.texture = frame:CreateTexture(nil, "OVERLAY")
SetAllPointsOffset(frame.texture, frame, 12)
end
-- update all available buttons
-- check if unit is in same party group
local ingroup
for name, unitstr in pairs(this.buttons) do
frame = _G[name]
for i = 1, 4 do
if UnitName(partyUnits[i]) == name then
ingroup = true
break
end
end
if frame and UnitExists(unitstr) then
icon = _G[name.."Icon"]
if icon then icon:SetTexture() end
-- update texture according to raid/group state
if ingroup and frame.texture.ingroup ~= "PARTY" then
frame.texture:SetTexture(addonpath .. "\\img\\circleparty")
frame.texture.ingroup = "PARTY"
elseif not ingroup and frame.texture.ingroup ~= "RAID" then
frame.texture:SetTexture(addonpath .. "\\img\\circleraid")
frame.texture.ingroup = "RAID"
end
-- create icon if not yet existing
if not frame.texture then
frame.texture = frame:CreateTexture(nil, "OVERLAY")
SetAllPointsOffset(frame.texture, frame, 12)
end
-- detect unit class and set color
local class
if unit then
local _, token = UnitClass(unit)
class = token
elseif name and ShaguTweaks.GetUnitData then
class = ShaguTweaks.GetUnitData(name)
end
-- check if unit is in same party group
local unitName = UnitName(unitstr)
ingroup = nil
for i=1,4 do
if UnitName(partyUnits[i]) == unitName then
ingroup = true
break
end
end
local color = class and RAID_CLASS_COLORS[class]
if color then
frame.texture:SetVertexColor(color.r, color.g, color.b)
else
frame.texture:SetVertexColor(.5,1,.5)
end
end
-- update texture according to raid/group state
if ingroup and frame.texture.ingroup ~= "PARTY" then
frame.texture:SetTexture(addonpath .. "\\img\\circleparty")
frame.texture.ingroup = "PARTY"
elseif not ingroup and frame.texture.ingroup ~= "RAID" then
frame.texture:SetTexture(addonpath .. "\\img\\circleraid")
frame.texture.ingroup = "RAID"
end
local function UpdateMapColors()
this.elapsed = (this.elapsed or UPDATE_INTERVAL) + arg1
if this.elapsed < UPDATE_INTERVAL then return end
this.elapsed = 0
-- detect unit class and set color
local _, class = UnitClass(unitstr)
local color = RAID_CLASS_COLORS[class]
if color then
frame.texture:SetVertexColor(color.r, color.g, color.b)
else
frame.texture:SetVertexColor(.5,1,.5)
end
local buttons = this.buttons
for i = 1, table.getn(buttons) do
local frame = _G[buttons[i][1]]
if frame and frame:IsVisible() then
UpdateIcon(frame, buttons[i][2])
end
end
end
local mapcolors = CreateFrame("Frame", nil, UIParent)
-- The updater is a child of its map, so its OnUpdate only runs while that
-- map is shown. This module used to run a frame-wide updater all the time.
local updaters = {}
local function AttachUpdater(map, prefix)
if not map or updaters[prefix] then return end
local updater = CreateFrame("Frame", nil, map)
updater.buttons = BuildButtons(prefix)
updater:SetScript("OnUpdate", UpdateMapColors)
updaters[prefix] = updater
end
module.enable = function(self)
mapcolors:SetScript("OnUpdate", UpdateWorldMapColors)
AttachUpdater(_G.WorldMapFrame, "WorldMap")
-- The battlefield minimap is a load-on-demand Blizzard addon.
if _G.BattlefieldMinimap then
AttachUpdater(_G.BattlefieldMinimap, "BattlefieldMinimap")
else
local loader = CreateFrame("Frame")
loader:RegisterEvent("ADDON_LOADED")
loader:SetScript("OnEvent", function()
if _G.BattlefieldMinimap then
AttachUpdater(_G.BattlefieldMinimap, "BattlefieldMinimap")
this:UnregisterEvent("ADDON_LOADED")
end
end)
end
end
+131 -3
View File
@@ -11,6 +11,9 @@ local module = ShaguTweaks:register({
enabled = true,
})
local DEFAULT_SCALE = .85
local MIN_SCALE, MAX_SCALE = .4, 2
local function AddSpecialFrame(name)
for i = 1, table.getn(UISpecialFrames) do
if UISpecialFrames[i] == name then return end
@@ -18,6 +21,123 @@ local function AddSpecialFrame(name)
table.insert(UISpecialFrames, name)
end
-- The scale chosen with Ctrl + Mousewheel is saved per character, so the map
-- reopens (and survives a reload) at the size it was left at.
local function GetSavedScale()
local scale = ShaguTweaks_config and tonumber(ShaguTweaks_config["WorldMapScale"])
if not scale then return DEFAULT_SCALE end
if scale < MIN_SCALE then return MIN_SCALE end
if scale > MAX_SCALE then return MAX_SCALE end
return scale
end
local function SaveScale(scale)
if not ShaguTweaks_config then return end
ShaguTweaks_config["WorldMapScale"] = scale
end
-- Turtle WoW Compatibility re-applies the window layout after Turtle's own
-- WorldMapFrame_Maximize and uses this instead of a fixed scale.
ShaguTweaks.GetWorldMapScale = GetSavedScale
-- The window's title bar is drawn above the frame's top edge (Turtle places
-- the title 17 units above it), so clicks there missed the frame and only
-- the dropdown row could be dragged. Extend the frame's clickable area up to
-- the top of the title. The close and minimize buttons are child frames on
-- a higher level, so they keep their clicks.
local TITLE_MARGIN = 6
local function TitleHeightAboveFrame()
local title = WorldMapFrameTitle
if not title then return 0 end
-- Prefer the anchor: exact, and valid before the layout is computed.
local count = title.GetNumPoints and title:GetNumPoints() or 1
for i = 1, count do
local point, relativeTo, relativePoint, _, y = title:GetPoint(i)
if point == "TOP" and relativeTo == WorldMapFrame
and (relativePoint == nil or relativePoint == "TOP") and y then
return y
end
end
local frameTop, titleTop = WorldMapFrame:GetTop(), title:GetTop()
if frameTop and titleTop then return titleTop - frameTop end
return 0
end
local function UpdateDragArea()
local extra = TitleHeightAboveFrame()
if extra > 0 then
extra = math.floor(extra + TITLE_MARGIN + .5)
if extra > 40 then extra = 40 end
else
extra = 0
end
WorldMapFrame:SetHitRectInsets(0, 0, -extra, 0)
end
ShaguTweaks.UpdateWorldMapDragArea = UpdateDragArea
-- OctoBus (github.com/Phridgey/OctoBus) shows transport timers. When it is
-- loaded, add a "Transports" button in front of the Continent dropdown that
-- toggles its window, the same as a left-click on its minimap icon.
local function OctoBusAvailable()
return OctoBusPanel and SlashCmdList and SlashCmdList["OCTOBUS"] and true or false
end
local function CreateTransportsButton()
if module.transports or not OctoBusAvailable() then return end
if not WorldMapContinentDropDown or not WorldMapZoomOutButton then return end
local button = CreateFrame("Button", "ShaguTweaksWorldMapTransports", WorldMapFrame, "UIPanelButtonTemplate")
button:SetWidth(WorldMapZoomOutButton:GetWidth() or 110)
button:SetHeight(WorldMapZoomOutButton:GetHeight() or 22)
-- Mirror of the Zoom Out button, which sits 10 units into the zone
-- dropdown's transparent right edge and 3 units higher.
button:SetPoint("RIGHT", WorldMapContinentDropDown, "LEFT", 10, 3)
button:SetText(T["Transports"])
-- OctoBus' window lives on the MEDIUM strata, behind the full-screen map.
-- Lift it above the map while both are open (also when OctoBus opens it
-- by itself at a dock), and give it its own strata back afterwards.
local function RaisePanel()
if not WorldMapFrame:IsShown() or not OctoBusPanel:IsShown() then return end
if not module.panelStrata then
module.panelStrata = OctoBusPanel:GetFrameStrata()
end
OctoBusPanel:SetFrameStrata("FULLSCREEN_DIALOG")
end
local function RestorePanel()
if module.panelStrata then
OctoBusPanel:SetFrameStrata(module.panelStrata)
module.panelStrata = nil
end
end
HookScript(WorldMapFrame, "OnShow", RaisePanel)
HookScript(WorldMapFrame, "OnHide", RestorePanel)
HookScript(OctoBusPanel, "OnShow", RaisePanel)
button:SetScript("OnClick", function()
-- OctoBus' "/ob" without arguments is the same toggle as its icon.
SlashCmdList["OCTOBUS"]("")
RaisePanel()
end)
button:SetScript("OnEnter", function()
GameTooltip:SetOwner(this, "ANCHOR_BOTTOM")
GameTooltip:SetText(T["Transports"])
GameTooltip:AddLine(T["Shows or hides the OctoBus transport timers."], 1, 1, 1, 1)
GameTooltip:Show()
end)
button:SetScript("OnLeave", function() GameTooltip:Hide() end)
module.transports = button
end
module.enable = function(self)
-- Only advertise the window as active after the delayed compatibility check.
-- Turtle WoW consumes this flag instead of looking at config alone.
@@ -65,14 +185,19 @@ module.enable = function(self)
HookScript(WorldMapFrame, "OnShow", function()
this:EnableKeyboard(false)
this:EnableMouseWheel(1)
WorldMapFrame:SetScale(.85)
WorldMapFrame:SetScale(GetSavedScale())
UpdateDragArea()
end)
HookScript(WorldMapFrame, "OnMouseWheel", function()
if API.IsShiftKeyDown() then
WorldMapFrame:SetAlpha(WorldMapFrame:GetAlpha() + arg1 / 10)
elseif API.IsControlKeyDown() then
WorldMapFrame:SetScale(WorldMapFrame:GetScale() + arg1 / 10)
local scale = WorldMapFrame:GetScale() + arg1 / 10
if scale < MIN_SCALE then scale = MIN_SCALE end
if scale > MAX_SCALE then scale = MAX_SCALE end
WorldMapFrame:SetScale(scale)
SaveScale(scale)
end
end)
@@ -88,12 +213,15 @@ module.enable = function(self)
WorldMapFrame:SetMovable(true)
WorldMapFrame:EnableMouse(true)
WorldMapFrame:SetScale(.85)
WorldMapFrame:SetScale(GetSavedScale())
WorldMapFrame:ClearAllPoints()
WorldMapFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 30)
WorldMapFrame:SetWidth(WorldMapButton:GetWidth() + 15)
WorldMapFrame:SetHeight(WorldMapButton:GetHeight() + 55)
BlackoutWorld:Hide()
UpdateDragArea()
CreateTransportsButton()
-- Initialization is complete. Keeping this event registered would repeat
-- the full layout work on every loading screen and reset a moved window.
+4 -5
View File
@@ -2,7 +2,7 @@ local T = ShaguTweaks.T
local module = ShaguTweaks:register({
title = T["XP Bar Text"],
description = T["Shows current XP and rested bonus percentage while hovering over the experience bar."],
description = T["Shows current and needed XP, percentage and rested bonus while hovering over the experience bar."],
expansions = { ["vanilla"] = true },
category = T["Interface"],
enabled = true,
@@ -40,15 +40,14 @@ module.enable = function(self)
local rest = GetXPExhaustion() or 0
local xpPct = math.floor(curr / max * 100)
local text
-- "1555/3000 51%", like the reputation bar's current/max text.
local text = "|cffffffff" .. curr .. "/" .. max .. " " .. xpPct .. "%|r"
if rest > 0 then
-- Turtle WoW 1.18.1 reduced rested creature XP from +100% to +50%.
-- The effective GetXPExhaustion full-rest threshold is about 112.5% of
-- the current level XP, so normalize that pool to a 0-100% display.
local restPct = math.floor(math.min(rest / (max * 1.125) * 100, 100))
text = "|cffffffff" .. xpPct .. "%|r |cffaaaaaa(|cffa78aca" .. restPct .. "%|cffaaaaaa)|r"
else
text = "|cffffffff" .. xpPct .. "%|r"
text = text .. " |cffaaaaaa(|cffa78aca" .. restPct .. "%|cffaaaaaa)|r"
end
if self.lastText ~= text then