| 12-29-2009, 03:26 PM | #1 |
Description: With his twisted vision the Demon Hunter looks into the nether realm to better discern the fel essences of his foes and strike them with greater proficiency. Requirements: - ABuff by Anitarf (http://www.wc3c.net/showthread.php?t=95521) - ABuffDisplay by Anitarf (http://www.wc3c.net/showthread.php?t=95521) - IDDS by Rising_Dusk (http://www.wc3c.net/showthread.php?t=100618) - SpellEvent by Anitarf (http://www.wc3c.net/showthread.php?t=105374) Code: JASS://=====================================================================================================================// // Spectral Sight //=====================================================================================================================// // // DESCRIPTION // // With his twisted vision the Demon Hunter looks into the nether realm to better discern the fel essences of his // foes and strike them with greater proficiency. // The Demon Hunter perceives the surge of energy that forms within a unit when it casts a spell. Whenever he // attacks an enemy unit, he deals D bonus damage to it for each spell it has casted within the last C duration. // Lasts S duration. // //---------------------------------------------------------------------------------------------------------------------// // // REQUIREMENTS // // - ABuff by Anitarf ([url]http://www.wc3c.net/showthread.php?t=95521[/url]) // - ABuffDisplay by Anitarf ([url]http://www.wc3c.net/showthread.php?t=95521[/url]) // - IntuitiveDamageSystem by Rising_Dusk ([url]http://www.wc3c.net/showthread.php?t=100618[/url]) // - SpellEvent by Anitarf ([url]http://www.wc3c.net/showthread.php?t=105374[/url]) // //=====================================================================================================================// scope SpectralSight initializer Initializer //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// // -- CONFIGURATION -- globals // Id of this spell. // Must be a no-target type spell. private constant integer SPELL_ABILITYID = 'A004' // Currently based-off 'Berserk' because I don't want there to be delay in casting this spell. // Id of buff that indicates that the caster of this spell "deals additional attack damage to enemies that // have casted spells recently". private constant integer EFFECT_BUFFPLACER_BUFFID = 'B002' // Id of 'Tornado (Slow Aura)'-based ability that places the above buff. // Must have 'Stats - Targets Allowed (atar)' set to 'Self'. private constant integer EFFECT_BUFFPLACER_ABILITYID = 'A003' // Priority of this spell's damage response function for IDDS. This value must be calibrated to // the scale of damage response priorities in your map. private constant integer IDDS_RESPONSE_PRIORITY = 10 // The maximum duration after a spell is casted during which it adds to the count of the number of penalties // of its caster. // This must be greater than or equal to every value that may actually be outputted by GetMarkerDuration() // (below). private constant real ABSOLUTE_MAX_MARKER_DURATION = 100.000 // Model name of special effect that represents the state of using Spectral Sight. // This model must have a 'stand' animation. private constant string BUFF_FX_MODELNAME = "Abilities\\Spells\\Other\\Parasite\\ParasiteMissile.mdl" // Currently set to the Parasite (ability) model becaues it gives a nice green glow. // Attachment point for the above special effect. private constant string BUFF_FX_ATTACHPOINT = "head" // Currently set to the 'head' so that the glow looks as if its emanating from the caster's eyes. // Model name of special effect that represents the 'rift' of energy around a unit that has casted a spell // recently. This special effect is visible only to players that have units with Spectral Sight within their // detection range of the former unit. This special effect 'stacks'; that is, its intensity on a unit grows more // the unit casts spells. This model must have a 'stand' animation. // This special effect is visible only to players with nearby units that have Spectral Sight; it can be seen // only through Spectral Sight. private constant string HIGHLIGHT_FX_MODELNAME = "Abilities\\Spells\\NightElf\\Tranquility\\TranquilityTarget.mdl" // Attachment point for the above special effect. private constant string HIGHLIGHT_FX_ATTACHPOINT = "origin" // Length of interval between updates to hightlighting displays (see above). private constant real HIGHLIGHT_UPDATE_INTERVAL = 0.250 // Model name of special effect that represents the 'disruption of the essences' of marked targets // that are hit by units with Spectral Sight. // Because the damage event is instantaneous, this special effect is meant to be an 'instantaneous effect': // instantaneous like the special effect for 'Abolish Magic'; only the birth animation is meant to be played. // (However, this can still be manipulated to play 'death' animations because death animations are ran when a // special effect is destroyed, which is done immediately after the special effect is created in its usage // here.) // This special effect is displayed a number times that is equal to the number of cast instances considered by // the Spectral Sight instance for which the hit is applied. private constant string HIT_FX_MODELNAME = "Abilities\\Weapons\\GargoyleMissile\\GargoyleMissile.mdl" // Currently 'manipulating' with the 'death' animation of the Gargoyle projectile model. // Attachment point for the above special effect. private constant string HIT_FX_ATTACHPOINT = "chest" endglobals // Determines the duration of this spell's effect. private constant function GetEffectDuration takes integer level returns real return 20.000 endfunction // The duration after a spell is casted during which it adds to the count of the number of penalties of its caster. // Every value that may actually be outputted by this function must be less than or equal to // ABSOLUTE_MAX_MARKER_DURATION (above). private constant function GetMarkerDuration takes integer level returns real return 10.000 endfunction // Determines the bonus damage dealt to units for each spell they have casted recently. private constant function GetBonusDamage takes integer level returns real return level * 10.000 endfunction // Determines the range at which units with Spectral Sight can detect enemies that have casted spells recently. private constant function GetDetectRange takes integer level returns real return 800.000 endfunction // Determines if a spell leaves a rift on a unit that is to be detected by units with Spectral Sight. private function IsSpellDetectable takes integer abilityId returns boolean return true // and not IsSpellDummy(abilityId) endfunction // Determines if a caster is to be detected by units with Spectral Sight. private function IsCasterDetectable takes unit caster returns boolean return GetUnitAbilityLevel(caster, 'Aloc') == 0 // and not IsUnitDummy(caster) endfunction // -- CONFIGURATION END -- //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// private keyword Instance private keyword Marker globals private aBuffType ABUFFTYPE_SPECTRAL_SIGHT_EFFECT = 0 private aBuffType ABUFFTYPE_SPECTRAL_SIGHT_MARKER = 0 private integer DAMAGE_TYPE_SPECTRAL_SIGHT = 0 private Instance array insts private integer instsN = 0 private Marker array marks private integer marksN = 0 private integer count = 0 endglobals private struct Instance public unit caster public integer level public real markDur public real bonusDmg public real detectRng public effect fx public integer index private method onDestroy takes nothing returns nothing set .caster = null set .fx = null endmethod endstruct private struct Marker public unit subject public effect fx = null public aBuff abuff = 0 public integer index public method display takes nothing returns nothing local integer i = 0 local string fxPath = null local player owner = null local boolean array sensed loop set sensed[i] = false set i = i + 1 exitwhen i == 12 endloop // Evaluate if the subject of this Marker instance is 'sighted' by players (through their units that // have Spectral Sight). set i = 0 loop exitwhen i == instsN set owner = GetOwningPlayer(insts[i].caster) // If .subject (a target) is a valid target for the query Spectral Sight instance and it is detected, // mark .subject as 'sighted' for the owner of the caster of this Spectral Sight instance. if IsUnitEnemy(.subject, owner) and IsUnitInRange(.subject, insts[i].caster, insts[i].detectRng) and ABSOLUTE_MAX_MARKER_DURATION-GetABuffTimeRemaining(.abuff) < insts[i].markDur then set sensed[GetPlayerId(owner)] = true endif set i = i + 1 endloop // Display the highlighting special effect for .subject to players that can 'see' .subject. if sensed[GetPlayerId(GetLocalPlayer())] then set fxPath = HIGHLIGHT_FX_MODELNAME endif set .fx = AddSpecialEffectTarget(fxPath, .subject, HIGHLIGHT_FX_ATTACHPOINT) set owner = null endmethod private method onDestroy takes nothing returns nothing set .subject = null set .fx = null endmethod endstruct //-------------------------------------------------------------------------------------------------------------// // -- ABuff Event Responses -- private function OnEffectABuffCreate takes aBuff eventBuff returns nothing local Instance inst = Instance.create() set inst.caster = eventBuff.caster set inst.level = eventBuff.level set inst.markDur = GetMarkerDuration(inst.level) set inst.bonusDmg = GetBonusDamage(inst.level) set inst.detectRng = GetDetectRange(inst.level) set inst.fx = AddSpecialEffectTarget(BUFF_FX_MODELNAME, inst.caster, BUFF_FX_ATTACHPOINT) set inst.index = instsN set insts[instsN] = inst set instsN = instsN + 1 set eventBuff.data1 = integer(inst) endfunction private function OnEffectABuffRefresh takes aBuff eventBuff returns nothing local Instance inst = Instance(eventBuff.data1) set inst.level = eventBuff.level set inst.markDur = GetMarkerDuration(inst.level) set inst.bonusDmg = GetBonusDamage(inst.level) set inst.detectRng = GetDetectRange(inst.level) call eventBuff.id.display.remove(eventBuff.target.u) call eventBuff.id.display.apply(eventBuff.target.u, eventBuff.level) endfunction private function PenaltyCountEnum takes aBuff enumBuff, integer data returns nothing if enumBuff.id == ABUFFTYPE_SPECTRAL_SIGHT_MARKER and ABSOLUTE_MAX_MARKER_DURATION-GetABuffTimeRemaining(enumBuff) < Instance(data).markDur then set count = count + 1 endif endfunction private function OnBuffedAttackDamage takes nothing returns nothing local aBuff ab = GetABuffFromUnitByType(GetTriggerDamageSource(), ABUFFTYPE_SPECTRAL_SIGHT_EFFECT) local Instance inst = Instance(ab.data1) local unit target = GetTriggerDamageTarget() local string fxPath = null if GetTriggerDamageType() == DAMAGE_TYPE_ATTACK and IsUnitEnemy(target, GetOwningPlayer(inst.caster)) then // Get the number of penalties for the target in relation to this instance (this instance's marker duration). set count = 0 call ABuffEnumerateUnit(PenaltyCountEnum, target, ab.data1) // Apply extra damage if there is any to be dealt. if count > 0 then // Deal damage. call UnitDamageTargetEx(inst.caster, target, count*inst.bonusDmg, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_SPECTRAL_SIGHT, true) // Display 'rift disruption' special effect. if GetLocalPlayer() == GetOwningPlayer(inst.caster) then set fxPath = HIT_FX_MODELNAME endif loop exitwhen count == 0 set count = count - 1 call DestroyEffect(AddSpecialEffectTarget(fxPath, target, HIT_FX_ATTACHPOINT)) endloop set fxPath = null endif endif endfunction private function OnEffectABuffCleanUp takes aBuff eventBuff returns nothing local Instance inst = Instance(eventBuff.data1) call DestroyEffect(inst.fx) set instsN = instsN - 1 set insts[instsN].index = inst.index set insts[inst.index] = insts[instsN] set insts[instsN] = Instance(0) call inst.destroy() endfunction private function OnMarkerABuffCreate takes aBuff eventBuff returns nothing local Marker mark = Marker.create() set mark.subject = eventBuff.target.u set mark.abuff = eventBuff call mark.display() set mark.index = marksN set marks[marksN] = mark set marksN = marksN + 1 set eventBuff.data1 = integer(mark) endfunction private function OnMarkerABuffCleanUp takes aBuff eventBuff returns nothing local Marker mark = Marker(eventBuff.data1) call DestroyEffect(mark.fx) set marksN = marksN - 1 set marks[marksN].index = mark.index set marks[mark.index] = marks[marksN] set marks[marksN] = Marker(0) call mark.destroy() endfunction //-------------------------------------------------------------------------------------------------------------// // -- Event Responses -- // Updates display of markers for players. private function UpdateMarkerDisplay takes nothing returns nothing local integer i = 0 loop exitwhen i == marksN call DestroyEffect(marks[i].fx) // Destroy old effect with 'configuration' for old 'circumstances' and call marks[i].display() // display new one with 'configuration' for present 'circumstances'. // (Cannot modify special effects; must destroy and recreate.) set i = i + 1 endloop endfunction // Instantiates this spell. private function OnSpellCast takes nothing returns nothing local integer level = GetUnitAbilityLevel(SpellEvent.CastingUnit, SPELL_ABILITYID) call ABuffApply(ABUFFTYPE_SPECTRAL_SIGHT_EFFECT, SpellEvent.CastingUnit, SpellEvent.CastingUnit, GetEffectDuration(level), level, 0) endfunction // Instantiates a marker on the target that increments a 'refcount' of the number of such 'markers'/ // 'debuffs' that target currently has. // This marker expires; and it decrements this 'refcount' when it does. // (Whenever a caster damages a marked unit, it deals damage as a function of the 'refcount': // bonusDmg = Instance(caster).bonusDmg * 'refcount'.) private function OnGeneralCast takes nothing returns nothing // If spell event is a valid 'sighting', 'sight' it. if IsSpellDetectable(SpellEvent.AbilityId) and IsCasterDetectable(SpellEvent.CastingUnit) then call ABuffApply(ABUFFTYPE_SPECTRAL_SIGHT_MARKER, SpellEvent.CastingUnit, null, ABSOLUTE_MAX_MARKER_DURATION, 1, 0) endif endfunction //-------------------------------------------------------------------------------------------------------------// // -- Scope Initializer -- private function Initializer takes nothing returns nothing local trigger trig = null // Declare effect buff. set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT = aBuffType.create() set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT.category = ABuff_STANDARD // Simply refreshes when re-applied. set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT.countsAsBuff = true // 'Object' buff that can be manipulated: set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT.ignoreAsBuff = false // dispelled, stolen, purged, etc.. set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT.display = aBuffDisplay.create(EFFECT_BUFFPLACER_ABILITYID, EFFECT_BUFFPLACER_BUFFID) set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT.eventCreate = OnEffectABuffCreate set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT.eventRefresh = OnEffectABuffRefresh set ABUFFTYPE_SPECTRAL_SIGHT_EFFECT.eventCleanup = OnEffectABuffCleanUp // Declare Marker 'debuff'. set ABUFFTYPE_SPECTRAL_SIGHT_MARKER = aBuffType.create() set ABUFFTYPE_SPECTRAL_SIGHT_MARKER.category = ABuff_STACKING // Instances are independent of each other. set ABUFFTYPE_SPECTRAL_SIGHT_MARKER.countsAsBuff = false // Doesn't count as a 'buff'. set ABUFFTYPE_SPECTRAL_SIGHT_MARKER.ignoreAsBuff = false // Manipulable. set ABUFFTYPE_SPECTRAL_SIGHT_MARKER.eventCreate = OnMarkerABuffCreate set ABUFFTYPE_SPECTRAL_SIGHT_MARKER.eventCleanup = OnMarkerABuffCleanUp // Register Spectral Sight damagetype. set DAMAGE_TYPE_SPECTRAL_SIGHT = RegisterDamageType() // Register spell effect response for this spell that is used to instantiate it. call RegisterSpellEffectResponse(SPELL_ABILITYID, OnSpellCast) // Register generic spell effect response that is used to 'mark' units that cast spells. call RegisterSpellEffectResponse(0, OnGeneralCast) // Create trigger that runs the extra damage effect. set trig = CreateTrigger() call TriggerRegisterDamageEvent(trig, IDDS_RESPONSE_PRIORITY) call TriggerAddAction(trig, function OnBuffedAttackDamage) // Start timer that updates the markers of targets, highlighting them to the owners of casters that see them. call TimerStart(CreateTimer(), HIGHLIGHT_UPDATE_INTERVAL, true, function UpdateMarkerDisplay) set trig = null endfunction endscope |
| 12-29-2009, 06:25 PM | #2 |
A lot spells coming into Submitted resources the last days :) Anyway, I suggest to add a screenshot showing the spell in action! |
| 12-29-2009, 09:09 PM | #3 |
I may be wrong but doesn't IDDS require you to code all spell damage? Seems like such a dependency is too much for one spell... |
| 12-29-2009, 09:33 PM | #4 | |
From the man himself: Quote:
http://www.wc3c.net/showthread.php?t=100618 |
| 12-29-2009, 10:42 PM | #5 |
i need to distinguish damage dealt by attacks. though i guess i should just use a damage modifier library (if there is such a thing) or, much simpler, abuse orb effects? |
| 12-30-2009, 02:18 PM | #6 | |
Quote:
Anyways, is it a passive ability? If so, you should use a passive icon. (Random thought) |
| 12-30-2009, 02:21 PM | #7 |
its an activated ability. u get the effects only for the duration of the ability. |
| 12-30-2009, 02:21 PM | #8 |
'Kay. |
| 01-02-2010, 10:32 PM | #9 | |
The ABuff's damage event that your spell uses will not function without the DamageEvent library, you need to list it in the requirements. It seems a bit superfluous to maintain a separate list of markers and instances when you could simply enumerate all the buffs of that type instead. Then again, having a dedicated list is somewhat faster so this is acceptable. You can avoid the data/olddata complications by using the new data1/data2/data3 members, since the ABuff system never modifies those the way it modifies data. It seems inconsistent that you can see the special effect on the enemies if they cast a spell within the last ABSOLUTE_MAX_MARKER_DURATION seconds, but you can only deal bonus damage if they cast it within the last GetMarkerDuration() seconds. It makes sense that most of the spell's special effects are local, however the damage effect should probably be visible to all players, since it concerns them all, not just the spectral sight owner. JASS:// You can replace this... loop if sensed[i] and GetPlayerId(GetLocalPlayer()) == i then set fxPath = HIGHLIGHT_FX_MODELNAME endif set i = i + 1 exitwhen i == 12 endloop // ...with this: if sensed[GetPlayerId(GetLocalPlayer())] then set fxPath = HIGHLIGHT_FX_MODELNAME endif Quote:
|
| 01-03-2010, 06:35 AM | #10 | ||
Quote:
not just that, but there might be some discrepancy too in the case where two units of a player that are both using the ability attack an enemy that has casted a spell within the 'markerDuration' of the former but not within that of the latter: the marker will be displayed to the player but the extra damage will not apply for both his units. i think the problem is that the marker special effect is displayed per player and not per unit (spectral sight casters). Quote:
i have addressed all of these problems except #4 because what ill do to fix that (or whether or not ill have to) might depend on what ill do to fix the problem i mentioned above. edit: decided to address problem #4 too, anyway. i added another condition in the 'if' in the second loop in Marker.display(): JASS:if IsUnitEnemy(.subject, owner) and IsUnitInRange(.subject, insts[i].caster, insts[i].detectRng) [b]and ABSOLUTE_MAX_MARKER_DURATION-GetABuffTimeRemaining(.abuff) < insts[i].markDur then[/b] |
| 02-03-2010, 06:36 AM | #11 |
fixed something wrong with how damage detection is used. no longer uses DamageEvent. |
| 02-13-2011, 03:50 PM | #12 |
You don't verify in the OnBuffedAttackDamage function if the unit has the buff in the first place, which you should do since that code runs on all damage events so right now even units without the buff take bonus damage (not sure why you didn't use ABuff's built-in damage event which runs only for specific buffs). Aside form this, the spell looks okay. |
