| 06-30-2011, 04:05 PM | #1 |
Hello guys, this is me asking another one of my questions. Basically, I have in my map a unit that I don't want to be killed or even attacked by friendly units. To prevent this I've simply made friendly attackers be ordered to stop if they try to attack, and this works just fine. But splash damage from for example catapults still deal damage (obviously), so what I need is a method of prevent my unit from taking splash damage from friendly sources. Thanks in advance. |
| 06-30-2011, 04:29 PM | #2 |
Damage detection is the way. You add a condition that detects if the attacking player is an ally and do the magic. |
| 06-30-2011, 04:30 PM | #3 |
Well what I'm asking is how to "do the magic", really. |
| 06-30-2011, 04:56 PM | #4 |
You could give your unit a classification such as "suicidal" (provided you don't already use it for anything else) and then only allow non-suicidal units to be damaged by splash damage of units the player can use. The problem with this solution is that if the enemies can use the same units as you, their splash damage will also ignore this unit. The alternative is to make all splash damage ignore friendly units, but then none of your units will take damage from your catapults. If you don't mind using triggers for this, you could use a Damage Modifier. |
| 06-30-2011, 07:45 PM | #5 |
I don't mind using triggers. Can you help a fellow newbie on how to use the Damage Modifier to achieve my desired result? |
| 06-30-2011, 08:35 PM | #6 |
Sure. First of all, you need to import the DamageEvent and DamageModifiers libraries into your map. I suggest the Table version of DamageModifiers since Table is slightly easier to import than AutoIndex. Once you have Table, DamageEvent and DamageModifiers in your map (all three are simple copy&paste jobs, just create a trigger, convert it to custom text and overwrite it's contents with the library code), try saving the map. If you have the NewGen editor with the latest JassHelper, this should give you no problems, else you should update your JassHelper. If the map saves successfully then DamageModifiers will automatically generate the bonus life ability it requires to be able to block damage that is higher than a unit's max life. At this point, you should close and reopen the map, then scroll through the DamageModifiers documentation until you find the objectmerger call that generated the aforementioned ability (you can't miss it, it's the only grey-coloured line in the library) and disable it. This will speed up map saves in the future since the editor won't have to generate that ability every time. With that, DamageModifiers should be ready to use. If you want, you can also import one of the preload libraries linked to from the Damage Event thread, but those are completely optional. They're just used to prevent a lag spike that otherwise occurs the first time DamageModifiers needs to use its bonus life ability. JASS:library AlliedDamageBlocker requires DamageModifiers private struct blocker extends DamageModifier private player owner method onDamageTaken takes unit damageSource, real damage returns real if IsUnitAlly(damageSource, .owner) then return -damage // Block all damage. endif return 0.0 endmethod static method create takes unit u returns blocker local blocker this = blocker.allocate(u, 0) set .owner=GetOwningPlayer(u) return this endmethod endstruct function UnitBlockDamageFromAllies takes unit u returns nothing call blocker.create(u) endfunction /* To use this function in a GUI trigger, copy the following line to a custom script action: call UnitBlockDamageFromAllies( udg_YourUnitVariable ) I didn't include any code that would clean up the damage modifier when the unit dies, but if this is only used on a few units during the course of the game then that's not a problem. */ endlibrary |
| 06-30-2011, 08:48 PM | #7 |
This should help you. Zinc://! zinc library PreventAlliedDamage requires DamageEvent, DamageModifiers, AutoIndex { constant integer UNIT_TYPE_ID = 'unit'; /* Change this to the type ID of the unit that you don't want to receive damage. */ constant integer DAMAGE_MODIFIER_PRIORITY = 0x7FFFFFFF; /* Highest possible priority for the damage modifier. */ struct preventIncomingDamage extends DamageModifier { /* This is the filter for the automatic allocation provided by the AutoCreate module. You are free to insert whatever conditions you need here. */ static method createFilter(unit enteringUnit) -> boolean { return (GetUnitTypeId(enteringUnit) == UNIT_TYPE_ID); } /* This method is called whenever the unit for which we created the damage modifier is damaged. */ method onDamageTaken(unit damageSource, real damageAmount) -> real { if (IsUnitAlly(damageSource, GetOwningPlayer(this.me))) { return -damageAmount; /* If the damage source was an ally, then block the whole damage that was dealt. */ } return 0.0; /* Otherwise, return zero in order not to block any damage at all. */ } /* This method will be called by AutoCreate when it tries to automatically create the struct for a certain unit. */ static method create(unit enteringUnit) -> thistype { thistype this = thistype.allocate(enteringUnit, DAMAGE_MODIFIER_PRIORITY); /* Since we are extending the DamageModifier struct, our .allocate() method needs to match the parameters of extended struct's .create() method. */ return this; } /* Implement the AutoCreate and the AutoDestroy modules. */ module AutoCreate; module AutoDestroy; } } //! endzinc EDIT: Okay, Anitarf got to it first. I spent five minutes for nothing. |
| 06-30-2011, 09:49 PM | #8 | |
Quote:
|
| 06-30-2011, 09:50 PM | #9 | |
Quote:
You can use a triggered evasion, with the ally condition. Link to my DD system: http://www.wc3c.net/showpost.php?p=1024305&postcount=2 1. Create a trigger, call it "damage detection", set it to custom text, erase the text done by default and replace it with this code: JASS://****************************************************************************** //* //* Damage Detection script //* By moyack. 2009 //* //* ============================================================================ //* Credits to Litany, DioD, Captain Griffen, Troll Brain and other nice guys * //* for their suggestions, comments and ideas. * //* ============================================================================ //* //* For Damage detection, you just need to use these functions: //* //* => AddDamageCondition(<Boolexpr variable>) returns nothing //* ------------------------------------------------------- //* Just add a condition function which manage the damaged unit and the script //* will use it with all the the units in the DD.D group. //* //* => CreateDummy( player, 'Unitid', x, y, facing_Angle ) returns unit //* ---------------------------------------------------------------- //* Like the CreateUnit function, but all the units created with this function //* won't trigger the this Damage Detection script. //* //* => DoNonDetectableDamage(unit, widget, damage, boolean_attack, boolean_ranged, attacktype, damagetype, weapontype) returns boolean //* ------------------------------------------------------------------------------------------------------------------------------- //* Like the UnitDamageTarget function, but it can be used inside condition functions. //* How to know if you need to use it? if you use UnitDamageTarget() inside a DD function and //* it freezes the game until it kills the attacked unit(s), then you have to replace that //* function by this custom one. //******************************************************************************** library UnitDamage struct DD // damage detection struct static group D // group of units that will have damage detection static group Dummies // group of units which will not be detected by the system static trigger T // Add the attacked unit to the damage detection if the unit is not in the pack static method AddUnit takes unit d returns nothing if not IsUnitInGroup(d, thistype.D) and not IsUnitInGroup(d, thistype.Dummies) then debug call DisplayTimedTextFromPlayer(GetLocalPlayer(), 0,0,2,"Added " + GetUnitName(d) + " to the DD") call TriggerRegisterUnitEvent(thistype.T, d, EVENT_UNIT_DAMAGED) call GroupAddUnit(thistype.D, d) endif set d = null endmethod private static method Attacked takes nothing returns nothing call thistype.AddUnit(GetTriggerUnit()) endmethod private static method Spelled takes nothing returns nothing if GetSpellTargetUnit() != null then call thistype.AddUnit(GetSpellTargetUnit()) endif endmethod private static method onInit takes nothing returns nothing set thistype.T = CreateTrigger() set thistype.D = CreateGroup() set thistype.Dummies = CreateGroup() call TriggerRegisterAnyUnitEventBJ(thistype.T, EVENT_PLAYER_UNIT_ATTACKED) call TriggerAddAction(thistype.T, function thistype.Attacked) set thistype.T = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(thistype.T, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddAction(thistype.T, function thistype.Spelled) set thistype.T = CreateTrigger() endmethod endstruct // Damage detection system functions... function AddDamageCondition takes code func returns nothing call TriggerAddCondition(DD.T, Condition(func)) endfunction function CreateDummy takes player p, integer id, real x, real y, real f returns unit local unit u = CreateUnit(p, id, x, y, f) call GroupAddUnit(DD.Dummies, u) return u endfunction function DoSafeDamage takes unit u, widget t, real damage, boolean attack, boolean ranged, attacktype AT, damagetype DT, weapontype WT returns boolean local boolean b call DisableTrigger(DD.T) set b = UnitDamageTarget(u, t, damage, attack, ranged, AT, DT, WT) call EnableTrigger(DD.T) return b endfunction function AddUnittoDD takes unit u returns nothing call DD.AddUnit(u) endfunction endlibrary 2. Create other trigger, put it "AntisplashDamage", and like the first step, erase the ugly code and replace it with this: Block Damage from Allies - requires TimerUtils:// requires a passive ability to work. I didi it in this way because it hink this can be somthing // upgradable and/or configurable. Requires Damage Detection scope AvoidAllySplashDamage initializer init globals private constant integer SpellID = 'A000' // the ability that allos the unit to avoid splash damage private constant real chance = 1. // 100% of chance to do the effect private constant real absorb = 1. // 100% damage absorbtion = total evasion private group G = CreateGroup() endglobals private struct data unit u static method create takes unit u returns data local data d = data.allocate() set d.u = u return d endmethod endstruct private function Heal takes nothing returns nothing local data d = data(GetTimerData(GetExpiredTimer())) call SetUnitInvulnerable(d.u, false) call ReleaseTimer(GetExpiredTimer()) call d.destroy() endfunction private function DoEffect takes nothing returns boolean local unit u = GetTriggerUnit()// unit that receives the damage local player p = GetOwningPlayer(GetEventDamageSource()) // player of the unit which deals damage local timer tm if GetUnitAbilityLevel(u, SpellID) > 0 and GetEventDamage() > 0 and IsUnitAlly(u, p) then call SetUnitInvulnerable(u, true) set tm = NewTimer() call SetTimerData(tm, integer(data.create( u))) call TimerStart(tm, 0., false, function Heal) endif set tm = null set u = null return false endfunction private function AddUnits takes nothing returns boolean call AddUnittoDD(GetFilterUnit()) return false endfunction private function DoStuff takes nothing returns nothing call GroupEnumUnitsOfPlayer(G, Player(0), Condition(function AddUnits)) call ReleaseTimer(GetExpiredTimer()) endfunction private function init takes nothing returns nothing call AddDamageCondition(function DoEffect) call TimerStart(NewTimer(), 0.1, false, function DoStuff) endfunction endscope 3. And that's all :) Edit: Map attached |
