RPTools.net

Discussion and Support

Skip to content

It is currently Mon Dec 18, 2017 11:53 am 






Reply to topic  [ 9 posts ] 

Previous topic | Next topic 

  Print view

Author Message
User avatar  Offline
Dragon
 
Joined: Wed Aug 31, 2011 8:49 pm
Posts: 495
Location: Somewhere between Heaven and Hell
 Post subject: Really complex damage tracking - coding help.
PostPosted: Sat Sep 09, 2017 10:47 am 
I have a (barely) functional code to help me track really complex damage (by my standards), and it is so ugly, and clunky, and counter-intuitive, I thought I'd put up a general question about how anyone else would handle it. There are vastly better coders than me here, so perhaps I'm overthinking/underthinking, and I'd really like to start improving my overall coding capabilities (which you guys have already helped me with immensely). Here's the damage rundown:

  • Damage is of two types: Stun, or Lethal
  • There are three types of 'Hit Points'; Armor, Stun, and Lethal
  • Most attacks deal armor damage first, but there are some that ignore armor.
  • Attacks that ignore armor have an 'AA' rating. This is a portion of the damage that ignores armor. It can equal the total damage, but not exceed (obviously).
  • There is a 'Toughness' score that reduces all damage before applying to any hit points. Some attacks ignore toughness, so this should be a checkbox.
  • Stun damage, after depleting armor, carries over into Stun hit points.
  • Lethal damage, after depleting armor, skips stun damage, and goes straight to lethal, with one exception - Hardness.
  • Hardness is a property that converts X amount of carried over lethal damage, to stun damage (after depleting armor).
  • AA damage is unaffected by Hardness; so 3 AA would go straight to lethal, regardless of Hardness.
  • Any damage delivered in excess to current Stun Hit Points carries over to Lethal.
An example damage input may look like: 5 Lethal Damage, AA 3. That would deal 5 lethal damage, 2 of which would go to armor, and the other 3 would skip armor and stun and go to Lethal.
Another might be 10 Lethal Damage, ignoring Toughness. So that'd have to skip Toughness, go to armor first, and then if there is hardness go to stun. If there is no hardness, it skips stun and goes to Lethal.

If this is too annoying for anyone to tackle, I understand - I spent an hour on my clunky spam of if statements to get it to work myself, but if anyone would have some cleaner concept or 'smart' way of doing this, I'd appreciate the insight.

EDIT: I thought I'd include my own code, so if you'd rather critique, it's here.
Code:
[h, if(macro.args==""), CODE:{
   [h: tokenName=getName(getSelected())]
};{

   [h: tokenName=macro.args]

}]

[h: carryOver=0]
[h: sendToStun=0]
[h: sendToLethal=0]

[h, token(tokenName), CODE:{
   [h: current_lethal=getProperty("Health_Current")]
   [h: maximum_lethal=getProperty("Health_Maximum")]
   [h: current_armor=getProperty("Armor_Current")]
   [h: maximum_armor=getProperty("Armor_Maximum")]
   [h: current_stun=getProperty("Stun_Current")]
   [h: maximum_stun=getProperty("Stun_Maximum")]
   [h: tgt_toughness=getProperty("Toughness")]
   [h: isHealed=getState("Healed")]
   [h: isRepaired=getState("Repaired")]
   [h: isHardened=getProperty("Hardened")]
}]

[h: status=input(
"setDamage|0|Damage Value|TEXT|WIDTH=4",
"setDamageType|Stun,Lethal|Damage Type|RADIO|ORIENT=H SELECT=1",
"setArmor|1|Apply Armor|CHECK",
"setToughness|" +tgt_toughness +"|Apply Toughness (" +tgt_toughness +")|CHECK",
"setHardened|" +isHardened +"|Apply Hardness (" +isHardened +")|CHECK")]
[h: abort(status)]

[h: setDamage=if(setToughness>0, sum(setDamage,-tgt_toughness),setDamage)]
   [h: setDamage=max(0,setDamage)]

[h, if(setArmor), CODE:{
   [h: carryOver=if(setDamage>current_armor,sum(setDamage,-current_armor),0)]
   [h: carryOver=max(0,carryOver)]
   [h: current_armor=sum(current_armor,-setDamage)]
   [h: current_armor=min(current_armor,maximum_armor)]
   [h: current_armor=max(0,current_armor)]
   [h: isRepaired=0]
};{
   [h: carryOver=setDamage]
}]

[h: sendToStun=if(carryOver>0 && setHardened>0 && setDamageType==1,min(carryOver,isHardened),0)]
[h: sendToStun=if(carryOver>0 && setDamageType==0,carryOver,sendToStun)]

