4n0n 2 years ago
commit 764655ea51
  1. 3
      .gitignore
  2. 2
      README.md
  3. 436
      scripts/restodruid.lua
  4. 76
      src/APL/APL.lua
  5. 245
      src/Aura/Aura.lua
  6. 229
      src/AuraTable/AuraTable.lua
  7. 46
      src/Cache/Cache.lua
  8. 68
      src/Cacheable/Cacheable.lua
  9. 51
      src/Class/Class.lua
  10. 49
      src/ClassMagic/ClassMagic.lua
  11. 60
      src/Command/Command.lua
  12. 60
      src/EventManager/EventManager.lua
  13. 343
      src/Item/Item.lua
  14. 23
      src/ItemBook/ItemBook.lua
  15. 65
      src/Module/Module.lua
  16. 340
      src/Spell/Spell.lua
  17. 23
      src/SpellBook/SpellBook.lua
  18. 41
      src/Timer/Timer.lua
  19. 368
      src/Unit/Unit.lua
  20. 275
      src/UnitManager/UnitManager.lua
  21. 240
      src/Vector3/Vector3.lua
  22. 152
      src/_bastion.lua

3
.gitignore vendored

@ -0,0 +1,3 @@
## Ignore mac files
.DS_Store
DS_Store

@ -0,0 +1,2 @@
# bastion
Bastion is a heavily cached World of Warcraft scripting platform

@ -0,0 +1,436 @@
local Tinkr, Bastion = ...
local RestoModule = Bastion.Module:New('resto_druid')
local Player = Bastion.UnitManager:Get('player')
local None = Bastion.UnitManager:Get('none')
local Target = Bastion.UnitManager:Get('target')
local AnomalyDetectionMarkI = Bastion.SpellBook:GetSpell(382499)
local AutoAttack = Bastion.SpellBook:GetSpell(6603)
local MechanismBypass = Bastion.SpellBook:GetSpell(382501)
local OverloadElementalDeposit = Bastion.SpellBook:GetSpell(388213)
local ReviveBattlePets = Bastion.SpellBook:GetSpell(125439)
local WarStomp = Bastion.SpellBook:GetSpell(20549)
local ArmorSkills = Bastion.SpellBook:GetSpell(76275)
local Brawn = Bastion.SpellBook:GetSpell(154743)
local Cultivation = Bastion.SpellBook:GetSpell(20552)
local Endurance = Bastion.SpellBook:GetSpell(20550)
local Languages = Bastion.SpellBook:GetSpell(79746)
local MasterRiding = Bastion.SpellBook:GetSpell(90265)
local NatureResistance = Bastion.SpellBook:GetSpell(20551)
local WeaponSkills = Bastion.SpellBook:GetSpell(76300)
local ActivateEmpowerment = Bastion.SpellBook:GetSpell(357857)
local BlessingofOhnara = Bastion.SpellBook:GetSpell(384522)
local BronzeTimelock = Bastion.SpellBook:GetSpell(374990)
local ChampionAbility = Bastion.SpellBook:GetSpell(356550)
local CenarionWard = Bastion.SpellBook:GetSpell(102351)
local CombatAlly = Bastion.SpellBook:GetSpell(211390)
local ConstructAbility = Bastion.SpellBook:GetSpell(347013)
local CovenantAbility = Bastion.SpellBook:GetSpell(313347)
local GarrisonAbility = Bastion.SpellBook:GetSpell(161691)
local HeartEssence = Bastion.SpellBook:GetSpell(296208)
local HuntingCompanion = Bastion.SpellBook:GetSpell(376280)
local SanityRestorationOrb = Bastion.SpellBook:GetSpell(314955)
local SignatureAbility = Bastion.SpellBook:GetSpell(326526)
local SkywardAscent = Bastion.SpellBook:GetSpell(372610)
local SummonPocopoc = Bastion.SpellBook:GetSpell(360078)
local SurgeForward = Bastion.SpellBook:GetSpell(372608)
local Throw = Bastion.SpellBook:GetSpell(385265)
local VenthyrAbility = Bastion.SpellBook:GetSpell(315594)
local WartimeAbility = Bastion.SpellBook:GetSpell(264739)
local WhirlingSurge = Bastion.SpellBook:GetSpell(361584)
local PocopocZoneAbilitySkill = Bastion.SpellBook:GetSpell(363942)
local DragonridingBasics = Bastion.SpellBook:GetSpell(376777)
local LiftOff = Bastion.SpellBook:GetSpell(383363)
local ThrilloftheSkies = Bastion.SpellBook:GetSpell(383366)
local Vigor = Bastion.SpellBook:GetSpell(383359)
local WindsoftheIsles = Bastion.SpellBook:GetSpell(373586)
local Barkskin = Bastion.SpellBook:GetSpell(22812)
local BearForm = Bastion.SpellBook:GetSpell(5487)
local CatForm = Bastion.SpellBook:GetSpell(768)
local Cyclone = Bastion.SpellBook:GetSpell(33786)
local EntanglingRoots = Bastion.SpellBook:GetSpell(339)
local FerociousBite = Bastion.SpellBook:GetSpell(22568)
local FrenziedRegeneration = Bastion.SpellBook:GetSpell(22842)
local Growl = Bastion.SpellBook:GetSpell(6795)
local Innervate = Bastion.SpellBook:GetSpell(29166)
local Mangle = Bastion.SpellBook:GetSpell(33917)
local MarkoftheWild = Bastion.SpellBook:GetSpell(1126)
local Moonfire = Bastion.SpellBook:GetSpell(8921)
local MoonfireAura = Bastion.SpellBook:GetSpell(164812)
local Prowl = Bastion.SpellBook:GetSpell(5215)
local Rebirth = Bastion.SpellBook:GetSpell(20484)
local Regrowth = Bastion.SpellBook:GetSpell(8936)
local Rejuvenation = Bastion.SpellBook:GetSpell(774)
local RejuvenationAura = Bastion.SpellBook:GetSpell(25299)
local Revive = Bastion.SpellBook:GetSpell(50769)
local Rip = Bastion.SpellBook:GetSpell(1079)
local Shred = Bastion.SpellBook:GetSpell(5221)
local Soothe = Bastion.SpellBook:GetSpell(2908)
local StampedingRoar = Bastion.SpellBook:GetSpell(106898)
local Sunfire = Bastion.SpellBook:GetSpell(93402)
local SunfireAura = Bastion.SpellBook:GetSpell(164815)
local Swiftmend = Bastion.SpellBook:GetSpell(18562)
local TeleportMoonglade = Bastion.SpellBook:GetSpell(18960)
local Thrash = Bastion.SpellBook:GetSpell(106832)
local TigerDash = Bastion.SpellBook:GetSpell(252216)
local TravelForm = Bastion.SpellBook:GetSpell(783)
local UrsolsVortex = Bastion.SpellBook:GetSpell(102793)
local WildGrowth = Bastion.SpellBook:GetSpell(48438)
local Wrath = Bastion.SpellBook:GetSpell(5176)
local AquaticForm = Bastion.SpellBook:GetSpell(276012)
local FlightForm = Bastion.SpellBook:GetSpell(276029)
local TigerDash = Bastion.SpellBook:GetSpell(252216)
local Efflorescence = Bastion.SpellBook:GetSpell(145205)
local IncarnationTreeofLife = Bastion.SpellBook:GetSpell(33891)
local Ironbark = Bastion.SpellBook:GetSpell(102342)
local Lifebloom = Bastion.SpellBook:GetSpell(33763)
local LifebloomAura = Bastion.SpellBook:GetSpell(188550)
local NaturesCure = Bastion.SpellBook:GetSpell(88423)
local NaturesSwiftness = Bastion.SpellBook:GetSpell(132158)
local Revitalize = Bastion.SpellBook:GetSpell(212040)
local Tranquility = Bastion.SpellBook:GetSpell(740)
local MasteryHarmony = Bastion.SpellBook:GetSpell(77495)
local Moonfire = Bastion.SpellBook:GetSpell(8921)
local Wrath = Bastion.SpellBook:GetSpell(5176)
local BearForm = Bastion.SpellBook:GetSpell(5487)
local AdaptiveSwarm = Bastion.SpellBook:GetSpell(391888)
local AdaptiveSwarmBuff = Bastion.SpellBook:GetSpell(391891)
local ClearCasting = Bastion.SpellBook:GetSpell(16870)
local ConvokeTheSpirits = Bastion.SpellBook:GetSpell(391528)
local Flourish = Bastion.SpellBook:GetSpell(197721)
local SoulOfTheForest = Bastion.SpellBook:GetSpell(114108)
local Bursting = Bastion.SpellBook:GetSpell(240443)
local Rake = Bastion.SpellBook:GetSpell(1822)
local RakeAura = Bastion.SpellBook:GetSpell(155722)
local Starsurge = Bastion.SpellBook:GetSpell(197626)
local NaturesVigil = Bastion.SpellBook:GetSpell(124974)
local SpringBlossoms = Bastion.SpellBook:GetSpell(207386)
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
end)
if not lowest then
lowest = Player
end
return lowest
end)
local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit)
local tank = nil
Bastion.UnitManager:EnumFriends(function(unit)
if Player:GetDistance(unit) > 40 then
return false
end
if not Player:CanSee(unit) then
return false
end
if unit:IsDead() then
return false
end
if unit:IsTank() then
tank = unit
return true
end
return false
end)
if tank == nil then
tank = Player
end
return tank
end)
local RejuvUnit = Bastion.UnitManager:CreateCustomUnit('rejuv', 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 not unit:GetAuras():FindMy(Rejuvenation):IsUp() then
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end
end)
if lowest == nil then
lowest = Player
end
return lowest
end)
local SwiftmendUnit = Bastion.UnitManager:CreateCustomUnit('swiftmend', 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 (
Player:CanSee(unit) and (
(unit:GetAuras():FindMy(Regrowth):IsUp())
or
(
unit:GetAuras():FindMy(Rejuvenation):IsUp() and
not unit:GetAuras():FindMy(WildGrowth):IsUp())
)
) then
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end
end)
if lowest == nil then
lowest = Player
end
return lowest
end)
local RestoCommands = Bastion.Command:New('resto')
local PLACE_EFFLO = false
RestoCommands:Register('efflo', 'Request the engine to place an Efflorescence', function()
PLACE_EFFLO = true
Bastion:Print('Efflorescence will be placed on next cast')
end)
local DefaultAPL = Bastion.APL:New('default')
local DamageAPL = Bastion.APL:New('damage')
DefaultAPL:AddSpell(
Efflorescence:CastableIf(function(self)
return PLACE_EFFLO and Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(None):OnCast(function(self)
local loc = Bastion.UnitManager:FindFriendsCentroid(10, 40)
PLACE_EFFLO = false
self:Click(loc)
end)
)
DefaultAPL:AddAction(
'cat_form_shift',
function()
if IsShiftKeyDown() and not Player:GetAuras():FindMy(CatForm):IsUp() and not Player:IsCastingOrChanneling() then
CatForm:Cast(Player)
elseif not IsShiftKeyDown() and Player:GetAuras():FindMy(CatForm):IsUp() then
CancelShapeshiftForm()
end
end
)
DefaultAPL:AddSpell(
NaturesSwiftness:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and
(Lowest:GetHP() < 75 or (Player:GetPartyHPAround(40, 65) >= 2 or Player:GetPartyHPAround(40, 70))
)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
ConvokeTheSpirits:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
self:IsInRange(Player) and (Player:GetPartyHPAround(40, 65) >= 2 or Player:GetPartyHPAround(40, 70) >= 3)
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
Flourish:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
self:IsInRange(Player) and (Player:GetPartyHPAround(40, 65) >= 2 or Player:GetPartyHPAround(40, 70) >= 3) and
(not ConvokeTheSpirits:IsKnownAndUsable() and ConvokeTheSpirits:GetTimeSinceLastCast() > 7) and
WildGrowth:GetTimeSinceLastCast() <= 4
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
AdaptiveSwarm:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Swiftmend:CastableIf(function(self)
return SwiftmendUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(SwiftmendUnit) and
(
SwiftmendUnit:GetHP() <= 85 or
(
Lowest:GetPartyHPAround(30, 90) >= 3 or Lowest:GetPartyHPAround(30, 85) >= 2
)
)
end):SetTarget(SwiftmendUnit)
)
DefaultAPL:AddSpell(
WildGrowth:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and
(
(
Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and
(
Player:GetAuras():FindMy(SoulOfTheForest):GetRemainingTime() <= 5 or
Lowest:GetPartyHPAround(30, 90) >= 2)) or
(Lowest:GetPartyHPAround(30, 90) >= 3 or Lowest:GetPartyHPAround(30, 85) >= 2)) and not Player:IsMoving()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Regrowth:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() < 75 and
(
NaturesSwiftness:GetTimeSinceLastCast() < 2 or Player:GetAuras():FindMy(NaturesSwiftness):IsUp() or
NaturesSwiftness:IsKnownAndUsable()) and not Player:IsMoving()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Regrowth:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and
(Lowest:GetHP() < 70 or (Lowest:GetHP() <= 85 and Player:GetAuras():FindMy(ClearCasting):IsUp())) and
not Player:GetAuras():FindMy(Regrowth):IsUp() and not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and
not Player:IsMoving()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
CenarionWard:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() <= 90
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Ironbark:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() <= 70 and not Lowest:GetAuras():FindMy(CenarionWard):IsUp()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Lifebloom:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and not Player:GetAuras():FindMy(LifebloomAura):IsUp() and Player:IsAffectingCombat()
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
Lifebloom:CastableIf(function(self)
return Tank:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and not Tank:GetAuras():FindMy(LifebloomAura):IsUp() and Tank:IsAffectingCombat()
end):SetTarget(Tank)
)
DefaultAPL:AddSpell(
Rejuvenation:CastableIf(function(self)
return RejuvUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(RejuvUnit) and RejuvUnit:GetHP() <= 94 and
not Player:GetAuras():FindMy(SoulOfTheForest):IsUp()
end):SetTarget(RejuvUnit)
)
DefaultAPL:AddSpell(
Sunfire:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Target) and not Target:GetAuras():FindMy(SunfireAura):IsUp()
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
Moonfire:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Target) and not Target:GetAuras():FindMy(MoonfireAura):IsUp()
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
Starsurge:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Target)
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
Wrath:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Target) and not Player:IsMoving()
end):SetTarget(Target)
)
RestoModule:Sync(function()
if IsShiftKeyDown() and Player:GetAuras():FindMy(CatForm):IsUp() then
return DamageAPL:Execute()
end
DefaultAPL:Execute()
end)
Bastion:Register(RestoModule)

