5E Product Walkthrough Playlist
Page 1 of 2 12 Last
  1. #1

    Recorded for Posterity - The secret tales of data_library.lua

    Today I spent a horrible amount of time deciphering where Spells window was generated in the right side toolbar of FG.

    So I never forget, though I desperately want to, I shall for posterity record how those right toobar buttons are defined.

    The tale will be told through the eyes of the 5E ruleset...

    It all starts with CoreRPG\scripts\data_library.lua and its function overrideRecordTypeInfo.

    Any ruleset that wants to define the buttons along the right side of the applications to their own way of doing things must play with the aRecords structure format and rules. Not sure where or if these are documented - but I have only delved into them as far as I needed to go. In my case, that was how to get some new buttons in the main 5E Spells window showing the group/lists/etc. of spells in the DB. The aRecords structure defines how the code will define and display these buttons. And rulesets want to override this CoreRPG data by providing their own set of aRecords structure to override the default CorRPG data - via the function function overrideRecordTypeInfo.

    The 5E ruleset does this in 5E\scripts\data_library_5E.lua in the aRecordOverrides structure which it will pass onto the CoreRPG function overrideRecordTypeInfo to override the default data. Here is an example of one of the pieces of data - items - in that structure - I show it because it seems to be the most complex one...

    (all my examples are from TEST server FYI because that is where my extension work is happening)
    Code:
    	["item"] = { 
    		fIsIdentifiable = isItemIdentifiable,
    		aDataMap = { "item", "reference.equipmentdata", "reference.magicitemdata" }, 
    		fRecordDisplayClass = getItemRecordDisplayClass,
    		aRecordDisplayClasses = { "item", "reference_magicitem", "reference_armor", "reference_weapon", "reference_equipment", "reference_mountsandotheranimals", "reference_waterbornevehicles", "reference_vehicle" },
    		aGMListButtons = { "button_item_armor", "button_item_weapons", "button_item_templates", "button_forge_item" },
    		aPlayerListButtons = { "button_item_armor", "button_item_weapons" },
    		aCustomFilters = {
    			["Type"] = { sField = "type" },
    			["Rarity"] = { sField = "rarity", fGetValue = getItemRarityValue },
    			["Attunement?"] = { sField = "rarity", fGetValue = getItemAttunementValue },
    		},
    	},
    Looking at the actual Items window I have deduced that this data structure has magical mysterious means (and I choose to ignore finding out the how/why of them) to build out the standard DB data window appearing on the right toolbar in FGU. You can see where the aGMListButtons seems list out all the buttons that will be displayed (I'm guessing to GM only) which is what I'm interested in. You can deduce what the others do on your own.

    Now I'm interested in spells so I look to the current 5E definition in aRecordOverrides for that one...

    Code:
    	["spell"] = {
    		bExport = true, 
    		aDataMap = { "spell", "reference.spelldata" }, 
    		sRecordDisplayClass = "power", 
    		aCustomFilters = {
    			["Source"] = { sField = "source", fGetValue = getSpellSourceValue },
    			["Level"] = { sField = "level", sType = "number" },
    			["Ritual"] = { sField = "ritual", sType = "boolean" },
    		},
    	},
    Now my plan is to add my new buttons by adding into this data structure...

    Code:
    aGMListButtons = { "button_thing_ONE", "button_thing_TWO" },
    Where I look to the item buttons like this one to define those buttons...

    5E\campaign\template_campaign.xml
    Code:
    	<template name="button_item_armor">
    		<button_text_sm>
    			<anchored to="buttonanchor" width="80">
    				<top />
    				<left anchor="right" relation="relative" offset="5" />
    			</anchored>
    			<state textres="item_button_armor" />
    			<script>
    				function onButtonPress()
    					local w = Interface.findWindow("reference_groupedlist", "reference.armor");
    					if w then
    						Interface.toggleWindow("reference_groupedlist", "reference.armor");
    					else
    						w = Interface.openWindow("reference_groupedlist", "reference.armor");
    						w.init({ sRecordType = "item", sListView = "armor" });
    					end
    				end
    			</script>
    		</button_text_sm>
    	</template>
    While I will keep the anchor point rules and other things I'll naturally have my own definitions in here to do what I want todo.

    Now, I don't know if this is going to work - but I felt that there was high probability that when I take a break shortly - my brain will reflexively BLOT OUT this entire data tracking adventure via keyword searches in notepad++ as a defensive attempt to preserve my sanity. So I'm recording it down here for posterity - so I won't lose the memory of how to add DB related buttons and how they operate in the right hand side of the FGU application.

    Now, if you will pardon me, I'm going to go purge my mind.
    Last edited by SilentRuin; February 23rd, 2021 at 19:44.
    Free(Forums/Forge) Extension(FGU 5E):
    Paid (Forge) Extension(FGU 5E):

  2. #2
    Update - adding the template buttons I created worked. Except for some reason I have to press the button twice before it will actually trigger the onButtonPress() logic - which mystifies me.
    Free(Forums/Forge) Extension(FGU 5E):
    Paid (Forge) Extension(FGU 5E):

  3. #3
    Superteddy57 solved it for me in discord - THANK YOU. I had put the new functions inside an older version of the function. Who knew lua could call a function in a function? Anyway I never would have spotted that as I would have thought the parser would drop dead on a mistake like that not just carry on requiring me to click it twice. Just goes to show my stupid is boundless. I miss IDE's
    Free(Forums/Forge) Extension(FGU 5E):
    Paid (Forge) Extension(FGU 5E):

  4. #4
    Minty23185Fresh's Avatar
    Join Date
    Dec 2015
    Location
    Goldstone, CA, USA
    Posts
    1,211
    Blog Entries
    29
    I did this work a few years ago, for my Field Filters extension.
    I wish I had known, I might have been able to help.
    Maybe I should pay a little more attention to the forums.

    (The guy says, sliding into home plate, after the game is over.)

    Now that you're an expert, it's all pretty slick, isn't it?

  5. #5
    Quote Originally Posted by Minty23185Fresh View Post
    I did this work a few years ago, for my Field Filters extension.
    I wish I had known, I might have been able to help.
    Maybe I should pay a little more attention to the forums.

    (The guy says, sliding into home plate, after the game is over.)

    Now that you're an expert, it's all pretty slick, isn't it?
    As most things - super easy and slick to do some things you want - and near impossible to do other things you want As it turns out it worked fine for what I wanted, and I shared to hopefully provide some light on this for anyone else who attempts something like it. If you have more info on this area feel free to add it! I only really told where to start looking and described one of the data structure fields (all I needed to learn about to do my task)
    Free(Forums/Forge) Extension(FGU 5E):
    Paid (Forge) Extension(FGU 5E):

  6. #6
    Varsuuk's Avatar
    Join Date
    Dec 2015
    Location
    New York
    Posts
    2,075
    This is of course extremely useful from CoreRPG:

    Code:
    -- RECORD TYPE FORMAT
    -- 		["recordtype"] = { 
    -- 			aDataMap = <table of strings>, (required)
    -- 			aDisplayIcon = <table of 2 strings>, (required)
    --
    --			bExport = <bool>, (optional)
    --			nExport = <number>, (optional; overriden by bExport)
    --			bExportNoReadOnly = <bool>, (optional; overrides bExport)
    --			sExportPath = <string>, (optional)
    --			bExportListSkip = <bool>, (optional)
    --			sExportListDisplayClass = <string>, (optional)
    --
    -- 			bHidden = <bool>, (optional)
    -- 			bID = <bool>, (optional)
    --			bNoCategories = <bool>, (optional)
    --
    -- 			sListDisplayClass = <string>, (optional)
    -- 			sRecordDisplayClass = <string>, (optional)
    --			aRecordDisplayCLasses = <table of strings>, (optional; overrides sRecordDisplayClass)
    --			fRecordDisplayClass = <function>, (optional; overrides sRecordDisplayClass)
    --			fGetLink = <function>, (optional)
    --
    --			aGMListButtons = <table of template names>, (optional)
    --			aPlayerListButtons = <table of template names>, (optional)
    --
    --			aCustomFilters = <table of custom filter table records>, (optional)
    -- 		},
    --
    
    -- FIELDS ADDED FROM STRING DATA
    -- 		sDisplayText = Interface.getString(library_recordtype_label_ .. sRecordType)
    --		sSingleDisplayText = Interface.getString(library_recordtype_single_ .. sRecordType)
    -- 		sEmptyNameText = Interface.getString(library_recordtype_empty_ .. sRecordType)
    --		sExportDisplayText = Interface.getString(library_recordtype_export_ .. sRecordType)
    -- FIELDS ADDED FROM STRING DATA (only when bID set)
    -- 		sEmptyUnidentifiedNameText = Interface.getString(library_recordtype_empty_nonid_ .. sRecordType)
    --
    
    -- RECORD TYPE LEGEND
    --		aDataMap = Required. Table of strings. defining the valid data paths for records of this type
    --			NOTE: For bExport/nExport, that number of data paths from the beginning of the data map list will be used as the source for exporting 
    --				and the target data paths will be the same in the module. (i.e. default campaign data paths, editable).
    --				The next nExport data paths in the data map list will be used as the export target data paths for read-only data paths for the 
    --				matching source data path.
    --			EX: { "item", "armor", "weapon", "reference.items", "reference.armors", "reference.weapons" } with a nExport of 3 would mean that
    --				the "item", "armor" and "weapon" data paths would be exported to the matching "item", "armor" and "weapon" data paths in the module by default.
    --				If the reference data path option is selected, then "item", "armor" and "weapon" data paths would be exported to 
    --				"reference.items", "reference.armors", and "reference.weapons", respectively.
    --		aDisplayIcon = Required. Table of strings. Provides icon resource names for sidebar/library buttons for this record type (normal and pressed icon resources)
    --
    --		bExport = Optional. Same as nExport = 1. Boolean indicating whether record should be exportable in the library export window for the record type.
    --		nExport = Optional. Overriden by bExport. Number indicating number of data paths which are exportable in the library export window for the record type.
    --			NOTE: See aDataMap for bExport/nExport are handled for target campaign data paths vs. reference data paths (editable vs. read-only)
    --		bExportNoReadOnly = Optional. Similar to bExport. Boolean indicating whether record should be exportable in the library export window for the record type, but read only option in export is ignored.
    --		sExportPath = Optional. When exporting records to a module, use this alternate data path when storing into a module, instead of the base data path for this record.
    --		sExportListDisplayClass = Optional. When exporting records, the list link created for records to be accessed from the library will use this display class. (Default is reference_list)
    --		bExportListSkip = Optional. When exporting records, a list link is normally created for the records to be accessed from the library. This option skips creation of the list and link.
    --
    --		bHidden = Optional. Boolean indicating whether record should be displayed in library, and sidebar options.
    -- 		bID = Optional. Boolean indicating whether record is identifiable or not (currently only items and images)
    --		bNoCategories = Optional. Disable display and usage of category information.
    --		sEditMode = Optional. Valid values are "play" or "none".  If "play" specified, then both players and GMs can add/remove records of this record type. Note, players can only remove records they have created. If "none" specified, then neither player nor GM can add/remove records. If not specified, then only GM can add/remove records.
    --			NOTE: The character selection dialog handles this in the custom character selection window class historically, so does not use this option.
    --
    --		sListDisplayClass = Optional. String. Class to use when displaying this record in a list. If not defined, a default class will be used.
    --		sRecordDisplayClass = Optional. String. Class to use when displaying this record in detail. (Defaults to record type key string) 
    --		aRecordDisplayClasses = Optional. Table of strings. List of valid display classes for records of this type. Use fRecordDisplayClass to specify which one to use for a given path.
    --		fRecordDisplayClass = Optional. Function. Function called when requesting to display this record in detail.
    --		fGetLink = Optional. Function. Function called to determine window class and data path to use when pressing or dragging sidebar button.
    --
    --		aGMListButtons = Optional. Table of template names. A list of control templates created and added to the master list window for this record type in GM mode.
    --		aPlayerListButtons = Optional. Table of template names. A list of control templates created and added to the master list window for this record type in Player mode.
    --
    --		aCustomFilters = Optional. Table of custom filter table records.  Key = Label string to display for filter; 
    --			Filter table record format is:
    --				sField = Required. String. Child data node that contains data to use to build filter value list; and to apply filter to.
    --				fGetValue = Optional. Function. Returns string or table of strings containing filter value(s) for the record passed as parameter to the function.
    --				sType = Optional. String. Valid values are: "boolean", "number".  
    --					NOTE: If no type specified, string is assumed. If empty string value returned, then the string resource (library_recordtype_filter_empty) will be used for display if available.
    --					NOTE 2: For boolean type, these string resources will be used for labels (library_recordtype_filter_yes, library_recordtype_filter_no).
    --
    --		sDisplayText = Required. String Resource. Text displayed in library and tooltips to identify record type textually.
    --		sEmptyNameText = Optional. String Resource. Text displayed in name field of record list and detail classes, when name is empty.
    --		sEmptyUnidentifiedNameText = Optional. String Resource. Text displayed in nonid_name field of record list and detail classes, when nonid_name is empty. Only used if bID flag set.
    --

    I was GOING to post a simple example of adding class/race controls since those were two easy ones added in my extension for people to compare against:

    Code:
    function onInit()
    
    aRecords = {
    	["pcclass"] = { 
    		bExport = true,
    		sEditMode = "play",
    		aDataMap = { "pcclass", "reference.pcclass" }, 
    		aDisplayIcon = { "button_pcclass", "button_pcclass_down" },
    		sRecordDisplayClass = "pcclass", 
    		},
    	["pcrace"] = { 
    		bExport = true,
    		sEditMode = "play",
    		aDataMap = { "pcrace", "reference.pcrace" }, 
    		aDisplayIcon = { "button_pcrace", "button_pcrace_down" },
    		sRecordDisplayClass = "pcrace", 
    		},
    ...
    }

    NOTE: I have not updated my FG in quite a couple months since when I work slowly, it is a pain to download and git commit all the FG changes to look at etc. I prefer to do it every "epoch" in FG or my work... cos I move at geological speed

  7. #7
    That was excellent information! Thank you very much!
    Free(Forums/Forge) Extension(FGU 5E):
    Paid (Forge) Extension(FGU 5E):

  8. #8
    Thanks to Superteddy57 for showing me how to handle this...

    Update on an additional problem I had (corrected more semicolons in first post) as I had to override spells which has a function in it its data structure as shown in red below:

    Code:
    aRecordOverrides = {
         ["spell"] = {
    		bExport = true, 
    		aDataMap = { "spell", "reference.spelldata" }, 
    		sRecordDisplayClass = "power", 
    		aGMListButtons = { "button_Stuff_1", "button_Stuff_2" },
    		aCustomFilters = {
    			["Source"] = { sField = "source", fGetValue = getSpellSourceValue },
    			["Level"] = { sField = "level", sType = "number" },
    			["Ritual"] = { sField = "ritual", sType = "boolean" },
    		},
    	},
    };
    Feeding this structure to

    Code:
    			for kRecordType,vRecordType in pairs(aRecordOverrides) do
    				LibraryData.overrideRecordTypeInfo(kRecordType, vRecordType);
    			end
    Will not give errors - but also will not work for the filters and will mess up your source filter searches (in this example). You need to provide your own copies of this function in order for the filters to work as it originally did. But this example applies to any function data you are overriding. In my case this function required me to provide two local copies of the original LibraryData5E code.

    Code:
    function getSpellSourceValue(vNode)
    	return StringManager.split(DB.getValue(vNode, "source", ""), ",", true);
    end
    
    function getSpellLevelValue(vNode)
    	local v = DB.getValue(vNode, "level", 0);
    	if (v >= 1) and (v <= 9) then
    		v = tostring(v);
    	else
    		v = Interface.getString("library_recordtype_value_spell_level_0");
    	end
    	return v;
    end
    Don't ask me why the getSpellLevelValue as I did not look into the full reason why - it was just the solution Superteddy57 provided and after so many failed attempts by me I was just happy to have it working
    Last edited by SilentRuin; February 23rd, 2021 at 20:04.
    Free(Forums/Forge) Extension(FGU 5E):
    Paid (Forge) Extension(FGU 5E):

  9. #9
    Xarxus's Avatar
    Join Date
    Mar 2019
    Location
    Rome (Italy)
    Posts
    244
    Okay, I'm also studying the sidebar buttons and I have many doubts, I'm not understanding them.

    I created a data_library_personalized.lua file with the following code.
    Code:
    aRecordOverrides = {
    	["vocation"] = {
    		bExport = true, 
    		aDataMap = { "vocation", "reference.vocationdata" }, 
    		sRecordDisplayClass = "reference_vocation", 
    		sSidebarCategory = "create",
    	},
    };
    
    function onInit()
    	LibraryData.overrideRecordTypes(aRecordOverrides);
    end
    The button I get is devoid of writing. First problem, how do I add it and how do I change the icon?




    My second problem is that pressing the button opens a generic window. I don't know what it is, I don't know how to implement it, and I don't even know how to make a title appear on it.

    Attached Images Attached Images
    FGU ULTIMATE License
    Click here for RPG Music or here for Dwarves songs on Spotify

  10. #10
    Without doing the work myself I have no idea. Basically, I find out stuff by trial and error and looking in the 5E and CoreRPG code for how things were done there. All the sidebar stuff has been redone also so I'm not an expert on any of that (This was all written before those changes). Look at what their code does and experiment around with it. Note, my examples were changing existing things not creating a new one. Obviously the new one would need to be reference by something in the broader code also.

    Maybe someone else is familiar with what it takes to create a fully functional new entry - I am not. My only advice is to search the rulesets that use one of the current ones to see EVERYWHERE they are used.
    Free(Forums/Forge) Extension(FGU 5E):
    Paid (Forge) Extension(FGU 5E):

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
DICE PACKS BUNDLE

Log in

Log in