A Neophyte Tackles the FG Extension - Lua Behind the Visibility Options
by
, July 9th, 2016 at 05:34 (9088 Views)
In a previous post I detailed how I added additional effect visibility button options to the Combat Tracker‘s GUI. This time I’ll step through the process of adding the Lua to make those button options functional.
Detour: This is post 18 in this series. I hope that by now, as a neo-developer, you have the confidence to do many things without the detailed instruction I have provided in the past. I’ll explain concept and intent but I won’t detail some of the more mundane code.
Setup: I started with a fresh, unzipped copy of the 5E ruleset. I created a new campaign. In the host I imported a couple PCs: Krystryd and a Paladin (in that order). I joined the game as Client1 and selected the Paladin as the client’s PC. I joined the game again as Client2 and picked Krystryd. I opened the Combat Tracker in the GM’s instance and added actors in the following order: an Orc, Krystryd, a Kobold, Paladin, and a Goblin. I made all actors visible. I opened the Effects Manager (EM) then used it or the PC’s Actions to apply the following effects: the Orc frightened (EM) the Paladin, Krystryd poisoned (EM) the Goblin and paralyzed (EM) the Orc, the Paladin applied (client Action) Aura of Courage to Krystryd and Aura of Warding to self. Finally I set the effect visibility options as shown here:
Attachment 14646
Note the following in the Combat Trackers: (1) The GM’s (left panel) displays all effects as desired. (2) The Goblin’s poisoned effect, does not show up in either the Paladin’s (upper right) nor Krystryd’s (lower right). (3) All other effects show up in both clients.
There are four button options, two of which behave appropriately, but I’ll have to address the Self option and the Target option in the client’s Combat Tracker.
Recall from previous work, the ct_host.xml file in the 5E\ct subfolder. There is also a ct_client.xml file. I started there. There are two windowclasses, one for the whole Combat Tracker and one for each list entry. I perused the <sheetdata> of the list entry, it was easy to reconcile the one-to-one correspondence to what is seen on screen. “string_ctentry_effects” must be the control that displays the effects. I searched for a template and found it in the Core’s template_ct.xml file. Which led me to the ct_effect_summary.lua file also in the Core. One of its functions points to the familiar EffectManager.getEffectsString(). Recall that I used a “forwarding” function for it. I put Debug statements in getEffectsString() in both the 5E and the extension manager_effect.lua files. I closed all instances of FG and restarted the host. Sure enough getEffectsString() in the 5E ruleset was called multiple times, but surprisingly, the one in the extension was never called.
A dilemma: should I chase down why the extension code is not being utilized, or work on modifying when the effects are displayed? The latter is the point of the current exercise, so I’ll do the development in the ruleset for now then move it to the extension and deal with the other issue later. After all, I might not even be in the right code yet!
Now to cursorily ascertain what the getEffectsString() function does: The first five or so lines create a “table” of nodes from the DB and sort it. Each table item is then operated on in a for loop that takes up the rest of the function. Within the for loop, some variables are set that are used in decision making further along. Then depending on the value of the variable bAddEffect, some string construction occurs.
I tried to change the display of the effects strings by changing the value of bAddEffect. I forced it to be false, just before it was tested (~line102, FG v3.1.7):
I saved, restarted, brought up the host and clients; the effects strings were gone! But in the host, when I clicked the Effects button, I saw the effects were still present, the summary strings just weren’t displayed.Code:bAddEffect = false; if bAddEffect then
I was in the right place! (I removed my negation statement.)
The following code took hours to develop. I constantly referred to the DB page of the XML and Scripting Reference. Numerous times I viewed the campaign‘s db.xml file and inserted Debug statements to confirm variable values.
A word about getEffectsString(): This function is called for every running instance of FG, and for every actor in the Combat Tracker. I had a console up for each instance, I observed the function being called 5 times in each instance, processing each of the 5 effects.
The decision tree: (1) Depending on certain criteria, e.g. what FG instance is being operated on, what the visibility button’s value is, etc., the code will need to force bAddEffect’s value to be false. (2) The effects strings in the host instance are always displayed, so further decisions need only be processed if the current instance is not the host. (3) Visibility button values of 0 (All), or 1 (GM), do not need to be processed. Only values of 2 (Self) or 3 (Target) need to be addressed. This leads to a base decision tree of:
The decision process for Self: Only display the effects string if the current instance is the same as the actor possessing the effect. Within the DB, the effect has a property that lists the character sheet id (i.e. which PC is afflicted). The decision specific to case 2 becomes:Code:if User.isHost() == false then local nVisibility = DB.getValue(v, "isgmonly", 0); if nVisibility == 2 then elseif nVisibility == 3 then end end
The decision process for Target: Display the effects string as above for Self or display it if the inflictor of the effect is the same as the instance. Again within the DB, there is a property that lists the source or inflictor. Note that I’ve stated when to display the string, not when not to. Fortunately there is a “math” that will help me write the code.Code:if nVisibility == 2 then local sUser = User.getCurrentIdentity(); local sCharSheetID = DB.getValue(DB.getValue(nodeCTEntry, "link")); if sCharSheetID ~= "charsheet." .. sUser then bAddEffect = false; end else …
Detour: Boolean Algebra is the “math” of true and false. For example, if one ORs something true with something false the result is always true. If one ANDs something true with something false the result is always false. What I have above is, if Self is true OR inflictor is true then display is true. A Boolean Algebra axiom states that if one negates each of the arguments and changes the operation (e.g. OR becomes AND) then the result is negated. In my case, if Self is false AND inflictor is false then don’t display.
Note that within the whole tree, there are duplicated statements for sUser and sCharSheetID, so I refactored the code. I also split the if statement, containing the and operation, into two statements and nested the nodeCTSource and sInflictorID evaluations between them. These variable resolutions don’t need to be done if the first part of the if statement fails. (This blog post is running long so I am not displaying the code here. I’ll put the refactored code in a comment attached to the blog.)Code:elseif nVisibility == 3 then local sUser = User.getCurrentIdentity(); local sCharSheetID = DB.getValue(DB.getValue(nodeCTEntry, "link")); local nodeCTSource = DB.findNode(sSource); local sInflictorID = DB.getValue(DB.getValue(nodeCTSource, "link")); if (sCharSheetID ~= "charsheet." .. sUser) and (sInflictorID ~= "charsheet." .. sUser) then bAddEffect = false; end end end
Bad News & Good News: As mentioned earlier in this post the getEffectsString() function in the extension was not being called. I tried to determine why and how to fix it. I copied quite a bit of code into the extension from both the Core and 5E rulesets without success. It appears as though it is some sort of interplay as code is loaded, parsed and executed from the various sources. As a last resort I posted a question in the FG Forums. To say the answer by Moon Wizard was inspiring, does not do him justice.
I employed the methodology brought to light in the forum thread. I discarded the manager_effect.lua and manager_effect_maker.lua files and replaced them with a manager_effect_boss.lua file. I copied the two functions, getEffectsString() and addEffect() from the 5E manager_effect.lua file to my “boss” file, renamed them, and added my functionality into them. Here is what the “boss” file contains (abbreviated):
Obviously the <base> in my extension.xml file changed too:Code:function onInit() -- supplant the getEffectsString() functionality EffectManager.getEffectsString = Ext_getEffectsString; -- supplant the addEffect() functionality EffectManager.addEffect = Ext_addEffect; end function Ext_getEffectsString(nodeCTEntry, bPublicOnly) -- override the functionality of the EffectManager's -- getEffectsString() function with that of this function . . . . . end function Ext_addEffect(sUser, sIdentity, nodeCT, rNewEffect, bShowMsg, sEffectTargetNode) -- override the functionality of the EffectManager's -- addEffect() function with that of this function . . . . . end
That’s it! Done. Note the effects in this screenshot:Code:<base> <includefile source="strings/strings_5e.xml" /> <includefile source="ct/ct_host.xml" /> <script name="SuperviseEffectManager" file="scripts/manager_effect_boss.lua" /> </base>
Attachment 14647
(PLEASE VIEW COMMENTS BELOW.)
Until next time keep on role playing.