@ -0,0 +1,76 @@
-- APL (Attack priority list) class
local APL = {}
APL.__index = APL
-- Constructor
function APL:New(name)
local self = setmetatable({}, APL)
self.apl = {}
self.variables = {}
self.name = name
return self
end
-- Add a variable to the APL
function APL:SetVariable(name, value)
self.variables[name] = value
end
-- Get and evaluate a variable
function APL:GetVariable(name)
return self.variables[name]
end
-- Add a manual action to the APL
function APL:AddAction(action, cb)
table.insert(self.apl, { action = action, cb = cb })
end
-- Add a spell to the APL
function APL:AddSpell(spell, condition)
local castableFunc = spell.CastableIfFunc
local target = spell:GetTarget()
table.insert(self.apl, { spell = spell, condition = condition, castableFunc = castableFunc, target = target })
end
-- Add an APL to the APL (for sub APLs)
function APL:AddAPL(apl, condition)
table.insert(self.apl, { apl = apl, condition = condition })
end
-- Execute the APL
function APL:Execute()
for _, actor in ipairs(self.apl) do
if actor.apl then
if actor.condition() then
-- print("Bastion: APL:Execute: Executing sub APL " .. actor.apl.name)
actor.apl:Execute()
end
end
if actor.spell then
if actor.condition then
-- print("Bastion: APL:Execute: Condition for spell " .. actor.spell:GetName())
actor.spell:CastableIf(actor.castableFunc):Cast(actor.target,
actor.condition)
end
-- print("Bastion: APL:Execute: No condition for spell " .. actor.spell:GetName())
actor.spell:CastableIf(actor.castableFunc):Cast(actor.target)
end
if actor.action then
-- print("Bastion: APL:Execute: Executing action " .. actor.action)
actor.cb(self)
end
end
end
-- tostring
function APL:__tostring()
return "Bastion.__APL(" .. self.name .. ")"
end
return APL

