forked from vibe/Bastion
- Abunai Interface added - Jeffs Config added Simple shaman script added for testing purposemain
parent
5cad5d3ea0
commit
1e5289abaf
@ -1,24 +0,0 @@ |
||||
local Tinkr, Bastion = ... |
||||
local ExampleModule = Bastion.Module:New('ExampleModule') |
||||
local Player = Bastion.UnitManager:Get('player') |
||||
|
||||
-- Create a local spellbook |
||||
local SpellBook = Bastion.SpellBook:New() |
||||
|
||||
local FlashHeal = SpellBook:GetSpell(2061) |
||||
|
||||
-- Get a global spell (this can collide with other modules, so be careful) |
||||
-- This is useful for caching common spells that you might not actually cast, and to avoid needless spell creation inline |
||||
local FlashHeal = Bastion.Globals.SpellBook:GetSpell(2061) |
||||
|
||||
local AdvancedMath = Bastion:Import('AdvancedMath') |
||||
|
||||
print(AdvancedMath:Add(1, 2)) |
||||
|
||||
ExampleModule:Sync(function() |
||||
if Player:GetHP() <= 50 then |
||||
FlashHeal:Cast(Player) |
||||
end |
||||
end) |
||||
|
||||
Bastion:Register(ExampleModule) |
@ -1,21 +0,0 @@ |
||||
local Tinkr, Bastion = ... |
||||
|
||||
local Player = Bastion.UnitManager:Get('player') |
||||
|
||||
Bastion:RegisterLibrary(Bastion.Library:New({ |
||||
name = 'Dependable', |
||||
exports = { |
||||
default = function() |
||||
local Dependable = {} |
||||
|
||||
Dependable.__index = Dependable |
||||
|
||||
function Dependable:Test(a) |
||||
print(a) |
||||
end |
||||
|
||||
return Dependable |
||||
end, |
||||
Test = 5 |
||||
} |
||||
})) |
@ -1,15 +0,0 @@ |
||||
local Tinkr, Bastion = ... |
||||
|
||||
Bastion:RegisterLibrary(Bastion.Library:New({ |
||||
name = 'Circular', |
||||
exports = { |
||||
default = function(self) |
||||
-- Return default first, and then the remaining exports |
||||
local Math, OtherExports = self:Import('AdvancedMath') |
||||
|
||||
print(Math:Add(1, 2)) |
||||
|
||||
return 'Circular' |
||||
end |
||||
} |
||||
})) |
@ -1,25 +0,0 @@ |
||||
local Tinkr, Bastion = ... |
||||
|
||||
Bastion:RegisterLibrary(Bastion.Library:New({ |
||||
name = 'AdvancedMath', |
||||
exports = { |
||||
default = function(self) -- Function exports are called when the library is loaded |
||||
-- Return default first, and then the remaining exports |
||||
local Dependable, OtherExports = self:Import('Dependable') |
||||
|
||||
local CircularDependency = self:Import('Circular') -- Causes a circular dependency error |
||||
|
||||
Dependable:Test(OtherExports.Test) |
||||
|
||||
local AdvancedMath = {} |
||||
|
||||
AdvancedMath.__index = AdvancedMath |
||||
|
||||
function AdvancedMath:Add(a, b) |
||||
return a + b |
||||
end |
||||
|
||||
return AdvancedMath |
||||
end |
||||
} |
||||
})) |
@ -0,0 +1,337 @@ |
||||
local Tinkr, Bastion = ... |
||||
|
||||
local Util = Tinkr.Util |
||||
if UnitClass('player') ~= 'Shaman' then return end |
||||
|
||||
Bastion.Rotation = {} |
||||
local BR = Bastion.Rotation |
||||
BR.Units = { |
||||
Player = Bastion.UnitManager:Get('player'), |
||||
Target = Bastion.UnitManager:Get('target'), |
||||
None = Bastion.UnitManager:Get('none') |
||||
} |
||||
|
||||
local ShamanModule = Bastion.Module:New('Shaman') |
||||
local SpellBook = Bastion.Globals.SpellBook |
||||
|
||||
local Player = BR.Units.Player |
||||
local None = BR.Units.None |
||||
local Target = BR.Units.Target |
||||
|
||||
Tinkr:require("scripts/bastion/scripts/ShamanSettings", Bastion) |
||||
BR.myconf = Tinkr.Util.Config:New('shaman') -- for saving variables |
||||
|
||||
local function _highestrank(spell) |
||||
local name = GetSpellInfo(spell) |
||||
local highrankid = (select(7, GetSpellInfo(name))) |
||||
if not highrankid then |
||||
highrankid = 1 |
||||
end |
||||
return highrankid |
||||
end |
||||
|
||||
local s = { |
||||
-- Basic Attack |
||||
AutoAttack = SpellBook:GetSpell(6603), |
||||
|
||||
-- Shocks |
||||
EarthShock = SpellBook:GetSpell(_highestrank(8042)), |
||||
FrostShock = SpellBook:GetSpell(_highestrank(8056)), |
||||
FlameShock = SpellBook:GetSpell(_highestrank(8050)), |
||||
|
||||
-- Specific Ranks for Utility |
||||
EarthShockRank1 = SpellBook:GetSpell(8042), -- Rank 1 (Interrupt) |
||||
FrostShockRank1 = SpellBook:GetSpell(8056), -- Rank 1 (Slow), |
||||
|
||||
-- Healing Spells |
||||
HealingWave = SpellBook:GetSpell(_highestrank(331)), |
||||
LesserHealingWave = SpellBook:GetSpell(_highestrank(8004)), |
||||
ChainHeal = SpellBook:GetSpell(_highestrank(1064)), |
||||
|
||||
-- Healing Sub Ranks (Healing Wave) |
||||
HealingWaveRank1 = SpellBook:GetSpell(331), -- Rank 1 |
||||
HealingWaveRank2 = SpellBook:GetSpell(332), -- Rank 2 |
||||
HealingWaveRank3 = SpellBook:GetSpell(547), -- Rank 3 |
||||
HealingWaveRank4 = SpellBook:GetSpell(913), -- Rank 4 |
||||
HealingWaveRank5 = SpellBook:GetSpell(939), -- Rank 5 |
||||
HealingWaveRank6 = SpellBook:GetSpell(959), -- Rank 6 |
||||
HealingWaveRank7 = SpellBook:GetSpell(8005), -- Rank 7 |
||||
HealingWaveRank8 = SpellBook:GetSpell(10395), -- Rank 8 |
||||
HealingWaveRank9 = SpellBook:GetSpell(10396), -- Rank 9 (Max Rank) |
||||
|
||||
-- Healing Sub Ranks (Lesser Healing Wave) |
||||
LesserHealingWaveRank1 = SpellBook:GetSpell(8004), -- Rank 1 |
||||
LesserHealingWaveRank2 = SpellBook:GetSpell(8008), -- Rank 2 |
||||
LesserHealingWaveRank3 = SpellBook:GetSpell(8010), -- Rank 3 |
||||
LesserHealingWaveRank4 = SpellBook:GetSpell(10466), -- Rank 4 |
||||
LesserHealingWaveRank5 = SpellBook:GetSpell(10467), -- Rank 5 |
||||
LesserHealingWaveRank6 = SpellBook:GetSpell(10468), -- Rank 6 (Max Rank) |
||||
|
||||
-- Healing Sub Ranks (Chain Heal) |
||||
ChainHealRank1 = SpellBook:GetSpell(1064), -- Rank 1 |
||||
ChainHealRank2 = SpellBook:GetSpell(10622), -- Rank 2 |
||||
ChainHealRank3 = SpellBook:GetSpell(10623), -- Rank 3 |
||||
ChainHealRank4 = SpellBook:GetSpell(25422), -- Rank 4 (Max Rank) |
||||
|
||||
-- Totems |
||||
EarthbindTotem = SpellBook:GetSpell(2484), |
||||
StoneclawTotem = SpellBook:GetSpell(_highestrank(5730)), |
||||
StrengthOfEarthTotem = SpellBook:GetSpell(_highestrank(8075)), |
||||
TremorTotem = SpellBook:GetSpell(_highestrank(8143)), |
||||
SearingTotem = SpellBook:GetSpell(_highestrank(3599)), |
||||
FireNovaTotem = SpellBook:GetSpell(_highestrank(1535)), |
||||
HealingStreamTotem = SpellBook:GetSpell(_highestrank(5394)), |
||||
ManaSpringTotem = SpellBook:GetSpell(_highestrank(5675)), |
||||
WindfuryTotem = SpellBook:GetSpell(_highestrank(8512)), |
||||
GraceOfAirTotem = SpellBook:GetSpell(_highestrank(8835)), |
||||
|
||||
-- Buffs |
||||
LightningShield = SpellBook:GetSpell(_highestrank(324)), |
||||
|
||||
-- Offensive Spells |
||||
LightningBolt = SpellBook:GetSpell(_highestrank(403)), |
||||
ChainLightning = SpellBook:GetSpell(_highestrank(421)), |
||||
|
||||
-- Utility |
||||
Purge = SpellBook:GetSpell(_highestrank(370)), |
||||
CurePoison = SpellBook:GetSpell(526), |
||||
CureDisease = SpellBook:GetSpell(2870), |
||||
GhostWolf = SpellBook:GetSpell(2645), |
||||
WaterWalking = SpellBook:GetSpell(_highestrank(546)), |
||||
WaterBreathing = SpellBook:GetSpell(_highestrank(131)), |
||||
|
||||
-- Weapon Buffs |
||||
FlametongueWeapon = SpellBook:GetSpell(_highestrank(8024)), |
||||
FrostbrandWeapon = SpellBook:GetSpell(_highestrank(8033)), |
||||
WindfuryWeapon = SpellBook:GetSpell(_highestrank(8232)), |
||||
RockbiterWeapon = SpellBook:GetSpell(_highestrank(8017)), |
||||
|
||||
-- Talents |
||||
ElementalMastery = SpellBook:GetSpell(16166), |
||||
NaturesSwiftness = SpellBook:GetSpell(16188), |
||||
ManaTideTotem = SpellBook:GetSpell(_highestrank(16190)), |
||||
|
||||
--Buffs |
||||
refreshment = SpellBook:GetList(430, 431, 432, 1133, 1135, 1137, 10250, 22734, 27089, 433, 434, 435, 1127, 1129, 1131, 5004, 11199, 11200, 22731, 25696) |
||||
} |
||||
|
||||
|
||||
-- Create APLs |
||||
local DefaultAPL = Bastion.APL:New('default') |
||||
local HealingAPL = Bastion.APL:New('healing') |
||||
local DamageAPL = Bastion.APL:New('damage') |
||||
local CooldownAPL = Bastion.APL:New('cooldown') |
||||
local DefensiveAPL = Bastion.APL:New('defensive') |
||||
|
||||
-- Default APL |
||||
--DefaultAPL:AddAPL(DefensiveAPL, function() return true end) |
||||
--DefaultAPL:AddAPL(CooldownAPL, function() return true end) |
||||
DefaultAPL:AddAPL(HealingAPL, function() return true end) |
||||
--DefaultAPL:AddAPL(ExecuteAPL, function() return true end) |
||||
--DefaultAPL:AddAPL(DamagePriorityAPL, function() return not HealingNeeded() and AggressiveDamagePhase() end) |
||||
--DefaultAPL:AddAPL(DamageAPL, function() return not HealingNeeded() end) |
||||
|
||||
local DispelTarget = Bastion.UnitManager:CreateCustomUnit('dispel', function(unit) |
||||
local lowest = nil |
||||
local lowestHP = math.huge |
||||
Bastion.UnitManager:EnumFriends(function(unit) |
||||
if unit:IsDead() then return false end |
||||
if not Player:CanSee(unit) then return false end |
||||
if Player:GetDistance(unit) > 40 then return false end |
||||
|
||||
if not unit:IsDead() and Player:CanSee(unit) and (unit:GetAuras():HasAnyDispelableAura(s.CurePoison) or unit:GetAuras():HasAnyDispelableAura(s.CureDisease)) then |
||||
local hp = unit:GetHP() |
||||
if hp < lowestHP then |
||||
lowest = unit |
||||
lowestHP = hp |
||||
end |
||||
end |
||||
end) |
||||
if lowest == nil then |
||||
lowest = None |
||||
end |
||||
return lowest |
||||
end) |
||||
|
||||
local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit) |
||||
local lowest = nil |
||||
local lowestHP = math.huge |
||||
Bastion.UnitManager:EnumFriends(function(unit) |
||||
if unit:IsDead() then return false end |
||||
if Player:GetDistance(unit) > 40 then return false end |
||||
if not Player:CanSee(unit) then return false end |
||||
local hp = unit:GetHP() |
||||
if hp < lowestHP then |
||||
lowest = unit |
||||
lowestHP = hp |
||||
end |
||||
if not lowest then |
||||
lowest = Player |
||||
end |
||||
end) |
||||
if lowest == nil then |
||||
lowest = None |
||||
end |
||||
return lowest |
||||
end) |
||||
|
||||
local InterruptTarget = Bastion.UnitManager:CreateCustomUnit('interrupttarget', function(unit) |
||||
local interrupt = nil |
||||
Bastion.UnitManager:EnumEnemies(function(unit) |
||||
if unit:IsDead() then return false end |
||||
if not Player:CanSee(unit) then return false end |
||||
if Player:GetDistance(unit) > 20 then return false end |
||||
if unit:IsInterruptibleAt(40) and Player:IsFacing(unit) then |
||||
interrupt = unit |
||||
--print(unit) |
||||
return true |
||||
end |
||||
end) |
||||
if interrupt == nil then |
||||
interrupt = None |
||||
end |
||||
return interrupt |
||||
end) |
||||
|
||||
local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purgetarget', function(unit) |
||||
local purgeTarget = nil |
||||
local highestPriority = 0 |
||||
|
||||
-- List of high-priority buffs to purge |
||||
local highPriorityBuffs = { |
||||
642, -- Divine Shield |
||||
1022, -- Blessing of Protection |
||||
19752, -- Divine Intervention |
||||
1044, -- Blessing of Freedom |
||||
6940, -- Blessing of Sacrifice |
||||
1461, -- Arcane Intellect |
||||
23028, -- Arcane Brilliance |
||||
10157, -- Arcane Intellect (Rank 6) |
||||
27126, -- Arcane Brilliance (Rank 2) |
||||
} |
||||
|
||||
Bastion.UnitManager:EnumEnemies(function(unit) |
||||
-- Basic checks |
||||
if unit:IsDead() then return false end |
||||
if not Player:CanSee(unit) then return false end |
||||
if Player:GetDistance(unit) > 30 then return false end |
||||
if not Player:IsFacing(unit) then return false end |
||||
if not unit:IsAffectingCombat() then return false end |
||||
|
||||
-- Check if unit has purgeable buffs |
||||
if unit:GetAuras():HasPurgeableBuff() then |
||||
local currentPriority = 0 |
||||
|
||||
-- Check if unit has any high priority buffs |
||||
for _, buffId in ipairs(highPriorityBuffs) do |
||||
if unit:GetAuras():Find(buffId):IsUp() then |
||||
currentPriority = 2 |
||||
break |
||||
end |
||||
end |
||||
|
||||
-- If no high priority buffs found, but unit has other purgeable buffs |
||||
if currentPriority == 0 then |
||||
currentPriority = 1 |
||||
end |
||||
|
||||
-- Update target if this unit has higher priority |
||||
if currentPriority > highestPriority then |
||||
purgeTarget = unit |
||||
highestPriority = currentPriority |
||||
end |
||||
end |
||||
end) |
||||
|
||||
if purgeTarget == nil then |
||||
purgeTarget = None |
||||
end |
||||
|
||||
return purgeTarget |
||||
end) |
||||
|
||||
-- Utility function to check if healing is needed |
||||
local function HealingNeeded() |
||||
return Lowest:Exists() and Lowest:GetHP() < 90 |
||||
end |
||||
|
||||
-- Utility function to check if emergency healing is needed |
||||
local function EmergencyHealingNeeded() |
||||
return Lowest:Exists() and Lowest:GetHP() < 50 |
||||
end |
||||
|
||||
|
||||
|
||||
|
||||
local function out_of_combat_function() |
||||
if Player:GetAuras():FindAnyOf(s.refreshment):IsUp() then return end |
||||
|
||||
end |
||||
|
||||
HealingAPL:AddSpell( |
||||
s.LesserHealingWave:CastableIf(function(self) |
||||
if not Lowest:Exists() then return false end |
||||
if Lowest:GetHP() > 20 then return false end |
||||
return self:IsKnownAndUsable() and HealingNeeded() |
||||
end):SetTarget(Lowest) |
||||
) |
||||
|
||||
HealingAPL:AddSpell( |
||||
s.HealingWave:CastableIf(function(self) |
||||
if not Lowest:Exists() then return false end |
||||
if Lowest:GetHP() > 50 or Lowest:GetHP() < 20 then return false end |
||||
return self:IsKnownAndUsable() and HealingNeeded() |
||||
end):SetTarget(Lowest) |
||||
) |
||||
|
||||
HealingAPL:AddSpell( |
||||
s.CurePoison:CastableIf(function(self) |
||||
if not DispelTarget:Exists() then return false end |
||||
return (DispelTarget:GetAuras():HasAnyDispelableAura(s.CurePoison)) and self:IsKnownAndUsable() |
||||
end):SetTarget(DispelTarget) |
||||
) |
||||
|
||||
HealingAPL:AddSpell( |
||||
s.CureDisease:CastableIf(function(self) |
||||
if not DispelTarget:Exists() then return false end |
||||
return (DispelTarget:GetAuras():HasAnyDispelableAura(s.CureDisease)) and self:IsKnownAndUsable() |
||||
end):SetTarget(DispelTarget) |
||||
) |
||||
|
||||
DamageAPL:AddSpell( |
||||
s.Purge:CastableIf(function(self) |
||||
if not PurgeTarget:Exists() then return false end |
||||
return self:IsKnownAndUsable() and PurgeTarget:GetAuras():HasPurgeableBuff() |
||||
end):SetTarget(PurgeTarget) |
||||
) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ShamanModule:Sync(function() |
||||
if Player:GetAuras():FindAnyOf(s.refreshment):IsUp() then return end |
||||
if Player:IsDead() then return end |
||||
if Player:IsCastingOrChanneling() then return end |
||||
if IsMounted() then return end |
||||
if InterruptTarget:Exists() and s.EarthShockRank1:IsKnownAndUsable() then |
||||
s.EarthShockRank1:Cast(InterruptTarget) |
||||
end |
||||
|
||||
if Player:IsAffectingCombat() then |
||||
|
||||
DefaultAPL:Execute() |
||||
else |
||||
if Player:GetPP() > 5 then |
||||
HealingAPL:Execute() |
||||
end |
||||
out_of_combat_function() |
||||
end |
||||
end) |
||||
|
||||
|
||||
Bastion:Register(ShamanModule) |
||||
ShamanModule:Toggle() |
@ -0,0 +1,124 @@ |
||||
local Unlocker, Bastion = ... |
||||
--! Creating the initial GUI !-- |
||||
|
||||
Bastion.Settings = Bastion.Interface.Category:New("Shaman") |
||||
|
||||
|
||||
Bastion.Settings:AddSubsection("General") |
||||
|
||||
Bastion.Settings:Slider({ |
||||
category = "bastion", |
||||
var = "tickrate", |
||||
name = "Engine Tickrate (milliseconds)", |
||||
tooltip = "How often the Bastion Engine runs. The lower this number, the more resources Bastion will use.\n\nYou must reload for changes to take effect.", |
||||
default = 100, |
||||
min = 50, |
||||
max = 500, |
||||
step = 50, |
||||
}) |
||||
|
||||
|
||||
Bastion.Settings:Dropdown({ |
||||
category = "bastion", |
||||
var = "pause_key", |
||||
name = "Hold-to-Pause", |
||||
tooltip = "Bastion will not run while this button is held down.", |
||||
default = false, |
||||
options = { |
||||
{ "None", false }, |
||||
{ "Shift", "SHIFT" }, |
||||
{ "Control", "CTRL" }, |
||||
{ "Alt", "ALT" }, |
||||
} |
||||
}) |
||||
|
||||
Bastion.Settings:AddSubsection("Engine Options") |
||||
|
||||
Bastion.Settings:Checkbox({ |
||||
category = "bastion", |
||||
var = "debug", |
||||
name = "Debug Mode", |
||||
tooltip = "Toggles Debug Mode for the Bastion Engine.", |
||||
default = false, |
||||
onClick = function() |
||||
Bastion.DebugMode = not Bastion.DebugMode |
||||
if Bastion.DebugMode then |
||||
Bastion:Debug("Debug Enabled") |
||||
else |
||||
Bastion:Print("Debug Disabled") |
||||
end |
||||
end, |
||||
}) |
||||
|
||||
Bastion.MainBar = Bastion.Interface.Hotbar:New({ |
||||
buttonCount = 3, |
||||
name = "Bastion", |
||||
options = Bastion.Settings |
||||
}) |
||||
|
||||
if Bastion:IsClassic() then |
||||
Bastion.MainBar:AddButton({ |
||||
name = "Toggle", |
||||
texture = "Interface\\ICONS\\Inv_drink_15", |
||||
tooltip = "This button toggle's Bastion on and off.", |
||||
toggle = true, |
||||
onClick = function() |
||||
Bastion.Enabled = not Bastion.Enabled |
||||
if Bastion.Enabled then |
||||
Bastion:Print("Enabled") |
||||
else |
||||
Bastion:Print("Disabled") |
||||
end |
||||
end, |
||||
}) |
||||
Bastion.MainBar:AddButton({ |
||||
name = "Options", |
||||
tooltip = "This button opens the Bastion Options menu.", |
||||
texture = "Interface\\ICONS\\Inv_misc_gear_01", |
||||
toggle = false, |
||||
onClick = function() |
||||
if SettingsPanel and SettingsPanel:IsVisible() then |
||||
HideUIPanel(SettingsPanel) |
||||
HideUIPanel(GameMenuFrame) |
||||
else |
||||
Settings.OpenToCategory(Bastion.MainBar.options.settings:GetID()) |
||||
end |
||||
end, |
||||
}) |
||||
|
||||
else |
||||
Bastion.MainBar:AddButton({ |
||||
name = "Toggle", |
||||
texture = "Interface\\ICONS\\ACHIEVEMENT_GUILDPERK_MRPOPULARITY", |
||||
tooltip = "This button toggle's Bastion on and off.", |
||||
toggle = true, |
||||
onClick = function() |
||||
-- if Bastion.Settings.config:Read("bastion_stimulant", true) then |
||||
-- Bastion.Stimulant:Toggle() |
||||
-- end |
||||
Bastion.Enabled = not Bastion.Enabled |
||||
if Bastion.Enabled then |
||||
Bastion:Print("Enabled") |
||||
else |
||||
Bastion:Print("Disabled") |
||||
end |
||||
end, |
||||
}) |
||||
Bastion.MainBar:AddButton({ |
||||
name = "Options", |
||||
tooltip = "This button opens the Bastion Options menu.", |
||||
texture = "Interface\\ICONS\\INV_Engineering_90_Gizmo", |
||||
toggle = false, |
||||
onClick = function() |
||||
if SettingsPanel and SettingsPanel:IsVisible() then |
||||
HideUIPanel(SettingsPanel) |
||||
HideUIPanel(GameMenuFrame) |
||||
else |
||||
Settings.OpenToCategory(Bastion.MainBar.options.settings:GetID()) |
||||
end |
||||
end, |
||||
}) |
||||
end |
||||
|
||||
|
||||
Bastion.Settings:Register() |
@ -0,0 +1,85 @@ |
||||
---@type Tinkr |
||||
local Tinkr, |
||||
---@class Bastion |
||||
Bastion = ... |
||||
|
||||
---@class Bastion.Config |
||||
---@field instantiated boolean |
||||
---@field config Tinkr.Util.Config.Instance |
||||
---@field defaults nil | table |
||||
local Config = { |
||||
instantiated = false, |
||||
} |
||||
|
||||
function Config:__index(k) |
||||
local response = Config[k] |
||||
|
||||
if response == nil then |
||||
response = rawget(self, k) |
||||
end |
||||
|
||||
if response == nil and self.instantiated then |
||||
response = self:Read(k) |
||||
end |
||||
|
||||
return response |
||||
end |
||||
|
||||
function Config:__newindex(key, value) |
||||
if self.instantiated then |
||||
self:Write(key, value) |
||||
else |
||||
rawset(self, key, value) |
||||
end |
||||
end |
||||
|
||||
---@generic D |
||||
---@param name string |
||||
---@param defaults? D |
||||
function Config:New(name, defaults) |
||||
---@type Bastion.Config |
||||
local self = setmetatable({}, Config) |
||||
self.config = Tinkr.Util.Config:New(name) |
||||
self.defaults = type(defaults) == "table" and defaults or {} |
||||
self.instantiated = true |
||||
return self |
||||
end |
||||
|
||||
---@generic D |
||||
---@param key string |
||||
---@param default? D |
||||
---@return D |
||||
function Config:Read(key, default) |
||||
if type(default) == "nil" then |
||||
default = self.defaults[key] |
||||
end |
||||
return self.config:Read(key, default) |
||||
end |
||||
|
||||
function Config:Reset() |
||||
if type(self.defaults) == "table" then |
||||
-- Clear all values currently in the config. |
||||
for key, _ in pairs(self.config.data) do |
||||
self:Write(key, nil) |
||||
end |
||||
-- Use default table to write new defaults. |
||||
for key, value in pairs(self.defaults) do |
||||
self:Write(key, value) |
||||
end |
||||
return true |
||||
end |
||||
return false |
||||
end |
||||
|
||||
---@param key string |
||||
---@param value any |
||||
function Config:Write(key, value) |
||||
self.config:Write(key, value) |
||||
end |
||||
|
||||
---@param key string |
||||
function Config:Sync(key) |
||||
self.config:Sync(key) |
||||
end |
||||
|
||||
Bastion.Config = Config |
@ -0,0 +1,268 @@ |
||||
local Tinkr, Bastion = ... |
||||
local Interface = {} |
||||
|
||||
--[[ |
||||
The current settings is named "Vertical" as it uses the Dragonflight Vertical |
||||
layout, rather than the Canvas layout. Vertical looks better, is easier to |
||||
format and use. Canvas layout allows you to add multi-select dropdowns, text |
||||
inputs, and more. I prefer vertical. Canvas support will be added in the future. |
||||
]]-- |
||||
|
||||
local Hotbar = {} |
||||
Hotbar.__index = Hotbar |
||||
|
||||
Bastion.Hotbars = {} |
||||
|
||||
-- Constructor |
||||
---@return Hotbar |
||||
function Hotbar:New(details) |
||||
local self = setmetatable({}, Hotbar) |
||||
self.button_size = 45 |
||||
self.button_spacing = 5 |
||||
self.button_count = details.buttonCount |
||||
self.name = details.name |
||||
self.options = details.options |
||||
|
||||
local position = self.options.config:Read("bar_location", { "CENTER", 0, -50 }) |
||||
|
||||
|
||||
self.frame = CreateFrame("Frame", nil, UIParent, "SecureHandlerStateTemplate") |
||||
self.frame:SetWidth(self.button_count * (32 + self.button_spacing) - self.button_spacing) -- width = total width of all buttons + total width of all spacings |
||||
self.frame:SetHeight(32 + 20) |
||||
|
||||
|
||||
self.frame:SetPoint(position[1], position[2], position[3]) |
||||
|
||||
self.title = self.frame:CreateFontString(nil, "OVERLAY", "Game15Font") |
||||
|
||||
self.title:SetPoint("BOTTOMLEFT", self.frame, "LEFT", 0, 10) -- adjust these values as necessary |
||||
|
||||
self.title:SetText(self.name) -- replace this with your desired title |
||||
|
||||
self.frame:EnableMouse(true) |
||||
self.frame:SetMovable(true) |
||||
self.frame:SetClampedToScreen(true) |
||||
self.frame:RegisterForDrag("LeftButton") |
||||
self.frame:SetScript("OnDragStart", self.frame.StartMoving) |
||||
self.frame:SetScript("OnDragStop", function() |
||||
self.frame:StopMovingOrSizing() |
||||
local point, _, _, x, y = self.frame:GetPoint() |
||||
self.options.config:Write("bar_location", { point, x, y }) |
||||
end) |
||||
|
||||
if not self.options.config:Read("display_hotbar_" .. self.options.name, true) then |
||||
self.frame:Hide() |
||||
end |
||||
|
||||
self.options:Checkbox({ |
||||
category = "display_hotbar", |
||||
var = self.name, |
||||
name = self.name .. " Hotbar", |
||||
tooltip = "Display the hotbar for " .. self.name, |
||||
default = true |
||||
}) |
||||
|
||||
self.buttons = {} |
||||
|
||||
table.insert(Bastion.Hotbars, self) |
||||
return self |
||||
end |
||||
|
||||
-- Needed to ensure hotbars are positioned properly and hidden when a user hides them in settings |
||||
function Hotbar:Refresh() |
||||
if not self.options.config:Read("display_hotbar_" .. self.options.name, true) then |
||||
if self.frame:IsVisible() then |
||||
self.frame:Hide() |
||||
end |
||||
else |
||||
if not self.frame:IsVisible() then |
||||
self.frame:Show() |
||||
end |
||||
end |
||||
|
||||
return self |
||||
end |
||||
|
||||
-- Adds a button to the hotbar. |
||||
-- TODO: Make this not a hot mess for classic/retail |
||||
function Hotbar:AddButton(details) |
||||
local button = CreateFrame("CheckButton", nil, self.frame, "UIPanelButtonTemplate") |
||||
button:SetNormalTexture(details.texture) |
||||
button:SetPoint("CENTER") |
||||
button:SetSize(32, 32) |
||||
table.insert(self.buttons, button) |
||||
if details.onClick then |
||||
if details.toggle then |
||||
local toggleAnim = false |
||||
button:SetScript("OnClick", function() |
||||
-- Toggle animation |
||||
if toggleAnim then |
||||
ActionButton_HideOverlayGlow(button) |
||||
toggleAnim = false |
||||
else |
||||
ActionButton_ShowOverlayGlow(button) |
||||
toggleAnim = true |
||||
end |
||||
details.onClick() |
||||
end) |
||||
else |
||||
button:SetScript("OnClick", details.onClick) |
||||
end |
||||
end |
||||
button:SetScript("OnEnter", function(self) |
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT") |
||||
GameTooltip:AddLine(details.name, 1, 1, 1) -- sets the tooltip as the button's name |
||||
GameTooltip:AddLine(details.tooltip) |
||||
GameTooltip:Show() |
||||
end) |
||||
button:SetScript("OnLeave", function(self) |
||||
GameTooltip:Hide() |
||||
end) |
||||
for i, btn in ipairs(self.buttons) do |
||||
button:SetPoint("BOTTOMLEFT", (i - 1) * (32 + self.button_spacing), 0) -- Position each button |
||||
end |
||||
|
||||
return self |
||||
end |
||||
|
||||
local Category = {} |
||||
Category.__index = Category |
||||
|
||||
-- Constructor |
||||
---@param name String |
||||
---@param config Config |
||||
---@return Category |
||||
function Category:New(name, parent) |
||||
local self = setmetatable({}, Category) |
||||
self.name = name |
||||
self.parent = parent or nil |
||||
|
||||
if self.parent then |
||||
self.settings, self.layout = Settings.RegisterVerticalLayoutSubcategory(parent.settings, self.name) |
||||
self.config = self.parent.config |
||||
else |
||||
self.settings, self.layout = Settings.RegisterVerticalLayoutCategory(self.name) |
||||
self.config = Bastion.Config:New(name) |
||||
end |
||||
|
||||
return self |
||||
end |
||||
|
||||
function Category:AddSubsection(section_title) |
||||
local section = CreateSettingsListSectionHeaderInitializer(section_title) |
||||
self.layout:AddInitializer(section) |
||||
return self |
||||
end |
||||
|
||||
-- Register the Category object with the Blizzard UI |
||||
---@return nil |
||||
function Category:Register() |
||||
Settings.RegisterAddOnCategory(self.settings) |
||||
end |
||||
|
||||
-- Adds a checkbox to the Category |
||||
---@param details Table |
||||
function Category:Checkbox(details) |
||||
local default_value = details.default or false |
||||
local variable_name = details.category .. "_" .. details.var |
||||
|
||||
local setting = Settings.RegisterAddOnSetting(self.settings, details.name, variable_name, type(default_value), self.config:Read(variable_name, default_value)) |
||||
|
||||
Settings.SetOnValueChangedCallback(variable_name, function(event) |
||||
self.config:Write(variable_name, setting:GetValue()) |
||||
end) |
||||
|
||||
local initializer = Settings.CreateCheckBox(self.settings, setting, details.tooltip and ("\n" .. details.tooltip) or "") |
||||
|
||||
if details.disabled then |
||||
initializer:AddModifyPredicate(function() return false end) |
||||
self.config:Write(variable_name, false) |
||||
end |
||||
|
||||
return self |
||||
end |
||||
|
||||
-- Adds a checkbox to the Category |
||||
---@param details Table |
||||
function Category:SubCheck(details) |
||||
local default_value = details.default or false |
||||
local variable_name = details.category .. "_" .. details.var |
||||
|
||||
local setting = Settings.RegisterAddOnSetting(self.settings, details.name, variable_name, type(default_value), self.config:Read(variable_name, default_value)) |
||||
|
||||
Settings.SetOnValueChangedCallback(variable_name, function(event) |
||||
print(variable_name, setting:GetValue()) |
||||
self.config:Write(variable_name, setting:GetValue()) |
||||
details.sub:Toggle() |
||||
end) |
||||
|
||||
local initializer = Settings.CreateCheckBox(self.settings, setting, details.tooltip and ("\n" .. details.tooltip) or "") |
||||
|
||||
return self |
||||
end |
||||
|
||||
-- Adds a slider to the Category |
||||
---@param details Table |
||||
function Category:Slider(details) |
||||
details = details or {} |
||||
local default_value = details.default or 0 |
||||
local min_value = details.min or 0 |
||||
local max_value = details.max or 100 |
||||
|
||||
local step = details.step or 1 |
||||
local variable_name = details.category .. "_" .. details.var |
||||
|
||||
local setting = Settings.RegisterAddOnSetting(self.settings, details.name, variable_name, type(default_value), tonumber(self.config:Read(variable_name, default_value))) |
||||
|
||||
Settings.SetOnValueChangedCallback(variable_name, function(event) |
||||
self.config:Write(variable_name, setting:GetValue()) |
||||
end) |
||||
|
||||
local options = Settings.CreateSliderOptions(min_value, max_value, step) |
||||
|
||||
options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right) |
||||
|
||||
local initializer = Settings.CreateSlider(self.settings, setting, options, details.tooltip and ("\n" .. details.tooltip) or "") |
||||
|
||||
-- if details.disabled then |
||||
-- initializer:AddModifyPredicate(function() return false end) |
||||
-- self.config:Write(variable_name, false) |
||||
-- end |
||||
|
||||
return self |
||||
end |
||||
|
||||
-- Adds a dropdown to the category |
||||
---@param details Table |
||||
function Category:Dropdown(details) |
||||
local default_value = details.default |
||||
local variable_name = details.category .. "_" .. details.var |
||||
|
||||
local setting = Settings.RegisterAddOnSetting(self.settings, details.name, variable_name, type(default_value), self.config:Read(variable_name, default_value)) |
||||
|
||||
local function GetOptions() |
||||
local container = Settings.CreateControlTextContainer() |
||||
for k, v in ipairs(details.options) do |
||||
container:Add(v[2], v[1]) |
||||
end |
||||
return container:GetData() |
||||
end |
||||
|
||||
Settings.SetOnValueChangedCallback(variable_name, function(event) |
||||
self.config:Write(variable_name, setting:GetValue()) |
||||
end) |
||||
|
||||
local initializer = Settings.CreateDropDown(self.settings, setting, GetOptions, details.tooltip or "") |
||||
|
||||
if details.disabled then |
||||
initializer:AddModifyPredicate(function() return false end) |
||||
self.config:Write(variable_name, false) |
||||
end |
||||
|
||||
return self |
||||
end |
||||
|
||||
Interface.Hotbar = Hotbar |
||||
Interface.Category = Category |
||||
print("Interface Loaded") |
||||
Bastion.Interface = Interface |
Loading…
Reference in new issue