| 02-13-2009, 11:47 AM | #1 |
Hi guys, I am making some sort of inverse crtitical strike with ADamage, in the case, when a unit with a certain buff takes damage, the damage is amplified by a factor, lets say 20%. Everything was going well, I am using ADamage and xedamage module, however I enter on a infinite loop (obvious...) because I can not tell teh difference between the damage being amplified and the damage taken in combat normally. This way, when a unit takes damage, I amplify the damage by dealing it again, and then the computer takes the amplified damage as normal damages and amplifies it again, this loop until the unit dies or the CPU blows, depends on the one that comes first. My possible solution would be to use boolean values, but they are useless, the other solution would be to cause the amplified damage as a special damage with xedamage, however I don't know how to do that either. The code is in my Light SHield thread, if some one is interested, but I really need to figure out a solution for this since I want to make many other amplify damage spells that don't involve shields. I would appreciate all possible help, thx in advance. |
| 02-13-2009, 12:07 PM | #2 |
Use a different type of damage for the amplified damage and then a triggercondition to detect it. |
| 02-13-2009, 12:32 PM | #3 | |
Quote:
I know I have to use xedamage tags, but I make no idea on how to use them and the documentation lacks examples on it =S |
| 02-13-2009, 12:36 PM | #4 |
option 1 (which I use) is just disabling the trigger before you do the bonus damage, then re-enabling it afterward option 2 would be to add some dependancy to the trigger that you can manipulate before nad after the damage - for instance, a group within which a unit cannot be for the trigger to fire (adding it before the damage and removing it after allows this to work) - there are several other methods to the same end, but you get it option 3 (when using something like IDDS) is to just deal the damage of a type that wont fire the trigger, option 4 - there are probably more but you get the point |
| 02-13-2009, 01:05 PM | #5 |
Use dummy unit(s) who ignored by system. If you need real damage source, you always can link real unit to dummy. |
| 02-13-2009, 01:14 PM | #6 |
Look at my Thornwine Armour spell, it only returns damage when it was non-triggered damage (you could also easily modify it to only ignore some damage tags rather than all triggered damage). |
| 02-13-2009, 01:14 PM | #7 | ||||||
Quote:
Quote:
Quote:
Quote:
Quote:
Quote:
Off-topic: - Having in mind how emjlr3 usually posts in my threads, I want to tank to emjlr3for being nice and helpful this time. Thx m8 =D |
| 02-13-2009, 01:32 PM | #8 | |
Quote:
|
| 02-13-2009, 01:35 PM | #9 |
What thing doesn't trigger a EVENT_UNIT_DAMAGED?? a simulated damage. Here's my example of a triggered critical strike using my powerful and uber simple damage detection script, you can adapt that function to other DD systems: JASS:scope TriggeredCriticalStrike initializer init globals private constant integer SpellID = 'A000' private constant real chance = 0.8 // 80% of chance to do the effect private constant real factor = 3. // multiplier damage factor endglobals private function DoEffect takes nothing returns boolean local real d = GetEventDamage()// Damage dealt normally local unit u = GetTriggerUnit()// unit that receives the damage local real l = GetWidgetLife(u)// unit life local unit c = GetEventDamageSource()// unit that deals the damage local texttag t = CreateTextTag() local real D // the damage bonus value... if GetRandomReal(0,1) < chance and GetUnitAbilityLevel(c, SpellID) > 0 and d > 0 then set D = d * (factor - 1.) if l - (d + D) <= 0.405 then // if the damage is enough to kill the target call SetWidgetLife(u, d * 0.9) // sets the life of the target unit in such way that the damage dealt will kill it else call SetWidgetLife(u, l - D) // Reduce the life of the unit by the damage bonus endif // the remaining damage will be done after the end of this code... // Eyecandy part :) call SetTextTagText(t, I2S(R2I(d + D))+"!", 0.024) call SetTextTagPos(t, GetUnitX(c), GetUnitY(c), 0.00) call SetTextTagColor(t, 255, 0, 0, 255) call SetTextTagVelocity(t, 0, 0.04) call SetTextTagVisibility(t, true) call SetTextTagFadepoint(t, 2) call SetTextTagLifespan(t, 5) call SetTextTagPermanent(t, false) else call DestroyTextTag(t) endif set t = null set u = null set c = null return false endfunction private function init takes nothing returns nothing call AddDamageCondition(Condition(function DoEffect)) endfunction endscope |
| 02-13-2009, 01:50 PM | #10 | ||
Quote:
On the other hand, I am totally not following/understanding your idea =S sorry Quote:
To be honest I already had that idea, but not your way ... I am amazed, you sure that thing runs well? Like, it I see you never kill the unit, you just make it weaker! This may be th perfect solution. Thx ! As far as I am concerned, it beats emil group solution =P |
| 02-13-2009, 02:14 PM | #11 |
Of course it works, and it has been tested. Remember that the EVENT_UNIT_DAMAGED happens just just just before deal the damage, when the code ends, the damage is actually dealt. |
| 02-13-2009, 02:31 PM | #12 |
Ha thx to all ! Problem fixed ! Here is the code of my Light Shield spell (for those who are curious. Yes, this is the wrong thread, but it is just for people who really are curious). I will update the Light SHield spell a little bit saying the good news to the people, and will wait for suggestions a few more hours (not more than 48). Then if no suggestions are made, I will submit the spell! And I am sure it will be approved, this is the most modular thing I have ever done without Ani or Dusk getting mad at me for not being modular (lol). JASS://=========================================================================== //A JESP spell that allows the hero to cast a shield on a not Ud ally or on an //Ud enemy (just like Holy Light). The shield will absorb an amount of damage for //the allied unit thus protecting it from harm, or it will amplify the damage caused //to the unit if it is enemy. //When the shield dies, the shield unit will get healed by the remaining energies //of teh shield if it is an ally, or it will be damage by the remaining energies if //it is an enemy. // //Requires: // - TimerUtils // - Table // - ABuff // - ADamage // - SimError // - LastOrder // - AbortSpell // //@author Flame_Phoenix // //@credits // - Vexorian, for TimerUtils, Table and SimError // - Anitarf, for ABuff and ADamage // - Rising_Dusk, for LastOrder and AbortSpell // - Moyack, for the tip on how to prevent infinite loops // - the_Immortal, the original creator of the spell gave me the idea for making my own // //@version 1.7 //=========================================================================== scope LightShield initializer Init //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A001' //raw of the ability of teh hero private constant integer AURA_ID = 'A002' //raw of the aura with the buff private constant integer AURA_BUFF = 'B000' //raw of the buff of the aura private constant string HEAL_EFFECT = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" //the effect that appears to heal allies or damage Ud private constant string BLOCK_EFFECT = "Abilities\\Weapons\\FlyingMachine\\FlyingMachineImpact.mdl" //the effect that appears to block damage private constant string AMPLIFY_EFFECT = "Objects\\Spawnmodels\\Orc\\Orcblood\\BattrollBlood.mdl" //the effect that appears when damage is amplified private constant integer SHIELD_PRIORITY = 0 //the priority of the damage prevention shield private constant string ERR_MESSAGE_1 = "\n|cffffcc00Can not cast on Undead allies.|r" private constant string ERR_MESSAGE_2 = "\n|cffffcc00The enemy must be Undead.|r" private constant string HOTKEY = "L" endglobals private constant function Duration takes integer level returns real //the duration of the spell return level * 15. endfunction private constant function ShieldAbsorb takes integer level returns real //the damage the shield can absorb return level * 100. endfunction private constant function BlockPercent takes integer level returns real //the percentage of the damage that will be blocked return .2 * level endfunction private constant function AmplifyPercent takes integer level returns real //the percentage of extra damage that the undead unit will take return .2 * level endfunction //the units that can be affected by the shield private function ShieldCond_1 takes unit caster, unit targ returns boolean //In this case we do not allow the shield to be cast on undead allies return IsUnitAlly(targ, GetOwningPlayer(caster)) and IsUnitType(targ, UNIT_TYPE_UNDEAD) endfunction private function ShieldCond_2 takes unit caster, unit targ returns boolean //In this case we do not allow the shield to be cast on enemies that are not undead return IsUnitEnemy(targ, GetOwningPlayer(caster)) and not(IsUnitType(targ, UNIT_TYPE_UNDEAD)) endfunction private function setupDamageOptions takes xedamage d returns nothing set d.dtype = DAMAGE_TYPE_UNIVERSAL set d.atype = ATTACK_TYPE_NORMAL endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== globals public aBuffType id = 0 private xedamage damageOptions endglobals //=========================================================================== //damage shield struct private struct lightShield extends ADamage_shield real prevented //the prevented damage real life //life of the shield unit shielded //the unit with the shield integer level boolean isUndead unit caster //this boolean prevents us from dealing extra damage when the shield dies //remember that when the shield dies, if it has energy, it will damage //the undead unit. This boolean is the last shield damage the undead unit //will receive, and we don't want it to be amplified boolean lastDamage static method create takes unit u, unit cast, integer level returns lightShield local lightShield shieldData = lightShield.allocate(u, SHIELD_PRIORITY) set shieldData.caster = cast set shieldData.shielded = u set shieldData.level = level set shieldData.life = ShieldAbsorb(level) set shieldData.isUndead = IsUnitType(u, UNIT_TYPE_UNDEAD) set shieldData.lastDamage = false return shieldData endmethod method damaged takes unit damageSource, real damage returns real //here we see if the unit is ud or not. //if Ud, we set prevented to 0, because we won't prevent anything //instead we calculate the extra damage in the Damage function //if not Ud it means it is an ally, and so we calculate the damage //it will block if .isUndead and not(.lastDamage) then set .prevented = 0 else set .prevented = damage * BlockPercent(.level) endif //If the damage would be enough to kill our shield, then we only block //the damage that the shield can, and we let the rest pass. if .prevented > .life then set .prevented = .life endif set .life = .life - .prevented return this.prevented endmethod method onDestroy takes nothing returns nothing //here we heal the unit with the shield with the remaining //energies of the shield, or damage it for the remaining energies //of the shield if it is Ud if .life > 0 and GetWidgetLife(.shielded) > 0.405 then if .isUndead then //to prevent infinite loop if (damageOptions.isInUse() == false) then set .lastDamage = true call damageOptions.damageTarget(.caster, .shielded, .life) endif else call SetWidgetLife(.shielded, GetWidgetLife(.shielded) + .life) endif call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT, .shielded, "chest")) endif endmethod endstruct //=========================================================================== private function Create takes aBuff eventBuff returns nothing //Add the aura to the shielded unit call UnitAddAbility(eventBuff.target.u, AURA_ID) //our buff aura only has 1 level, so we don't need this line //call SetUnitAbilityLevel(eventBuff.target.u, AURA_ID, eventBuff.level) //prevent morphing from removing the ability call UnitMakeAbilityPermanent(eventBuff.target.u, true, AURA_ID) //create the shield and attach it to the buff set eventBuff.data = integer(lightShield.create(eventBuff.target.u, eventBuff.caster, eventBuff.level)) endfunction //=========================================================================== private function Refresh takes aBuff eventBuff returns nothing call SetUnitAbilityLevel(eventBuff.target.u, AURA_ID, eventBuff.level) //get the old shield from the buff and destroy it call lightShield(eventBuff.olddata).destroy() //create the shield and attach it to the buff set eventBuff.data = lightShield.create(eventBuff.target.u, eventBuff.caster, eventBuff.level) //by remaking the shield instead of keeping the old one we make sure that //the shields follow the order defined by the NEW_SHIELD_ON_TOP boolean //this of course only matters if the map has multiple shields with the same priority endfunction //=========================================================================== private function Cleanup takes aBuff eventBuff returns nothing //remove the aura ability call UnitRemoveAbility(eventBuff.target.u, AURA_ID) //we also remove the buff, so we don't have to wait 2 seconds for it to disappear call UnitRemoveAbility(eventBuff.target.u, AURA_BUFF) //get the shield from the buff data and destroy it call lightShield(eventBuff.data).destroy() endfunction //=========================================================================== private function Damaged takes aBuff eventBuff, real damage, unit damageSource returns nothing local lightShield shieldData = lightShield(eventBuff.data) local real vicLife = GetWidgetLife(shieldData.shielded) local real bonus = damage * AmplifyPercent(shieldData.level) //if the shielded unit is undead, we damage it even more //else we do nothing because ADamage will heal it if shieldData.isUndead and not(shieldData.lastDamage) then if vicLife - (damage + bonus) <= 0.405 then // if the damage is enough to kill the target call SetWidgetLife(shieldData.shielded, damage * 0.9) // sets the life of the target unit in such way that the damage dealt will kill it else call SetWidgetLife(shieldData.shielded, vicLife - bonus) // Reduce the life of the unit by the damage bonus endif //here we damage the shield for the bonus amount of damage caused set shieldData.life = shieldData.life - bonus call DestroyEffect(AddSpecialEffectTarget(AMPLIFY_EFFECT, eventBuff.target.u, "chest")) else if shieldData.prevented > 0.0 then call DestroyEffect(AddSpecialEffectTarget(BLOCK_EFFECT, eventBuff.target.u, "chest")) endif endif //if the shield has no life, we destroy it if shieldData.life <= 0. then call ABuffDestroy(eventBuff) endif endfunction //=========================================================================== private function Conditions takes nothing returns boolean return GetSpellAbilityId() == AID endfunction //=========================================================================== private function Actions takes nothing returns nothing local unit caster = GetTriggerUnit() local integer level = GetUnitAbilityLevel(caster, AID) call ABuffApply(id, GetSpellTargetUnit(), caster, Duration(level), level, 0) set caster = null endfunction //=========================================================================== private function B4_Actions takes nothing returns nothing local unit caster = GetTriggerUnit() local unit targ = GetSpellTargetUnit() if ShieldCond_1(caster, targ) then call AbortSpell(caster, ERR_MESSAGE_1, HOTKEY) elseif ShieldCond_2(caster, targ) then call AbortSpell(caster, ERR_MESSAGE_2, HOTKEY) endif set caster = null set targ = null 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 ) ) call TriggerAddAction(LightShieldTrg, function Actions) //in case the minimum range is not met set LightShieldTrg = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(LightShieldTrg, EVENT_PLAYER_UNIT_SPELL_CAST ) call TriggerAddCondition(LightShieldTrg, Condition(function Conditions )) call TriggerAddAction(LightShieldTrg, function B4_Actions ) //Setting ABuff set id = aBuffType.create() set id.eventCreate = ABuffEvent_Create.Create set id.eventRefresh = ABuffEvent_Refresh.Refresh set id.eventCleanup = ABuffEvent_Cleanup.Cleanup set id.eventDamaged = ABuffEvent_BUnitDamaged.Damaged // Initializing the damage options: set damageOptions = xedamage.create() // first instanciate a xeobject. call setupDamageOptions(damageOptions) // now call the function we saw before. //Preloading our effects call Preload(HEAL_EFFECT) call Preload(BLOCK_EFFECT) call Preload(AMPLIFY_EFFECT) endfunction endscope |
| 02-13-2009, 03:24 PM | #13 |
Use 'hook' function. This is not easy, you cant "fix" natives, but you always can replace functions inside your code. JASS:function GetEventDamageSourceEX takes nothing returns unit if GetUnitTypeId(GetEventDamageSource()) == 'dummy' then return GetUnitUserData(GetEventDamageSource()) endif return GetEventDamageSource() endfunction NOTE: may contain functions misstypes. Replace GetEventDamageSource() with GetEventDamageSourceEX() in code, then paste function to map header. Such modifications allow you to ajust "natives". This "native" will return real damage source ever if damage done by dummy. Just store data inside custom value. |
| 02-13-2009, 03:33 PM | #14 |
Mmmm, I don't like the sound of UnitUserData and I think you are treating xedamage like IDDS. Anyway the problem was fixed, you can all visit this thread if you have cool ideas: http://www.wc3campaigns.net/showthread.php?t=104575 Anyway, +rep to all =D (only if wc3c doesn't block.) |
| 02-13-2009, 04:41 PM | #15 | ||
Quote:
ok, that really doesn't make sense but whatever - not sure why one would consider this bad practice (it works), and whats all this about modularity? Quote:
the only real difference being that you weren't acting like a child, so I didn't answer you as such |
