ToME: the Tales of Maj'Eyal

Everything about ToME
It is currently Sat Sep 22, 2018 11:47 am

All times are UTC




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Sun Feb 11, 2018 2:52 pm 
Offline
Uruivellas

Joined: Mon Sep 21, 2015 8:45 pm
Posts: 651
Location: Middle of Nowhere
I want all damage to a target to be split into a percentage of its original damage as its original type, with the remaining damage converted to a specific damage type. The hook I'm using is kind of working, but the instance of damage as the original type is getting processed twice, resulting in two hits for the reduced amount, and I can't figure out why. The converted damage is behaving properly, hitting the target for the correct value and only hitting once. Full code below:

Code:
--for reference, this is designed to convert a percentage of all incoming damage into Physical, if the target has the Blocking effect and has gained a specific attribute from a new talent I am working on.

class:bindHook("DamageProjector:base", function(self, data)
   if data.state.no_sw_recurse then return data end
   local target = game.level.map(data.x, data.y, Map.ACTOR)
   if not target then return data end
   if target.dead then return data end
   if not target:attr("steel_wall") then return data end
   local eff = target:hasEffect(target.EFF_BLOCKING)
   if not eff then return data end
   local source_talent = data.src.__projecting_for and data.src.__projecting_for.project_type and (data.src.__projecting_for.project_type.talent_id or data.src.__projecting_for.project_type.talent) and data.src.getTalentFromId and data.src:getTalentFromId(data.src.__projecting_for.project_type.talent or data.src.__projecting_for.project_type.talent_id)

   
   if data.src and data.src.__CLASSNAME ~= "mod.class.Grid" and eff and data.type ~= DamageType.PHYSICAL then
      data.state.no_sw_recurse = true
      local convertedDam = data.dam * target.steel_wall
      if convertedDam < 1 then return data end
      local baseDam = data.dam - convertedDam
      local old_sw = target.steel_wall
      target.steel_wall = nil
      data.dam = 0
      data.dam = data.dam + DamageType:get(data.type).projector(data.src, data.x, data.y, data.type, baseDam, data.state)
      data.dam = data.dam + DamageType:get(DamageType.PHYSICAL).projector(data.src, data.x, data.y, DamageType.PHYSICAL, convertedDam, data.state)
      target.steel_wall = old_sw
   end
   
   return data
end)


Here's the log of my test character facing the Forest Troll Hedge Wizard, so you can get an idea of the output I'm getting.

Code:
[LOG]   Forest Troll Hedge-Wizard casts Flame.
[SPELL CRIT %]   1.6
Adding entity   8388   after   8380
[LOG]   
[LOG]   Player uses Block.
[PROJECTOR] starting dam   FIRE   17.53178929211
[PROJECTOR] starting dam   FIRE   12.066451874129
[PROJECTOR] after difficulty dam   12.066451874129
[PROJECTOR] after DamageType increase dam   12.790438986576
[PROJECTOR] res   0   1    on dam   12.790438986576
[PROJECTOR] after resists dam   12.790438986576
[PROJECTOR] after flat damage armor   12.790438986576
[PROJECTOR] final dam after static checks   12.790438986576
[PROJECTOR] final dam after hooks and callbacks   12.790438986576
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[PROJECTOR] starting dam   PHYSICAL   5.4653374179816
[PROJECTOR] after difficulty dam   5.4653374179816
[PROJECTOR] res   17.810413038841   0.82189586961159    on dam   0
[PROJECTOR] after resists dam   0
[PROJECTOR] final dam after static checks   0
[PROJECTOR] final dam after hooks and callbacks   0
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[PROJECTOR] after difficulty dam   12.790438986576
[PROJECTOR] after DamageType increase dam   13.557865325771
[PROJECTOR] res   0   1    on dam   13.557865325771
[PROJECTOR] after resists dam   13.557865325771
[PROJECTOR] after flat damage armor   13.557865325771
[PROJECTOR] final dam after static checks   13.557865325771
[PROJECTOR] final dam after hooks and callbacks   13.557865325771
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[LOG]   Player is on fire!
Cloned shader 46
[SHADER] setting reset param   tick_start   581065
[LOG]   Forest Troll Hedge-Wizard's Flame hits #fbd578#player#LAST# for #LIGHT_RED#13 fire#LAST#, #aaaaaa#(5 blocked)#LAST#, #aaaaaa#0 physical#LAST#, #LIGHT_RED#14 fire#LAST# (26 total damage).
[PROJECTOR] starting dam   FIRE   5.8439297640368
[PROJECTOR] starting dam   FIRE   4.0221506247096
[PROJECTOR] after difficulty dam   4.0221506247096
[PROJECTOR] after DamageType increase dam   4.2634796621922
[PROJECTOR] res   0   1    on dam   4.2634796621922
[PROJECTOR] after resists dam   4.2634796621922
[PROJECTOR] after flat damage armor   4.2634796621922
[PROJECTOR] final dam after static checks   4.2634796621922
[PROJECTOR] final dam after hooks and callbacks   4.2634796621922
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[PROJECTOR] starting dam   PHYSICAL   1.8217791393272
[PROJECTOR] after difficulty dam   1.8217791393272
[PROJECTOR] res   17.810413038841   0.82189586961159    on dam   0
[PROJECTOR] after resists dam   0
[PROJECTOR] final dam after static checks   0
[PROJECTOR] final dam after hooks and callbacks   0
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[PROJECTOR] after difficulty dam   4.2634796621922
[PROJECTOR] after DamageType increase dam   4.5192884419237
[PROJECTOR] res   0   1    on dam   4.5192884419237
[PROJECTOR] after resists dam   4.5192884419237
[PROJECTOR] after flat damage armor   4.5192884419237
[PROJECTOR] final dam after static checks   4.5192884419237
[PROJECTOR] final dam after hooks and callbacks   4.5192884419237
[ENGINE] Setting requested FPS to 30 (33 ms)
[ENGINE] Setting requested FPS to 30 (33 ms)
[LOG]   
[LOG]   Burning from Forest Troll Hedge-Wizard hits #fbd578#player#LAST# for #LIGHT_RED#4 fire#LAST#, #aaaaaa#(2 blocked)#LAST#, #aaaaaa#0 physical#LAST#, #LIGHT_RED#5 fire#LAST# (9 total damage).



