package Damager constant MAX_SUB_OPTIONS = 3 /** Needs to be around 20 if armor is not wanted to be ignored */ constant FACTOR_TEST_DAMAGE = 0.01 /** Maximum damage factor */ constant MAX_DAMAGE_FACTOR = 3.00 constant EPSILON = 0.000000001 unit dmger public class Damager boolean damageSelf = false // the damage and factor methods usually have a source unit parameter // xedamage would consider this unit as immune unless you damageSelf to true boolean damageAllies = false // Alliance dependent target options. boolean damageEnemies = true // * boolean damageNeutral = true // * boolean ranged = true // Is the attack ranged? This has some effect on the AI of the affected units // true by default, you may not really need to modify this. boolean visibleOnly = false // Should only units that are visible for source unit's owner be affected? boolean deadOnly = false // Should only corpses be affected by "the damage"? (useful when using xedamage as a target selector) boolean alsoDead = false // Should even corpses and alive units be considered? boolean damageTrees = false //Also damage destructables? Notice this is used only in certain methods. //AOE for example targets a circle, so it can affect the destructables //in that circle, a custom spell using xedamage for targetting configuration //could also have an if--else implemented so it can verify if it is true // affect trees manually. // // Damage type stuff: // .dtype : the "damagetype" , determines if the spell is physical, magical or ultimate. // .atype : the "attacktype" , deals with armor. // .wtype : the "weapontype" , determines the sound effect to be played when damage is done. // // Please use common.j/blizzard.j/ some guide to know what damage/attack/weapon types can be used // damagetype dtype = DAMAGE_TYPE_UNIVERSAL attacktype atype = ATTACK_TYPE_NORMAL weapontype wtype = WEAPON_TYPE_WHOKNOWS // // Damage type 'tag' people might use xedamage.isInUse() to detect xedamage usage, there are other static // variables like xedamage.CurrentDamageType and xedamage.CurrentDamageTag. The tag allows you to specify // a custom id for the damage type ** Notice the tag would aid you for some spell stuff, for example, // you can use it in a way similar to Rising_Dusk's damage system. // int tag = 0 // // if true, forceDamage will make xedamage ignore dtype and atype and try as hard as possible to deal 100% // damage. boolean forceDamage = false // // Ally factor! Certain spells probably have double purposes and heal allies while harming enemies. This // field allows you to do such thing. // real allyfactor = 1.0 // // field: .exception = SOME_UNIT_TYPE // This field adds an exception unittype (classification), if the unit belongs to this unittype it will // be ignored. // function setException( unittype ut ) use_ex = true ex_ut = ut // // field: .required = SOME_UNIT_TYPE // This field adds a required unittype (classification), if the unit does not belong to this unittype // it will be ignored. // function setReq( unittype ut ) use_req = true req_ut = ut boolean use_ex = false unittype ex_ut = null boolean use_req = false unittype req_ut = null int array[MAX_SUB_OPTIONS] fct real array[MAX_SUB_OPTIONS] fc integer fcn=0 // // method .factor(SOME_UNIT_TYPE, factor) // You might factor() if you wish to specify a special damage factor for a certain classification, // for example d.factor(UNIT_TYPE_STRUCTURE, 0.5) makes xedamage do half damage to structures. // function factor( unittype ut, real fc ) if fcn == MAX_SUB_OPTIONS printWarning("In one instance of Damager, you are doing too much calls to factor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of factor() calls") return fct[fcn] = convertUnitTypeToInt( ut ) this.fc[fcn] = fc fcn++ Table abifct = new Table() Table abifc = new Table() int abifcn=0 // // method .abilityFactor('abil', factor) // You might abilityFactor() if you wish to specify a special damage factor for units that have a // certain ability/buff. // for example d.abilityFactor('A000', 1.5 ) makes units that have the A000 ability take 50% more // damage than usual. // function abilityFactor( int abilityId, real fc ) if abifcn == MAX_SUB_OPTIONS printWarning("In one instance of Damager, you are doing too much calls to abilityFactor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of abilityFactor() calls") return abifct.saveInt( abifcn, abilityId ) abifc.saveReal( abifcn, fc ) abifcn++ boolean usefx = false string fxpath string fxattach // // method .useSpecialEffect("effect\\path.mdl", "origin") // Makes it add (and destroy) an effect when damage is performed. // function useSpecialEffect( string path, string attach ) usefx = true fxpath=path fxattach=attach //******************************************************************** //* Now, the usage stuff: //* //================================================================================ // static method xedamage.isInUse() will return true during a unit damaged // event in case this damage was caused by xedamage, in this case, you can // read variables like CurrentDamageType, CurrentAttackType and CurrentDamageTag // to be able to recognize what sort of damage was done. // static damagetype currentDamageType=null static attacktype currentAttackType=null static int currentDamageTag =0 static int inUse = 0 function isInUse() returns boolean return inUse > 0 //inline friendly. static boolean isDummyDamage = false //======================================================================================================== // This function calculates the damage factor caused by a certain attack and damage // type, it is static : xedamage.getDamageTypeFactor(someunit, ATTAcK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, 100) // static function getDamageTypeFactor( unit u, attacktype a, damagetype d ) returns real real hp = GetWidgetLife(u) real mana = GetUnitState(u,UNIT_STATE_MANA) real r real fc = FACTOR_TEST_DAMAGE //Since a unit is in that point, we don't need checks. SetUnitX(dmger,GetUnitX(u)) SetUnitY(dmger,GetUnitY(u)) SetUnitOwner(dmger,GetOwningPlayer(u),false) r = hp if hp < FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR SetWidgetLife(u, hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR ) r = hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR fc = GetWidgetLife(u)-hp + EPSILON isDummyDamage = true UnitDamageTarget(dmger,u, fc ,false,false,a,d,null) if IsUnitType(u, UNIT_TYPE_DEAD) and (hp>0.405) error("xedamage: For some reason, the unit being tested by getDamageTypeFactor has died. Verify MAX_DAMAGE_FACTOR is to a correct value. ") isDummyDamage = false SetUnitOwner(dmger,Player(15),false) if (mana>GetUnitState(u,UNIT_STATE_MANA)) //Unit had mana shield, return 1 and restore mana too. SetUnitState(u,UNIT_STATE_MANA,mana) r=1 else r= (r-GetWidgetLife(u)) / fc SetWidgetLife(u,hp) return r function getTargetFactorCore( unit source, unit target, boolean usetypes ) returns real player p=GetOwningPlayer(source) boolean allied=IsUnitAlly(target,p) boolean enemy =IsUnitEnemy(target,p) boolean neutral=allied real f real negf=1.0 int i if damageAllies != damageNeutral neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_REQUEST )) //I thought accuracy was not as important as speed , I think that REQUEST is false is enough to consider // it neutral. //neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_RESPONSE )) //neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_XP )) //neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_SPELLS )) allied= allied and not(neutral) if (not this.damageAllies) and allied return 0.0 else if (not this.damageEnemies) and enemy return 0.0 else if( (not this.damageSelf) and (source==target) ) return 0.0 else if (not this.damageNeutral) and neutral return 0.0 else if( this.use_ex and IsUnitType(target, this.ex_ut) ) return 0.0 else if( this.visibleOnly and not IsUnitVisible(target,p) ) return 0.0 else if ( this.deadOnly and not IsUnitType(target,UNIT_TYPE_DEAD) ) return 0.0 else if ( not(this.alsoDead) and IsUnitType(target,UNIT_TYPE_DEAD) ) return 0.0 f=1.0 if ( IsUnitAlly(target,p) ) f=f*this.allyfactor if f <= -EPSILON f=-f negf=-1.0 if (this.use_req and not IsUnitType(target,this.req_ut)) return 0.0 i=fcn-1 while i >= 0 if( IsUnitType(target, ConvertUnitType(fct[i]) ) ) f=f*this.fc[i] if(f <= -EPSILON) f=-f negf=-1.0 i=i-1 i= abifcn-1 while i >= 0 if( GetUnitAbilityLevel(target,this.abifct.loadInt(i) )>0 ) f=f*this.abifc.loadReal(i) if(f <= -EPSILON) f=-f negf=-1.0 i=i-1 f=f*negf if ( f < EPSILON) and (f > -EPSILON) return 0.0 if( this.forceDamage or not usetypes ) return f f=f*Damager.getDamageTypeFactor(target,this.atype,this.dtype) if ( f < EPSILON) and (f > -EPSILON) return 0.0 return f //==================================================================== // With this you might decide if a unit is a valid target for a spell. // function getTargetFactor( unit source, unit target ) returns real return this.getTargetFactorCore(source,target,true) //====================================================================== // a little better, I guess // function allowedTarget( unit source, unit target ) returns boolean return (this.getTargetFactorCore(source,target,false)!=0.0) //======================================================================= // performs damage to the target unit, for unit 'source'. // function damageTarget( unit source, unit target, real damage ) returns boolean damagetype dt=currentDamageType attacktype at=currentAttackType int tg=currentDamageTag real f = this.getTargetFactorCore(source,target,false) real pl if(f!=0.0) currentDamageType = dtype currentAttackType = atype currentDamageTag = tag inUse = inUse +1 pl=GetWidgetLife(target) UnitDamageTarget(source,target, f*damage, true, ranged, atype, dtype, wtype ) inUse = inUse -1 currentDamageTag = tg currentDamageType = dt currentAttackType = at if(pl != GetWidgetLife(target) ) if(usefx) DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) ) return true return false //======================================================================================= // The same as damageTarget, but it forces a specific damage value, good if you already // know the target. // function damageTargetForceValue( unit source, unit target, real damage ) damagetype dt= currentDamageType attacktype at= currentAttackType int tg= currentDamageTag currentDamageType = dtype currentAttackType = atype currentDamageTag = tag if( usefx) DestroyEffect( AddSpecialEffectTarget(fxpath, target, fxattach) ) inUse = inUse +1 UnitDamageTarget(source,target, damage, true, ranged, null, null, wtype ) inUse = inUse -1 currentDamageTag = tg currentDamageType = dt currentAttackType = at //===================================================================================== // Notice: this will not Destroy the group, but it will certainly empty the group. // function damageGroup( unit source, group targetGroup, real damage ) returns int damagetype dt=currentDamageType attacktype at=currentAttackType int tg=currentDamageTag unit target real f int count=0 real hp = 0. currentDamageType = dtype currentAttackType = atype currentDamageTag = tag inUse = inUse +1 target=FirstOfGroup(targetGroup) while target != null GroupRemoveUnit(targetGroup,target) f= this.getTargetFactorCore(source,target,false) if (f!=0.0) count=count+1 if(usefx) hp = GetWidgetLife(target) UnitDamageTarget(source,target, f*damage, true, ranged, atype, dtype, wtype ) if(usefx and (hp > GetWidgetLife(target)) ) DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) ) target=FirstOfGroup(targetGroup) inUse = inUse -1 currentDamageTag=tg currentDamageType = dt currentAttackType = at return count static Damager instc int countAOE unit sourceAOE real aoeX real aoeY real aoeRadius real aoeDamage static boolexpr filterAOE static boolexpr filterDestAOE static group enumgroup static rect aoeRect //========================================================================================== // will affect trees if damageTrees is true! // function damageAOE( unit source, real x, real y, real radius, real damage ) returns int damagetype dt=currentDamageType attacktype at=currentAttackType int tg=currentDamageTag currentDamageType = dtype currentAttackType = atype currentDamageTag = tag inUse = inUse +1 instc=this countAOE=0 sourceAOE=source aoeX=x aoeRadius=radius aoeY=y aoeDamage=damage GroupEnumUnitsInRange(enumgroup,x,y,radius+MAX_COLLISION_SIZE, filterAOE) if(damageTrees) SetRect(aoeRect, x-radius, y-radius, x+radius, y+radius) aoeRadius=aoeRadius*aoeRadius EnumDestructablesInRect(aoeRect, filterDestAOE, null) inUse = inUse -1 currentDamageTag = tg currentDamageType = dt currentAttackType = at return countAOE //========================================================================================== // only affects trees, ignores damageTrees // function damageDestructablesAOE( unit source, real x, real y, real radius, real damage ) returns int instc=this countAOE=0 sourceAOE=source aoeX=x aoeRadius=radius*radius aoeY=y aoeDamage=damage //if(.damageTrees) SetRect(aoeRect, x-radius, y-radius, x+radius, y+radius) EnumDestructablesInRect(aoeRect, filterDestAOE, null) //endif return countAOE //'friend' with the library init static function initD() aoeRect= Rect(0,0,0,0) filterAOE= Condition(function damageAOE_Enum) filterDestAOE = Condition( function damageAOE_DestructablesEnum) enumgroup = CreateGroup() function damageAOE_Enum() returns boolean unit target=GetFilterUnit() Damager dmg=Damager.instc //adopting a instance. real f real hp = 0. if( not IsUnitInRangeXY(target, dmg.aoeX, dmg.aoeY, dmg.aoeRadius) ) return false f = dmg.getTargetFactorCore(dmg.sourceAOE, target, false) if(f!=0.0) dmg.countAOE++ if(dmg.usefx) hp =GetWidgetLife(target) UnitDamageTarget(dmg.sourceAOE,target, f*dmg.aoeDamage, true, dmg.ranged, dmg.atype, dmg.dtype, dmg.wtype ) if dmg.usefx and (hp > GetWidgetLife(target) ) DestroyEffect( AddSpecialEffectTarget(dmg.fxpath, target, dmg.fxattach) ) Damager.instc= dmg //better restore, nesting IS possible! return false function damageAOE_DestructablesEnum() returns boolean destructable target=GetFilterDestructable() Damager dmg=Damager.instc //adopting a instance. real dx=dmg.aoeX-GetDestructableX(target) real dy=dmg.aoeY-GetDestructableY(target) if( dx*dx + dy*dy >= dmg.aoeRadius+EPSILON ) return false dmg.countAOE++ if(dmg.usefx) DestroyEffect( AddSpecialEffectTarget(dmg.fxpath, target, dmg.fxattach) ) UnitDamageTarget(dmg.sourceAOE,target, dmg.aoeDamage, true, dmg.ranged, dmg.atype, dmg.dtype, dmg.wtype ) Damager.instc= dmg //better restore, nesting IS possible! return false init dmger=CreateUnit(Player(15), DUMMY_UNIT_ID , 0.,0.,0.) UnitAddAbility(dmger,'Aloc') Damager.initD()