FFXIAH.com

Language: JP EN DE FR
New Items
2023-11-19
users online
Gearswap_Academia_6
Class #6

Master thread

Lesson 13 - Including external files and using Modes.lua

Today we're going to look into including files, specifically Modes.lua. What is an include file? Including files lets you in essence add the content of another lua file into your own lua. It's like Inception, lua edition. One of the main reason to do this is so you can write the extra file once, and re-use it's content in many different luas. We'll be using Modes.lua for now but do note that you can make and include any .lua file as long as those files are located in any of the following folder paths:
Code
Windower\addons\libs-dev\
Windower\addons\libs\
Windower\addons\GearSwap\data\<player.name>\
Windower\addons\GearSwap\data\common\
Windower\addons\GearSwap\data\
Windower\addons\GearSwap\<player.name>\
Windower\addons\GearSwap\common\
Windower\addons\GearSwap\
Windower\addons\GearSwap\libs


To get started, in your Test_JOB.lua that we've been working with so far. Add the following line at the very very top of the file.
Code
include('Modes.lua')


This files comes with gearswap so it'll already be avaible to be included as we just did. Why modes.lua? Well this allows us to construct a "Mode" data structure in our lua, which is basically a table but it comes with a few functions that are going to be very useful for us. For now under the include line we just added, and somewhere above the get_sets() function.
Code
NukingMode = M{'Normal', 'Acc', 'MagicBurst', MagicBurstAcc}


Note: Since we included the Modes.lua file, we can now put a capital M before the brackets {} that types what inside the brackets as modes.

Ok So we have a NukingMode now... So we need a way to selected what mode in that list we want. Back in the previous lesson(12) in Class #5 we learned about user functions. So we'll add a command in there to select which mode we want. Here's where we left this function the last time:
Code
function self_command(command)
    local commandArgs = command                 -- First we copy the content of command inside a new variable for our use. 
    if #commandArgs:split(' ') >= 2 then             -- We check if there is 2 or more words in the commandArgs.
            commandArgs = T(commandArgs:split(' '))         -- If there is indeed 2 or more words, we split each words into a different entry, so commandArgs[1] and commandArgs[2] if there was 2 words.
         
        if commandArgs[1]:lower() == "toggle" then      -- the :lower() part makes sure that capital letters are now lowercase, so "Toggle" and "toggle" are both fine. 
            if commandArgs[2]:lower() == "usedtsets" then   -- We passed the first word test, now we check for the 2nd word.
                if useDTsets then           -- if useDTsets is true
                    useDTsets = false       -- then we set it to fasle
                else                    -- else, means it was false
                    useDTsets = true        -- so we set it to true.
                end
            else
                windower.add_to_chat(123,'Error. 2nd word unkown, don't know what to toggle.')
            end
        else
            windower.add_to_chat(123,'Error. The first word is not toggle.')
        end
    else
        windower.add_to_chat(123,'Error. Only 1 word command. Need 2 words')
    end
end


Now we'll keep the first word "toggle" but we'll add "nukemode" as the 2nd word. Also we'll make use of one of the available functions specific to modes, specifically the 'cycle' function, as well as the 'current' way to query which mode it is currently in. The available functions are as follow:
Code
-- Public mode manipulation functions:
--
-- 1) m:cycle() -- Cycles through the list going forwards.  Acts as a toggle on boolean mode vars.
-- 2) m:cycleback() -- Cycles through the list going backwards.  Acts as a toggle on boolean mode vars.
-- 3) m:toggle() -- Toggles a boolean Mode between true and false.
-- 4) m:set(n) -- Sets the current mode value to n.
--    Note: If m is boolean, n can be boolean true/false, or string of on/off/true/false.
--    Note: If m is boolean and no n is given, this forces m to true.
-- 5) m:unset() -- Sets a boolean mode var to false.
-- 6) m:reset() -- Returns the mode var to its default state.
-- 7) m:default() -- Same as reset()


If you want to know more on Modes, you can find the Modes.lua in Windower\addons\GearSwap\libs and the top of the files explains a lot about it.

