- Joined
- Mar 29, 2016
- Messages
- 688
Incinerate v1.0b
System Description:
This system allows you to add an Incinerate (the buggy Firelord ability made by Blizzard) ability to a unit. This system is a remake and a vJass version of my spell Triggered Incinerate v1.2. But unlike, the mentioned spell, this system does not oblige you give the unit an actual "ability" and therefore, is more flexible and gives the users more freedom.
System Information:
- Only damage coming from same incinerate instance can stack on each other
- An incinerate instance can have as many units registered into it
- A unit can be under the effect of any number of incinerate instances
- A unit can be registered into any number of incinerate instances
- When a unit dies, the system will iterate all incinerate instances affecting the unit then it will perform their effects for explosion/incineration
Bugs and Issues:
- When two or more incinerate (even from same incinerate instance) explosions affect the same unit at the same time, the damage dealt is somehow negated/nullified if the attacktype is set to
Read the documentation for more information and a demo map is also available below.
System Script:
v1.0b
- You can now add as many incinerates to a unit, unlike the previous version where the system allocates 1 incinerate per unit
- Added an incinerate explosion event handler and its event responses together with its api for the users
- On the onDamage method, instead of directly changing the PDDS.amount, it now uses UnitDamageTargetEx()
- The damage stacking special effect is now created once the effect is null instead of destroying and recreating per damage stacking
- Added a sample usage script
- Some code optimization and changes
v1.0
- First Release
System Description:
This system allows you to add an Incinerate (the buggy Firelord ability made by Blizzard) ability to a unit. This system is a remake and a vJass version of my spell Triggered Incinerate v1.2. But unlike, the mentioned spell, this system does not oblige you give the unit an actual "ability" and therefore, is more flexible and gives the users more freedom.
System Information:
- Only damage coming from same incinerate instance can stack on each other
- An incinerate instance can have as many units registered into it
- A unit can be under the effect of any number of incinerate instances
- A unit can be registered into any number of incinerate instances
- When a unit dies, the system will iterate all incinerate instances affecting the unit then it will perform their effects for explosion/incineration
Bugs and Issues:
- When two or more incinerate (even from same incinerate instance) explosions affect the same unit at the same time, the damage dealt is somehow negated/nullified if the attacktype is set to
ATTACK_TYPE_NORMAL
(aka Spell damage). This is probably a problem regarding the damage system.Read the documentation for more information and a demo map is also available below.
System Script:
JASS:
library Incinerate /* v1.0b
*/requires /*
*/DamageEvent /* http://www.hiveworkshop.com/threads/system-physical-damage-detection.228456/
*/Table /* http://www.hiveworkshop.com/threads/snippet-new-table.188084/
*/UnitDex /* http://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
*/TimerUtils /* http://www.wc3c.net/showthread.php?t=101322
*/optional ResourcePreloader /* http://www.hiveworkshop.com/threads/snippet-resource-preloader.287358/
*/optional RegisterPlayerUnitEvent /* http://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*/optional GroupUtils /* http://www.wc3c.net/showthread.php?t=104464
[Resource Link] - http://www.hiveworkshop.com/threads/system-incinerate-damage-stacking.290046/
Description:
This system is an attempt to remake the Firelord's Incinerate spell made by Blizzard.
This was designed to solve the bugs in that spell namely, the object data of the
original Incinerate overriding the data of any custom ability made from it, which as
a result, you can't many Incinerate based abilities without having trouble configuring
their data.
With this system, you can create as many type of Incinerates, all with different stats
and effects. This system does not also obligue to use an ability but instead, you
can instantly allocate Incinerate for a certain unit (You will probably do it when a
unit mets the condition you've specified such as having a certain item, reaching a
certain level, etc.).
*///! novjass
|=========|
| CREDITS |
|=========|
/*
- AGD (Author)
- Looking_For_Help, TriggerHappy, Vexorian, Bannar, Rising_Dusk (For the dependencies)
- Aniki (For pointing out some flaws in the system)
*/
|-----|
| API |
|-----|
struct Incinerate
boolean spellAsTrigger /* Determines if spell damage can trigger incinerate */
boolean incinerateAsTrigger /* Determines if damage from an incinerate explosion can trigger incinerate */
real stackingDamage /* Damage increment per attack */
real duration /* Duration of the incinerate */
real damageCap /* Maximum value for damage stacking (Zero value means no limit) */
real exMainAoe /* Full damage radius of explosion */
real exSecondaryAoe /* Small damage radius of explosion */
real exSmallDamageFactor /* Small damage factor of explosion */
attacktype attackType /* Attack-type of the Incinerate */
damagetype damageType /* Damage-type of the Incinerate */
weapontype weaponType /* Weapon-type of the Incinerate */
string damageStackEffect /* Special effect model attached to units that have been stacked upon with incinerate damage */
string explosionEffect /* Special effect model for the incinerate explosion */
string effectAttachPoint /* Special effect attachment point for <damageStackEffect> */
boolean allyFilter /* Determines if the Incinerate works on allies */
boolean structureFilter /* Determines if the Incinerate works on structures */
boolean mechanicalFilter /* Determines if the Incinerate works on mechanical */
boolean magicImmuneFilter /* Determines if the Incinerate works on magic-immunes */
boolean illusionFilter /* Determines if the Incinerate works on illusions */
/* Event Responses */
readonly static Incinerate exploded /* The Incinerate instance that exploded */
readonly static unit explodingUnit /* The exploding unit */
readonly static unit explosionSource /* The unit who stacked the incinerate upon the explodedUnit */
readonly static unit triggerUnit /* The unit who landed the final blow upon the explodedUnit */
static method create takes nothing returns Incinerate/*
- Allocates a new Incinerate instance initialized with the default
values specified in the configuration
*/method destroy takes nothing returns nothing/*
- Destroys an Incinerate instance
*/method register takes unit u returns Incinerate/*
- Adds this Incinerate instance to the specified unit
*/method unregister takes unit u returns Incinerate/*
- Removes this Incinerate instance from the specified unit
*/static method addExplosionHandler takes code c returns triggercondition/*
- Registers a code to run upon an incinerate explosion event
*/static method removeExplosionHandler takes triggercondition tc returns nothing/*
- Unregisters a triggercondition from the incinerate explosion event
*///! endnovjass
/*==============================================*/
/* Default Incinerate Stats Configuration */
/*----------------------------------------------*/
/* A newly allocated Incinerate instance's data */
/* will be initialized to these values */
/*==============================================*/
private module InitDefaultIncinerate
set .spellAsTrigger = false
set .incinerateAsTrigger = false
set .stackingDamage = 10.00
set .duration = 10.00
set .damageCap = 500.00
set .exMainAoe = 175.00
set .exSecondaryAoe = 250.00
set .exSmallDamageFactor = 0.60
set .attackType = ATTACK_TYPE_HERO
set .damageType = DAMAGE_TYPE_MAGIC
set .weaponType = null
set .damageStackEffect = "Abilities\\Spells\\Other\\Incinerate\\IncinerateBuff.mdl"
set .explosionEffect = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
set .effectAttachPoint = "chest"
set .allyFilter = true
set .structureFilter = false
set .mechanicalFilter = false
set .magicImmuneFilter = false
set .illusionFilter = true
endmodule
/*==============================================*/
/* End of Default Incinerate Configuration */
/*==============================================*/
/*========================================================================*/
native UnitAlive takes unit u returns boolean
struct Incinerate
static if not LIBRARY_GroupUtils then
private static group ENUM_GROUP = CreateGroup()
endif
readonly static thistype exploded = 0
readonly static unit explodingUnit = null
readonly static unit explosionSource = null
readonly static unit triggerUnit = null
private static trigger trig = CreateTrigger()
private static trigger eventHandler = CreateTrigger()
private static unit tempUnit
private static boolean explosion = false
private static Table index
private TableArray table
private thistype prev
private thistype next
private group group
private string sfxModel1
private string sfxModel2
boolean spellAsTrigger
boolean incinerateAsTrigger
real stackingDamage
real duration
real damageCap
real exMainAoe
real exSecondaryAoe
real exSmallDamageFactor
attacktype attackType
damagetype damageType
weapontype weaponType
string effectAttachPoint
boolean allyFilter
boolean structureFilter
boolean mechanicalFilter
boolean magicImmuneFilter
boolean illusionFilter
method operator damageStackEffect= takes string model returns nothing
set .sfxModel1 = model
static if LIBRARY_ResourcePreloader then
call PreloadEffect(model)
endif
endmethod
method operator damageStackEffect takes nothing returns string
return .sfxModel1
endmethod
method operator explosionEffect= takes string model returns nothing
set .sfxModel2 = model
static if LIBRARY_ResourcePreloader then
call PreloadEffect(model)
endif
endmethod
method operator explosionEffect takes nothing returns string
return .sfxModel2
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set .next = 0
set .prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
set .table = TableArray[0x2000]
set .group = CreateGroup()
implement optional InitDefaultIncinerate
return this
endmethod
method destroy takes nothing returns nothing
call .deallocate()
call .table.flush()
call DestroyGroup(.group)
set .next.prev = .prev
set .prev.next = .next
set .spellAsTrigger = false
set .stackingDamage = 0
set .duration = 0
set .damageCap = 0
set .exMainAoe = 0
set .exSecondaryAoe = 0
set .exSmallDamageFactor = 0
set .attackType = null
set .damageType = null
set .weaponType = null
set .sfxModel1 = null
set .sfxModel2 = null
set .effectAttachPoint = null
set .allyFilter = false
set .structureFilter = false
set .mechanicalFilter = false
set .magicImmuneFilter = false
set .illusionFilter = false
endmethod
method register takes unit u returns thistype
call GroupAddUnit(.group, u)
return this
endmethod
method unregister takes unit u returns thistype
call GroupRemoveUnit(.group, u)
return this
endmethod
static method addExplosionHandler takes code c returns triggercondition
return TriggerAddCondition(eventHandler, Filter(c))
return null
endmethod
static method removeExplosionHandler takes triggercondition tc returns nothing
call TriggerRemoveCondition(eventHandler, tc)
endmethod
private method filter takes unit target, unit source returns boolean
return (UnitAlive(target)) and/*
*/ (target != source) and/*
*/ (IsUnitEnemy(target, GetOwningPlayer(source)) or .allyFilter) and/*
*/ (not IsUnitType(target, UNIT_TYPE_STRUCTURE) or .structureFilter) and/*
*/ (not IsUnitType(target, UNIT_TYPE_MECHANICAL) or .mechanicalFilter) and/*
*/ (not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) or .magicImmuneFilter) and/*
*/ (not IsUnitIllusion(target) or .illusionFilter)
endmethod
private static method debuff takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetTimerData(t)
local thistype this = index[GetHandleId(t)]
local Table tb = .table[id]
call DestroyEffect(tb.effect[5])
call ReleaseTimer(t)
call tb.flush()
set t = null
endmethod
private static method onDeath takes nothing returns nothing
local unit killer = GetKillingUnit()
local unit dying = GetTriggerUnit()
local real x = GetUnitX(dying)
local real y = GetUnitY(dying)
local integer id = GetUnitId(dying)
local thistype this = 0
local boolean b
local attacktype at
local damagetype dt
local weapontype wt
local unit buffSource
local unit target
local real dx
local real dy
local real damage
local real radius
local real damageFactor
local Table tb
loop
set this = .next
exitwhen this == 0
set tb = .table[id]
if tb.boolean[7] then
set b = .incinerateAsTrigger
set at = .attackType
set dt = .damageType
set wt = .weaponType
set damage = tb.real[1]
set radius = tb.real[2]
set damageFactor = tb.real[4]
set buffSource = tb.unit[8]
call DestroyEffect(tb.effect[5])
call DestroyEffect(AddSpecialEffect(.explosionEffect, x, y))
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, tb.real[3], null)
loop
set target = FirstOfGroup(ENUM_GROUP)
exitwhen target == null
call GroupRemoveUnit(ENUM_GROUP, target)
if .filter(target, buffSource) then
set dx = GetUnitX(target) - x
set dy = GetUnitY(target) - y
set explosion = b
if (dx*dx + dy*dy > radius*radius and UnitDamageTarget(buffSource, target, damage*damageFactor, true, false, at, dt, wt)) or UnitDamageTarget(buffSource, target, damage, true, false, at, dt, wt) then
endif
set explosion = false
endif
endloop
call ReleaseTimer(tb.timer[6])
call tb.flush()
/* Run inicneration handler */
set triggerUnit = buffSource
set explosionSource = killer
set explodingUnit = dying
set exploded = this
call TriggerEvaluate(eventHandler)
set triggerUnit = null
set explosionSource = null
set explodingUnit = null
set exploded = 0
endif
endloop
set buffSource = null
set killer = null
set dying = null
endmethod
private static method onDamage takes nothing returns nothing
local integer id = GetUnitId(PDDS.target)
local thistype this = 0
local timer t
local real dcap
local Table tb
loop
set this = .next
exitwhen this == 0
if (IsUnitInGroup(PDDS.source, .group)) and (not explosion) and (PDDS.damageType != CODE) and ((PDDS.damageType != SPELL) or .spellAsTrigger) then
set dcap = .damageCap
if .filter(PDDS.target, PDDS.source) then
set tb = .table[id]
set t = tb.timer[6]
if t == null then
set t = NewTimer()
endif
call SetTimerData(t, id)
set index.integer[GetHandleId(t)] = this
set tb.real[1] = tb.real[1] + .stackingDamage
set tb.real[2] = .exMainAoe
set tb.real[3] = .exSecondaryAoe
set tb.real[4] = .exSmallDamageFactor
set tb.timer[6] = t
set tb.boolean[7] = true
set tb.unit[8] = PDDS.source
if not tb.effect.has(5) then
set tb.effect[5] = AddSpecialEffectTarget(.damageStackEffect, PDDS.target, .effectAttachPoint)
endif
if dcap > 0.00 and tb.real[1] > dcap then
set tb.real[1] = dcap
endif
set PDDS.amount = 0.00
call UnitDamageTargetEx(PDDS.source, PDDS.target, PDDS.amount + tb.real[1], true, false, .attackType, .damageType, .weaponType)
call TimerStart(t, .duration, false, function thistype.debuff)
endif
endif
endloop
set t = null
endmethod
private static method onInit takes nothing returns nothing
static if RPUE_VERSION_NEW then
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
elseif LIBRARY_RegisterPlayerUnitEvent then
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
else
local code c = function thistype.onDeath
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Filter(c))
endif
call AddDamageHandler(function thistype.onDamage)
set index = Table.create()
endmethod
endstruct
endlibrary
JASS:
library TestLibrary requires UnitDex, Incinerate
private struct Init extends array
private static constant real CREEP_RESPAWN_TIME = 30.00
private static constant real HERO_RESPAWN_TIME = 2.00
private static constant integer CAMP_COUNT = 15
private static constant integer CREEP_COUNT = 20
private static constant real CAMP_OFFSET = 1300.00
private static real array originalX
private static real array originalY
private static real array originalFacing
private static Incinerate furySwipe
private static method respawn takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer i = GetUnitId(u)
local real x = originalX[i]
local real y = originalY[i]
call TriggerSleepAction(HERO_RESPAWN_TIME)
if not ReviveHero(u, x, y, false) then
call TriggerSleepAction(CREEP_RESPAWN_TIME - HERO_RESPAWN_TIME)
call CreateUnit(GetTriggerPlayer(), GetUnitTypeId(u), x, y, originalFacing[i])
call RemoveUnit(u)
endif
set u = null
endmethod
private static method levelUp takes nothing returns nothing
local unit u = GetTriggerUnit()
if IsUnitType(u, UNIT_TYPE_HERO) and IsUnitAlly( u, GetTriggerPlayer()) then
call SetHeroLevel(u, (GetHeroLevel(u) + 1), true)
endif
set u = null
endmethod
private static method setup takes nothing returns nothing
local player p = Player(1)
local integer i = 360/CAMP_COUNT
local real x
local real y
local integer a
local real facing
local integer index = 16
local trigger t1 = CreateTrigger()
local trigger t2 = CreateTrigger()
call FogEnable(false)
call FogMaskEnable(false)
call PanCameraToTimed(0, 0, 0.00)
call SetHeroLevel(CreateUnit(Player(0), 'H000', -200, 0, 270), 10, false)
call SetHeroLevel(CreateUnit(Player(0), 'H001', 200, 0, 270), 10, false)
call SetPlayerState(p, PLAYER_STATE_GIVES_BOUNTY, 1)
loop
exitwhen i > 360
set i = i + (360/CAMP_COUNT)
set x = CAMP_OFFSET*Cos(i*bj_DEGTORAD)
set y = CAMP_OFFSET*Sin(i*bj_DEGTORAD)
set facing = bj_RADTODEG*Atan2(-y, -x)
set a = 0
loop
set a = a + 1
call CreateUnit(p, 'hpea', x, y, facing)
exitwhen a == CREEP_COUNT
endloop
endloop
loop
set index = index - 1
call TriggerRegisterPlayerUnitEvent(t1, Player(index), EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(t2, Player(index), EVENT_PLAYER_UNIT_SELECTED, null)
exitwhen index == 0
endloop
call TriggerAddAction(t1, function thistype.respawn)
call TriggerAddAction(t2, function thistype.levelUp)
endmethod
private static method register takes nothing returns nothing
local unit u = GetIndexedUnit()
local integer id = GetUnitTypeId(u)
// If the entering unit is Firelord, give it the default Incinerate ability
if id == 'H000' then
call Incinerate.create().register(u)
// If the entering unit is Ursa, give it a Fury Swipe-like ability similar to that in DotA
elseif id == 'H001' then
set furySwipe = Incinerate.create().register(u)
set furySwipe.stackingDamage = 25.00
set furySwipe.duration = 12.00
set furySwipe.damageCap = 0.00
set furySwipe.exMainAoe = 0.00
set furySwipe.exSecondaryAoe = 0.00
set furySwipe.exSmallDamageFactor = 0.00
set furySwipe.attackType = ATTACK_TYPE_HERO
set furySwipe.damageType = DAMAGE_TYPE_NORMAL
set furySwipe.damageStackEffect = "Abilities\\Spells\\NightElf\\BattleRoar\\RoarTarget.mdl"
set furySwipe.explosionEffect = "Objects\\Spawnmodels\\Undead\\UndeadBlood\\UndeadBloodNecromancer.mdl"
set furySwipe.effectAttachPoint = "overhead"
endif
set originalX[GetIndexedUnitId()] = GetUnitX(u)
set originalY[GetIndexedUnitId()] = GetUnitY(u)
set originalFacing[GetIndexedUnitId()] = GetUnitFacing(u)
set u = null
endmethod
private static method onDamage takes nothing returns nothing
local string targetName
if PDDS.amount != 0 then
if IsUnitType(PDDS.target, UNIT_TYPE_HERO) then
set targetName = GetHeroProperName(PDDS.target)
else
set targetName = GetUnitName(PDDS.target)
endif
call BJDebugMsg(GetHeroProperName(PDDS.source) + " dealt " + I2S(R2I(PDDS.amount)) + " damage to " + targetName + ".")
endif
endmethod
// Play a undead dissipate special effect when a dying unit is under the effect of fury swipe
private static method onExplode takes nothing returns nothing
if Incinerate.exploded == furySwipe then
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl", GetUnitX(Incinerate.explodingUnit), GetUnitY(Incinerate.explodingUnit)))
endif
endmethod
private static method onInit takes nothing returns nothing
call AddDamageHandler(function thistype.onDamage)
call Incinerate.addExplosionHandler(function thistype.onExplode)
call OnUnitIndex(function thistype.register)
call thistype.setup()
endmethod
endstruct
endlibrary
v1.0b
- You can now add as many incinerates to a unit, unlike the previous version where the system allocates 1 incinerate per unit
- Added an incinerate explosion event handler and its event responses together with its api for the users
- On the onDamage method, instead of directly changing the PDDS.amount, it now uses UnitDamageTargetEx()
- The damage stacking special effect is now created once the effect is null instead of destroying and recreating per damage stacking
- Added a sample usage script
- Some code optimization and changes
v1.0
- First Release
Attachments
Last edited: