Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5b3df38a1 | ||
|
|
15712e82c7 | ||
|
|
f3fd1d5c56 | ||
|
|
0a0cd055c4 | ||
|
|
151abf3280 | ||
|
|
a482808743 | ||
|
|
f5f651936c | ||
|
|
4ef534fabb | ||
|
|
c7d0f9c5f4 | ||
|
|
1ae8398a2d | ||
|
|
e4c7741226 | ||
|
|
d705a9b221 | ||
|
|
48d45f8fe6 |
@@ -0,0 +1,8 @@
|
||||
* text=auto eol=lf
|
||||
*.tga -text
|
||||
*.blp -text
|
||||
*.jpg -text
|
||||
*.jpeg -text
|
||||
*.png -text
|
||||
*.gif -text
|
||||
*.zip -text
|
||||
@@ -0,0 +1,10 @@
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*.rej
|
||||
*.log
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
.vscode/
|
||||
.idea/
|
||||
@@ -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`).
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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**.
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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**.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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"] = "Ваш хлам продан, и вы заработали",
|
||||
}
|
||||
@@ -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"] = "你的垃圾物品已出售,你获得了:",
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||