PDA

View Full Version : Advanced Bestiary NPC Factory for 3.5



Kelevraa
December 1st, 2018, 20:14
Hello everyone,

I am having trouble using the NPC factory for 3.5 creatures. I just can't seem to figure out the correct format for importing monster data. I have searched for days now. Granted, I'm not the best at using advanced search functions, but I have tried my best.

Literally, everything I come across is for Pathfinder and the PF format. I bought the Advanced Bestiary because it specifically states compatibility with 3.5 and not just Pathfinder. I guess I wouldn't ask for a refund, because the template tool is also handy, but not having this functionality detracts from the value to a significant degree. It's 80% of the reason I bought it. I feel like it's something simple and I'm just missing it. Is there an example of D&D 3.5 NPC format necessary for the import function to work? (again, i only ever see PF examples and that doesn't work w/o so much editing I might as well type everything in by hand anyway).

I tried searching for a thread addressing this. This has to have been addressed before, but I can't find it.

Thank you.

Trenloe
December 2nd, 2018, 15:28
I just can't seem to figure out the correct format for importing monster data.
Use the Paizo statblock format. Info here: https://legacy.aonprd.com/bestiary/introduction.html

Also see the video on the product page for some guidelines: https://www.fantasygrounds.com/store/product.php?id=GRR2901e

ddavison
December 2nd, 2018, 16:07
It looks like this one was a little light on the documentation for the statblock importer. It was tested with statblocks I could find at the time for 3.5E and Pathfinder, but I remember there being some finicky bits. Do you have a sample stat block you are trying to do?

During the 3.5E era, it was hard to find any two companies or sites that did them the same.
From this page: https://mikael.borjesson.net/dnd/monster-data.asp?monster=Angel,+Astral+Deva

ORIGINAL:


Angel, Astral Deva CR 14
Always good (any) Medium Outsider (Angel, Extraplanar, Good)
Init +8; Senses darkvision 60 ft., low-light vision; Listen +23, Spot +23
Languages Celestal, Infernal, Draconic; tongues
AC 29, touch 14, flat-footed 25; protective aura, uncanny dodge (+4 Dex, +15 natural)
hp 102 (12 HD); DR 10/evil
Immune acid, cold, petrification
Resist electricity 10, fire 10; SR 30
Fort +14 (+18 against poison), Ref +12, Will +12
Speed 50 ft., fly 100 ft. (good)
Melee +3 heavy mace of disruption +21/+16/+11 (1d8+12 plus stun); or Melee Slam +18 (1d8+9)
Space 5 ft.; Reach 5 ft.
Base Atk +12; Grp +18
Atk Options Cleave, Power Attack
Special Actions change shape
Spell-like Abilities (CL 12):
At will—aid, continual flame, detect evil, discern lies (DC 19), dispel evil (DC 20), dispel magic, holy aura (DC 23), holy smite (DC 19), holy word (DC 22), invisibility (self only), plane shift (DC 22), remove curse (DC 18), remove disease (DC 18), remove fear (DC 16)
7/day—cure light wounds (DC 16), see invisibility
1/day—blade barrier (DC 21), heal (DC 21)
The save DCs are Charisma-based
Abilities Str 22, Dex 18, Con 18, Int 18, Wis 18, Cha 20
Feats Alertness, Cleave, Great Fortitude, Improved Initiative, Power Attack
Skills Concentration +19, Craft or Knowledge (any three) +19, Diplomacy +22, Escape Artist +19, Hide +19, Intimidate +20, Listen +23, Move Silently +19, Sense Motive +19, Spot +23, Use Rope +4 (+6 with bindings)
Advancement 13-18 HD (Medium); 19-36 HD (Large)
Level adjustment +8
Stun (Su) If an astral deva strikes an opponent twice in one round with its mace, that creature must succeed on a DC 22 Fortitude save or be stunned for 1d6 rounds. The save DC is Strength-based.
Uncanny Dodge (Ex) An astral deva retains its Dexterity bonus to AC when flat-footed, and it cannot be flanked except by a rogue of at least 16th level. It can flank characters with the uncanny dodge ability as if it were a 12th-level rogue.
Change Shape (Su) An astral deva can assume the form of any Small or Medium humanoid.


MODIFIED TO PARSE:


Angel, Astral Deva
CR 14
LG Medium Outsider (Angel, Extraplanar, Good)
Init +8; Senses darkvision 60 ft., low-light vision; Listen +23, Spot +23
Languages Celestal, Infernal, Draconic; tongues
AC 29, touch 14, flat-footed 25; protective aura, uncanny dodge (+4 Dex, +15 natural)
hp 102 (12 HD);
DR 10/evil
Immune acid, cold, petrification;
Resist electricity 10, fire 10; SR 30;
OFFENSE
Fort +14 (+18 against poison), Ref +12, Will +12
Speed 50 ft., fly 100 ft. (good)
Melee +3 heavy mace of disruption +21/+16/+11 (1d8+12 plus stun); or Melee Slam +18 (1d8+9)
Space 5 ft.; Reach 5 ft.
Base Atk +12; Grp +18
Special Attacks Cleave, Power Attack, Change Shape.
Spell-like Abilities (CL 12): At will—aid, continual flame, detect evil, discern lies (DC 19), dispel evil (DC 20), dispel magic, holy aura (DC 23), holy smite (DC 19), holy word (DC 22), invisibility (self only), plane shift (DC 22), remove curse (DC 18), remove disease (DC 18), remove fear (DC 16);7/day—cure light wounds (DC 16), see invisibility;1/day—blade barrier (DC 21), heal (DC 21)
The save DCs are Charisma-based
STATISTICS
Abilities Str 22, Dex 18, Con 18, Int 18, Wis 18, Cha 20
Feats Alertness, Cleave, Great Fortitude, Improved Initiative, Power Attack
Skills Concentration +19, Craft or Knowledge (any three) +19, Diplomacy +22, Escape Artist +19, Hide +19, Intimidate +20, Listen +23, Move Silently +19, Sense Motive +19, Spot +23, Use Rope +4 (+6 with bindings).
Advancement 13-18 HD (Medium); 19-36 HD (Large)
Level Adjustment: +8
Stun (Su) If an astral deva strikes an opponent twice in one round with its mace, that creature must succeed on a DC 22 Fortitude save or be stunned for 1d6 rounds. The save DC is Strength-based.
Uncanny Dodge (Ex) An astral deva retains its Dexterity bonus to AC when flat-footed, and it cannot be flanked except by a rogue of at least 16th level. It can flank characters with the uncanny dodge ability as if it were a 12th-level rogue.
Change Shape (Su) An astral deva can assume the form of any Small or Medium humanoid.


Notable changes:

For the type, it looks for alignment to be one of the standard values like LG, CG, LE, etc. and then grabs the type from the rest of that line.
Put CR on a new line
Special Actions should actually be Special Attacks: {whatever}
After the Special Attacks, there should be a section called STATISTICS just before the abilities start. That tells the parser to grab everything between for the SA section
You can add an OFFENSE tag as well, just before the saves
If it is picking up "extra" stuff for certain fields, you can often control this by appending a ; or a . at the end of a line.


The new NPC should be added to the NPCs window under the New group heading.

ddavison
December 2nd, 2018, 16:13
Here is the extension for this. It has an ext extension that you can unzip into a folder in your extensions folder and then modify from there. The script with the statblock importer is found in the template_npc_factory_build_npc_rom_statblock.lua file.

Here is the relative code for the parsing routine:
PART 1:


function onButtonPress()
local templateFactory = DB.findNode("template_npc_factory_data");
strStatblock = DB.getText(templateFactory,"statblock","");
ABUtility.Trace(strStatblock);

wndNPCs = Interface.openWindow("masterindex","npc");
wndNPCs.setRecordType("npc");
local nodeNPC = DB.createChild("npc");
--ABUtility.update(nodeNPC,"name","New NPC");

-- Name and CR
_,_,sName,sCR = string.find(strStatblock,"^(.-)%s*%(*CR*%s*(%d+)%)*");
if sName == nil then
_,_,sName = string.find(strStatblock,"^(.-)%s*[:;\n]+");
end
setNodeValue(nodeNPC,"name",stripCRLF(sName),"string","New NPC");
setNodeValue(nodeNPC,"cr",sCR,"number",0);

-- Abilities
_,_,sSTR = string.find(strStatblock,"[sS][tT][rR]:?%s*(%d+),*%s*");
_,_,sDEX = string.find(strStatblock,"[dD][eE][xX]:?%s*(%d+),*%s*");
_,_,sCON = string.find(strStatblock,"[cC][oO][nN]:?%s*(%d+),*%s*");
_,_,sWIS = string.find(strStatblock,"[wW][iI][sS]:?%s*(%d+),*%s*");
_,_,sINT = string.find(strStatblock,"[iI][nN][tT]:?%s*(%d+),*%s*");
_,_,sCHA = string.find(strStatblock,"[cC][hH][aA]:?%s*(%d+),*%s*");
setNodeValue(nodeNPC,"strength",sSTR,"number",0);
setNodeValue(nodeNPC,"dexterity",sDEX,"number",0);
setNodeValue(nodeNPC,"constitution",sCON,"number",0);
setNodeValue(nodeNPC,"wisdom",sWIS,"number",0);
setNodeValue(nodeNPC,"intelligence",sINT,"number",0);
setNodeValue(nodeNPC,"charisma",sCHA,"number",0);

-- AC, HP and HD
_,_,sAC1,sAC2,sHP,sHD = string.find(strStatblock,"[aA][cC]:?%s*(%d+)(.-)%s*[hH][pP]:?%s*(%d+)%s*%((.-)%)%s*[fF][oO][rR][tT]");
if sAC1 == nil then
_,_,sAC1,sAC2 = string.find(strStatblock,"[aA][cC]:?%s*(%d+)%s*(.-)[.;\n]");
end
if sAC1 == nil then
-- look for armor class spelled out
_,_,sAC1,sAC2 = string.find(strStatblock,"[aA][rR][mM][oO][rR]%s*[cC][lL][aA][sS][sS]:?%s*(%d+)%s*(.-);");
end
if sAC1 == nil then
sAC1 = 10;
sAC2 = "";
end
if sHP == nil then
_,_,sHP,sHD = string.find(strStatblock,"[hH][pP]:?%s*(%d+)%s*%((.-)%);?%s*");
end
if sHP == nil then
-- look for older hit point style 2d8+2 (11 hp)
_,_,sHP = string.find(strStatblock,"%((%d+) [hH][pP]");
end
if sHD == nil then
_,_,sHD = string.find(strStatblock,"[hH][dD]:?%s*(.-)%s*;");
_,_,sHD2 = string.find(strStatblock,"[hH][iI][tT]%s*[dD][iI][cC][eE]:?%s*(.-)%s*;");
sHD = getSmallest(sHD,sHD2);
end
setNodeValue(nodeNPC,"hp",sHP,"number",0);
setNodeValue(nodeNPC,"ac",stripCRLF(sAC1 .. sAC2), "string","0");
setNodeValue(nodeNPC,"hd",stripCRLF(sHD), "string", "0");

-- Saves
_,_,sFort = string.find(strStatblock,"[fF][oO][rR][tT]:?%s*%+%s*(%d+)");
_,_,sRef = string.find(strStatblock,"[rR][eE][fF]:?%s*%+%s*(%d+)");
_,_,sWill = string.find(strStatblock,"[wW][iI][lL][lL]:?%s*%+%s*(%d+)");
setNodeValue(nodeNPC,"fortitudesave",sFort,"number",0);
setNodeValue(nodeNPC,"reflexsave",sRef,"number",0);
setNodeValue(nodeNPC,"willsave",sWill,"number",0);

-- Senses
_,_,sSenses = string.find(strStatblock,"[sS][eE][nN][sS][eE][sS]:?%s*(.-)%s*[aAdD][uUeE][rRfF][aAeE]");
_,_,sSenses2 = string.find(strStatblock,"[dD][eE][tT][eE][cC][tT][iI][oO][nN]%s*:?%-?%s*(.-)%s*[.;\n]");
sSenses = getSmallest(sSenses,sSenses2);
setNodeValue(nodeNPC,"senses",stripCRLF(sSenses),"string","");

-- Initiative
_,_,sInit = string.find(strStatblock,"[iI][nN][iI][tT]:?%s*%+?%-?(%d+)");
setNodeValue(nodeNPC,"init",sInit,"number",0);

-- Space / Reach
_,_,sSpace = string.find(strStatblock,"[sS][pP][aA][cC][eE]:?%s*(%d+)%s?[fF][tT]");
_,_,sReach = string.find(strStatblock,"[rR][eE][aA][cC][hH]:?%s*(%d+)%s?[fF][tT]");
if sSpace then
if sReach then
sSpaceReach = sSpace .. " ft./" .. sReach .. " ft.";
end
end
setNodeValue(nodeNPC,"spacereach",sSpaceReach,"string","5 ft./5 ft.");

-- Attack
-- Replace cases like Crossbow, Heavy and Crossbow, Light in attack strings.
strStatblock = string.gsub(strStatblock,"%s+[oO][rR]%s*[\n]%s*"," or ");
strStatblock = string.gsub(strStatblock,"%s+[aA][nN][dD]%s*[\n]%s*"," and ");
strStatblock = string.gsub(strStatblock, "(%w+),%s?[hH][eE][aA][vV][yY]","Heavy %1");
strStatblock = string.gsub(strStatblock, "(%w+),%s?[lL][iI][gG][hH][tT]","Light %1");
ABUtility.Trace(strStatblock);
_,_,sAtk = string.find(strStatblock,"[mM][eE][lL][eE][eE]:?%s*(.-)%s*[sStT][aApP][aAcCeE][cCtT][eEiI]");
_,_,sAtk2 = string.find(strStatblock,"[aA][tT][tT][aA][cC][kK]:+%s*(.-)%s*;");
sAtk = getSmallest(sAtk,sAtk2);
if sAtk then
sAtk = string.gsub(sAtk,"\n[rR][aA][nN][gG][eE][dD]"," or Ranged");
--sAtk = ABUtility.stringReplace(sAtk,")%s*ranged"," or");
end
if sAtk == nil then
_,_,sAtk = string.find(strStatblock,"[mM][eE][lL][eE][eE]%s*(.-)%s*[sS][pP][aAeE][cC][eEiI]");
end
if sAtk == nil then
_,_,sAtk = string.find(strStatblock,"[mMrR][eEaA][lLnN][eEgG][eEeE][dD]*%s*(.-)%s*[.;]");
end
setNodeValue(nodeNPC,"atk",ABUtility.stringReplace(stripCRLF(sAtk),","," or "),"string","");
setNodeValue(nodeNPC,"fullatk",ABUtility.stringReplace(stripCRLF(sAtk),","," and "),"string","");

-- Aura (may be missing from many statblocks
_,_,sAura = string.find(strStatblock,"[aA][uU][rR][aA]:?%s+(.-)[)]");
if sAura then
sAura = sAura .. ")";
end
if sAura == nil then
_,_,sAura = string.find(strStatblock,"[aA][uU][rR][aA]:?%s+(.-);");
end
setNodeValue(nodeNPC,"aura",stripCRLF(sAura),"string","");

-- Speed
_,_,sSpeed = string.find(strStatblock,"[sS][pP][eE]*[eE]*[dD]:?%s*(.-)%s*;?%s*[mMrR][eEaA][lLnN][eEgG][eE]");
_,_,sSpeed2 = string.find(strStatblock,"[sS][pP][eE]*[eE]*[dD]:?%s*(.-)%s*;");
sSpeed = getSmallest(sSpeed,sSpeed2);
setNodeValue(nodeNPC,"speed",stripCRLF(sSpeed),"string","");

-- BAB, CM and CMD
_,_,sBAB = string.find(strStatblock,"[bB][aA][sS][eE] [aA][tT][kK]:?%s*%+?(%d+);+%s*");
_,_,sCM = string.find(strStatblock,"[cC][mM][bB]:?%s*%+?(%d+);?%s*");
_,_,sCMD = string.find(strStatblock,"[cC][mM][dD]:?%s*%+?(.-)%s*[sSfF][kKeE][iIaA][lLtT]");
if sBAB then
sBABCMD = "+" .. sBAB;
else
sBABCMD = "-";
end
if sCM then
sBABCMD = sBABCMD .. "/+" .. sCM .. "; ";
else
sBABCMD = sBABCMD .. "/-; ";
end
if sCMD then
sBABCMD = sBABCMD .. "CMD " .. sCMD;
end
setNodeValue(nodeNPC,"babgrp",sBABCMD,"string","");

-- Feats
_,_,sFeats = string.find(strStatblock,"[fF][eE][aA][tT][sS]:?%s*(.-)%s*[eEsS][cCkK][oOiI][lL][oOlL][gGsS]:?");
_,_,sFeats2 = string.find(strStatblock,"[fF][eE][aA][tT][sS]:?%s*(.-)%s*;%s*");
if sFeats2 then
sFeats = getSmallest(sFeats, sFeats2);
end
setNodeValue(nodeNPC,"feats",stripCRLF(sFeats),"string","");

-- Skills
_,_,sSkills = string.find(strStatblock,"[sS][kK][iI][lL][lL][sS]:?(.-)%s*[lL][aA][nN][gG][uU][aA][gG][eE][sS]");
if sSkills then
setNodeValue(nodeNPC,"skills",stripCRLF(sSkills),"string","");
else
_,_,sSkills = string.find(strStatblock,"[sS][kK][iI][lL][lL][sS]:?(.-)[sS][qQ]:?%s+");
end
_,_,sSkills2 = string.find(strStatblock,"[sS][kK][iI][lL][lL][sS]:?(.-)%s*;%s*");
if sSkills2 then
sSkills = getSmallest(sSkills,sSkills2);
end
_,_,sSkills2 = string.find(strStatblock,"[sS][kK][iI][lL][lL][sS]:?(.-)[pP][oO][sS][sS][eE][sS][iI][oO][nN]");
if sSkills2 then
sSkills = getSmallest(sSkills, sSkills2);
end
if sSkills then
setNodeValue(nodeNPC,"skills",stripCRLF(sSkills),"string","");
end

ddavison
December 2nd, 2018, 16:13
Part 2:



-- SQ
_,_,sSQ1 = string.find(strStatblock,"SQ:?%s*(.-)%s*[gG][eE][aA][rR]:?%s*");
_,_,sSQ2 = string.find(strStatblock,"SQ:?%s*(.-)%s*[eE][cC][oO][lL][oO][gG][yY]");
_,_,sSQ3 = string.find(strStatblock,"SQ:?%s*(.-)%s*[sS][pP][eE][cC][iI][aA][lL] [aA][bB][iI][lL][iI][tT][iI][eE][sS]");
_,_,sSQ4 = string.find(strStatblock,"SQ:?%s*(.-)%s*[oO][fF][fF][eE][nN][sS][eE]");
sSQ = getSmallest(sSQ1, sSQ2);
sSQ = getSmallest(sSQ, sSQ3);
sSQ = getSmallest(sSQ, sSQ4);
_,_,sSQ4 = string.find(strStatblock,"SQ:?%s*(.-)%s*;");
sSQ = getSmallest(sSQ, sSQ4);
if sSQ then
sSQ = sSQ .. "; ";
end
setNodeValue(nodeNPC,"specialqualities",sSQ,"string","");

_,_,sSA = string.find(strStatblock,"[sS][pP][eE][cC][iI][aA][lL] [aA][tT][tT][aA][cC][kK][sS]:?%s*(.-)%s*[sS][tT][aA][tT][iI][sS][tT][iI][cC][sS]");
setNodeValue(nodeNPC,"specialattacks",sSA,"string","");

-- Languages
_,_,sLanguages = string.find(strStatblock,"[lL][aA][nN][gG][uU][aA][gG][eE][sS]*:?%s*(.-)[;.]");
_,_,sLanguages2 = string.find(strStatblock,"[lL][aA][nN][gG][uU][aA][gG][eE][sS]*:?%s*(.-)%s*SQ");
sLanguages = getSmallest(sLanguages,sLanguages2);
_,_,sLanguages2 = string.find(strStatblock,"[lL][aA][nN][gG][uU][aA][gG][eE][sS]*:?%s*(.-)%s*[dD][eE][fF][eE][nN][sS][eE]");
if sLanguages2 then
sLanguages = getSmallest(sLanguage,sLanguages2);
end
setNodeValue(nodeNPC,"languages",stripCRLF(sLanguages),"string","");

-- Environment
_,_,sEnv = string.find(strStatblock,"[eE][nN][vV][iI][rR][oO][nN][mM][eE][nN][tT]:?%s*(.-)%s*[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]");
if not sEnv then
_,_,sEnv = string.find(strStatblock,"[eE][nN][vV][iI][rR][oO][nN][mM][eE][nN][tT]:?%s*(.-)[.;]");
end
_,_,sEnv2 = string.find(strStatblock,"[eE][nN][vV][iI][rR][oO][nN][mM]?[eE]?[nN]?[tT]?:?%s*(.-)%s*;");
sEnv = getSmallest(sEnv,sEnv2);
setNodeValue(nodeNPC,"environment",stripCRLF(sEnv),"string","");

-- Organization
_,_,sOrg = string.find(strStatblock,"[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]:?%s*(.-)%s*[tT][rR][eE][aA][sS][uU][rR][eE]");
if not sOrg then
_,_,sOrg = string.find(strStatblock,"[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]:?%s*(.-)[.;]");
end
_,_,sOrg2 = string.find(strStatblock,"[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]:?%s*(.-)%s*;");
if sOrg2 then
sOrg = getSmallest(sOrg,sOrg2);
end
setNodeValue(nodeNPC,"organization",stripCRLF(sOrg),"string","");

-- Treasure
_,_,sTreasure = string.find(strStatblock,"[tT][rR][eE][aA][sS][uU][rR][eE]:?%s*(.-)[.;]");
_,_,sTreasure2 = string.find(strStatblock,"[tT][rR][eE][aA][sS][uU][rR][eE]:?%s*(.-)$");
_,_,sTreasure3 = string.find(strStatblock,"[tT][rR][eE][aA][sS][uU][rR][eE]:?%s*(.-)%s*[sS][pP][eE][cC][iI][aA][lL] [aA][bB]");
sTreasure = getSmallest(sTreasure,sTreasure2);
sTreasure = getSmallest(sTreasure,sTreasure3);
if sTreasure == nil then
_,_,sTreasure = string.find(strStatblock,"[pP][oO][sS][sS][eE][sS][sS][iI][oO][nN][sS]:?%s*(.-)[.;]");
_,_,sTreasure2 = string.find(strStatblock,"[pP][oO][sS][sS][eE][sS][sS][iI][oO][nN][sS]:?%s*(.-)$");
sTreasure = getSmallest(sTreasure,sTreasure2);
end
if sTreasure == nil then
_,_,sTreasure = string.find(strStatblock,"[gG][eE][aA][rR]:?%s*(.-)[.;]");
_,_,sTreasure2 = string.find(strStatblock,"[gG][eE][aA][rR]:?%s*(.-)$");
sTreasure = getSmallest(sTreasure,sTreasure2);
_,_,sTreasure2 = string.find(strStatblock,"[gG][eE][aA][rR]:?%s*(.-)[sS][pP][eE][cC][iI][aA][lL] [aA][bB][iI][lL]");
sTreasure = getSmallest(sTreasure,sTreasure2);
end
setNodeValue(nodeNPC,"treasure",stripCRLF(sTreasure),"string","");

-- DR, Immune, Resist, SR, Weakness
-- Immunities
_,_,sImmune = string.find(strStatblock,"[iI][mM][mM][uU][nN][eE]:?%s*(.-)[.;\n]");
_,_,sImmune2 = string.find(strStatblock,"[iI][mM][mM][uU][nN][eE]:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sImmune = getSmallest(sImmune,sImmune2);
if sImmune then
sImmune = " Immune " .. sImmune .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sImmune));
-- Resistances
_,_,sResist = string.find(strStatblock,"[rR][eE][sS][iI][sS][tT]:?%s*(.-)[.;]");
_,_,sResist2 = string.find(strStatblock,"[rR][eE][sS][iI][sS][tT]:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sResist = getSmallest(sResist,sResist2);
if sResist then
sResist = " Resist " .. sResist .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sResist));
-- Weaknesses
_,_,sWeak = string.find(strStatblock,"[wW][eE][aA][kK][nN][eE][sS][sS][eE]?[sS]?:?%s*(.-)[.;]");
_,_,sWeak2 = string.find(strStatblock,"[wW][eE][aA][kK][nN][eE][sS][sS][eE]?[sS]?:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sWeak = getSmallest(sWeak,sWeak2);
if sWeak then
sWeak = " Weakness " .. sWeak .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sWeak));
-- DR
_,_,sDR = string.find(strStatblock,"DR:?%s*(.-)[.;]");
_,_,sDR2 = string.find(strStatblock,"DR:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sDR = getSmallest(sDR,sDR2);
if sDR then
sDR = " DR " .. sDR .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sDR));
-- SR
_,_,sSR = string.find(strStatblock,"SR:?%s*(.-)[.;]");
_,_,sSR2 = string.find(strStatblock,"SR:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sSR = getSmallest(sSR,sSR2);
if sSR then
sSR = " SR " .. sSR .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sSR));

