• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Custom Ability Button System

Status
Not open for further replies.
Custom Ability Button System (CABS) is a custom UI that is meant to be more compatible for maps in which the player controls only 1 main unit a hero. It features a custom interface to activade default and vanila Abilities of warcraft 3, as long they are setuped inside the system. One can let CABS create alot of Ability Buttons as seen below in the images there are 27 buttons. This buttons can have Hotkeys which can differ between Players, as the ability the button activades. In CABS active abilities are only kept onto a unit as long it is needed, that is done to removed the problem of conflicting orderIds which exist in warcraft 3 when an unit owns for example 2 stormbolts the newer gained one is if castable casted regardless if the user wanted to cast the other one.

CABS's activation differs from warcraft 3s ability button. In warcraft 3 one clicks a button with that starting the targeting then a target is confirmed and the ability is ordered that is quite fitting for a strategic game like warcraft 3, but it might be unsuited for some other genres.
In CABS one presses a Ability Button which pops up an indicator showing the current selected target/point + the area of impact. As soon the key/button is released the ability is ordered to cast onto that position/target.
For single target abilities one selects a unit and it becomes the current selected Target. All single target abilities will be casted onto that target. Or onto oneself, if that feature is eanbled and the casting onto the selected target fails (can't heal/buff enemies).
No Target Spells like Thunderclap/Berserk will display as long as clicked an aoe Indicator around the Hero, the ability starts as soon the hotkey/button is released.
Point skills follow the mouse and are casted/ordered as soon the button is released.

Further features are, with CABS one can give abilities charges which can autorefill based on time and give abilities shared cooldown. Shared cooldown means that when using an ability all ability it is connected for shared Cooldown will also start the same amount of cooldown, if they are not alreay have a longer cooldown.
In CABS one does not have the own hero selected to give move, cast or attack orders.

what does CABS feature:
  • Shared Cooldown
  • No orderId conflicts (an unit can have any amount of stormbolts for example)
  • Abilities can have charges
  • Cast Target Spells onto current selected Target
  • Customizeable Hotkeys
  • Customizeable Button positions
  • Show AoE for non point skills (Fan of Knives, shockwave, chain lightning and others)
    • From V2.0a
    • Drag and Drop Frames (which the mapper has choosen as moveable)
    • moved Frames can be saved and loaded from files
    • Swap the skills of two buttons also Drag and Drop (through not animated)

Disadvantages:
There are some skills are not useable in CABS cause it is heavly based on Stops/end-Cast event.
Charge Gold/Lumber, Defend, Magic Defence, Ambush... and some I do not know of.
Does not display if an ability is uncastable by silence, requirement.
Can not enable autocast.
The targeting uses multiple syned events which is bad with bad connection.

How to setup a skill to be useable in CABS?
First one teaches CABS what the skills in your map are, inside scriptfile HeroSkills.
There are 4 setups in HeroSkills, 2 are required: How to perform orders and which order do abilities use.
can look like that:
Lua:
SkillData.banish = SkillData.TARGET_TYPE_TARGET --this is a single Target order
SkillData.blink = SkillData.TARGET_TYPE_POINT   -- this is a point/ground order
SkillData.avatar = {SkillData.TARGET_TYPE_SELF, SkillData.DELAY_TYPE_2s} --this is no target/self cast order which is removed 2 seconds after it ended casting
SkillData.sphinxform = {SkillData.TARGET_TYPE_SELF, SkillData.DELAY_TYPE_MORPH, OrderId("unsphinxform"), true} --this is a morph spell having special rules for removing the ability.
This line tells CABS that the skill "AHtc" uses the order "thunderclap".
SkillData.setSkill("AHtc", "thunderclap")

With such lines CABS could order Heroes to activate abilities as soon a button is pressed, but yet this abilities were not given to anyone.

To give an unit/Player abilities one uses one of these functions. CABS is more of an player based system.
Lua:
        function CustomUI.setSkillButton(player, buttonIndex, spellCode)
            set for player the ability of buttonIndex to spellCode

        function CustomUI.setSkillButtonBatch(player, dataTable)
            interpretts an string or table adds the skills to current Hero of the player for the system and changes the skills of buttons
            expected string format: "buttonIndex,spellCode,buttonIndex,spellCode" example "1,AHtb,2,AOwk,21,ANdo"
            expected table format: {buttonIndex,spellCode,buttonIndex,spellCode...}
            that can be repeated

        function CustomUI.setSkillButtons(player, spellTable)
            changes the skills used for 1 to lenght of spellTable is expected to be an array of abilityCodes as integer
            {FourCC("AHbz"),FourCC("AHtc")}
After one used one of them some button should be visible and have a skill.

current API
Lua:
 --Custom Ability Button System
    --[[ API
        function getDisabledIcon(icon)
            ReplaceableTextures\CommandButtons\BTNHeroPaladin.tga -> ReplaceableTextures\CommandButtonsDisabled\DISBTNHeroPaladin.tga
        function stringToFramePointRow(text)
            converts a string into 2 points which would built a row of Frames in that direction, attaching the new to the previous Frame
            The First returned value is the point of the current frame the second the point of the previous Frame
            returns as 3 factor of x and as 4. factor of y
        function framePoint2Index(point)
            reversed ConvertFramePointType
     
        function CustomUI.setTarget(player, unit)
        function CustomUI.getTarget(player)
        function CustomUI.setHero(player, unit)
        function CustomUI.getHero(player)
        function CustomUI.getUnitStats(value)

        function CustomUI.skillButtonActionDo(player, buttonIndex, isKeyReleased)
            this could be used to force the activation of an button for player, isKeyReleased (false) = press it stats the targeting
            isKeyReleased (true) = fires the button
 
        function CustomUI.setSkillButton(player, buttonIndex, spellCode)
            set for player the ability of buttonIndex to spellCode

        function CustomUI.setSkillButtonBatch(player, dataTable)
            interpretts an string or table adds the skills to current Hero of the player for the system and changes the skills of buttons
            expected string format: "buttonIndex,spellCode,buttonIndex,spellCode" example "1,AHtb,2,AOwk,21,ANdo"
            expected table format: {buttonIndex,spellCode,buttonIndex,spellCode...}
            that can be repeated

        function CustomUI.setSkillButtons(player, spellTable)
            changes the skills used for 1 to lenght of spellTable is expected to be an array of abilityCodes as integer
            {FourCC("AHbz"),FourCC("AHtc")}
 
        function CustomUI.getHeroSpellLevel(unit, spellCode)

        function CustomUI.setHeroSpellLevel(unit, spellCode, level, ignoreMaxLevel)
            in this system abilities are only owned from pressing to casting an ability, its current level is read from an tableValue which is written with this function.
            ignoreMaxLevel(true) allows to set it to a level above what is defined in object Editor

        function CustomUI.improveAllSpells(unit, ignoreMaxLevel)
            wrapper for CustomUI.setHeroSpellLevel improving all abilities the unit knows by this system

        function CustomUI.swapSkillButtons(player, buttonA, buttonB)

        function CustomUI.startCooldown(unit, spellCode[, time, canReduceCooldown])
            start the cooldown for spellCode for unit with time, with canReduceCooldown(false) a running cooldown is not shorten
            if time is not passed the spellCodes cooldown is used
        function CustomUI.getHeroSpellCooldown(unit, spellCode)

        function CustomUI.setHeroSpellCharge(unit, spellCode, currentCharges, rechargeTime, rechargeMax)
            alters the charge behaviour of that spell for that unit.
            sets the current Charges to currentCharges.
            if you don't want to change a field use nil
            if currentCharges, rechargeTime, rechargeMax are all nil the spellCode will not use charges for that unit anymore.
            with a rechargeTime > 0.0 used Charges will recharge upto rechargeMax

        function CustomUI.comsumeCharge(unit, spellCode)
            reduces a charge and starts the refreshTimer, if it is not running currently.
            returns the new currentCharge count
     
        function CustomUI.regainCharge(chargeObject, ignoreMax)
            with ignoreMax one can regaineCharges of the maximum. Returns if the current charges after regaining one is lower then the max
        function CustomUI.heroRegainCharge(unit, spellCode, ignoreMax)
            wrapper for CustomUI.regainCharge

        function CustomUI.getHeroSpellChargeCurrent(unit, spellCode)
 
        function CustomUI.getHeroSpellChargeMax(unit, spellCode)
 
        function CustomUI.getHeroSpellChargeRechargeTime(unit, spellCode)

        function CustomUI.setButtonPos(player, buttonIndex, x, y)
            moves FRAMEPOINT_BOTTOMLEFT of button for player to x y

        function CustomUI.setRowPos(player, rowNumber, x, y)
            wrapper for CustomUI.setButtonPos

        function CustomUI.setButtonPosBatch(player, dataTable)
            interpretts an string or table
            expected string format: "buttonIndex,x,y,buttonIndex,x,y" example "1,0.775,0.0,2,0.775,0.03"
            expected table format: {buttonIndex,x,y,buttonIndex,x,y...}
            that can be repeated
            instead of 1 one can use "R1" to move the first button of row 1.

        function CustomUI.setRowAlign(player, rowNumber, align)
            for player row becomes changed to align
            allign should be a a string like TOPLEFT or BOTTOMRIGHT
            using this will Link Buttons, after that moving individual buttons will move all buttons connected in the row

        function CustomUI.setRowAlignBatch(player, dataTable)
            interpretts an string or table
            expected string format: "rowNumber,align,rowNumber,align" example "1,TOP,2,LEFT"
            expected table format: {buttonIndex,x,y,buttonIndex,x,y...}

 
        function CustomUI.setHotkeys(hotkeys)
            changes hotkeys of buttonIndex 1 to lenghth of hotkeys, hotkeys is expected to be a string in uppercase example "YXCVBNMASDFGHJKL1234567890"
            do not use "+" "Ä" "Ö" "Ü" "#" "-", if you want such special chars you have to use CustomUI.setHotkeyBatch
 
        function CustomUI.setHotkey(buttonIndex, hotkey)

        function CustomUI.setHotkeyBatch(player, dataTable)
            interpretts an string or table
            expected string format: "buttonIndex,hotkey,buttonIndex,hotkey" example "1,A,2,Ä,3,4,12,W" Button1:A, Button2:Ä, Button3:4, Button12:W
            expected table format: {buttonIndex,hotkey,buttonIndex,hotkey...}


        function UnitInfo.Create(unit, parentFrame, barMode, right2Left, toolTips, isNameTooltip)
            creates an ui-element that displays an unit, its face, name, mana and life that ui-element can also be clicked to select the unit or move the camera to it.
            returns a new UnitInfo frame and has to be positionated.
            UnitInfo can be displayed and hold different Units localy, but the have to be created for all players.

        function UnitInfo.setUnit(unitInfo, unit)
            can be done async

     
        function CustomUI.createHeroSpell(unit, spellCode)
            this teaches a spellCode to a hero is normaly used automatically if spellCode data is required
            Use this if you want to add a skill to a unit without placing it onto a SkillButton

        function CustomUI.updateButtons(player)
            update all buttons for player
        function CustomUI.updateButton(player, buttonIndex)

        userMoveAbleFrame API

        function CustomUI.userMoveAbleFrame(frame)
            makes this frame moveable by user with drag and drop, only works on frameTypes supporting FRAMEEVENT_MOUSE_ENTER and FRAMEEVENT_MOUSE_LEAVE
            has to be called sync cause it creates events, unlike the other userMoveAbleFrame functions which should be used async
            The user changed position can be saved in a file and loaded. When providing the Save Load Mechanic. The frames are identyfied by the order they were added to userMoveAbleFrame.
            Means when adding more userMoveAbleFrames not at the end order wise, then a saved File can move a wrong frame.

        function CustomUI.userMovedFrame(frame, point, frameB, pointB, x, y)
            save movement done by user
        function CustomUI.userMovedFrameAbs(frame, point, x, y)
            wrapper

        function CustomUI.userMovedFrameReset(frame)
            removes the user moved mark and moves the frame back to its defined Reset pos
        function CustomUI.userMovedFrameResetAll(player)
            batch for userMovedFrameReset for all userMoveAbleFrames

        function CustomUI.userMovedFrameDefineReset(frame, point, frameB, pointB, x, y, doMove)
            where the frame will be moved back to, doMove(true) move them with this call
        function CustomUI.userMovedFrameDefineResetAbs(frame, point, x, y, doMove)
            wrapper

        function CustomUI.userMovedFrameSave(fileName)
            saves userMovedFrames into a file fileName should end with .txt
     

        function CustomUI.userMovedFrameLoad(fileName)
            Loads data from a saved File.
    --]]

The uploaded map contains an simple deathmatch game, Each player has an hero that revives as long its altar is alive the last player alive wins, providing 1 Hero per playable race in Warcraft 3. CABS also creates this menu Button at the bottom Left of the screen which open other menus allowing to config the buttons during the game runs using text commands insert into editboxes.
WC3ScrnShot_101219_145606_02.png WC3ScrnShot_101219_144558_01.png WC3ScrnShot_101019_095238_03.png

The terrain of the demo map is a terrain map uploaded in hiveworkshop: Ashenvale Template 1.

Credits: Bribe, CanFight, TriggerHappy
ChangeLog:
2.0c
Removed the Custom GUI Action.
The used UnitIndexer was simpled down.
2.0a/b
Added multiple Getters, Added a function to regain a charge
Extended the userMoveableFrames, they can now be saved to file and read from a file.
All UserMoveableFrames can now have a reset position.
Seperated AltarInfo and Demo Map Menu from the System Code.
Skills of CABS can now be swaped by drag and drop using right click while not being inside UI-Edit Mode
CustomUI.SpellCodeButton[spellCode] is now written localy as it was meant to be.
b) changed the Layerstyle of Skill Text Tooltip inside fdf.
1.9 Added UI-Edit during this Mode one can move SkillButtons and UnitInfos around.
1.8 First Release
 