@ -0,0 +1,245 @@
local Tinkr, Bastion = ...
-- Create a new Aura class
local Aura = {}
function Aura:__index(k)
local response = Bastion.ClassMagic:Resolve(Aura, k)
if response == nil then
response = rawget(self, k)
end
if response == nil then
error("Aura:__index: " .. k .. " does not exist")
end
return response
end
function Aura:__tostring()
return "Bastion.__Aura(" .. self:GetSpell():GetID() .. ")" .. " - " .. (self:GetName() or "''")
end
-- Constructor
function Aura:New(unit, index, type)
if unit == nil then
local self = setmetatable({}, Aura)
self.aura = {
name = nil,
icon = nil,
count = 0,
dispelType = nil,
duration = 0,
expirationTime = 0,
source = nil,
isStealable = false,
nameplateShowPersonal = false,
spellId = 0,
canApplyAura = false,
isBossDebuff = false,
castByPlayer = false,
nameplateShowAll = false,
timeMod = 0,
index = nil,
type = nil,
}
return self
end
local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal,
spellId, canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod = UnitAura(unit.unit, index, type)
local self = setmetatable({}, Aura)
self.aura = {
name = name,
icon = icon,
count = count,
dispelType = dispelType,
duration = duration,
expirationTime = expirationTime,
source = source,
isStealable = isStealable,
nameplateShowPersonal = nameplateShowPersonal,
spellId = spellId,
canApplyAura = canApplyAura,
isBossDebuff = isBossDebuff,
castByPlayer = castByPlayer,
nameplateShowAll = nameplateShowAll,
timeMod = timeMod,
index = index,
type = type,
}
return self
end
function Aura:CreateFromUnitAuraInfo(unitAuraInfo)
local self = setmetatable({}, Aura)
self.aura = {
name = unitAuraInfo.name,
icon = unitAuraInfo.icon,
count = unitAuraInfo.applications,
dispelType = unitAuraInfo.dispelName,
duration = unitAuraInfo.duration,
expirationTime = unitAuraInfo.expirationTime,
source = unitAuraInfo.sourceUnit,
isStealable = unitAuraInfo.isStealable,
nameplateShowPersonal = unitAuraInfo.nameplateShowPersonal,
spellId = unitAuraInfo.spellId,
canApplyAura = unitAuraInfo.canApplyAura,
isBossDebuff = unitAuraInfo.isBossAura,
castByPlayer = unitAuraInfo.isFromPlayerOrPlayerPet,
nameplateShowAll = unitAuraInfo.nameplateShowAll,
timeMod = unitAuraInfo.timeMod,
index = nil,
type = unitAuraInfo.isHarmful and "HARMFUL" or "HELPFUL",
}
return self
end
-- Check if the aura is valid
function Aura:IsValid()
return self.aura.name ~= nil
end
-- Check if the aura is up
function Aura:IsUp()
return self:IsValid() and (self:GetDuration() == 0 or self:GetRemainingTime() > 0)
end
-- Get the auras index
function Aura:GetIndex()
return self.aura.index
end
-- Get the auras type
function Aura:GetType()
return self.aura.type
end
-- Get the auras name
function Aura:GetName()
return self.aura.name
end
-- Get the auras icon
function Aura:GetIcon()
return self.aura.icon
end
-- Get the auras count
function Aura:GetCount()
return self.aura.count
end
-- Get the auras dispel type
function Aura:GetDispelType()
return self.aura.dispelType
end
-- Get the auras duration
function Aura:GetDuration()
return self.aura.duration
end
-- Get the auras remaining time
function Aura:GetRemainingTime()
local remainingTime = self.aura.expirationTime - GetTime()
if remainingTime < 0 then
remainingTime = 0
end
return remainingTime
end
-- Get the auras expiration time
function Aura:GetExpirationTime()
return self.aura.expirationTime
end
-- Get the auras source
function Aura:GetSource()
return Bastion.UnitManager[self.aura.source]
end
-- Get the auras stealable status
function Aura:GetIsStealable()
return self.aura.isStealable
end
-- Get the auras nameplate show personal status
function Aura:GetNameplateShowPersonal()
return self.aura.nameplateShowPersonal
end
-- Get the auras spell id
function Aura:GetSpell()
return Bastion.Spell:New(self.aura.spellId)
end
-- Get the auras can apply aura status
function Aura:GetCanApplyAura()
return self.aura.canApplyAura
end
-- Get the auras is boss debuff status
function Aura:GetIsBossDebuff()
return self.aura.isBossDebuff
end
-- Get the auras cast by player status
function Aura:GetCastByPlayer()
return self.aura.castByPlayer
end
-- Get the auras nameplate show all status
function Aura:GetNameplateShowAll()
return self.aura.nameplateShowAll
end
-- Get the auras time mod
function Aura:GetTimeMod()
return self.aura.timeMod
end
-- Check if the aura is a buff
function Aura:IsBuff()
return self.aura.type == "HELPFUL"
end
-- Check if the aura is a debuff
function Aura:IsDebuff()
return self.aura.type == "HARMFUL"
end
-- Check if the aura is dispelable by a spell
function Aura:IsDispelableBySpell(spell)
if self:GetDispelType() == nil then
return false
end
if self:GetDispelType() == 'Magic' and spell:IsMagicDispel() then
return true
end
if self:GetDispelType() == 'Curse' and spell:IsCurseDispel() then
return true
end
if self:GetDispelType() == 'Poison' and spell:IsPoisonDispel() then
return true
end
if self:GetDispelType() == 'Disease' and spell:IsDiseaseDispel() then
return true
end
return false
end
return Aura

@ -0,0 +1,229 @@
local Tinkr, Bastion = ...
-- Create a new AuraTable class
local AuraTable = {}
AuraTable.__index = AuraTable
-- Constructor
function AuraTable:New(unit)
local self = setmetatable({}, AuraTable)
self.unit = unit
self.buffs = {}
self.debuffs = {}
self.auras = {}
-- Our player is usually the most important unit, so we cache the auras for it
self.playerAppliedBuffs = {}
self.playerAppliedDebuffs = {}
self.playerAuras = {}
self.guid = unit:GetGUID()
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
local u = Bastion.UnitManager[unit]
if not self.unit:IsUnit(unit) then
return
end
local addedAuras = auras.addedAuras
if #addedAuras > 0 then
for i = 1, #addedAuras do
local aura = Bastion.Aura:CreateFromUnitAuraInfo(addedAuras[i])
if aura:IsBuff() then
if aura:GetSource():Exists() and aura:GetSource():IsUnit(Bastion.UnitManager['player']) then
if not self.playerAppliedBuffs[aura:GetSpell():GetID()] then
self.playerAppliedBuffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.playerAppliedBuffs[aura:GetSpell():GetID()], aura)
else
if not self.buffs[aura:GetSpell():GetID()] then
self.buffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.buffs[aura:GetSpell():GetID()], aura)
end
else
if aura:GetSource():Exists() and aura:GetSource():IsUnit(Bastion.UnitManager['player']) then
if not self.playerAppliedDebuffs[aura:GetSpell():GetID()] then
self.playerAppliedDebuffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.playerAppliedDebuffs[aura:GetSpell():GetID()], aura)
else
if not self.debuffs[aura:GetSpell():GetID()] then
self.debuffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.debuffs[aura:GetSpell():GetID()], aura)
end
end
end
end
end)
return self
end
-- Get a units buffs
function AuraTable:GetUnitBuffs()
for i = 1, 40 do
local aura = Bastion.Aura:New(self.unit, i, 'HELPFUL')
if aura:IsValid() then
if aura:GetSource():Exists() and aura:GetSource():IsUnit(Bastion.UnitManager['player']) then
if not self.playerAppliedBuffs[aura:GetSpell():GetID()] then
self.playerAppliedBuffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.playerAppliedBuffs[aura:GetSpell():GetID()], aura)
else
if not self.buffs[aura:GetSpell():GetID()] then
self.buffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.buffs[aura:GetSpell():GetID()], aura)
end
end
end
end
-- Get a units debuffs
function AuraTable:GetUnitDebuffs()
for i = 1, 40 do
local aura = Bastion.Aura:New(self.unit, i, 'HARMFUL')
if aura:IsValid() then
if aura:GetSource():Exists() and aura:GetSource():IsUnit(Bastion.UnitManager['player']) then
if not self.playerAppliedDebuffs[aura:GetSpell():GetID()] then
self.playerAppliedDebuffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.playerAppliedDebuffs[aura:GetSpell():GetID()], aura)
else
if not self.debuffs[aura:GetSpell():GetID()] then
self.debuffs[aura:GetSpell():GetID()] = {}
end
table.insert(self.debuffs[aura:GetSpell():GetID()], aura)
end
end
end
end
local function merge(t1, t2)
for k, v in pairs(t2) do
if type(v) == "table" then
if type(t1[k] or false) == "table" then
merge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
-- Update auras
function AuraTable:Update()
self:Clear()
self.lastUpdate = GetTime()
self:GetUnitBuffs()
self:GetUnitDebuffs()
self.auras = merge(self.buffs, self.debuffs)
self.playerAuras = merge(self.playerAppliedBuffs, self.playerAppliedDebuffs)
end
-- Get a units auras
function AuraTable:GetUnitAuras()
-- For token units, we need to check if the GUID has changed
if self.unit:GetGUID() ~= self.guid then
self.guid = self.unit:GetGUID()
self:Update()
return self.auras
end
-- Cache the auras for the unit so we don't have to query the API every time we want to check if the unit has a specific aura or not
-- If it's less than .4 seconds since the last time we queried the API, return the cached auras
if self.lastUpdate and GetTime() - self.lastUpdate < 0.5 then
return self.auras
end
self:Update()
return self.auras
end
-- Get a units auras
function AuraTable:GetMyUnitAuras()
-- For token units, we need to check if the GUID has changed
if self.unit:GetGUID() ~= self.guid then
self.guid = self.unit:GetGUID()
self:Update()
return self.playerAuras
end
-- Cache the auras for the unit so we don't have to query the API every time we want to check if the unit has a specific aura or not
-- If it's less than .4 seconds since the last time we queried the API, return the cached auras
if self.lastUpdate and GetTime() - self.lastUpdate < 0.5 then
return self.playerAuras
end
self:Update()
return self.playerAuras
end
-- Clear the aura table
function AuraTable:Clear()
self.buffs = {}
self.debuffs = {}
self.auras = {}
self.playerAppliedBuffs = {}
self.playerAppliedDebuffs = {}
self.playerAuras = {}
end
-- Check if the unit has a specific aura
function AuraTable:Find(spell)
local auras = self:GetUnitAuras()
local aura = auras[spell:GetID()]
if aura then
return aura[1]
end
return Bastion.Aura:New()
end
function AuraTable:FindMy(spell)
local auras = self:GetMyUnitAuras()
local aura = auras[spell:GetID()]
if aura then
return aura[1]
end
return Bastion.Aura:New()
end
-- Has any stealable aura
function AuraTable:HasAnyStealableAura()
for _, auras in pairs(self:GetUnitAuras()) do
for _, aura in pairs(auras) do
if aura:GetIsStealable() then
return true
end
end
end
return false
end
-- Has any dispelable aura
function AuraTable:HasAnyDispelableAura(spell)
for _, auras in pairs(self:GetUnitAuras()) do
for _, aura in pairs(auras) do
if aura:IsDebuff() and aura:IsDispelableBySpell(spell) then
return true
end
end
end
return false
end
return AuraTable

