- Joined
- Nov 26, 2006
- Messages
- 11,136
Here's the ABC system. It needs JNGP to compile, apparently.
I'm kinda confused about it, because in the ABC map this trigger had the "Run on map initialization" box checked, but in the two spell maps that box was unchecked.
EDIT: Okay here's the tooltips and the problems.
The Problem:
Here's Lirch's Headbutt spell.
And Scorch's Flash Fire spell.
I'm kinda confused about it, because in the ABC map this trigger had the "Run on map initialization" box checked, but in the two spell maps that box was unchecked.
EDIT: Okay here's the tooltips and the problems.
Flash Fire | Ignites the target unit in flames, dealing initial damage and 10 damage per second for up to 10 seconds. After the duration/the target dies, there is a 75% chance that it will bounce to a random nearby enemy unit, igniting it and repeating the whole process. Stops bouncing if there is no nearby target/the maximum number of bounces is reached/the chance to bounce fails. |
Headbutt | Charge to the target point. The first unit you encounter will take X damage and be stunned for Y seconds, you will also be stunned for Z seconds due to concussion. If you hit a building, you stop charging. |
The Problem:
When I click the map button on my campaign screen (where you choose to play Chapter 1, 2, etc) it fades out like normal but then I just get a black screen. When I disabled the ABC trigger and the new spell triggers it loads, but as soon as the starting cinematic ends or I skip the cinematic then wc3 crashes with a fatal error. EDIT2: That was actually a problem with a fade filter.
This only started happening when I added those spells. I've imported lots of custom spells before (Things like Vile's knockback system, Pyrogasm's Cloud spell (haven't added the Fear one yet), and others), and those work fine.
This only started happening when I added those spells. I've imported lots of custom spells before (Things like Vile's knockback system, Pyrogasm's Cloud spell (haven't added the Fear one yet), and others), and those work fine.
JASS:
//TESH.scrollpos=88
//TESH.alwaysfold=0
//==============================================================================
// ABC -- STRUCT ATTACHMENT SYSTEM BY COHADAR -- v5.1
//==============================================================================
//==============================================================================
// Quick function index:
//==============================================================================
//
// ----------------------------------------------------------------------
// Set Functions - these functions attach struct to a handle
// ----------------------------------------------------------------------
// SetTimerStructA(timer, struct)
// SetTimerStructB(timer, struct)
// SetTimerStructC(timer, struct)
//
// SetTriggerStructA(trigger, struct)
// SetTriggerStructB(trigger, struct)
// SetTriggerStructC(trigger, struct)
//
// SetDialogStructA(dialog, struct)
// SetDialogStructB(dialog, struct)
// SetDialogStructC(dialog, struct)
//
// ----------------------------------------------------------------------
// Get Functions - these functions retrieve attached structs
// ----------------------------------------------------------------------
// GetTimerStructA(timer) -> struct
// GetTimerStructB(timer) -> struct
// GetTimerStructC(timer) -> struct
//
// GetTriggerStructA(trigger) -> struct
// GetTriggerStructB(trigger) -> struct
// GetTriggerStructC(trigger) -> struct
//
// GetDialogStructA(dialog) -> struct
// GetDialogStructB(dialog) -> struct
// GetDialogStructC(dialog) -> struct
//
// ----------------------------------------------------------------------
// Clear Functions - these functions clear and return attached value
// ----------------------------------------------------------------------
// ClearTimerStructA(timer) -> struct
// ClearTimerStructB(timer) -> struct
// ClearTimerStructC(timer) -> struct
//
// ClearTriggerStructA(trigger) -> struct
// ClearTriggerStructB(trigger) -> struct
// ClearTriggerStructC(trigger) -> struct
//
// ClearDialogStructA(dialog) -> struct
// ClearDialogStructB(dialog) -> struct
// ClearDialogStructC(dialog) -> struct
//
//==============================================================================
//==============================================================================
// DOCUMENTATION:
//==============================================================================
//
// PURPOUSE OF ABC:
// * Attaching multiple structs to a <handle>
// * Not using stupid game cache.
//
// * Currently supported <handle> types are timer, trigger and dialog
// ABC can NOT be used to attach things to units,
// Systems that can attach stuff to units require a mission key
// ABC does not have mission keys, witch is one of the reasons it is so fast.
//
// * These are the only 3 handle types I found that need attaching (except units)
// If you have the need for some other handle types please let me know
//
// HOW TO USE:
// * Lets assume you want to attach some spell data to a timer.
// You would do the following:
//
// 1. Create struct that will contain all your data.
// 2. call SetTimerStructA(myTimer, myStruct)
//
// and then you decide you need one more struct on that timer...
//
// call SetTimerStructB(myTimer, myStruct2)
//
// Then in a periodic timer you just get the stored value
// set myStruct = GetTimerStructA(myTimer)
//
// In your final itearation of periodic timer
// you need to clear stored values
// ClearTimerStructA(myTimer)
// ClearTimerStructB(myTimer)
// If you don't system will start to overflow
//
// DIFFERENCE FROM v5.0:
// * You cannot use SetStructA two times on the same <handle>
// without clearing the previous value.
//
// * ABC v5.0 used to overwrite the old values when you did that.
// This caused errors when collisions get over 3
// Since collisions almost never happen in real maps noone noticed this before
// Only some hardcore tests will detect this
//
// * New version of ABC treats overwrite as an error.
// BAD: SetStructA(X, value1); SetStructA(X, value2)
// GOOD: SetStructA(X, value1); ClearStructA(X); SetStructA(X, value2)
//
// * HASH_COLLISION_LIMIT is reduced from 8 to 7 to simplyfy algorithms
// (using null borders at modulo 8)
// This effectively means that you can store around 6000 attachments per hash
// before you get an overflow. (used to be 7000)
//
// * This simplyfication increased ABC speed a little.
//
// PROS:
// * ABC is faster than any gamecache based system.
//
// * you can attach any struct to any supported <handle> type
//
// * you CAN attach up to 3 structs on the same <handle>
// but only if you use different functions, for example
// SetTriggerStructA(), SetTriggerStructB(), SetTriggerStructC()
//
// * get and set functions are extremelly fast (using hash algorithm)
//
// * System reports collision, overwrite and overflow.
// Basically if you do anything stupid system will tell you.
//
// * This system will work even if your map leaks
// and will NOT slow down because of it.
//
// * This is the system that spits torment test in the eye.
//
// * For compatibility with ABC v4.6 look at ABCC library
//
//
// CONS:
// * you must manually clean the stored value - REMEMBER THIS RULE!!!
// Don't forget to use ClearStruct() functions
//
// * you should NOT use Get without a Set - REMEMBER THIS RULE!!!
// It will simply return a zero and nothing bad will happen,
// but it will lag really really bad
// (system will report this as an error)
//
// * System starts to collide if you have more than 1024 structs in one hash.
// (you can see this very obviosly with -overload test)
// If that happens it can mean one of 2 things:
// 1. you are using more than 1024 timers, triggers or dialogs - !?
// 2. you forgot to use ClearStruct and you are leaking handles somewhere
// if this is the case simply do a search on your code and find
// what trigger uses ABC but has no ClearStruct calls.
//
// DETAILS:
// * when struct is detached from <handle> it is not destroyed
// you still have to do that manually if necessary
//
// * ABC will not interfere with other attachemt systems
// You can freely use any other system alongside ABC
// But if you use only ABC for attachments in your map
// you don't have to null local variables.
// The reason for this is that gamecache gets abnormaly slow
// when you don't null locals, ABC does not have this flaw.
//
//
// SPECIAL THANKS TO:
// * NagelBagel - for finding errors in versions 4.3 and 4.4
// * Here-b-Trollz - for testing ABC and for making cool spells with it.
// * Toadcop - for being pain in the ass and for pushing me to improve ABC.
// * emjlr3 - for pointing out the need for non-generic trigger attachments
// * PandaMine - I found a bug in ABC by examining his HSAS vs ABC test
// * All those people out there who use and support ABC
// Thank you guys.
//
// HOW TO IMPORT:
// * Just create a trigger named ABC
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
//------------------------------------------------------------------------------
// We will use textmacros to create multiple instances of system
//------------------------------------------------------------------------------
//! textmacro ABC takes X, NAME, TYPE
//------------------------------------------------------------------------------
// Global arrays represent our hash tables.
// System is currently using 3 hash tables per handle type. (A, B, C)
//------------------------------------------------------------------------------
globals
private $TYPE$ array $TYPE$Key$X$
private integer array $TYPE$Value$X$
private integer $TYPE$maxCollision$X$ = 0
endglobals
//------------------------------------------------------------------------------
// returns the maximum collision so far
//------------------------------------------------------------------------------
function Get$NAME$Collision$X$ takes nothing returns integer
return $TYPE$maxCollision$X$
endfunction
//------------------------------------------------------------------------------
// initializes hash arrays to prevent lag when ABC is used for the first time
//------------------------------------------------------------------------------
private function Init$NAME$Hash$X$ takes nothing returns nothing
set $TYPE$Key$X$[HASH_INDEX_LIMIT] = null
set $TYPE$Value$X$[HASH_INDEX_LIMIT] = 0
endfunction
//------------------------------------------------------------------------------
// attaches struct to a handle by using hash table
//------------------------------------------------------------------------------
function Set$NAME$Struct$X$ takes $TYPE$ t, integer s returns nothing
debug local integer collision
// hash using 32-bit integer overflow
local integer i = (H2I(t) * HASH_UP) / HASH_DOWN + HASH_BIAS
if $TYPE$Key$X$[i] == null then
set $TYPE$Value$X$[i] = s
set $TYPE$Key$X$[i] = t
return
endif
debug if $TYPE$Key$X$[i] == t then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] attachment overwrite on $TYPE$ #" +I2S(H2I(t)))
debug return
debug endif
// if function gets below this line we have a collision
debug set collision = 1
loop
debug if collision >= HASH_COLLISION_LIMIT then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] overflow")
debug return
debug endif
debug set collision = collision + 1
set i = i + 1
exitwhen $TYPE$Key$X$[i] == null
debug if $TYPE$Key$X$[i] == t then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] attachment overwrite on $TYPE$ #" +I2S(H2I(t)))
debug return
debug endif
endloop
debug if collision > $TYPE$maxCollision$X$ then
debug call BJDebugMsg("|cFFFF4444Warning: Hash[$X$] maximum collision is now: " + I2S(collision))
debug set $TYPE$maxCollision$X$ = collision
debug endif
set $TYPE$Value$X$[i] = s
set $TYPE$Key$X$[i] = t
return
endfunction
//------------------------------------------------------------------------------
// gets stored struct from a handle
//------------------------------------------------------------------------------
function Get$NAME$Struct$X$ takes $TYPE$ t returns integer
debug local integer collision
// hash using 32-bit integer overflow
local integer i = (H2I(t) * HASH_UP) / HASH_DOWN + HASH_BIAS
if $TYPE$Key$X$[i] == t then
return $TYPE$Value$X$[i]
endif
// if function gets below this line we have a collision
debug set collision = 1
loop
debug if collision >= HASH_COLLISION_LIMIT then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] : get request on unknown handle")
debug return 0
debug endif
debug set collision = collision + 1
set i = i + 1
exitwhen $TYPE$Key$X$[i] == t
endloop
return $TYPE$Value$X$[i]
endfunction
//------------------------------------------------------------------------------
// clears stored struct from a handle, also returns cleared value
//------------------------------------------------------------------------------
function Clear$NAME$Struct$X$ takes $TYPE$ t returns integer
debug local integer collision
local integer ik
local integer ret
// hash using 32-bit integer overflow
local integer i = (H2I(t) * HASH_UP) / HASH_DOWN + HASH_BIAS
// first find the index on witch key is stored
debug set collision = 0
loop
debug if collision >= HASH_COLLISION_LIMIT then
debug call BJDebugMsg("|cFFFF0000ERROR: Hash[$X$] : clear request on unknown handle")
debug return 0
debug endif
debug set collision = collision + 1
exitwhen $TYPE$Key$X$[i] == t
set i = i + 1
endloop
set ik = i
set ret = $TYPE$Value$X$[ik]
// then find last used key index in bucket
loop
set i = i + 1
// we use the fact bucket borders (mod 8 indexes) are always null
exitwhen $TYPE$Key$X$[i] == null
endloop
// shift last bucket entry to the place of removed one
set $TYPE$Key$X$[ik] = $TYPE$Key$X$[i-1]
set $TYPE$Value$X$[ik] = $TYPE$Value$X$[i-1]
// clear the previous last bucket entry
set $TYPE$Key$X$[i-1] = null
return ret
endfunction
//! endtextmacro
//==============================================================================
// Macro execution -- this is where real functions get created
//==============================================================================
library ABC initializer Init
globals
public constant integer HASH_SIZE = 8192
public constant integer HASH_INDEX_LIMIT = 8190
public constant integer HASH_DOWN = 524288 // 2^19
public constant integer HASH_UP = 2134900736 // 2^22 * 509
public constant integer HASH_BIAS = 4096 // HASH_SIZE / 2
public constant integer HASH_COLLISION_LIMIT = 7 // ABC v5.0 had limit 8
// 509 is the prime closest to 512
endglobals
//------------------------------------------------------------------------------
// conversion function used by the system internally
// you will not need to use it directly
//------------------------------------------------------------------------------
public function H2I takes handle h returns integer
return h
return 0
endfunction
//! runtextmacro ABC("A","Timer","timer")
//! runtextmacro ABC("B","Timer","timer")
//! runtextmacro ABC("C","Timer","timer")
//! runtextmacro ABC("A","Trigger","trigger")
//! runtextmacro ABC("B","Trigger","trigger")
//! runtextmacro ABC("C","Trigger","trigger")
//! runtextmacro ABC("A","Dialog","dialog")
//! runtextmacro ABC("B","Dialog","dialog")
//! runtextmacro ABC("C","Dialog","dialog")
private function Init takes nothing returns nothing
call InitTimerHashA()
call InitTimerHashB()
call InitTimerHashC()
call InitTriggerHashA()
call InitTriggerHashB()
call InitTriggerHashC()
call InitDialogHashA()
call InitDialogHashB()
call InitDialogHashC()
endfunction
endlibrary
//==============================================================================
// END OF ABC STRUCT ATTACHMENT SYSTEM
//==============================================================================
Here's Lirch's Headbutt spell.
JASS:
//TESH.scrollpos=24
//TESH.alwaysfold=0
scope Headbutt
globals
private constant integer Id = 'A61L'
private constant real RefreshRate = 0.035
private constant string ChargeAnim = "walk"
private group tempgroup = null
private unit Caster = null
endglobals
//! runtextmacro MoveStruct()
//Please just ignore that textmacro call
private function YourFilter takes nothing returns boolean
//This is where the global 'Caster' is used
return GetFilterUnit() != Caster and GetWidgetLife(GetFilterUnit()) > .405
// By default, it filters out the Caster(because he may be caught in the radius)
//and dead units. Structures are not filtered out, so you may do
//anything you want in YourFunc if the Caster collides with a Structure
endfunction
private constant function Damage takes integer lvl returns real
return 100. + (50. * (lvl - 1.))
endfunction
private function YourFunc takes data Event, unit Target returns nothing
// Event.u is the caster
// Do anything you want when the caster collides with a unit/structure
if IsUnitType(Target,UNIT_TYPE_STRUCTURE) == false or IsUnitType(Target,UNIT_TYPE_GROUND) == false then
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",GetUnitX(Event.u),GetUnitY(Event.u)))
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",GetUnitX(Target),GetUnitY(Target)))
call UnitDamageTarget(Event.u,Target,Damage(Event.lvl),true,false,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_NORMAL,null)
set Event.stop = true //Set this to false if you want your unit to go through
// units ONLY if he did not hit a structure
else
set Event.stop = true //Do this if you want to stop charging when
//the caster hits a building
//Now do anything, like deal damage to the caster because he hit a structure
endif
endfunction
private constant function Radius takes integer lvl returns real
return 90.
endfunction
private constant function Speed takes integer lvl returns real
return 700. * RefreshRate //DO NOT REMOVE the " * RefreshRate " part!
//Returned speed is in WC3 speed, 900 speed = 900 units travelled per second.
endfunction
//! textmacro MoveStruct
private struct data
unit u = null
integer lvl = 1
real casterx
real castery
real targetx
real targety
real max = 0
real distance = 0
real angle
real sin
real cos
timer t = null
boolean stop = false
static method create takes unit u, location l returns data
local data d = data.allocate()
set d.u = u
set d.casterx = GetUnitX(d.u)
set d.castery = GetUnitY(d.u)
set d.targetx = GetLocationX(l)
set d.targety = GetLocationY(l)
set d.t = CreateTimer()
set d.angle = Atan2(d.targety - d.castery, d.targetx - d.casterx)
set d.sin = Sin(d.angle)
set d.cos = Cos(d.angle)
set d.max = SquareRoot(((d.targetx - d.casterx) * (d.targetx - d.casterx))+ ((d.targety - d.castery) * (d.targety - d.castery)))
set d.distance = 0
set d.lvl = GetUnitAbilityLevel(d.u,Id)
call SetTimerStructA(d.t,d)
call PauseUnit(d.u,true)
return d
endmethod
endstruct
//! endtextmacro
private function Execute takes nothing returns nothing
local data d = data(GetTimerStructA(GetExpiredTimer()))
//^-- Sweet, no local timer var,
//this is why ABC > Gamecache and vJass > Jass
if d.distance > d.max or d.stop or GetWidgetLife(d.u) < .405 or d.u == null then
call PauseUnit(d.u,false)
call PauseTimer(d.t)
call DestroyTimer(d.t)
call d.destroy()
else
set d.distance = d.distance + Speed(d.lvl)
set d.casterx = d.casterx + Speed(d.lvl) * d.cos
set d.castery = d.castery + Speed(d.lvl) * d.sin
call SetUnitX(d.u,d.casterx)
call SetUnitY(d.u,d.castery)
call SetUnitAnimation(d.u,"walk")
set Caster = d.u
call GroupEnumUnitsInRange(tempgroup,d.casterx,d.castery,Radius(d.lvl),Condition(function YourFilter))
if CountUnitsInGroup(tempgroup) > 0 then
call YourFunc(d,FirstOfGroup(tempgroup))
endif
set Caster = null
call GroupClear(tempgroup)
endif
endfunction
private function Actions takes nothing returns nothing
// Super inlined LOLOL
call TimerStart(data(data.create(GetTriggerUnit(),GetSpellTargetLoc())).t,RefreshRate,true,function Execute)
endfunction
private function Check takes nothing returns boolean
return GetSpellAbilityId() == Id
endfunction
//===========================================================================
public function InitTrig_Headbutt takes nothing returns nothing
local trigger t = CreateTrigger()
set tempgroup = CreateGroup()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function Check))
call TriggerAddAction(t,function Actions )
endfunction
endscope
And Scorch's Flash Fire spell.
JASS:
//TESH.scrollpos=84
//TESH.alwaysfold=0
scope FlashFire
globals
private constant integer FlashFireID = 'FIRE' //Rawcode of your spell
private constant attacktype ATYPE = ATTACK_TYPE_NORMAL
private constant damagetype DTYPE = DAMAGE_TYPE_FIRE
private constant boolean AffectAllies = false
private constant string SFX = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
private constant string Attach = "overhead"
private constant string SFX2 = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl" //Effect created instantly at the location each unit bounced on. Only plays the model's death anim or birth anim(if it doesn't have a death anim)
endglobals
private function Filter takes nothing returns boolean
return not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) and GetWidgetLife(GetFilterUnit()) > .405
endfunction
private constant function Timeout takes integer lvl returns real
return 10. //The duration of the burn
endfunction
private constant function BurnDamage takes integer lvl returns real
return 10. //Damage done per second
endfunction
private constant function InitialDamage takes integer lvl returns real
return (25*lvl)+25. //Initial damage done
endfunction
private constant function Interval takes integer lvl returns real
return 1. //The interval, doh
endfunction
private constant function Bounces takes integer lvl returns integer
return lvl //The number of bounces for each level
endfunction
private constant function Chance takes integer lvl returns integer
return 75 //The chance to bounce
endfunction
private constant function Radius takes integer lvl returns real
return 350. //Radius where a random unit is chosen to be the next target
endfunction
private struct data
unit u
unit t
real timeout
real dps
effect e
integer i
integer bounces
static method create takes unit u, unit t, integer i returns data
local data d = data.allocate()
set d.u = u
set d.t = t
set d.timeout = Timeout(i)
set d.dps = BurnDamage(i)
set d.i = i
set d.e = AddSpecialEffectTarget(SFX,d.t,Attach)
set d.bounces = Bounces(d.i)
return d
endmethod
method onDestroy takes nothing returns nothing
call DestroyEffect(.e)
endmethod
endstruct
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == FlashFireID
endfunction
private function Callback takes nothing returns nothing
local timer t = GetExpiredTimer()
local data d = data(GetTimerStructA(t))
local group g
local boolexpr b
local unit temp
local integer i = GetRandomInt(1,100)
debug call BJDebugMsg("|cffffaa00Callback func called!|r")
if d.timeout <= 0 or GetWidgetLife(d.t) < .405 or d.t == null then
debug call BJDebugMsg("|cffff00aaChance is|r "+I2S(i))
if i <= Chance(d.i) and d.bounces > 0 then
debug call BJDebugMsg("|cffffaa00Bouncing!|r")
set g = CreateGroup()
set b = Condition(function Filter)
call GroupEnumUnitsInRange(g,GetUnitX(d.t),GetUnitY(d.t),Radius(d.i),b)
set d.t = null
loop
set temp = FirstOfGroup(g)
exitwhen temp == null
if IsUnitEnemy(temp,GetOwningPlayer(d.u)) or AffectAllies then
set d.t = temp
endif
call GroupRemoveUnit(g,temp)
exitwhen d.t != null
endloop
call DestroyEffect(d.e)
if d.t == null then
call BJDebugMsg("|cffff0000No nearby enemies!|r")
call ClearTimerStructA(t)
call data.destroy(d)
call DestroyTimer(t) //You can use ReleaseTimer(t) if you have CSSafety
else
set d.bounces = d.bounces - 1
set d.e = AddSpecialEffectTarget(SFX,d.t,Attach)
set d.timeout = 10.
call DestroyEffect(AddSpecialEffect(SFX2,GetUnitX(d.t),GetUnitY(d.t)))
endif
call DestroyGroup(g)
call DestroyBoolExpr(b)
set g = null
set b = null
else
call ClearTimerStructA(t)
call data.destroy(d)
call DestroyTimer(t) //You can use ReleaseTimer(t) if you have CSSafety
endif
else
call UnitDamageTarget(d.u,d.t,d.dps,true,false,ATYPE,DTYPE,null)
endif
//set t = null
//You can remove the '//' if you want to null the timer. There are known bugs about nulling timers, but I haven't encountered that yet
endfunction
private function Actions takes nothing returns nothing
local data d = data.create(GetTriggerUnit(),GetSpellTargetUnit(),GetUnitAbilityLevel(GetTriggerUnit(),FlashFireID))
local timer t = CreateTimer() //You can replace with NewTimer if you have CSSafety
call SetTimerStructA(t,d)
call TimerStart(t,Interval(d.i),true,function Callback)
call UnitDamageTarget(d.u,d.t,InitialDamage(d.i),true,false,ATYPE,DTYPE,null)
//set t = null
//You can remove the '//' if you want to null the timer. There are known bugs about nulling timers, but I haven't encountered that yet
endfunction
//===========================================================================
function InitTrig_Flash_Fire takes nothing returns nothing
set gg_trg_Flash_Fire = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Flash_Fire, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_Flash_Fire, Condition( function Conditions ) )
call TriggerAddAction( gg_trg_Flash_Fire, function Actions )
endfunction
endscope
Last edited: