[Lua] TasAbilityFieldTooltip

This is a Lua system created for Warcraft 3 V1.31.1 or higher. It updates a given placeholder in an Extended Ability Tooltip with the current value from one of the abilityLevelField of the ability. A simple math operation can be used onto it for display reasons, the second value in the operation can be an abilityLevelField of the same ability. This system only works probably for single unit selection and is only useful if one uses BlzSetAbilityIntegerLevelField, BlzSetAbilityRealLevelField or their GUI versions in the map script.

One still writes the tooltips in Object Editor but can write a placeholder to have a value that changes dynamicly during runtime.
An example of such is:
Calls down 'Hbz1' freezing ice shard waves; Deals 'Hbz2,*Hbz1' damage to units in an area over 'Hbz1,*acas' seconds.

To use it copy paste the code into your map's script, if one does not uses world editor then one has to replace %% with %.
One can start the auto update system by calling TasAbilityFieldTooltipInitAuto(true) at map init/function main.
The auto update system of TasAbilityFieldTooltip needs to know which abilities to update for an unitcode learned skills will be auto added if TasAbilityFieldTooltipInitAuto(true) is called. for not learned skills or if you don't want that learn detection you need to fill this.HeroSkills with data.
This system uses custom frames hence to support Warcraft 3 Save&Load one needs a system (FrameLoader) that takes care of broken frames after Save&Load or one just does not care about Save&Load.
The new version requires HoverOriginButton by Tasyen [Lua] - HoverOriginButton
Lua:
--[[ TasAbilityFieldTooltip V1 by Tasyen
requires HoverOriginButton by Tasyen https://www.hiveworkshop.com/threads/hoveroriginbutton.337963/
fills placeHolders in a extended ability Tooltip with current values of a 'abilityLevelField' can do simple displayFormating.
   Calls down 'Hbz1' freezing ice shard waves; Deals 'Hbz2,*Hbz1' damage to units in an area over 'Hbz1,*acas'.
displays fraction digits 'Hbz1,2' -> 10.xx 'Hbz1,1' -> 10.x
displays value*100 without fraction digits 'Hbz4,%'
One can do 1 math action onto the field for displaying purposes '<field>,<mathOp><Factor>'.
mathop is one of them + - * /
Factor is either a number or another AbilityLevelField RawCodeString when an RawCodeString is given it reads from the same ability
'Hbz2,*adur' display Blizzard Wavedamage*duration(unit)
'Hbz2,+10' display the value of 'Hbz2'+10

function TasAbilityFieldTooltip.InitAuto(addLearned) 
   start the auto updating, which only works correctly for single unit selection
   addLearned = (true) autodetects learned skills and adds them to the unitCode of the learner

function TasAbilityFieldTooltip.GetRuntimeText(unit, abilityCode)

Credits: Slayer of Reforged, Mayday, Zed
--]]
do
   local PercentChar = "\x25"
    TasAbilityFieldTooltip = {
       DefaultTooltip = {}
       ,Selected = {}
    }
    local this = TasAbilityFieldTooltip
     -- the skills for an unitCode to update. Learned hero skills are added when they are learned the first time.
     -- so you only would need to add not learnedSkills
    this.HeroSkills  = {
       Hamg = "AHbz,AHab,AHwe,AHmt"
       --,Hpal = "AHhb,AHds,AHre,AHad"
    }
 
 function this.GetRuntimeText(unit, abilityCode)
    if type(abilityCode) == "string" then abilityCode = FourCC(abilityCode) end
    local index = GetUnitAbilityLevel(unit, abilityCode) - 1
    local abi = BlzGetUnitAbility(unit, abilityCode)
 
    -- use the backup string or generate it on first use of this ability[index]
    local text = this.DefaultTooltip[abilityCode ..",".. index]
    if not text then text = BlzGetAbilityExtendedTooltip(abilityCode, index) this.DefaultTooltip[abilityCode ..",".. index] = text end
    
    --local result =  string.gsub(text, "'....,?"..PercentChar.."d?"..PercentChar.."p?"..PercentChar.."p?'", function(s)
    local result =  string.gsub(text, "'....?,?.?"..PercentChar.."w*'", function(s)
       --print(s)
       -- 'Hbz1' -> Hbz1
       local fieldCode = string.sub(s,2,5)       
       if this.AbilityFieldData[fieldCode] then
          --print(fieldCode)      
          local func = this.AbilityFieldData[fieldCode]
          local field = func(FourCC(fieldCode))
         
         --print(string.len( s ))
 
         -- 'Hbz1' or advanced like 'Hbz1,2'
          if string.len(s) > 6 then
             local digits = string.sub(s,7,string.len( s ) - 1)
             local firstDigit = string.sub(digits, 1, 1)
              --print("'"..digits.."'")
             -- display value*100 
             if digits == PercentChar then
                return string.format(PercentChar..".0f", this.AbilityFieldData[func][1](abi, field, index)*100)
             -- inline Math ,* ,+,-,/
             elseif firstDigit == "*" or firstDigit == "/" or firstDigit == "+" or firstDigit == "-" then
                -- do one math action onto the display 
                local remain = string.sub(digits, 2)
                
                local func2 = this.AbilityFieldData[remain]
                local value2
                if func2 then
                   local field2 = func2(FourCC(remain))
                   value2 = this.AbilityFieldData[func2][1](abi, field2, index)
                else
                   value2 = tonumber(remain)
                end 
 
                if firstDigit == "*" then
                   return string.format(PercentChar..".0f", this.AbilityFieldData[func][1](abi, field, index) * value2)
                elseif firstDigit == "/" then
                   return string.format(PercentChar..".0f", this.AbilityFieldData[func][1](abi, field, index) / value2)
                elseif firstDigit == "+" then
                   return string.format(PercentChar..".0f", this.AbilityFieldData[func][1](abi, field, index) + value2)
                elseif firstDigit == "-" then
                   return string.format(PercentChar..".0f", this.AbilityFieldData[func][1](abi, field, index) - value2)
                end
                -- when everything goes wrong
                return s
             -- display with this amount of fractions?
             -- this has to happen after inline Math because +1/-1 are interpreted as inline math but tonumber would see them as number
             elseif tonumber(digits) then
                return string.format(PercentChar.."."..digits.."f", this.AbilityFieldData[func][1](abi, field, index))
             else
                print("unsupported TasAbilityFieldTooltip")
                print(GetObjectName(abilityCode), "Level", index+1, s)
                return s
             end
          else
             return string.format(PercentChar..".0f", this.AbilityFieldData[func][1](abi, field, index))
          end
       else
          return s
       end
    end)
    abi = nil
    return result
 end
 
 local function UpdateRuntimeText(unit, abilityCode)
 --xpcall(function()
    if GetLocalizedString("REFORGED") == "REFORGED" then
       BlzSetAbilityExtendedTooltip(abilityCode, this.GetRuntimeText(unit, abilityCode), GetUnitAbilityLevel(unit, abilityCode) - 1)
    else
       BlzSetAbilityStringLevelField(BlzGetUnitAbility(unit, abilityCode), ABILITY_SLF_TOOLTIP_NORMAL_EXTENDED, GetUnitAbilityLevel(unit, abilityCode) - 1, this.GetRuntimeText(unit, abilityCode))
    end
 --end, print)
 end
  
 function this.InitAuto(addLearned)
    -- transform Hamg = "AHbz,AHab,AHwe,AHmt"
    -- -> FourCC("Hamg") = {FourCC["AHbz"] = true , FourCC["AHab"] = true ...}
    -- this is done for easier setup in this.HeroSkills
    for key, v in pairs(this.HeroSkills) do
       if type(v) == "string" then
          local vNew = {}
          local startIndex = 1
          while startIndex + 3 <= string.len(v)  do
                local skillCode = string.sub(v, startIndex, startIndex + 3)
                
                startIndex = startIndex + 5
                skillCode = FourCC(skillCode)
                vNew[skillCode] = true
          end
          v = vNew
       end
       if type(key) == "string" then this.HeroSkills[FourCC(key)] = v end      
    end
    HoverOriginButton.Add(true, false, function(int)
        local unitCode = GetUnitTypeId(this.Selected[GetLocalPlayer()])
        if this.HeroSkills[unitCode] then
            for abiCode in pairs(this.HeroSkills[unitCode]) do
                UpdateRuntimeText(this.Selected[GetLocalPlayer()], abiCode)
            end
        end
    end)
  
    local trigger = CreateTrigger()
    TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_SELECTED)
    TriggerAddAction(trigger, function()
       this.Selected[GetTriggerPlayer()] = GetTriggerUnit()
    end)
 
    -- when wanted auto add learned skills to the update que for the learner's unitCode
    if addLearned then
       trigger = CreateTrigger()
       TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_HERO_SKILL)
       TriggerAddAction(trigger, function()
          local unitCode =  GetUnitTypeId(GetTriggerUnit())
          if not this.HeroSkills[unitCode] then this.HeroSkills[unitCode] = {} end
          this.HeroSkills[unitCode][GetLearnedSkill()] = true
       end)
    end
 end
 
 local fLInt = ConvertAbilityIntegerLevelField
 local fLReal = ConvertAbilityRealLevelField


 -- this data tells what to do with the given field 4 digit codeString
 -- This only cares about Ability Level Fields
 this.AbilityFieldData = {
 [fLInt] = {BlzGetAbilityIntegerLevelField, BlzSetAbilityIntegerLevelField},
 [fLReal] = {BlzGetAbilityRealLevelField, BlzSetAbilityRealLevelField},
 -- AbilityBooleanLevelField is not used it is AbilityIntegerLevelField instead
 --[ConvertAbilityBooleanLevelField] = {BlzGetAbilityBooleanLevelField,BlzSetAbilityBooleanLevelField},
 [ConvertAbilityStringLevelField] = {BlzGetAbilityStringLevelField,BlzSetAbilityStringLevelField},
 acas = fLReal,
 adur = fLReal,
 ahdu = fLReal,
 acdn = fLReal,
 amcs = fLInt,
 aare = fLReal,
 aran = fLReal,
 Hbz1 = fLInt,
 Hbz2 = fLReal,
 Hbz3 = fLInt,
 Hbz4 = fLReal,
 Hbz5 = fLReal,
 Hbz6 = fLReal,
 Hab1 = fLReal,
 Hab2 = fLInt,
 Hmt1 = fLInt,
 Hmt2 = fLReal,
 Hmt3 = fLInt,
 Hwe1 = fLInt,
 Hwe2 = fLInt,
 Oww1 = fLReal,
 Oww2 = fLReal,
 Ocr1 = fLReal,
 Ocr2 = fLReal,
  Ocr3 = fLReal,
  Ocr4 = fLReal,
  Ocr5 = fLInt,
  Ocr6 = fLInt,
  Omi1 = fLInt,
  Omi2 = fLReal,
  Omi3 = fLReal,
  Omi4 = fLReal,
  Owk1 = fLReal,
  Owk2 = fLReal,
  Owk3 = fLReal,
  Owk4 = fLInt,
  Owk5 = fLInt,
  Uan1 = fLInt,
  Uan3 = fLInt,
  Udc1 = fLReal,
  Udp1 = fLReal,
  Udp2 = fLReal,
  Udp3 = fLInt,
  Udp4 = fLInt,
  Udp5 = fLInt,
  Uau1 = fLReal,
  Uau2 = fLReal,
  Uau3 = fLInt,
  Eev1 = fLReal,
  Eim1 = fLReal,
  Eim2 = fLReal,
  Eim3 = fLReal,
  Emb1 = fLReal,
  Emb2 = fLReal,
  Emb3 = fLReal,
  Eme1 = fLInt,
  Eme2 = morphFlags,
  Eme3 = fLReal,
  Eme4 = fLReal,
  Eme5 = fLReal,
  Ncr5 = fLReal,
  Ncr6 = fLReal,
  Nrg5 = fLInt,
  Nrg6 = fLInt,
  ave5 = fLReal,
  Emeu = fLInt,
  Usl1 = fLReal,
  Uav1 = fLReal,
  Ucs1 = fLReal,
  Ucs2 = fLReal,
  Ucs3 = fLReal,
  Ucs4 = fLReal,
  Uin1 = fLReal,
  Uin2 = fLReal,
  Uin3 = fLReal,
  Uin4 = fLInt,
  Ocl1 = fLReal,
  Ocl2 = fLInt,
  Ocl3 = fLReal,
  Oeq1 = fLReal,
  Oeq2 = fLReal,
  Oeq3 = fLReal,
  Oeq4 = fLReal,
  Ofs1 = fLInt,
  Osf1 = fLInt,
  Osf2 = fLInt,
  Eer1 = fLReal,
  Efn1 = fLInt,
  Efnu = fLInt,
  Eah1 = fLReal,
  Eah2 = fLInt,
  Etq1 = fLReal,
  Etq2 = fLReal,
  Etq3 = fLReal,
  Etq4 = fLReal,
  Udd1 = fLReal,
  Udd2 = fLReal,
  Ufa1 = fLReal,
  Ufa2 = fLReal,
  Ufn1 = fLReal,
  Ufn2 = fLReal,
  Ufn5 = fLReal,
  Hfa1 = fLReal,
  Esf1 = fLReal,
  Esf2 = fLReal,
  Esf3 = fLReal,
  Ear1 = fLReal,
  Ear2 = fLInt,
  Ear3 = fLInt,
  Ear4 = fLInt,
  Hav1 = fLReal,
  Hav2 = fLReal,
  Hav3 = fLReal,
  Hav4 = fLReal,
  Hbh1 = fLReal,
  Hbh2 = fLReal,
  Hbh3 = fLReal,
  Hbh4 = fLReal,
  Hbh5 = fLInt,
  Htb1 = fLReal,
  Htc1 = fLReal,
  Htc2 = fLReal,
  Htc3 = fLReal,
  Htc4 = fLReal,
  Htc5 = fLReal,
  Had1 = fLReal,
  Had2 = fLInt,
  Hds1 = fLInt,
  Hhb1 = fLReal,
  Hre1 = fLInt,
  Hre2 = fLInt,
  Hca1 = fLReal,
  Hca2 = fLReal,
  Hca3 = fLReal,
  Hca4 = stackFlags,
  Oae1 = fLReal,
  Oae2 = fLReal,
  Ore1 = fLReal,
  Osh1 = fLReal,
  Osh2 = fLReal,
  Osh3 = fLReal,
  Osh4 = fLReal,
  Nfd1 = fLReal,
  Nfd2 = fLReal,
  Nfd3 = fLReal,
  Ndp1 = unitList,
  Ndp2 = fLInt,
  Ndp3 = fLInt,
  Nrc1 = fLInt,
  Nrc2 = fLInt,
  Ams1 = fLReal,
  Ams2 = fLReal,
  Ams3 = fLInt,
  Ams4 = fLInt,
  Apl1 = fLReal,
  Apl2 = fLReal,
  Apl3 = fLReal,
  Aplu = fLInt,
  Oar1 = fLReal,
  Oar2 = fLInt,
  Akb1 = fLReal,
  Adm1 = fLReal,
  Adm2 = fLReal,
  Btl1 = fLInt,
  Btl2 = fLInt,
  Bli1 = fLReal,
  Bli2 = fLInt,
  Bgm1 = fLInt,
  Bgm2 = fLReal,
  Bgm3 = fLInt,
  Bgm4 = fLReal,
  Blo1 = fLReal,
  Blo2 = fLReal,
  Blo3 = fLReal,
  Can1 = fLReal,
  Can2 = fLReal,
  Car1 = fLInt,
  Dev2 = fLReal,
  Dev3 = fLInt,
  Chd1 = fLReal,
  Chd2 = fLReal,
  Chd3 = fLReal,
  Cha1 = fLInt,
  Cri1 = fLReal,
  Cri2 = fLReal,
  Cri3 = fLReal,
  Crs = fLReal,
  Dda1 = fLReal,
  Dda2 = fLReal,
  Dda3 = fLReal,
  Dda4 = fLReal,
  Sds1 = fLReal,
  Sds6 = fLInt,
  Uco5 = fLReal,
  Uco6 = fLReal,
  Def1 = fLReal,
  Def2 = fLReal,
  Def3 = fLReal,
  Def4 = fLReal,
  Def5 = fLReal,
  Def6 = fLReal,
  Def7 = fLReal,
  Def8 = fLReal,
  Dev1 = fLInt,
  Eat1 = fLReal,
  Eat2 = fLReal,
  Eat3 = fLReal,
  Ens1 = fLReal,
  Ens2 = fLReal,
  Ens3 = fLReal,
  ent1 = fLInt,
  Egm1 = fLInt,
  Egm2 = fLReal,
  Fae1 = fLInt,
  Fae2 = fLInt,
  Fla1 = fLInt,
  Fla2 = fLReal,
  Fla3 = fLInt,
  Gld1 = fLInt,
  Gld2 = fLReal,
  Gld3 = fLInt,
  Gyd1 = fLInt,
  Gyd2 = fLReal,
  Gyd3 = fLReal,
  Gydu = fLInt,
  Har1 = fLInt,
  Har2 = fLInt,
  Har3 = fLInt,
  Hea1 = fLReal,
  Inf1 = fLReal,
  Inf2 = fLInt,
  Inf3 = fLReal,
  Inf4 = fLReal,
  Lit1 = fLReal,
  Lit2 = fLReal,
  Lsh1 = fLReal,
  Loa1 = fLInt,
  Mbt1 = fLReal,
  Mbt2 = fLReal,
  Mbt3 = fLReal,
  Mbt4 = fLReal,
  Mbt5 = fLInt,
  Mil1 = fLInt,
  Mil2 = fLInt,
  Min1 = fLReal,
  Min2 = fLReal,
  Neu1 = fLReal,
  Neu2 = fLInt,
  Neu3 = fLInt,
  Neu4 = fLInt,
  Ndt1 = fLInt,
  Ndt2 = fLInt,
  Ndt3 = fLInt,
  Ans5 = ConvertAbilityStringLevelField,
  Ans6 = fLInt,
  Arm1 = fLReal,
  Arm2 = fLInt,
  Poi1 = fLReal,
  Poi2 = fLReal,
  Poi3 = fLReal,
  Poi4 = stackFlags,
  Poa1 = fLReal,
  Poa2 = fLReal,
  Poa3 = fLReal,
  Poa4 = fLReal,
  Poa5 = stackFlags,
  Ply1 = fLInt,
  Ply2 = unitList,
  Ply3 = unitList,
  Ply4 = unitList,
  Ply5 = unitList,
  Pos1 = fLInt,
  Pos2 = fLReal,
  Pos3 = fLInt,
  Pos4 = fLInt,
  War1 = fLReal,
  War2 = fLReal,
  War3 = fLReal,
  War4 = fLReal,
  Prg1 = fLInt,
  Prg2 = fLInt,
  Prg3 = fLReal,
  Prg4 = fLReal,
  Prg5 = fLReal,
  Prg6 = fLInt,
  Rai1 = fLInt,
  Rai2 = fLInt,
  Rai3 = fLInt,
  Rai4 = fLInt,
  Raiu = fLInt,
  Ucb5 = fLInt,
  Ucb6 = fLInt,
  Rej1 = fLReal,
  Rej2 = fLReal,
  Rej3 = fLInt,
  Rej4 = fLInt,
  Rpb3 = fLReal,
  Rpb4 = fLReal,
  Rpb5 = fLInt,
  Rpb6 = fLInt,
  Rep1 = fLReal,
  Rep2 = fLReal,
  Rep3 = fLReal,
  Rep4 = fLReal,
  Rep5 = fLReal,
  Rtn1 = fLInt,
  Rtn2 = fLInt,
  Roa1 = fLReal,
  Roa2 = fLInt,
  Roa3 = fLReal,
  Roa4 = fLReal,
  Roa5 = fLInt,
  Roa6 = fLInt,
  Roa7 = fLInt,
  Nbr1 = fLReal,
  Roo1 = attackBits,
  Roo2 = attackBits,
  Roo3 = fLInt,
  Roo4 = defenseTypeInt,
  Sal1 = fLReal,
  Sal2 = fLInt,
  Esn1 = fLReal,
  Esn2 = fLReal,
  Esn3 = fLReal,
  Esn4 = fLInt,
  Esn5 = fLReal,
  Shm1 = fLReal,
  Shm2 = fLReal,
  Shm3 = fLReal,
  Shm4 = fLInt,
  Slo1 = fLReal,
  Slo2 = fLReal,
  Slo3 = fLInt,
  Spo1 = fLReal,
  Spo2 = fLReal,
  Spo3 = fLReal,
  Spo4 = stackFlags,
  Sod1 = fLInt,
  Sod2 = fLInt,
  Spa1 = fLInt,
  Sta1 = fLReal,
  Sta2 = fLReal,
  Sta3 = fLReal,
  Sta4 = fLReal,
  Sta5 = fLReal,
  Stau = fLInt,
  Uhf1 = fLReal,
  Uhf2 = fLReal,
  Wha1 = fLReal,
  Wha2 = fLInt,
  Wha3 = fLReal,
  Wrp1 = fLReal,
  Wrp2 = fLReal,
  Iagi = fLInt,
  Iint = fLInt,
  Istr = fLInt,
  Ihid = fLInt,
  Iatt = fLInt,
  Idef = fLInt,
  Isn1 = fLInt,
  Ist1 = fLInt,
  Isn2 = fLInt,
  Ist2 = fLInt,
  Ixpg = fLInt,
  Ihpg = fLInt,
  Impg = fLInt,
  Ihp2 = fLInt,
  Imp2 = fLInt,
  Ivam = fLReal,
  Idic = fLInt,
  Iarp = fLInt,
  Idam = fLReal,
  Iob5 = fLInt,
  Iob2 = fLReal,
  Iob3 = fLReal,
  Iob4 = fLReal,
  Iobu = fLInt,
  Ilev = fLInt,
  Ilif = fLInt,
  Iman = fLInt,
  Igol = fLInt,
  Ilum = fLInt,
  Ifa1 = fLInt,
  Idel = fLReal,
  Icre = fLInt,
  Imvb = fLInt,
  Ihpr = fLInt,
  Isib = fLInt,
  Icfd = fLInt,
  Icfm = fLInt,
  Icfx = fLInt,
  Idet = fLInt,
  Idim = fLInt,
  Idid = fLInt,
  Iild = fLReal,
  Iilw = fLReal,
  Irec = fLInt,
  Imrp = fLReal,
  Ircd = fLInt,
  irc2 = fLInt,
  irc3 = fLInt,
  Ihps = fLInt,
  Imps = fLInt,
  Ispi = fLReal,
  Itpm = fLInt,
  Itp2 = fLInt,
  Idps = fLReal,
  Cad1 = fLInt,
  Cac1 = fLReal,
  Cor1 = fLReal,
  Isx1 = fLReal,
  Wrs1 = fLReal,
  Wrs2 = fLReal,
  Wrs3 = fLInt,
  Ctc1 = fLReal,
  Ctc2 = fLReal,
  Ctc3 = fLReal,
  Ctc4 = fLReal,
  Ctb1 = fLReal,
  Ibl1 = unitList,
  Uds1 = fLInt,
  Uds2 = fLReal,
  Ndc1 = unitRace,
  Ndc2 = fLInt,
  Nsl1 = fLInt,
  Chl1 = fLInt,
  Det1 = fLInt,
  Dtn1 = fLReal,
  Dtn2 = fLReal,
  Eth1 = fLInt,
  Eth2 = fLInt,
  Gho1 = fLInt,
  Gho2 = fLInt,
  Gho3 = fLInt,
  Ivs1 = fLReal,
  Nmr1 = fLReal,
  Nsp1 = fLInt,
  Nsp2 = fLInt,
  Nsp3 = fLInt,
  Ssk1 = fLReal,
  Ssk2 = fLReal,
  Ssk3 = fLReal,
  Ssk4 = fLInt,
  Ssk5 = fLInt,
  Hfs1 = fLReal,
  Hfs2 = fLReal,
  Hfs3 = fLReal,
  Hfs4 = fLReal,
  Hfs5 = fLReal,
  Hfs6 = fLReal,
  Nms1 = fLReal,
  Nms2 = fLReal,
  Uim1 = fLReal,
  Uim2 = fLReal,
  Uim3 = fLReal,
  Uim4 = fLReal,
  Uim5 = fLInt,
  Uim6 = fLInt,
  Uls1 = fLInt,
  Uls2 = fLReal,
  Uls3 = fLInt,
  Uls4 = fLReal,
  Uls5 = fLReal,
  Ulsu = fLInt,
  Uts1 = fLReal,
  Uts2 = fLReal,
  Uts3 = fLReal,
  Nba1 = fLReal,
  Nba2 = fLInt,
  Nba3 = fLReal,
  Nbau = fLInt,
  Nch1 = fLInt,
  Cmg2 = fLReal,
  Cmg3 = fLReal,
  Ndr1 = fLReal,
  Ndr2 = fLReal,
  Ndr3 = fLReal,
  Ndr4 = fLReal,
  Ndr5 = fLReal,
  Ndr6 = fLReal,
  Ndr7 = fLReal,
  Ndr8 = fLReal,
  Ndr9 = fLReal,
  NdrA = fLInt,
  Nsi1 = fLInt,
  Nsi2 = fLReal,
  Nsi3 = fLReal,
  Nsi4 = fLReal,
  Ntou = fLInt,
  Tdg1 = fLReal,
  Tdg2 = fLReal,
  Tdg3 = fLReal,
  Tdg4 = fLReal,
  Tdg5 = fLReal,
  Tsp1 = fLReal,
  Tsp2 = fLReal,
  Nbf5 = fLReal,
  Ebl1 = fLReal,
  Ebl2 = fLReal,
  Efk1 = fLReal,
  Efk2 = fLReal,
  Efk3 = fLInt,
  Efk4 = fLReal,
  Esh1 = fLReal,
  Esh2 = fLReal,
  Esh3 = fLReal,
  Esh4 = fLReal,
  Esh5 = fLReal,
  Esv1 = fLInt,
  Esvu = fLInt,
  abs1 = fLReal,
  abs2 = fLReal,
  bsk1 = fLReal,
  bsk2 = fLReal,
  bsk3 = fLReal,
  coau = fLInt,
  coa1 = fLInt,
  coa2 = fLInt,
  cyc1 = fLInt,
  dcp1 = fLInt,
  dcp2 = fLInt,
  dvm1 = fLReal,
  dvm2 = fLReal,
  dvm3 = fLReal,
  dvm4 = fLReal,
  dvm5 = fLReal,
  dvm6 = fLInt,
  exh1 = fLInt,
  exhu = fLInt,
  fak1 = fLReal,
  fak2 = fLReal,
  fak3 = fLReal,
  fak4 = fLReal,
  fak5 = fLReal,
  hwdu = fLInt,
  inv1 = fLInt,
  inv2 = fLInt,
  inv3 = fLInt,
  inv4 = fLInt,
  inv5 = fLInt,
  liq1 = fLReal,
  liq2 = fLReal,
  liq3 = fLReal,
  liq4 = fLInt,
  mim1 = fLReal,
  mfl1 = fLReal,
  mfl2 = fLReal,
  mfl3 = fLReal,
  mfl4 = fLReal,
  mfl5 = fLReal,
  mfl6 = fLInt,
  tpi1 = fLInt,
  tpi2 = fLInt,
  spl1 = fLReal,
  spl2 = fLInt,
  irl1 = fLReal,
  irl2 = fLReal,
  irl3 = fLInt,
  irl4 = fLInt,
  irl5 = fLInt,
  idc1 = fLReal,
  idc2 = fLReal,
  idc3 = fLInt,
  imo1 = fLInt,
  imo2 = fLReal,
  imo3 = fLReal,
  imou = fLInt,
  ict1 = fLInt,
  ict2 = fLInt,
  isr1 = fLReal,
  isr2 = fLReal,
  ipv1 = fLReal,
  ipv2 = fLReal,
  ipv3 = fLInt,
  mec1 = fLInt,
  spb1 = ConvertAbilityStringLevelField,
  spb2 = fLInt,
  spb3 = fLInt,
  spb4 = fLInt,
  spb5 = ConvertAbilityStringLevelField,
  ast1 = fLReal,
  ast2 = fLReal,
  gra1 = fLReal,
  gra2 = fLReal,
  gra3 = fLInt,
  gra4 = fLInt,
  gra5 = fLInt,
  ipmu = fLInt,
  Npr1 = fLInt,
  Nsa1 = fLInt,
  Nsa2 = fLReal,
  Nsa3 = fLReal,
  Nsa4 = fLReal,
  Nsa5 = fLReal,
  Iaa1 = fLInt,
  Ixs1 = fLReal,
  Ixs2 = fLReal,
  Nef1 = unitList,
  Npa5 = fLInt,
  Npa6 = fLReal,
  Igl1 = fLInt,
  Iglu = fLInt,
  Nse1 = fLReal,
  Ndo1 = fLReal,
  Ndo2 = fLInt,
  Ndo3 = fLReal,
  Ndo4 = fLInt,
  Ndo5 = fLReal,
  Ndou = fLInt,
  flk1 = fLReal,
  flk2 = fLReal,
  flk3 = fLReal,
  flk4 = fLReal,
  flk5 = fLReal,
  Hbn1 = fLReal,
  Hbn2 = fLReal,
  fbk1 = fLReal,
  fbk2 = fLReal,
  fbk3 = fLReal,
  fbk4 = fLReal,
  fbk5 = fLReal,
  nca1 = fLReal,
  pxf1 = fLReal,
  pxf2 = fLReal,
  mls1 = fLReal,
  sla1 = fLInt,
  Nst1 = fLInt,
  Nst2 = fLReal,
  Nst3 = fLReal,
  Nst4 = fLReal,
  Nst5 = fLReal,
  sla2 = fLInt,
  Ncl1 = fLReal,
  Ncl2 = fLInt,
  Ncl3 = fLInt,
  Ncl4 = fLReal,
  Ncl5 = fLInt,
  Ncl6 = ConvertAbilityStringLevelField,
  Nab1 = fLReal,
  Nab2 = fLReal,
  Nab3 = fLInt,
  Nab4 = fLReal,
  Nab5 = fLReal,
  Nab6 = fLReal,
  Nhs6 = fLInt,
  Ntm1 = fLReal,
  Ntm2 = fLReal,
  Ntm3 = fLInt,
  Ntm4 = fLInt,
  Neg1 = fLReal,
  Neg2 = fLReal,
  Neg3 = ConvertAbilityStringLevelField,
  Neg4 = ConvertAbilityStringLevelField,
  Neg5 = ConvertAbilityStringLevelField,
  Neg6 = ConvertAbilityStringLevelField,
  Ncs1 = fLReal,
  Ncs2 = fLReal,
  Ncs3 = fLInt,
  Ncs4 = fLReal,
  Ncs5 = fLReal,
  Ncs6 = fLReal,
  Nsy1 = fLReal,
  Nsy2 = fLInt,
  Nsy3 = fLReal,
  Nsy4 = fLReal,
  Nsy5 = fLReal,
  Nsyu = fLInt,
  Nfy1 = fLReal,
  Nfy2 = fLReal,
  Nfyu = fLInt,
  Nde1 = fLReal,
  Nde2 = fLReal,
  Nde3 = fLReal,
  Nde4 = fLReal,
  Nic1 = fLReal,
  Nic2 = fLReal,
  Nic3 = fLReal,
  Nic4 = fLReal,
  Nic5 = fLReal,
  Nic6 = fLReal,
  Nso1 = fLReal,
  Nso2 = fLReal,
  Nso3 = fLReal,
  Nso4 = fLReal,
  Nso5 = fLReal,
  Nlm2 = fLReal,
  Nlm3 = fLInt,
  Nlm4 = fLReal,
  Nlm5 = fLReal,
  Nlm6 = fLInt,
  Nvc1 = fLInt,
  Nvc2 = fLInt,
  Nvc3 = fLReal,
  Nvc4 = fLReal,
  Nvc5 = fLReal,
  Nvc6 = fLReal,
  Nvcu = fLInt,
  Tau1 = fLInt,
  Tau2 = fLInt,
  Tau3 = fLInt,
  Tau4 = fLInt,
  Tau5 = fLReal,
  Idg1 = fLInt,
  Idg2 = fLInt,
  Idg3 = targetList,
  Uuf1 = fLInt,
  Uuf2 = fLInt,
  Uuf3 = targetList,
  Hsb1 = fLReal,
  Hsb2 = fLReal,
  Hsb3 = defenseTypeInt,
  Iofr = fLReal,
  AIvu = fLInt,
  Akb2 = fLInt,
  ausk = unitSkinList,
  Aat1 = fLInt,
  }
