Name | Type | is_array | initial_value |
//TESH.scrollpos=102
//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
//*
//* Thievery
//* - by jim7777
//*
//* This ability is based on the ability of Zidane Tribal in Final Fantasy IX
//* This spell is fully MUI
//*
//* Installation:
//* -Copy this trigger, the GroupUtils libraries (if you don't have it on your map)
//* -Copy the 2 dummy units and the ability into your map
//* -Edit the configurables to fit your needs
//* -DO NOT FORGET to edit the calculate function to suit your needs!!!
//* -Test your map
//* -Lastly, enjoy it!
//*
//*****************************************************************************
library Thievery initializer Init requires GroupUtils
//CONFIGURABLES
globals
private constant integer ABILITY_ID = 'A000' // RawCode of the ability (Thievery)
private constant integer DUMMY_ID = 'h000' // RawCode of the dummy unit (Thievery Dummy)
private constant integer ATTACKER_ID = 'n000' //RawCode of the attacker unit (Dummy Thievery Attacker)
private constant real INTERVAL = 0.3 //interval seconds between each minor damage.
private constant real MINOR_DMG = 30. //damage dealt every after interval
private constant real RANGE = 400. //maximum range of units to be affected
private constant real FINAL_DMG = 100. // set the base of the final damage here to be multiplied to the ability level
private constant string SFX = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl" //SFX for final blast
private constant string SFX2 = "Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCaster.mdl" //SFX for final blast
private constant string SFX3 = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl" //SFX for final blast
private constant string PERSFX = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl"
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO //Attack type when dealing damage
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL //Damage type when dealing damage
private constant boolean ENABLE_RECYCLING = false
//set this to false if you want more special effects, set this to true if you want to work the spell faster but no special effects
// ENABLE_RECYLING is very important as it will almost change the spell if you want it more effects but use more memory (because of periodically creating units) or vice-versa
endglobals
//Set some calculations here on how it would calculate the final damage
private function Calculate takes unit u returns real
local real r
local integer level = GetUnitAbilityLevel(u,ABILITY_ID)
set r = FINAL_DMG*level // Deals FINAL_DMG*level of ability final damage (simple math will do this and you could do this in the way you like. This is just a sample
// FINAL_DMG should be set in your globals or use a custom one if you want
return r
endfunction
//End of setting damage
//* *//
//*--------------- END OF CONFIGURATION ---------------*//
//*
globals
private hashtable thiefht = InitHashtable()
endglobals
private function ThiefSetUnitId takes unit u, unit caster, integer value returns nothing
local integer i = LoadInteger(thiefht,StringHash("thiefcastables"),GetHandleId(caster)) + 1
call SaveUnitHandle(thiefht,value,GetHandleId(caster),u)
call SaveInteger(thiefht,StringHash("thiefcastables"),GetHandleId(caster),i)
endfunction
private function ThiefEndRecycle takes unit caster returns nothing
call SaveInteger(thiefht,StringHash("thiefcastables"),GetHandleId(caster),0)
endfunction
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == ABILITY_ID
endfunction
private function ShowSFX takes unit u returns nothing
local real x
local real y
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local integer i = 1
loop
exitwhen(i > 4)
set x = ux + RANGE * Cos(1.570796*i)
set y = uy + RANGE * Sin(1.570796*i)
call DestroyEffect(AddSpecialEffect(SFX2,x,y))
call DestroyEffect(AddSpecialEffect(SFX3,x,y))
set i = i + 1
endloop
endfunction
private struct Thief
unit caster
unit dummy
location target
integer count = 0
real dmg
boolean GroupDmg
boolean MinorDmg
static integer array Index
static integer Total = 0
static timer time = null
static method GroupDamage takes nothing returns nothing
local Thief dat
local unit f = GetEnumUnit()
local integer i = 0
loop
exitwhen i >= thistype.Total
set dat = thistype.Index[i]
if IsUnitEnemy(f,GetOwningPlayer(dat.caster)) and dat.GroupDmg then
call UnitDamageTarget(dat.caster,f,dat.dmg,true,true,ATTACK_TYPE,DAMAGE_TYPE,null)
endif
set i = i + 1
endloop
set f = null
endmethod
static method MinorDamage takes nothing returns nothing
local Thief dat
local unit f = GetEnumUnit()
local integer i = 0
loop
exitwhen i >= thistype.Total
set dat = thistype.Index[i]
if IsUnitEnemy(f,GetOwningPlayer(dat.caster)) and dat.MinorDmg then
call UnitDamageTarget(dat.caster,f,MINOR_DMG,true,true,ATTACK_TYPE,DAMAGE_TYPE,null)
endif
set i = i + 1
endloop
set f = null
endmethod
static method onLoop takes nothing returns nothing
local Thief dat
local real rnd = GetRandomReal(0,75)
local real ang
local real dist = GetRandomReal(100,500)
local real x
local real y
local group g = NewGroup()
local unit f
local unit d
local integer i = 0
loop
exitwhen i >= thistype.Total
set dat = thistype.Index[i]
set ang = GetUnitFacing(dat.caster) + GetRandomReal(0,360)
set x = GetUnitX(dat.dummy)
set y = GetUnitY(dat.dummy)
if ENABLE_RECYCLING == true then
call DestroyEffect(AddSpecialEffect(PERSFX,x,y))
if LoadInteger(thiefht,StringHash("thiefcastables"),GetHandleId(dat.caster)) == 0 then
set d = CreateUnit(GetOwningPlayer(dat.caster),ATTACKER_ID,x,y,ang)
call ThiefSetUnitId(d,dat.caster,dat)
else
set d = LoadUnitHandle(thiefht,dat,GetHandleId(dat.caster))
endif
else
set d = CreateUnit(GetOwningPlayer(dat.caster),ATTACKER_ID,x,y,ang)
endif
set dat.count = dat.count + 1
if dat.count > 8 then
call DestroyEffect(AddSpecialEffect(SFX,GetUnitX(dat.dummy),GetUnitY(dat.dummy)))
call GroupUnitsInArea(g, GetUnitX(dat.dummy), GetUnitY(dat.dummy), RANGE)
set dat.GroupDmg = true
call ForGroup(g,function Thief.GroupDamage)
set dat.GroupDmg = false
call ShowSFX(dat.dummy)
call PauseUnit(dat.caster,false)
call RemoveLocation(dat.target)
call RemoveUnit(dat.dummy)
call ThiefEndRecycle(dat.caster)
if ENABLE_RECYCLING == true then
call UnitApplyTimedLife(d,'BTLF',0.8)
set d = null
endif
call dat.destroy()
set thistype.Total = thistype.Total - 1
set thistype.Index[i] = thistype.Index[thistype.Total]
set i = i - 1
else
set x = GetUnitX(dat.dummy) + dist * Cos(ang * 0.017453292)
set y = GetUnitY(dat.dummy) + dist * Sin(ang * 0.017453292)
if dat.count == 1 then
call SetUnitAnimation(dat.dummy,"stand")
call IssuePointOrderLoc(d,"attackground",Location(x,y))
else
call IssuePointOrderLoc(d, "attackground",Location(x,y))
call GroupUnitsInArea(g, GetUnitX(dat.dummy), GetUnitY(dat.dummy), RANGE)
set dat.MinorDmg = true
call ForGroup(g,function Thief.MinorDamage)
set dat.MinorDmg = false
endif
endif
call ReleaseGroup(g)
if ENABLE_RECYCLING == false then
call UnitApplyTimedLife(d,'BTLF',0.8)
set d = null
endif
set i = i + 1
endloop
if thistype.Total == 0 then
call PauseTimer(thistype.time)
endif
set f = null
set g = null
endmethod
static method start takes unit u, location t returns Thief
local Thief dat = Thief.allocate()
local real facing = GetUnitFacing(u) - 45
local real x = GetLocationX(t)
local real y = GetLocationY(t)
call PauseUnit(u,true)
set dat.GroupDmg = false
set dat.MinorDmg = false
set dat.caster = u
set dat.target = t
set dat.dmg = Calculate(u)
set dat.dummy = CreateUnit(GetOwningPlayer(u),DUMMY_ID,x,y,facing)
call SetUnitAnimation(dat.dummy,"birth")
if thistype.Total == 0 then
call TimerStart(thistype.time, INTERVAL, true, function thistype.onLoop)
endif
set thistype.Index[thistype.Total] = dat
set thistype.Total = thistype.Total + 1
return dat
endmethod
endstruct
private function Actions takes nothing returns nothing
call Thief.start(GetTriggerUnit(),GetSpellTargetLoc())
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddAction(t,function Actions)
call TriggerAddCondition(t,Condition(function Conditions))
set Thief.time = CreateTimer()
call Preload(SFX)
call Preload(SFX2)
call Preload(SFX3)
call Preload(PERSFX)
set t = null
endfunction
endlibrary