• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Custom Damage/Resist system

Status
Not open for further replies.
Level 20
Joined
Jul 14, 2011
Messages
3,213
Hi!

I've spent some time doing this. I hope it's good enough for everyone to enjoy. It's fully made in JASS; but it's insanely commented, really easy to understand as well.

The system allows creating as many Damage types as you want (Fire, Earth, Physical, Pure, Psychic, Magical, Elemental, Nature, Wood, Composite, Magma, Stone, etc.) Also allows easily adding each unit a percentage resistance to each DamageType, and add a fixed resistance to each DamageType. Also allows handling Critical Strike, Critical Ratio, and Evasion.

Has a Global Boolean called IsHealed you can switch true/false at any time. True will mean that negative damage will heal units; false will make negative damage do nothing.

Has a Global Boolean called "ShownText" you can swithc true/false at any time. True will make damage popup as a floating text from target unit. If the unit is healed (IsHealed = true) the text will be green and will have a "+" before the amount. If It's false, nothing will be shown.

It's pretty easy to add a unit data:
JASS:
call SpartDSAddUnitDmg('xxxx', SpartDSFire, 12, 0.15, 6)
That would add 12 Fire dmg, and 15% Fire resistance, and 6 Fire damage reduction to units of type 'xxxx'
JASS:
call SpartDSAddUnitStat('xxxx', 10, 2, 6)
That would add all units of type 'xxxx' 10% chance to do Damage*2 and 6% chance to evade.

It works exactly the same to add data to items.
JASS:
call SpartDSAddItemDmg('ofir', Fire, 22, .1, 0)
That would add 22 fire damage, 10% fire resistance, 0 fire damage reduction to units that picks an item of type 'ofir'
JASS:
call SpartDSAddItemStat('ofir', 12, 1.2, 0)
That will give units that pick an item of type 'ofir' a 12% chance to deal 20% more damage on an attack, and 0% chance to evade.

Item effects are cummulative. If you pick 6 items that give 10% chance to do damage*2, and 5 evasion, the unit will have 60% chance to do Damage*12, and 30% evasion.

Also, The unit name and all the unit damages/resistances/stats are displayed in a Multiboard (Screenshot at the end). Any selected unit data will be shown in the Multiboard.

The Path for Damage/Armor icons in the Multiboard is really easy to set
JASS:
        set IconPath[SpartDS_Fire] = "ReplaceableTextures\\CommandButtons\\BTNOrbOfFire.blp"
        set IconPath[SpartDS_Water] = "ReplaceableTextures\\CommandButtons\\BTNSummonWaterElemental.blp"
        set IconPath[SpartDS_Wind] = "ReplaceableTextures\\CommandButtons\\BTNCyclone.blp"
        set IconPath[SpartDS_Earth] = "ReplaceableTextures\\CommandButtons\\BTNEarthquake.blp"
        set IconPath[SpartDS_Electric] = "ReplaceableTextures\\CommandButtons\\BTNChainLightning.blp"
And so on, untill all of the Damage Types has their own icon.

The System is pretty much explained inside the testmap and script comments. In fact, I think there are more comments than system scripts.

PD: It's the first time is create a system like this one, I didn't find anywhere else to post for review or public check.

attachment.php


<< EDIT 22/09/2012 >>


· Credits to Adiktuz for most suggestion.
· Added Critical Chance, Critical Ratio, and Evasion.
· Made all UnitType data be applied to the UnitID when enters the map.
· Separated most of Core Functions into a library
· Improved code generally
· Updated Multiboard to fit new features
 

Attachments

  • Spart Damage System.w3x
    30.3 KB · Views: 179
  • Image Example.jpg
    Image Example.jpg
    148.1 KB · Views: 1,000
Last edited:
Cannot download the map for now... anyway, seems pretty cool... somehow similar to what I'm currently using...

btw, I'd rather have the registration as a simple function call...

Something like

JASS:
//vJASS using structs to save maybe
call SpartSystem.Register("Fire", "ReplaceableTextures\\CommandButtons\\BTNOrbOfFire.blp")

//JASS using arrays or hashtables to save
call SpartSystemRegister("Fire", "ReplaceableTextures\\CommandButtons\\BTNOrbOfFire.blp")

Also, if this doesn't exist yet, it's cool to be able to change the damage values/bonuses dynamically during game time...

if you finish this up, I might make some custom systems/snippets for it...
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
I was thinking I could match it with Unit Indexer to improve it, since there are so many Hash calls, but i've never worked with UnitIndexer.

@adiktuz: Thanks! Sadly, i don't know vJASS (I could learn tough)

Also, if this doesn't exist yet, it's cool to be able to change the damage values/bonuses dynamically during game time...

It can be done with Items :p I could add abilities aswell. But, what do you mean exactly?

About Fixed reduction.. well, I could add an boolean to check if the armor is considered as Percent, or as a fixed value. It could be a Global (like IsHealed or ShowText) To make both apply (fixed+%) I would have to add both values to all units/items, plus the boolean (3 more callings, ¡yay!)

If any of you have better ideas/suggestions to improve efficiency, speed, smoothness, size, user-friendly things, scripts, snippets, etc. i would really appreciate it if you could share them or even help me apply them to this.
 
The lower one is a JASS version... :)
you only need to create the functions, so you can just do it in normal jass
and you already did it with the AddUnit and AddItem so why not just make the Set-up of "damage-types" to work in the same way?

I mean changing a unit's damage and everything else during gametime just from a function call... of course maybe we can just re-run the Register function to override the old value but its not a really good idea because we should only change what we need to and not all data... this way the user can easily manipulate things enabling them to make abilities that modify damage etc...

for the fixed reduction, its better to have both so we could combine reductions... like 30% + 20 damage reduction...

Also, I'd prefer the heal option to be specific per unit coz it brings up possibilities like for example I would normally just negate negative damage (turn it to 0) then if I cast a certain spell, negative damages will heal me up for the duration of the spell...

same thing for critical chance and ratio... its better to have it per unit so we can even enable critical without using an ability plus we can modify the chance during gametime...


so basically, make every modifier linked to the unit... there should be no global modifier... take note, PER UNIT not per UNIT-TYPE...

and I think it will be a good idea to make this work with Bribe's UnitIndexer + his Damage engine...

doing that you can let the users create a base table for properties of every unit type, then once a unit gets indexed, you register that unit into your system using the values saved on the base table...

Why should it be per unit and not per unit-type? Simply because if you add in-game modifying capabilities and system only has a per unit-type setting then all units of that type will be affected... And if you choose not to add that capability, then the usefulness of such a system drops greatly...
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
you register that unit into your system using the values saved on the base table
I register the unit data In another table under UnitHandle or in several arrays (one for each damage/resist%/resistfix type) all based on UnitValue?

Note: This is something I've always tough about Unit Indexer, ir lets us get rid of Hashtables by creating several arrays for each thing, all based on Custom Value, or it still uses Hashtable?
 
It depends on you whether you prefer arrays or hashes... though most people use arrays since its faster than hashes... though arrays has an index limit but I don't think you'll reach it anyways... but arrays can be easier to read to as long as you give it a good name...

If you're gonna use UnitIndexer then you'll save those data for each unit using the UnitValue (Custom Value)... again it's up to you whether you'll use hashes or arrays...

  • Set Unit = (Picked Unit)
  • Custom script: set udg_UnitType = GetUnitTypeId(udg_Unit)
  • Set ID = Custom Value of Unit
  • Set UnitDamage[ID] = BaseDamage[UnitType]
is easier to read and understand than

  • Set Unit = (Picked Unit)
  • Custom script: set udg_UnitType = GetUnitTypeId(udg_Unit)
  • Set ID = Custom Value of Unit
  • Hashtable - Save (Load StringHash(Damage) of UnitType in Hash) as StringHash(Damage) of ID in Hash
PS: not sure if I got the hashtable parameters on the right order
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Don't worry, i'm getting it. I would use several global arrays, now...

How would the user create the globals for each damage, resist type?

JASS:
function SpartDSType requires integer index, string name, string iconpath takes nothing
    globals
        real array name + "Dmg"
        real array name + "Resper"
        real array name + "Resfix"
    endglobals
    set IconPath[index] = iconpath
endfunction
 
You're better off using hashes for saving the values...

JASS:
globals
    string array CustomTypeName
    string array CustomTypeIcon
    integer MaxType = 0
endglobals

function RegisterType takes string name, string iconpath returns nothing
    set MaxType = MaxType + 1
    set CustomTypeName[MaxType] = name
    set CustomTypeIcon[MaxType] = iconpath
endfunction

then they just need to remember the sequence that they registered each type since that would correspond to their index

as for saving the damage values of units per CustomType, then you'd probably be better off with hashtables for that... using the unit's index/customvalue as parent key and the CustomType index as childkey
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Well.. I can still put clear instructions there to guide the user through the globals creations by himself/herself...
 
For the user yes... but that could actually be hard on you... I mean how would your system cope up with the new arrays? because they'll be creating a new array per Custom damage type... you'll really need an array inside an array if you want to make this work, which is why you should just use a hashtable for those values...

