| 04-14-2010, 12:04 PM | #1 |
Hey Anitarf, my JassNewGenPack (JNGP) works now. The problem was that I used WIII:TFT in V1.21b. Still I think Vexorian should add this in his instruction for using 2 versions of W3, that vjass with hashtables won't work then by using JNGP with WIII-V1.21b. Now I can ask my core question :) I want to use your system with DamageEvent & DamageModifiers. My purpose is quite easy, but I reread your ABuffSystem1.5 again and again and I come to estimated 1 dubious line of code per hour. I studied the RetributionBarrier-Trigger, but without success till now. If you could give me code for following situation so I can understand it and work it out to my desires? I don't need buffs with effects. I just have to detect different damage and armor types. I think I need abilities and buffs. (The abilities can already be added to the appropriate units in the object editor, the buffs are needed for your damage system?) We have fire and ice classes. There is a unit A1, a tower. There is a unit B1, a creep. A1 has an ability (& buff?) for ice-damage-detection. B1 has an ability (& buff?) for ice-armor-detection. A1 makes 0.2 * X dmg to B1. (both are ice) Then we have another unit B2, a creep. B2 has an ability (& buff?) for fire-armor-detection. A1 makes 2.0 * X dmg to B2. That would be all. If you are so nice to give my a sample code so I can work it out to 8 different damage and armor types... |
| 04-14-2010, 11:10 PM | #2 |
Dummy abilities as proxies for armour types are one way to go. However, this might not be the fastest way because in order to find out which elemental armour a unit has, you would need to check for N abilities, where N is equal to the number of different elements you have. Actually, N is only equal to the number of elements an attack gets modified against, so if you have 8 elements but an elemental attack only has bonuses/penalties against 2 of them, then you would only have to check for two abilities for that attack. The alternative would be to use a hashtable to associate unit types with armour types with a hashtable. In that case, finding out a unit's armour type would only require a GetUnitTypeId call and a hashtable read call. Once you have one of these two systems in place, you can easily tell what a unit's armour (and weapon) element is. Now for the actual damage modifiers: Whenever a unit that can deal damage is created in game (if these are towers in a TD, you would use the "finishes construction" event, otherwise use "unit enters region" or whatever), create a damage modifier for it. If this unit can never be destroyed, such as towers in some TDs, then you don't have to worry about cleanup, otherwise you should somehow attach the damage modifier to your unit so when the unit dies, you can destroy the modifier. One way to do this is with ABuffs, but I won't go into more detail unless you need this, because if you don't then you can easily do this without ABuff alltogether, just with Damage Modifiers. There are two ways to approach the damage modifier itself. One way is to have a different type of modifier for each element: then, in each damage modifier's onDamageDealt method, you can hardcode what elements to check for in the target and what the modifier factors are for those elements. This is useful if you are using dummy abilities since hardcoding it means there are fewer abilities you need to check; you just need to look for those abilities on the damaged unit for which the damage source gets a damage bonus or penalty. The second approach is to have a single generic damage modifier applied to all units that deal damage. This modifier then checks the attack element of the damage source, the defense element of the damaged unit and then looks up what the damage modifier for this combination is, for example in a hashtable. This approach is useful if you are already using hashtables for getting a unit type's element and it allows you to very easily create and maintain complicated damage bonus tables. |
| 05-13-2010, 11:47 PM | #4 |
I have finally made progress I'm using DamageEvent, DamageModifiers and required Table now. Here is my current working hashtable library JASS:library InitHashtable initializer Init globals hashtable ht_SoM = InitHashtable() endglobals private function Init takes nothing returns nothing // 1 function must be named "Init" for library to run //call SaveReal(ht, parentkey?, childkey?, 0.2) //parentKey Usage by UnitId //childKey Usage //1=Water //2=Earth //3=Wind //4=Fire //5=Darkness //6=Light //7=Moon //8=Tree // List of Damager Towers // Water call SaveReal(ht_SoM, 'hgtw', 1, 0.2) //native SaveReal - takes hashtable table, integer parentKey, integer childKey, real value returns nothing call SaveReal(ht_SoM, 'hgtw', 4, 12.0) call SaveReal(ht_SoM, 'hgtw', 5, 0.5) call SaveReal(ht_SoM, 'hgtw', 6, 1.5) endfunction endlibrary And here is the current WORKING DamageSystem library JASS:library DamageSystem initializer Init requires DamageModifiers struct Test extends DamageModifier static integer PRIORITY=0 // Default priority real power // This lets us give different units differently strong armour. unit damager // create method is optional, if you don't declare one then you must use // the .allocate parameters (unit, integer) when creating a modifier of this kind. static method create takes unit u returns Test //, real power // Note the parameters for .allocate, this is because this struct extends // DamageModifier which asks for these parameters in its create method: local Test this = Test.allocate(u, Test.PRIORITY) set this.damager = u //set this.power = power return this endmethod // This is the method that runs when damage is dealt from the unit with the modifier. // The damage parameter tells how much damage got to this modifier past any modifiers // with a higher priority that the unit may have. // The value that the method returns tells the system by how much to modify the damage, // a positive return value increases damage while a negative value reduces it. method onDamageDealt takes unit damagedUnit, real damage returns real local string s = SubString(UnitId2String(GetUnitTypeId(damagedUnit)),8,9) // cut "custom_c" + "00" from "custom_cX00" (X = 1-8) local integer i = S2I(s) local real r = LoadReal(ht_SoM, GetUnitTypeId(this.damager), i) //native LoadReal - takes hashtable table, integer parentKey, integer childKey returns real return -((1-r) * damage) endmethod // onDestroy method is missing... endstruct function Well takes nothing returns nothing local unit u = GetConstructedStructure() local Test A = Test.create(u) endfunction private function Init takes nothing returns nothing // 1 function must be named "Init" for library to run local trigger t = CreateTrigger() call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH ) // b.j-function for choosing all players call TriggerAddAction( t, function Well ) endfunction endlibrary Note that I don't store both, the armor and the damage type, in the hashtable. I only store the real damage modication and get the armor type from the unitIDs of the creeps themselves, set up with Grimexs Object Merger. I think thats quite fine, although I don't know how fast "UnitId2String" is... So, Anitarf, now I dare to ask you again about the cleanup. How can I destroy the modifiers created? Somehow with a onDestroy - method I guess... |
| 05-14-2010, 10:25 AM | #5 |
I think you'll need a lot of needless copy-pasting to get this to work for all towers. Also, it will be a pain to change damage percentages later on. Let's restructure the elemental armour system: JASS:library ElementalSystem initializer Init globals // Let us make things a bit more readable: public constant integer WATER = 1 public constant integer EARTH = 2 public constant integer WIND = 3 public constant integer FIRE = 4 public constant integer DARKNESS = 5 public constant integer LIGHT = 6 public constant integer MOON = 7 public constant integer TREE = 8 private constant integer ARMOUR = 9 private constant integer WEAPON = 10 private hashtable ht = InitHashtable() endglobals // DATA SETUP: private function Init takes nothing returns nothing //Repeat this for all elements: call SaveReal(ht, WATER, WATER, 0.2) call SaveReal(ht, WATER, EARTH, 1.0) call SaveReal(ht, WATER, WIND, 1.0) call SaveReal(ht, WATER, FIRE, 12.0) call SaveReal(ht, WATER, DARKNESS, 0.5) call SaveReal(ht, WATER, LIGHT, 1.5) call SaveReal(ht, WATER, MOON, 1.0) call SaveReal(ht, WATER, TREE, 1.0) //Repeat this for all towers: call SaveInteger(ht, WEAPON, 'hgtw', WATER) //Repeat this for all creeps: call SaveInteger(ht, ARMOUR, 'c100', WATER) endfunction // PUBLIC FUNCTIONS: function GetElementalDamageFactor takes integer attackElement, integer defenceElement returns real return LoadReal(ht, attackElement, defenceElement) endfunction function GetUnitAttackElement takes unit u returns integer return LoadInteger(ht, WEAPON, GetUnitTypeId(u)) endfunction function GetUnitDefenceElement takes unit u returns integer return LoadInteger(ht, ARMOUR, GetUnitTypeId(u)) endfunction endlibrary Then we make the damage modifiers using the above library. As long as towers in your map can not be sold/destroyed, there is no need to worry about cleaning up the damage modifiers. In case you need that, though, I'll add support for it as well. JASS:library ElementalDamageSystem initializer Init requires ElementalSystem, DamageModifiers, Table private struct ElementalDamage extends DamageModifier private static integer PRIORITY=0 // Default priority private static HandleTable ht private integer element private unit tower static method onInit takes nothing returns nothing set ht=HandleTable.create() endmethod static method create takes unit u, integer element returns ElementalDamage local ElementalDamage this = ElementalDamage.allocate(u, Test.PRIORITY) set this.tower = u set this.element = element set ht[u]=integer(this) return this endmethod method onDestroy takes nothing returns nothing call ht.flush(this.tower) set this.tower=null endmethod static method clear takes unit u returns boolean local ElementalDamage this= ElementalDamage( ht[u] ) if this != 0 then call this.destroy() return true endif return false endmethod method onDamageDealt takes unit damagedUnit, real damage returns real return -(( 1 - GetElementalDamageFactor(this.element, GetUnitDefenceElement(damagedUnit)) ) * damage) endmethod endstruct private function RegisterTower takes nothing returns nothing local unit u=GetTriggerUnit() local integer element = GetUnitAttackElement(u) if element>0 then call ElementalDamage.create(u, element) endif set u=null endfunction public function TowerDeath takes unit u returns nothing call ElementalDamage.clear(u) // Call this function when a tower dies/is sold endfunction private function Init takes nothing returns nothing local trigger t = CreateTrigger() call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH ) call TriggerAddAction( t, function RegisterTower ) endfunction endlibrary |
| 05-14-2010, 12:28 PM | #6 | |
This is so wonderful! I would not have been able to code such a neat script, not in 4 weeks or longer, so thanks a lot! Especially the clean and more useful reconstruction of the elemental armour system is something I like. Thanks for this! Quote:
My problems are hereby done for now. I will give you rep as soon as I can In my credits you will be listet at the top Apparently you had to donate a bit time for this code, I really appreciate it. |