-- Type
_,_,sAlignment,sType = string.find(strStatblock,"%s+([LCN][EGN ])%s*(.-)%s*[dDiI][eEnN][fFiItT][eEtT]");
_,_,sName2,sType2 = string.find(strStatblock,"^(.-)%s*[:](.-);");
sType = getSmallest(sType,sType2);
if sAlignment and sType then
sType = sAlignment .. " " .. sType;
end
setNodeValue(nodeNPC,"type",stripCRLF(sType),"string","");
setNodeValue(nodeNPC,"alignment",sAlignment,"string","");

-- Description
strStatblock = DB.getValue(templateFactory,"statblock","");
if strStatblock then
strStatblock = "<linklist><link class=\"\" recordname=\"\">Image</link></linklist>" .. strStatblock;
end
setNodeValue(nodeNPC,"text",strStatblock,"formattedtext","<p></p>");

-- Token set to defaul letter of first character in name
if sName then
_,_,sLetter = string.find(sName,"^(%a)");
if sLetter then
sLetter = "tokens/Medium/" .. sLetter .. ".png@Letter Tokens";
setNodeValue(nodeNPC,"token",sLetter,"token","");
end
end

end

Kelevraa
December 2nd, 2018, 16:48
Use the Paizo statblock format. Info here: https://legacy.aonprd.com/bestiary/introduction.html