[h: sendToLethal=if(carryOver>0 && setHardened>0 && setDamageType==1,sum(carryOver,-isHardened),0)]
[h: sendToLethal=if(carryOver>0 && setDamageType==1 && setHardened==0,carryOver,sendToLethal)]

   [h: sendToLethal=max(0,sendToLethal)]
   [h: sendToStun=max(0,sendToStun)]

[h: carryOver=0]

[h, if(sendToStun>0 ), CODE:{
   [h: carryOver=if(sendToStun>current_stun,sum(sendToStun,-current_stun),0)]
   [h: current_stun=sum(current_stun,-sendToStun)]
   [h: current_stun=min(current_stun,maximum_stun)]
   [h: current_stun=max(0,current_stun)]
   [h: isHealed=0]
};{}]

[h: setLethalDamage=sum(sendToLethal,carryOver)]

[h, if(setLethalDamage>0), CODE:{
   [h: current_lethal=sum(current_lethal,-setLethalDamage)]
   [h: current_lethal=min(current_lethal,maximum_lethal)]
   [h: current_lethal=max(0,current_lethal)]
   [h: isHealed=0]
};{}]

[h: woundedCheck=if(current_lethal<maximum_lethal,1,0)]
[h: debilitatedCheck=if(current_stun<maximum_stun,1,0)]
[h: deadCheck=if(current_lethal<1,1,0)]

[h, token(tokenName), CODE:{
   [h: setProperty("Health_Current",current_lethal)]
   [h: setProperty("Stun_Current",current_stun)]
   [h: setProperty("Armor_Current",current_Armor)]
   [h: setState("Healed",isHealed)]
   [h: setState("Wounded",woundedCheck)]
   [h: setState("Debilitated",debilitatedCheck)]
   [h: setState("Repaired",isRepaired)]
   [h: setState("Dead",deadCheck)]
   [h: bar.Lethal = current_lethal / maximum_lethal]
   [h: bar.Stun = current_stun / maximum_stun]
   [h: bar.Armor = current_armor / maximum_armor]
}]

_________________
"An arrogant person considers himself perfect. This is the chief harm of arrogance. It interferes with a person's main task in life - becoming a better person." - Leo Tolstoy

Image


Last edited by Xaelvaen on Sat Sep 09, 2017 4:16 pm, edited 1 time in total.

Top
 Profile  
 
User avatar  Offline
Deity
 
Joined: Tue Nov 10, 2009 6:11 pm
Posts: 7969
Location: Bay Area
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sat Sep 09, 2017 1:02 pm 
I'll put this in terms I'm more familiar with. I'll use AP for AA, stands for Armor Piercing and Pen to bypass toughness, penetrating.

First I would list all the possible types of resistance, damage and their combos:
Code:
Resistances:
ArmorHP - Is reduced by non armor piercing stun and lethal damage
StunHP - Is reduced by stun damage and lethal damage equal to hardness
LethalHP - Is reduced by lethal damage
Toughness - Reduces armor, stun and lethal damage by non penetrating source
Hardness - Converts lethal to stun damage for the hardness value

Damage Types
X Stun -> X - Toughness > ArmorHP > StunHP
X Lethal -> X - Toughness > ArmorHP > StunHP(Hardness) > LethalHP
X Stun Y AP -> X - Toughness > ArmorHP - Y > StunHP
X Lethal Y AP -> X - Toughness > ArmorHP - Y > LethalHP
X Stun Pen -> X > ArmorHP > StunHP
X Lethal Pen -> X > ArmorHP > StunHP(Hardness) > LethalHP
X Stun Y AP Pen -> X > ArmorHP - Y > StunHP
X Lethal Y AP Pen -> X > ArmorHP - Y > LethalHP


What happens if you receive more stun damage than you have StunHP? Is it converted to lethal?

I would probably have a function like this:
Code:
applyDamage(damage,isLethal,AP,Pen)
<!--
   damage - amount of basic damage
   isLethal - damage type, 0 stun, 1 lethal
   AP - Amount that is Armor Piercing (is this an additive or inclusive value?) default 0
   Pen - Is penetrating ignoring toughness, default 0, 1 for is Penetrating
-->

_________________
Downloads:


Top
 Profile  
 
User avatar  Offline
Dragon
 
Joined: Wed Aug 31, 2011 8:49 pm
Posts: 495
Location: Somewhere between Heaven and Hell
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sat Sep 09, 2017 4:18 pm 
I knew I forgot one in my list! Thanks Alias, corrected on the top post. Yes indeed, excess stun damage carries over to lethal, regardless of the source that sent it to the Stun Damage Track.

Your list is basically the approach I took, my coding just seems really clunky, but I'm really not sure there's many more ways to do it - a whole mega-ton of if statements.

_________________
"An arrogant person considers himself perfect. This is the chief harm of arrogance. It interferes with a person's main task in life - becoming a better person." - Leo Tolstoy

