PDA

View Full Version : Trying to beef up Story Templates. Help needed



JimSocks
October 12th, 2020, 06:23
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:
40151

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. I'll attach the code so far, and post it below too.

JimSocks
October 12th, 2020, 06:24
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

Three of Swords
October 16th, 2020, 02:27
I like what you're trying to do. But way beyond my feeble skills to assist.

Good luck!

JimSocks
October 16th, 2020, 04:14
Thanks. Pulling my damn hair out trying to figure this out. It really should be so damn simple... yet my little brain is smoking.

damned
October 18th, 2020, 09:36
Good luck JimSocks - keeping an eye on this!

JimSocks
October 27th, 2020, 05:03
Good luck JimSocks - keeping an eye on this!

Thanks Damned, but this project has broken me. I have been working on this nearly every single day since I started. I have paid for and completed an online Lua course just for this purpose. And yet, I think I might be defeated. I just can't figure this out with my brain.

I have located WHERE the needed code needs to go, but for the life of me even after trying what feels like three hundred different ways I can't get it to act like I envision. I am sure a more competent coder could figure this out in 10 minutes, probably even less, but for me even several weeks isn't enough... ...Feeling pretty broken down over here.

JimSocks
October 31st, 2020, 02:20
Wait. Holy **** I think I have cracked it!

It’s WORKING!!!! Muahahahahahahahaaaa!!!!!

As soon as I stopped trying to repurpose bits of the existing code to do what I wanted, and began just writing my own from scratch, things started to click.

I thought I could only succeed by standing on the shoulders of giants, trying to use their super-advanced code and shape it, but that was a fools errand. I needed to write it for myself, and when I did it was much easier to wrap my head around.

So, it’s working. Now I need to learn how to make an extension that replaces stock FG code with my hot-rodded version. Here’s hoping that’s easier than THIS was!

JimSocks
October 31st, 2020, 06:59
Man you guys, this is amazing. I have this 100% working as I envisioned at the outset, and it's just killer. (There is only ONE bug I have noticed that needs to be squashed now: It doesn't like quotes " in the text for some weird reason. Must be a fat-fingered pattern or something, I am sure I will find it soon.)

I am on a roll- I might just look into passing parameters into story templates now!

Three of Swords
November 1st, 2020, 17:40
Nice! Glad you didn't give up. I want to see some of the things you do w/ this.

JimSocks
November 1st, 2020, 20:52
I’m excited too! I have been doing extensive testing of the code, and have found some bugs that I am still wrestling with.

For starters: the weird quote issue I mentioned above wasn’t the only character that’d throw it for a loop. & > < and # were also causing spasms. I think I‘ve got that under control now, but it took me a whole day to figure out.

NOW, I am realizing if there are sub-tables, my column storage solution just wants to store the name of those tables in the column data, instead of the results. This is a real brain-teaser- been working on it for a day and a half. I’m sure I’ll figure it out!

JimSocks
November 2nd, 2020, 20:58
I am kicking so much *** with this right now, it’s exciting!

I am definitely going to keep going with this project. I am thinking it’ll turn into a “Story Template PRO” extension.

Goals:
1. Hidden roll results (useful in combo with the other goals below)
2. Cross-template rolling (pass parameters in)
3. Column referencing (almost done!)
4. Callout Internal Rolling using embedded tables to determine which table to roll on. Ie: [[race] [gender] NPC ]

I’ve drafted up a proof-of-concept/roadmap document already for all these goals, so it’s time to get cracking while the excitement is high.

The amount of work going into this however, will probably mean it either ends up as a patron benefit on my Patreon or on DMs Guild for a small fee. I’ve already poured well over a month into it and there’s a long way to go...

JimSocks
November 7th, 2020, 06:41
And I have SUCCEEDED!!!

I got everything I wanted working in an extension now, EXCEPT passing parameters in from another template (because io.read and io.write being blocked put a roadblock up for me that I don't care to fiddle with at the moment)

But man did I get a LOT implemented!

Column Referencing #a|3#
Callout Custom Naming [:a:b]
Callout Internal Rolling [::[a] [b]:c]
Callout Internal Referencing [<a>]
Callout Internal Column Referencing [#a|3#]
Hidden rolls (using “?”)

ALL WORKING FLAWLESSLY! (It only took me 323 versions and taking a lua class to get it right... ) I will be making a video soon showing how they work, then it will likely appear on GMs guild or my Patreon. THEN I will set to work on Content Generator 2021, which will make heavy use of these new superpowers!!!!!

LordEntrails
November 7th, 2020, 23:12
Hey Jim, I wonder if your extension could take the current date from the in game calendar and enter it in a story template? Wouldn't it be nice if I could generate the contents of a store and the resulting story title have the date in it? I think so :)

Three of Swords
November 8th, 2020, 01:46
Hey Jim, I wonder if your extension could take the current date from the in game calendar and enter it in a story template? Wouldn't it be nice if I could generate the contents of a store and the resulting story title have the date in it? I think so :)

I agree w/ LE. I would find it very useful.

JimSocks
November 8th, 2020, 05:04
Actually not that hard to achieve I’d wager, judging by what I’ve already had to do to get this other stuff to work.

I’m not sure I see the utility you do, but I can see if I can add it. Is it just so when you need to refer back to it later you can use the date to search? Would the in-game calendar be more handy, or the actual date the story was generated in the real world?

I feel like the latter might be more handy, unless a lot of people regularly keep up with the in game timekeeping system?

JimSocks
November 8th, 2020, 20:10
Hey Jim, I wonder if your extension could take the current date from the in game calendar and enter it in a story template? Wouldn't it be nice if I could generate the contents of a store and the resulting story title have the date in it? I think so :)

10 versions later, and I have built this functionality in now too. It uses the current date of the computer system. UPDATE: 10 MORE versions later, and it now has the OPTION to use the computer's date, or the date from the calendar within Fantasy Grounds. Using the Fantasy Grounds date is called with [FGDate:FORMAT].

Using a call of [Date:FORMAT] (where "FORMAT" is replaced by any of the following: "mm/yyyy", "mmm/yyyy", "month/yyyy", "yyyy", "mmm/dd", "mmm/dd/yyyy", "mm/dd/yyyy", "month", "day", "month/day", "day/mmm/dd", "day/month/dd", or "oldschool") (Update note: Because of my limited coding ability, however, [FGDate:FORMAT] has fewer formatting options available.)

Using these format codes results in the date being generated within your story, in the format you desired.

For clarity, if the date is November 28th, 2021 (a Sunday), then the format means the following:
mm = 11
mmm = Nov
month = November
dd = 28th
day = Sunday

"day/mmm/dd" and "day/month/dd" result in "Sunday, Nov 28th" and "Sunday, November 28th", respectively.

"oldschool" results in "Sunday, the 28th of November, in the year of our Lord: Two Thousand and Twenty One"
NOTE: "oldschool" is only coded to work with dates from this year on, so if you want a year in the past, manually replace that in your generated output. It is also not a format option for [FGDate:FORMAT]

damned
November 8th, 2020, 22:34
Nineteen thousand?

JimSocks
November 9th, 2020, 00:21
Haha hundred. It’s only coded for now into the future actually so it will only say two thousand. I just used an example from the 1980s and forgot to change the thousand. It was just to illustrate the output. I should change it eh?