@ -0,0 +1,46 @@
local Cache = {}
Cache.__index = Cache
-- Constructor
function Cache:New()
local self = setmetatable({}, Cache)
self.cache = {}
return self
end
function Cache:Set(key, value, duration)
self.cache = self.cache or {}
self.cache[key] = {
value = value,
duration = duration,
time = GetTime()
}
end
function Cache:Get(key)
self.cache = self.cache or {}
local cache = self.cache[key]
if cache and cache.time + cache.duration > GetTime() then
return cache.value
end
-- It's old or doesn't exist, so remove it
self.cache[key] = nil
return nil
end
function Cache:IsCached(key)
self.cache = self.cache or {}
local cache = self.cache[key]
if cache and cache.time + cache.duration > GetTime() then
return true
end
-- It's old or doesn't exist, so remove it
self.cache[key] = nil
return false
end
return Cache

@ -0,0 +1,68 @@
local Tinkr, Bastion = ...
-- Define a Cacheable class
local Cacheable = {
cache = nil,
callback = nil,
value = nil
}
-- On index check the cache to be valid and return the value or reconstruct the value and return it
function Cacheable:__index(k)
if Cacheable[k] then
return Cacheable[k]
end
if self.cache == nil then
error("Cacheable:__index: " .. k .. " does not exist")
end
if not self.cache:IsCached('self') then
self.value = self.callback()
self.cache:Set('self', self.value, 0.5)
end
return self.value[k]
end
-- When the object is accessed return the value
function Cacheable:__tostring()
return "Bastion.__Cacheable(" .. tostring(self.value) .. ")"
end
-- Create
function Cacheable:New(value, cb)
local self = setmetatable({}, Cacheable)
self.cache = Bastion.Cache:New()
self.value = value
self.callback = cb
self.cache:Set('self', self.value, 0.5)
return self
end
-- Try to update the value
function Cacheable:TryUpdate()
if self.cache:IsCached("value") then
self.value = self.callback()
end
end
-- Update the value
function Cacheable:Update()
self.value = self.callback()
end
-- Set a new value
function Cacheable:Set(value)
self.value = value
end
-- Set a new callback
function Cacheable:SetCallback(cb)
self.callback = cb
end
return Cacheable

@ -0,0 +1,51 @@
local Tinkr, Bastion = ...
-- Create a new Class class
local Class = {}
function Class:__index(k)
local response = Bastion.ClassMagic:Resolve(Class, k)
if response == nil then
response = rawget(self, k)
end
if response == nil then
error("Class:__index: " .. k .. " does not exist")
end
return response
end
-- Constructor
function Class:New(locale, name, id)
local self = setmetatable({}, Class)
self.class = {
locale = locale,
name = name,
id = id
}
return self
end
-- Get the classes locale
function Class:GetLocale()
return self.class.locale
end
-- Get the classes name
function Class:GetName()
return self.class.name
end
-- Get the classes id
function Class:GetID()
return self.class.id
end
-- Return the classes color
function Class:GetColor()
return C_ClassColor.GetClassColor(self.class.name)
end
return Class

@ -0,0 +1,49 @@
local ClassMagic = {}
ClassMagic.__index = ClassMagic
function ClassMagic:Resolve(Class, key)
if Class[key] or Class[key] == false then
return Class[key]
end
if Class['Get' .. key:sub(1, 1):upper() .. key:sub(2)] then
local func = Class['Get' .. key:sub(1, 1):upper() .. key:sub(2)]
-- Call the function and return the result if there's more than one return value return it as a table
local result = { func(self) }
if #result > 1 then
return result
end
return result[1]
end
if Class['Get' .. key:upper()] then
local func = Class['Get' .. key:upper()]
-- Call the function and return the result if there's more than one return value return it as a table
local result = { func(self) }
if #result > 1 then
return result
end
return result[1]
end
if Class['Is' .. key:upper()] then
local func = Class['Is' .. key:upper()]
-- Call the function and return the result if there's more than one return value return it as a table
local result = { func(self) }
if #result > 1 then
return result
end
return result[1]
end
return Class[key]
end
return ClassMagic

@ -0,0 +1,60 @@
-- Create a wow command handler class
local Command = {}
Command.__index = Command
function Command:__tostring()
return "Command(" .. self.command .. ")"
end
function Command:New(prefix)
local self = setmetatable({}, Command)
self.prefix = prefix
self.commands = {}
_G['SLASH_' .. prefix:upper() .. '1'] = "/" .. prefix
SlashCmdList[prefix:upper()] = function(msg, editbox)
self:OnCommand(msg)
end
return self
end
function Command:Register(command, helpmsg, cb)
self.commands[command] = {
helpmsg = helpmsg,
cb = cb
}
end
function Command:Parse(msg)
local args = {}
for arg in msg:gmatch("%S+") do
table.insert(args, arg)
end
return args
end
function Command:OnCommand(msg)
local args = self:Parse(msg)
if #args == 0 then
self:PrintHelp()
return
end
local runner = self.commands[args[1]]
if runner then
runner.cb(args)
end
end
function Command:PrintHelp()
for k, v in pairs(self.commands) do
print('/' .. self.prefix .. ' ' .. k .. " - " .. v.helpmsg)
end
end
return Command

@ -0,0 +1,60 @@
-- Create an EventManager class
local EventManager = {
events = {},
eventHandlers = {},
wowEventHandlers = {},
frame = nil
}
EventManager.__index = EventManager
-- Constructor
function EventManager:New()
local self = setmetatable({}, EventManager)
self.events = {}
self.eventHandlers = {}
self.wowEventHandlers = {}
-- Frame for wow events
self.frame = CreateFrame("Frame")
self.frame:SetScript('OnEvent', function(f, event, ...)
if self.wowEventHandlers[event] then
for _, callback in ipairs(self.wowEventHandlers[event]) do
callback(...)
end
end
end)
return self
end
-- Register an event
function EventManager:RegisterEvent(event, handler)
if not self.events[event] then
self.events[event] = {}
end
table.insert(self.events[event], handler)
end
-- Register a wow event
function EventManager:RegisterWoWEvent(event, handler)
if not self.wowEventHandlers[event] then
self.wowEventHandlers[event] = {}
self.frame:RegisterEvent(event)
end
table.insert(self.wowEventHandlers[event], handler)
end
-- Trigger an event
function EventManager:TriggerEvent(event, ...)
if self.events[event] then
for _, handler in pairs(self.events[event]) do
handler(...)
end
end
end
return EventManager