Image


Top
 Profile  
 
User avatar  Offline
Deity
 
Joined: Tue Nov 10, 2009 6:11 pm
Posts: 7969
Location: Bay Area
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sat Sep 09, 2017 6:29 pm 
Xaelvaen wrote:
Your list is basically the approach I took, my coding just seems really clunky, but I'm really not sure there's many more ways to do it - a whole mega-ton of if statements.


With so many conditions, lots of if statements is hard to avoid. One thing you're missing is comments. Comments are really important to understand what's going on in the code. You may know it inside out right now, but in 2 weeks you'll be scratching your head when looking at it again. It also helps organize your thoughts and code.

Looking at your code it looks like armor piercing is all or nothing, you can't have some of the damage be AA. So, in your example 5 Lethal, 3 AA couldn't be applied. I'll write up how I would approach this code, as an example.

_________________
Downloads:


Top
 Profile  
 
User avatar  Offline
Dragon
 
Joined: Wed Aug 31, 2011 8:49 pm
Posts: 495
Location: Somewhere between Heaven and Hell
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sat Sep 09, 2017 7:53 pm 
What's the best approach to commenting? I've seen <!----> as well as [h: "<!---->"] and so forth - is there any one particularly better (cleaner) way to do this? All of my output is always through [h: broadcast()] so I send nothing to chat unformatted, if that matters for commenting.

Secondly, yeah - armor (at the moment) is all or nothing and I just run the macro twice to apply the piercing portion. I'm working on the coding separately for armor piercing being a variable - just finished a game session tonight, so had to get a quicker code functional.

Thanks for your time, by the way, much appreciated.

_________________
"An arrogant person considers himself perfect. This is the chief harm of arrogance. It interferes with a person's main task in life - becoming a better person." - Leo Tolstoy

Image


Top
 Profile  
 
User avatar  Offline
Deity
 
Joined: Tue Nov 10, 2009 6:11 pm
Posts: 7969
Location: Bay Area
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sat Sep 09, 2017 8:32 pm 
<!-- comment --> is an html comment. The [H: "<!-- comment -->"] is a work around I came up with to put in a comment but not have it go to chat. When clicking a macro button all the <!-- comments --> go to chat, so if your macro is supposed to have no output then it will show a blank line because of the comment. Using the H: makes it hidden but is much slower to do. This really only matters if you're working with big data and loops where you have a comment in the loop. You can run out of stack that way too.

The solution is to use UDFs and put in normal HTML comments. When defining the UDF specify it to suppress output. Your broadcast() will post to chat no matter what.

Here's what I have so far for a UDF. The user input and damage output would be in another macro that calls this one:

Code:
<!-- applyDamage(token,damage,isLethal,damageAA,isPenetrating): output
   token 
token name or id to apply damage to
   damage 
amount of basic damage
   isLethal 
damage type0 stun1 lethal
   damageAA 
Amount of basic that is armor piercing. default 0, -1 all of basic1value of AA
   isPenetrating 
ignores toughness
   output 
html output of damage taken
   
   This 
function will apply attack damage to a token and set token states based on damage.
-->

[
Htoken arg(0)]
[
Hdamage arg(1)]
[
HisLethal arg(2)]
[
H, if(argCount() >= 4): damageAA arg(3); damageAA 0]
[
H, if(argCount() >= 5): isPenetrating arg(4); isPenetrating 0]

<!-- 
get token info -->
[
HtokenId findToken(token)]
[
H, if(tokenId), code: {
   [
HswitchToken(tokenId)]
   [
HtokenName getName()]
   
   [
HmaxArmor getProperty("Armor_Maximum")]
   [
HcurArmor getProperty("Armor_Current")]
   [
HmaxStun getProperty("Stun_Maximum")]
   [
HcurStun getProperty("Stun_Current")]
   [
HmaxHealth getProperty("Health_Maximum")]
   [
HcurHealth getProperty("Health_Current")]
   
   <!-- 
amount of resistance that could be applied due to toughness -->
   [
H, if(isPenetrating): toughnessResist 0toughnessResist getProperty("Toughness")]
   <!-- 
amount of damage that could bypass armor -->
   [
H, if(damageAA), code: {
      [
H, if(damageAA 0): amountAA damageamountAA damageAA]
   };{
      [
HamountAA 0]
   }]
   <!-- 
amount of hardness that could be applied to lethal damage -->
   [
H, if(! amountAA && isLethal): hardnessResist getProperty("Hardened"); hardnessResist 0]
};{
   <!-- 
token not on current map -->
   [
Habort(0)]
}]

<!-- 
### APPLY DAMAGE ### -->

<!-- apply toughness resistance -->
[
HcurDamage damage toughnessResist]
 

_________________
Downloads:


Top
 Profile  
 
