Name | Type | is_array | initial_value |
demo_loc | location | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Frostmyr
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Frostmyr v1.7
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
// Created by: Quilnez
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// 1. Description
// The caster summons and controls 5 freezing projectiles which will periodically
// combusts his mana as cost. Every projectile deals damage periodically to nearby
// opponents. While controlling these projectiles, the caster will be unable to
// move and attack until it's ordered to stop. The spell will also stop once the
// caster's mana is depleted or it's dead.
//
// Level 1 - Burns 20 mana points per second. Deals 80 damage per second.
// Level 2 - Burns 30 mana points per second. Deals 160 damage per second.
// Level 3 - Burns 40 mana points per second. Deals 240 damage per second.
//
// To control the projectile you need to have the caster in your selection. Then
// simply right-click on the target location/unit. If you have a unit as target,
// the projectile will automatically follow it until it's dead.
//
// 2. External scripts
// (Required)
// • TimerUtils by Vexorian | wc3c.net/showthread.php?t=101322
// • Stack by Nestharus | github.com/nestharus/JASS/tree/master/jass/Data%20Structures/Stack
// • World Bounds by Nestharus | github.com/nestharus/JASS/tree/master/jass/Systems/WorldBounds
// • Unit Indexer by Nestharus | hiveworkshop.com/forums/spells-569/unit-indexer-v5-3-0-1-a-260859/
// • UnitZ by Garfield1337 | hiveworkshop.com/forums/jass-resources-412/snippet-getterrainz-unitz-236942/
// • RapidSound by Quilnez | hiveworkshop.com/threads/snippet-rapidsound.258991/
// (Optional)
// • AutoFly by Nestharus | github.com/nestharus/JASS/tree/master/jass/Systems/AutoFly
//
// 3. How to Install
// • Export all files in Import Manager to your map
// • Copy dummy unit, main spell, and cancel spell to your map
// • Copy Frostmyr and Scripts folders at Trigger Editor to your map
// • Just delete duplicated external scripts
// • Install all used scripts (libraries) properly
// • Configure the spell properly (spell id, dummy id, etc.)
//
// 4. Credits
// • BTNIce_by67chrome.blp by 67chrome
// • Mini Comet.mdx by 00110000
// • dummy.mdx by Vexorian
// • Cold Wind ambient by Google.com
//
// 5. Link
// Visit this link to check out the newest update of this spell:
// hiveworkshop.com/forums/spells.php?id=ixhbcf
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// CONFIGURATION
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
globals
//
// A. General
//
// 1. Main spell's raw code
private constant integer SPELL_ID = 'A000'
//
// 2. Cancel spell's raw code
private constant integer CANCEL_ID = 'A001'
//
// 3. Dummy unit's raw code
private constant integer DUMMY_ID = 'h000'
//
// 4. Order id used to give order to missiles
private constant integer ORDER_ID = 851971 //smart
//
// 5. Played animation for caster
private constant string ANIMATION = "channel"
//
// 6. Created special effect whenever a unit takes damage from spell
private constant string DAMAGE_SFX = "war3mapImported\\ForstmyrTarget.mdx"
private constant string DAMAGE_SFX_PT = "chest"
//
// 7. Played sound effect whenever a unit takes damage from spell
private constant string DAMAGE_SOUND = "war3mapImported\\FrostmyrDamage.wav"
private constant integer DSOUND_VOLUME = 127
//
// 8. Times given for special effects to decay
private constant real SFX_DECAY_TIME = 5.0
//
// 9. Sound effect for missiles
private constant string SOUND_PATH = "war3mapImported\\FrostmyrAmbient.wav"
private constant real PITCH = 0.5
private constant integer VOLUME = 65
//
// 10. Dealt damage configurations
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_COLD
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
//
// B. Missile
//
// 1. Missile's model filepath
private constant string MODEL_PATH = "war3mapImported\\Mini Comet.mdx"
//
// 2. If true missiles will be able to enter idle state
// - Makes it easier to control
// - Increases damage efficiency
private constant boolean ALLOW_IDLE = true
//
// 3. Default fly height for missiles
private constant real Z_OFFSET = 125.0
//
// 4. Missile trajectory configurations
private constant real LENGTH = 100.0
private constant real WIDTH = 100.0
private constant real HEIGHT = 100.0
//
// 5. Movespeed for missiles
private constant real MOVE_RATE = 13.0
//
// 6. Turn rate for missiles
private constant real TURN_RATE = 3.0*bj_DEGTORAD
//
// 7. Initial offset for missiles
private constant real SPAWN_OFFSET = 100.0
//
// 8. Rotate (spin) rate for missiles
// - Use negative value to inverse the rotation
private constant real SPIN_RATE = 5*bj_DEGTORAD
//
// 9. Speed factor when missiles deaccelerate
// - Only takes effect if ALLOW_IDLE is true
// - Must be between 0 ~ 1
// - The higher the slower
private constant real SLOW_RATE = 0.95
//
// 10. Maximum range between missile and target to deal damage
private constant real COLLISION_SIZE = 120.0
//
// Better not to touch this
private constant real INTERVAL = 0.03125
//
endglobals
//
// C. Non-constant
//
// 1. Created missile per cast
private function MissileCount takes integer level returns integer
return 5
endfunction
//
// 2. Damage dealt by every single missile
private function DamageAmount takes integer level returns real
return 10.0 * level
endfunction
//
// 3. Delay before next damage will be dealt
private function DamageDelay takes integer level returns real
return 0.125
endfunction
//
// 4. Combusted mana amount every certain times
private function ManaCost takes integer level returns real
return 2.5 + 2.5 *level
endfunction
//
// 5. Delay between mana reduction
private function CostDelay takes integer level returns real
return 0.25
endfunction
//
// 6. Target classification that can be hit by this spell
private function filterTarget takes player caster, unit target returns boolean
return IsUnitEnemy(target, caster) and not(IsUnitType(target, UNIT_TYPE_STRUCTURE) or IsUnitType(target, UNIT_TYPE_MECHANICAL))
endfunction
//
// 7. This function let you to set up what condition to stop the channel
// The caster will stop channeling when this function returns true
private function stopChannel takes unit caster returns boolean
return GetUnitAbilityLevel(caster, 'BSTN') > 0 // Stop when the caster is stunned
endfunction
//
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// EVENT CATCHER
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
private module EventCatcher
// Called whenever a unit takes damage from this spell
// - caster : caster of the spell (damage source)
// - target : damaged unit
// - level : caster's Frostmyr ability level
static method onDamage takes unit caster, unit target, integer level returns nothing
endmethod
endmodule
//
//
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
native UnitAlive takes unit id returns boolean
private module InitModule
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct FrostmyrMissile extends array
effect sfx
unit missile
real hOffset
real vOffset
real hMax
real vMax
implement Stack
endstruct
private struct Frostmyr
real locX
real locY
real targetX
real targetY
real missileX
real missileY
real angle
real hOffset
real vOffset
real damage
real damageDelay
real damageDelayX
real manaCost
real costDelay
real costDelayX
real missileSpin
real missileDistance
real missileDistanceX
real missileHPos
real missileVPos
real missileAngle
boolean idle
integer count
integer level
sound sfx
player owner
unit caster
unit target
timer tick
FrostmyrMissile stack
static RSound SoundFx
static trigger OrderTrigg= CreateTrigger()
static group TempGroup = CreateGroup()
static integer array Index
static constant real TAU = bj_PI*2
static constant real HP = bj_PI/2
implement optional EventCatcher
method destroy takes nothing returns nothing
local FrostmyrMissile node = .stack.first
// Kill missiles
loop
exitwhen node == 0
call UnitApplyTimedLife(node.missile, 'BTLF', SFX_DECAY_TIME)
call DestroyEffect(node.sfx)
set node.missile = null
set node.sfx = null
set node = node.next
endloop
call ReleaseTimer(.tick)
call StopSound(.sfx, true, true)
call .stack.destroy()
call deallocate()
// Remove leaks
set .caster = null
set .target = null
set .tick = null
set .sfx = null
endmethod
method stop takes nothing returns nothing
// Reset animation if not dead
if UnitAlive(.caster) then
call SetUnitAnimation(.caster, "stand")
endif
set Index[GetUnitUserData(.caster)] = 0
call UnitRemoveType(.caster, UNIT_TYPE_PEON)
call UnitRemoveAbility(.caster, CANCEL_ID)
call SetUnitAbilityLevel(.caster, SPELL_ID, .level)
call destroy()
endmethod
static method onPeriodic takes nothing returns nothing
local FrostmyrMissile node
local thistype this = GetTimerData(GetExpiredTimer())
local boolean b
local unit u
local real a
local real a2
local real as
local real d
local real f
local real h
local real m
local real v
local real x
local real y
local real z
local real x2
local real y2
local real z2
local real sf
// If have to stop channeling
if not UnitAlive(.caster) or stopChannel(.caster) then
call stop()
return
endif
if .target != null then
if UnitAlive(.target) then
set .targetX = GetUnitX(.target)
set .targetY = GetUnitY(.target)
else
set .target = null
endif
endif
// Check if the projectiles need to move
set b = not ALLOW_IDLE or (.targetX-.locX)*(.targetX-.locX)+(.targetY-.locY)*(.targetY-.locY) >= MOVE_RATE*MOVE_RATE
if b then
set a = Atan2(.targetY-.locY, .targetX-.locX)
if TURN_RATE > 0 and Cos(.angle-a) < Cos(TURN_RATE) then
if Sin(a-.angle) >= 0 then
set .angle = .angle + TURN_RATE
else
set .angle = .angle - TURN_RATE
endif
else
set .angle = a
endif
set .locX = .locX + MOVE_RATE * Cos(.angle)
set .locY = .locY + MOVE_RATE * Sin(.angle)
// Stop if the order point exceeds map boundaries
if .locX >= WorldBounds.maxX or .locX <= WorldBounds.minX or .locY >= WorldBounds.maxY or .locY <= WorldBounds.minY then
call stop()
return
endif
call SetSoundPosition(.sfx, .locX, .locY, Z_OFFSET)
endif
set .damageDelay = .damageDelay - INTERVAL
set .costDelay = .costDelay - INTERVAL
if .costDelay <= 0 then
set m = GetUnitState(.caster, UNIT_STATE_MANA) - .manaCost
call SetUnitState(.caster, UNIT_STATE_MANA, m)
set .costDelay = .costDelayX
if m <= 0 then
call stop()
return
endif
endif
// If idle calculate deaccelerated speed
if .idle then
set sf = missileDistanceX/2
set sf = 1-RAbsBJ(.missileDistance-sf)/sf*SLOW_RATE
else
set sf = 1
endif
set .missileDistance = .missileDistance + MOVE_RATE*sf
if .missileDistance > .missileDistanceX then
set .missileDistance = .missileDistanceX
endif
// Adjust control angle
set a = Atan2(.locY-.missileY, .locX-.missileX)
if TURN_RATE > 0 and Cos(.missileAngle-a) < Cos(TURN_RATE) then
if Sin(a-.missileAngle) >= 0 then
set .missileAngle = .missileAngle + TURN_RATE
else
set .missileAngle = .missileAngle - TURN_RATE
endif
else
set .missileAngle = a
endif
// Update missiles
set .missileSpin = .missileSpin + SPIN_RATE*.missileHPos
set f = (.missileDistanceX-.missileDistance)*(.missileDistance/.missileDistanceX)
set node = .stack.first
set as = TAU/.count
set a = as /2
loop
exitwhen node == 0
set node.hMax = WIDTH *Sin(a+.missileSpin)
set node.vMax = HEIGHT*Cos(a+.missileSpin)
set h = (4*node.hMax/.missileDistanceX)*f
set v = (4*node.vMax/.missileDistanceX)*f
set d = SquareRoot(.missileDistance*.missileDistance+h*h)
set a2 = .missileAngle-Atan(h/.missileDistance)
set x = .missileX + d * Cos(a2)
set y = .missileY + d * Sin(a2)
if x < WorldBounds.maxX and x > WorldBounds.minX and y < WorldBounds.maxY and y > WorldBounds.minY then
call ShowUnit(node.missile, true)
call SetUnitX(node.missile, x)
call SetUnitY(node.missile, y)
call SetUnitFacing(node.missile, .missileAngle*bj_RADTODEG)
call SetUnitFlyHeight(node.missile, Z_OFFSET+v*.missileVPos, 0)
set z = GetUnitZ(node.missile)
if .damageDelay <= 0 then
call GroupEnumUnitsInRange(TempGroup, x, y, COLLISION_SIZE, null)
loop
set u = FirstOfGroup(TempGroup)
exitwhen u == null
call GroupRemoveUnit(TempGroup, u)
if UnitAlive(u) and filterTarget(.owner, u) then
set x2 = GetUnitX(u)
set y2 = GetUnitY(u)
set z2 = GetUnitZ(u)
set d = SquareRoot((x-x2)*(x-x2)+(y-y2)*(y-y2))
set h = RAbsBJ(z2-z)
// Spherical collision
if d*d+h*h < COLLISION_SIZE*COLLISION_SIZE then
static if thistype.onDamage.exists then
call onDamage(.caster, u, .level)
endif
call SoundFx.play(x2, y2, z2, DSOUND_VOLUME)
call UnitDamageTarget(.caster, u, .damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, u, DAMAGE_SFX_PT))
endif
endif
endloop
endif
else
// Temporary hide missile if exceeds map boundaries
call ShowUnit(node.missile, false)
endif
set a = a + as
set node = node.next
endloop
if .damageDelay <= 0 then
set .damageDelay = .damageDelayX
endif
if .missileDistance == .missileDistanceX then
set .missileX = x
set .missileY = y
set .missileAngle = Atan2(.locY-.missileY, .locX-.missileX)
set .missileDistance = 0
set .missileVPos = -.missileVPos
if b then
set .idle = false
set .missileHPos = -.missileHPos
else
set .idle = true
set .missileHPos = 1
endif
endif
endmethod
static method orderStop takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call DisableTrigger(OrderTrigg)
call IssueImmediateOrder(.caster, "stop")
call SetUnitAnimation(.caster, ANIMATION)
call EnableTrigger(OrderTrigg)
call ReleaseTimer(t)
set t = null
endmethod
static method onOrder takes nothing returns boolean
local thistype this = Index[GetUnitUserData(GetTriggerUnit())]
local integer id
local widget w
local unit u
// If unit is casting Frostmyr
if this != 0 then
set id = GetIssuedOrderId()
if id == ORDER_ID then
set u = GetOrderTargetUnit()
if u == null then
set w = GetOrderTarget()
if w == null then
set .targetX = GetOrderPointX()
set .targetY = GetOrderPointY()
else
set .targetX = GetWidgetX(w)
set .targetY = GetWidgetY(w)
endif
set .target = null
set w = null
else
set .target = u
set u = null
endif
set .idle = false
endif
// This is done because seems like immediate (no target) order
// is somehow unable to prevent attack move order immediately
if id == 851983 then // If order is "attack"
call IssuePointOrder(.caster, "move", 0, 0)
else
call TimerStart(NewTimerEx(this), 0, false, function thistype.orderStop)
endif
endif
return false
endmethod
static method onCast takes nothing returns boolean
local FrostmyrMissile node
local thistype this
local integer data = GetUnitUserData(GetTriggerUnit())
local integer id = GetSpellAbilityId()
local integer i
local integer h
local integer v
local real a
local real as
if id == SPELL_ID and Index[data] == 0 then
set this = allocate()
set .caster = GetTriggerUnit()
set .owner = GetTriggerPlayer()
set .angle = GetUnitFacing(.caster)*bj_DEGTORAD
set .missileX = GetUnitX(.caster) + SPAWN_OFFSET * Cos(.angle)
set .missileY = GetUnitY(.caster) + SPAWN_OFFSET * Sin(.angle)
set .missileAngle= .angle
set .locX = .missileX + LENGTH * Cos(.angle)
set .locY = .missileY + LENGTH * Sin(.angle)
set .targetX = .locX
set .targetY = .locY
set .idle = ALLOW_IDLE
set .hOffset = 0
set .vOffset = 0
set .level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .damage = DamageAmount(.level)
set .damageDelayX= DamageDelay (.level)
set .manaCost = ManaCost (.level)
set .costDelayX = CostDelay (.level)
set .count = MissileCount(.level)
set .stack = FrostmyrMissile.create()
set .damageDelay = .damageDelayX
set .costDelay = .costDelayX
set as = TAU/.count
set a = as/2
set i = .count
set .missileSpin = 0
loop
set i = i - 1
set node = .stack.push()
set node.missile = CreateUnit(.owner, DUMMY_ID, .missileX, .missileY, .angle*bj_RADTODEG)
set node.sfx = AddSpecialEffectTarget(MODEL_PATH, node.missile, "origin")
set node.hMax = WIDTH *Sin(a)
set node.vMax = HEIGHT*Cos(a)
static if not LIBRARY_AutoFly then
if UnitAddAbility(node.missile, 'Amrf') and UnitRemoveAbility(node.missile, 'Amrf') then
endif
endif
call PauseUnit(node.missile, true)
call SetUnitFlyHeight(node.missile, Z_OFFSET, 0)
set a = a + as
exitwhen i == 0
endloop
set .locX = .targetX
set .locY = .targetY
set .missileDistanceX = LENGTH*2
set .missileDistance = 0
set .missileHPos = 1
set .missileVPos = 1
set .tick = NewTimerEx(this)
set .sfx = CreateSound(SOUND_PATH, true, true, false, 10, 10, "")
set Index[GetUnitUserData(.caster)] = this
call SetSoundVolume(.sfx, VOLUME)
call SetSoundPitch(.sfx, PITCH)
call StartSound(.sfx)
call UnitAddType(.caster, UNIT_TYPE_PEON)
call SetUnitAnimation(.caster, ANIMATION)
// Credits to Lambdadelta for this trick to hide main spell
call SetUnitAbilityLevel(.caster, SPELL_ID, 999)
call IncUnitAbilityLevel(.caster, SPELL_ID)
call UnitAddAbility(.caster, CANCEL_ID)
call TimerStart(.tick, INTERVAL, true, function thistype.onPeriodic)
elseif id == CANCEL_ID and Index[data] != 0 then
set this = Index[data]
call stop()
endif
return false
endmethod
// Needed so that the sound effect will appear since the first cast
static method preloadSound takes nothing returns nothing
local sound s = CreateSound(SOUND_PATH, false, false, true, 12700, 12700, "")
call StartSound(s)
call StopSound(s, true, false)
call ReleaseTimer(GetExpiredTimer())
set s = null
endmethod
static method init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local player p
loop
exitwhen i > 11
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
call TriggerRegisterPlayerUnitEvent(OrderTrigg, p, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
call TriggerRegisterPlayerUnitEvent(OrderTrigg, p, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
call TriggerRegisterPlayerUnitEvent(OrderTrigg, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
set i = i + 1
endloop
call TriggerAddCondition(t, Condition(function thistype.onCast))
call TriggerAddCondition(OrderTrigg, Condition(function thistype.onOrder))
call TimerStart(NewTimer(), 0, false, function thistype.preloadSound)
set SoundFx = RSound.create(DAMAGE_SOUND, true, true, 12700, 12700)
set t = null
endmethod
implement InitModule
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Stack /* v1.0.0.7
************************************************************************************
*
* */uses/*
*
* */ ErrorMessage /* hiveworkshop.com/forums/submissions-414/snippet-error-message-239210/
*
************************************************************************************
*
* module Stack
*
* Description
* -------------------------
*
* NA
*
* Fields
* -------------------------
*
* readonly static integer sentinel
*
* readonly thistype first
* readonly thistype next
*
* Methods
* -------------------------
*
* static method create takes nothing returns thistype
* method destroy takes nothing returns nothing
* - May only destroy stacks
*
* method push takes nothing returns thistype
* method pop takes nothing returns nothing
*
* method clear takes nothing returns nothing
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
************************************************************************************/
module Stack
private static thistype collectionCount = 0
private static thistype nodeCount = 0
debug private boolean isNode
debug private boolean isCollection
private thistype _next
method operator next takes nothing returns thistype
debug call ThrowError(this == 0, "Stack", "next", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "Stack", "next", "thistype", this, "Attempted To Read Invalid Node.")
return _next
endmethod
private thistype _first
method operator first takes nothing returns thistype
debug call ThrowError(this == 0, "Stack", "first", "thistype", this, "Attempted To Read Null Stack.")
debug call ThrowError(not isCollection, "Stack", "first", "thistype", this, "Attempted To Read Invalid Stack.")
return _first
endmethod
static method operator sentinel takes nothing returns integer
return 0
endmethod
private static method allocateCollection takes nothing returns thistype
local thistype this = thistype(0)._first
if (0 == this) then
debug call ThrowError(collectionCount == 8191, "Stack", "allocateCollection", "thistype", 0, "Overflow.")
set this = collectionCount + 1
set collectionCount = this
else
set thistype(0)._first = _first
endif
return this
endmethod
private static method allocateNode takes nothing returns thistype
local thistype this = thistype(0)._next
if (0 == this) then
debug call ThrowError(nodeCount == 8191, "Stack", "allocateNode", "thistype", 0, "Overflow.")
set this = nodeCount + 1
set nodeCount = this
else
set thistype(0)._next = _next
endif
return this
endmethod
static method create takes nothing returns thistype
local thistype this = allocateCollection()
debug set isCollection = true
set _first = 0
return this
endmethod
method push takes nothing returns thistype
local thistype node = allocateNode()
debug call ThrowError(this == 0, "Stack", "push", "thistype", this, "Attempted To Push On To Null Stack.")
debug call ThrowError(not isCollection, "Stack", "push", "thistype", this, "Attempted To Push On To Invalid Stack.")
debug set node.isNode = true
set node._next = _first
set _first = node
return node
endmethod
method pop takes nothing returns nothing
local thistype node = _first
debug call ThrowError(this == 0, "Stack", "pop", "thistype", this, "Attempted To Pop Null Stack.")
debug call ThrowError(not isCollection, "Stack", "pop", "thistype", this, "Attempted To Pop Invalid Stack.")
debug call ThrowError(node == 0, "Stack", "pop", "thistype", this, "Attempted To Pop Empty Stack.")
debug set node.isNode = false
set _first = node._next
set node._next = thistype(0)._next
set thistype(0)._next = node
endmethod
private method getBottom takes nothing returns thistype
set this = _first
loop
exitwhen _next == 0
set this = _next
endloop
return this
endmethod
method clear takes nothing returns nothing
debug local thistype node = _first
debug call ThrowError(this == 0, "Stack", "clear", "thistype", this, "Attempted To Clear Null Stack.")
debug call ThrowError(not isCollection, "Stack", "clear", "thistype", this, "Attempted To Clear Invalid Stack.")
static if DEBUG_MODE then
loop
exitwhen node == 0
set node.isNode = false
set node = node._next
endloop
endif
if (_first == 0) then
return
endif
set getBottom()._next = thistype(0)._next
set thistype(0)._next = _first
set _first = 0
endmethod
method destroy takes nothing returns nothing
debug call ThrowError(this == 0, "Stack", "destroy", "thistype", this, "Attempted To Destroy Null Stack.")
debug call ThrowError(not isCollection, "Stack", "destroy", "thistype", this, "Attempted To Destroy Invalid Stack.")
static if DEBUG_MODE then
debug call clear()
debug set isCollection = false
else
if (_first != 0) then
set getBottom()._next = thistype(0)._next
set thistype(0)._next = _first
endif
endif
set _first = thistype(0)._first
set thistype(0)._first = this
endmethod
static if DEBUG_MODE then
static method calculateMemoryUsage takes nothing returns integer
local thistype start = 1
local thistype end = 8191
local integer count = 0
loop
exitwhen integer(start) > integer(end)
if (integer(start) + 500 > integer(end)) then
return count + checkRegion(start, end)
else
set count = count + checkRegion(start, start + 500)
set start = start + 501
endif
endloop
return count
endmethod
private static method checkRegion takes thistype start, thistype end returns integer
local integer count = 0
loop
exitwhen integer(start) > integer(end)
if (start.isNode) then
set count = count + 1
endif
if (start.isCollection) then
set count = count + 1
endif
set start = start + 1
endloop
return count
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
local thistype start = 1
local thistype end = 8191
local string memory = null
loop
exitwhen integer(start) > integer(end)
if (integer(start) + 500 > integer(end)) then
if (memory != null) then
set memory = memory + ", "
endif
set memory = memory + checkRegion2(start, end)
set start = end + 1
else
if (memory != null) then
set memory = memory + ", "
endif
set memory = memory + checkRegion2(start, start + 500)
set start = start + 501
endif
endloop
return memory
endmethod
private static method checkRegion2 takes thistype start, thistype end returns string
local string memory = null
loop
exitwhen integer(start) > integer(end)
if (start.isNode) then
if (memory == null) then
set memory = I2S(start)
else
set memory = memory + ", " + I2S(start) + "N"
endif
endif
if (start.isCollection) then
if (memory == null) then
set memory = I2S(start)
else
set memory = memory + ", " + I2S(start) + "C"
endif
endif
set start = start + 1
endloop
return memory
endmethod
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ErrorMessage /* v1.0.1.4
*************************************************************************************
*
* Issue 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=0
//TESH.alwaysfold=0
library_once WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
* readonly static integer centerX
* readonly static integer centerY
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX=R2I(GetRectMaxX(world))
set maxY=R2I(GetRectMaxY(world))
set minX=R2I(GetRectMinX(world))
set minY=R2I(GetRectMinY(world))
set centerX=R2I((maxX+minX)/2)
set centerY=R2I((minY+maxY)/2)
set worldRegion=CreateRegion()
call RegionAddRect(worldRegion,world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library AutoFly /* v1.0.0.1
-Credits to Magtheridon96 and Bribe for code update
-Credits to Azlier for original
-thehelper.net/forums/showthread.php/139729-AutoFly
*************************************************************************************
*
* Makes SetUnitFlyHeight possible
*
*************************************************************************************
*
* */uses/*
*
* */ UnitIndexer /* hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
*
************************************************************************************/
private function i takes nothing returns boolean
return UnitAddAbility(GetIndexedUnit(), 'Amrf') and UnitRemoveAbility(GetIndexedUnit(), 'Amrf')
endfunction
private module Init
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function i), UnitIndexer.INDEX)
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.7
*************************************************************************************
*
* 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 = 'A002'
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 boolean enabled=true
private static method onEnter takes nothing returns boolean
local unit Q=GetFilterUnit()
local integer i
local integer d=o
if (enabled and 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_once Event /* v2.0.0.1
************************************************************************************
*
* Functions
*
* function CreateEvent takes nothing returns integer
* function TriggerRegisterEvent takes trigger t, 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 TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
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
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 RapidSound requires optional TimerUtils
globals
// Actually, just leave this value
private constant real MIN_DELAY_FACTOR = 4.0
endglobals
/* v1.6
Description
¯¯¯¯¯¯¯¯¯¯¯
Allows you to play sounds rapidly and flawlessly without limit.
Remember one sound file can only have one RSound instance.
External Dependencies
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
(Optional)
TimerUtils by Vexorian
wc3c.net/showthread.php?t=101322
User API
¯¯¯¯¯¯¯¯
struct RSound
Instantiate an RapidSound instance
| static method create takes string fileName, boolean is3D, boolean autoStop, integer inRate, integer outRate returns thistype
autoStop => stop when sound is out of range
inRate => fade in rate
outRate => fade out rate
Play the sound at given coordinate
| method play takes real x, real y, real z, integer volume returns nothing
Stop sound
| method stop takes boolean fadeOut returns nothing
Destroy RapidSound instance
| method kill takes nothing returns nothing
Sound file duration (in second)
| method operator duration takes nothing returns real
Resource Link
¯¯¯¯¯¯¯¯¯¯¯¯¯
hiveworkshop.com/threads/snippet-rapidsound.258991/
*/
struct RSound
private static constant integer MAX_COUNT = 4
private static integer Counter = -1
private static string array StrLib
private static thistype array StrDex
private integer ct
private integer lib
private integer dex
private real dur
private sound array snd[thistype.MAX_COUNT]
private timer array tmr[thistype.MAX_COUNT]
method operator duration takes nothing returns real
return .dur*MIN_DELAY_FACTOR
endmethod
method kill takes nothing returns nothing
local integer i
set .ct = .ct - 1
if .ct == 0 then
set i = 0
loop
exitwhen i == MAX_COUNT
call StopSound(.snd[i], true, false)
static if LIBRARY_TimerUtils then
call ReleaseTimer(.tmr[i])
else
call DestroyTimer(.tmr[i])
endif
set .snd[i] = null
set .tmr[i] = null
set i = i + 1
endloop
set StrLib[.lib] = StrLib[Counter]
set StrDex[.lib] = StrDex[Counter]
set Counter = Counter - 1
call deallocate()
endif
endmethod
method stop takes boolean fadeOut returns nothing
local integer i = 0
loop
exitwhen i == MAX_COUNT
call StopSound(.snd[i], false, fadeOut)
set i = i + 1
endloop
endmethod
method play takes real x, real y, real z, integer volume returns nothing
set .dex = .dex + 1
if .dex == MAX_COUNT then
set .dex = 0
endif
if TimerGetRemaining(.tmr[.dex]) == 0 then
call StopSound(.snd[.dex], false, false)
call SetSoundPosition(.snd[.dex], x, y, z)
call SetSoundVolume(.snd[.dex], volume)
call StartSound(.snd[.dex])
call TimerStart(.tmr[.dex], .dur, false, null)
endif
endmethod
static method create takes string fileName, boolean is3D, boolean autoStop, integer inRate, integer outRate returns thistype
local thistype this
local integer i = 0
local boolean b = true
loop
exitwhen i > Counter
if fileName == StrLib[i] then
set b = false
exitwhen true
endif
set i = i + 1
endloop
if b then
set this = allocate()
set Counter = Counter + 1
set StrLib[Counter] = fileName
set StrDex[Counter] = this
set .ct = 1
set .dex = -1
set .lib = Counter
set .dur = I2R(GetSoundFileDuration(fileName))/(1000.*MIN_DELAY_FACTOR)
set i = 0
loop
exitwhen i == MAX_COUNT
set .snd[i] = CreateSound(fileName, false, is3D, autoStop, inRate, outRate, "")
static if LIBRARY_TimerUtils then
set .tmr[i] = NewTimer()
call TimerStart(.tmr[i], 0, false, null)
call PauseTimer(.tmr[i])
else
set .tmr[i] = CreateTimer()
endif
set i = i + 1
endloop
else
set this = StrDex[i]
set .ct = .ct + 1
endif
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Test initializer init
private function start takes nothing returns nothing
call EnableUserControl(true)
endfunction
private function init takes nothing returns nothing
call TimerStart(CreateTimer(), 6.0, false, function start)
call SetPlayerFlagBJ( PLAYER_STATE_GIVES_BOUNTY, true, Player(1) )
call CinematicFadeBJ( bj_CINEFADETYPE_FADEIN, 6.00, "ReplaceableTextures\\CameraMasks\\Black_mask.blp", 0, 0, 0, 0 )
call CameraSetSmoothingFactor(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 0, " " + GradientTextEx("[Frostmyr v1.5]", "55dddd", "eeffff", 50))
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 0, " " + "|cffffaa00Created by|r: Quilnez")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 0, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 0, "|cffffff44If you find any glitch/bug please report it to me.|r")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 0, "|cffffff44And if you demand any new feature don't hesitate|r")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 0, "|cffffff44to tell me. Thanks!|r")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 0, " ")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.5, 0, 0, "(Press 'esc' to reset cooldown, mana, and hp.)")
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GradientText /* v3.2 */ requires HexString
/********************************************************************************************
* *
* 'Gradient Text' *
* ***** *
* by Dalvengyr *
* *
* Description: *
* This library is used to convert inputted string into the degradated one based on given *
* or specified color code. *
* *
* !! ADD_PREFIX at HexString library must be "false" !! *
* *
* Requirement: *
* - HexString by Bannar aka Spinnaker *
* hiveworkshop.com/forums/jass-resources-412/snippet-hexstring-248993/ *
* - JNGP *
* *
* APIs *
* *
* 1. Create gradations for inputed string *
* *
* function GradientText takes string s returns string *
* *
* 2. Create gradations for inputed string with specific color code and size. Size is in *
* percentage *
* *
* function GradientTextEx takes string s, string code1, string code2, integer size returns string
* *
********************************************************************************************/
globals
// Configuration //
// For identifying a color code. Don't use "|c" !!
public constant string IDENTIFIER = "#"
// You are recommended to keep this as true. Will delete any character '|'
private constant boolean DELETE = true
// Furthermore, edit them by your own risk
private constant integer LENGTH = StringLength(IDENTIFIER)
private string array String
private integer array Integer
endglobals
private function addGradation takes string s, string code1, string code2, integer len returns string
local boolean b
if len > 1 and code1 != code2 then
set String[0] = ""
set Integer[0] = 1
// Convert hex code to integer
set Integer[1] = HS2I(SubString(code1, 0, 2))
set Integer[2] = HS2I(SubString(code1, 2, 4))
set Integer[3] = HS2I(SubString(code1, 4, 6))
// Set the color interval from code1 to code2 devided by length of string
set Integer[4] = (HS2I(SubString(code2, 0, 2)) - Integer[1]) / (len + 1)
set Integer[5] = (HS2I(SubString(code2, 2, 4)) - Integer[2]) / (len + 1)
set Integer[6] = (HS2I(SubString(code2, 4, 6)) - Integer[3]) / (len + 1)
loop
exitwhen Integer[0] > len
set String[4] = SubString(s, Integer[0] - 1, Integer[0])
if String[4] != " " then
set b = true
static if DELETE then
if String[4] == "|" then
set b = false
endif
endif
if b then
// Convert integer to hex code
set String[1] = I2HS(Integer[1] + Integer[4] * Integer[0], false)
set String[2] = I2HS(Integer[2] + Integer[5] * Integer[0], false)
set String[3] = I2HS(Integer[3] + Integer[6] * Integer[0], false)
// Attach '0' if hex code only contains one letter
set Integer[8] = 1
loop
exitwhen Integer[8] > 3
if StringLength(String[Integer[8]]) == 1 then
set String[Integer[8]] = "0" + String[Integer[8]]
endif
set Integer[8] = Integer[8] + 1
endloop
set String[0] = String[0] + "|cff" + String[1] + String[2] + String[3] + String[4]
endif
else
set String[0] = String[0] + " "
endif
set Integer[0] = Integer[0] + 1
endloop
else
static if DELETE then
// Search for symbol '|' first
set Integer[0] = 0
loop
exitwhen Integer[0] > len
if SubString(s, Integer[0], Integer[0] + 1) == "|" then
set s = SubString(s, 0, Integer[0]) + SubString(s, Integer[0] + 1, len)
set len = len - 1
endif
set Integer[0] = Integer[0] + 1
endloop
endif
set String[0] = "|cff" + code1 + s
endif
return String[0]
endfunction
function GradientText takes string s returns string
local string code1 = ""
local string code2 = ""
local string grad
local integer i = 0
local integer t = 0
local integer dex1
local integer dex2
local integer len = StringLength(s)
// t is used to save last search point in the loop
// dex1(2) is used to save found hex code's position
loop
exitwhen i > len
if SubString(s, i, i + LENGTH) == IDENTIFIER then
// If first code is still empty then save found code to it
if code1 == "" then
set t = i
set dex1 = i
set code1 = SubString(s, i + LENGTH, i + LENGTH + 6)
// Remove found code from string
set s = SubString(s, 0, i) + SubString(s, i + LENGTH + 6, len)
set len = len - (LENGTH + 6)
else
set dex2 = i
set code2 = SubString(s, i + LENGTH, i + LENGTH + 6)
set s = SubString(s, 0, i) + SubString(s, i + LENGTH + 6, len)
set len = len - (LENGTH + 6)
if HS2I(code1) >= 0 and HS2I(code2) >= 0 then
set grad = addGradation(SubString(s, dex1, dex2), code1, code2, dex2 - dex1)
else
set grad = SubString(s, dex1, dex2)
debug call BJDebugMsg("Invalid color code: " + code1 + " - " + code2)
endif
// Insert the result into string s
set s = SubString(s, 0, dex1) + grad + SubString(s, dex2, len)
// Preparation to search for another hex code
set code1 = code2
set t = t + StringLength(grad)
set i = t
set len = len - (dex2 - dex1) + t
set dex1 = t
endif
endif
set i = i + 1
endloop
if code1 != "" then
if code2 == "" then
// If there is only one found hex code
if HS2I(code1) >= 0 then
// Use function addGradation to remove symbol '|' before inserting hex code
set s = SubString(s, 0, dex1) + addGradation(SubString(s, dex1, len), code1, code1, len - dex1) + "|r"
debug else
debug call BJDebugMsg("Invalid color code: " + code1 + " - " + code2)
endif
else
set s = s + "|r"
endif
endif
return s
endfunction
function GradientTextEx takes string s, string code1, string code2, integer size returns string
local boolean b = true
local integer len = StringLength(s)
local integer sz = R2I(len * (size * .01))
local integer i = 0
local integer mod
local string res = ""
// If specified code is invalid then skip the rest
if HS2I(code1) < 0 or HS2I(code2) < 0 or StringLength(code1) != 6 or StringLength(code2) != 6 then
debug call BJDebugMsg("Invalid color code: " + code1 + " - " + code2)
return s
endif
// Avoid critical error
if sz < 1 then
set sz = 1
endif
// Loop to insert hex code into string based on given size
loop
set mod = i - (i/sz) * sz
if mod == 0 then
if b then
set res = res + IDENTIFIER + code1
else
set res = res + IDENTIFIER + code2
endif
set b = not b
endif
if i <= len then
set res = res + SubString(s, i, i + 1)
endif
exitwhen i >= len and mod == 0
set i = i + 1
endloop
return GradientText(res)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* HexString v1.0.0.3
* by Bannar aka Spinnaker
*
* Performs conversions from hexadecimal numbers to strings and vice versa.
*
* Credits to TheDamien for hash formula from original Ascii project
*
******************************************************************************
*
* private constant boolean ADD_PREFIX
* whether to add or not the "0x" prefix; used only in I2HS function
*
*
* function I2HS takes integer hex, boolean upper returns string
* returns hexadecimal representation of hex as string
*
* function HS2I takes string s returns integer
* returns integral number retrieved from s representing number in hexadecimal convention
*
*****************************************************************************/
library HexString
globals
/**
Config
*/
private constant boolean ADD_PREFIX = false
endglobals
globals
private string array Id2CharMap // (16)
private integer array Int2IdMap // (16)
endglobals
function I2HS takes integer hex, boolean upper returns string
local string s = ""
local integer div
loop
set div = hex/16
set s = Id2CharMap[hex - div * 16] + s
set hex = div
exitwhen hex == 0
endloop
if ( upper ) then
set s = StringCase(s, true)
endif
static if ( ADD_PREFIX ) then
set s = "0x" + s
endif
return s
endfunction
function HS2I takes string s returns integer
local integer size = StringLength(s)
local integer hex = 0
local integer it = 0
local integer key
local string char
if ( SubString(s, 0, 2) == "0x" ) then
set s = SubString(s, 2, size)
set size = size - 2
endif
loop
exitwhen it == size
set char = SubString(s, it, it+1)
set key = Int2IdMap[StringHash(char) / 0x1F0748 + 0x3EA]
debug if (key == 0) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "DEBUG: Hex::HS2I Invalid string character.")
debug return -1
debug endif
set hex = hex*16 + (key-1)
set it = it+1
endloop
return hex
endfunction
private module HexInit
private static method onInit takes nothing returns nothing
set Id2CharMap[0] = "0"
set Id2CharMap[1] = "1"
set Id2CharMap[2] = "2"
set Id2CharMap[3] = "3"
set Id2CharMap[4] = "4"
set Id2CharMap[5] = "5"
set Id2CharMap[6] = "6"
set Id2CharMap[7] = "7"
set Id2CharMap[8] = "8"
set Id2CharMap[9] = "9"
set Id2CharMap[10] = "a"
set Id2CharMap[11] = "b"
set Id2CharMap[12] = "c"
set Id2CharMap[13] = "d"
set Id2CharMap[14] = "e"
set Id2CharMap[15] = "f"
set Int2IdMap[883] = 1
set Int2IdMap[1558] = 2
set Int2IdMap[684] = 3
set Int2IdMap[582] = 4
set Int2IdMap[668] = 5
set Int2IdMap[538] = 6
set Int2IdMap[672] = 7
set Int2IdMap[1173] = 8
set Int2IdMap[71] = 9
set Int2IdMap[277] = 10
set Int2IdMap[222] = 11
set Int2IdMap[178] = 12
set Int2IdMap[236] = 13
set Int2IdMap[184] = 14
set Int2IdMap[1295] = 15
set Int2IdMap[1390] = 16
endmethod
endmodule
private struct Hex extends array
implement HexInit
endstruct
endlibrary