@ -0,0 +1,343 @@
local Tinkr, Bastion = ...
-- Create a new Item class
local Item = {
UsableIfFunc = false,
PreUseFunc = false,
OnUseFunc = false,
wasLooking = false,
lastUseAt = 0,
conditions = {},
target = false,
}
local usableExcludes = {
[18562] = true,
}
function Item:__index(k)
local response = Bastion.ClassMagic:Resolve(Item, k)
if response == nil then
response = rawget(self, k)
end
if response == nil then
error("Item:__index: " .. k .. " does not exist")
end
return response
end
-- tostring
function Item:__tostring()
return "Bastion.__Item(" .. self:GetID() .. ")" .. " - " .. self:GetName()
end
-- Constructor
function Item:New(id)
local self = setmetatable({}, Item)
self.ItemID = id
return self
end
-- Get the Items id
function Item:GetID()
return self.ItemID
end
-- Get the Items name
function Item:GetName()
return GetItemInfo(self:GetID())
end
-- Get the Items icon
function Item:GetIcon()
return select(3, GetItemInfo(self:GetID()))
end
-- Get the Items cooldown
function Item:GetCooldown()
return select(2, GetItemCooldown(self:GetID()))
end
-- Return the Usable function
function Item:GetUsableFunction()
return self.UsableIfFunc
end
-- Return the preUse function
function Item:GetPreUseFunction()
return self.PreUseFunc
end
-- Get the on Use func
function Item:GetOnUseFunction()
return self.OnUseFunc
end
-- Get the Items cooldown remaining
function Item:GetCooldownRemaining()
local start, duration = GetItemCooldown(self:GetID())
return start + duration - GetTime()
end
-- Use the Item
function Item:Use(unit, condition)
if condition and not self:EvaluateCondition(condition) then
return false
end
if not self:Usable() then
return false
end
-- Call pre Use function
if self:GetPreUseFunction() then
self:GetPreUseFunction()(self)
end
-- Check if the mouse was looking
self.wasLooking = IsMouselooking()
-- Use the Item
UseItemByName(self:GetName(), unit.unit)
Bastion:Debug("Using", self)
-- Set the last Use time
self.lastUseAt = GetTime()
-- Call post Use function
if self:GetOnUseFunction() then
self:GetOnUseFunction()(self)
end
end
-- Check if the Item is known
function Item:IsEquipped()
return IsEquippedItem(self:GetID())
end
-- Check if the Item is on cooldown
function Item:IsOnCooldown()
return select(2, GetItemCooldown(self:GetID())) > 0
end
-- Check if the Item is usable
function Item:IsUsable()
local usable, noMana = IsUsableItem(self:GetID())
return usable or usableExcludes[self:GetID()]
end
-- Check if the Item is Usable
function Item:IsEquippedAndUsable()
return (self:IsEquippable() and self:IsEquipped()) or (not self:IsEquippable() and self:IsUsable())
end
-- Is equippable
function Item:IsEquippable()
return IsEquippableItem(self:GetID())
end
-- Check if the Item is Usable
function Item:Usable()
if self:GetUsableFunction() then
return self:GetUsableFunction()(self)
end
return self:IsKnownAndUsable()
end
-- Set a script to check if the Item is Usable
function Item:UsableIf(func)
self.UsableIfFunc = func
return self
end
-- Set a script to run before the Item has been Use
function Item:PreUse(func)
self.PreUseFunc = func
return self
end
-- Set a script to run after the Item has been Use
function Item:OnUse(func)
self.OnUseFunc = func
return self
end
-- Get was looking
function Item:GetWasLooking()
return self.wasLooking
end
-- Click the Item
function Item:Click(x, y, z)
if type(x) == 'table' then
x, y, z = x.x, x.y, x.z
end
if IsItemPending() == 64 then
MouselookStop()
Click(x, y, z)
if self:GetWasLooking() then
MouselookStart()
end
return true
end
return false
end
-- Check if the Item is Usable and Use it
function Item:Call(unit)
if self:Usable() then
self:Use(unit)
return true
end
return false
end
-- Check if the Item is in range of the unit
function Item:IsInRange(unit)
local name, rank, icon, UseTime, Itemmin, Itemmax, ItemID = GetItemInfo(self:GetID())
local them = Object(unit.unit)
local tx, ty, tz = ObjectPosition(unit.unit)
local px, py, pz = ObjectPosition('player')
if not them then
return false
end
if tx == 0 and ty == 0 and tz == 0 then
return true
end
local combatReach = ObjectCombatReach("player")
local themCombatReach = ObjectCombatReach(unit.unit)
if Bastion.UnitManager['player']:InMelee(unit) and Itemmin == 0 then
return true
end
local distance = FastDistance(px, py, pz, tx, ty, tz)
if Itemmax
and distance >= Itemmin
and distance <= combatReach + themCombatReach + Itemmax
then
return true
end
return false
end
-- Get the last use time
function Item:GetLastUseTime()
return self.lastUseAt
end
-- Get time since last use
function Item:GetTimeSinceLastUse()
if not self:GetLastUseTime() then
return math.huge
end
return GetTime() - self:GetLastUseTime()
end
-- Get the Items charges
function Item:GetCharges()
return GetItemCharges(self:GetID())
end
-- Get the Items charges remaining
function Item:GetChargesRemaining()
local charges, maxCharges, start, duration = GetItemCharges(self:GetID())
return charges
end
-- Create a condition for the Item
function Item:Condition(name, func)
self.conditions[name] = {
func = func
}
return self
end
-- Get a condition for the Item
function Item:GetCondition(name)
local condition = self.conditions[name]
if condition then
return condition
end
return nil
end
-- Evaluate a condition for the Item
function Item:EvaluateCondition(name)
local condition = self:GetCondition(name)
if condition then
return condition.func(self)
end
return false
end
-- Check if the Item has a condition
function Item:HasCondition(name)
local condition = self:GetCondition(name)
if condition then
return true
end
return false
end
-- Set the Items target
function Item:SetTarget(unit)
self.target = unit
return self
end
-- Get the Items target
function Item:GetTarget()
return self.target
end
-- IsMagicDispel
function Item:IsMagicDispel()
return ({
[88423] = true
})[self:GetID()]
end
-- IsCurseDispel
function Item:IsCurseDispel()
return ({
[88423] = true
})[self:GetID()]
end
-- IsPoisonDispel
function Item:IsPoisonDispel()
return ({
[88423] = true
})[self:GetID()]
end
-- IsDiseaseDispel
function Item:IsDiseaseDispel()
return ({
})[self:GetID()]
end
function Item:IsItem(Item)
return self:GetID() == Item:GetID()
end
return Item

@ -0,0 +1,23 @@
local Tinkr, Bastion = ...
-- Create a new ItemBook class
local ItemBook = {}
ItemBook.__index = ItemBook
-- Constructor
function ItemBook:New()
local self = setmetatable({}, ItemBook)
self.items = {}
return self
end
-- Get a spell from the ItemBook
function ItemBook:GetItem(id)
if self.items[id] == nil then
self.items[id] = Bastion.Item:New(id)
end
return self.items[id]
end
return ItemBook

@ -0,0 +1,65 @@
-- Create a module class for a bastion module
local Module = {}
Module.__index = Module
-- __tostring
function Module:__tostring()
return "Bastion.__Module(" .. self.name .. ")"
end
function Module:New(name)
local module = {}
setmetatable(module, Module)
module.name = name
module.enabled = false
module.synced = {}
return module
end
-- Enable the module
function Module:Enable()
self.enabled = true
end
-- Disable the module
function Module:Disable()
self.enabled = false
end
-- Toggle the module
function Module:Toggle()
if self.enabled then
self:Disable()
else
self:Enable()
end
end
-- Add a function to the sync list
function Module:Sync(func)
table.insert(self.synced, func)
end
-- Remove a function from the sync list
function Module:Unsync(func)
for i = 1, #self.synced do
if self.synced[i] == func then
table.remove(self.synced, i)
return
end
end
end
-- Sync
function Module:Tick()
if self.enabled then
for i = 1, #self.synced do
self.synced[i]()
end
end
end
return Module

@ -0,0 +1,340 @@
local Tinkr, Bastion = ...
-- Create a new Spell class
local Spell = {
CastableIfFunc = false,
PreCastFunc = false,
OnCastFunc = false,
wasLooking = false,
lastCastAt = 0,
conditions = {},
target = false,
}
local usableExcludes = {
[18562] = true,
}
function Spell:__index(k)
local response = Bastion.ClassMagic:Resolve(Spell, k)
if response == nil then
response = rawget(self, k)
end
if response == nil then
error("Spell:__index: " .. k .. " does not exist")
end
return response
end
-- tostring
function Spell:__tostring()
return "Bastion.__Spell(" .. self:GetID() .. ")" .. " - " .. self:GetName()
end
-- Constructor
function Spell:New(id)
local self = setmetatable({}, Spell)
self.spellID = id
return self
end
-- Get the spells id
function Spell:GetID()
return self.spellID
end
-- Get the spells name
function Spell:GetName()
return GetSpellInfo(self:GetID())
end
-- Get the spells icon
function Spell:GetIcon()
return select(3, GetSpellInfo(self:GetID()))
end
-- Get the spells cooldown
function Spell:GetCooldown()
return select(2, GetSpellCooldown(self:GetID()))
end
-- Return the castable function
function Spell:GetCastableFunction()
return self.CastableIfFunc
end
-- Return the precast function
function Spell:GetPreCastFunction()
return self.PreCastFunc
end
-- Get the on cast func
function Spell:GetOnCastFunction()
return self.OnCastFunc
end
-- Get the spells cooldown remaining
function Spell:GetCooldownRemaining()
local start, duration = GetSpellCooldown(self:GetID())
return start + duration - GetTime()
end
-- Cast the spell
function Spell:Cast(unit, condition)
if condition and not self:EvaluateCondition(condition) then
return false
end
if not self:Castable() then
return false
end
-- Call pre cast function
if self:GetPreCastFunction() then
self:GetPreCastFunction()(self)
end
-- Check if the mouse was looking
self.wasLooking = IsMouselooking()
-- Cast the spell
CastSpellByName(self:GetName(), unit.unit)
Bastion:Debug("Casting", self)
-- Set the last cast time
self.lastCastAt = GetTime()
-- Call post cast function
if self:GetOnCastFunction() then
self:GetOnCastFunction()(self)
end
end
-- Check if the spell is known
function Spell:IsKnown()
local isKnown = IsSpellKnown(self:GetID())
local isPlayerSpell = IsPlayerSpell(self:GetID())
return isKnown or isPlayerSpell
end
-- Check if the spell is on cooldown
function Spell:IsOnCooldown()
return select(2, GetSpellCooldown(self:GetID())) > 0
end
-- Check if the spell is usable
function Spell:IsUsable()
local usable, noMana = IsUsableSpell(self:GetID())
return usable or usableExcludes[self:GetID()]
end
-- Check if the spell is castable
function Spell:IsKnownAndUsable()
return self:IsKnown() and not self:IsOnCooldown() and self:IsUsable()
end
-- Check if the spell is castable
function Spell:Castable()
if self:GetCastableFunction() then
return self:GetCastableFunction()(self)
end
return self:IsKnownAndUsable()
end
-- Set a script to check if the spell is castable
function Spell:CastableIf(func)
self.CastableIfFunc = func
return self
end
-- Set a script to run before the spell has been cast
function Spell:PreCast(func)
self.PreCastFunc = func
return self
end
-- Set a script to run after the spell has been cast
function Spell:OnCast(func)
self.OnCastFunc = func
return self
end
-- Get was looking
function Spell:GetWasLooking()
return self.wasLooking
end
-- Click the spell
function Spell:Click(x, y, z)
if type(x) == 'table' then
x, y, z = x.x, x.y, x.z
end
if IsSpellPending() == 64 then
MouselookStop()
Click(x, y, z)
if self:GetWasLooking() then
MouselookStart()
end
return true
end
return false
end
-- Check if the spell is castable and cast it
function Spell:Call(unit)
if self:Castable() then
self:Cast(unit)
return true
end
return false
end
-- Check if the spell is in range of the unit
function Spell:IsInRange(unit)
local name, rank, icon, castTime, spellmin, spellmax, spellID = GetSpellInfo(self:GetID())
local them = Object(unit.unit)
local tx, ty, tz = ObjectPosition(unit.unit)
local px, py, pz = ObjectPosition('player')
if not them then
return false
end
if tx == 0 and ty == 0 and tz == 0 then
return true
end
local combatReach = ObjectCombatReach("player")
local themCombatReach = ObjectCombatReach(unit.unit)
if Bastion.UnitManager['player']:InMelee(unit) and spellmin == 0 then
return true
end
local distance = FastDistance(px, py, pz, tx, ty, tz)
if spellmax
and distance >= spellmin
and distance <= combatReach + themCombatReach + spellmax
then
return true
end
return false
end
-- Get the last cast time
function Spell:GetLastCastTime()
return self.lastCastAt
end
-- Get time since last cast
function Spell:GetTimeSinceLastCast()
if not self:GetLastCastTime() then
return math.huge
end
return GetTime() - self:GetLastCastTime()
end
-- Get the spells charges
function Spell:GetCharges()
return GetSpellCharges(self:GetID())
end
-- Get the spells charges remaining
function Spell:GetChargesRemaining()
local charges, maxCharges, start, duration = GetSpellCharges(self:GetID())
return charges
end
-- Create a condition for the spell
function Spell:Condition(name, func)
self.conditions[name] = {
func = func
}
return self
end
-- Get a condition for the spell
function Spell:GetCondition(name)
local condition = self.conditions[name]
if condition then
return condition
end
return nil
end
-- Evaluate a condition for the spell
function Spell:EvaluateCondition(name)
local condition = self:GetCondition(name)
if condition then
return condition.func(self)
end
return false
end
-- Check if the spell has a condition
function Spell:HasCondition(name)
local condition = self:GetCondition(name)
if condition then
return true
end
return false
end
-- Set the spells target
function Spell:SetTarget(unit)
self.target = unit
return self
end
-- Get the spells target
function Spell:GetTarget()
return self.target
end
-- IsMagicDispel
function Spell:IsMagicDispel()
return ({
[88423] = true
})[self:GetID()]
end
-- IsCurseDispel
function Spell:IsCurseDispel()
return ({
[88423] = true
})[self:GetID()]
end
-- IsPoisonDispel
function Spell:IsPoisonDispel()
return ({
[88423] = true
})[self:GetID()]
end
-- IsDiseaseDispel
function Spell:IsDiseaseDispel()
return ({
})[self:GetID()]
end
function Spell:IsSpell(spell)
return self:GetID() == spell:GetID()
end
return Spell