User avatar  Offline
Dragon
 
Joined: Wed Aug 31, 2011 8:49 pm
Posts: 495
Location: Somewhere between Heaven and Hell
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sun Sep 10, 2017 7:40 am 
My knowledge of UDFs is limited to me having been studying the wiki this past week, so I know the calling macro would send args. These args would be token, damage, isLethal, damageAA (if present), and isPenetrating (if present), if I read your script right. Being very new at this, could you tell me about the following lines?
Code:
[H, if(argCount() >= 4): damageAA = arg(3); damageAA = 0]
[H, if(argCount() >= 5): isPenetrating = arg(4); isPenetrating = 0]

I am -guessing- it's because the arguments start at 0 (like a list), so even though it would be argument 3 and 4, the argCount would return 4 and 5?

Nice trick with the semi-colons on the ifs to make it a single line instead of using code as well, haven't seen that before. (Haven't actually looked at a lot of code, either, but that'll be seriously useful for me).

I just can't say thank you enough, this will turn my code around big time, and much more easily read.

_________________
"An arrogant person considers himself perfect. This is the chief harm of arrogance. It interferes with a person's main task in life - becoming a better person." - Leo Tolstoy

Image


Top
 Profile  
 
User avatar  Offline
Deity
 
Joined: Tue Nov 10, 2009 6:11 pm
Posts: 7969
Location: Bay Area
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sun Sep 10, 2017 8:36 am 
Right on all accounts. arg() is an array and starts at 0 but argCount gives you the number of arguments which starts at 1.

Life will be much easier for you once you start using UDFs, but one thing to watch out for is function creep. You may want to jam a bunch of stuff in to a function but functions should be very task oriented. They do one thing and one thing well. Another advantage to functions is when you reference them from many other places and you need to make a slight change then you only change the code in one spot, the function.

That's why I defined the function as applyDamage. It's not going to ask for user input, it's not going to send output to the chat, but does return it to the calling function. I wouldn't normally apply state updates but the nature of the code kind of needs you too. You could return the state changes from the function to have it update elsewhere but that kind of goes against the do one thing rule, ie return output and state changes. Since the state changes being made are solely dependent upon the damage done, it's okay to add that to the function.

I have some basic coding best practices here along with some MT specific tips. It's a short read: viewtopic.php?f=20&t=22886&p=233681#p233681

_________________
Downloads:


Top
 Profile  
 
User avatar  Offline
Dragon
 
Joined: Wed Aug 31, 2011 8:49 pm
Posts: 495
Location: Somewhere between Heaven and Hell
 Post subject: Re: Really complex damage tracking - coding help.
PostPosted: Sun Sep 10, 2017 9:14 am 
Read your best practices again - read it once long ago, thus why I have the lowercaseUppercase variable naming convention, just never got in the habit of commenting - I should do that.

I'll keep in mind the UDF creep, though it'll be a very slow implementation - I've done so much with just calling lib:token macros, it'll just slowly be replacing everything.

I have a slightly related question - is there a function to extract numbers and letters from combined input? In example, if I label a damage value as 5L (5 Lethal), is there a way to extract the numbers from the letters and get two distinct variables out of that? I'm on the wiki blindly clicking things that sound like they -might- work for that, if I stumble upon it myself I'll update here.

EDIT: I found 'endsWith' and wrote a code to choose the damage type based on an S or an L appearing at the end of the string. Now I just need a way to remove the number portion and use that in a math function.

EDIT 2: I also found 'replace' and got the code working hehe.

Should anyone be looking in the future using a search for this criteria, I'll write the code (in brief) that I used.
Code:
[h: tDamage=json.get(macro.args,"tDamage")]
[h: damageType=if(endsWith(tDamage,"L"),"Lethal","Stun")]
[h: trueDamage=if(endsWith(tDamage,"L"),replace(tDamage,"L",""),tDamage)]
[h: trueDamage=if(endsWith(trueDamage,"S"),replace(trueDamage,"S",""),trueDamage)]
[h: trueDamage=number(trueDamage)]
[h: "<!---- I added the number section just in case the previous coding made it not act like a number - really dunno if it is necessary ---->"]

_________________
"An arrogant person considers himself perfect. This is the chief harm of arrogance. It interferes with a person's main task in life - becoming a better person." - Leo Tolstoy

Image


Top
 Profile  
 
Display posts from previous:  Sort by  
Reply to topic  [ 9 posts ] 

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:

Who is online

In total there are 2 users online :: 0 registered, 0 hidden and 2 guests (based on users active over the past 5 minutes)
Most users ever online was 243 on Sun Nov 04, 2012 6:14 am

Users browsing this forum: No registered users and 2 guests





Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group

Style based on Andreas08 by Andreas Viklund

Style by Elizabeth Shulman