So back to our command function. The updated function will look like this:
Code
function self_command(command)
    local commandArgs = command                 -- First we copy the content of command inside a new variable for our use. 
    if #commandArgs:split(' ') >= 2 then             -- We check if there is 2 or more words in the commandArgs.
            commandArgs = T(commandArgs:split(' '))         -- If there is indeed 2 or more words, we split each words into a different entry, so commandArgs[1] and commandArgs[2] if there was 2 words.
         
        if commandArgs[1]:lower() == "toggle" then      -- the :lower() part makes sure that capital letters are now lowercase, so "Toggle" and "toggle" are both fine. 
            if commandArgs[2]:lower() == "usedtsets" then   -- We passed the first word test, now we check for the 2nd word.
                if useDTsets then           -- if useDTsets is true
                    useDTsets = false       -- then we set it to fasle
                else                    -- else, means it was false
                    useDTsets = true        -- so we set it to true.
                end
            elseif commandArgs[2]:lower() == "nukemode" then
                NukingMode:cycle()
                windower.add_to_chat(123,'Nuking Mode now set to '..tostring(NukingMode.current))            
            else
                windower.add_to_chat(123,'Error. 2nd word unkown, don't know what to toggle.')
            end
        else
            windower.add_to_chat(123,'Error. The first word is not toggle.')
        end
    else
        windower.add_to_chat(123,'Error. Only 1 word command. Need 2 words')
    end
end


And now, reload your lua and when we type in the chat box //gs c toggle nukemode it will tell us what nuking mode we are currently in.

So that's all cool and stuff but how does this become useful to us? We will now make our nuking sets. And this is where the Modes come in. For reference we added these modes:
Code
NukingMode = M{'Normal', 'Acc', 'MagicBurst', MagicBurstAcc}


So now we'll create a set that matches each of those 4 nuking modes.
Code
sets.midcast.Normal= {}
sets.midcast.Acc= {}
sets.midcast.MagicBurst= {}
sets.midcast.MagicBurstAcc= {}


I'll let you add those in your lua and fill them with the proper gear. Now that we have those sets. It's time to use them!

Now if you remember in Class #3 we've made the midcast function handle our cure set, by matching the spell name.
Code
function midcast(spell)
	if spell.name:match('Cure') or spell.name:match('Cura') then
			equip(sets.midcast.cure)
	else
		-- do something for when I cast non cure spells
	end
end


We're now going to add in nuking support. Remember the elseif stuff! So we need to add a rule for black magic. Note: this following rule would include spells like Burn or Helixes but by now you should know how to make another elseif prior for those so that if you handle them before in another elseif then this rule will only have nuke spells left to go through.

Now the great thing about modes is that it will let us simplify a lot the logic here. Because we will not have to do a If-Then-Else rule to check if we should be in MagicBurst or Acc mode or anything like that. Because we can simply make the set we call use the mode value as a name. Remember in the user function we added to cycle the modes, we added tostring(Nuking.current). That NukingMode.current was giving us the name of the mode we're in. And because we matched our sets name to our mode names, we can simply use that again to make sure we get the correct set based on the mode we're in! So the new nuking rule becomes simply:
Code
function midcast(spell)
	if spell.name:match('Cure') or spell.name:match('Cura') then
		equip(sets.midcast.cure)
	elseif spell.type == 'BlackMagic' then
		equip(sets.midcast[NukingMode.current]) 	
	end
end


Basically, sets.midcast[NukingMode.current] is equivalent to sets.midcast.Acc if we're in 'Acc' mode.. or sets.midcast.Normal if we're in 'Normal' mode, etc.

So what would be:
Code
elseif spell.type == 'BlackMagic' then
	if NukingMode.current == 'Normal' then
		equip(sets.midcast.Normal) 
	elseif NukingMode.current == 'Acc' then
		equip(set[code]
s.midcast.Acc)
elseif NukingMode.current == 'MagicBurst' then
equip(sets.midcast.MagicBurst)
elseif NukingMode.current == 'MagicBurstAcc' then
equip(sets.midcast.MagicBurstAcc)
end
end[/code]

Simply becomes:
Code
elseif spell.type == 'BlackMagic' then
	equip(sets.midcast[NukingMode.current]) 
end


Lesson 14 - Spell Mappings

What's a mapping? Well imagine a map where there is a few countries and spells are people and we wanna know which country are they from etc.. Ok bad analogy. Basically, we want to be able to classify each spells and put them into a box, and later we'll be able to ask which box a spell belongs to. For now, we'll "keep it simple" and only use enfeebles. Technically you could add every damn spells and ability in the game, but we'll just keep the enfeebling subset as it's varied enough and can work in isolation so it'll do for our learning purpose.

For a RDM, it's generally agreed upon that we have 6 sets for enfeebling, depending on the spells we cast because they use different stats. We'll name these categories to the following: potency, mndpot, macc, intpot, skillmndpot, skillpot. As to why that is, you can look in the RDM guide thread.

So naturally, we'll make sets that match those so...
First an empty one for the enfeebling sub category
Code
sets.midcast.enfeebling = {}


Then those we're we'll put each gearsets in.
Code
sets.midcast.enfeebling.mndpot = {}
sets.midcast.enfeebling.intpot = {}
sets.midcast.enfeebling.potency = {}
sets.midcast.enfeebling.macc= {}
sets.midcast.enfeebling.skillmndpot= {}
sets.midcast.enfeebling.skillpot= {}



Now that the ground work is done, we'll need to create a table that contains all the actual enfeebling spells we want to use these sets with. This one needs to be added before the function that get_sets() function.

So in the above, we've basically put each spells in it's proper box. Next we need to add a function that will ask a spell what box it is in. So we'll create this new function. You can put this one at the very bottom of your lua.
Code
-- Get a spell mapping for the enfeeble.
function get_enfeeb_map(spell)
    return enfeeb_maps[spell.name]
end


This function will receive the spell from in input (seen in parenthesis) and then it will use spell.name (remember that from class #3) but instead of returning the name, it uses the name to find what box we put that spell into in our enfeeb_maps table.

It's time to get back to our midcast function.
Code
function midcast(spell)
	if spell.name:match('Cure') or spell.name:match('Cura') then
		equip(sets.midcast.cure)
	elseif spell.type == 'BlackMagic' then
		equip(sets.midcast[NukingMode.current]) 	
	end
end


Now first we'll add a local variable at the beginning of that function in order to check if our spells is found in enfeeb_maps and if it is then our variable enfeebType will now be set to the type of enfeeble we're casting. If it's not an enfeeble then that variable wil lbe undefined but also unused so no problem.
Code
function midcast(spell)
	local enfeebType = get_enfeeb_map(spell)

	if spell.name:match('Cure') or spell.name:match('Cura') then
		equip(sets.midcast.cure)
	elseif spell.type == 'BlackMagic' then
		equip(sets.midcast[NukingMode.current]) 	
	end
end


Then we'll add our enfeeble rule:
Code
function midcast(spell)
	local enfeebType = get_enfeeb_map(spell)

	if spell.name:match('Cure') or spell.name:match('Cura') then
		equip(sets.midcast.cure)
	elseif spell.skill == 'Enfeebling Magic' then
		equip(sets.midcast.enfeebling[enfeebType])
	elseif spell.type == 'BlackMagic' then
		equip(sets.midcast[NukingMode.current])	
	end
end


Note: We need to put enfeebles before the nuking rule because our nuking rule is *any* black magic and some enfeebles (like sleep or frazzle) are black magic so they need to be handled before.

And just like with modes, we can use the mappings to keep a simple logic in the midcast function but ensure the correct set is always put on depending on which spell we're casting.

In the above context, say we were casting Dia III then sets.midcast.enfeebling[enfeebMap] would be equal to sets.midcast.enfeebling.potency because when we cast Dia III, we run the function get_enfeeb_map(spell) sending the spell (Dia III) to the function, which then checks for "Dia III" using the spell.name and looks for that in the enfeeb_maps table. In there, "Dia II" is equal to "potency" so the function returns "potency" which is then stored into the variable enfeebType we set at the beginning of the function. And since we named our sets to the same names as the mappings, then when a mapping is found, then corresponding set is used.

Alrighty, that's all for class #6! Don't be scared to ask anything or more details in this thread for anything that's unclear. We're getting into some more complex stuff here!

Ciao~

Author: Elizabet
Date Created: 2020-02-18 12:59:19
Date Last Modified: 2020-07-11 21:38:14
Updates: 12
Bytes: 15062