Also see the video on the product page for some guidelines: https://www.fantasygrounds.com/store/product.php?id=GRR2901e

Yeah, that's clearly the thought process I see throughout the site. I just don't want to use the PF format. I was hoping it was just a minor alteration, but putting 3.5 into PF format takes slightly longer than it does for me to enter stats by hand. Maybe I type faster than I can highlight, cut, paste, cleanup. Idk, but it's negative value for me to try and convert to PF overall.

So I gave up and started altering a pathfinder import tool. Which is kinda hard, because idk enough about programming in any language. I just barely grasp the entry-level concepts. So I can walk, but I'm blind and deaf. But I'm grabbing 80% of everything (which is nice since i started at 0%) and making steady progress.

Damned's videos should be stickied just about everywhere. By far the best resource I've come across in my search for diy FG problem solving.

I also watched those videos you linked, it's just putting PF creatures into a PF import tool. Which is not what I'm trying to do. Unless someone made a tool to format it correctly form 3.5. Then maybe, as long as the end result is 3.5 format and not PF. There was one part in a third video (I think, I'm very tired) not shown on the product page, but buried on their YT channel, where he struggled with a 3.5 stat block. I can only assume that path was abandoned as the feature is not very usable in the 3.5 ruleset as is. Just a little disappointed that it says 3.5 compatible, but only if you convert everything to PF. Which isn't really 3.5. The templates were still worth the purchase imo. Just a slight bummer is all.

Frankencoding will only get me so far I'm sure.

Kelevraa
December 2nd, 2018, 16:50
Part 2:



-- SQ
_,_,sSQ1 = string.find(strStatblock,"SQ:?%s*(.-)%s*[gG][eE][aA][rR]:?%s*");
_,_,sSQ2 = string.find(strStatblock,"SQ:?%s*(.-)%s*[eE][cC][oO][lL][oO][gG][yY]");
_,_,sSQ3 = string.find(strStatblock,"SQ:?%s*(.-)%s*[sS][pP][eE][cC][iI][aA][lL] [aA][bB][iI][lL][iI][tT][iI][eE][sS]");
_,_,sSQ4 = string.find(strStatblock,"SQ:?%s*(.-)%s*[oO][fF][fF][eE][nN][sS][eE]");
sSQ = getSmallest(sSQ1, sSQ2);
sSQ = getSmallest(sSQ, sSQ3);
sSQ = getSmallest(sSQ, sSQ4);
_,_,sSQ4 = string.find(strStatblock,"SQ:?%s*(.-)%s*;");
sSQ = getSmallest(sSQ, sSQ4);
if sSQ then
sSQ = sSQ .. "; ";
end
setNodeValue(nodeNPC,"specialqualities",sSQ,"string","");

_,_,sSA = string.find(strStatblock,"[sS][pP][eE][cC][iI][aA][lL] [aA][tT][tT][aA][cC][kK][sS]:?%s*(.-)%s*[sS][tT][aA][tT][iI][sS][tT][iI][cC][sS]");
setNodeValue(nodeNPC,"specialattacks",sSA,"string","");

-- Languages
_,_,sLanguages = string.find(strStatblock,"[lL][aA][nN][gG][uU][aA][gG][eE][sS]*:?%s*(.-)[;.]");
_,_,sLanguages2 = string.find(strStatblock,"[lL][aA][nN][gG][uU][aA][gG][eE][sS]*:?%s*(.-)%s*SQ");
sLanguages = getSmallest(sLanguages,sLanguages2);
_,_,sLanguages2 = string.find(strStatblock,"[lL][aA][nN][gG][uU][aA][gG][eE][sS]*:?%s*(.-)%s*[dD][eE][fF][eE][nN][sS][eE]");
if sLanguages2 then
sLanguages = getSmallest(sLanguage,sLanguages2);
end
setNodeValue(nodeNPC,"languages",stripCRLF(sLanguages),"string","");

-- Environment
_,_,sEnv = string.find(strStatblock,"[eE][nN][vV][iI][rR][oO][nN][mM][eE][nN][tT]:?%s*(.-)%s*[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]");
if not sEnv then
_,_,sEnv = string.find(strStatblock,"[eE][nN][vV][iI][rR][oO][nN][mM][eE][nN][tT]:?%s*(.-)[.;]");
end
_,_,sEnv2 = string.find(strStatblock,"[eE][nN][vV][iI][rR][oO][nN][mM]?[eE]?[nN]?[tT]?:?%s*(.-)%s*;");
sEnv = getSmallest(sEnv,sEnv2);
setNodeValue(nodeNPC,"environment",stripCRLF(sEnv),"string","");

-- Organization
_,_,sOrg = string.find(strStatblock,"[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]:?%s*(.-)%s*[tT][rR][eE][aA][sS][uU][rR][eE]");
if not sOrg then
_,_,sOrg = string.find(strStatblock,"[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]:?%s*(.-)[.;]");
end
_,_,sOrg2 = string.find(strStatblock,"[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN]:?%s*(.-)%s*;");
if sOrg2 then
sOrg = getSmallest(sOrg,sOrg2);
end
setNodeValue(nodeNPC,"organization",stripCRLF(sOrg),"string","");

-- Treasure
_,_,sTreasure = string.find(strStatblock,"[tT][rR][eE][aA][sS][uU][rR][eE]:?%s*(.-)[.;]");
_,_,sTreasure2 = string.find(strStatblock,"[tT][rR][eE][aA][sS][uU][rR][eE]:?%s*(.-)$");
_,_,sTreasure3 = string.find(strStatblock,"[tT][rR][eE][aA][sS][uU][rR][eE]:?%s*(.-)%s*[sS][pP][eE][cC][iI][aA][lL] [aA][bB]");
sTreasure = getSmallest(sTreasure,sTreasure2);
sTreasure = getSmallest(sTreasure,sTreasure3);
if sTreasure == nil then
_,_,sTreasure = string.find(strStatblock,"[pP][oO][sS][sS][eE][sS][sS][iI][oO][nN][sS]:?%s*(.-)[.;]");
_,_,sTreasure2 = string.find(strStatblock,"[pP][oO][sS][sS][eE][sS][sS][iI][oO][nN][sS]:?%s*(.-)$");
sTreasure = getSmallest(sTreasure,sTreasure2);
end
if sTreasure == nil then
_,_,sTreasure = string.find(strStatblock,"[gG][eE][aA][rR]:?%s*(.-)[.;]");
_,_,sTreasure2 = string.find(strStatblock,"[gG][eE][aA][rR]:?%s*(.-)$");
sTreasure = getSmallest(sTreasure,sTreasure2);
_,_,sTreasure2 = string.find(strStatblock,"[gG][eE][aA][rR]:?%s*(.-)[sS][pP][eE][cC][iI][aA][lL] [aA][bB][iI][lL]");
sTreasure = getSmallest(sTreasure,sTreasure2);
end
setNodeValue(nodeNPC,"treasure",stripCRLF(sTreasure),"string","");

-- DR, Immune, Resist, SR, Weakness
-- Immunities
_,_,sImmune = string.find(strStatblock,"[iI][mM][mM][uU][nN][eE]:?%s*(.-)[.;\n]");
_,_,sImmune2 = string.find(strStatblock,"[iI][mM][mM][uU][nN][eE]:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sImmune = getSmallest(sImmune,sImmune2);
if sImmune then
sImmune = " Immune " .. sImmune .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sImmune));
-- Resistances
_,_,sResist = string.find(strStatblock,"[rR][eE][sS][iI][sS][tT]:?%s*(.-)[.;]");
_,_,sResist2 = string.find(strStatblock,"[rR][eE][sS][iI][sS][tT]:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sResist = getSmallest(sResist,sResist2);
if sResist then
sResist = " Resist " .. sResist .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sResist));
-- Weaknesses
_,_,sWeak = string.find(strStatblock,"[wW][eE][aA][kK][nN][eE][sS][sS][eE]?[sS]?:?%s*(.-)[.;]");
_,_,sWeak2 = string.find(strStatblock,"[wW][eE][aA][kK][nN][eE][sS][sS][eE]?[sS]?:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sWeak = getSmallest(sWeak,sWeak2);
if sWeak then
sWeak = " Weakness " .. sWeak .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sWeak));
-- DR
_,_,sDR = string.find(strStatblock,"DR:?%s*(.-)[.;]");
_,_,sDR2 = string.find(strStatblock,"DR:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sDR = getSmallest(sDR,sDR2);
if sDR then
sDR = " DR " .. sDR .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sDR));
-- SR
_,_,sSR = string.find(strStatblock,"SR:?%s*(.-)[.;]");
_,_,sSR2 = string.find(strStatblock,"SR:?%s*(.-)%s*[oO][fF][eE][nN][sS][eE]");
sSR = getSmallest(sSR,sSR2);
if sSR then
sSR = " SR " .. sSR .. "; ";
end
appendNodeValue(nodeNPC,"specialqualities",stripCRLF(sSR));