You can see how the damage from Flame and Burning both have the Fire portion returned twice (and slightly increased on the second instance?), but not the portion that get split off into Physical.

I'm very much an amateur at this stuff, and I've hardly touched hooks before this, aside from copying a couple from other folks' work. As far as I can understand it though, it works more or less like a callback. Aside from just being stumped as to how the base damage is getting processed twice, I'm also a little unclear on where exactly the value I return is going to be returned to. That is, there are a few things that fire off in the damage projector before the hook is called; when my hook returns its data, does that start back at the beginning of the damage projector, or is it spit out back into the original loop right after the place where the hook is called? As far as I can tell, it basically gets sent into a new loop, starting from the beginning, because I was getting some recursion before I put a flag into the state to stop it. Unfortunately, this does not shed any light on what's going on for me.

Any help anyone can offer would be greatly appreciated. I spent the entire day yesterday working on this thing to get it to the point where it is now. It's so close to working properly, and yet so not, it's making me crazy.


Top
 Profile  
 
PostPosted: Sun Feb 11, 2018 4:35 pm 
Offline
Uruivellas

Joined: Mon Oct 09, 2006 7:47 pm
Posts: 615
I did something similar for a talent, that if sustained convert x% of damage to a specific type.
I used a callback and it seems to work. And callbacks are easier to maintain if they can be used IMHO.

Here is the code

Code:
   callbackOnTakeDamageBeforeResists = function(self, t, src, x, y, dtype, dam, tmp)
       local ff = self:isTalentActive(t.id)
       local ret, mdam
       ret = {dam = dam}
       if not ff or dtype == DamageType.MIND then
          return ret end
       mdam = ret.dam * t.getMindConvert(self,t)
       ret.dam = ret.dam - mdam
       DamageType:get(DamageType.MIND).projector(src, x, y, DamageType.MIND, {dam=mdam})
       game:delayedLogMessage(self, self, "Mind conversion", "#Source# diverts some damage to its mind!")
       return ret
   end,


The callback is called in the defaultprojector and dam is always a number, but the callback expects a table returned, that explains part of the code.
Otherwise it should be straightforward.


Top
 Profile  
 
PostPosted: Sun Feb 11, 2018 5:24 pm 
Offline
Uruivellas

Joined: Mon Sep 21, 2015 8:45 pm
Posts: 651
Location: Middle of Nowhere
Thanks for the reply!

I changed your callaback to this:

