| 08-20-2008, 07:41 PM | #1 | |
Okay, been giving this a shot now, and while I think I have a handle on the situation, I wanted to see if anyone had some advice or a new perspective on how to delay the application of damage. What I am attempting to do: 1. Unit X takes damage. 2. Unit X is healed for the amount of damage taken, the damage is saved. 3. 6 seconds later, the damage is dealt to Unit X, using as many of the orginal characteristics as possible. The primary problem I'm having is with damage recursion, the secondary problem I'm having is with preventing the first instance of damage. Update: I've just had it pointed out to me (thankyou Dusk!) that you can disable the trigger before the delayed damage is dealt, then re-enable it after. D'oh! So, my question now becomes: is there a possibility of the trigger missing a damage event in the brief time it is disabled? Quote:
JASS:scope FDLeft globals private constant real DamageDelay = 6.0 // The time before damage is dealt. private unit dawn // This is the only unit with the ability, it gets set at InitTrig; //I'm not concerned with MUI at the moment, it will be handled with an array //or something later. This is more of a proof-of-concept for me. private real size = 8.0*0.023/10 //Size of the textag private real speed = 73.0*0.071/128 //Velocity of the texttag endglobals struct damstore unit u real damg integer dtype //Stores the damage type. endstruct private function DawnLeft takes nothing returns nothing local timer c = GetExpiredTimer() local string m = I2S(h2i(c)) local damstore ds = damstore(GetStoredInteger(DamageSystem_Cache, m, "damstore")) local texttag tag = CreateTextTag() call SetUnitUserData(ds.u, R2I(ds.damg)) call UnitDamageTargetEx(ds.u, dawn, ds.damg, ATTACK_TYPE_NORMAL, ds.dtype, false) call SetTextTagText(tag, I2S(R2I(ds.damg)), size) call SetTextTagPosUnit(tag, dawn, 15.0) if ds.dtype==0 then call SetTextTagColor(tag, 165, 110, 200, 255) elseif ds.dtype==1 then call SetTextTagColor(tag, 250, 240, 20, 255) elseif ds.dtype==2 then call SetTextTagColor(tag, 150, 250, 250, 255) elseif ds.dtype==3 then call SetTextTagColor(tag, 255, 115, 40, 255) else call SetTextTagColor(tag, 180, 10, 10, 255) endif call SetTextTagVelocity(tag, 0.0, speed) call SetTextTagFadepoint(tag, 2.0) call SetTextTagLifespan(tag, 3.0) call SetTextTagPermanent(tag, false) call ds.destroy() call PauseTimer(c) call DestroyTimer(c) call FlushStoredMission(DamageSystem_Cache, m) set c = null set tag = null endfunction private function HealDamage takes nothing returns nothing local real life = GetWidgetLife(dawn) local timer c = GetExpiredTimer() local string m = I2S(h2i(c)) local damstore dt = GetStoredInteger(DamageSystem_Cache, m, "dt") call SetWidgetLife(dawn, life+dt.damg) call dt.destroy() call PauseTimer(c) call DestroyTimer(c) call FlushStoredMission(DamageSystem_Cache, m) set c = null endfunction private function Conditions takes nothing returns boolean local unit u = GetTriggerDamageSource() local integer i = R2I(GetTriggerDamage()) if GetTriggerDamageTarget() == dawn and GetUnitUserData(u) == i then call SetUnitUserData(u, 0) set u = null return false elseif GetTriggerDamageTarget() == dawn then set u = null return true else set u=null return false endif endfunction private function Actions takes nothing returns nothing local unit u = GetTriggerDamageSource() local real dam = GetTriggerDamage() local real life = GetWidgetLife(dawn) local real maxlife = GetUnitState(dawn, UNIT_STATE_MAX_LIFE) local real damage = maxlife-life local timer c = CreateTimer() local timer t = CreateTimer() local string mc = I2S(h2i(c)) local string mt = I2S(h2i(t)) local damstore ds = damstore.create() local damstore dt = damstore.create() if dam <= damage then call SetWidgetLife(dawn, life+dam) elseif dam<= life+0.405 then set dt.damg = dam call StoreInteger(DamageSystem_Cache, mt, "dt", dt) else call SetWidgetLife(dawn, life+dam) set dt.damg = dam-damage call StoreInteger(DamageSystem_Cache, mt, "dt", dt) endif set ds.damg = dam set ds.u = u set ds.dtype = GetTriggerDamageType() call StoreInteger(DamageSystem_Cache, mc, "damstore", ds) call TimerStart(t, 0.01, false, function HealDamage) call TimerStart(c, DamageDelay, false, function DawnLeft) set u = null set c = null set t = null endfunction public function InitTrig takes nothing returns nothing local trigger t = CreateTrigger() set dawn = Hero_Dawn[0] call TriggerRegisterDamageEvent(t) call TriggerAddCondition(t, Condition(function Conditions)) call TriggerAddAction(t,function Actions) set t = null endfunction endscope I am concerned that a unit's damage will be able to slip through the delay between the time I just realised my concern there is utterly irrelevant. If a unit deals the same damage, and thus falsely unflags itself before the delayed damage is dealt, the delayed damage will merely recur, and it is the exact same damage, so there's no actual problem. Silly me. Anyway, I'm not quite certain how WC3 will handle this event - is it actually possible to have a new trigger execute while an existing trigger is running? I do know that War3 will never handle events simultaneously (at least as far as damage is concerned) - I'm just not sure how this applies to threads. Second problem - actions taken as a damage response occur before the damage is dealt. Is there a way to deal the damage and then apply actions without using a 0.01 timer? At the moment, I can notice the rapid health changes when the unit takes damage and has to be post-healed. As a final note, am I correct in assuming it is impossible to acquire the details of Weapon Type and/or Attack Type from the damage event? Anyway, this was a bit long-winded, but thankyou for any help. I hope I've been clear :). |
| 08-20-2008, 07:55 PM | #2 | |
Quote:
In order to prevent the initial damage... Use timers instead of TriggerSleepAction if you want it to be less noticeable. JASS:function OnTakesDamage takes unit target, real takenDamage returns nothing call UnitAddAbility(target,ITEM_ABILITY_HIT_POINT_BONUS) // TSA is okay for 0. call TriggerSleepAction(0) call UnitRemoveAbility(target,ITEM_ABILITY_HIT_POINT_BONUS) call SetUnitState(target,UNIT_STATE_LIFE,takenDamage + GetUnitState(target,UNIT_STATE_LIFE)) endfunction |
| 08-20-2008, 08:01 PM | #3 | |||
Quote:
Quote:
Quote:
|
| 08-20-2008, 08:17 PM | #4 |
Threads can get interrupted in execution by other threads even when they don't wait - it happens precisely in situations when a function call in one thread causes an event of a trigger to run, for example creating units and unit-enters-region events as well as damage actions and unit-takes-damage events. You're hitting a lot of very complicated problems with this spell, and it'll be difficult for me to adress them all in a single post. It would be smart to leave damage handling to systems; multiple spells trying to do damage detection and prevention each with it's own code is bound to bug sometime. For starters, there's Vexorian's xedamage that lets you tag triggered damage you deal, this is a good way to avoid recursion since when damage is dealt you can check if it was triggered damage and what it's tag is, that way you can prevent the delayed damage from triggering the delay effect again. Then there's my ADamage system that handles damage detection and prevention (not approved yet so I might have to change things that will break backwards compatibility; I mention it because no other damage detection systems in the database cover damage prevention), I don't consider it sane to try to do damage prevention without a system like that, due to all the nuances involved; just think of the trouble you can get in if you try to deal damage in response to damage being dealt. You're right that you can't detect attack and damage types of damage being dealt ingame, but the good news is that when you detect damage being dealt it is already reduced by armour type and amount, so when you deal that same damage with a delayed effect you shouldn't deal it with it's original attack&damage type, but a type that doesn't get reduced by armour at all. |