end
[/spoiler]
 

Attachments

  • TasAbilityFieldTooltip.w3x
    28 KB · Views: 14
Last edited:
Level 16
Joined
Mar 25, 2016
Messages
1,327
That's pretty cool. HoverOriginButton and FrameLoader are only required if you use TasAbilityFieldTooltipInitAuto?
I think the user could also manually update the skills whenever attributes change or the skill is leveled up instead of using the hover event.

In the demo map, the values are not updated correctly when you drop items. It's always one action behind:
Blizzard 1st item picked -> 240 dmg
2nd item picked -> 276 dmg
3rd item picked -> 312 dmg
item dropped -> still 312 dmg
item dropped -> 276 dmg
last item dropped -> 240 dmg
If you now pick up strength, it forces an update to 204, which seems correct.
The damage at the start is 180. When you pick up strength to force an update, you end up with 204.

Is it also possible to access values outside the ability? E.g. the damage of the Water Elemental or even better provide user values. You could add an optional 3rd parameter to TasAbilityFieldTooltipGetRuntimeText, which is a list of strings. Then in the tooltip, have some tokens like %1, %2, ... that are replaced by these strings. That would give more option with coded abilities, where you often have completely different effects than listed in the object editor.
 
HoverOriginButton and FrameLoader are only required if you use TasAbilityFieldTooltipInitAuto?
yes, the core TasAbilityFieldTooltipGetRuntimeText works without HoverOriginButton, without FrameLoader and without calling TasAbilityFieldTooltipInitAuto. But then it only returns an updated text with the placeholders replaced, no tooltip updating.

Is it also possible to access values outside the ability?
It can only read fields of the same ability, the idea was to have dynamic tooltips for default skills. Of which you set a field somewhere and the systems shows it inside the tooltip.
Technically it would be possible to read other ability fields or unit/item/script data but tasabilityfieldtooltip does not.

Then in the tooltip, have some tokens like %1, %2, ... that are replaced by these strings. That would give more option with coded abilities, where you often have completely different effects than listed in the object editor.
Would not work that well with the auto system (i mean how to get this strings numbers then), but nice with manual usage. Maybe.

In the demo map, the values are not updated correctly when you drop items. It's always one action behind:
Blizzard 1st item picked -> 240 dmg
Thats because of the naive approach in the drop event, which happens before the unit lost the item. One would need to wait a 0s timer in the drop event. But nah simple demo map trigger.
 
Just to reduce the file size impact of importing this into a map, I would recommend either caching the functions ConvertAbilityRealLevelField and ConvertAbilityIntegerLevelField, or instead just pass a massive list of abilities that use each of them to a handler function that will assign them correctly. Something like:

Lua:
local function Carl(...)
    for _,id in ipairs{...} do ConvertAbilityRealLevelField(id) end
end

local function Cail(...)
    for _,id in ipairs{...} do ConvertAbilityIntegerLevelField(id) end
end

I would also recommend reducing the API to extend off of TasAbilityFieldTooltip instead of writing values arbitrarily to the _G table:

TasAbilityFieldTooltipGetRuntimeText
-> this.GetRuntimeText

TasAbilityFieldTooltipInitAuto
-> this.InitAuto
 
Last edited:
Top