| 01-09-2009, 10:05 PM | #1 |
My last unfinished spell was a simple shield that could absorb damage from enemy units. To create this shield I saw some very old codes but they were bad coded and used Kattana's which I hate by now deeply (hurray cache corruption). So I decided to post here for help, but I only managed to start a fight because I entered the world of dynamic triggers, where avoiding leaks is the price for stability.As an idiot said "just use dynamic triggers and stop crying". Ofc I didn't listen to the idiot at first .... I decided to search for damage detection system and I tried to find as many as possible, but in the end, I came only with 2options: - IDDS by Rising_Dusk, although intuitive and easy it doesn't allow for the use of shields, which I require - ADamage, although it has everything the system is so complex I can't even create a Crtical Strike spell (which is simple)... So, now that I am done with system I decided to either create my own or to do the spells without any. I don't have experience to create my own system, so I decided to just use dynamic triggers. This is the code of my spell, I made some improvements, and it works with only 1 timer however, nothing happens ! The hero doesn't get any heal and it is being damage all the time !! If some one could help me in the new world of Damage detection I would appreciate, but please, enough systems, I want to do this without one. If some could help I would appreciate. JASS://=========================================================================== //A JESP spell that allows the hero to cast a shield on himself. The shield //will absorb an amount of damage for the hero thus protecting him from harm. // //Requires TimerUtils and Table // //@author Flame_Phoenix // //@credits // - Pyrogasm, for the one time algorithm // //@version 1.0 //=========================================================================== scope LightShield initializer Init //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A001' private constant integer BUFF_ID = 'BNab' private constant real TIMER_CYCLE = 0.1 endglobals private constant function Duration takes integer level returns real return level * 90. endfunction private constant function ShieldAbsorb takes integer level returns real return level * 100. endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== private keyword SpellData globals private group LightShieldUnits private HandleTable activeTable private timer t private integer instancesCount private SpellData array datas endglobals private function onDamage takes nothing returns boolean local SpellData data local unit u = GetTriggerUnit() call BJDebugMsg("DmgCond") //we make sure that the unit that is hit is our victim if(IsUnitInGroup(u, LightShieldUnits)) then //recover that data (the struct) from the victim set data = activeTable[u] call BJDebugMsg("onGroup") if (GetUnitAbilityLevel(u, BUFF_ID) > 0) then call BJDebugMsg("buffOn") call data.prevent() endif endif set u = null return false endfunction private struct SpellData unit caster unit target integer level real shieldLife real dur boolean done static method create takes unit caster, unit target, integer level returns SpellData local SpellData data = SpellData.allocate() local trigger damageTrg = CreateTrigger( ) set data.caster = caster set data.level = level set data.target = target set data.shieldLife = ShieldAbsorb(data.level) set data.dur = 0 set data.done = false //put the struct in the Table, we just use the target's //handle adress as the key which tells us where in the //Table the struct is stored set activeTable[data.target] = data call GroupAddUnit(LightShieldUnits, data.target) //when the hero takes damage call TriggerRegisterUnitEvent(damageTrg, data.target, EVENT_UNIT_DAMAGED ) call TriggerAddCondition(damageTrg, Condition(function onDamage)) return data endmethod method shieldTime takes nothing returns nothing set .dur = .dur + TIMER_CYCLE if (.dur >= Duration(.level)) or (GetUnitAbilityLevel(.target, BUFF_ID) < 1) then set .done = true endif endmethod method prevent takes nothing returns nothing local real dmg = GetEventDamage() if (dmg >= .shieldLife) then //heal the unit to with the remaining hp of the shield to prevent some //of teh damage. Prevention is the shield hp, the rest will pass call SetWidgetLife(.target, GetWidgetLife(.target) + .shieldLife) set .done = true else //heal the unit to prevent the damage call SetWidgetLife(.target, GetWidgetLife(.target) + dmg) set .shieldLife = .shieldLife - dmg call BJDebugMsg(R2S(.shieldLife)) endif endmethod method onDestroy takes nothing returns nothing //we remove the buff call UnitRemoveAbility(.target, BUFF_ID) //since the spell is not active anymore, we clean the Table call activeTable.flush(.target) //the units are not anymore in the active units group. call GroupRemoveUnit(LightShieldUnits, .target) endmethod endstruct private function Periodic takes nothing returns nothing local integer currentIndex = 0 local SpellData currentInstance loop set currentInstance = datas[currentIndex] call currentInstance.shieldTime() //if our instance is done, we decrement the number of total instances //and then we check if there are any more instances being run if (currentInstance.done) then set instancesCount = instancesCount - 1 //if there are, then we update our instance to the next of the array //and we correct the index if (instancesCount > 0) then set datas[currentIndex] = datas[instancesCount] set currentIndex = currentIndex - 1 //else we just release the timer else call ReleaseTimer(t) endif //now before we leave current instance for good, we destroy it! call currentInstance.destroy() endif set currentIndex = currentIndex + 1 exitwhen currentIndex >= instancesCount endloop endfunction //=========================================================================== private function Conditions takes nothing returns boolean if (GetSpellAbilityId() == AID) then //if there are no instances of the spell then it means the timer does not //exist, so we create it and start it! if (instancesCount == 0) then set t = NewTimer() call TimerStart(t, TIMER_CYCLE, true, function Periodic) endif set datas[instancesCount] = SpellData.create(GetTriggerUnit(), GetSpellTargetUnit(), GetUnitAbilityLevel(GetTriggerUnit(), AID)) set instancesCount = instancesCount + 1 endif return false endfunction //=========================================================================== private function Init takes nothing returns nothing //when the hero casts the spell local trigger LightShieldTrg = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( LightShieldTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(LightShieldTrg, Condition(function Conditions)) set LightShieldTrg = null //setting our globals set LightShieldUnits = CreateGroup() set activeTable = HandleTable.create() set instancesCount = 0 endfunction endscope This is the first post I create for help in many months, I hope I get some help because I don't know when I will post another ... According to the debugg messages I assume the problem is on the dynamic trigger. The condition is NEVER run and I don't know why. Thx in advance, hee is the map for your joy. Please note I didn't create the map nor the original spell. I am remaking the spell so I can change it in the future, but first I need the basics, I ned this thing working. EDIT EDIT EDIT Bump Ok guys I fixed the problem, now the spell works,m however I have 2 small problems... 1 - When the shielded unit is damaged, if it is full HP, then it will lose life and I don't know how to fix this =( 2 - the trigger leaks ... The spell works good the first time, but because the trigger leaks, the second time I cast the spell, the shield will take 2x damage, because it will run same code twice. If I cast it 3 times, it runs the same code 3x so shield takes 3x extra damage, and I don't want it =( If some one could help ... JASS://=========================================================================== //A JESP spell that allows the hero to cast a shield on himself. The shield //will absorb an amount of damage for the hero thus protecting him from harm. // //Requires TimerUtils and Table // //@author Flame_Phoenix // //@credits // - Pyrogasm, for the one time algorithm // //@version 1.1 //=========================================================================== scope LightShield initializer Init //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A001' private constant integer BUFF_ID = 'BNab' private constant real TIMER_CYCLE = 0.1 endglobals private constant function Duration takes integer level returns real return level * 90. endfunction private constant function ShieldAbsorb takes integer level returns real return level * 100. endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== private keyword SpellData globals private group LightShieldUnits private HandleTable activeTable private timer t private integer instancesCount private SpellData array datas endglobals private function onDamage takes nothing returns boolean local SpellData data local unit u = GetTriggerUnit() //we make sure that the unit that is hit is our victim if(IsUnitInGroup(u, LightShieldUnits)) then //recover that data (the struct) from the victim set data = activeTable[u] if (GetUnitAbilityLevel(u, BUFF_ID) > 0) then call data.prevent() else set data.done = true endif endif set u = null return false endfunction private struct SpellData unit caster unit target integer level real shieldLife real dur boolean done static method create takes unit caster, unit target, integer level returns SpellData local SpellData data = SpellData.allocate() local trigger damageTrg = CreateTrigger( ) set data.caster = caster set data.level = level set data.target = caster //our target is the caster.... set data.shieldLife = ShieldAbsorb(data.level) set data.dur = 0 set data.done = false //put the struct in the Table, we just use the target's //handle adress as the key which tells us where in the //Table the struct is stored set activeTable[data.target] = data call GroupAddUnit(LightShieldUnits, data.target) //when the hero takes damage call TriggerRegisterUnitEvent(damageTrg, data.target, EVENT_UNIT_DAMAGED ) call TriggerAddCondition(damageTrg, Condition(function onDamage)) return data endmethod method shieldTime takes nothing returns nothing set .dur = .dur + TIMER_CYCLE if (.dur >= Duration(.level)) or (GetUnitAbilityLevel(.target, BUFF_ID) < 1) then set .done = true endif endmethod method prevent takes nothing returns nothing local real dmg = GetEventDamage() if (dmg >= .shieldLife) then //heal the unit to with the remaining hp of the shield to prevent some //of teh damage. Prevention is the shield hp, the rest will pass call SetWidgetLife(.target, GetWidgetLife(.target) + .shieldLife) set .done = true else if GetWidgetLife(.target) == GetUnitState(.target, UNIT_STATE_MAX_LIFE) then call SetWidgetLife(.target, GetUnitState(.target, UNIT_STATE_MAX_LIFE)) call BJDebugMsg("max me!") else //heal the unit to prevent the damage call SetWidgetLife(.target, GetWidgetLife(.target) + dmg) set .shieldLife = .shieldLife - dmg call BJDebugMsg(R2S(.shieldLife)) endif endif endmethod method onDestroy takes nothing returns nothing call BJDebugMsg("============================================================") //we remove the buff call UnitRemoveAbility(.target, BUFF_ID) //since the spell is not active anymore, we clean the Table call activeTable.flush(.target) //the units are not anymore in the active units group. call GroupRemoveUnit(LightShieldUnits, .target) endmethod endstruct //=========================================================================== private function Periodic takes nothing returns nothing local integer currentIndex = 0 local SpellData currentInstance loop set currentInstance = datas[currentIndex] call currentInstance.shieldTime() //if our instance is done, we decrement the number of total instances //and then we check if there are any more instances being run if (currentInstance.done) then set instancesCount = instancesCount - 1 //if there are, then we update our instance to the next of the array //and we correct the index if (instancesCount > 0) then set datas[currentIndex] = datas[instancesCount] set currentIndex = currentIndex - 1 //else we just release the timer else call ReleaseTimer(t) endif //now before we leave current instance for good, we destroy it! call currentInstance.destroy() endif set currentIndex = currentIndex + 1 exitwhen currentIndex >= instancesCount endloop endfunction //=========================================================================== private function Conditions takes nothing returns boolean if (GetSpellAbilityId() == AID) then //if there are no instances of the spell then it means the timer does not //exist, so we create it and start it! if (instancesCount == 0) then set t = NewTimer() call TimerStart(t, TIMER_CYCLE, true, function Periodic) endif set datas[instancesCount] = SpellData.create(GetTriggerUnit(), GetSpellTargetUnit(), GetUnitAbilityLevel(GetTriggerUnit(), AID)) set instancesCount = instancesCount + 1 endif return false endfunction //=========================================================================== private function Init takes nothing returns nothing //when the hero casts the spell local trigger LightShieldTrg = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( LightShieldTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(LightShieldTrg, Condition(function Conditions)) set LightShieldTrg = null //setting our globals set LightShieldUnits = CreateGroup() set activeTable = HandleTable.create() set instancesCount = 0 endfunction endscope |
| 01-10-2009, 02:06 PM | #2 | |
Quote:
Update: Ok guys I fixed problem 2 of the spell. Now the shield works properly, except in case 1, which is when the unit has full hp. I would appreciate any possible help, since I feel this thread is being ignored =( I tried adding an ability that increases hp as I saw in a DD system, but so far that isn't working. JASS://=========================================================================== //A JESP spell that allows the hero to cast a shield on himself. The shield //will absorb an amount of damage for the hero thus protecting him from harm. // //Requires TimerUtils and Table // //@author Flame_Phoenix // //@credits // - Pyrogasm, for the one time algorithm // - ixtreon, the original creator of the spell gave me the idea for making my own // //@version 1.2 //=========================================================================== scope LightShield initializer Init //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A001' private constant integer BUFF_ID = 'BNab' private constant integer LIFE_ID = '7A00' private constant real TIMER_CYCLE = 0.1 endglobals private constant function Duration takes integer level returns real return level * 90. endfunction private constant function ShieldAbsorb takes integer level returns real return level * 100. endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== private keyword SpellData globals private group LightShieldUnits private HandleTable activeTable private timer t private integer instancesCount private SpellData array datas endglobals private function onDamage takes nothing returns boolean local SpellData data local unit u = GetTriggerUnit() //we make sure that the unit that is hit is our victim if(IsUnitInGroup(u, LightShieldUnits)) then //recover that data (the struct) from the victim set data = activeTable[u] if (GetUnitAbilityLevel(u, BUFF_ID) > 0) then call data.prevent() else set data.done = true endif endif set u = null return false endfunction private struct SpellData unit caster unit target integer level real shieldLife real dur boolean done trigger damageTrg static method create takes unit caster, unit target, integer level returns SpellData local SpellData data = SpellData.allocate() set data.caster = caster set data.level = level set data.target = caster //our target is the caster.... set data.shieldLife = ShieldAbsorb(data.level) set data.dur = 0 set data.done = false //put the struct in the Table, we just use the target's //handle adress as the key which tells us where in the //Table the struct is stored set activeTable[data.target] = data call GroupAddUnit(LightShieldUnits, data.target) //when the hero takes damage if data.damageTrg == null then set data.damageTrg = CreateTrigger() call TriggerRegisterUnitEvent(data.damageTrg, data.target, EVENT_UNIT_DAMAGED ) call TriggerAddCondition(data.damageTrg, Condition(function onDamage)) endif return data endmethod method shieldTime takes nothing returns nothing set .dur = .dur + TIMER_CYCLE if (.dur >= Duration(.level)) or (GetUnitAbilityLevel(.target, BUFF_ID) < 1) then set .done = true endif endmethod method prevent takes nothing returns nothing local real dmg = GetEventDamage() if (dmg >= .shieldLife) then //heal the unit to with the remaining hp of the shield to prevent some //of teh damage. Prevention is the shield hp, the rest will pass call SetWidgetLife(.target, GetWidgetLife(.target) + .shieldLife) set .done = true else //if the damage plus unit current life exceed max life, then the unit takes damage. //Here we prevent the unit from taking that damage if (dmg + GetWidgetLife(.target) >= GetUnitState(.target, UNIT_STATE_MAX_LIFE)) then call UnitAddAbility(.target, LIFE_ID) call SetWidgetLife(.target, GetUnitState(.target, UNIT_STATE_MAX_LIFE)) call BJDebugMsg("max me!") call UnitRemoveAbility(.target, LIFE_ID) //this is a normal case, where we heal the unit and damage the shield else //heal the unit to prevent the damage call SetWidgetLife(.target, GetWidgetLife(.target) + dmg) call BJDebugMsg(R2S(.shieldLife)) endif //update the life of the shield! set .shieldLife = .shieldLife - dmg endif endmethod method onDestroy takes nothing returns nothing call BJDebugMsg("============================================================") //we remove the buff call UnitRemoveAbility(.target, BUFF_ID) //since the spell is not active anymore, we clean the Table call activeTable.flush(.target) //the units are not anymore in the active units group. call GroupRemoveUnit(LightShieldUnits, .target) endmethod endstruct //=========================================================================== private function Periodic takes nothing returns nothing local integer currentIndex = 0 local SpellData currentInstance loop set currentInstance = datas[currentIndex] call currentInstance.shieldTime() //if our instance is done, we decrement the number of total instances //and then we check if there are any more instances being run if (currentInstance.done) then set instancesCount = instancesCount - 1 //if there are, then we update our instance to the next of the array //and we correct the index if (instancesCount > 0) then set datas[currentIndex] = datas[instancesCount] set currentIndex = currentIndex - 1 //else we just release the timer else call ReleaseTimer(t) endif //now before we leave current instance for good, we destroy it! call currentInstance.destroy() endif set currentIndex = currentIndex + 1 exitwhen currentIndex >= instancesCount endloop endfunction //=========================================================================== private function Conditions takes nothing returns boolean if (GetSpellAbilityId() == AID) then //if there are no instances of the spell then it means the timer does not //exist, so we create it and start it! if (instancesCount == 0) then set t = NewTimer() call TimerStart(t, TIMER_CYCLE, true, function Periodic) endif set datas[instancesCount] = SpellData.create(GetTriggerUnit(), GetSpellTargetUnit(), GetUnitAbilityLevel(GetTriggerUnit(), AID)) set instancesCount = instancesCount + 1 endif return false endfunction //=========================================================================== private function Init takes nothing returns nothing //when the hero casts the spell local trigger LightShieldTrg = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( LightShieldTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(LightShieldTrg, Condition(function Conditions)) set LightShieldTrg = null //setting our globals set LightShieldUnits = CreateGroup() set activeTable = HandleTable.create() set instancesCount = 0 endfunction endscope |
| 01-10-2009, 05:38 PM | #3 |
It is because the event fires BEFORE the unit takes damage, so it heals them while they are already at full. You could have: JASS:if damage > hp then //heal elseif hp + damage > maxhp then //start a 0. second timer to heal else //heal endif |
| 01-10-2009, 05:58 PM | #4 | |
Quote:
I tried using a Wait(0) but it didn't work (as expected...) Anyway thx for being the only 1 to help me out. You deserve +rep and credits. |
| 01-10-2009, 06:15 PM | #5 |
Well TriggerSleepAction is very unstable, so you shouldn't use it anyways. The timer will fire at the same time, just after the unit takes damage. AFAIK that is the best way... |
| 01-10-2009, 06:23 PM | #6 | |
Quote:
PS: How do I make a timer run a method ? JASS:method specialPrevent takes nothing returns nothing endmethod //in next method call TimerStart(t, 0, false, function specialPrevent) It says function does not exist =( |
| 01-10-2009, 07:11 PM | #7 |
Use a static method instead JASS:static method specialPrevent takes nothing returns nothing endmethod //in next method call TimerStart(t, 0, false, function structname.specialPrevent) |
| 01-10-2009, 10:15 PM | #8 |
Use Captain Griffen's Damage Detect system and tag team it with Vexorian's Table system. I personally use these two in combination to make shield spells, and since CG's Damage Detect system is just a additional function so you can easily link your variable to it using Vexorian Table -Av3n |
| 01-10-2009, 10:17 PM | #9 | |
Quote:
Thx for your advice though, I still appreciate it. Ok guys I fixed the spell and submited a final(?) version of it: http://www.wc3campaigns.net/showthre...00#post1057700 Thx for all help, please comment and enjoy. |
| 01-10-2009, 10:43 PM | #10 |
I'd suggest trying out Cohadars damage detection system called "ORBEngine" that can be found in this map: http://www.thehelper.net/forums/showthread.php?t=80046 |
| 01-10-2009, 10:44 PM | #11 |
My system's not at all outdated, just a lightweight up to date system (where you can choose whether to have it in leaking or possibly safer modes). |
