Thread Rating:
  • 8 Vote(s) - 3.88 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tutorial: Getting Started with ACR
#1
Hello,

I've been trying to get up and running with ACR however I've found the current documentation to be incredibly poorly written and frustrating to navigate, so here is a quick summary of my findings so far with regards to creating ACR profiles.

I'm probably getting several things wildly incorrect, however hopefully it should serve as a launchpad for others to get started and attract some useful feedback.

The included script is released under MIT license so do what you like with it.

1. Basic Information
ACR profiles consist of a single .lua file.
ACR profiles are created by placing this lua file in C:\MINIONAPP\Bots\FFXIVMinion64\LuaMods\ACR\CombatRoutines.
Example: http://i.imgur.com/mJalxsB.png
ACR Documentation is available at: http://wiki.mmominion.com/doku.php?id=acr
FFXIVMinion Documentation is available at: https://github.com/MINIONBOTS/FFXIVMinion/wiki (navigate using the sidebar on the right)
More FFXIVMinion Documentation is available at http://wiki.mmominion.com/doku.php?id=lua_api

2. Example Profile
I have taken the default ACR profile listed on the wiki and modified it to better suit my needs. Here is the example profile I have created:
Pastebin: https://pastebin.com/636Zd7F9
Code:
local profile = {}

local library = {
   stone = { skillType = 1, id = 119 },
   cure = { skillType = 1, id = 120 }
}

local state = {
   config = {
       dps = false,
       autoTargeting = false
   }
}

local config = {
   IsDpsEnabled = function() return state.config.dps end,
   IsAutoTargetingEnabled = function() return state.config.autoTargeting end
}

local function GetStateName()
   return profile.GUI.name .. "_State"
end

local function OnCast()
   local stone = ActionList:Get(library.stone.skillType, library.stone.id)
   local cure = ActionList:Get(library.cure.skillType, library.cure.id)

   -- First, check our hp
   if Player.hp.percent < 75 then
       -- We're low on hp, so cast cure
       if cure and cure:IsReady(Player.id) then
           cure:Cast(Player.id)
       end
       return true
   end

   -- We don't need to heal, but are we configured for DPS?
   if not config.IsDpsEnabled() then
       return false
   end

   -- We're not low on hp, so check if we have a target
   local target = Player:GetTarget()
   if target == nil then
       -- Nothing to do
       return false
   end

   -- We have a target, so cast stone on it
   if (stone and stone:IsReady(target.id)) then
       stone:Cast(target.id)
       return true
   end

   return false
end

local function OnUpdate(event, tickcount)
   -- This is called when ACR is running in standalone mode.
   -- Combat logic should be in OnCast.
   -- Targeting, movement etc should be in OnUpdate.
   local target = Player:GetTarget()
   
   if target == nil and config.IsAutoTargetingEnabled() then
       -- It's important to specify the target is alive, otherwise we will just keep targeting the monster we killed
       local targetList = EntityList("nearest,onmesh,attackable,maxdistance=20,alive")
       if targetList then
           local entityId, entity = next(targetList)
           if entity ~= nil then
               Player:SetTarget(entityId)
           end
       end
   end

   if Player.incombat then
       -- Call this regardless of whether we have a target, as the combat routine may not rely on one (buffs, healing etc)
       OnCast()
   end
end

local function OnDraw()
   if (profile.GUI.open) then
       profile.GUI.visible, profile.GUI.open = GUI:Begin(profile.GUI.name, profile.GUI.open)
       if (profile.GUI.visible) then
           state.config.dps = GUI:Checkbox("Enable DPS Rotation", state.config.dps)
           state.config.autoTargeting = GUI:Checkbox("Enable Auto Targeting", state.config.autoTargeting)
           if GUI:Button("Save", 64, 16) then
               ACR.SetGUIVar(state.config, GetStateName())
           end
       end
       GUI:End()
   end    
end

local function OnOpen()
   profile.GUI.open = true
end

local function OnLoad()
   state.config = ACR.GetSetting(GetStateName(), state.config)
end

profile.classes = {
   -- GLA/PLD
   [FFXIV.JOBS.GLADIATOR]      = false,
   [FFXIV.JOBS.PALADIN]        = false,
   -- PGL/MNK
   [FFXIV.JOBS.PUGILIST]       = false,
   [FFXIV.JOBS.MONK]           = false,
   -- MRD/WAR
   [FFXIV.JOBS.MARAUDER]       = false,
   [FFXIV.JOBS.WARRIOR]        = false,
   -- LNC/DRG
   [FFXIV.JOBS.LANCER]         = false,
   [FFXIV.JOBS.DRAGOON]        = false,
   -- ARC/BRD
   [FFXIV.JOBS.ARCHER]         = false,
   [FFXIV.JOBS.BARD]           = false,
   -- ROG/NIN
   [FFXIV.JOBS.ROGUE]          = false,
   [FFXIV.JOBS.NINJA]          = false,
   -- CNJ/WHM
   [FFXIV.JOBS.CONJURER]       = true, -- Enabled
   [FFXIV.JOBS.WHITEMAGE]      = true, -- Enabled
   -- THM/BLM
   [FFXIV.JOBS.THAUMATURGE]    = false,
   [FFXIV.JOBS.BLACKMAGE]      = false,
   -- ACN/SCH/SMN
   [FFXIV.JOBS.ARCANIST]       = false,
   [FFXIV.JOBS.SCHOLAR]        = false,
   [FFXIV.JOBS.SUMMONER]       = false,
   -- DRK
   [FFXIV.JOBS.DARKKNIGHT]     = false,
   -- MCH
   [FFXIV.JOBS.MACHINIST]      = false,
   -- AST
   [FFXIV.JOBS.ASTROLOGIAN]    = false,
   -- BTN        
   [FFXIV.JOBS.BOTANIST]       = false,
   -- FSH
   [FFXIV.JOBS.FISHER]         = false,
   -- MIN
   [FFXIV.JOBS.MINER]          = false,
   -- CRP
   [FFXIV.JOBS.CARPENTER]      = false,
   -- BLK
   [FFXIV.JOBS.BLACKSMITH]     = false,
   -- ARM
   [FFXIV.JOBS.ARMORER]        = false,
   -- GLD
   [FFXIV.JOBS.GOLDSMITH]      = false,
   -- LTW
   [FFXIV.JOBS.LEATHERWORKER]  = false,
   -- WVR
   [FFXIV.JOBS.WEAVER]         = false,
   -- ALC
   [FFXIV.JOBS.ALCHEMIST]      = false,
   -- CLN
   [FFXIV.JOBS.CULINARIAN]     = false,
}

