-
February 23rd, 2021, 17:01 #1
MoreCore: When rolling an attack on multiple targets
I added 2 different types of monsters to the CT.
Then I added 2 PCs to test their class bonuses.
The PC (elf) when attacking a goblin or Lich received a +1 to hit. With proper text.
When I try to target both together, it does not use the racial vs target mods.
I then put a printf on the method (onMod) and see it is called only one time and without a target string.
What would I look at to address this? Is it a limitation on the MoreCore roller system? Is there a simple example on multi-target targeting where each target is looked at that I can see if I can make a change to handle this?
Screen Shot 2021-02-23 at 11.57.27 AM.png
Above is the chat log.
The logging on the call to onMod(...):
Runtime Notice: s'onModHandler' | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00003', s'sCTNode' = s'combattracker.list.id-00002', s'sName' = s'Pete' } | nil | { s'aDice' = { #1 = s'd20' }, s'nMod' = #0, s'sType' = s'swthac0', s'aAttributes' = { }, s'sDesc' = s'[THAC0:19]' }
-
February 24th, 2021, 02:20 #2
OK - I loaded up manager_custom_die in MoreCore to trace through and see if there is either a hook or something I can modify so it works as I want/expect.
Then, decided - first trace existing code to check ALL calls and arguments vs just the listing of single mod call without target. So I did and I think I can get around this much easier. And for all I know, it was always intended to be split up this way that I will change to.
This is what I saw (first set is single attack, second is 2 targets and the elf should get a +1 (str) and another +1 for different reasons on each:
Code:Runtime Notice: s'performAction' | nil | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00002', s'sCTNode' = s'combattracker.list.id-00003', s'sName' = s'Joe' } | s'| Melee Attack' Runtime Notice: s'onModHandler' | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00002', s'sCTNode' = s'combattracker.list.id-00003', s'sName' = s'Joe' } | { s'sType' = s'npc', s'sCreatureNode' = s'combattracker.list.id-00007', s'sCTNode' = s'combattracker.list.id-00007', s'sName' = s'Acerak' } | { s'aDice' = { #1 = s'd20' }, s'nMod' = #0, s'sType' = s'swthac0', s'aAttributes' = { }, s'sDesc' = s'[THAC0:19]' } Runtime Notice: s'onResultHandler' | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00002', s'sCTNode' = s'combattracker.list.id-00003', s'sName' = s'Joe' } | { s'sType' = s'npc', s'sCreatureNode' = s'combattracker.list.id-00007', s'sCTNode' = s'combattracker.list.id-00007', s'sName' = s'Acerak' } | { s'aDice' = { #1 = { s'result' = #9, s'type' = s'd20' } }, s'nMod' = #1, s'sType' = s'swthac0', s'aAttributes' = s'', s'bSecret' = bFALSE, s'sDesc' = s'[THAC0:19][Str:1]' } Runtime Notice: s'performAction' | nil | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00003', s'sCTNode' = s'combattracker.list.id-00002', s'sName' = s'Pete' } | s'| Melee Attack' Runtime Notice: s'onModHandler' | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00003', s'sCTNode' = s'combattracker.list.id-00002', s'sName' = s'Pete' } | nil | { s'aDice' = { #1 = s'd20' }, s'nMod' = #0, s'sType' = s'swthac0', s'aAttributes' = { }, s'sDesc' = s'[THAC0:19]' } Runtime Notice: s'onResultHandler' | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00003', s'sCTNode' = s'combattracker.list.id-00002', s'sName' = s'Pete' } | { s'nOrder' = #1, s'sType' = s'npc', s'sName' = s'Acerak', s'sCTNode' = s'combattracker.list.id-00007', s'sCreatureNode' = s'combattracker.list.id-00007' } | { s'aDice' = { #1 = { s'result' = #18, s'type' = s'd20' } }, s'nMod' = #1, s'sType' = s'swthac0', s'aAttributes' = s'', s'bSecret' = bFALSE, s'sDesc' = s'[THAC0:19][Str:1]' } Runtime Notice: s'onResultHandler' | { s'sType' = s'pc', s'sCreatureNode' = s'charsheet.id-00003', s'sCTNode' = s'combattracker.list.id-00002', s'sName' = s'Pete' } | { s'nOrder' = #2, s'sType' = s'npc', s'sName' = s'Goblin 3', s'sCTNode' = s'combattracker.list.id-00006', s'sCreatureNode' = s'combattracker.list.id-00006' } | { s'bSecret' = bFALSE, s'aDice' = { #1 = { s'result' = #18, s'type' = s'd20' } }, s'nMod' = #1, s'aTotal' = #19, s'sType' = s'swthac0', s'aAttributes' = s'', s'aHitResult' = s'-> [at Acerak] [HIT]', s'sDesc' = s'[THAC0:19][Str:1]' }
Now, I think I will do it so that onMod ONLY adds in "inherent" mods - like STR/DEX and racial based on SOURCE's attack method.
The onResults gets both source and target so there I can see if I can prior to creatMessage I can look at the source/target and modify rRoll accordingly (ie individually for this target)
Code:function onResultHandler(rSource, rTarget, rRoll) Debug.console("onResultHandler", rSource, rTarget, rRoll) ----> I would call my mod-appliers here looking at source/taget. local rMessage = ActionsManager.createActionMessage(rSource, rRoll) rRoll = getDiceResults(rRoll, rTarget); rMessage = createChatMessage(rSource, rRoll, rTarget) rMessage.type = sCmd Comm.deliverChatMessage(rMessage) end
I'll update once I can try it out.
-
February 24th, 2021, 05:43 #3
Was a good idea... kinda... but forgot changing rRoll will carry to the next results
Screen Shot 2021-02-24 at 12.42.22 AM.png
I guess I need to look at how it is called after all for ideas.
-
February 24th, 2021, 14:55 #4
OK prior to breakfast I remembered seeing a call to a deep copy and that gave me a solution to try. It worked. So at the start of the results handler I use not the roll passed in but a new copy of it instead.
This will happen for ALL rolls so I will go back and add a flag to indicate multi-targets and copy only when the flag was "true" because I assume a deep copy would be a pointless performance hit. But I wanted to test first and now I am starting my "Day job"
Code:function onResultHandler(rSource, rTarget, rRoll) Debug.console("onResultHandler", rSource, rTarget, rRoll) local rNewRoll = UtilityManager.copyDeep(rRoll); local nodeSourceActor = ActorManager.getCreatureNode(rSource) local sType, nodeTargetActor = ActorManager.getTypeAndNode(rTarget) if nodeSourceActor and nodeTargetActor and ActorManager.isPC(rSource) then ...
Screen Shot 2021-02-24 at 9.42.04 AM.png
Example of 3 targets one with no special bonus and one each with different applicable bonuses.
EDIT: I still feel I am missing something though, because of the comments in both CoreRPG and MoreCore on this:
Code:local bModStackUsed = false; if bMultiTarget then if vTarget and #vTarget == 1 then bModStackUsed = applyModifiers(rSource, vTarget[1], rNewRoll); else -- Only apply non-target specific modifiers before roll bModStackUsed = applyModifiers(rSource, nil, rNewRoll); end else bModStackUsed = applyModifiers(rSource, vTarget, rNewRoll); end
Last edited by Varsuuk; February 24th, 2021 at 15:10.
-
February 24th, 2021, 15:45 #5
Please feel free to offer suggestions
I could not determine a way to detect that the action is aimed at multiple targets because those calls are not exposed to me.
Calls like Core/MoreCore's actionRoll(rSource, vTarget, rRolls) and applyModifiersAndRoll(...) both can check if multi target.
If there is no existing way to determine if something is multi target and we are supposed to calculate per target mods in the results handler (not sure assumptions are right) - maybe the code in Core/More can add a flag to the rRoll object?
Code:function applyModifiersAndRoll(rSource, vTarget, bMultiTarget, rRoll) local rNewRoll = UtilityManager.copyDeep(rRoll); local bModStackUsed = false; if bMultiTarget then if vTarget and #vTarget == 1 then bModStackUsed = applyModifiers(rSource, vTarget[1], rNewRoll); else rNewRoll.bIsMultiTarget = 'true' -- Only apply non-target specific modifiers before roll bModStackUsed = applyModifiers(rSource, nil, rNewRoll); end else bModStackUsed = applyModifiers(rSource, vTarget, rNewRoll); end roll(rSource, vTarget, rNewRoll, bMultiTarget); return bModStackUsed; end
If it were done IN those 2, then it would rock
I used a string because of the request to use string->string mapping in Action manager. Also, I didn't add it everywhere (with = 'false') which is fine if you only check against "== 'true'" like I did in my code knowing I wrote it. But in Core, I'd change it so would set to "false" initially or otherwise so user could check however they wished.
Still works as before with my isMultiTarget (local) check:
Screen Shot 2021-02-24 at 10.33.07 AM.png
Code:Debug.console("onModHandler", rSource, rTarget, rRoll) local nodeSourceActor = ActorManager.getCreatureNode(rSource) local sType, nodeTargetActor = ActorManager.getTypeAndNode(rTarget) if nodeSourceActor then local sModString = "" if rTarget == nil then -- I do not know how to detect the difference between NO target -- (rolling on sheet) and multi-target (passes nil here). So I will assume -- we are a multi-target and cause perhaps one unneeded deep copy in those -- cases. I chose to do it prior to checking if this is a PC because I intend -- on allowing for NPCs to go through similar trait/race checking in future. rRoll.bIsMultiTarget = 'true' end if ActorManager.isPC(rSource) then ... ... function onResultHandler(rSource, rTarget, rRoll) local rNewRoll = rRoll if rRoll.bIsMultiTarget == 'true' then -- If this is a multi-target roll, the modifications warranted based on -- target can vary. Use a different rRoll object for each in case. rNewRoll = UtilityManager.copyDeep(rRoll); end local nodeSourceActor = ActorManager.getCreatureNode(rSource) local sType, nodeTargetActor = ActorManager.getTypeAndNode(rTarget) if nodeSourceActor and nodeTargetActor and ActorManager.isPC(rSource) then local sRaceID = Character.getRaceID(nodeSourceActor) local sModDesc, nModToHit = Character.getToHitBonus(nodeSourceActor, rNewRoll.bIsMeleeAttack == 'true') if nModToHit ~= 0 then rNewRoll.nMod = rNewRoll.nMod + nModToHit rNewRoll.sDesc = rNewRoll.sDesc .. "[" .. sModDesc .. ":" .. tostring(nModToHit) .. "]" end aModTable = RaceCommon.race_attack_bonus_to_hit_for_target_tag[sRaceID] ModManager.applyVsActor(aModTable, nodeTargetActor, rNewRoll, sModString) end ...
Thread Information
Users Browsing this Thread
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks