FG Spreadshirt Swag
  1. #1
    Varsuuk's Avatar
    Join Date
    Dec 2015
    Location
    New York
    Posts
    2,075

    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]' }

  2. #2
    Varsuuk's Avatar
    Join Date
    Dec 2015
    Location
    New York
    Posts
    2,075
    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]' }
    Originally, I setup the roll then on onMod I would check source/target for str or dex depending on melee/missle, racial with weapons, racial against target etc modes.
    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.

  3. #3
    Varsuuk's Avatar
    Join Date
    Dec 2015
    Location
    New York
    Posts
    2,075
    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.

  4. #4
    Varsuuk's Avatar
    Join Date
    Dec 2015
    Location
    New York
    Posts
    2,075
    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
    The above bold implies you should add AFTER roll, I guess, and if so - I want to make sure I am doing it in the right "after" spot, if you will.
    Last edited by Varsuuk; February 24th, 2021 at 15:10.

  5. #5
    Varsuuk's Avatar
    Join Date
    Dec 2015
    Location
    New York
    Posts
    2,075
    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
    Not doing this because I have a (more costly) workaround and maintaining my replacement code vs Core / MoreCore would be a headache for just one method.
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
STAR TREK 2d20

Log in

Log in