@ -0,0 +1,23 @@
local Tinkr, Bastion = ...
-- Create a new SpellBook class
local SpellBook = {}
SpellBook.__index = SpellBook
-- Constructor
function SpellBook:New()
local self = setmetatable({}, SpellBook)
self.spells = {}
return self
end
-- Get a spell from the spellbook
function SpellBook:GetSpell(id)
if self.spells[id] == nil then
self.spells[id] = Bastion.Spell:New(id)
end
return self.spells[id]
end
return SpellBook

@ -0,0 +1,41 @@
local Tinkr, Bastion = ...
-- Create a new Timer class
local Timer = {
startTime = nil,
resetAfterCombat = false,
}
Timer.__index = Timer
-- Constructor
function Timer:New(type)
local self = setmetatable({}, Timer)
self.startTime = nil
self.type = type
return self
end
-- Start the timer
function Timer:Start()
self.startTime = GetTime()
end
-- Get the time since the timer was started
function Timer:GetTime()
if not self:IsRunning() then
return 0
end
return GetTime() - self.startTime
end
-- Check if the timer is running
function Timer:IsRunning()
return self.startTime ~= nil
end
-- Reset the timer
function Timer:Reset()
self.startTime = nil
end
return Timer

@ -0,0 +1,368 @@
local Tinkr, Bastion = ...
-- Create a new Unit class
local Unit = {
cache = nil,
aura_table = nil,
unit = nil
}
function Unit:__index(k)
local response = Bastion.ClassMagic:Resolve(Unit, k)
if response == nil then
response = rawget(self, k)
end
if response == nil then
error("Unit:__index: " .. k .. " does not exist")
end
return response
end
-- tostring
function Unit:__tostring()
return "Bastion.__Unit(" .. self.unit .. ")" .. " - " .. (self:GetName() or '')
end
-- Constructor
function Unit:New(unit)
local self = setmetatable({}, Unit)
self.unit = unit
self.cache = Bastion.Cache:New()
self.aura_table = Bastion.AuraTable:New(self)
return self
end
-- Check if the unit is valid
function Unit:IsValid()
return self.unit ~= nil and self:Exists()
end
-- Check if the unit exists
function Unit:Exists()
return UnitExists(self.unit)
end
-- Get the units token
function Unit:Token()
return self.unit
end
-- Get the units name
function Unit:GetName()
return UnitName(self.unit)
end
-- Get the units GUID
function Unit:GetGUID()
return ObjectGUID(self.unit)
end
-- Get the units health
function Unit:GetHealth()
return UnitHealth(self.unit)
end
-- Get the units max health
function Unit:GetMaxHealth()
return UnitHealthMax(self.unit)
end
-- Get the units health percentage
function Unit:GetHP()
return self:GetHealth() / self:GetMaxHealth() * 100
end
function Unit:GetHealthPercent()
return self:GetHP()
end
-- Get the units power type
function Unit:GetPowerType()
return UnitPowerType(self.unit)
end
-- Get the units power
function Unit:GetPower(powerType)
local powerType = powerType or self:GetPowerType()
return UnitPower(self.unit, powerType)
end
-- Get the units max power
function Unit:GetMaxPower(powerType)
local powerType = powerType or self:GetPowerType()
return UnitPowerMax(self.unit, powerType)
end
-- Get the units power percentage
function Unit:GetPP(powerType)
local powerType = powerType or self:GetPowerType()
return self:GetPower(powerType) / self:GetMaxPower(powerType) * 100
end
-- Get the units power deficit
function Unit:GetPowerDeficit(powerType)
local powerType = powerType or self:GetPowerType()
return self:GetMaxPower(powerType) - self:GetPower(powerType)
end
-- Get the units position
function Unit:GetPosition()
local x, y, z = ObjectPosition(self.unit)
return Bastion.Vector3:New(x, y, z)
end
-- Get the units distance from another unit
function Unit:GetDistance(unit)
local pself = self:GetPosition()
local punit = unit:GetPosition()
return pself:Distance(punit)
end
-- Is the unit dead
function Unit:IsDead()
return UnitIsDeadOrGhost(self.unit)
end
-- Is the unit alive
function Unit:IsAlive()
return not UnitIsDeadOrGhost(self.unit)
end
-- Is the unit a pet
function Unit:IsPet()
return UnitIsUnit(self.unit, "pet")
end
-- Is the unit a friendly unit
function Unit:IsFriendly()
return UnitIsFriend("player", self.unit)
end
-- Is the unit a hostile unit
function Unit:IsHostile()
return UnitIsEnemy("player", self.unit)
end
-- Is the unit a boss
function Unit:IsBoss()
return UnitClassification(self.unit) == "worldboss"
end
-- Is the unit a target
function Unit:IsTarget()
return UnitIsUnit(self.unit, "target")
end
-- Is the unit a focus
function Unit:IsFocus()
return UnitIsUnit(self.unit, "focus")
end
-- Is the unit a mouseover
function Unit:IsMouseover()
return UnitIsUnit(self.unit, "mouseover")
end
-- Is the unit a tank
function Unit:IsTank()
return UnitGroupRolesAssigned(self.unit) == "TANK"
end
-- Is the unit a healer
function Unit:IsHealer()
return UnitGroupRolesAssigned(self.unit) == "HEALER"
end
-- Is the unit a damage dealer
function Unit:IsDamage()
return UnitGroupRolesAssigned(self.unit) == "DAMAGER"
end
-- Is the unit a player
function Unit:IsPlayer()
return UnitIsPlayer(self.unit)
end
-- Is the unit a player controlled unit
function Unit:IsPCU()
return UnitPlayerControlled(self.unit)
end
-- Get if the unit is affecting combat
function Unit:IsAffectingCombat()
return UnitAffectingCombat(self.unit)
end
-- Get the units class id
function Unit:GetClass()
local locale, class, classID = UnitClass(self.unit)
return Bastion.Class:New(locale, class, classID)
end
-- Get the units auras
function Unit:GetAuras()
return self.aura_table
end
-- Get the raw unit
function Unit:GetRawUnit()
return self.unit
end
local isClassicWow = select(4, GetBuildInfo()) < 40000
-- Check if two units are in melee
function Unit:InMelee(unit)
local combatReach = ObjectCombatReach(self.unit)
local themCombatReach = ObjectCombatReach(unit.unit)
if not combatReach or not themCombatReach then
return false
end
return self:GetDistance(unit)
< math.max(combatReach + 1.3333 + themCombatReach, 5)
end
local losFlag = bit.bor(0x1, 0x10, 0x100000)
-- Check if the unit can see another unit
function Unit:CanSee(unit)
-- mechagon smoke cloud
-- local mechagonID = 2097
-- local smokecloud = 298602
-- local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceID, instanceGroupSize, LfgDungeonID =
-- GetInstanceInfo()
-- otherUnit = otherUnit and otherUnit or "player"
-- if instanceID == 2097 then
-- if (self:debuff(smokecloud, unit) and not self:debuff(smokecloud, otherUnit))
-- or (self:debuff(smokecloud, otherUnit) and not self:debuff(smokecloud, unit))
-- then
-- return false
-- end
-- end
local ax, ay, az = ObjectPosition(self.unit)
local ah = ObjectHeight(self.unit)
local attx, atty, attz = GetUnitAttachmentPosition(unit.unit, 34)
if (ax == 0 and ay == 0 and az == 0) or (attx == 0 and atty == 0 and attz == 0) then
return true
end
if not attx or not ax then
return true
end
local x, y, z = TraceLine(ax, ay, az + ah, attx, atty, attz, losFlag)
if x ~= 0 or y ~= 0 or z ~= 0 then
return false
else
return true
end
end
-- Check if the unit is casting a spell
function Unit:IsCasting()
return UnitCastingInfo(self.unit) ~= nil
end
-- Check if the unit is channeling a spell
function Unit:IsChanneling()
return UnitChannelInfo(self.unit) ~= nil
end
-- Check if the unit is casting or channeling a spell
function Unit:IsCastingOrChanneling()
return self:IsCasting() or self:IsChanneling()
end
-- Check if the unit can attack the target
function Unit:CanAttack(unit)
return UnitCanAttack(self.unit, unit.unit)
end
-- Check if unit is interruptible
function Unit:IsInterruptible(percent)
local percent = percent or math.random(2, 5)
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self
.unit)
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit)
end
if name and startTimeMS and endTimeMS and not notInterruptible then
local castTimeRemaining = endTimeMS / 1000 - GetTime()
local castTimeTotal = (endTimeMS - startTimeMS) / 1000
if castTimeTotal > 0 and castTimeRemaining / castTimeTotal * 100 >= percent then
return true
end
end
return false
end
-- Get the number of enemies in a given range of the unit and cache the result for .5 seconds
function Unit:GetEnemies(range)
local enemies = self.cache:Get("enemies_" .. range)
if enemies then
return enemies
end
local count = 0
local objs = Objects()
local numobjs = #objs
for i = 1, numobjs do
local object = objs[i]
-- unit types 5,6,7
if ObjectType(object) == 5 or ObjectType(object) == 6 then
local unit = Unit:New(object)
if Bastion.UnitManager['player']:CanAttack(unit) and unit:IsAffectingCombat() and unit:IsAlive() and
unit:CanSee(self)
and
self:GetDistance(unit) <= range then
count = count + 1
end
end
end
self.cache:Set("enemies_" .. range, count, .5)
return count
end
function Unit:GetPartyHPAround(distance, percent)
local count = 0
Bastion.UnitManager:EnumFriends(function(unit)
if not self:IsUnit(unit) and unit:GetDistance(self) <= distance and unit:IsAlive() and self:CanSee(unit) and
unit:GetHP() <= percent then
count = count + 1
end
end)
return count
end
-- Is moving
function Unit:IsMoving()
return GetUnitSpeed(self.unit) > 0
end
function Unit:GetComboPoints(unit)
return GetComboPoints(self.unit, unit.unit)
end
-- IsUnit
function Unit:IsUnit(unit)
return UnitIsUnit(self.unit, unit.unit)
end
return Unit

@ -0,0 +1,275 @@
local Tinkr, Bastion = ...
local Unit = Bastion.Unit
local prefixes = {
'^player',
'^pet',
'^vehicle',
'^target',
'^focus',
'^mouseover',
'^none',
'^npc',
'^party[1-4]',
'^raid[1-4]?[0-9]',
'^boss[1-5]',
'^arena[1-5]'
}
-- Validate a unit is a valid token
local function Validate(token)
local start, index
local length, offset = string.len(token), 0
for i = 1, #prefixes do
start, index = string.find(token, prefixes[i])
if start then
offset = index + 1
if offset > length then
return true
else
while true do
start, index = string.find(token, 'target', offset, true)
if start then
offset = index + 1
if offset > length then
return true
end
else
return false
end
end
end
end
end
return false
end
-- Create a new UnitManager class
local UnitManager = {
units = {},
customUnits = {},
cache = {}
}
function UnitManager:__index(k)
if UnitManager[k] then
return UnitManager[k]
end
local k = k or 'none'
-- if custom unit exists, return it it's cache expired return a new one
if self.customUnits[k] then
if not self.cache:IsCached(k) then
self.customUnits[k].unit:Update()
self.cache:Set(k, self.customUnits[k].unit, 0.5)
end
return self.customUnits[k].unit
end
-- if not Validate(k) then
-- error("UnitManager:Get - Invalid token: " .. k)
-- end
if self.units[k] == nil then
self.units[k] = Unit:New(k)
end
return self.units[k]
end
-- Constructor
function UnitManager:New()
local self = setmetatable({}, UnitManager)
self.units = {}
self.customUnits = {}
self.cache = Bastion.Cache:New()
return self
end
function UnitManager:Validate(token)
return Validate(token)
end
-- Get or create a unit
function UnitManager:Get(token)
-- if not Validate(token) then
-- error("UnitManager:Get - Invalid token: " .. token)
-- end
if self.units[token] == nil then
self.units[token] = Unit:New(token)
end
return self.units[token]
end
-- Create a custom unit and cache it for .5 seconds
function UnitManager:CreateCustomUnit(token, cb)
local unit = cb()
local cachedUnit = Bastion.Cacheable:New(unit, cb)
if unit == nil then
error("UnitManager:CreateCustomUnit - Invalid unit: " .. token)
end
if self.customUnits[token] == nil then
self.customUnits[token] = {
unit = cachedUnit,
cb = cb
}
end
self.cache:Set(token, cachedUnit, 0.5)
return cachedUnit
end
-- Enum Friends (party/raid members)
function UnitManager:EnumFriends(cb)
local isRaid = IsInRaid()
local n = GetNumGroupMembers()
if cb(self:Get('player')) then
return
end
if isRaid then
for i = 1, n do
local unit = self:Get('raid' .. i)
if unit:IsValid() then
if cb(unit) then
break
end
end
end
else
for i = 1, n do
local unit = self:Get('party' .. i)
if unit:IsValid() then
if cb(unit) then
break
end
end
end
end
end
-- Enum Enemies (object manager)
function UnitManager:EnumEnemies(cb)
local objs = Objects()
for i = 1, #objs do
local obj = objs[i]
if ObjectType(obj) == 5 or ObjectType(obj) == 6 then
local unit = Unit:New(obj)
if unit:IsHostile() and unit:IsAffectingCombat() and unit:IsAlive() and unit:CanSee(self)
then
cb(unit)
end
end
end
end
-- Enum Units (object manager)
function UnitManager:EnumUnits(cb)
local objs = Objects()
for i = 1, #objs do
local obj = objs[i]
if ObjectType(obj) == 5 or ObjectType(obj) == 6 then
local unit = Unit:New(obj)
cb(unit)
end
end
end
-- Get the number of friends with a buff (party/raid members)
function UnitManager:GetNumFriendsWithBuff(spell)
local count = 0
self:EnumFriends(function(unit)
if unit:GetAuras():FindMy(spell):IsUp() then
count = count + 1
end
end)
return count
end
-- Get the number of friends alive (party/raid members)
function UnitManager:GetNumFriendsAlive()
local count = 0
self:EnumFriends(function(unit)
if unit:IsAlive() then
count = count + 1
end
end)
return count
end
-- Get the friend with the most friends within a given radius (party/raid members)
-- Return unit, friends
function UnitManager:GetFriendWithMostFriends(radius)
local unit = nil
local count = 0
local friends = {}
self:EnumFriends(function(u)
if u:IsAlive() then
local c = 0
self:EnumFriends(function(other)
if other:IsAlive() and u:GetDistance(other) <= radius then
c = c + 1
end
end)
if c > count then
unit = u
count = c
friends = {}
self:EnumFriends(function(other)
if other:IsAlive() and u:GetDistance(other) <= radius then
table.insert(friends, other)
end
end)
end
end
end)
return unit, friends
end
-- Find the centroid of the most dense area of friends (party/raid members) of a given radius within a given range
function UnitManager:FindFriendsCentroid(radius, range)
local unit, friends = self:GetFriendWithMostFriends(radius)
if unit == nil then
return nil
end
local centroid = Bastion.Vector3:New(0, 0, 0)
local zstart = -math.huge
for i = 1, #friends do
local p = friends[i]:GetPosition()
centroid = centroid + p
zstart = p.z > zstart and p.z or zstart
end
centroid = centroid / #friends
if unit:GetPosition():Distance(centroid) > range then
return unit:GetPosition()
end
local _, _, z = TraceLine(
centroid.x,
centroid.y,
centroid.z + 5,
centroid.x,
centroid.y,
centroid.z - 5,
0x100151
)
centroid.z = z + 0.01
return centroid
end
return UnitManager

@ -0,0 +1,240 @@
-- Create a Vector3 class
local Vector3 = {}
Vector3.__index = Vector3
function Vector3:__tostring()
return "Vector3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")"
end
function Vector3:__add(other)
return Vector3:New(self.x + other.x, self.y + other.y, self.z + other.z)
end
function Vector3:__sub(other)
if type(other) == "number" then
return Vector3:New(self.x - other, self.y - other, self.z - other)
end
return Vector3:New(self.x - other.x, self.y - other.y, self.z - other.z)
end
function Vector3:__mul(other)
return Vector3:New(self.x * other, self.y * other, self.z * other)
end
function Vector3:__div(other)
return Vector3:New(self.x / other, self.y / other, self.z / other)
end
function Vector3:__eq(other)
return self.x == other.x and self.y == other.y and self.z == other.z
end
function Vector3:__lt(other)
return self.x < other.x and self.y < other.y and self.z < other.z
end
function Vector3:__le(other)
return self.x <= other.x and self.y <= other.y and self.z <= other.z
end
function Vector3:__unm()
return Vector3:New(-self.x, -self.y, -self.z)
end
function Vector3:__len()
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
function Vector3:__index(k)
if Vector3[k] then
return Vector3[k]
end
if k == "length" then
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
if k == "normalized" then
local length = math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
return Vector3:New(self.x / length, self.y / length, self.z / length)
end
if k == "magnitude" then
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
if k == "sqrMagnitude" then
return self.x * self.x + self.y * self.y + self.z * self.z
end
if k == "zero" then
return Vector3:New(0, 0, 0)
end
if k == "one" then
return Vector3:New(1, 1, 1)
end
if k == "up" then
return Vector3:New(0, 1, 0)
end
if k == "down" then
return Vector3:New(0, -1, 0)
end
if k == "left" then
return Vector3:New(-1, 0, 0)
end
if k == "right" then
return Vector3:New(1, 0, 0)
end
if k == "forward" then
return Vector3:New(0, 0, 1)
end
if k == "back" then
return Vector3:New(0, 0, -1)
end
if k == "positiveInfinity" then
return Vector3:New(math.huge, math.huge, math.huge)
end
if k == "negativeInfinity" then
return Vector3:New(-math.huge, -math.huge, -math.huge)
end
if k == "nan" then
return Vector3:New(0 / 0, 0 / 0, 0 / 0)
end
if k == "epsilon" then
return 1.401298E-45
end
if k == "maxValue" then
return 3.402823E+38
end
if k == "minValue" then
return -3.402823E+38
end
if k == "x" then
return self[1]
end
if k == "y" then
return self[2]
end
if k == "z" then
return self[3]
end
return nil
end
function Vector3:__newindex(k, v)
if k == "x" then
self[1] = v
elseif k == "y" then
self[2] = v
elseif k == "z" then
self[3] = v
else
rawset(self, k, v)
end
end
function Vector3:New(x, y, z)
if x == false then
return Vector3:New(0, 0, 0)
end
local self = setmetatable({ x, y, z }, Vector3)
return self
end
function Vector3:Dot(rhs)
return self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
end
function Vector3:Cross(rhs)
return Vector3:New(self.y * rhs.z - self.z * rhs.y, self.z * rhs.x - self.x * rhs.z, self.x * rhs.y - self.y * rhs.x)
end
function Vector3:Distance(b)
return FastDistance(self.x, self.y, self.z, b.x, b.y, b.z)
end
function Vector3:Angle(to)
return math.acos(self:Dot(to) /
(
math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) *
math.sqrt(to.x * to.x + to.y * to.y + to.z * to.z)))
end
function Vector3:ClampMagnitude(maxLength)
if self:Dot(self) > maxLength * maxLength then
return self.normalized * maxLength
end
return self
end
-- Implement a clamp function
local function clamp(x, min, max)
return x < min and min or (x > max and max or x)
end
function Vector3:Lerp(b, t)
t = clamp(t, 0, 1)
return Vector3:New(self.x + (b.x - self.x) * t, self.y + (b.y - self.y) * t, self.z + (b.z - self.z) * t)
end
function Vector3:MoveTowards(target, maxDistanceDelta)
local toVector = target - self
local distance = toVector.magnitude
if distance <= maxDistanceDelta or distance == 0 then
return target
end
return self + toVector / distance * maxDistanceDelta
end
function Vector3:Scale(b)
return Vector3:New(self.x * b.x, self.y * b.y, self.z * b.z)
end
function Vector3:Project(onNormal)
local num = onNormal:Dot(onNormal)
if num < 1.401298E-45 then
return Vector3:New(0, 0, 0)
end
return onNormal * self:Dot(onNormal) / num
end
function Vector3:ProjectOnPlane(planeNormal)
return self - self:Project(planeNormal)
end
function Vector3:Reflect(inNormal)
return -2 * inNormal:Dot(self) * inNormal + self
end
function Vector3:Normalize()
local num = self:Dot(self)
if num > 1E-05 then
return self / math.sqrt(num)
end
return Vector3:New(0, 0, 0)
end
return Vector3

@ -0,0 +1,152 @@
local Tinkr = ...
local Bastion = {
DebugMode = true
}
Bastion.__index = Bastion
function Bastion.require(class)
if Bastion[class] then
return Bastion[class]
end
Bastion[class] = Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
return Bastion[class]
end
Bastion.ClassMagic = Bastion.require("ClassMagic")
Bastion.Vector3 = Bastion.require("Vector3")
Bastion.Commmand = Bastion.require("Command")
Bastion.Cache = Bastion.require("Cache")
Bastion.Cacheable = Bastion.require("Cacheable")
Bastion.Unit = Bastion.require("Unit")
Bastion.Aura = Bastion.require("Aura")
Bastion.APL = Bastion.require("APL")
Bastion.Module = Bastion.require("Module")
Bastion.UnitManager = Bastion.require("UnitManager"):New()
Bastion.EventManager = Bastion.require("EventManager"):New()
Bastion.Spell = Bastion.require("Spell")
Bastion.Item = Bastion.require("Item")
Bastion.SpellBook = Bastion.require("SpellBook"):New()
Bastion.ItemBook = Bastion.require("ItemBook"):New()
Bastion.AuraTable = Bastion.require("AuraTable")
Bastion.Class = Bastion.require("Class")
Bastion.Timer = Bastion.require("Timer")
Bastion.CombatTimer = Bastion.Timer:New('combat')
Bastion.modules = {}
Bastion.Enabled = false
Bastion.Ticker = C_Timer.NewTicker(0.1, function()
if not Bastion.CombatTimer:IsRunning() and UnitAffectingCombat("player") then
Bastion.CombatTimer:Start()
elseif Bastion.CombatTimer:IsRunning() and not UnitAffectingCombat("player") then
Bastion.CombatTimer:Reset()
end
if Bastion.Enabled then
for i = 1, #Bastion.modules do
Bastion.modules[i]:Tick()
end
end
end)
function Bastion:Register(module)
table.insert(Bastion.modules, module)
Bastion:Print("Registered", module)
end
-- Find a module by name
function Bastion:FindModule(name)
for i = 1, #Bastion.modules do
if Bastion.modules[i].name == name then
return Bastion.modules[i]
end
end
return nil
end
function Bastion:Print(...)
local args = { ... }
local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
end
print(str)
end
function Bastion:Debug(...)
if not Bastion.DebugMode then
return
end
local args = { ... }
local str = "|cFFDF6520[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
end
print(str)
end
local Command = Bastion.Commmand:New('bastion')
Command:Register('toggle', 'Toggle bastion on/off', function()
Bastion.Enabled = not Bastion.Enabled
if Bastion.Enabled then
Bastion:Print("Enabled")
else
Bastion:Print("Disabled")
end
end)
Command:Register('debug', 'Toggle debug mode on/off', function()
Bastion.DebugMode = not Bastion.DebugMode
if Bastion.DebugMode then
Bastion:Print("Debug mode enabled")
else
Bastion:Print("Debug mode disabled")
end
end)
Command:Register('dumpspells', 'Dump spells to a file', function()
local i = 1
local rand = math.random(100000, 999999)
while true do
local spellName, spellSubName = GetSpellBookItemName(i, BOOKTYPE_SPELL)
if not spellName then
do break end
end
-- use spellName and spellSubName here
local spellID = select(7, GetSpellInfo(spellName))
if spellID then
WriteFile('bastion-' .. UnitClass('player') .. '-' .. rand .. '.lua',
"local " .. spellName .. " = Bastion.SpellBook:GetSpell(" .. spellID .. ")", true)
end
i = i + 1
end
end)
Command:Register('module', 'Toggle a module on/off', function(args)
local module = Bastion:FindModule(args[2])
if module then
module:Toggle()
if module.enabled then
Bastion:Print("Enabled", module.name)
else
Bastion:Print("Disabled", module.name)
end
else
Bastion:Print("Module not found")
end
end)
local files = ListFiles("scripts/bastion/scripts")
for i = 1, #files do
local file = files[i]
if file:sub(-4) == ".lua" or file:sub(-5) == '.luac' then
Tinkr:require("scripts/bastion/scripts/" .. file:sub(1, -5), Bastion)
end
end
Loading…
Cancel
Save