Attachments

  • Custom UI CABS 2.0c release2.w3x
    242.5 KB · Views: 281
Last edited:
Kind of. Each Button represents 1 real ability inside Object Editor, Until a Button is clicked the Unit does not have the Ability or should not, there are cases in which units have the ability hidden from a previous cast which than is unhidden. When clicking a CABS Button the ability Order Type is loaded and based on that a image is displayed. CABS orders the Hero of a player to perform a normal warcraft 3 abilities as soon the Button is released. Hotkeys also press/release Buttons.

If you ask about the right Click. That is done using SyncEvent and an simple UnitIndex. This UnitIndex is required to convert the async BlzGetMouseFocusUnit into an sync value for all players, Through that is only used for right Click attacking.

The order unit natives tells, if the order failed, if it fails then the ability is removed instantly, otherwise wait until endCast-Event, shortly after the EndCast-Event the unit loses the casted ability that is done to avoid coliding OrderIds, Results into having to manage cooldown by oneself. But not all abilities are lost after the endcast Event some abilities require special rules to perform correctly, Like Manashield, morphings, spiritWolf, MirrorImage, Immolation, Divine Shiel and many others many of such skills are not removed but instead are hidden until something happens or the player wants to perform them again.

Each Button has saved one abilityCode for each player. When pressing or releasing a Skillbutton CABS loads the ability for the current player. Units have a databse for known spells which are tables, again this abilities are not considered as beeing on the unit in a warcraft 3 perspective. The Abilities are setuped inside the scriptFile HeroSkills. Here the orders behaviour is set and for each ability the order being used. Also charges and shared Cooldown. One could do that in another file, if one wanted, As one could set the charges unit specific.
 