Code:
   callbackOnTakeDamageBeforeResists = function(self, t, src, x, y, dtype, dam, tmp)
      local eff = self:hasEffect(self.EFF_BLOCKING)
      local ret, pdam
      ret = {dam = dam}      
      if eff and self:attr("steel_wall") and dtype ~= DamageType.PHYSICAL then
         pdam = ret.dam * self.steel_wall
         ret.dam = ret.dam - pdam
         DamageType:get(DamageType.PHYSICAL).projector(src, x, y, DamageType.PHYSICAL, {dam=pdam})
         game:delayedLogMessage(self, self, "Physical conversion", "#Source# diverts some damage with its shield!")
         return ret
      else
         return ret
      end
   end,

...but it did not work. The damage was not being split up and returned. It seemed like I was just getting the starting value back.

However, based on your code, I tried changing my hook to:
Code:
class:bindHook("DamageProjector:base", function(self, data)
   if data.state.no_sw_recurse then return data end
   local target = game.level.map(data.x, data.y, Map.ACTOR)
   if not target then return data end
   if target.dead then return data end
   if not target:attr("steel_wall") then return data end
   local eff = target:hasEffect(target.EFF_BLOCKING)
   if not eff then return data end
   local source_talent = data.src.__projecting_for and data.src.__projecting_for.project_type and (data.src.__projecting_for.project_type.talent_id or data.src.__projecting_for.project_type.talent) and data.src.getTalentFromId and data.src:getTalentFromId(data.src.__projecting_for.project_type.talent or data.src.__projecting_for.project_type.talent_id)

   
   if data.src and data.src.__CLASSNAME ~= "mod.class.Grid" and eff and data.type ~= DamageType.PHYSICAL and not data.state.no_sw_recurse then
      data.state.no_sw_recurse = true
      local convertedDam = data.dam * target.steel_wall
      if convertedDam < 1 then return data end
      local baseDam = data.dam - convertedDam
      local old_sw = target.steel_wall
      target.steel_wall = nil
      data.dam = 0
      data.dam = baseDam
      DamageType:get(DamageType.PHYSICAL).projector(data.src, data.x, data.y, DamageType.PHYSICAL, convertedDam, data.state)
      target.steel_wall = old_sw
   end
   
   return data
end)

... which does work! I was using the dam = dam + DamaeType:get... (etc) method based on some code used for handling elemental and light/dark split, but it seems that was not necessary and was the source of the redundant damage. Just calling DamageType:get(etc) seems to fire off the damage, while the original damage is changed to the reduced amount and passed on normally.

I'm sure the callback could work and I'm just missing something, but I'm happy enough that this method is working. Thanks again!


Top
 Profile  
 
PostPosted: Sun Feb 11, 2018 6:17 pm 
Offline
Uruivellas

Joined: Mon Oct 09, 2006 7:47 pm
Posts: 615
It seems that your callback is not triggered. Maybe you can check with a print() right at the beginning

Do you properly associate the callback to either the talent or the effect that will call it?
As I understand callbacks are automatically called if they are a field of a talent that is either passive and known or sustained.
If the callback is part of an activated talent, better use an effect callback by adding a field
callbackOnTakeDamageBeforeResists = function(self, eff, src, x, y, dtype, dam, tmp)
to the effect.
It will be called whenever the effect is on.

it should even (maybe) be possible to add the callback directly to the BLOCKING effect.
I think tt can be done by adding to the talent that allows the conversion the following code
on_learn = function(s,t)
if not Talents.T_BLOCKING.callbackOnTakeDamageBeforeResists then
Talents.T_BLOCKING.callbackOnTakeDamageBeforeResists =function(self, eff, src, x, y, dtype, dam, tmp)
...
end
(untested and ungaranteed)

The only potential problem is if another addon tries to do the same thing for the same talent... Not sure it would be considered as a good practice.


Top
 Profile  
 
PostPosted: Sun Feb 11, 2018 8:38 pm 
Offline
Uruivellas

Joined: Mon Sep 21, 2015 8:45 pm
Posts: 651
Location: Middle of Nowhere
I'm still not sure why the callback was not firing before, but I got it working now and have switched to using that instead of the hook. I also prefer to use callbacks whenever possible, and had only resorted to a hook because I wasn't sure how to fire off the converted damage and thought I would need to hook into the damage projector to handle it. As is usually the case when I'm not sure how to go about something, it was much simpler than I had anticipated, and your code showed me how to work it out. Thanks again for the help!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group