PDA

View Full Version : Putting NPC Attributes into the Combat Tracker Entry



LordEntrails
October 6th, 2025, 04:38
So I have the attribute "bodypoints" for each NPC. I want this to populate the Combat Tracker Entry field "totalbodypoints".
I create a script file manager_combat2.lua and added the following code to it:



function onInit()
CombatRecordManager.setRecordTypePostAddCallback("npc", CombatManager2.onNPCPostAdd(tCustom));
end




function onNPCPostAdd(tCustom)
-- Parameter validation
if not tCustom.nodeRecord or not tCustom.nodeCT then
return;
end


-- Set current body points
local nBP = DB.getValue(tCustom.nodeRecord, "bodypoints", 0);
DB.setValue(tCustom.nodeCT, "bptotal", "number", nBP);


And I'm getting the error:


[ERROR] Script execution error: [string "FrontierSpace:scripts/manager_combat2.lua"]:16: attempt to call field 'onNPCPostAdd' (a nil value)
stack traceback:
[string "FrontierSpace:scripts/manager_combat2.lua"]:16: in function <[string "FrontierSpace:scripts/manager_combat2.lua"]:1>



I've tried the variations that I can think of but am stuck. I'm thinking it's either something to do with "tCustom" but I can't find it's source, a find in file gives me over 600 hits. Or with the CombatManager2, but I am lost on that one too.


Thanks in advance for any help!

superteddy57
October 6th, 2025, 05:12
The code after '-- Set current body points' is outside of the function

jkeller
October 6th, 2025, 14:22
Right, try this:



function onNPCPostAdd(tCustom)
-- Parameter validation
if not tCustom.nodeRecord or not tCustom.nodeCT then
return;
end

-- Set current body points
local nBP = DB.getValue(tCustom.nodeRecord, "bodypoints", 0);
DB.setValue(tCustom.nodeCT, "bptotal", "number", nBP);
end

LordEntrails
October 6th, 2025, 15:33
Doh! Thanks both of you. I'll try and let you know if I have any issues.

LordEntrails
October 6th, 2025, 15:57
That wasn't it. I'm still getting this error (in a new campaign, no extensions, FronteirSpace Ruleset, CoreRPG child)

[ERROR] Script execution error: [string "FrontierSpace:scripts/manager_combat2.lua"]:16: attempt to call field 'onNPCPostAdd' (a nil value)

superteddy57
October 6th, 2025, 15:59
Is the script name called CombatManager2?

LordEntrails
October 6th, 2025, 17:04
The script file is called manager_combat2.lua.
Which CoreRPG base.xml has the line

<script name="CombatManager2" file="scripts/manager_combat2.lua" />

So I assumed using that (like 5E does) would work. But, ...
My ruleset base.xml has:

<script name="manager_combat2" file="scripts/manager_combat2.lua" />

So maybe that's overwriting. So, I changed my onInit to

CombatRecordManager.setRecordTypePostAddCallback("npc", manager_combat2.onNPCPostAdd(tCustom));

But now I get a different error:

[ERROR] Script execution error: [string "FrontierSpace:scripts/manager_combat2.lua"]:35: attempt to index local 'tCustom' (a nil value)

Which point back to tCustom, but I don't see anything in 5e's manager_combat2 that defines tCustom or anywhere in 5E or Core where tCustom is defined. If it needs to be...

I'm guess guessing at this point :O

jkeller
October 6th, 2025, 17:18
Can you post manager_combat2.lua with line numbers?

LordEntrails
October 6th, 2025, 17:24
Here's the whole file. Line 35 is

if not tCustom.nodeRecord or not tCustom.nodeCT then

And I'll try pasting it here in its full form, but that won't have line numbers. But zip is at the bottom.


