View Full Version : Duplicating the Type field in the NPC sheet and in the search of the NPC module
LordDante123
January 4th, 2025, 20:00
Hello fellow roleplayers!
I would like to know if there is an easier way to duplicate the "Type" field of the NPC sheet and also make it searchable in the NPC module like the "Type" field.
I want to make a "Location" field and if possibly others.
I believe there are at least two ways of doing that ?
By modifying the existing master data or by using an extension ?
What do you suggest and do you have any insight on how I might do that ?
Thank you for your help!
LordDante123
damned
January 4th, 2025, 22:08
You will need to crack open the ruleset and look at the code and then write an extension.
Mike Serfass
January 5th, 2025, 07:05
Are you looking for something like in this image? It's highlighted in green boxes and it's called region rather than location.
63137
LordDante123
January 5th, 2025, 17:53
You will need to crack open the ruleset and look at the code and then write an extension.
Thanks, I will look into it.
Are you looking for something like in this image? It's highlighted in green boxes and it's called region rather than location.
63137
Yes, that would be great. I only have Toughness, Type and Wilcard in the search and Type in the main tab of the NPC sheet.
Doswelk
January 11th, 2025, 07:50
Not all modules will have data there, until Savage Pathfinder came along (and I got Ikael to update the SWD/SWADE/SWPF Base code) that field was hidden and used to contain NPC I think it was used by the combat tracker, that said being able to search that field (assuming it has been filled out) is a brilliant idea, +1 from me!
LordDante123
January 12th, 2025, 11:57
Hey there,
After some search in the ruleset, I think I've found most of the stuff if not all.
So I guess I now need to make :
an extension to add another field
a script based on the one I found
and register them in some lua file or something
Thank you for your help.
Here is the file and code I've found :
SavageWorlds.pak\campaign\record_npc_xml
<!--
Please see the license.html file included with this distribution for
attribution and copyright information.
-->
<root>
<windowclass name="npc" copy="record_window_tabbed">
<sizelimits>
<minimum width="365" height="440"/>
</sizelimits>
<minimize>minimized_npc</minimize>
<gmexport>npc</gmexport>
<tab>
<name>main</name>
<resource>tab_main</resource>
<class>npc_main</class>
<embed/>
</tab>
<tab merge="add">
<name>traits</name>
<resource>tab_traits</resource>
<class>npc_traits</class>
<embed/>
<activate/>
</tab>
<tab merge="add">
<name>combat</name>
<resource>tab_combat</resource>
<class>npc_combat</class>
<embed/>
</tab>
<tab merge="add">
<name>pictures</name>
<resource>tab_pictures</resource>
<class>record_content_pictures</class>
<embed/>
</tab>
<script> function onInit() super.onInit(); if Session.IsHost then NPCManagerSW.initializeAttackTypes(getDatabaseNode ()); NPCManagerSW.initializePowers(getDatabaseNode()); end self.onIDChanged(); end function onIDChanged() self.onLockChanged(); if not Session.IsHost then local bID = LibraryData.getIDState("npc", getDatabaseNode(), true); tabs.setVisible(bID); end end </script>
</windowclass>
<windowclass name="npc_header" merge="join">
<script> function update() super.update(); local nodeRecord = getDatabaseNode(); local bReadOnly = WindowManager.getReadOnlyState(nodeRecord); wildcardtool.update(bReadOnly); if not Session.IsHost then local bID = LibraryData.getIDState("npc", nodeRecord); wildcardtool.setVisible(bID); end end </script>
<sheetdata>
<hs name="wilddie"/>
<hn name="wildcard"/>
<record_wildcardtool name="wildcardtool" insertbefore="name"/>
</sheetdata>
</windowclass>
<windowclass name="npc_main">
<script file="campaign/scripts/npc_main.lua"/>
<sheetdata>
<anchor_column name="columnanchor"/>
<label_column name="type_label">
<static textres="npc_label_type"/>
</label_column>
<string_columnh name="type">
<delaykeyupdate/>
</string_columnh>
<label_column name="conviction_label">
<static textres="npc_label_conviction"/>
<tooltip textres="npc_toolip_conviction"/>
</label_column>
<npc_conviction name="conviction"/>
<line_column name="divider"/>
<ft_columnh name="text">
<empty textres="ft_empty"/>
</ft_columnh>
<header_column_sw name="gear_header">
<static textres="npc_gear"/>
</header_column_sw>
<npc_gear name="gear"/>
</sheetdata>
</windowclass>
SavageWorlds.pak\campaign\scripts\main_npc.lua
--
-- Please see the license.html file included with this distribution for
-- attribution and copyright information.
--
function onInit()
OptionsManager.registerCallback("CONV", update)
self.update()
end
function onClose()
OptionsManager.unregisterCallback("CONV", update)
end
function VisDataCleared()
self.update()
end
function InvisDataAdded()
self.update()
end
function update()
local nodeRecord = getDatabaseNode()
local bReadOnly = WindowManager.getReadOnlyState(nodeRecord)
local bID = LibraryData.getIDState("npc", nodeRecord)
local bHideConviction = not OptionsManager.isOption("CONV", "on")
local bSection1 = false
if type.update(bReadOnly) then bSection1 = true end
if conviction.update(bReadOnly, bHideConviction) then bSection1 = true end
divider.setVisible(bSection1)
text.update(bReadOnly)
gear.update(bReadOnly)
end
function onDrop(x, y, draginfo)
if draginfo.isType("shortcut") then
local sClass = draginfo.getShortcutData()
if ItemManager2.getItemType(sClass) then
gear.onDrop(x, y, draginfo)
end
end
end
Mike Serfass
January 12th, 2025, 16:17
Give me a day or two and I'll put an extension on the Forge for this.
LordDante123
January 13th, 2025, 13:00
Give me a day or two and I'll put an extension on the Forge for this.
That would be awesome!
My guess is to add only one new field like the "Type" in the NPC sheet and the search like "Type" in the NPC module.
Would be amazing to know if we need to duplicate the code and adapt it if we want more fields.
For my need I will be using it to add several fields so I guess it would be to duplicate the code and not make several extension in terms of performance.
Sincerely,
LordDante123
Mike Serfass
January 15th, 2025, 23:49
I created the extension. It's pending approval in the Forge. I'll update the thread with the store link when it's available.
[Extension] NPC Region (https://www.fantasygrounds.com/forums/showthread.php?83922-Extension-NPC-Region&p=735052#post735052)
LordDante123
January 17th, 2025, 12:09
I created the extension. It's pending approval in the Forge. I'll update the thread with the store link when it's available.
[Extension] NPC Region (https://www.fantasygrounds.com/forums/showthread.php?83922-Extension-NPC-Region&p=735052#post735052)
Amazing! I'll try to look into it this week-end.
Thank you for your work!
LordDante123
February 25th, 2025, 18:44
Amazing job Mike, it works like magic!
I was even able to duplicate the region field and features.
Now I just want to add those 2 functions to the Region field in the filter of the NPC view that I found in the file and folder \scripts\data_library_sw.lua of SWADE.
Does anyone knows if I can just add those 2 functions at the start of the of the file and folder \scripts\npc_tweaks.lua of the extension from Mike that already created a simple filter ?
I have changed the line regarding the filter to a fGetValue like the Type filter and showed it at the end of the code.
I believe that I will need to rename some contstants and variables in the functions but I'm not sure which is which.
-- Returns a table containing all the individual NPC types from the campaign and all loaded modules.
function loadNpcTypes()
local npcTypes = {}
-- parse out words to list individual Types in Type combo box
local parseWords = function(v)
local t = DB.getValue(v, "type", "")
-- get individual words parsed by spaces, commas, slashes, parenthesis
for w in t:gmatch(typeParseRegEx) do
npcTypes[w:upper()] = w:lower()
end
end
-- load unique NPC types from current game
for _,v in ipairs(DB.getChildrenGlobal("npc")) do
parseWords(v)
end
-- load unique NPC types from loaded modules
for _,v in ipairs(DB.getChildrenGlobal("reference.npcs")) do
parseWords(v)
end
return npcTypes
end
-- This controls what displays in the Types combo box. The combo box does its own sorting, so don't bother sorting the npcTypes table.
-- This also affects the filter results.
function getNPCTypeValue(vNode)
local npcTypes = loadNpcTypes()
local found = false
local foundTypes = {}
local nodeNpcType = DB.getValue(vNode, "type", "")
-- must use pairs, not ipairs
for _,npcType in pairs(npcTypes) do
local matches = {}
-- get individual words parsed by spaces, commas, slashes, parenthesis
for t in nodeNpcType:gmatch(typeParseRegEx) do
table.insert(matches, t:lower())
end
-- make exact match on one of the individual words
for _,m in ipairs(matches) do
if m == npcType then
found = true
table.insert(foundTypes, npcType)
end
end
end
if found then
return foundTypes
else
return ""
end
end
-- add region filtering
aRecordOverride.aCustomFilters["Region"] = { sField = "region", fGetValue = getNPCRegionValue } }
Mike Serfass
February 27th, 2025, 05:53
You probably don't need that code.
You probably want the way it works in the NPC region extension.
The loadNPCTypes and getNPCTypeValue functions are unusual because Type accepts a delimited list of values that can contain subtypes in parenthesis and half types with slashes.
Type is so complex because it's duplicating D&D behavior / values.
I recommend against using that kind of complexity.
What is the field you're adding, and what kind of values does it contain?
Powered by vBulletin® Version 4.2.1 Copyright © 2026 vBulletin Solutions, Inc. All rights reserved.