Name | Type | is_array | initial_value |
//TESH.scrollpos=12
//TESH.alwaysfold=0
library PhoenixStrike /* v1.0.0.8 by Doomlord
*************************************************************************************
*
* Gives a percentage chance to unleash a raging phoenix
* that deals damage to enemies in its wake when attacking.
*
*************************************************************************************
*
* Credits
*
* Dirac
* -----------------------
*
* Missile library
*
* Cokemonkey11
* -----------------------
*
* DamageType library
*
* Garfield1337
* -----------------------
*
* UnitZ library
*
* Nestharus
* -----------------------
*
* Alloc Alternative
*
* Maker
* -----------------------
*
* Arcing Floating Text library
*
*************************************************************************************
*
* */ uses /*
*
* */ Missile /* hiveworkshop.com/forums/jass-resources-412/system-missile-207854/
* */ DamageType /* hiveworkshop.com/forums/jass-resources-412/system-damagetype-structureddd-extension-228883/
* */ UnitZ /* hiveworkshop.com/forums/jass-resources-412/snippet-getterrainz-unitz-236942/
* */ Alloc /* hiveworkshop.com/forums/jass-resources-412/snippet-alloc-alternative-221493/
* */ optional FloatingTextArc /* hiveworkshop.com/forums/spells-569/arcing-floating-text-1-0-0-3-a-228710/
*
************************************************************************************
*
* SETTINGS
*
*/
globals
// Ability raw code
private constant integer ABI_ID = 'A002'
// Maximum travel range
private constant real MAX_TRAVEL_RANGE = 1000
// Missile model
private constant string MISSILEFX = "Units\\Human\\Phoenix\\Phoenix.mdl"
// On Collide Model
private constant string COLLIDEFX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
// Attachment point
private constant string ATTACHPOINT = "chest"
// Attack type
private constant attacktype ATT = ATTACK_TYPE_HERO
// Damage type
private constant damagetype DAM = DAMAGE_TYPE_NORMAL
// Weapon type
private constant weapontype WEA = WEAPON_TYPE_METAL_HEAVY_SLICE
endglobals
// Trigger chance
private function getChance takes integer level returns boolean
return GetRandomInt(1, 100) <= 10*level
endfunction
// Area of effect
private function getAoE takes integer level returns real
return 200.
endfunction
// Agility multiplier
private function getMultiplier takes integer level returns integer
return 1 + level*2
endfunction
// Damage on collide
private function getDamage takes unit u, integer level, integer multiplier returns real
if not IsUnitType(u, UNIT_TYPE_HERO) then
return 100 + level*50.
endif
return GetHeroAgi(u, true)*multiplier*1.
endfunction
// Missile speed per 0.03125s
private function getSpeed takes integer lvl returns real
return 45.
endfunction
// Filter out some targets
private function filterUnit takes unit caster, unit u returns boolean
return /*
*/ IsUnitEnemy(u, GetOwningPlayer(caster)) and /* Target is an enemy of caster
*/ not IsUnitType(u, UNIT_TYPE_DEAD) and /* Target is alive
*/ GetUnitAbilityLevel(u, 'Avul') == 0 and /* Target is not invulnerable
*/ not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* Target is not a structure
*/
endfunction
// Just skip this if you want. There is not much to read anyway.
private struct PS extends array
implement Alloc
Missile m
static method onFinish takes Missile m returns boolean
call DestroyEffect(AddSpecialEffect(MISSILEFX, m.impact.x, m.impact.y))
call thistype(m.data).deallocate()
return true
endmethod
static method onCollide takes Missile m, unit justHit returns boolean
if filterUnit(m.source, justHit) then
// Reproduce the weapon sound
call UnitDamageTarget(m.source, justHit, 0, false, false, ATT, DAM, WEA)
// The function somehow reverses the damage dealt. Hence the need to use a negative amount.
call DamageType.dealCodeDamage(m.source, justHit, getDamage(m.source, GetUnitAbilityLevel(m.source, ABI_ID), getMultiplier(GetUnitAbilityLevel(m.source, ABI_ID))))
call DestroyEffect(AddSpecialEffectTarget(COLLIDEFX, justHit, ATTACHPOINT))
endif
return false
endmethod
implement MissileStruct
private static method create takes nothing returns thistype
local real a = GetRandomReal(-bj_PI, bj_PI)
local unit u = GetEventDamageSource()
local integer i = GetUnitAbilityLevel(u, ABI_ID)
local real r = GetRandomReal(300, MAX_TRAVEL_RANGE)
local real x = GetUnitX(u) + r*Cos(a)
local real y = GetUnitY(u) + r*Sin(a)
local unit u1 = GetTriggerUnit()
local thistype this = thistype.allocate()
set .m = Missile.create (x, y, GetUnitZ(u), Atan2(GetUnitY(u1) - y, GetUnitX(u1) - x), MAX_TRAVEL_RANGE, GetUnitZ(u1))
set m.source = u
set m.speed = getSpeed(i)
set m.model = MISSILEFX
set m.collision = getAoE(i)
set m.scale = 1.5
set m.data = this
static if LIBRARY_FloatingTextArc then
call ArcingTextTag.create("|c00ECEC00 PHOENIX STRIKE!|r", u)
endif
call launch(m)
return this
endmethod
static method handler takes nothing returns nothing
local unit u = GetEventDamageSource ()
if DamageType.get() == DamageType.ATTACK and /*
*/ GetUnitAbilityLevel(u, ABI_ID) > 0 and /*
*/ IsUnitEnemy(GetTriggerUnit(), GetOwningPlayer(u)) and /*
*/ getChance(GetUnitAbilityLevel(u, ABI_ID)) then
call thistype.create ()
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
call StructuredDD.addHandler(function thistype.handler)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Alloc /* v1.0.0.1
*************************************************************************************
*
* */uses/*
*
* */ ErrorMessage /* hiveworkshop.com/forums/submissions-414/snippet-error-message-239210/
*
************************************************************************************
*
* module Alloc
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
************************************************************************************/
module Alloc
private static integer array recycler
private static integer instanceCount = 0
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
if (this == 0) then
debug call ThrowError(instanceCount == 8191, "Alloc", "allocate", "thistype", 0, "Overflow.")
set this = instanceCount + 1
set instanceCount = this
else
set recycler[0] = recycler[this]
endif
debug set recycler[this] = -1
return this
endmethod
method deallocate takes nothing returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ErrorMessage /* v1.0.1.4
*************************************************************************************
*
* Issue THW Compliant Error Messages
*
************************************************************************************
*
* debug function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
* - In the event of an error the game will be permanently paused
*
* debug function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
*
************************************************************************************/
static if DEBUG_MODE then
private struct Fields extends array
static constant string COLOR_RED = "|cffff0000"
static constant string COLOR_YELLOW = "|cffffff00"
static string lastError = null
endstruct
private function Pause takes nothing returns nothing
call PauseGame(true)
endfunction
private function ThrowMessage takes string libraryName, string functionName, string objectName, integer objectInstance, string description, string errorType, string color returns nothing
local string str
local string color_braces = "|cff66FF99"
local string orange = "|cffff6600"
set str = "->\n-> " + color_braces + "{|r " + "Library" + color_braces + "(" + orange + libraryName + color_braces + ")"
if (objectName != null) then
if (objectInstance > 0) then
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + " (|rinstance = " + orange + I2S(objectInstance) + color_braces + ") )" + "|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
else
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + ")|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
else
set str = str + "|r." + "Function" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
set str = str + color_braces + " }|r " + "has thrown an exception of type " + color_braces + "(" + color + errorType + color_braces + ")|r."
set Fields.lastError = str + "\n->\n" + "-> " + color + description + "|r\n->"
endfunction
function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Error", Fields.COLOR_RED)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
call TimerStart(CreateTimer(), 0, true, function Pause)
set objectInstance = 1/0
endif
endfunction
function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Warning", Fields.COLOR_YELLOW)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
set Fields.lastError = null
endif
endfunction
endif
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.1
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
private hashtable ht = InitHashtable()
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=66
//TESH.alwaysfold=0
//* API:
//* boolean ADD_ALL_UNITS: If enabled, a trigger turns on which automatically
//* registers all units in the map.
//* integer BUCKET_SIZE: How many units to add to each 'bucket' - a larger
//* bucket will have their trigger refresh less frequently but will be
//* more computationally expensive. A good starting value is about 20.
//* real PER_CLEANUP_TIMEOUT: How many seconds to wait in between each
//* scan for empty buckets. This value should be lower if units die often
//* in your map. A good starting value is about 60.
//* static method addHandler: Registers a callback function to the generic
//* unit damage event. Example: call StructuredDD.addHandler(function h)
//* static method add: Adds a unit to a bucket. If ADD_ALL_UNITS is enabled,
//* this method need not be used.
library StructuredDD
globals
//<< BEGIN SETTINGS SECTION
//* Set this to true if you want all units in your map to be
//* automatically added to StructuredDD. Otherwise you will have to
//* manually add them with StructuredDD.add(u).
private constant boolean ADD_ALL_UNITS=true
//* This is the amount of units that exist in each trigger bucket.
//* This number should be something between 5 and 30. A good starting
//* value will be an estimate of your map's average count of units,
//* divided by 10. When in doubt, just use 20.
private constant integer BUCKET_SIZE=20
//* This is how often StructuredDD will search for empty buckets. If
//* your map has units being created and dying often, a lower value
//* is better. Anything between 10 and 180 is good. When in doubt,
//* just use 60.
private constant real PER_CLEANUP_TIMEOUT=60.
//>> END SETTINGS SECTION
endglobals
//* Our bucket struct which contains a trigger and its associated contents.
private struct bucket
integer bucketIndex=0
trigger trig=CreateTrigger()
unit array members[BUCKET_SIZE]
endstruct
//* Our wrapper struct. We never intend to actually instanciate "a
//* StructuredDD", we just use this for a pretty, java-like API :3
struct StructuredDD extends array
private static boolexpr array conditions
private static bucket array bucketDB
private static integer conditionsIndex=-1
private static integer dbIndex=-1
private static integer maxDBIndex=-1
//* This method gets a readily available bucket for a unit to be added.
//* If the "current" bucket is full, it returns a new one, otherwise
//* it just returns the current bucket.
private static method getBucket takes nothing returns integer
local integer index=0
local integer returner=-1
local bucket tempDat
if thistype.dbIndex!=-1 and thistype.bucketDB[thistype.dbIndex].bucketIndex<BUCKET_SIZE then
return thistype.dbIndex
else
set thistype.maxDBIndex=thistype.maxDBIndex+1
set thistype.dbIndex=thistype.maxDBIndex
set tempDat=bucket.create()
set thistype.bucketDB[.maxDBIndex]=tempDat
loop
exitwhen index>thistype.conditionsIndex
call TriggerAddCondition(tempDat.trig,thistype.conditions[index])
set index=index+1
endloop
return thistype.dbIndex
endif
return -1
endmethod
//* This method is for adding a handler to the system. Whenever a
//* handler is added, damage detection will immediately trigger that
//* handler. There is no way to deallocate a handler, so don't try to
//* do this dynamically (!) Support for handler deallocation is
//* feasible (please contact me)
public static method addHandler takes code func returns nothing
local bucket tempDat
local integer index=0
set thistype.conditionsIndex=thistype.conditionsIndex+1
set thistype.conditions[thistype.conditionsIndex]=Condition(func)
loop
exitwhen index>thistype.maxDBIndex
set tempDat=thistype.bucketDB[index]
call TriggerAddCondition(tempDat.trig,thistype.conditions[thistype.conditionsIndex])
set index=index+1
endloop
endmethod
//* This method adds a unit to the damage detection system. If
//* ADD_ALL_UNITS is enabled, this method need not be used.
public static method add takes unit member returns nothing
local bucket tempDat
local integer whichBucket=thistype.getBucket()
set tempDat=thistype.bucketDB[whichBucket]
set tempDat.bucketIndex=tempDat.bucketIndex+1
set tempDat.members[tempDat.bucketIndex]=member
call TriggerRegisterUnitEvent(tempDat.trig,member,EVENT_UNIT_DAMAGED)
endmethod
//* This is just an auxillary function for ADD_ALL_UNITS' implementation
static if ADD_ALL_UNITS then
private static method autoAddC takes nothing returns boolean
call thistype.add(GetTriggerUnit())
return false
endmethod
endif
//* This method is used to check if a given bucket is empty (and thus
//* can be deallocated) - this is an auxillary reoutine for the
//* periodic cleanup system.
private static method bucketIsEmpty takes integer which returns boolean
local bucket tempDat=thistype.bucketDB[which]
local integer index=0
loop
exitwhen index==BUCKET_SIZE
//GetUnitTypeId(unit)==0 means that the unit has been removed.
if GetUnitTypeId(tempDat.members[index])!=0 then
return false
endif
set index=index+1
endloop
return true
endmethod
//* This method cleans up any empty buckets periodically by checking
//* if it has been fully allocated and then checking if all its
//* members no longer exist.
private static method perCleanup takes nothing returns nothing
local integer index=0
loop
exitwhen index>thistype.maxDBIndex
if index!=thistype.dbIndex and thistype.bucketIsEmpty(index) then
call DestroyTrigger(thistype.bucketDB[index].trig)
call thistype.bucketDB[index].destroy()
set thistype.bucketDB[index]=thistype.bucketDB[thistype.maxDBIndex]
set thistype.maxDBIndex=thistype.maxDBIndex-1
if thistype.maxDBIndex==thistype.dbIndex then
set thistype.dbIndex=index
endif
set index=index-1
endif
set index=index+1
endloop
endmethod
//* This is a initialization function necessary for the setup of
//* StructuredDD.
private static method onInit takes nothing returns nothing
local group grp
local region reg
local trigger autoAddUnits
local timer perCleanup
local unit FoG
static if ADD_ALL_UNITS then
//Add starting units
set grp=CreateGroup()
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
call thistype.add(FoG)
call GroupRemoveUnit(grp,FoG)
endloop
//Add entering units
set autoAddUnits=CreateTrigger()
set reg=CreateRegion()
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(autoAddUnits,reg,null)
call TriggerAddCondition(autoAddUnits,Condition(function thistype.autoAddC))
set autoAddUnits=null
set reg=null
endif
//enable periodic cleanup:
set perCleanup=CreateTimer()
call TimerStart(perCleanup,PER_CLEANUP_TIMEOUT,true,function thistype.perCleanup)
set perCleanup=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//* API
//* DamageType.NULLED Value returned when DamageType cannot conclusively
//* recognize damage type
//* DamageType.ATTACK Value returned when DamageType recognizes the
//* damage type as a physical attack
//* DamageType.SPELL Value returned when DamageType recognizes the
//* damage type as a spell
//* DamageType.CODE Value returned when DamageType recognizes the
//* damage type as having been dealt via dealCodeDamage()
//* DamageType.get() Requests what type of damage was dealt in the
//* scope of a StructuredDD handler
//* DamageType.dealCodeDamage() Deals an exact amount of damage to a unit. Arguments
//* are of the form (unit,unit,real) where the
//* first unit deals real damage to the second
//* unit.
library DamageType requires StructuredDD, Table
//* This struct contains data to deal damage to a target that would otherwise
//* die when hit with double damage in correction of spell damage recognition.
//* This is to counter the effect of a unit instantly healing some damage due
//* to negative spell damage, without using any unnecessary triggers.
private struct delayDat
unit target
unit source
real damage
endstruct
//* This is a struct shim which won't be instanciated; it is only useful for
//* making the pretty API like DamageType.doSomething()
struct DamageType
///< BEGIN CUSTOMIZE SECTION
//* One limitation of the spell damage reduction ability is that if a unit
//* is meant to receive bonus damage due to the damage table, such that the
//* result is greater than 100% damage, the unit would incorrectly take
//* damage capped at 100%. Using standard melee armor tables, this can only
//* occur when a damaged unit is ethereal; a simple ethereal shim no more
//* than 20 lines can be created, but for maps with custom damage tables,
//* this can be a useful option. If USE_BONUS_CALCULATOR is true, units that
//* would take greater than 100% spell damage will properly exhibit such
//* properly. (Disable this feature to improve performance)
private static constant boolean USE_BONUS_CALCULATOR=true
//* Make sure this matches the rawcode/id of your version of the DamageTypeCheck
//* ability (you must copy/paste it from the editor to your map)
private static constant integer DAMAGE_TYPE_CHECK_ID='A000'
///> END CUSTOMIZE SECTION
public static constant integer NULLED=-1
public static constant integer ATTACK= 0
public static constant integer SPELL = 1
public static constant integer CODE = 2
//ConvertAttackType(7) is a hidden attack type with some unique properties
//that make it very useful for us. For more information see goo.gl/9k8tn
private static constant attacktype ATTACK_TYPE_UNIVERSAL=ConvertAttackType(7)
//How long to delay before dealing the second half of the "correction" damage.
//Significant tests have shown that a delay of 0. will have no issues.
private static constant real DELAY_AMOUNT=0.
private static integer lastDamageType=thistype.NULLED
private static HandleTable delayed
//* Use this to get damage type in a damage event handler.
public static method get takes nothing returns integer
local real sourceDamage=GetEventDamage()
if thistype.lastDamageType==thistype.CODE then
return thistype.CODE
elseif sourceDamage>0. then
return thistype.ATTACK
elseif sourceDamage<0. then
return thistype.SPELL
endif
return thistype.NULLED
endmethod
//* Use this to damage units by trigger in your map without causing an infinite
//* loop.
public static method dealCodeDamage takes unit who, unit target, real damage returns nothing
local integer prevType=thistype.lastDamageType
local real hp=GetWidgetLife(target)-.405
local real d=damage
set thistype.lastDamageType=thistype.CODE
if hp>d then
call SetWidgetLife(target,hp-d+.405)
call UnitDamageTarget(who,target,0.,true,false,thistype.ATTACK_TYPE_UNIVERSAL,DAMAGE_TYPE_UNIVERSAL,null)
else
call UnitDamageTarget(who,target,1000000.+d,true,false,thistype.ATTACK_TYPE_UNIVERSAL,DAMAGE_TYPE_UNIVERSAL,null)
//Also deal magic damage for the special (unmodifiable) case of ethereal
//units.
call UnitDamageTarget(who,target,1000000.+d,true,false,ATTACK_TYPE_MAGIC,DAMAGE_TYPE_UNIVERSAL,null)
endif
set thistype.lastDamageType=prevType
endmethod
//* Auxillary function for adding the dummy ability to all units.
private static method c takes nothing returns boolean
local unit tU=GetTriggerUnit()
call UnitAddAbility(tU,thistype.DAMAGE_TYPE_CHECK_ID)
call UnitMakeAbilityPermanent(tU,true,thistype.DAMAGE_TYPE_CHECK_ID)
set tU=null
return false
endmethod
//* This is the function that occurs 0 seconds after damaging a unit, for the
//* case that damaging it by twice as much at the same time would kill it
//* unexpectedly.
private static method after takes nothing returns nothing
local timer time=GetExpiredTimer()
local delayDat tempDat=thistype.delayed[time]
call thistype.dealCodeDamage(tempDat.source,tempDat.target,tempDat.damage)
call thistype.delayed.flush(time)
call tempDat.destroy()
call DestroyTimer(time)
set time=null
endmethod
private static method getUnitBonusSpellResistance takes unit u returns real
local integer prevType=thistype.lastDamageType
local real life=GetWidgetLife(u)
local real scale=GetUnitState(u,UNIT_STATE_MAX_LIFE)
call SetWidgetLife(u,scale)
set thistype.lastDamageType=thistype.CODE
call UnitDamageTarget(u,u,-scale/2.,false,false,null,DAMAGE_TYPE_UNIVERSAL,null)
set scale=2.*(scale-GetWidgetLife(u))/scale
call SetWidgetLife(u,life)
set thistype.lastDamageType=prevType
return scale
endmethod
//* This is the method that will invert any negative spell damage. It MUST be
//* StructuredDD.conditions[0] to function properly; thus, any library using a
//* StructuredDD handler should 'requires DamageType' (!)
private static method handler takes nothing returns nothing
local timer time
local delayDat tempDat
local real attemptedDamage=-GetEventDamage()
local unit tU
local real sampledLife
local real scale
if thistype.get()==thistype.SPELL then
set tU=GetTriggerUnit()
static if thistype.USE_BONUS_CALCULATOR then
set scale=thistype.getUnitBonusSpellResistance(tU)
if scale>1. then
set attemptedDamage=attemptedDamage*(scale+1.)/2.
endif
endif
set sampledLife=GetWidgetLife(tU)-.405
if sampledLife>=attemptedDamage and sampledLife<=2.*attemptedDamage then
call SetWidgetLife(tU,sampledLife-attemptedDamage)
set time=CreateTimer()
set tempDat=delayDat.create()
set tempDat.target=tU
set tempDat.source=GetEventDamageSource()
set tempDat.damage=attemptedDamage
set thistype.delayed[time]=tempDat
call TimerStart(time,DELAY_AMOUNT,false,function thistype.after)
set time=null
else
call thistype.dealCodeDamage(GetEventDamageSource(),tU,2.*attemptedDamage)
endif
set tU=null
endif
endmethod
//* Initialization method to enable the system.
private static method onInit takes nothing returns nothing
local group grp=CreateGroup()
local region reg=CreateRegion()
local trigger addBracer=CreateTrigger()
local unit FoG
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(addBracer,reg,null)
call TriggerAddCondition(addBracer,Condition(function thistype.c))
call StructuredDD.addHandler(function thistype.handler)
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
call UnitAddAbility(FoG,thistype.DAMAGE_TYPE_CHECK_ID)
call UnitMakeAbilityPermanent(FoG,true,thistype.DAMAGE_TYPE_CHECK_ID)
call GroupRemoveUnit(grp,FoG)
endloop
set thistype.delayed=HandleTable.create()
call DestroyGroup(grp)
set grp=null
set reg=null
set addBracer=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=30
//TESH.alwaysfold=0
library Missile /* v2.0.1
*/uses/*
*/ CTL /* hiveworkshop.com/forums/jass-resources-412/snippet-constant-timer-loop-32-a-201381/
*/ AdvLoc /* hiveworkshop.com/forums/submissions-414/snippet-lacking-loc-209322/
*/ optional WorldBounds /* hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
*/ optional MissileRecycler /* hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
Credits:
- emjlr3 for the deflect angle equation
- AceHart for the parabola equation
**********************************************************************/
globals
//***********************************************************************
// This dummy must use Vexorian's dummy model and it's movement type
// should be "Hover" if you want to correctly move over water, otherwise
// use "None". The dummy must also have Crow Form. If you don't have a
// dummy unit in your map then use the one MissileRecycler provides.
private constant integer DUMMY_RAW_CODE = 'n000'
endglobals
/**********************************************************************
*
* struct Missile
*
* static method create takes real originX, real originY, real originZ...
* ...real targetX, real targetY, real targetZ returns Missile
* - Missiles have the following values to be set by you.
* - real speed
* - real turn
* - real open / curve
* - real height / arc
* - real acceleration
* - real collision
* - unit target
* - unit source
* - string model
* - integer data
*
* real x
* real y
* real z
* - The missile's position
*
* real slide
* - The amount of distance it has covered.
* AdvLoc origin
* - The missile's origin Loc
* AdvLoc impact
* - Where the missile will aim to.
*
* method deflect takes real x, real y returns nothing
* - Deflects the missile from the target point, changing
* - its course.
*
* method bounce takes nothing returns nothing
* - Bounces the missile from its current Z position.
* - Useful when assigning targets to missiles that were
* - already active, it fixes the rough Z position change.
*
***********************************************************************
*
* (optional)
* static method onCollide takes Missile this, unit justHit returns boolean
* - Runs every time the missile collides with something.
* - If returns true the missile is destroyed.
*
* (optional)
* static method onPeriod takes Missile this returns boolean
* - Runs every period.
* - If returns true the missile is destroyed.
*
* (optional)
* static method onFinish takes Missile this returns boolean
* - Runs whenever the missile finishes its course.
* - If returns true the missile is destroyed.
*
* (optional)
* static method onMissile takes Missile this, Missile hit, real range returns boolean
* - Runs whenever the missile encounters another missile.
* - If returns true the missile is destroyed.
*
* (optional)
* static method onRemove takes Missile this returns nothing
* - Runs whenever the missile is deallocated.
*
* module MissileStruct
*
* static method launch takes Missile toLaunch returns nothing
*
***********************************************************************
*
* DISCLAIMER:
*
* - Missile's default owner is Player(15) and this is meant to
* - not be modifiable.
*
* - For curved missiles the "x" and "y" coordinates are only
* - correct in the moment of launch or impact, but during it's
* - air time they ignore the curve it has.
*
* - This system can support up to 600 proyectiles with complex
* - mathematics (curve, arc, collision) before it lags.
*
**********************************************************************/
struct MissileList extends array
implement LinkedList
endstruct
struct Missile extends array
//***********************************************************************
// Why LinkedList? In order to reach a high level of dynamism i'm basing
// this system on iterating through multiple linked lists, each struct
// has one and each struct iterates through it.
implement LinkedList
AdvLoc impact
AdvLoc origin
real slide
readonly real x
readonly real y
readonly real z
private real cA //current angle
private effect fx //effect
private string fP //model
private real dS
readonly unit dummy
readonly group unitsHit
boolean recycle
boolean wantDestroy
unit target
unit source
real collision
real height
real turn
real open
real damage
real speed
real acceleration
integer data
method operator model= takes string path returns nothing
call DestroyEffect(fx)
set fP=path
set fx=AddSpecialEffectTarget(path,dummy,"origin")
endmethod
method operator model takes nothing returns string
return fP
endmethod
method operator curve= takes real value returns nothing
set open=Tan(value)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)
endmethod
method operator arc= takes real value returns nothing
set height=Tan(value)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)
endmethod
method operator scale= takes real v returns nothing
call SetUnitScale(dummy,v,v,v)
set dS=v
endmethod
method operator scale takes nothing returns real
return dS
endmethod
static method createEx takes unit whichUnit, AdvLoc o, AdvLoc i returns thistype
local thistype this=allocate()
set source = null
set target = null
set acceleration = 0
set height = 0
set turn = 0
set open = 0
set unitsHit = CreateGroup()
set collision = 0
set recycle = false
set wantDestroy = false
set fP = ""
set x = o.x
set y = o.y
set z = o.z
set origin = o
set impact = i
set cA = origin.angle
set slide=0
set dummy=whichUnit
call MoveLocation(Loc.global,o.x,o.y)
call SetUnitFlyHeight(dummy,o.z-GetLocationZ(Loc.global),0)
call MissileList.base.insertNode(this)
return this
endmethod
static method createLoc takes AdvLoc o, AdvLoc i returns thistype
call AdvLoc.link(o,i)
static if LIBRARY_MissileRecycler then
return createEx(GetRecycledMissile(o.x,o.y,o.z,o.angle*bj_RADTODEG),o,i)
else
return createEx(CreateUnit(Player(15),DUMMY_RAW_CODE,o.x,o.y,o.angle*bj_RADTODEG),o,i)
endif
endmethod
static method create takes real ox, real oy, real oz, real a, real d, real iz returns thistype
local AdvLoc o = AdvLoc.create(ox,oy,oz)
local AdvLoc i = AdvLoc.create(ox+d*Cos(a),oy+d*Sin(a),iz)
call AdvLoc.link(o,i)
static if LIBRARY_MissileRecycler then
return createEx(GetRecycledMissile(o.x,o.y,o.z,o.angle*bj_RADTODEG),o,i)
else
return createEx(CreateUnit(Player(15),DUMMY_RAW_CODE,o.x,o.y,o.angle*bj_RADTODEG),o,i)
endif
endmethod
method bounce takes nothing returns nothing
call origin.move(x,y,z)
set slide=0
endmethod
method deflect takes real tx, real ty returns nothing
local real a = 2*Atan2(ty-y,tx-x)+bj_PI-cA
call impact.move(x+(origin.distance-slide)*Cos(a),y+(origin.distance-slide)*Sin(a),impact.z)
call this.bounce()
endmethod
method destroy takes nothing returns nothing
set wantDestroy=true
endmethod
method terminate takes nothing returns nothing
call DestroyEffect(fx)
call DestroyGroup(unitsHit)
set recycle=false
set fx=null
static if LIBRARY_MissileRecycler then
call RecycleMissile(dummy)
else
call RemoveUnit(dummy)
endif
call impact.unlock()
call origin.unlock()
call MissileList(this).removeNode()
call this.removeNode()
call this.deallocate()
endmethod
method move takes nothing returns nothing
local real a
local real d
local real s
local real h
local real tx
local real ty
local real ox
local real oy
local AdvLoc o
loop
exitwhen head
set o = origin
set ox = o.x
set oy = o.y
set h = height
if target!=null and GetUnitTypeId(target)!=0 then
call impact.move(GetUnitX(target),GetUnitY(target),GetUnitFlyHeight(target))
set a = Atan2(impact.y-y,impact.x-x)
set slide = origin.distance-SquareRoot((impact.x-x)*(impact.x-x)+(impact.y-y)*(impact.y-y))
else
set a = o.angle
set target = null
endif
if turn!=0 and not(Cos(cA-a)>=Cos(turn)) then
if Sin(a-cA)>=0 then
set cA = cA+turn
else
set cA = cA-turn
endif
else
set cA = a
endif
set d = o.distance
set s = slide+speed
set slide = s
call SetUnitFacing(dummy,cA*bj_RADTODEG)
set tx = x+speed*Cos(cA)
set ty = y+speed*Sin(cA)
set speed = speed + acceleration
set x = tx
set y = ty
if h!=0 or o.slope!=0 then
call MoveLocation(Loc.global,tx,ty)
set z = 4*h*s*(d-s)/(d*d)+o.slope*s+o.z
call SetUnitFlyHeight(dummy,z-GetLocationZ(Loc.global),0)
call SetUnitAnimationByIndex(dummy,R2I(Atan(origin.slope)-Atan((8*h*s-4*d*h)/(d*d))*bj_RADTODEG)+90)
endif
if open!=0 then
set a = 4*open*s*(d-s)/(d*d)
set tx = tx+a*Cos(cA+1.57)
set ty = ty+a*Sin(cA+1.57)
call SetUnitFacing(dummy,(cA+Atan(-(8*open*s-4*d*open)/(d*d)))*bj_RADTODEG)
endif
static if LIBRARY_WorldBounds then
if tx>WorldBounds.maxX or tx<WorldBounds.minX or ty>WorldBounds.maxY or ty<WorldBounds.minY then
call destroy()
else
call SetUnitX(dummy,tx)
call SetUnitY(dummy,ty)
endif
else
call SetUnitX(dummy,tx)
call SetUnitY(dummy,ty)
endif
if s>=d then
set recycle = true
endif
set this = next
endloop
endmethod
endstruct
globals
private integer ACTIVE = 0
private integer SIZE = 0
private trigger FIRE = CreateTrigger()
private integer array STACK
private integer array INSTANCES
private TimerGroup32 array TIMER
private Missile array NODE
endglobals
//***********************************************************************
// This function runs periodically. Can you see the trigger evaluation
// at the end? If you've read T32 then you know exactly what it does.
// The loop above is for cleaning up, the SIZE variable keeps track of
// how many instances have been deallocated by the user, if higher than
// 0 then some of them need to be removed. STACK[SIZE] stores the value
// of the deallocated instances.
private function Execute takes nothing returns nothing
loop
exitwhen SIZE==0
set ACTIVE=ACTIVE-1
set SIZE=SIZE-1
set INSTANCES[STACK[SIZE]]=INSTANCES[STACK[SIZE]]-1
if INSTANCES[STACK[SIZE]]==0 then
call TIMER[STACK[SIZE]].stop()
if ACTIVE==0 then
return
endif
endif
endloop
call TriggerEvaluate(FIRE)
endfunction
//***********************************************************************
// Adds a new instance to the given struct index (This system attaches
// indexes to every struct you implement MissileStruct to) If the amount
// of INSTANCES[index] was 0 then it adds the struct's iterate method to
// the FIRE trigger for it's evaluation. ACTIVE keeps track of all
// allocated instances, if it was 0 that means the timer isn't even
// running yet, it needs to be started.
private function StartPeriodic takes integer index returns nothing
if INSTANCES[index]==0 then
call TIMER[index].start()
endif
set ACTIVE=ACTIVE+1
set INSTANCES[index]=INSTANCES[index]+1
endfunction
//***********************************************************************
// Adds the struct's index to the stack to clear it in the Execute
// function above.
private function StopPeriodic takes integer index returns nothing
set STACK[SIZE]=index
set SIZE=SIZE+1
endfunction
module MissileStruct
private static method missileTerminate takes Missile this returns nothing
static if thistype.onRemove.exists then
call onRemove(this)
endif
call this.terminate()
call StopPeriodic(thistype.typeid)
endmethod
static method unpin takes Missile this returns nothing
call this.removeNode()
call StopPeriodic(thistype.typeid)
endmethod
static if thistype.onMissile.exists then
private static method onMissileLoop takes Missile this returns nothing
local Missile node = MissileList.base
local real ox = this.x
local real oy = this.y
loop
set node = node.next
exitwhen MissileList(node).head
loop
exitwhen node.head
if node!=this and onMissile(this,node,SquareRoot((ox-node.x)*(ox-node.x)+(oy-node.y)*(oy-node.y))) then
call missileTerminate(this)
endif
set node = MissileList(node).next
endloop
endloop
endmethod
endif
//***********************************************************************
// First it takes the struct's first instance, which is the next to the
// head (NODE[thistype.typeid].next) and starts iterating through it until it hits
// the head again. Then checks if the missile is marked for recycling,
// if so it runs the methods and destroys the missile.
private static method missileIterate takes nothing returns nothing
local unit u
local Missile this = NODE[thistype.typeid].next
call this.move()
loop
exitwhen this.head
if this.wantDestroy then
static if thistype.onFinish.exists then
call thistype.onFinish(this)
endif
call missileTerminate(this)
else
if this.recycle then
static if thistype.onCollide.exists then
static if thistype.onFinish.exists then
if this.target==null then
if thistype.onFinish(this) then
call missileTerminate(this)
else
set this.recycle = false
endif
elseif thistype.onCollide(this,this.target) then
call missileTerminate(this)
else
set this.recycle = false
endif
else
if this.target==null then
call missileTerminate(this)
elseif thistype.onCollide(this,this.target) then
call missileTerminate(this)
else
set this.recycle = false
endif
endif
else
static if thistype.onFinish.exists then
if thistype.onFinish(this) then
call missileTerminate(this)
else
set this.recycle = false
endif
else
call missileTerminate(this)
endif
endif
else
static if thistype.onCollide.exists then
if this.collision!=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup,this.x,this.y,this.collision,null)
loop
set u = FirstOfGroup(bj_lastCreatedGroup)
exitwhen u==null
if not(IsUnitInGroup(u,this.unitsHit)) and u!=this.target and thistype.onCollide(this,u) then
call missileTerminate(this)
endif
call GroupAddUnit(this.unitsHit,u)
call GroupRemoveUnit(bj_lastCreatedGroup,u)
endloop
endif
endif
endif
endif
static if thistype.onMissile.exists then
call onMissileLoop(this)
endif
static if thistype.onPeriod.exists then
if thistype.onPeriod(this) then
call missileTerminate(this)
endif
endif
set this = this.next
endloop
set u=null
endmethod
static method launch takes Missile this returns nothing
call StartPeriodic(thistype.typeid)
call NODE[thistype.typeid].insertNode(this)
endmethod
private static method onInit takes nothing returns nothing
set NODE[thistype.typeid] = Missile.createNode()
set TIMER[thistype.typeid] = TimerGroup32.create(function thistype.missileIterate)
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.8
*************************************************************************************
*
* Assigns unique indexes to units via unit user data.
*
*************************************************************************************
*
* */uses/*
*
* */ WorldBounds /* hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
* */ Event /* hiveworkshop.com/forums/submissions-414/snippet-event-186555/
*
************************************************************************************
*
* SETTINGS
*/
globals
constant integer ABILITIES_UNIT_INDEXER = 'A000'
endglobals
/*
************************************************************************************
*
* Functions
*
* function RegisterUnitIndexEvent takes boolexpr codeToRegister, Event unitIndexEvent returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger triggerToRegister, Event unitIndexEvent returns nothing
*
* function GetUnitById takes integer index returns unit
* - Returns unit given a unit index
* function GetUnitId takes unit u returns integer
* - Returns unit index given a unit
*
* function IsUnitIndexed takes unit u returns boolean
* function IsUnitDeindexing takes unit u returns boolean
*
* function GetIndexedUnitId takes nothing returns integer
* function GetIndexedUnit takes nothing returns unit
*
************************************************************************************
*
* module UnitIndexStructMethods
* static method operator [] takes unit u returns thistype
* - Return GetUnitUserData(u)
*
* readonly unit unit
* - The indexed unit of the struct
*
************************************************************************************
*
* module UnitIndexStruct extends UnitIndexStructMethods
*
* - A pseudo module interface that runs a set of methods if they exist and provides
* - a few fields and operators. Runs on static ifs to minimize code.
*
* readonly boolean allocated
* - Is unit allocated for the struct
*
* Interface:
*
* - These methods don't have to exist. If they don't exist, the code
* - that calls them won't even be in the module.
*
* private method index takes nothing returns nothing
* - called when a unit is indexed and passes the filter.
* -
* - thistype this: Unit's index
* private method deindex takes nothing returns nothing
* - called when a unit is deindexed and is allocated for struct
* -
* - thistype this: Unit's index
* private static method filter takes unit unitToIndex returns boolean
* - Determines whether or not to allocate struct for unit
* -
* - unit unitToIndex: Unit being filtered
*
************************************************************************************
*
* struct UnitIndexer extends array
*
* - Controls the unit indexer system.
*
* static constant Event UnitIndexer.INDEX
* static constant Event UnitIndexer.DEINDEX
* - Don't register functions and triggers directly to the events. Register them via
* - RegisterUnitIndexEvent and TriggerRegisterUnitIndexEvent.
*
* static boolean enabled
* - Enables and disables unit indexing. Useful for filtering out dummy units.
*
************************************************************************************
*
* struct UnitIndex extends UnitIndexStructMethods
*
* - Constrols specific unit indexes.
*
* method lock takes nothing returns nothing
* - Locks an index. When an index is locked, it will not be recycled
* - when the unit is deindexed until all locks are removed. Deindex
* - events still fire at the appropriate times, the index just doesn't
* - get thrown into the recycler.
* method unlock takes nothing returns nothing
* - Unlocks an index.
*
************************************************************************************/
globals
private trigger q=CreateTrigger()
private trigger l=CreateTrigger()
private unit array e
private integer r=0
private integer y=0
private integer o=0
private boolean a=false
private integer array n
private integer array p
private integer array lc
endglobals
function GetIndexedUnitId takes nothing returns integer
return o
endfunction
function GetIndexedUnit takes nothing returns unit
return e[o]
endfunction
//! runtextmacro optional UNIT_LIST_LIB()
private struct PreLoader extends array
public static method run takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
set a=true
endmethod
public static method eval takes trigger t returns nothing
local integer f=n[0]
local integer d=o
loop
exitwhen 0==f
if (IsTriggerEnabled(t)) then
set o=f
if (TriggerEvaluate(t)) then
call TriggerExecute(t)
endif
else
exitwhen true
endif
set f=n[f]
endloop
set o=d
endmethod
public static method evalb takes boolexpr c returns nothing
local trigger t=CreateTrigger()
local thistype f=n[0]
local integer d=o
call TriggerAddCondition(t,c)
loop
exitwhen 0==f
set o=f
call TriggerEvaluate(t)
set f=n[f]
endloop
call DestroyTrigger(t)
set t=null
set o=d
endmethod
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO()
private module UnitIndexerInit
private static method onInit takes nothing returns nothing
local integer i=15
local boolexpr bc=Condition(function thistype.onLeave)
local boolexpr bc2=Condition(function thistype.onEnter)
local group g=CreateGroup()
local player p
set INDEX=CreateEvent()
set DEINDEX=CreateEvent()
call TriggerRegisterEnterRegion(q,WorldBounds.worldRegion,bc2)
loop
set p=Player(i)
call TriggerRegisterPlayerUnitEvent(l,p,EVENT_PLAYER_UNIT_ISSUED_ORDER,bc)
call SetPlayerAbilityAvailable(p,ABILITIES_UNIT_INDEXER,false)
call GroupEnumUnitsOfPlayer(g,p,bc2)
exitwhen 0==i
set i=i-1
endloop
call DestroyGroup(g)
set bc=null
set g=null
set bc2=null
set p=null
call TimerStart(CreateTimer(),0,false,function PreLoader.run)
endmethod
endmodule
struct UnitIndex extends array
method lock takes nothing returns nothing
debug if (null!=e[this]) then
set lc[this]=lc[this]+1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO LOCK NULL INDEX")
debug endif
endmethod
method unlock takes nothing returns nothing
debug if (0<lc[this]) then
set lc[this]=lc[this]-1
if (0==lc[this] and null==e[this]) then
set n[this]=y
set y=this
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO UNLOCK UNLOCKED INDEX")
debug endif
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
static method operator [] takes unit whichUnit returns thistype
return GetUnitUserData(whichUnit)
endmethod
endstruct
struct UnitIndexer extends array
readonly static Event INDEX
readonly static Event DEINDEX
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(q)
endmethod
static method operator enabled= takes boolean b returns nothing
if (b) then
call DisableTrigger(q)
call DisableTrigger(l)
else
call EnableTrigger(q)
call EnableTrigger(l)
endif
endmethod
private static method onEnter takes nothing returns boolean
local unit Q=GetFilterUnit()
local integer i
local integer d=o
if (Q!=e[GetUnitUserData(Q)] and 0==GetUnitUserData(Q)) then
if (0==y) then
set r=r+1
set i=r
else
set i=y
set y=n[y]
endif
call UnitAddAbility(Q,ABILITIES_UNIT_INDEXER)
call UnitMakeAbilityPermanent(Q,true,ABILITIES_UNIT_INDEXER)
call SetUnitUserData(Q,i)
set e[i]=Q
static if not LIBRARY_UnitList then
if (not a)then
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
endif
else
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
call GroupAddUnit(g,e[i])
endif
set o=i
call FireEvent(INDEX)
set o=d
endif
set Q=null
return false
endmethod
private static method onLeave takes nothing returns boolean
static if LIBRARY_UnitEvent then
implement optional UnitEventModule
else
local unit u=GetFilterUnit()
local integer i=GetUnitUserData(u)
local integer d=o
if (0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER) and u==e[i]) then
static if not LIBRARY_UnitList then
if (not a)then
set n[p[i]]=n[i]
set p[n[i]]=p[i]
endif
else
set n[p[i]]=n[i]
set p[n[i]]=p[i]
call GroupRemoveUnit(g,e[i])
endif
set o=i
call FireEvent(DEINDEX)
set o=d
if (0==lc[i]) then
set n[i]=y
set y=i
endif
set e[i]=null
endif
set u=null
endif
return false
endmethod
implement UnitIndexerInit
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO_2()
function RegisterUnitIndexEvent takes boolexpr c,integer ev returns nothing
call RegisterEvent(c, ev)
if (not a and ev==UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.evalb(c)
endif
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t,integer ev returns nothing
call TriggerRegisterEvent(t,ev)
if (not a and ev == UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.eval(t)
endif
endfunction
function GetUnitById takes integer W returns unit
return e[W]
endfunction
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
function IsUnitIndexed takes unit u returns boolean
return u==e[GetUnitUserData(u)]
endfunction
function IsUnitDeindexing takes unit u returns boolean
return IsUnitIndexed(u) and 0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER)
endfunction
module UnitIndexStructMethods
static method operator [] takes unit u returns thistype
return GetUnitUserData(u)
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
endmodule
module UnitIndexStruct
implement UnitIndexStructMethods
static if thistype.filter.exists then
static if thistype.index.exists then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
elseif (thistype.index.exists) then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
static if thistype.index.exists then
private static method onIndexEvent takes nothing returns boolean
static if thistype.filter.exists then
if (filter(e[o])) then
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
else
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
return false
endmethod
endif
static if thistype.deindex.exists then
private static method onDeindexEvent takes nothing returns boolean
static if thistype.filter.exists then
static if thistype.index.exists then
if (thistype(o).allocated) then
set thistype(o).allocated=false
call thistype(o).deindex()
endif
else
if (filter(e[o])) then
call thistype(o).deindex()
endif
endif
else
static if thistype.index.exists then
set thistype(o).allocated=false
endif
call thistype(o).deindex()
endif
return false
endmethod
endif
static if thistype.index.exists then
static if thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
else
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
endmethod
endif
elseif thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Event
//2.0.0.1
/////////////////////////////////////////////////////////////////////////
//function CreateEvent takes nothing returns integer
//function TriggerRegisterEvent takes trigger t, integer ev returns nothing
//function RegisterEvent takes boolexpr c, integer ev returns nothing
//function FireEvent takes integer ev returns nothing
//struct Event extends array
//static method create takes nothing returns thistype
//method registerTrigger takes trigger t returns nothing
//method register takes boolexpr c returns nothing
//method fire takes nothing returns nothing
/////////////////////////////////////////////////////////////////////////
globals
private real q=0
endglobals
struct Event extends array
private static integer w=0
private static trigger array e
static method create takes nothing returns thistype
set w=w+1
set e[w]=CreateTrigger()
return w
endmethod
method registerTrigger takes trigger t returns nothing
call TriggerRegisterVariableEvent(t,SCOPE_PRIVATE+"q",EQUAL,this)
endmethod
method register takes boolexpr c returns nothing
call TriggerAddCondition(e[this],c)
endmethod
method fire takes nothing returns nothing
set q=0
set q=this
call TriggerEvaluate(e[this])
endmethod
endstruct
function CreateEvent takes nothing returns Event
return Event.create()
endfunction
function TriggerRegisterEvent takes trigger t,Event ev returns nothing
call ev.registerTrigger(t)
endfunction
function RegisterEvent takes boolexpr c,Event ev returns nothing
call ev.register(c)
endfunction
function FireEvent takes Event ev returns nothing
call ev.fire()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library WorldBounds
//struct WorldBounds extends array
//static readonly rect world
// same as GetWorldBounds()
//static readonly region worldRegion
// contains world for triggers
//static readonly real maxX
//static readonly real maxY
//static readonly real minX
//static readonly real minY
//static readonly real centerX
//static readonly real centerY
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX=GetRectMaxX(world)
set maxY=GetRectMaxY(world)
set minX=GetRectMinX(world)
set minY=GetRectMinY(world)
set centerX=(maxX+minX)/2
set centerY=(minY+maxY)/2
set worldRegion=CreateRegion()
call RegionAddRect(worldRegion,world)
endmethod
endmodule
struct WorldBounds extends array
readonly static real maxX
readonly static real maxY
readonly static real minX
readonly static real minY
readonly static real centerX
readonly static real centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LinkedListModule /* v2.3.1
Easy implementation of linked lists into structs.
***********************************************************************
*
* module LinkedList
*
* - Implement at the top of your struct, must extend array
*
* thistype next
* thistype prev
* boolean head
*
* readonly static thistype base
* - Precreated head, useful for non-dynamic lists.
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
* static method createNode takes nothing returns thistype
* - Allocates a new node pointing towards itself.
* - These nodes are considered "heads" therefore it's head
* - boolean member is set to true.
* method insertNode takes thistype toInsert returns thistype
* - Inserts the instance before the node.
* method removeNode takes nothing returns nothing
* - Removes the node from the list.
* method clearNode takes nothing returns nothing
* - Deallocates all the instances within the node's range.
* method flushNode takes nothing returns nothing
* - Clears and deallocates the node.
*
* module LinkedListLite
* - Only has the members and the allocation methods.
* - To be used with the provided textmacros.
*
* textmacro LINKED_LIST_HEAD takes node
* - Turns the node into a head.
* textmacro LINKED_LIST_INSERT takes node, toInsert
* - Inserts the instance before the node.
* textmacro LINKED_LIST_REMOVE takes node
* - Removes the node from the list.
* textmacro LINKED_LIST_CLEAR takes node
* - Deallocates all the instances within the node's range.
* textmacro LINKED_LIST_FLUSH takes node
* - Clears and deallocates the node.
* textmacro LINKED_LIST_MERGE takes nodeA, nodeB
* - Merges two lists together (Don't merge loose nodes!)
*
**********************************************************************/
module LinkedListLite
private static integer instanceCount = 0
thistype next
thistype prev
boolean head
static method allocate takes nothing returns thistype
local thistype this = thistype(0).prev
if this==0 then
debug if instanceCount==8190 then
debug call BJDebugMsg("[LinkedList] Error: attempted to allocate too many instances.")
debug return 0
debug endif
set instanceCount = instanceCount+1
return instanceCount
endif
set thistype(0).prev = prev
return this
endmethod
method deallocate takes nothing returns nothing
set this.prev=thistype(0).prev
set thistype(0).prev=this
set this.head=false
endmethod
endmodule
module LinkedList
implement LinkedListLite
static method operator base takes nothing returns thistype
return 8190
endmethod
static method createNode takes nothing returns thistype
local thistype this=allocate()
//! runtextmacro LINKED_LIST_HEAD("this")
return this
endmethod
method clearNode takes nothing returns nothing
//! runtextmacro LINKED_LIST_CLEAR("this")
endmethod
method flushNode takes nothing returns nothing
//! runtextmacro LINKED_LIST_FLUSH("this")
endmethod
method insertNode takes thistype toInsert returns nothing
//! runtextmacro LINKED_LIST_INSERT("this","toInsert")
endmethod
method removeNode takes nothing returns nothing
//! runtextmacro LINKED_LIST_REMOVE("this")
endmethod
private static method onInit takes nothing returns nothing
set thistype(8190).next = 8190
set thistype(8190).prev = 8190
set thistype(8190).head = true
endmethod
static if DEBUG_MODE then
method print takes nothing returns nothing
local string s=""
local thistype exit=this
loop
set s=s+I2S(this)
set this = next
exitwhen this==exit
set s = s+" - "
endloop
call BJDebugMsg("[ "+s+" ]")
endmethod
endif
endmodule
//! textmacro LINKED_LIST_HEAD takes node
set $node$.next = this
set $node$.prev = this
set $node$.head = true
//! endtextmacro
//! textmacro LINKED_LIST_CLEAR takes node
if $node$!=$node$.next then
set $node$.next.prev = thistype(0).prev
set thistype(0).prev = $node$.prev
set $node$.next = $node$
set $node$.prev = $node$
endif
//! endtextmacro
//! textmacro LINKED_LIST_FLUSH takes node
set $node$.next.prev = thistype(0).prev
set thistype(0).prev = $node$
set $node$.head = false
//! endtextmacro
//! textmacro LINKED_LIST_INSERT takes node, toInsert
set $node$.prev.next = $toInsert$
set $toInsert$.prev = $node$.prev
set $node$.prev = $toInsert$
set $toInsert$.next = $node$
//! endtextmacro
//! textmacro LINKED_LIST_REMOVE takes node
set $node$.prev.next = $node$.next
set $node$.next.prev = $node$.prev
//! endtextmacro
//! textmacro LINKED_LIST_MERGE takes nodeA, nodeB
set $nodeA$.next.prev = $nodeB$.prev
set $nodeB$.prev.next = $nodeA$.next
set $nodeA$.next = $nodeB$
set $nodeB$.prev = $nodeA$
//! endtextmacro
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Loc /* v2.1.0
Replacement of the Location native.
***********************************************************************
*
* struct Loc
*
* readonly real x
* readonly real y
* - The Loc's position in the XY grid.
* readonly real z
* - The Loc's total height (including ground z)
*
*
* static method create takes real x, real y, real z returns Loc
* - Creates a new Loc with the given coordinates.
* method lock takes nothing returns nothing
* - Locks the instance, preventing it from being
* - deallocating when unlocked
* method unlock takes nothing returns nothing
* - Attempts to deallocate the instance, wont happen if
* - it's still being used.
*
* method move takes real x, real y, real z returns nothing
* - Assigns the Loc new coordinates.
*
**********************************************************************/
struct Loc extends array
private static integer array r
private static integer ic = 0
readonly static location global = Location(0,0)
readonly real x
readonly real y
readonly real z
//*********************************************************************
// Lock counters: a way to keep track of how many times a Loc has been
// locked, starts counting from 1. Unlocking drops the counters by 1,
// if it reaches 0 the instance is deallocated.
private integer lc
method move takes real sx, real sy, real sz returns nothing
call MoveLocation(global,sx,sy)
set x = sx
set y = sy
set z = sz+GetLocationZ(global)
endmethod
static method create takes real sx, real sy, real sz returns thistype
local thistype this = r[0]
if this==0 then
set ic = ic+1
set this = ic
else
set r[0] = r[this]
endif
call this.move(sx,sy,sz)
set lc = 1
return this
endmethod
method lock takes nothing returns nothing
set lc = lc+1
endmethod
/* idea brought by Nestharus */
method unlock takes nothing returns nothing
set lc = lc-1
if lc==0 then
set r[this] = r[0]
set r[0] = this
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=39
//TESH.alwaysfold=0
library AdvLoc /* v1.0.0
*/uses/*
*/ Loc /* hiveworkshop.com/forums/submissions-414/snippet-lacking-loc-209322/
Advanced Loc handling
***********************************************************************
*
* struct AdvLoc
* - Has the exact same API as Loc with extended functionality
*
* static method link takes Loc a, Loc b returns nothing
* - Creates a link between the two Locs, linked Locs keep
* - data relative to the other Loc's position such as...
*
* readonly real angle
* readonly real distance
* readonly real slope
* - Each Loc knows the angle/distance/slope from itself
* - to it's linked partner. Updates automatically.
*
* method lock takes nothing returns nothing
* - Locks the instance, preventing it from being
* - deallocating when unlocked
* method unlock takes nothing returns nothing
* - Attempts to deallocate the instance, wont happen if
* - it's still being used.
*
**********************************************************************/
struct AdvLoc extends array
readonly thistype ref
readonly real angle
readonly real distance
readonly real slope
//*********************************************************************
// Lock counters: a way to keep track of how many times a Loc has been
// locked, starts counting from 1. Unlocking drops the counters by 1,
// if it reaches 0 the instance is deallocated.
private integer lc
method operator x takes nothing returns real
return Loc(this).x
endmethod
method operator y takes nothing returns real
return Loc(this).y
endmethod
method operator z takes nothing returns real
return Loc(this).z
endmethod
static method create takes real ox, real oy, real oz returns thistype
local thistype this = Loc.create(ox,oy,oz)
set ref = this
return this
endmethod
private static method math takes thistype a, thistype b returns nothing
set a.angle = Atan2(b.y-a.y,b.x-a.x)
set a.distance = SquareRoot((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y))
set a.slope = (b.z-a.z)/a.distance
set b.angle = a.angle+bj_PI
set b.distance = a.distance
set b.slope = -a.slope
endmethod
static method link takes AdvLoc a, AdvLoc b returns nothing
set a.ref = b
set b.ref = a
call math(a,b)
endmethod
method unlink takes nothing returns nothing
if lc==0 then
set ref.ref = ref
set ref = this
endif
endmethod
method lock takes nothing returns nothing
set lc = lc+1
call Loc(this).lock()
endmethod
method unlock takes nothing returns nothing
set lc = lc-1
if lc==0 then
set ref.ref = ref
set ref = this
endif
call Loc(this).unlock()
endmethod
method move takes real nx, real ny, real nz returns nothing
call Loc(this).move(nx,ny,nz)
if this!=ref then
call math(this,ref)
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Dummy /* v1.0.0.5
*************************************************************************************
*
* Allows one to create dummy units that are either at or are close
* to the angle specified.
*
* Dummy recycling minimizes the number of dummy units on the map while supporting near
* instant SetUnitFacing.
*
* Assigned dummy indexes are unit indexes.
*
* Errors
* ----------------------------
*
* Any error will result in the system disabling itself and an error message
*
* -> May not kill dummies
* -> May not remove dummies
* -> May not attempt to recycle non dummies
*
*************************************************************************************
*
* Credits
*
* Vexorian for dummy.mdx
*
* Bribe
*
* Delayed recycling implemetation
* ----------------------------
*
* Bribe's delayed recycling implementation uses timestamps rather than timers, which
* helps improve performance.
*
* Stamps for queue node movement
* ----------------------------
*
* Convinced me that this was worth it
*
* Time it takes to rotate 180 degrees
* ----------------------------
*
* Supplied me with the number .73
*
*************************************************************************************
*
* */ uses /*
*
* /* Any Unit Indexer */
* */ UnitIndexer /* can be any, but one must be chosen
*
************************************************************************************
*
* SETTINGS
*
*/
globals
/*
* The unit id of dummy.mdx
*/
private constant integer DUMMY_ID = 'n000'
/*
* The space between angles for the recycler
*
* Angles used are angles from 0 to 359 in intervals of ANGLE_SPACING
*
* Higher spacing means less units but lower accuracy when creating the facing
*
*/
private constant integer ANGLE_SPACING = 15
/*
* How many projectiles to preload per angle
*
* Preloaded projectile count is 360/ANGLE_SPACING*MAX_PROJECTILES
*
*/
private constant integer PRELOAD_PROJECTILES_PER_ANGLE = 1//50
/*
* How much to delay before recycling dummy
*/
private constant real RECYCLE_DELAY = 2
endglobals
/*
************************************************************************************
*
* library MissileRecycler uses Dummy
* ----------------------------
*
* For compatibility with Bribe's resource
*
* function GetRecycledMissile takes real x, real y, real z, real facing returns unit
* function RecycleMissile takes unit whichUnit returns nothing
*
************************************************************************************
*
* Functions
* ----------------------------
*
* function IsUnitDummy takes unit whichUnit returns boolean
*
************************************************************************************
*
*
* struct Dummy extends array
*
* Creators/Destructors
* ----------------------------
*
* static method create takes real x, real y, real facing returns Dummy
* - For those of you who really want this to return a unit, getting
* - the unit from this is very easy, so don't whine
*
* - Dummy.create().unit -> unit
*
* method destroy takes nothing returns nothing
* - For those of you who really want this to take a unit, getting
* - the dummy index is very easy.
*
* - Dummy[whichUnit].destroy()
*
* Fields
* ----------------------------
*
* readonly unit unit
*
* Operators
* ----------------------------
*
* static method operator [] takes unit dummyUnit returns Dummy
*
************************************************************************************/
private keyword Queue
globals
/*
* Used for dummy instancing
* Dummy indexes are never destroyed, so there is no need for a recycler
*/
private Queue dummyCount = 0
/*
* Used to retrieve unit handle via dummy index
*/
private unit array dummies
private integer array indexPointer
private integer array dummyPointer
/*
* The owner of all dummy units. This shouldn't be changed.
*/
private constant player DUMMY_OWNER = Player(15)
/*
* Used to apply time stamps to dummies for recycling
* purposes. A dummy is only considered recycled if its
* stamp is less than the elapsed time of stamp timer.
*/
private timer stampTimer
endglobals
function IsUnitDummy takes unit whichUnit returns boolean
return dummies[GetUnitUserData(whichUnit)] == whichUnit
endfunction
/*
* min == max - 1
* max == min + 1
*
* variance of counts must be 1
*/
private struct ArrayStack extends array
/*
* The minimum and maximum counts
*/
static thistype max = 0
static thistype min = 0
/*
* list[count].first
*/
thistype first
/*
* queue.size
*/
thistype count_p
/*
* list[count].next
*/
thistype next
/*
* list[count].prev
*/
thistype prev
/*
* list[count].first -> queue of dummies
*/
static method operator [] takes thistype index returns thistype
return index.first
endmethod
/*
* list[count].add(queue of dummies)
*/
private method add takes thistype node returns nothing
/*
* Update min/max
*/
if (integer(this) > integer(max)) then
set max = this
elseif (integer(this) < integer(min)) then
set min = this
endif
/*
* Push on to front of list like a stack
*/
set node.next = first
set node.next.prev = node
set node.prev = 0
set first = node
set node.count_p = this
endmethod
/*
* list[count].remove(list of dummies)
*/
private method remove takes thistype node returns nothing
/*
* If node is the first, update the first
*/
if (node == first) then
set first = node.next
/*
* If list is empty, update min/max
*/
if (0 == first) then
if (this == min) then
set min = max
else
set max = min
endif
endif
else
/*
* Simple removal
*/
set node.prev.next = node.next
set node.next.prev = node.prev
endif
endmethod
method operator count takes nothing returns integer
return count_p
endmethod
method operator count= takes thistype value returns nothing
/*
* Remove from list node was on
*/
call count_p.remove(this)
/*
* Add to new list
*/
call value.add(this)
endmethod
endstruct
/*
* queue = angle + 1
*/
private struct Queue extends array
private real stamp
thistype next
thistype last
/*
* Update dummy count for queue
*/
private method operator count takes nothing returns integer
return ArrayStack(this).count
endmethod
private method operator count= takes integer value returns nothing
set ArrayStack(this).count = value
endmethod
/*
* Queue with smallest number of dummies
*/
private static method operator min takes nothing returns thistype
return ArrayStack.min.first
endmethod
/*
* Queue with largest number of dummies
*/
private static method operator max takes nothing returns thistype
return ArrayStack.max.first
endmethod
static method add takes thistype dummy returns nothing
/*
* Always add to the queue with the least amount of dummies
*/
local thistype this = min
/*
* Add to end of queue
*/
set last.next = dummy
set last = dummy
set dummy.next = 0
/*
* Update queue count
*/
set count = count + 1
/*
* Match unit angle with queue
*/
call SetUnitFacing(dummies[indexPointer[dummy]], this - 1)
/*
* Apply stamp so that dummy isn't used until the stamp is expired
*/
set dummy.stamp = TimerGetElapsed(stampTimer) + RECYCLE_DELAY - .01
endmethod
static method pop takes thistype this, real x, real y, real facing returns integer
/*
* Retrieve queue and first dummy on queue given angle
*/
local unit dummyUnit //dummy unit
local thistype dummyIndex = next //dummy index
local integer unitIndex //unit idex
local thistype this2
local thistype node
local real stamp
/*
* If the queue is empty, return new dummy
*/
if (0 == dummyIndex or dummyIndex.stamp > TimerGetElapsed(stampTimer)) then
/*
* Allocate new dummy
*/
debug if (dummyCount == 8191) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DUMMY RECYCLER FATAL ERROR: DUMMY OVERLOAD")
debug set Dummy.enabled = false
debug set this = 1/0
debug endif
set dummyIndex = dummyCount + 1
set dummyCount = dummyIndex
/*
* Create and initialize new unit handle
*/
set dummyUnit = CreateUnit(DUMMY_OWNER, DUMMY_ID, x, y, facing)
set unitIndex = GetUnitUserData(dummyUnit)
set indexPointer[dummyIndex] = unitIndex
set dummyPointer[unitIndex] = dummyIndex
set dummies[unitIndex] = dummyUnit
call UnitAddAbility(dummyUnit, 'Amrf')
call UnitRemoveAbility(dummyUnit, 'Amrf')
call PauseUnit(dummyUnit, true)
return dummyIndex
endif
/*
* Remove the dummy from the queue
*/
set next = dummyIndex.next
if (0 == next) then
set last = this
endif
/*
* Only remove from the count if the queue has most dummies in it
*
* If queue doesn't have most dummies in it, take a dummy from the queue
* with most dummies in it and keep count the same
*/
if (count == ArrayStack.max) then
set count = count - 1
else
/*
* Retrieve the queue with most dummies in it as well as the
* first dummy in that queue
*/
set this2 = max
set node = this2.next
/*
* Remove first dummy from largest queue
*/
if (0 == node.next) then
set this2.last = this2
else
set this2.next = node.next
endif
set this2.count = this2.count - 1
/*
* Add first dummy to current queue
*/
set last.next = node
set last = node
set node.next = 0
/*
* Match unit angle with queue
*/
call SetUnitFacing(dummies[indexPointer[node]], this - 1)
/*
* .73 seconds is how long it takes for a dummy to rotate 180 degrees
*
* Credits to Bribe for these 4 lines of code and the .73 value
*/
set stamp = TimerGetElapsed(stampTimer) + .73
if (stamp > node.stamp) then
set node.stamp = stamp
endif
endif
/*
* Move dummy to target position
*/
set dummyUnit = dummies[indexPointer[dummyIndex]]
call SetUnitX(dummyUnit, x)
call SetUnitY(dummyUnit, y)
call SetUnitFacing(dummyUnit, facing)
set dummyUnit = null
/*
* Return first dummy from current queue
*/
return dummyIndex
endmethod
endstruct
struct Dummy extends array
debug static boolean enabled = false
debug private boolean allocated
/*
* Retrieve index given unit handle
*/
static method operator [] takes unit dummyUnit returns thistype
debug if (not enabled) then
debug return 1/0
debug endif
return GetUnitUserData(dummyUnit)
endmethod
/*
* Retrieve unit handle given index
*/
method operator unit takes nothing returns unit
debug if (not enabled) then
debug set this = 1/0
debug endif
return dummies[this]
endmethod
/*
* Slightly faster than ModuloInteger due to less args + constants
*/
private static method getClosestAngle takes integer angle returns integer
set angle = angle - angle/360*360
if (0 > angle) then
set angle = angle + 360
endif
return angle/ANGLE_SPACING*ANGLE_SPACING
endmethod
/*
* Returns either a new or a recycled dummy index
*/
static method create takes real x, real y, real facing returns Dummy
static if DEBUG_MODE then
local thistype this
if (not enabled) then
set x = 1/0
endif
set this = indexPointer[Queue.pop(getClosestAngle(R2I(facing)) + 1, x, y, facing)]
debug set allocated = true
return this
else
return indexPointer[Queue.pop(getClosestAngle(R2I(facing)) + 1, x, y, facing)]
endif
endmethod
/*
* Recycles dummy index
*/
method destroy takes nothing returns nothing
debug if (not enabled) then
debug set this = 1/0
debug endif
/*
* If the recycled dummy was invalid, issue critical error
*/
debug if (0 == GetUnitTypeId(unit) or 0 == GetWidgetLife(unit) or not allocated) then
debug if (not allocated) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: DOUBLE FREE")
debug elseif (null == unit) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: REMOVED A DUMMY")
debug elseif (0 == GetWidgetLife(unit)) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: KILLED A DUMMY")
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: ATTEMPTED TO RECYCLE NON DUMMY UNIT")
debug endif
debug set enabled = false
debug set this = 1/0
debug endif
debug if (indexPointer[dummyPointer[this]] != this) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ERROR")
debug endif
debug set allocated = false
call SetUnitPosition(dummies[this], 2147483647, 2147483647)
call Queue.add(dummyPointer[this])
endmethod
endstruct
/*
* Initialization
*/
private function Initialize takes nothing returns nothing
local unit dummy
local integer last
local integer angle
local ArrayStack queue
local integer count
/*
* This timer
*/
set stampTimer = CreateTimer()
call TimerStart(stampTimer, 604800, false, null)
/*
* The highest possible angle
*/
set last = 360/ANGLE_SPACING*ANGLE_SPACING
if (360 == last) then
set last = last - ANGLE_SPACING
if (last < ANGLE_SPACING) then
set last = 0
endif
endif
/*
* The lowest possible angle
*/
set angle = 0
/*
* Start dummy count at the last possible angle so that
* angles don't overlap with dummy indexes. This is done
* to simplify queue algorithm and improve overall performance.
* At most 360 possible dummy unit indexes will be lost due to this.
*/
set dummyCount = last + 1
/*
* Initialize ArrayStack
*/
set ArrayStack.min = PRELOAD_PROJECTILES_PER_ANGLE
set ArrayStack.max = PRELOAD_PROJECTILES_PER_ANGLE
set ArrayStack(PRELOAD_PROJECTILES_PER_ANGLE).first = 1
loop
/*
* queue pointer is angle + 1
*/
set queue = angle + 1
/*
* Only add projectiles to queue if MAX_PROJECTILES < 0
*/
if (0 < PRELOAD_PROJECTILES_PER_ANGLE) then
set count = PRELOAD_PROJECTILES_PER_ANGLE
set queue.count_p = PRELOAD_PROJECTILES_PER_ANGLE
set dummyCount = dummyCount + 1
set Queue(queue).next = dummyCount
/*
* Create and add all dummies to queue
*/
loop
/*
* Create and initialize unit handle
*/
set dummy = CreateUnit(DUMMY_OWNER, DUMMY_ID, 0, 0, angle)
set indexPointer[dummyCount] = GetUnitUserData(dummy)
set dummyPointer[GetUnitUserData(dummy)] = dummyCount
set dummies[indexPointer[dummyCount]] = dummy
call UnitAddAbility(dummy, 'Amrf')
call UnitRemoveAbility(dummy, 'Amrf')
call PauseUnit(dummy, true)
set count = count - 1
exitwhen 0 == count
/*
* Point to next
*/
set dummyCount.next = dummyCount + 1
set dummyCount = dummyCount + 1
endloop
set Queue(queue).last = dummyCount
else
set Queue(queue).last = queue
endif
exitwhen last == angle
/*
* Go to next angle
*/
set angle = angle + ANGLE_SPACING
/*
* Link queues together
*/
set queue.next = angle + 1
set ArrayStack(angle + 1).prev = queue
/*
* Go to next queue
*/
set queue = angle + 1
endloop
set dummy = null
debug set Dummy.enabled = true
endfunction
private module Init
private static method onInit takes nothing returns nothing
static if DEBUG_MODE then
call ExecuteFunc(SCOPE_PRIVATE + "Initialize")
if (not Dummy.enabled) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DUMMY RECYCLER FATAL ERROR: INITIALIZATION CRASHED, LOWER PRELOAD DUMMY COUNT")
endif
else
call Initialize()
endif
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
library MissileRecycler uses Dummy
function GetRecycledMissile takes real x, real y, real z, real facing returns unit
local Dummy dummy = Dummy.create(x, y, facing)
call SetUnitFlyHeight(dummy.unit, z, 0)
return dummy.unit
endfunction
function RecycleMissile takes unit u returns nothing
call Dummy[u].destroy()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CTL /* v1.2.0.1
*************************************************************************************
*
* CTL or Constant Timer Loop provides a loop for constant merged timers of timeout .03125
*
* Similar to T32 but pauses timer when no structs have instances and removes structs
* from timer trigger when those structs have no instances.
*
* This can also create new timers after destroying a previous timer and generates less
* code in the module. It also generates no triggers so long as the module is implemented
* at the top of the struct.
*
************************************************************************************
*
* module CTL
*
* Allows creation/destruction of timers in a struct. Provides instancing of those timers.
*
* - static method create takes nothing returns thistype
* - method destroy takes nothing returns nothing
*
* CTL (optional)
* local variables, code before running any timers
* CTLExpire (not optional)
* timer code
* CTLNull (optional)
* null any locals, runs after all timers
* CTLEnd (not optional)
*
* module CT32
*
* Converts struct into a timer group. Allows the timer group to be started and stopped.
* Instancing and looping through active timers is up to the user.
*
* - static method start takes nothing returns nothing
* - static method stop takes nothing returns nothing
*
* CT32 (not optional)
* timer code
* CT32End (not optional)
*
* struct TimerGroup32 extends array
*
* Allows for the creation of timer groups. Timer instancing and looping is entirely up
* to the user.
*
* - static method create takes code func returns thistype
* - method destroy takes nothing returns nothing
* - method start takes nothing returns nothing
* - method stop takes nothing returns nothing
*
************************************************************************************/
globals
private integer tgc = 0 //timer group count
private integer array tgr //timer group recycler
private integer ic=0 //instance count
private integer tc=0 //timer count
private integer array rf //root first
private integer array n //next
private integer array p //previous
private integer array th //timer head
private integer array ns //next stack
private trigger t=CreateTrigger()
private timer m=CreateTimer()
private triggercondition array ct
private conditionfunc array rc
private boolean array e32 //enabled
private integer array i32r //ct32 recycler
private integer i32cr = 0 //ct32 count recycler
private boolean array ir32 //is recycling
private boolean array id32 //is destroying
endglobals
private function E takes nothing returns nothing
local integer i=ns[0]
set ns[0]=0
loop
exitwhen 0==i
if (0==p[i]) then
if (0==n[i]) then
call TriggerRemoveCondition(t,ct[th[i]])
set ct[th[i]]=null
set tc=tc-1
set rf[th[i]]=0
else
set rf[th[i]]=n[i]
set p[n[i]]=0
endif
else
set p[n[i]]=p[i]
set n[p[i]]=n[i]
endif
set n[i]=n[0]
set n[0]=i
set i=ns[i]
endloop
loop
exitwhen 0 == i32cr
set i32cr = i32cr - 1
set i = i32r[i32cr]
if (not e32[i]) then
call TriggerRemoveCondition(t,ct[i])
set ct[i] = null
if (id32[i]) then
set tgr[i] = tgr[0]
set tgr[0] = i
set id32[i] = false
set e32[i] = false
set ir32[i] = false
endif
endif
endloop
if (0==tc) then
call PauseTimer(m)
else
call TriggerEvaluate(t)
endif
endfunction
private function CT takes integer r returns integer
local integer i
local integer f
if (0==n[0]) then
set i=ic+1
set ic=i
else
set i=n[0]
set n[0]=n[i]
endif
set th[i]=r
set ns[i]=-1
set f=rf[r]
if (0==f) then
set n[i]=0
set p[i]=0
set rf[r]=i
set ct[r]=TriggerAddCondition(t,rc[r])
//set ct[r] = null
if (0==tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc=tc+1
else
set n[i]=f
set p[i]=0
set p[f]=i
set rf[r]=i
endif
return i
endfunction
private function DT takes integer t returns nothing
debug if (0>ns[t]) then
set ns[t]=ns[0]
set ns[0]=t
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"TIMER LOOP ERROR: ATTEMPT TO DESTROY NULL TIMER")
debug endif
endfunction
private function A takes code c returns integer
local integer i = tgr[0]
if (0 == i) then
set i = tgc + 1
set tgc = i
else
set tgr[0] = tgr[i]
endif
set rc[i]=Condition(c)
return i
endfunction
private function A32 takes integer i returns nothing
if (not e32[i]) then
if (not ir32[i] and not id32[i]) then
set ct[i] = TriggerAddCondition(t, rc[i])
endif
if (0 == tc) then
call TimerStart(m,.031250000,true,function E)
endif
set tc = tc + 1
set e32[i] = true
endif
endfunction
private function SR32 takes integer i returns nothing
if (e32[i]) then
if (not ir32[i] and not id32[i]) then
set i32r[i32cr] = i
set i32cr = i32cr + 1
set ir32[i] = true
endif
set e32[i] = false
set tc = tc - 1
endif
endfunction
private function DT32 takes integer i returns nothing
if (not id32[i]) then
if (not ir32[i]) then
set ir32[i] = true
set tc = tc - 1
set i32r[i32cr] = i
set i32cr = i32cr + 1
set e32[i] = false
endif
set id32[i] = true
endif
endfunction
private keyword r
private keyword e
module CTL
static integer rctl32
static method create takes nothing returns thistype
return CT(rctl32)
endmethod
method destroy takes nothing returns nothing
call DT(this)
endmethod
static method ectl32 takes nothing returns boolean
local thistype this=rf[rctl32]
endmodule
module CTLExpire
implement CTL
loop
exitwhen 0==this
endmodule
module CTLNull
set this=n[this]
endloop
endmodule
module CTLEnd
implement CTLNull
return false
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
module CT32
static integer rctl32
static method ectl32 takes nothing returns boolean
endmodule
module CT32End
return false
endmethod
static method start takes nothing returns nothing
call A32(rctl32)
endmethod
static method stop takes nothing returns nothing
call SR32(rctl32)
endmethod
private static method onInit takes nothing returns nothing
set rctl32 = A(function thistype.ectl32)
endmethod
endmodule
struct TimerGroup32 extends array
static method create takes code c returns thistype
return A(c)
endmethod
method destroy takes nothing returns nothing
call DT32(this)
endmethod
method start takes nothing returns nothing
call A32(this)
endmethod
method stop takes nothing returns nothing
call SR32(this)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitZ /* v1.0.0.0
********************************************************************
*
* */uses/*
* */ GetTerrainZ /*
* */optional/*
* */ AutoFly /* hiveworkshop.com/forums/jass-resources-412/snippet-autofly-unitindexer-version-195563/
*
* function GetUnitZ takes unit whichUnit returns real
* function SetUnitZ takes unit whichUnit, real z returns real
*
********************************************************************/
function GetUnitZ takes unit u returns real
return GetTerrainZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endfunction
function SetUnitZ takes unit u, real z returns nothing
call SetUnitFlyHeight(u, z - GetTerrainZ(GetUnitX(u), GetUnitY(u)), 0)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GetTerrainZ /* v1.0.0.0
********************************************************************
*
* function GetTerrainZ takes real x, real y returns real
*
********************************************************************/
globals
private constant location L = Location(0, 0)
endglobals
function GetTerrainZ takes real x, real y returns real
call MoveLocation(L, x, y)
return GetLocationZ(L)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Arcing Text Tag v1.0.0.3 by Maker
library FloatingTextArc
globals
private constant real SIZE_MIN = 0.014 // Minimum size of text
private constant real SIZE_BONUS = 0.010 // Text size increase
private constant real TIME_LIFE = 1.0 // How long the text lasts
private constant real TIME_FADE = 0.8 // When does the text start to fade
private constant real Z_OFFSET = 30 // Height above unit
private constant real Z_OFFSET_BON = 30 // How much extra height the text gains
private constant real VELOCITY = 1.5 // How fast the text move in x/y plane
private constant real ANGLE = bj_PI/2 // Movement angle of the text. Does not apply if
// ANGLE_RND is true
private constant boolean ANGLE_RND = true // Is the angle random or fixed
private timer TMR = CreateTimer()
endglobals
struct ArcingTextTag extends array
private texttag tt
private real as // angle, sin component
private real ac // angle, cos component
private real ah // arc height
private real t // time
private real x // origin x
private real y // origin y
private string s // text
private static integer array next
private static integer array prev
private static integer array rn
private static integer ic = 0 // Instance count
private static method update takes nothing returns nothing
local thistype this=next[0]
local real p
loop
set p = Sin(bj_PI*.t)
set .t = .t - 0.03125
set .x = .x + .as
set .y = .y + .ac
call SetTextTagPos(.tt, .x, .y, Z_OFFSET + Z_OFFSET_BON * p)
call SetTextTagText(.tt, .s, SIZE_MIN + SIZE_BONUS * p)
if .t <= 0 then
set .tt = null
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
set rn[this] = rn[0]
set rn[0] = this
if next[0]==0 then
call PauseTimer(TMR)
endif
endif
set this = next[this]
exitwhen this == 0
endloop
endmethod
public static method create takes string s, unit u returns thistype
local thistype this = rn[0]
static if ANGLE_RND then
local real a = GetRandomReal(0, 2*bj_PI)
else
local real a = ANGLE
endif
if this == 0 then
set ic = ic + 1
set this = ic
else
set rn[0] = rn[this]
endif
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
set .s = s
set .x = GetUnitX(u)
set .y = GetUnitY(u)
set .t = TIME_LIFE
set .as = Sin(a)*VELOCITY
set .ac = Cos(a)*VELOCITY
set .ah = 0.
if IsUnitVisible(u, GetLocalPlayer()) then
set .tt = CreateTextTag()
call SetTextTagPermanent(.tt, false)
call SetTextTagLifespan(.tt, TIME_LIFE)
call SetTextTagFadepoint(.tt, TIME_FADE)
call SetTextTagText(.tt, s, SIZE_MIN)
call SetTextTagPos(.tt, .x, .y, Z_OFFSET)
endif
if prev[this] == 0 then
call TimerStart(TMR, 0.03125, true, function thistype.update)
endif
return this
endmethod
endstruct
endlibrary