-- Type
_,_,sAlignment,sType = string.find(strStatblock,"%s+([LCN][EGN ])%s*(.-)%s*[dDiI][eEnN][fFiItT][eEtT]");
_,_,sName2,sType2 = string.find(strStatblock,"^(.-)%s*[:](.-);");
sType = getSmallest(sType,sType2);
if sAlignment and sType then
sType = sAlignment .. " " .. sType;
end
setNodeValue(nodeNPC,"type",stripCRLF(sType),"string","");
setNodeValue(nodeNPC,"alignment",sAlignment,"string","");

-- Description
strStatblock = DB.getValue(templateFactory,"statblock","");
if strStatblock then
strStatblock = "<linklist><link class=\"\" recordname=\"\">Image</link></linklist>" .. strStatblock;
end
setNodeValue(nodeNPC,"text",strStatblock,"formattedtext","<p></p>");

-- Token set to defaul letter of first character in name
if sName then
_,_,sLetter = string.find(sName,"^(%a)");
if sLetter then
sLetter = "tokens/Medium/" .. sLetter .. ".png@Letter Tokens";
setNodeValue(nodeNPC,"token",sLetter,"token","");
end
end

end


Thank you so much. I watch just about all your videos realted to any products I use on FG. I'll get right on trying to figure that all out. I appreciate it very much!

Kelevraa
December 2nd, 2018, 17:17
[QUOTE=ddavison;416556]It looks like this one was a little light on the documentation for the statblock importer. It was tested with statblocks I could find at the time for 3.5E and Pathfinder, but I remember there being some finicky bits. Do you have a sample stat block you are trying to do?

During the 3.5E era, it was hard to find any two companies or sites that did them the same.
From this page: https://mikael.borjesson.net/dnd/monster-data.asp?monster=Angel,+Astral+Deva

I see what you mean, that would make it very difficult. I never thought of all the other products out there. I stayed very close to just the wizards products and didn't notice any variations in stat blocks. Never had anything like FG before though, so wasn't paying attention either. I love this tool, I just want to understand it better =] (again, damned's videos are incredible.)

ddavison
December 2nd, 2018, 18:26
Do you have a link to the stat blocks you are trying to import? It might be a common thing that they are doing that could be accounted for with a few quick changes.

Kelevraa
December 2nd, 2018, 20:17
I don't have a link but it's the standard mm format and most of the core rules format. I think oozes are safe and not copyrighted (I hope?). This one comes from the planar Handbook.

VOID OOZE
Large Ooze (Extraplanar)
Hit Dice: 10d10+80 (135 hp)
Initiative: –5
Speed: Fly 30 ft. (6 squares) (perfect)
Armor Class: 4 (–5 Dex, –1 size), touch 4, flat-footed 4
Base Attack/Grapple: +7/+16
Attack: Slam +11 melee (2d4+5 plus 1d8 negative energy
plus energy drain)
Full Attack: Slam +11 melee (2d4+5 plus 1d8 negative
energy plus energy drain)
Space/Reach: 10 ft./5 ft.
Special Attacks: Energy drain, engulf
Special Qualities: Blindsight 60 ft., death ward, ooze traits,
shadow shield, split
Saves: Fort +11, Ref +0, Will +0
Abilities: Str 20, Dex 1, Con 26, Int —, Wis 1, Cha 1
Skills: —
Feats: —
Environment: Negative Energy Plane
Organization: Solitary, midnight (2–7)
Challenge Rating: 8
Treasure: None
Alignment: Always neutral
Advancement: —
Level Adjustment: —


I have noticed what you mean that even within wizards own publications they vary a bit from this, but this is the format I see the most. Some other (I assume later publications) seem like they would fit with only a few tweaks like you mention. Some look almost exactly like PF format. I never knew there was such variance. TBH I'm going to put myself through the gauntlet and alter the NPC sheet a tiny bit, so I'm probably going to use your posts to guide me on making my own thing. But I have already learned a lot by hacking up cgen. Which I hope is ok. So I'm going to hack up those functions you pointed out and see what kind of abomination I can come up with. To handle importing that standard mm style statblock.

Kelevraa
December 3rd, 2018, 14:47
If it is picking up "extra" stuff for certain fields, you can often control this by appending a ; or a . at the end of a line.



That answered my question almost exactly. Was wondering if there was something like that to fix the major issue (which was it grabbing a ton of extra stuff, like most of it). This helps a lot. Thank you.

Kelrugem
December 3rd, 2018, 17:54
That answered my question almost exactly. Was wondering if there was something like that to fix the major issue (which was it grabbing a ton of extra stuff, like most of it). This helps a lot. Thank you.

Hi :)

I also play 3.5e and have the advanced bestiary :) But I didn't really play a lot with it yet; so, I would be glad when you could post then an example of a "monster-writing" which worked for you if you find some :)

Best,

Kelrugem

Kelevraa
December 3rd, 2018, 21:27
I'll let you know if I come across anything, although my main mission is tweaking the code to make it work for me. So I probably won't find anything tbh. Use the ";" (w/o the quotes) at the end of entries to stop it from grabbing others. Lets you at use everything it does pick up.

Kelrugem
December 3rd, 2018, 21:49
I'll let you know if I come across anything, although my main mission is tweaking the code to make it work for me. So I probably won't find anything tbh. Use the ";" (w/o the quotes) at the end of entries to stop it from grabbing others. Lets you at use everything it does pick up.

Thanks :) When I find time for that (too much work at the moment) then I also look at the code and could post it here to share a possible way of using that for 3.5e :) (but I am not sure if I will be able to do that)