profile.GUI = {
   open = false,
   visible = true,
   name = "AutoWHM Settings"
}

-- The Draw() function provides a place where a developer can show custom options.
profile.Draw = function() if OnDraw ~= nil then return OnDraw() end end

-- The OnOpen() function is fired when a user pressed "View Profile Options" on the main ACR window.
profile.OnOpen = function() if OnOpen ~= nil then return OnOpen() end end

-- The OnLoad() function is fired when a profile is prepped and loaded by ACR.
profile.OnLoad = function() if OnLoad ~= nil then return OnLoad() end end

-- The OnClick function is fired when a user clicks on the ACR party interface.
-- It accepts 5 parameters:
-- mouse /int/ - Possible values are 0 (Left-click), 1 (Right-click), 2 (Middle-click)
-- shiftState /bool/ - Is shift currently pressed?
-- controlState /bool/ - Is control currently pressed?
-- altState /bool/ - Is alt currently pressed?
-- entity /table/ - The entity information for the party member that was clicked on.
profile.OnClick = function(mouse,shiftState,controlState,altState,entity) if OnClick ~= nil then return OnClick(mouse,shiftState,controlState,altState,entity) end end

-- The OnUpdate() function is fired on the gameloop, like any other OnUpdate function found in FFXIVMinion code.
profile.OnUpdate = function(event, tickcount) if OnUpdate() ~= nil then return OnUpdate(event, tickcount) end end

-- Cast is called constantly by the FFXIVMinion assist bot. This contains our combat logic. It must be called manually by OnUpdate to use with ACR standalone.
profile.Cast = function() if OnCast ~= nil then return OnCast() end end

return profile

Some key things to note are that:
  1. The interface that FFXIVMinion expects is all defined in the profile table at the bottom of the script. I have designed it this way to make a clear distinction between which methods are mandatory for an ACR script, and which is just convention.
  2. Everything in the file is local. I have no idea if this results in unintentional side effects, and have no way to test it.
  3. The profile variable must be returned at the bottom of the script.
  4. The classes are all initialised to false by default. I have included a complete list of classes for ease of re-use.
3. Design Goals
This script does not implement any "real" features (e.g. monitoring tick count to avoid spamming actions, predicting results to avoid overhealing, cancelling useless casts etc) and is not suitable for actual use. It is a framework for further development for people wishing to write their own ACR scripts.
When writing this script, my main design goal was to answer as many basic questions as possible. The main ones I've found so far are:

------------------------------------

What interface must be provided for an ACR script?
All the members of the profile table are mandatory for an ACR script to function correctly.

How do I persist settings?
Settings are persisted by calling ACR.SetGUIVar(datagloballyUniqueSettingName). They are loaded by calling ACR.GetSetting(globallyUniqueSettingNamedefaultData).

Where do I place combat logic?
The Cast() function is called constantly by the FFXIVMinion assist bot to do combat. For this reason, all combat logic should be in this function, or in functions called by this function.

Where do I place targeting logic?
Targeting and other non-combat logic should be placed in OnUpdate(). Targeting should be separated from the combat logic to avoid conflicts - for example, if the assist bot is trying to target the nearest enemy but your script is trying to target the lowest health enemy, they will conflict.

How do I select nearby targets?
An example of this is in the OnUpdate function.

How do I cast spells and abilities?
An example of this is in the OnCast function. The ids are found by starting FFXIVMinion and then choosing MMOMinion->FFXIVMinion->Dev Tools->ActionList and selecting "Filter Actions" to show only the actions you currently have.

------------------------------------

4. Conclusion
It took me a very long time to figure all this stuff out and track down the necessary documentation but I think I've found enough of it now to be able to start developing ACR scripts. I've probably missed a lot of things that you may wish to know, in which case feel free to ask in a reply to this thread and I'll do my best to answer.

Additionally, a lot of this information is probably wrong or not best practices. This is my starting point, this is what I got to work, and with the poor quality documentation and incredible lack of examples I have no way of verifying whether what I am doing is correct. If someone wants to correct me then by all means go ahead. I may start my own documentation for this stuff but I don't know if that's a project I want to take up yet or not.

I'll continue to develop AutoWHM for the time being but I don't know if I'll ever release it in its entirety, as that would require me to actually get it to a state where it's worth using and I don't know if I want to spend that much time on it :)

Thank you for taking the time to read this, and I hope it helped.
Reply
#2
Well that didn't take long :) I've found my first mistake.

The loading code should be:
Code:
local function OnLoad()
   local persistedConfig = ACR.GetSetting(GetStateName(), state.config)
   for k,v in pairs(persistedConfig) do
       state.config[k] = v
   end
end
This is to avoid overwriting any new keys you add to the config table.
Reply
 


Forum Jump:


Users browsing this thread: 1 Guest(s)

We help you win the game.

FFXIV Bot and More.

 

Products