Level 18
Joined
Oct 17, 2012
Messages
820
It would be nice if one could move ability buttons in game by holding on to a button and then hovering it over another ability and letting go to switch the buttons' positions. Such feature would be similar to how one moves items in an inventory. Heck, if one could move any of the UI elements freely in game, that would be grand.
 
I remembered something CanFight said, Hence one can now in V1.9 move SkillButtons and the 3 precreated UnitInfos of the demo Map around. This feature is useable by toggling UI-Edit inside menu, While it is toggled a right Click on a moveable Frame will start the moving. Until the Right Click is released the Frame follows the mouse position.

CanFight shared his Idea of clustering the Screen with a grid of Frames having Mouse Enter Events, that way one can get the mouse position on screen based on a grid with accuracy x without having a Get Mouse on Screen pos native.
 
Last edited:
Uploaded 2.0a
2 CABS Buttons can now swap skills using drag and drop (right Mouse button) while not being in UI-Edit Mode, (This is not animated). Right Click Button A then Move to Button B and release the button.
Seperated some Demo Map features from the system core.
Making an Frame drag and drop was improved and a gathered api handling it, called CustomUI.userMove. this same api also handles reset positions which was previoulsy kinda bad with that now removed resetButton function.
Uses now a custom version of TriggerHappy's FileIO -> one can save and Load moved Frames to files. The files are saved in user\\Documents\Warcraft III\CustomMapData\<name your have choosen inside CustomUI>\CABS\
Added multiple missing getter functions.
The frames popingup (center ones) inside the demo map are also now moveable Frames.

Edit: 2.0b)
SkillButton's Tooltip's text is no ignored by FrameEvents and mouse. Peviously it could bug when a button was at the same position as the Tooltip Frames text.
 
Last edited:

Eye

Eye

Level 2
Joined
Jun 30, 2018
Messages
19
Map did not open with 131 editor for me. It displayed two errors on trigger loading, then editor crashed.
1.PNG

2.PNG

3.PNG
 
The demo map specific rules were created with GUI and I use a custom GUI action database, that demo Map Triggers contained such a custom GUI action, Therefore this error popup.
I uploaded a new version 2.0c without such custom actions. Also:
Added more supported hotkeys.
Simpled down the UnitIndexer, it was reduced to handleId -> Unit.
 
Status
Not open for further replies.
Top