Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CallOfNature requires GroupUtils, T32, optional GT
// v 1.1.6
//////////////////////////////////////////////
// Credits:
// Spell made by 3yeballz
// please give credits if you use :-)
//
// GroupUtils by Rising Dusk
// T32 by Jesus4Lyf
// GTrigger by Jesus4Lyf
// model: Navi by Elenai
///////////////////////////////////////////////
/////////////////////////////////////////
/////////// User Configuration //////////
/////////////////////////////////////////
globals
private constant integer SPELLID = 'A000' // rawcode of spell
private constant integer UNITID = 'h000' // rawcode of wisp
private constant integer TREE1 = 'ATtr' // rawcode for trees where wisps can spawn
private constant integer TREE2 = 'ATtc'
// if you require more different trees go to line 218 and change it
private constant real RADIUS = 400. // radius spell cast
private constant real RADIUSC = 80. // radius detonation init
private constant real RADIUSDMG = 150. // radius detonation damage
private constant integer ARRAYSIZE = 200 // -> 40 instances allowed
private constant integer ENDCOUNT = 650 // wisps will move outside the map after x callbacks
private constant integer MAXCOUNT = 1500 // should be much bigger than ENDCOUNT. after x callbacks struct will be destroyed (if it's not destroyed yet)
private constant real HEIGHT = 60 // fly height of wisps
////////////////////////////////////
// constants for flight route //////
////////////////////////////////////
private constant real ACCELERATION = 0.5 //default: 0.5
private constant real ACCURACY = 0.002 //default: 0.002
private constant real INERTIA = 0.06 //default: 0.06, the lower the stronger is inertia, affects acceleration as well
private constant real TOLERANCE = 1.27 //default: 1.27
private constant real MAXSPEED = 30 //default: 30
private constant real STARTSPEED = 25 //default: 25
private attacktype atktype = ATTACK_TYPE_NORMAL
private damagetype dmgtype = DAMAGE_TYPE_MAGIC
private weapontype wpntype = null
endglobals
private function GetDamageAmount takes integer level returns real
return 25. + 25. * level
endfunction
/////////////////////////////////////////
/////// End of User Configuration ///////
/////////////////////////////////////////
private module WispInit
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
static if LIBRARY_GT then
call TriggerAddCondition(GT_RegisterStartsEffectEvent(t, SPELLID), Filter(function thistype.SpellCast))
else
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function thistype.SpellCast))
endif
set t = null
set check = Filter(function thistype.CheckForEnemy)
set filter = Filter(function thistype.DealDamage)
set filt = Filter(function thistype.Init)
set mapmaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set mapmaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set mapminX = GetRectMinX(bj_mapInitialPlayableArea)
set mapminY = GetRectMinY(bj_mapInitialPlayableArea)
set rct = Rect(-RADIUS,-RADIUS,RADIUS,RADIUS)
endmethod
endmodule
private struct Wisp
private static thistype temp
private static boolean explode = false
private static boolexpr filter
private static boolexpr filt
private static boolexpr check
private static real mapmaxX
private static real mapmaxY
private static real mapminX
private static real mapminY
private static real px
private static real py
private static rect rct
private unit caster
private player owner
private real x
private real y
private integer i
private integer count
private integer timecount
private integer level
private real damage
private unit array wisp [ARRAYSIZE]
private real array facing [ARRAYSIZE]
private real array speed [ARRAYSIZE]
private real array locx [ARRAYSIZE]
private real array locy [ARRAYSIZE]
static method DealDamage takes nothing returns boolean
local unit u = GetFilterUnit()
if IsUnitEnemy(u, temp.owner) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and GetUnitAbilityLevel(u, 'Avul')==0 then
call UnitDamageTarget(temp.caster, u, temp.damage, false, false, atktype, dmgtype, wpntype)
endif
set u = null
return false
endmethod
static method CheckForEnemy takes nothing returns boolean
local unit u
if explode then
return false
endif
set u = GetFilterUnit()
if IsUnitEnemy(u, temp.owner) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and GetUnitAbilityLevel(u, 'Avul')==0 then
set explode = true
endif
set u = null
return false
endmethod
private method ClearData takes nothing returns nothing
set .wisp[i] = .wisp[.count]
set .locx[i] = .locx[.count]
set .locy[i] = .locy[.count]
set .facing[i] = .facing[.count]
set .speed[i] = .speed[.count]
set .wisp[.count] = null
set .locx[.count] = 0.
set .locy[.count] = 0.
set .facing[.count] = 0.
set .speed[.count] = 0.
set .count = .count - 1
endmethod
private method periodic takes nothing returns nothing
local real gravitation
set .i = 1
loop
exitwhen i > .count
if this.timecount <= ENDCOUNT then
if .facing[i] < 0 then
set .facing[i] = facing[i]+360.0
endif
set gravitation = Atan2(GetUnitY(.caster) - .locy[i], GetUnitX(.caster) - .locx[i]) * bj_RADTODEG
if gravitation < 0 then
if .facing[i] > 180.0 then
set gravitation = gravitation + 360.0
endif
endif
set .facing[i] = .facing[i] + (gravitation-.facing[i]) * ACCURACY * (MAXSPEED-.speed[i])
set .speed[i] = .speed[i] + ACCELERATION * (TOLERANCE - Pow(RAbsBJ(gravitation-.facing[i]), INERTIA))
if .speed[i] > MAXSPEED then
set .speed[i] = MAXSPEED
endif
if .speed[i] < 0 then
set .speed[i] = 0
endif
call SetUnitFacing(.wisp[i], .facing[i])
else
if .speed[i] < MAXSPEED then
set .speed[i] = .speed[i] + 0.1
endif
endif
set .locx[i] = .locx[i] + Cos(.facing[i] * bj_DEGTORAD) * .speed[i]
set .locy[i] = .locy[i] + Sin(.facing[i] * bj_DEGTORAD) * .speed[i]
if .locx[i] > mapmaxX or .locy[i] > mapmaxY or .locx[i] < mapminX or .locy[i] < mapminY then
call RemoveUnit(.wisp[i])
call .ClearData()
else
call SetUnitX(.wisp[i], .locx[i])
call SetUnitY(.wisp[i], .locy[i])
set temp = this
call GroupEnumUnitsInArea(ENUM_GROUP, .locx[i], .locy[i], RADIUSC, check)
if explode then
call GroupEnumUnitsInArea(ENUM_GROUP, .locx[i], .locy[i], RADIUSDMG, filter)
call UnitApplyTimedLife(.wisp[i], 'BTLF', 0.01)
call .ClearData()
set explode = false
else
set i = i + 1
endif
endif
endloop
set .timecount = .timecount + 1
if .count == 0 or .timecount >= MAXCOUNT then
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
private static method Init takes nothing returns boolean
local thistype this = temp
local destructable d = GetFilterDestructable()
local real x = GetDestructableX(d)
local real y = GetDestructableY(d)
local real dx = x - .x
local real dy = y - .y
local integer destrID = GetDestructableTypeId(d)
if SquareRoot(dx*dx+dy*dy) > RADIUS then
return false
endif
if (destrID == TREE1 or destrID == TREE2) then // change this if you require more trees
set .count = .count + 1
set .facing[.count] = GetRandomReal(0., 360.)
set .wisp[.count] = CreateUnit(.owner, UNITID, x, y, .facing[.count])
set .locx[.count] = x
set .locy[.count] = y
set .speed[.count] = STARTSPEED
call UnitAddAbility(.wisp[.count], 'Amrf')
call UnitRemoveAbility(.wisp[.count], 'Amrf')
call SetUnitFlyHeight(.wisp[.count], HEIGHT, 0)
endif
set d = null
return false
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set .caster = GetTriggerUnit()
set .owner = GetTriggerPlayer()
set .x = GetSpellTargetX()
set .y = GetSpellTargetY()
set .count = 0
set .timecount = 0
set .level = GetUnitAbilityLevel(.caster, SPELLID)
set .damage = GetDamageAmount(.level)
set temp = this
call MoveRectTo(rct, .x, .y)
call EnumDestructablesInRect(rct, filt, null)
return this
endmethod
private static method SpellCast takes nothing returns boolean
static if LIBRARY_GT then
call thistype.create().startPeriodic()
else
if GetSpellAbilityId() == SPELLID then
call thistype.create().startPeriodic()
endif
endif
return false
endmethod
implement WispInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary