My Balance Druid custom Rotation

18 posts Page 2 of 2
Kaminaris
Site Admin
Posts: 322
Joined: 25 Jun 2017, 03:45


Nooo lol...

Caching result within local variable will not work. It will evaluate each time again.
Caching it in global variable will stay forever the same value.
Gadgetsan
Posts: 12
Joined: 13 Aug 2018, 19:33


it doesn't get evaluated when we don't need it cause the 'cache' function would then never get called
Kaminaris
Site Admin
Posts: 322
Joined: 25 Jun 2017, 03:45


I think you are misunderstanding how programming works:
  1. function rotation()
  2.     local cache = {};
  3.     function getSomething(what)
  4.         if cache[what] then
  5.             print('cache hit')
  6.             return cache[what];
  7.         end
  8.  
  9.         print('I am being called without cache');
  10.         cache[what] = math.random(1,10);
  11.     end
  12.     -- do some other checks.
  13.     getSomething('abc');
  14.     getSomething('abc');
  15.     getSomething('abc');
  16. end
  17.  
  18. rotation();
  19. rotation();
  20. rotation();
Is literally the same as

  1. function rotation()
  2.     function getSomething(what)
  3.  
  4.         print('I am being called without cache');
  5.         cache[what] = math.random(1,10);
  6.     end
  7.     -- do some other checks.
  8.     local beforeFirstCheck = getSomething('abc');
  9.     beforeFirstCheck;
  10.     beforeFirstCheck;
  11. end
  12.  
  13. rotation();
  14. rotation();
  15. rotation();
and its even worse because you are creating a lot of functions on the fly.

I just made you some fixes:
  1. function(_, timeShift, currentSpell, gcd, talents)
  2.     local DEBUG = false;
  3.     --Links
  4.     --http://maxdps.net/viewtopic.php?f=6&t=5&sid=134ea7dae4e3b8092032fb116836c364
  5.  
  6.     --SKILLS
  7.     local _Moonfire = 8921;
  8.     local _Sunfire = 93402;
  9.     local _Starsurge = 78674;
  10.     local _LunarEmpowerment = 164547;
  11.     local _SolarEmpowerment = 164545;
  12.     local _LunarStrike = 194153;
  13.     local _SolarWrath = 190984;
  14.     local _NewMoon = 274281;
  15.     local _HalfMoon = 274282;
  16.     local _FullMoon = 274283;
  17.     local _CelestialAlignment = 194223;
  18.     local _IncarnationChosenofElune = 102560;
  19.     local _StellarFlare = 202347;
  20.     local _Starfall = 191034;
  21.     local _WarriorofElune = 202425;
  22.     local _MoonkinMoonfire = 164812;
  23.     local _MoonkinSunfire = 164815;
  24.     local _WarriorofEluneAura = 202425;
  25.  
  26.     -------------------------------VARIABLES--------------------------------------------
  27.     local AP = UnitPower('player', Enum.PowerType.LunarPower);
  28.     --print(string.format("AstralPower: %s", AP)); -- dont define functions here unless you absolutely must
  29.     --Note: we can cast 30% before the end because of pandemic dotting
  30.     local targetHasMoonfireDot = MaxDps:TargetAura(_MoonkinMoonfire, timeShift + 5);
  31.     local targetHasSunfireDot = MaxDps:TargetAura(_MoonkinSunfire, timeShift + 4);
  32.     local targetHasStellarFlareDot = MaxDps:TargetAura(_StellarFlare, timeShift + 5);
  33.  
  34.     local solarE, solarCharges = MaxDps:Aura(_SolarEmpowerment, timeShift);
  35.     local lunarE, lunarCharges = MaxDps:Aura(_LunarEmpowerment, timeShift);
  36.  
  37.     local currentMoon, nextMoon, cd, currentMoonCharges, maxCharges; -- no variable was in parent scope
  38.     if talents[_NewMoon] then
  39.         currentMoon = _NewMoon;
  40.         if MaxDps:FindSpell(_NewMoon) ~= nil then
  41.             currentMoon = _NewMoon;
  42.         end
  43.         if MaxDps:FindSpell(_HalfMoon) ~= nil then
  44.             currentMoon = _HalfMoon;
  45.         end
  46.         if MaxDps:FindSpell(_FullMoon) ~= nil then
  47.             currentMoon = _FullMoon;
  48.         end
  49.         --nextMoon is necessary in the case where we are currently casting
  50.         nextMoon = currentMoon;
  51.         -- horrible thing to do. You just created 4 global variables
  52.         cd, currentMoonCharges, maxCharges = MaxDps:SpellCharges(currentMoon, timeShift);
  53.     end
  54.  
  55.     --we look after the current casting to see in advance if we will have enough AP
  56.     if currentSpell == _SolarWrath then
  57.         AP = AP + 8;
  58.         solarCharges = solarCharges - 1;
  59.     elseif currentSpell == _LunarStrike then
  60.         AP = AP + 12;
  61.         lunarCharges = lunarCharges - 1;
  62.     elseif currentSpell == _NewMoon then
  63.         AP = AP + 10;
  64.         currentMoonCharges = currentMoonCharges - 1;
  65.         nextMoon = _HalfMoon
  66.     elseif currentSpell == _HalfMoon then
  67.         AP = AP + 20;
  68.         currentMoonCharges = currentMoonCharges - 1;
  69.         nextMoon = _FullMoon
  70.     elseif currentSpell == _FullMoon then
  71.         AP = AP + 40;
  72.         currentMoonCharges = currentMoonCharges - 1;
  73.         nextMoon = _NewMoon
  74.     end
  75.  
  76.     --i once ended up with negative solar charges...
  77.     if lunarCharges < 0 then
  78.         lunarCharges = 0
  79.     end
  80.     if solarCharges < 0 then
  81.         solarCharges = 0
  82.     end
  83.     --print(string.format("lunarCharges: %s solarCharges: %s ", lunarCharges, solarCharges));
  84.  
  85.  
  86.     -------------------------------COOLDOWNS--------------------------------------------
  87.     if talents[_IncarnationChosenofElune] then
  88.         MaxDps:GlowCooldown(_IncarnationChosenofElune, MaxDps:SpellAvailable(_IncarnationChosenofElune, timeShift));
  89.     else
  90.         MaxDps:GlowCooldown(_CelestialAlignment, MaxDps:SpellAvailable(_CelestialAlignment, timeShift));
  91.     end
  92.  
  93.     if talents[_WarriorofElune] then
  94.         MaxDps:GlowCooldown(_WarriorofElune, MaxDps:SpellAvailable(_WarriorofElune, timeShift)
  95.             and not MaxDps:Aura(_WarriorofEluneAura, timeShift));
  96.     end
  97.  
  98.     -------------------------------PRIORITY--------------------------------------------
  99.  
  100.     --DOTS so they are always applied
  101.     if not targetHasMoonfireDot then
  102.         --print("Target does not have Moonfire!");
  103.         return _Moonfire;
  104.     end
  105.  
  106.     if not targetHasSunfireDot then
  107.         --print("Target does not have Sunfire!");
  108.         return _Sunfire;
  109.     end
  110.  
  111.     --Stellar Flare (i don't use it...)
  112.     if talents[_StellarFlare] and not targetHasStellarFlareDot and currentSpell ~= _StellarFlare then
  113.         return _StellarFlare;
  114.     end
  115.  
  116.  
  117.     --New/Half/Full Moon, as suggested by HamOfMoose » 30 Aug 2018, 21:20. Requires New Moon Talent
  118.     if talents[_NewMoon] then
  119.         local isAvailable  = MaxDps:SpellAvailable(currentMoon, timeShift)
  120.         --print(string.format("Found %s stakcs for moon thing", currentMoonCharges));
  121.         if currentMoonCharges > 1 and isAvailable then
  122.             --we look at the current moon (or the next if we are currently casting in order to decide if we cast it or not)
  123.             if (nextMoon == _NewMoon and AP < 90) or (nextMoon == _HalfMoon and AP < 80) or (nextMoon == _FullMoon and AP < 60) then
  124.                 return currentMoon;
  125.             end
  126.         end
  127.     end
  128.  
  129.     --if i have a lot of AP, i want to be casting Star Surge or Starfall in priority
  130.     local targets;
  131.     if AP > 80 then
  132.         if targets == nil then
  133.             targets = MaxDps:TargetsInRange(_LunarStrike);
  134.         end
  135.  
  136.         if (targets > 3) then
  137.             return _Starfall;
  138.         else
  139.             return _Starsurge;
  140.         end
  141.     end
  142.  
  143.  
  144.     --Empowerments so i use spend them
  145.     if (lunarCharges > 0 or solarCharges > 0) then
  146.         if lunarCharges > solarCharges then
  147.             return _LunarStrike
  148.         else
  149.             return _SolarWrath
  150.         end
  151.     end
  152.  
  153.     --StarSurge or StarFall to spend AP (LunarStrike and Starfall have same range)
  154.     if targets == nil then
  155.         targets = MaxDps:TargetsInRange(_LunarStrike);
  156.     end
  157.  
  158.     if (targets > 3) then
  159.         if AP > 50 then
  160.             return _Starfall;
  161.         end
  162.     else
  163.         if AP > 40 then
  164.             return _Starsurge;
  165.         end
  166.     end
  167.  
  168.     --SLunarStrike to Get AP if more than 1 in range
  169.     if (targets > 1) then
  170.         return _LunarStrike;
  171.     else
  172.         return _SolarWrath;
  173.     end
  174.  
  175.     return _LunarStrike;
  176. end
Kaminaris
Site Admin
Posts: 322
Joined: 25 Jun 2017, 03:45


If you wish to improve, general rules:
1. Dont make variables you are not using in code
2. Dont create functions unless you have to
3. If you wish to call function twice, just save result to variable
4. Don't ever make global variables inside it.


If you absolutely have to make function inside rotation function, do it like this:
  1.     local someFunction = _G.MyUniqueFunctionName;
  2.     if not someFunction then
  3.         someFunction = function(params)
  4.             -- do something here
  5.         end
  6.         _G.MyUniqueFunctionName = someFunction;
  7.     end
Gadgetsan
Posts: 12
Joined: 13 Aug 2018, 19:33


you are mistaken in thinking the generalisation of a concept is not useful. Code reuse is an extremely important concept in software engineering which i apply daily.

the advantage of my caching function is that if i want to use targetsInRangeCache(_FullMoon) later in my code, i can still do it! i admit it's less useful with a couple dozens lines of code but it's a godsend when you have software spanning hundreds of thousand of lines of codes accross thousand files (which i program daily).

the usage of function takes up more space for the code it's true but it offers a lot of advantage for development of software and it is definetly worth it! if it was not we would all be programming using the Assembly language of our processor. Abstraction is (almost) always good.
Kaminaris
Site Admin
Posts: 322
Joined: 25 Jun 2017, 03:45


You are mistaken about few things. I never said functions are wrong. Functions are wrong inside rotation function!

1. You cannot create a ONE function inside rotation. You are creating one function each 0.15 seconds which gets garbage collected later. I bet it would create like 100 functions before it gets garbage collected.

2. This makes tremendous impact on performance which is crucial here. This function gets called each 0.15s. While it is configurable and you can make it 0.01s. Your function only lives one cycle.

So not only you are using expensive function, but you also wrap it around another function which could be replaced by one variable. Then you are making garbage collector sweat because it needs to get rid of your 100 functions. With debug function it will be 200.

So in about 15 seconds you created 200 functions on the fly. You really think this has anything to do with code reuse?

The only way to do it properly is to declare a function inside a global scope _G IF it is not defined already. And you can see how to do it in my previous post.

I have 14 years of experience with development, I see issues like this on first glance. And trust me, calling function twice where result will not change is performance issue.

If you doubt me, try other rotation addons and measure them with Addon CPU usage. So far every rotation I wrote takes from 0.1% - 1% cpu. While those with targets checking takes considerably more.
Gadgetsan
Posts: 12
Joined: 13 Aug 2018, 19:33


if you explained it clearly the first time i would have understood it earlier.

you however have a good point. to be honest i didn't think about the fact that this code needed to run four time every seconds. 250ms isn't that much of a delay for lower level language but i guess for LUA it is.

i will build myself a library that can be used without having to rebuild it every time (a kind of singleton if you wish)
Gadgetsan
Posts: 12
Joined: 13 Aug 2018, 19:33


I have updated my code to use my new library system that will (hopefully) satisfy my maintenance requirements as well as your performance concerns.

I will be updating my initial post as to make the most recent version easy to find.

i will also update my Restoration rotation with my Library system soon.

  1. function(_, timeShift, currentSpell, gcd, talents)
  2.  
  3.     if _GadgetsanLibrary == nil or _GadgetsanLibrary.libVersion ~= "Moonkin3.0" then
  4.         _GadgetsanLibrary = {};
  5.         _GadgetsanLibrary.libVersion = "Moonkin3.0";
  6.  
  7.     --CACHEs setup
  8.         _GadgetsanLibrary.hitCountCache = {};
  9.         _GadgetsanLibrary.movingCache = nil;
  10.         _GadgetsanLibrary.hasAuraCache = {};
  11.         _GadgetsanLibrary.meAuraCountCache = {};
  12.  
  13.         --a function that should be called when a new rotation has started so it can do some wuick housekeeping
  14.         _GadgetsanLibrary.newRotation = function()            
  15.             _GadgetsanLibrary.hitCountCache = {};
  16.             _GadgetsanLibrary.movingCache = nil;
  17.             _GadgetsanLibrary.hasAuraCache = {};
  18.             _GadgetsanLibrary.meAuraCountCache = {};
  19.         end
  20.  
  21.  
  22.     --HELPER FUNCTIONS
  23.  
  24.         --for debugging purposes (i turn this off if i want to output some text)
  25.         _GadgetsanLibrary.DebugToggle = false;
  26.         _GadgetsanLibrary.debug = function(text)
  27.             if(_GadgetsanLibrary.DebugToggle) then print(text);end;
  28.         end
  29.  
  30.         --caching the value of TargetsInRange so i don't make unnecesary calls
  31.         _GadgetsanLibrary.hitCount = function(spellId)
  32.             if _GadgetsanLibrary.hitCountCache[spellId] == nil then
  33.                 _GadgetsanLibrary.hitCountCache[spellId] = MaxDps:TargetsInRange(spellId);
  34.             end
  35.             return _GadgetsanLibrary.hitCountCache[spellId];
  36.         end
  37.  
  38.         --caching the value of TargetAura so i don't make unnecesary calls
  39.         _GadgetsanLibrary.hasAura = function(spellId, shift)
  40.             if _GadgetsanLibrary.hasAuraCache[spellId] == nil then
  41.                 _GadgetsanLibrary.hasAuraCache[spellId] = MaxDps:TargetAura(spellId, shift);
  42.             end
  43.             return _GadgetsanLibrary.hasAuraCache[spellId];
  44.         end
  45.  
  46.         --caching the value of Aura so i don't make unnecesary calls
  47.         _GadgetsanLibrary.meAuraCount = function(spellId)
  48.             if _GadgetsanLibrary.meAuraCountCache[spellId] == nil then
  49.                 local trueOrFalse, count = MaxDps:Aura(spellId, timeShift);
  50.                 _GadgetsanLibrary.meAuraCountCache[spellId] = count;
  51.             end
  52.             return _GadgetsanLibrary.meAuraCountCache[spellId];
  53.         end
  54.  
  55.  
  56.         --function to test if character is moving (and caching the result)
  57.         _GadgetsanLibrary.isMoving = function()
  58.             if _GadgetsanLibrary.movingCache ~= nil then
  59.                 return _GadgetsanLibrary.movingCache
  60.             end
  61.             local currentSpeed, runSpeed, flightSpeed, swimSpeed = GetUnitSpeed("Player");
  62.             if currentSpeed == 0 then
  63.                 _GadgetsanLibrary.movingCache = false;
  64.             else
  65.                 _GadgetsanLibrary.movingCache = true;
  66.             end
  67.             return _GadgetsanLibrary.movingCache;
  68.         end
  69.        
  70.     --SKILLS
  71.         _GadgetsanLibrary.Moonfire = 8921;
  72.         _GadgetsanLibrary.Sunfire = 93402;
  73.         _GadgetsanLibrary.Starsurge = 78674;
  74.         _GadgetsanLibrary.LunarEmpowerment = 164547;
  75.         _GadgetsanLibrary.SolarEmpowerment = 164545;
  76.         _GadgetsanLibrary.LunarStrike = 194153;
  77.         _GadgetsanLibrary.SolarWrath = 190984;
  78.         _GadgetsanLibrary.NewMoon = 274281;
  79.         _GadgetsanLibrary.HalfMoon = 274282;
  80.         _GadgetsanLibrary.FullMoon = 274283;
  81.         _GadgetsanLibrary.CelestialAlignment = 194223;
  82.         _GadgetsanLibrary.IncarnationChosenofElune = 102560;
  83.         _GadgetsanLibrary.StellarFlare = 202347;
  84.         _GadgetsanLibrary.Starfall = 191034;
  85.         _GadgetsanLibrary.MasteryStarlight = 77492;
  86.         _GadgetsanLibrary.StellarEmpowerment = 197637;
  87.         _GadgetsanLibrary.Heroism = 32182;
  88.         _GadgetsanLibrary.Bloodlust = 2825;
  89.         _GadgetsanLibrary.Berserking = 26297;
  90.         _GadgetsanLibrary.ForceofNature = 205636;
  91.         _GadgetsanLibrary.WarriorofElune = 202425;
  92.         _GadgetsanLibrary.AstralCommunion = 202359;
  93.         _GadgetsanLibrary.BlessingoftheAncients = 202360;
  94.         _GadgetsanLibrary.BlessingofElune = 202737;
  95.         _GadgetsanLibrary.FuryofElune = 202770;
  96.         _GadgetsanLibrary.MoonkinMoonfire = 164812;
  97.         _GadgetsanLibrary.MoonkinSunfire = 164815;
  98.  
  99.  
  100.     end
  101.  
  102.     local GL = _GadgetsanLibrary;
  103.     GL.newRotation();
  104.  
  105. --Links
  106.     --http://maxdps.net/viewtopic.php?f=6&t=5&sid=134ea7dae4e3b8092032fb116836c364
  107.  
  108.  
  109. -------------------------------VARIABLES--------------------------------------------
  110.     local AP = UnitPower('player', Enum.PowerType.LunarPower);
  111.     --debug(string.format("AstralPower: %s", AP));
  112.     --Note: we can cast 30% before the end because of pandemic dotting
  113.     local solarE, solarCharges = MaxDps:Aura(GL.SolarEmpowerment, timeShift);
  114.     local lunarE, lunarCharges = MaxDps:Aura(GL.LunarEmpowerment, timeShift);
  115.    
  116.     local currentMoonCharges = 0;
  117.     if talents[GL.NewMoon] then
  118.         currentMoon = GL.NewMoon;
  119.         if MaxDps:FindSpell(GL.NewMoon) ~= nil then
  120.             currentMoon = GL.NewMoon;
  121.         end
  122.         if MaxDps:FindSpell(GL.HalfMoon) ~= nil then
  123.             currentMoon = GL.HalfMoon;
  124.         end
  125.         if MaxDps:FindSpell(GL.FullMoon) ~= nil then
  126.             currentMoon = GL.FullMoon;
  127.         end
  128.         --nextMoon is necessary in the case where we are currently casting
  129.         nextMoon = currentMoon;
  130.         local cd, charges, maxCharges = MaxDps:SpellCharges(currentMoon, timeShift);
  131.         currentMoonCharges = charges;
  132.     end
  133.  
  134.     --we look after the current casting to see in advance if we will have enough AP
  135.     if currentSpell == GL.SolarWrath then
  136.         AP = AP + 8;
  137.         solarCharges = solarCharges - 1;
  138.     elseif currentSpell == GL.LunarStrike then
  139.         AP = AP + 12;
  140.         lunarCharges = lunarCharges - 1;
  141.     elseif currentSpell == GL.NewMoon then
  142.         AP = AP + 10;
  143.         currentMoonCharges = currentMoonCharges-1;
  144.         nextMoon = GL.HalfMoon
  145.     elseif currentSpell == GL.HalfMoon then
  146.         AP = AP + 20;
  147.         currentMoonCharges = currentMoonCharges-1;
  148.         nextMoon = GL.FullMoon
  149.     elseif currentSpell == GL.FullMoon then
  150.         AP = AP + 40;
  151.         currentMoonCharges = currentMoonCharges-1;
  152.         nextMoon = GL.NewMoon
  153.     end
  154.     --i once ended up with negative solar charges...
  155.     if lunarCharges < 0 then lunarCharges = 0 end
  156.     if solarCharges < 0 then solarCharges = 0 end
  157.     --GL.debug(string.format("lunarCharges: %s solarCharges: %s ", lunarCharges, solarCharges));
  158.    
  159.  
  160. -------------------------------COOLDOWNS--------------------------------------------
  161.     if talents[GL.IncarnationChosenofElune] then
  162.         MaxDps:GlowCooldown(GL.IncarnationChosenofElune, MaxDps:SpellAvailable(GL.IncarnationChosenofElune, timeShift));
  163.     else
  164.         MaxDps:GlowCooldown(GL.CelestialAlignment, MaxDps:SpellAvailable(GL.CelestialAlignment, timeShift));
  165.     end
  166.  
  167.     if talents[GL.WarriorofElune] then
  168.         MaxDps:GlowCooldown(GL.WarriorofElune, MaxDps:SpellAvailable(GL.WarriorofElune, timeShift)
  169.             and not MaxDps:Aura(GL.WarriorofElune));
  170.     end
  171.    
  172. -------------------------------PRIORITY--------------------------------------------
  173.  
  174. --DOTS so they are always applied
  175.     if not GL.hasAura(GL.MoonkinMoonfire, timeShift + 5) then      
  176.         GL.debug("Target does not have Moonfire!");
  177.         return GL.Moonfire;
  178.     end
  179.  
  180.     if not GL.hasAura(GL.MoonkinSunfire, timeShift + 4) then
  181.         GL.debug("Target does not have Sunfire");
  182.         return GL.Sunfire;
  183.     end    
  184.  
  185. --if i'm moving, i want to instant cast only
  186.     if GL.isMoving() then
  187.         GL.debug("Currently Moving");
  188.         if AP >= 40 then
  189.             return GL.Starsurge;
  190.         end
  191.  
  192.         --if i have warrior of elune, i wanna spend my StellarFlare instant casts        
  193.         if GL.meAuraCount(GL.WarriorofElune) > 0 then
  194.             return GL.LunarStrike
  195.         end
  196.  
  197.         -- is i'm still moving, i wanna cast Warrior of Elune
  198.         if talents[GL.WarriorofElune] and MaxDps:SpellAvailable(GL.WarriorofElune, timeShift) then
  199.             return GL.WarriorofElune
  200.         end
  201.  
  202.         --if nothing else, i can cast this while moving
  203.         return GL.Sunfire;
  204.     end
  205.  
  206. --Stellar Flare (i don't use it...)
  207.     if talents[GL.StellarFlare] and not GL.hasAura(GL.StellarFlare, timeShift + 5) and currentSpell ~= GL.StellarFlare then
  208.         return GL.StellarFlare;
  209.     end
  210.  
  211.    
  212. --New/Half/Full Moon, as suggested by HamOfMoose » 30 Aug 2018, 21:20. Requires New Moon Talent
  213.     if talents[GL.NewMoon] then        
  214.         GL.debug("New Moon Management");
  215.         local isAvailable, cd = MaxDps:SpellAvailable(currentMoon, timeShift)
  216.         --GL.debug(string.format("Found %s stakcs for moon thing", currentMoonCharges));
  217.         if currentMoonCharges > 1 and isAvailable then
  218.             --we look at the current moon (or the next if we are currently casting in order to decide if we cast it or not)
  219.             if(nextMoon == GL.NewMoon and AP < 90) or (nextMoon == GL.HalfMoon and AP < 80) or (nextMoon == GL.FullMoon and AP < 60) then
  220.                 return currentMoon;
  221.             end
  222.         end
  223.     end
  224.    
  225. --if i'm maxed on enhancement, i priorise this
  226.     if(lunarCharges > 2 or solarCharges > 2) then
  227.         GL.debug("Almost at max enhancement");
  228.         if lunarCharges > solarCharges then
  229.             return GL.LunarStrike
  230.         else
  231.             return GL.SolarWrath
  232.         end
  233.     end
  234.    
  235. --StarSurge or StarFall to spend AP (LunarStrike and Starfall have same range)
  236.     if(GL.hitCount(GL.LunarStrike) > 3) then
  237.         if AP > 90 then
  238.             GL.debug("enough AP to cast Starfall!");
  239.             return GL.Starfall;
  240.         end
  241.     else
  242.         if AP > 80 then
  243.             GL.debug("enough AP to cast Starsurge!");
  244.             return GL.Starsurge;
  245.         end
  246.     end
  247.  
  248. --Empowerments so i use them
  249.     if(lunarCharges > 0 or solarCharges > 0) then
  250.         GL.debug("casting remaining enhancements");
  251.         if lunarCharges > solarCharges then
  252.             return GL.LunarStrike
  253.         else
  254.             return GL.SolarWrath
  255.         end
  256.     end
  257.  
  258. --SLunarStrike to Get AP if more than 1 in range
  259.     if(GL.hitCount(GL.LunarStrike) > 1) then        
  260.         GL.debug("casting Lunarstrike to raise AP");
  261.         return GL.LunarStrike;
  262.     else
  263.         GL.debug("casting Solar Wrath to raise AP");
  264.         return GL.SolarWrath;
  265.     end
  266.  
  267.     GL.debug("I've got nothing to do!");
  268.     return GL.LunarStrike;
  269. end
18 posts Page 2 of 2
drogie łóżka stolik kawowy stół dębowy rozkładany