function onInit()
-- From 5E
-- CombatManager.setCustomRoundStart(CombatManager2.o nRoundStart);
-- CombatManager.setCustomTurnStart(CombatManager2.on TurnStart);
-- CombatManager.setCustomTurnEnd(CombatManager2.onTu rnEnd);
-- CombatManager.setCustomCombatReset(CombatManager2. resetInit);
-- CombatManager.setCustomInitSwapPlayerAllow(CombatM anager2.isInitSwapPlayerAllowed);
--
-- CombatRecordManager.addStandardVehicleCombatRecord Type();
--
-- ActorCommonManager.setDefaultSpaceReachFromActorSi zeKey("5E");
-- ActorCommonManager.setRecordTypeSpaceReachCallback ("charsheet", ActorCommonManager.getSpaceReachFromSizeFieldCore) ;
-- ActorCommonManager.setRecordTypeSpaceReachCallback ("npc", ActorCommonManager.getSpaceReachFromSizeFieldCore) ;
-- ActorCommonManager.setRecordTypeSpaceReachCallback ("vehicle", ActorCommonManager.getSpaceReachFromSizeFieldCore) ;
-- NEXT LINE
CombatRecordManager.setRecordTypePostAddCallback("npc", CombatManager2.onNPCPostAdd(tCustom));
-- CombatRecordManager.setRecordTypePostAddCallback("vehicle", CombatManager2.onVehiclePostAdd);
end


--function onNPCPostAdd(tCustom)
-- -- Parameter validation
-- if not tCustom.nodeRecord or not tCustom.nodeCT then
-- return;
-- end
--
-- -- Set current body points
-- local nBP = DB.getValue(tCustom.nodeRecord, "bodypoints", 0);
-- DB.setValue(tCustom.nodeCT, "bptotal", "number", nBP);
--
-- -- Roll initiative and sort
---- CombatRecordManager.handleCombatAddInitDnD(tCustom );
--end
function onNPCPostAdd(tCustom)
-- Parameter validation
if not tCustom.nodeRecord or not tCustom.nodeCT then
return;
end


-- Set current body points
local nBP = DB.getValue(tCustom.nodeRecord, "bodypoints", 0);
DB.setValue(tCustom.nodeCT, "bptotal", "number", nBP);
end

superteddy57
October 6th, 2025, 17:29
The script declaration needs to be match the script call.

You have CombatManager2.onNPCPostAdd, so the script name needs to match CombatManager2

LordEntrails
October 6th, 2025, 17:34
The script declaration needs to be match the script call.

You have CombatManager2.onNPCPostAdd, so the script name needs to match CombatManager2
This?

CombatRecordManager.setRecordTypePostAddCallback("npc", manager_combat2.onNPCPostAdd(tCustom));

I updated that, the post above is extracted from the ruleset before I made that change.

LordEntrails
October 6th, 2025, 17:39
Here's the latest for reference:


function onInit()
-- From 5E
-- CombatManager.setCustomRoundStart(CombatManager2.o nRoundStart);
-- CombatManager.setCustomTurnStart(CombatManager2.on TurnStart);
-- CombatManager.setCustomTurnEnd(CombatManager2.onTu rnEnd);
-- CombatManager.setCustomCombatReset(CombatManager2. resetInit);
-- CombatManager.setCustomInitSwapPlayerAllow(CombatM anager2.isInitSwapPlayerAllowed);
--
-- CombatRecordManager.addStandardVehicleCombatRecord Type();
--
-- ActorCommonManager.setDefaultSpaceReachFromActorSi zeKey("5E");
-- ActorCommonManager.setRecordTypeSpaceReachCallback ("charsheet", ActorCommonManager.getSpaceReachFromSizeFieldCore) ;
-- ActorCommonManager.setRecordTypeSpaceReachCallback ("npc", ActorCommonManager.getSpaceReachFromSizeFieldCore) ;
-- ActorCommonManager.setRecordTypeSpaceReachCallback ("vehicle", ActorCommonManager.getSpaceReachFromSizeFieldCore) ;
-- NEXT LINE
CombatRecordManager.setRecordTypePostAddCallback("npc", manager_combat2.onNPCPostAdd(tCustom));
-- CombatRecordManager.setRecordTypePostAddCallback("vehicle", CombatManager2.onVehiclePostAdd);
end


--function onNPCPostAdd(tCustom)
-- -- Parameter validation
-- if not tCustom.nodeRecord or not tCustom.nodeCT then
-- return;
-- end
--
-- -- Set current body points
-- local nBP = DB.getValue(tCustom.nodeRecord, "bodypoints", 0);
-- DB.setValue(tCustom.nodeCT, "bptotal", "number", nBP);
--
-- -- Roll initiative and sort
---- CombatRecordManager.handleCombatAddInitDnD(tCustom );
--end
function onNPCPostAdd(tCustom)
-- Parameter validation
if not tCustom.nodeRecord or not tCustom.nodeCT then
return;
end


-- Set current body points
local nBP = DB.getValue(tCustom.nodeRecord, "bodypoints", 0);
DB.setValue(tCustom.nodeCT, "bptotal", "number", nBP);
end

jkeller
October 6th, 2025, 17:41
Yeah, I would add a trace to make sure you're running the code you think you're running.

You can call:
print("some text"):
That will write to the console, which you can open using the /console command in the chat window.

Or you can call:



postMessage("some message");

function postMessage(text)
local message = {}
message.text = text
Comm.addChatMessage(message);
end


That will write to the chat window.

If you're running the script you posted, line 35 is:

if not tCustom.nodeRecord or not tCustom.nodeCT then

I guess you could add "if not tCustom or ..." in case tCustom is nil.

superteddy57
October 6th, 2025, 17:48
Yeah, I would add a trace to make sure you're running the code you think you're running.

You can call:
print("some text"):
That will write to the console, which you can open using the /console command in the chat window.

Or you can call:



postMessage("some message");

function postMessage(text)
local message = {}
message.text = text
Comm.addChatMessage(message);
end


That will write to the chat window.

If you're running the script you posted, line 35 is:

if not tCustom.nodeRecord or not tCustom.nodeCT then

I guess you could add "if not tCustom or ..." in case tCustom is nil.

You can do Debug.chat() to send to chat or Debug.console() to send to the console window. You don't need that function for debugging purposes.

jkeller
October 6th, 2025, 18:36
You can do Debug.chat() to send to chat or Debug.console() to send to the console window. You don't need that function for debugging purposes.

Thanks! I'm fairly new to FG LUA; appreciate any tips.

Do those support icons? I didn't see any obvious examples using them in the code I have.

I didn't include it in my example above (to keep it simple), but I usually show an icon in the chat window, so I can easily find it.



function postMessage(text)
local message = {}
message.text = text
message.icon = "someIcon"
Comm.addChatMessage(message);
end

superteddy57
October 6th, 2025, 18:39
No, but I just add asterisks or other delimiters to help with highlighting the break points.

LordEntrails
October 6th, 2025, 20:11
There are a bunch of Debug examples in this thread: Debug examples (https://www.fantasygrounds.com/forums/showthread.php?67603-Debug-examples)

LordEntrails
October 7th, 2025, 01:27
I'm still stumped and quite confused. I've added Debug statements so my manager)combat2.lua now looks like this:


function onInit()
Debug.console("DEBUG Init")
CombatRecordManager.setRecordTypePostAddCallback("npc", manager_combat2.onNPCPostAdd(tCustom));
Debug.console("DEBUG Init DONE")
end


function onNPCPostAdd(tCustom)
-- Parameter validation
Debug.console("DEBUG Validation")
-- Debug.console("DEBUG" getValue(tCustom))
if not tCustom.nodeRecord or not tCustom.nodeCT then
return;
end
-- Set current body points
Debug.console("DEBUG setValue")
-- Debug.console(getValue(bodypoints))
local nBP = DB.getValue(tCustom.nodeRecord, "bodypoints", 0);
DB.setValue(tCustom.nodeCT, "bptotal", "number", nBP);
end

And my console.log shows this

[10/6/2025 5:21:29 PM] MEASURE: LOAD - PART 1 - 17.3424459
[10/6/2025 5:21:30 PM] s'DEBUG Init'
[10/6/2025 5:21:30 PM] s'DEBUG Validation'
[10/6/2025 5:21:30 PM] [ERROR] Script execution error: [string "FrontierSpace:scripts/manager_combat2.lua"]:11: attempt to index local 'tCustom' (a nil value)
[10/6/2025 5:21:31 PM] [WARNING] window: Anchored static width ignored for control (adv_strength) in windowclass (sub_npc_major)
(warning is only included to show you that their is nothing relevant after)

Notice how several of the Debug staetments are missing in the console? What's happening here?
I've also been unable to get the Debug.console(getValue()) statements to work, so any help there would be appricated. But I think this is because those values don't exist until an NPC is added to the combat tracker.

Moon Wizard
October 7th, 2025, 03:59
Are you sure you're not changing CombatManager behaviors elsewhere? Because this is exactly how it is set up in 5E, 4E, 3.5E, and PFRPG; and they are not throwing nil values for tCustom.

Regards,
JPG

LordEntrails
October 7th, 2025, 05:39
No, I'm not sure...
Where should I look? Where does tCustom get defined/declared/instantiated?

Random other speculation and in fo that may help:
I don't have a manager_combat.lua (just manager_combat2.lua).

I did fully replace the combat tracker (which is on my list to revert where I can), so am I missing something from one of those files? Could it be something missing in my GameSystem.lua or dataLibrary.lua? Here's my LibraryData declaration;

LibraryData.aRecords["npc"] = {
aDataMap = { "npc", "reference.npcs" },
sListDisplayClass = "masterindexitem_id",
aGMEditButtons = { "button_add_npc_import" },
aCustom = {
tWindowMenu = { ["left"] = { "chat_speak" } },
},
sGMExportTag = "npc",
aCustomFilters = {
["Type"] = { sField = "npc_type" } },
tOptions = {
bExport = true,
bID = true,
bPicture = true,
bToken = true,
bCustomDie = true,
bInventory = true,
},
}

Moon Wizard
October 7th, 2025, 06:19
tCustom is passed around inside the CombatManager code that automatically handles the addition of combatants to the combat tracker.

Are you overriding any CombatManager functions directly? (i.e. CombatManager.functionname = yourfunction)

Perhaps some of the calls aren't hooked up the same with your combat tracker override?

Regards,
JPG

RosenMcStern
October 7th, 2025, 08:58
Debug.console("DEBUG Init")
CombatRecordManager.setRecordTypePostAddCallback("npc", manager_combat2.onNPCPostAdd(tCustom));
Debug.console("DEBUG Init DONE")


It happens to me all the time, too :)

You are passing a function call to setRecordTypePostAddCallback, instead of a function reference. Basically, what you see is your function being called, and not installed as a callback, during initialization. THe fact that the function call happens beforDEBUG Init DONE is printed confirms this. And since tCustom does not exist at that particular point of execution, it ka-booms. The error happens long before any NPC is added to the combat tracker.

Remove the (tCustom) after onNPCPostAdd and try again. Something should definitely change.

Also, may I humbly suggest that you use Debug.print() instead of Debug.console() ? It avoids those ugly s and quotes being printed. Just aesthetics, it's functionally equivalent.

jkeller
October 7th, 2025, 13:56
Rosen is right.

"tCustom" is not defined in your init function (so it's nil).

So try this I thinik:

CombatRecordManager.setRecordTypePostAddCallback(" npc", manager_combat2.onNPCPostAdd);

Moon Wizard
October 7th, 2025, 18:28
Good catch, guys. I didn't catch that.

Thanks,
JPG