unless you want to burden the user by adding lots of lines to your system's core for each new damage type that he wants to add... and lots of if-then-elses...
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Bump. Updated the main post, and uploaded the new system version. It's still based on Hashtables (not table+structs) and still uses Weep's GDD (not Bribe's Damage Engine), but, from my point of view, is still greatly improved, and added several new features.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
-Still has vJASS features with Library and free globals declaration. So why not use Table or Structs? :p

-Also if you want the critical strike to really copy the WC3 engine it should have some sort of psuedo randomness and also I dont think the object editor can handle certain percentages below 5% or whatever the value is.

-local real life = GetUnitState(udg_GDD_DamagedUnit, UNIT_STATE_LIFE)

->

local real life = GetWidgetLife(udg_GDD_DamagedUnit)

-call SetUnitState(udg_GDD_DamagedUnit, UNIT_STATE_LIFE, life+udg_GDD_Damage)

->

call SetWidgetLife(udg_GDD_DamagedUnit, life+udg_GDD_Damage)

-This is a really bad idea.

call DisableTrigger(gg_trg_SpartDS_Damage)

If you're using free global declaration you should create a trigger variable.

-WEAPON_TYPE_WHOKNOWS -> null (shorter code, faster execution, same function)

-call TriggerSleepAction(0.00)

Bad idea, rather the block after it put in a new function, use a timer that has 0. time and just call ResumeTimer(timer) and it will fire it.

-The items trigger, you should store the GetTriggerUnit() and the item.

-I heard that this breaks Vex's optimizer (did back in the day at least)

call ExecuteFunc("SpartDS_MultiboardUpdate")

-I also wonder, you have some triggers requiring vJASS and the rest is pure JASS. If you want this system in pure JASS, you have to put the function in the mapheader.

I like where this is going, keep it up.
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
I don't know very much about vJASS or JASS differences. I starting improving GUI by removing BJ's, then using locals, then custom functions. I'll follow your suggestions and update the map/post as soon as possible. I know vJASS has some strong poinst that would improve this system speed (most of all) but I have no idea about how to use vJASS.

I don't aim Critical to mimic Wc3 system. I think that setting a percentaje and a ratio is enough for most users to have control over it.

I just did the library thing to keep that triggers in hand, and not having to look for them in the Header, wich, sometimes, I forget exists.

-This is a really bad idea.
call DisableTrigger(gg_trg_SpartDS_Damage)
If you're using free global declaration you should create a trigger variable.

I don't know what you mean :) That's the most popular suggestion I've heard around (disable/enable) what's the other choice... Creating a global variable and check it in the trigger conditions, and set it false/true before/after dealing dmg?

-call TriggerSleepAction(0.00)

Bad idea, rather the block after it put in a new function, use a timer that has 0. time and just call ResumeTimer(timer) and it will fire it.

I tried in several ways, but when the item was being acquire all the Multiboard data was being reset to 0, tough the Multiboard Update was running. I added this function to the Item triggers (without care for leaks, just for testing)

JASS:
function SpartDSItemTimer takes nothing returns nothing
    local timer t = CreateTimer()
    call TimerStart(t, 0, false, function SpartDS_Item_Effect)
endfunction

It runs the function SpartDS_Item_Effect, but all the multiboard data goes to 0.

I already did all the other changes you suggested, but these 2 are still left to do.

<< EDIT >>

I aim to add later the same effect that items have for Abilities, also lifesteal, and some easy way to do magic damage/percentResiste/FixedResist separated from attack damage.
 
Level 12
Joined
Sep 11, 2011
Messages
1,176
how do we remove the message when attacking? something like "Novice deals 50 natural damage and 100 triggered damage for a total of 150..."

and also, if we already have a multiboard.. this system doesn't show it's multiboard anymore.. :ogre_frown:

another thing is, on your screenshot it said "8 natural damages and 32 triggered damages for a total of 41". shouldn't it be 40 ?
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
These numbers are reals turned to integers, so, the math is done with reals, but displayed as integers, that's why there's 1+ or 1- in the text math.

To remove the message read through the Spart DS Damage trigger and remove the lines that display the message, there are 2 commented functions to do that.

The multiboard is optional; the events i'm using to display it is Select/Deselect a unit. You can create a Boolean Array, and set it to "true" for the player that has the "Damage" table being selected as "The table to display" (I guess you switch between tables with Escape), and add the condition to that trigger (Spart DS Multiboard Update) for the boolean to be true in order to update. You would also have to store the multiboard in your Player_Multiboard array. (Set YourPlayerBoardVariable = SpartDSBoard)
 
Status
Not open for further replies.
Top