PDA

View Full Version : Is this possible with an extension?



JimSocks
September 12th, 2020, 02:35
Can an extension add this functionality to story templates? I am just wondering if it is even theoretically possible?

Basically, I want to roll once on a main table, and then reference the previously rolled columns individually a-la-carte afterwards.

39285

Willot
September 12th, 2020, 04:42
Not sure what your trying to achieve? But I think it could be done.

Remember You can nest tables too, if that would help??

JimSocks
September 12th, 2020, 04:53
Nope- nesting tables doesn't work for recalling usable data within a story template.

I want to call a table with several columns, then individually use the results of that initial table roll's individual column results later. The implications of being able to achieve this would blow the roof off my story templates! In the attached picture from the first post, you can see the table, the story template, and the generated result. I am wondering if an extension COULD be made which makes the <Star Type|2> reference work as I want it to in this example picture. Namely, if it worked the way I am looking for then the generated story would replace that reference with "AIII" in this instance, since that was the result from column 2 when the entire table was previously rolled.

LordEntrails
September 15th, 2020, 05:46
Sure, but you would have to re-write the table functionality. Paramaterize everything, and write the LUA so that all those parameters can be stored. And determine how long they should be stored for.

JimSocks
September 15th, 2020, 15:15
I figured it had more to do with the output of the tables themselves than the story templates (though I assume story template code would have to be altered some too, in order to parse the altered output of the tables). I am thinking the values shouldn't be stored past the generation of the result from the template? Once the story is successfully generated, the storage locations get cleaned out? I can't imagine needing them past that point, and that could free up the space for the next table's data. Also, only table output when requested from story template needs to be altered, all others can stay the same.

WAIT! Normally, when a table with several columns outputs to a story, the result ends up with semi-colons between column data! Maybe it's possible to keep table output as-is, but have the story template store the output into "columns" using the semicolons as a de-limiter!? This would mean using semicolons in your text would be off limits, but that's a teeny price to pay. So, here's the idea (tell me if I am crazy!):

Within the story template, you type: [Test Table] The Test Table has two columns that say: Column one: ONE Column two: TWO. Normally, when generated this would output in the resulting story entry as: ONE; TWO. So, we alter the story template to take the results of a given table's output, in this case [Test Table], and look for semicolons in the output. If it finds it, it stores each in a location that can be called with <Test Table|1> and <Test Table|2>. In this case, thereafter calling <Test Table|2> would output only: TWO. The storage solution for this could be very similar to how the data for previously rolled tables is already handled.

LordEntrails
September 15th, 2020, 15:50
You're crazy.
But your idea is sane :)
Still would require an extension though!

esmdev
September 15th, 2020, 16:19
Unless you expect to need to regularly change the table data you might do better to just build an extension with the options you want rather than monkey with the table system.

JimSocks
September 16th, 2020, 20:03
Anyone here know where I should start looking in the FG code for the Story Template sections?

Sadly, the only thing I only ever learned to code poorly was C++, so looking at the lua code confounds me. I think this is something I would have to look into commissioning someone to do on my behalf... lua looks so confusing to me :(

JimSocks
September 23rd, 2020, 04:41
OK so... I enrolled in a lua class for this.

I am already picking it up quickly enough to cause trouble, but I am also only just getting started. I have located the lua code that handles the tables in FG, but now I need to find the lua that handles the story templates and I am coming up empty handed.

Can anyone point me to the right filename? EDIT: After looking for ages, I Found it right after posting this...

ALSO OF NOTE HOWEVER... I found this little ditty in the FG code, which might be useful to gaze at while I figure out how to capture the incoming data from a table headed towards the story template output, and then de-limit it based on the semicolons to be stored as separate column data for later retrieval:



-- SPLIT CLAUSES
--
-- The source string is divided into substrings as defined by the delimiters parameter.
-- Each resulting string is stored in a table along with the start and end position of
-- the result string within the original string. The result tables are combined into
-- a table which is then returned.
--
-- NOTE: Set trimspace flag to trim any spaces that trail delimiters before next result
-- string
--

function split(sToSplit, sDelimiters, bTrimSpace)
if not sToSplit then
return {}, {};
end

-- SETUP
local aStrings = {};
local aStringStats = {};

-- BUILD DELIMITER PATTERN
local sDelimiterPattern = "[" .. sDelimiters .. "]+";
if bTrimSpace then
sDelimiterPattern = sDelimiterPattern .. "%s*";
end

-- DEAL WITH LEADING/TRAILING SPACES
local nStringStart = 1;
local nStringEnd = #sToSplit;
if bTrimSpace then
_, nStringStart, nStringEnd = trim(sToSplit);
end

-- SPLIT THE STRING, BASED ON THE DELIMITERS
local sNextString = "";
local nIndex = nStringStart;
local nDelimiterStart, nDelimiterEnd = sToSplit:find(sDelimiterPattern, nIndex);
while nDelimiterStart do
sNextString = sToSplit:sub(nIndex, nDelimiterStart - 1);
if sNextString ~= "" then
table.insert(aStrings, sNextString);
table.insert(aStringStats, {startpos = nIndex, endpos = nDelimiterStart});
end

nIndex = nDelimiterEnd + 1;
nDelimiterStart, nDelimiterEnd = sToSplit:find(sDelimiterPattern, nIndex);
end
sNextString = sToSplit:sub(nIndex, nStringEnd);
if sNextString ~= "" then
table.insert(aStrings, sNextString);
table.insert(aStringStats, {startpos = nIndex, endpos = nStringEnd + 1});
end

-- RESULTS
return aStrings, aStringStats;
end


Hmmm... I also just found this inside the story template code:


sLocalReplace = table.concat(aOutputResults, "; ");

This seems to indicate that the story templates themselves are adding the column delimiters, and that kind of seems like the place to be fiiddling (but I could be wrong!). I would THINK that if the story template already has the data in column chunks, and then stitches it together with this line of code as glue, then this area of the code might be a good intercept point to store those column chunks into addressable locations BEFORE they get glued together. Also I might remove the stupid semicolon- it's distracting in a story template output anyways!

JimSocks
October 7th, 2020, 04:50
Ok so I am making some headway, and I have a plan. There are some mysteries yet to figure out, like on the line


for nStartTag, sTag, nEndTag in sOutput:gmatch("())%[([^%]]+%]()")

I am having a Hell of a time figuring out the pattern being used with "())%[([^%]]+%]()" I get that these are pattern codes used to search the strings, but... what the hell do they actually mean all together like that? The lua reference site I have been using wasn't much help in figuring this out- any of you know?

Also, where can I find the class defininitions for the "DB" class called here with the getValue tacked onto it:


sText = performTableLookups(DB.getValue(node, "text", ""));

I'd like to take a look at what is going on under the hood of that function, but I can't find it anywhere. Hints?

For the record, here is the plan right now: have the story template search for tables with a special character set FIRST, and resolve those tables storing any results into arrays. Next, look the template over for reference links of those tables, using the same special character sets. When a "special character set table" is referenced, insert the data stored in it's array for that location.

Practical use: A story template has a Table to roll on, but it has a double asterisk at it's beginning, and later there are references to it using the same double asterisk:


[**NPC Table]

<**NPC Table|4> <**NPC Table|1> <**NPC Table|6> <**NPC Table|4>



Here, the story template would first roll on the NPC Table, storing it's column results into an array. Next, the references to that table ACTUALLY invoke the stored strings in the array. Now, if I can pull this off it makes story templates WAY cooler. It means we can have storys make SENSE with pronouns and a million other uses. It ALSO means that since I intend to resolve these FIRST, that when the story template resumes business as usual it won't know that what it's reading MAY have been rolled results already, and that is a very cool thing with other implications. It could essentially make the following possible:


[**TableA] [**TableB]
[<**TableA|1><**TableB|1>]

In this example, whatever the first columns of TableA and TableB retrieve as results will end up within table brackets THEMSELVES, which would then be evaluated during the normal story template evaluation code. Very cool. I hope I can get it to work. I'll probably need you awesome folks as I bumble through it...

JimSocks
October 12th, 2020, 05:19
Getting farther down the rabbit hole... but man am I just so perplexed. If ANYONE here is a veteran Lua whiz, I could really use some big brain power. Moonwizard, you out there? *lights up the BatSignal*

Here is a visual representation of what I am trying to do:
40146

And here is the code so far (it is also attached in a zip file):



local aLiteralReplacements = {};
local aLiteralReplacements2 = {};
local aTableNameTable = {}
local aColumnTable = {}
local StoredTableName = ""
local incrementor = 0 --I might not use this


function onButtonPress() --This runs when you press "Generate Story" in the Story Template
local node = window.getDatabaseNode();

aLiteralReplacements2 = {}

sText2 = performTableLookups2(DB.getValue(node, "text", "")); -- parse the text in the Story Template, find tables, roll on them, and store the results in an array
sText2 = performStoredReplacements2(sText2); -- retrieve Reference Link data from the array

nodeTarget = DB.createChild("encounter"); -- make a new window
DB.setValue(nodeTarget, "text", "formattedtext", sText2); -- put all our new goodies into the new window

Interface.openWindow("encounter", nodeTarget); -- open the new window for the user to see

end


-- Look for table roll expressions
function performTableLookups2(sOriginal)
-- store the original value in a variable. We will replace matches as we go and search for the next matching
-- expression.
local sOutput = sOriginal;


-- Resolve table expressions
local nMult = 1;
local aLookupResults = {};
for nStartTag, sTag, nEndTag in sOutput:gmatch("()[%{]([^%}]+)%}()") do
local sMult = sTag:match("^(%d+)x$");
StoredTableName = sTag; -- added this to save the table name up top and return into the function below
if sMult then
nMult = math.max(tonumber(sMult), 1);
table.insert(aLookupResults, { nStart = nStartTag, nEnd = nEndTag, vResult = "" });
else
local sTable = sTag;
local nCol = 0;

local sColumn = sTable:match("|(%d+)$");
if sColumn then
sTable = sTable:sub(1, -(#sColumn + 2));
nCol = tonumber(sColumn) or 0;
end
local nodeTable = TableManager.findTable(sTable);

local aMultLookupResults = {};
local aMultLookupLinks = {};
for nCount = 1,nMult do
local sLocalReplace = "";
local aLocalLinks = {};
if nodeTable then
bContinue = true;

local aDice, nMod = TableManager.getTableDice(nodeTable);
local nRollResults = StringManager.evalDice(aDice, nMod);
local aTableResults = TableManager.getResults(nodeTable, nRollResults, nCol);

local aOutputResults = {};
for _,v in ipairs(aTableResults) do
if (v.sClass or "") ~= "" then
if v.sClass == "table" then
local sTableName = DB.getValue(DB.getPath(v.sRecord, "name"), "");
if sTableName ~= "" then
globalTableName = sTableName
sTableName = "[" .. sTableName .. "]";
local sMultTag, nEndMultTag = v.sText:match("%[(%d+x)%]()");
if nEndMultTag then
v.sText = v.sText:sub(1, nEndMultTag - 1) .. sTableName .. " " .. v.sText:sub(nEndMultTag);
else
v.sText = sTableName .. " " .. v.sText;
end
end
table.insert(aOutputResults, v.sText);
else
table.insert(aLocalLinks, { sClass = v.sClass, sRecord = v.sRecord, sText = v.sText });
end
else
table.insert(aOutputResults, v.sText);
end
end

sLocalReplace = table.concat(aOutputResults, " ");

-- BELOW HERE is where I am hitting a mental roadblock... HOW do I get each column of the table results to go into their own array location,
--AND THEN how to I ensure I can accurately retrieve that data later? I DON'T KNOW....
local sTableName = globalTableName;
incrementor = incrementor + 1;
table.insert(aTableNameTable, { incrementor = sTableName });
table.insert(aColumnTable, { StoredTableName = aOutputResults }); -- added storedtablename as the KEY, with the other as the value


else
sLocalReplace = sTag;
end

-- Recurse to address any new math/table lookups
sLocalReplace = performTableLookups2(sLocalReplace);

table.insert(aMultLookupResults, sLocalReplace);
for _,vLink in ipairs(aLocalLinks) do
table.insert(aMultLookupLinks, vLink);
end
end

local sReplace = table.concat(aMultLookupResults, " ");
if aLiteralReplacements2[sTable] then
table.insert(aLiteralReplacements2[sTable], sReplace);
else
aLiteralReplacements2[sTable] = { sReplace };
end

for _,vLink in ipairs(aMultLookupLinks) do
sReplace = sReplace .. "||" .. vLink.sClass .. "|" .. vLink.sRecord .. "|" .. vLink.sText .. "||";
end

table.insert(aLookupResults, { nStart = nStartTag, nEnd = nEndTag, vResult = sReplace });
nMult = 1;
end

end
for i = #aLookupResults,1,-1 do
sOutput = sOutput:sub(1, aLookupResults[i].nStart - 1) .. aLookupResults[i].vResult .. sOutput:sub(aLookupResults[i].nEnd);
end

return sOutput;
end


function performStoredReplacements2(sOriginal) --AND this function is missing something VERY important,
-- which I can't figure out how to accomplish. How do I get the Reference Link to pull specific column
-- data via the use of the pipe "|" symbol followed by the column number? EX: on a reference link of
-- #Table A|4# I want only the data from column 4 of the previously rolled {Table A}. How do I do this!?
local sOutput = sOriginal;
local TableName = StoredTableName;
local nCol = 0

local isFGU = UtilityManager.isClientFGU();

for incrementor,v in pairs(aTableNameTable) do
local sLiteral = "%#" .. TableName:gsub("([%-%+%.%?%*%(%)%[%]%^%$%%])", "%%%1") .."%#";
local sColumn = sLiteral:match("|(%d+)$");
if sColumn then
TableName = TableName:sub(1, -(#sColumn + 2));
local sLiteral = "%#" .. TableName:gsub("([%-%+%.%?%*%(%)%[%]%^%$%%])", "%%%1") .."%#";
nCol = tonumber(sColumn) or 0;
else
nCol = 0;
end

for nCol,o in pairs(aColumnTable) do

sOutput = sOutput:gsub(sLiteral, table.concat(o, " "));
end

end
return sOutput;
end

JimSocks
October 12th, 2020, 06:19
Now here's the long version of my plan, in case anyone does have the ability to help and would like more data:

Within the software, we have a feature called Tables (where you can roll virtual dice and receive an outcome. These tables can have multiple columns forming their output.), and another feature called Story Templates (where you can write a story, and embed “callouts” to Tables.) When a “Generate Story” button is pressed within the Story Template, all the Table “callouts” you typed are replaced by the retrieved results from the Tables you were asking for. In practice, it would look like this in the Story Template before the button was pressed:


The merchant had [Eye Color] eyes, and [NPC Interesting Feature].


Once the Generate Story button is pressed, a new record is created that replaces the Table callouts, it might look like this:


The merchant had blue eyes, and a pink scar on both palms.


Furthermore, there is another layer to what is baked in out of the box, and these are called Reference Links. They let you recall something you have already retrieved from a given table, and would look like this at first:


The merchant had [Eye Color] eyes, and [NPC Interesting Feature]. His <Eye Color> eyes quickly searched the scene ahead.


Once the Generate Story button is pressed, a new record is created that replaces the Table callouts, and ALSO retrieves data it already stored from the [Eye Color] Table Callout, in order to populate the <Eye Color> Reference Link with the same data. It might look like this:


The merchant had green eyes, and a nervous twitch. His green eyes quickly searched the scene ahead.


I aim to provide a bit more functionality, in the form of a second type of Table Callout, and a second type of Reference Link. My callouts will look like {Table Callout} instead of [Table Callout], and my Reference Links will look like #Reference Link|Column Number# (where "Column Number" is a numerical digit) instead of <Reference Link>. This is intended to be additive, and not replace original functionality but augment it. (Though for prototype purposes, I have removed all original functionality from the code below. I'll add it back in when I get this working)

When I make a Table callout using the new method, I want the output of the table's different columns to be placed into discrete positions within an array, so I can later retrieve them separately with the new Reference Link. (The original system doesn’t let you do this: any time you use a Reference Link, it verbal-diarrheas every single column result from the initial Table callout) Here's an example of how I want to cherry-pick the column results from Table callouts under my added system:

Table A has 3 columns and 5 rows. When rolling for a result on Table A, no matter which of the 5 rows is rolled as the result, you will get output from all three columns of that row. So within the story template, let’s say we type:

{Table A}

#Table A|2#

#Table A|3#

The desired outcome would be that Table A’s output goes into an array, with each column occupying a different position. In the following two lines, we retrieve only what came from the second column of the original {Table A} callout, and on the third line we retrieve only what came from the third column.

Conceptually, I know what I want to do, and I feel like I am close, but visualizing what I need to do next with my code is boggling me… Any suggestions would be greatly welcome